2 * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include "byte-order.h"
24 #include "openflow/openflow.h"
25 #include "openvswitch/dynamic-string.h"
26 #include "openvswitch/match.h"
27 #include "openvswitch/meta-flow.h"
28 #include "openvswitch/ofp-actions.h"
29 #include "openvswitch/ofp-errors.h"
30 #include "openvswitch/ofp-util.h"
31 #include "openvswitch/ofpbuf.h"
32 #include "vl-mff-map.h"
33 #include "unaligned.h"
36 /* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid,
37 * otherwise an OFPERR_*. */
39 learn_check(const struct ofpact_learn
*learn
, const struct match
*src_match
)
41 const struct ofpact_learn_spec
*spec
;
42 struct match dst_match
;
44 match_init_catchall(&dst_match
);
45 OFPACT_LEARN_SPEC_FOR_EACH (spec
, learn
) {
48 /* Check the source. */
49 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
50 error
= mf_check_src(&spec
->src
, src_match
);
56 /* Check the destination. */
57 switch (spec
->dst_type
) {
58 case NX_LEARN_DST_MATCH
:
59 error
= mf_check_src(&spec
->dst
, &dst_match
);
63 if (spec
->src_type
& NX_LEARN_SRC_IMMEDIATE
) {
64 mf_write_subfield_value(&spec
->dst
,
65 ofpact_learn_spec_imm(spec
),
70 case NX_LEARN_DST_LOAD
:
71 error
= mf_check_dst(&spec
->dst
, &dst_match
);
77 case NX_LEARN_DST_OUTPUT
:
85 /* Composes 'fm' so that executing it will implement 'learn' given that the
86 * packet being processed has 'flow' as its flow.
88 * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize
89 * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the
92 * The caller has to actually execute 'fm'. */
94 learn_execute(const struct ofpact_learn
*learn
, const struct flow
*flow
,
95 struct ofputil_flow_mod
*fm
, struct ofpbuf
*ofpacts
)
97 const struct ofpact_learn_spec
*spec
;
99 match_init_catchall(&fm
->match
);
100 fm
->priority
= learn
->priority
;
101 fm
->cookie
= htonll(0);
102 fm
->cookie_mask
= htonll(0);
103 fm
->new_cookie
= learn
->cookie
;
104 fm
->modify_cookie
= fm
->new_cookie
!= OVS_BE64_MAX
;
105 fm
->table_id
= learn
->table_id
;
106 fm
->command
= OFPFC_MODIFY_STRICT
;
107 fm
->idle_timeout
= learn
->idle_timeout
;
108 fm
->hard_timeout
= learn
->hard_timeout
;
110 fm
->buffer_id
= UINT32_MAX
;
111 fm
->out_port
= OFPP_NONE
;
112 fm
->ofpacts_tlv_bitmap
= 0;
114 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
115 fm
->flags
|= OFPUTIL_FF_SEND_FLOW_REM
;
120 if (learn
->fin_idle_timeout
|| learn
->fin_hard_timeout
) {
121 struct ofpact_fin_timeout
*oft
;
123 oft
= ofpact_put_FIN_TIMEOUT(ofpacts
);
124 oft
->fin_idle_timeout
= learn
->fin_idle_timeout
;
125 oft
->fin_hard_timeout
= learn
->fin_hard_timeout
;
128 OFPACT_LEARN_SPEC_FOR_EACH (spec
, learn
) {
129 struct ofpact_set_field
*sf
;
130 union mf_subvalue value
;
132 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
133 mf_read_subfield(&spec
->src
, flow
, &value
);
135 mf_subvalue_from_value(&spec
->dst
, &value
,
136 ofpact_learn_spec_imm(spec
));
139 switch (spec
->dst_type
) {
140 case NX_LEARN_DST_MATCH
:
141 mf_write_subfield(&spec
->dst
, &value
, &fm
->match
);
142 mf_vl_mff_set_tlv_bitmap(
143 spec
->dst
.field
, &fm
->match
.flow
.tunnel
.metadata
.present
.map
);
146 case NX_LEARN_DST_LOAD
:
147 sf
= ofpact_put_reg_load(ofpacts
, spec
->dst
.field
, NULL
, NULL
);
148 bitwise_copy(&value
, sizeof value
, 0,
149 sf
->value
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
151 bitwise_one(ofpact_set_field_mask(sf
), spec
->dst
.field
->n_bytes
,
152 spec
->dst
.ofs
, spec
->n_bits
);
153 mf_vl_mff_set_tlv_bitmap(spec
->dst
.field
, &fm
->ofpacts_tlv_bitmap
);
156 case NX_LEARN_DST_OUTPUT
:
157 if (spec
->n_bits
<= 16
158 || is_all_zeros(value
.u8
, sizeof value
- 2)) {
159 ofp_port_t port
= u16_to_ofp(ntohll(value
.integer
));
161 if (ofp_to_u16(port
) < ofp_to_u16(OFPP_MAX
)
162 || port
== OFPP_IN_PORT
163 || port
== OFPP_FLOOD
164 || port
== OFPP_LOCAL
165 || port
== OFPP_ALL
) {
166 ofpact_put_OUTPUT(ofpacts
)->port
= port
;
173 fm
->ofpacts
= ofpacts
->data
;
174 fm
->ofpacts_len
= ofpacts
->size
;
177 /* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
178 * the learn action 'learn'. */
180 learn_mask(const struct ofpact_learn
*learn
, struct flow_wildcards
*wc
)
182 const struct ofpact_learn_spec
*spec
;
183 union mf_subvalue value
;
185 memset(&value
, 0xff, sizeof value
);
186 OFPACT_LEARN_SPEC_FOR_EACH (spec
, learn
) {
187 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
188 mf_write_subfield_flow(&spec
->src
, &value
, &wc
->masks
);
193 /* Returns NULL if successful, otherwise a malloc()'d string describing the
194 * error. The caller is responsible for freeing the returned string. */
195 static char * OVS_WARN_UNUSED_RESULT
196 learn_parse_load_immediate(union mf_subvalue
*imm
, const char *s
,
197 const char *full_s
, struct ofpact_learn_spec
*spec
,
198 struct ofpbuf
*ofpacts
)
200 struct mf_subfield dst
;
203 error
= mf_parse_subfield(&dst
, s
);
207 if (!mf_nxm_header(dst
.field
->id
)) {
208 return xasprintf("%s: experimenter OXM field '%s' not supported",
212 if (!bitwise_is_all_zeros(imm
, sizeof *imm
, dst
.n_bits
,
213 (8 * sizeof *imm
) - dst
.n_bits
)) {
214 return xasprintf("%s: value does not fit into %u bits",
218 spec
->n_bits
= dst
.n_bits
;
219 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
220 spec
->dst_type
= NX_LEARN_DST_LOAD
;
223 /* Push value last, as this may reallocate 'spec'! */
224 unsigned int n_bytes
= DIV_ROUND_UP(dst
.n_bits
, 8);
225 uint8_t *src_imm
= ofpbuf_put_zeros(ofpacts
, OFPACT_ALIGN(n_bytes
));
226 memcpy(src_imm
, &imm
->u8
[sizeof imm
->u8
- n_bytes
], n_bytes
);
231 /* Returns NULL if successful, otherwise a malloc()'d string describing the
232 * error. The caller is responsible for freeing the returned string. */
233 static char * OVS_WARN_UNUSED_RESULT
234 learn_parse_spec(const char *orig
, char *name
, char *value
,
235 struct ofpact_learn_spec
*spec
,
236 struct ofpbuf
*ofpacts
, struct match
*match
)
238 /* Parse destination and check prerequisites. */
239 struct mf_subfield dst
;
241 char *error
= mf_parse_subfield(&dst
, name
);
242 bool parse_error
= error
!= NULL
;
246 if (!mf_nxm_header(dst
.field
->id
)) {
247 return xasprintf("%s: experimenter OXM field '%s' not supported",
251 spec
->n_bits
= dst
.n_bits
;
252 spec
->dst_type
= NX_LEARN_DST_MATCH
;
254 /* Parse source and check prerequisites. */
255 if (value
[0] != '\0') {
256 struct mf_subfield src
;
257 error
= mf_parse_subfield(&src
, value
);
260 char *imm_error
= NULL
;
262 /* Try an immediate value. */
263 if (dst
.ofs
== 0 && dst
.n_bits
== dst
.field
->n_bits
) {
264 /* Full field value. */
265 imm_error
= mf_parse_value(dst
.field
, value
, &imm
);
268 /* Partial field value. */
269 if (parse_int_string(value
, (uint8_t *)&imm
,
270 dst
.field
->n_bytes
, &tail
)
272 imm_error
= xasprintf("%s: cannot parse integer value", orig
);
276 !bitwise_is_all_zeros(&imm
, dst
.field
->n_bytes
,
278 dst
.field
->n_bytes
* 8 - dst
.n_bits
)) {
282 mf_format(dst
.field
, &imm
, NULL
, &ds
);
283 imm_error
= xasprintf("%s: value %s does not fit into %d bits",
284 orig
, ds_cstr(&ds
), dst
.n_bits
);
289 char *err
= xasprintf("%s: %s value %s cannot be parsed as a subfield (%s) or an immediate value (%s)",
290 orig
, name
, value
, error
, imm_error
);
296 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
298 /* Update 'match' to allow for satisfying destination
300 mf_write_subfield_value(&dst
, &imm
, match
);
302 /* Push value last, as this may reallocate 'spec'! */
303 unsigned int imm_bytes
= DIV_ROUND_UP(dst
.n_bits
, 8);
304 uint8_t *src_imm
= ofpbuf_put_zeros(ofpacts
,
305 OFPACT_ALIGN(imm_bytes
));
306 memcpy(src_imm
, &imm
, imm_bytes
);
312 if (spec
->src
.n_bits
!= spec
->dst
.n_bits
) {
313 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
314 "differ", orig
, name
, spec
->src
.n_bits
, value
,
318 spec
->src
= spec
->dst
;
321 spec
->src_type
= NX_LEARN_SRC_FIELD
;
322 } else if (!strcmp(name
, "load")) {
323 union mf_subvalue imm
;
325 char *dst_value
= strstr(value
, "->");
327 if (dst_value
== value
) {
328 return xasprintf("%s: missing source before `->' in `%s'", name
,
332 return xasprintf("%s: missing `->' in `%s'", name
, value
);
335 if (!parse_int_string(value
, imm
.u8
, sizeof imm
.u8
, (char **) &tail
)
337 if (tail
!= dst_value
) {
338 return xasprintf("%s: garbage before `->' in `%s'",
342 char *error
= learn_parse_load_immediate(&imm
, dst_value
+ 2, value
, spec
,
348 struct ofpact_reg_move move
;
351 error
= nxm_parse_reg_move(&move
, value
);
356 spec
->n_bits
= move
.src
.n_bits
;
357 spec
->src_type
= NX_LEARN_SRC_FIELD
;
358 spec
->src
= move
.src
;
359 spec
->dst_type
= NX_LEARN_DST_LOAD
;
360 spec
->dst
= move
.dst
;
362 } else if (!strcmp(name
, "output")) {
363 char *error
= mf_parse_subfield(&spec
->src
, value
);
368 spec
->n_bits
= spec
->src
.n_bits
;
369 spec
->src_type
= NX_LEARN_SRC_FIELD
;
370 spec
->dst_type
= NX_LEARN_DST_OUTPUT
;
372 return xasprintf("%s: unknown keyword %s", orig
, name
);
378 /* Returns NULL if successful, otherwise a malloc()'d string describing the
379 * error. The caller is responsible for freeing the returned string. */
380 static char * OVS_WARN_UNUSED_RESULT
381 learn_parse__(char *orig
, char *arg
, struct ofpbuf
*ofpacts
)
383 struct ofpact_learn
*learn
;
387 learn
= ofpact_put_LEARN(ofpacts
);
388 learn
->idle_timeout
= OFP_FLOW_PERMANENT
;
389 learn
->hard_timeout
= OFP_FLOW_PERMANENT
;
390 learn
->priority
= OFP_DEFAULT_PRIORITY
;
393 match_init_catchall(&match
);
394 while (ofputil_parse_key_value(&arg
, &name
, &value
)) {
395 if (!strcmp(name
, "table")) {
396 learn
->table_id
= atoi(value
);
397 if (learn
->table_id
== 255) {
398 return xasprintf("%s: table id 255 not valid for `learn' "
401 } else if (!strcmp(name
, "priority")) {
402 learn
->priority
= atoi(value
);
403 } else if (!strcmp(name
, "idle_timeout")) {
404 learn
->idle_timeout
= atoi(value
);
405 } else if (!strcmp(name
, "hard_timeout")) {
406 learn
->hard_timeout
= atoi(value
);
407 } else if (!strcmp(name
, "fin_idle_timeout")) {
408 learn
->fin_idle_timeout
= atoi(value
);
409 } else if (!strcmp(name
, "fin_hard_timeout")) {
410 learn
->fin_hard_timeout
= atoi(value
);
411 } else if (!strcmp(name
, "cookie")) {
412 learn
->cookie
= htonll(strtoull(value
, NULL
, 0));
413 } else if (!strcmp(name
, "send_flow_rem")) {
414 learn
->flags
|= NX_LEARN_F_SEND_FLOW_REM
;
415 } else if (!strcmp(name
, "delete_learned")) {
416 learn
->flags
|= NX_LEARN_F_DELETE_LEARNED
;
417 } else if (!strcmp(name
, "limit")) {
418 learn
->limit
= atoi(value
);
419 } else if (!strcmp(name
, "result_dst")) {
421 learn
->flags
|= NX_LEARN_F_WRITE_RESULT
;
422 error
= mf_parse_subfield(&learn
->result_dst
, value
);
426 if (!learn
->result_dst
.field
->writable
) {
427 return xasprintf("%s is read-only", value
);
429 if (learn
->result_dst
.n_bits
!= 1) {
430 return xasprintf("result_dst in 'learn' action must be a "
434 struct ofpact_learn_spec
*spec
;
437 spec
= ofpbuf_put_zeros(ofpacts
, sizeof *spec
);
438 error
= learn_parse_spec(orig
, name
, value
, spec
, ofpacts
, &match
);
442 learn
= ofpacts
->header
;
445 ofpact_finish_LEARN(ofpacts
, &learn
);
450 /* Parses 'arg' as a set of arguments to the "learn" action and appends a
451 * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
454 * Returns NULL if successful, otherwise a malloc()'d string describing the
455 * error. The caller is responsible for freeing the returned string.
457 * If 'flow' is nonnull, then it should be the flow from a struct match that is
458 * the matching rule for the learning action. This helps to better validate
459 * the action's arguments.
462 char * OVS_WARN_UNUSED_RESULT
463 learn_parse(char *arg
, struct ofpbuf
*ofpacts
)
465 char *orig
= xstrdup(arg
);
466 char *error
= learn_parse__(orig
, arg
, ofpacts
);
471 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
474 learn_format(const struct ofpact_learn
*learn
, struct ds
*s
)
476 const struct ofpact_learn_spec
*spec
;
479 match_init_catchall(&match
);
481 ds_put_format(s
, "%slearn(%s%stable=%s%"PRIu8
,
482 colors
.learn
, colors
.end
, colors
.special
, colors
.end
,
484 if (learn
->idle_timeout
!= OFP_FLOW_PERMANENT
) {
485 ds_put_format(s
, ",%sidle_timeout=%s%"PRIu16
,
486 colors
.param
, colors
.end
, learn
->idle_timeout
);
488 if (learn
->hard_timeout
!= OFP_FLOW_PERMANENT
) {
489 ds_put_format(s
, ",%shard_timeout=%s%"PRIu16
,
490 colors
.param
, colors
.end
, learn
->hard_timeout
);
492 if (learn
->fin_idle_timeout
) {
493 ds_put_format(s
, ",%sfin_idle_timeout=%s%"PRIu16
,
494 colors
.param
, colors
.end
, learn
->fin_idle_timeout
);
496 if (learn
->fin_hard_timeout
) {
497 ds_put_format(s
, "%s,fin_hard_timeout=%s%"PRIu16
,
498 colors
.param
, colors
.end
, learn
->fin_hard_timeout
);
500 if (learn
->priority
!= OFP_DEFAULT_PRIORITY
) {
501 ds_put_format(s
, "%s,priority=%s%"PRIu16
,
502 colors
.special
, colors
.end
, learn
->priority
);
504 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
505 ds_put_format(s
, ",%ssend_flow_rem%s", colors
.value
, colors
.end
);
507 if (learn
->flags
& NX_LEARN_F_DELETE_LEARNED
) {
508 ds_put_format(s
, ",%sdelete_learned%s", colors
.value
, colors
.end
);
510 if (learn
->cookie
!= 0) {
511 ds_put_format(s
, ",%scookie=%s%#"PRIx64
,
512 colors
.param
, colors
.end
, ntohll(learn
->cookie
));
514 if (learn
->limit
!= 0) {
515 ds_put_format(s
, ",%slimit=%s%"PRIu32
,
516 colors
.param
, colors
.end
, learn
->limit
);
518 if (learn
->flags
& NX_LEARN_F_WRITE_RESULT
) {
519 ds_put_format(s
, ",%sresult_dst=%s", colors
.param
, colors
.end
);
520 mf_format_subfield(&learn
->result_dst
, s
);
523 OFPACT_LEARN_SPEC_FOR_EACH (spec
, learn
) {
524 unsigned int n_bytes
= DIV_ROUND_UP(spec
->n_bits
, 8);
527 switch (spec
->src_type
| spec
->dst_type
) {
528 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_MATCH
: {
529 if (spec
->dst
.ofs
== 0
530 && spec
->dst
.n_bits
== spec
->dst
.field
->n_bits
) {
531 union mf_value value
;
533 memset(&value
, 0, sizeof value
);
534 memcpy(&value
.b
[spec
->dst
.field
->n_bytes
- n_bytes
],
535 ofpact_learn_spec_imm(spec
), n_bytes
);
536 ds_put_format(s
, "%s%s=%s", colors
.param
,
537 spec
->dst
.field
->name
, colors
.end
);
538 mf_format(spec
->dst
.field
, &value
, NULL
, s
);
540 ds_put_format(s
, "%s", colors
.param
);
541 mf_format_subfield(&spec
->dst
, s
);
542 ds_put_format(s
, "=%s", colors
.end
);
543 ds_put_hex(s
, ofpact_learn_spec_imm(spec
), n_bytes
);
547 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_MATCH
:
548 ds_put_format(s
, "%s", colors
.param
);
549 mf_format_subfield(&spec
->dst
, s
);
550 ds_put_format(s
, "%s", colors
.end
);
551 if (spec
->src
.field
!= spec
->dst
.field
||
552 spec
->src
.ofs
!= spec
->dst
.ofs
) {
553 ds_put_format(s
, "%s=%s", colors
.param
, colors
.end
);
554 mf_format_subfield(&spec
->src
, s
);
558 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_LOAD
:
559 ds_put_format(s
, "%sload:%s", colors
.special
, colors
.end
);
560 ds_put_hex(s
, ofpact_learn_spec_imm(spec
), n_bytes
);
561 ds_put_format(s
, "%s->%s", colors
.special
, colors
.end
);
562 mf_format_subfield(&spec
->dst
, s
);
565 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_LOAD
:
566 ds_put_format(s
, "%sload:%s", colors
.special
, colors
.end
);
567 mf_format_subfield(&spec
->src
, s
);
568 ds_put_format(s
, "%s->%s", colors
.special
, colors
.end
);
569 mf_format_subfield(&spec
->dst
, s
);
572 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_OUTPUT
:
573 ds_put_format(s
, "%soutput:%s", colors
.special
, colors
.end
);
574 mf_format_subfield(&spec
->src
, s
);
578 ds_put_format(s
, "%s)%s", colors
.learn
, colors
.end
);