2 * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 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"
23 #include "openvswitch/dynamic-string.h"
24 #include "openvswitch/match.h"
25 #include "openvswitch/meta-flow.h"
27 #include "ofp-actions.h"
29 #include "openvswitch/ofpbuf.h"
30 #include "openflow/openflow.h"
31 #include "openvswitch/ofp-errors.h"
32 #include "unaligned.h"
35 /* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid,
36 * otherwise an OFPERR_*. */
38 learn_check(const struct ofpact_learn
*learn
, const struct flow
*flow
)
40 const struct ofpact_learn_spec
*spec
;
43 match_init_catchall(&match
);
44 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
47 /* Check the source. */
48 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
49 error
= mf_check_src(&spec
->src
, flow
);
55 /* Check the destination. */
56 switch (spec
->dst_type
) {
57 case NX_LEARN_DST_MATCH
:
58 error
= mf_check_src(&spec
->dst
, &match
.flow
);
63 mf_write_subfield(&spec
->dst
, &spec
->src_imm
, &match
);
66 case NX_LEARN_DST_LOAD
:
67 error
= mf_check_dst(&spec
->dst
, &match
.flow
);
73 case NX_LEARN_DST_OUTPUT
:
81 /* Composes 'fm' so that executing it will implement 'learn' given that the
82 * packet being processed has 'flow' as its flow.
84 * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize
85 * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the
88 * The caller has to actually execute 'fm'. */
90 learn_execute(const struct ofpact_learn
*learn
, const struct flow
*flow
,
91 struct ofputil_flow_mod
*fm
, struct ofpbuf
*ofpacts
)
93 const struct ofpact_learn_spec
*spec
;
95 match_init_catchall(&fm
->match
);
96 fm
->priority
= learn
->priority
;
97 fm
->cookie
= htonll(0);
98 fm
->cookie_mask
= htonll(0);
99 fm
->new_cookie
= learn
->cookie
;
100 fm
->modify_cookie
= fm
->new_cookie
!= OVS_BE64_MAX
;
101 fm
->table_id
= learn
->table_id
;
102 fm
->command
= OFPFC_MODIFY_STRICT
;
103 fm
->idle_timeout
= learn
->idle_timeout
;
104 fm
->hard_timeout
= learn
->hard_timeout
;
106 fm
->buffer_id
= UINT32_MAX
;
107 fm
->out_port
= OFPP_NONE
;
109 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
110 fm
->flags
|= OFPUTIL_FF_SEND_FLOW_REM
;
114 fm
->delete_reason
= OFPRR_DELETE
;
116 if (learn
->fin_idle_timeout
|| learn
->fin_hard_timeout
) {
117 struct ofpact_fin_timeout
*oft
;
119 oft
= ofpact_put_FIN_TIMEOUT(ofpacts
);
120 oft
->fin_idle_timeout
= learn
->fin_idle_timeout
;
121 oft
->fin_hard_timeout
= learn
->fin_hard_timeout
;
124 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
125 struct ofpact_set_field
*sf
;
126 union mf_subvalue value
;
128 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
129 mf_read_subfield(&spec
->src
, flow
, &value
);
131 value
= spec
->src_imm
;
134 switch (spec
->dst_type
) {
135 case NX_LEARN_DST_MATCH
:
136 mf_write_subfield(&spec
->dst
, &value
, &fm
->match
);
139 case NX_LEARN_DST_LOAD
:
140 sf
= ofpact_put_reg_load(ofpacts
);
141 sf
->field
= spec
->dst
.field
;
142 bitwise_copy(&value
, sizeof value
, 0,
143 &sf
->value
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
145 bitwise_one(&sf
->mask
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
149 case NX_LEARN_DST_OUTPUT
:
150 if (spec
->n_bits
<= 16
151 || is_all_zeros(value
.u8
, sizeof value
- 2)) {
152 ofp_port_t port
= u16_to_ofp(ntohll(value
.integer
));
154 if (ofp_to_u16(port
) < ofp_to_u16(OFPP_MAX
)
155 || port
== OFPP_IN_PORT
156 || port
== OFPP_FLOOD
157 || port
== OFPP_LOCAL
158 || port
== OFPP_ALL
) {
159 ofpact_put_OUTPUT(ofpacts
)->port
= port
;
166 fm
->ofpacts
= ofpacts
->data
;
167 fm
->ofpacts_len
= ofpacts
->size
;
170 /* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
171 * the learn action 'learn'. */
173 learn_mask(const struct ofpact_learn
*learn
, struct flow_wildcards
*wc
)
175 const struct ofpact_learn_spec
*spec
;
176 union mf_subvalue value
;
178 memset(&value
, 0xff, sizeof value
);
179 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
180 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
181 mf_write_subfield_flow(&spec
->src
, &value
, &wc
->masks
);
186 /* Returns NULL if successful, otherwise a malloc()'d string describing the
187 * error. The caller is responsible for freeing the returned string. */
188 static char * OVS_WARN_UNUSED_RESULT
189 learn_parse_load_immediate(const char *s
, struct ofpact_learn_spec
*spec
)
191 const char *full_s
= s
;
192 struct mf_subfield dst
;
193 union mf_subvalue imm
;
197 err
= parse_int_string(s
, imm
.u8
, sizeof imm
.u8
, (char **) &s
);
199 return xasprintf("%s: too many bits in immediate value", full_s
);
202 if (strncmp(s
, "->", 2)) {
203 return xasprintf("%s: missing `->' following value", full_s
);
207 error
= mf_parse_subfield(&dst
, s
);
211 if (!mf_nxm_header(dst
.field
->id
)) {
212 return xasprintf("%s: experimenter OXM field '%s' not supported",
216 if (!bitwise_is_all_zeros(&imm
, sizeof imm
, dst
.n_bits
,
217 (8 * sizeof imm
) - dst
.n_bits
)) {
218 return xasprintf("%s: value does not fit into %u bits",
222 spec
->n_bits
= dst
.n_bits
;
223 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
225 spec
->dst_type
= NX_LEARN_DST_LOAD
;
230 /* Returns NULL if successful, otherwise a malloc()'d string describing the
231 * error. The caller is responsible for freeing the returned string. */
232 static char * OVS_WARN_UNUSED_RESULT
233 learn_parse_spec(const char *orig
, char *name
, char *value
,
234 struct ofpact_learn_spec
*spec
)
236 if (mf_from_name(name
)) {
237 const struct mf_field
*dst
= mf_from_name(name
);
241 error
= mf_parse_value(dst
, value
, &imm
);
246 spec
->n_bits
= dst
->n_bits
;
247 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
248 memset(&spec
->src_imm
, 0, sizeof spec
->src_imm
);
249 memcpy(&spec
->src_imm
.u8
[sizeof spec
->src_imm
- dst
->n_bytes
],
251 spec
->dst_type
= NX_LEARN_DST_MATCH
;
252 spec
->dst
.field
= dst
;
254 spec
->dst
.n_bits
= dst
->n_bits
;
255 } else if (strchr(name
, '[')) {
256 /* Parse destination and check prerequisites. */
259 error
= mf_parse_subfield(&spec
->dst
, name
);
263 if (!mf_nxm_header(spec
->dst
.field
->id
)) {
264 return xasprintf("%s: experimenter OXM field '%s' not supported",
268 /* Parse source and check prerequisites. */
269 if (value
[0] != '\0') {
270 error
= mf_parse_subfield(&spec
->src
, value
);
274 if (spec
->src
.n_bits
!= spec
->dst
.n_bits
) {
275 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
276 "differ", orig
, name
, spec
->src
.n_bits
, value
,
280 spec
->src
= spec
->dst
;
283 spec
->n_bits
= spec
->src
.n_bits
;
284 spec
->src_type
= NX_LEARN_SRC_FIELD
;
285 spec
->dst_type
= NX_LEARN_DST_MATCH
;
286 } else if (!strcmp(name
, "load")) {
287 if (value
[strcspn(value
, "[-")] == '-') {
288 char *error
= learn_parse_load_immediate(value
, spec
);
293 struct ofpact_reg_move move
;
296 error
= nxm_parse_reg_move(&move
, value
);
301 spec
->n_bits
= move
.src
.n_bits
;
302 spec
->src_type
= NX_LEARN_SRC_FIELD
;
303 spec
->src
= move
.src
;
304 spec
->dst_type
= NX_LEARN_DST_LOAD
;
305 spec
->dst
= move
.dst
;
307 } else if (!strcmp(name
, "output")) {
308 char *error
= mf_parse_subfield(&spec
->src
, value
);
313 spec
->n_bits
= spec
->src
.n_bits
;
314 spec
->src_type
= NX_LEARN_SRC_FIELD
;
315 spec
->dst_type
= NX_LEARN_DST_OUTPUT
;
317 return xasprintf("%s: unknown keyword %s", orig
, name
);
323 /* Returns NULL if successful, otherwise a malloc()'d string describing the
324 * error. The caller is responsible for freeing the returned string. */
325 static char * OVS_WARN_UNUSED_RESULT
326 learn_parse__(char *orig
, char *arg
, struct ofpbuf
*ofpacts
)
328 struct ofpact_learn
*learn
;
332 learn
= ofpact_put_LEARN(ofpacts
);
333 learn
->idle_timeout
= OFP_FLOW_PERMANENT
;
334 learn
->hard_timeout
= OFP_FLOW_PERMANENT
;
335 learn
->priority
= OFP_DEFAULT_PRIORITY
;
338 match_init_catchall(&match
);
339 while (ofputil_parse_key_value(&arg
, &name
, &value
)) {
340 if (!strcmp(name
, "table")) {
341 learn
->table_id
= atoi(value
);
342 if (learn
->table_id
== 255) {
343 return xasprintf("%s: table id 255 not valid for `learn' "
346 } else if (!strcmp(name
, "priority")) {
347 learn
->priority
= atoi(value
);
348 } else if (!strcmp(name
, "idle_timeout")) {
349 learn
->idle_timeout
= atoi(value
);
350 } else if (!strcmp(name
, "hard_timeout")) {
351 learn
->hard_timeout
= atoi(value
);
352 } else if (!strcmp(name
, "fin_idle_timeout")) {
353 learn
->fin_idle_timeout
= atoi(value
);
354 } else if (!strcmp(name
, "fin_hard_timeout")) {
355 learn
->fin_hard_timeout
= atoi(value
);
356 } else if (!strcmp(name
, "cookie")) {
357 learn
->cookie
= htonll(strtoull(value
, NULL
, 0));
358 } else if (!strcmp(name
, "send_flow_rem")) {
359 learn
->flags
|= NX_LEARN_F_SEND_FLOW_REM
;
360 } else if (!strcmp(name
, "delete_learned")) {
361 learn
->flags
|= NX_LEARN_F_DELETE_LEARNED
;
363 struct ofpact_learn_spec
*spec
;
366 spec
= ofpbuf_put_zeros(ofpacts
, sizeof *spec
);
367 learn
= ofpacts
->header
;
370 error
= learn_parse_spec(orig
, name
, value
, spec
);
375 /* Update 'match' to allow for satisfying destination
377 if (spec
->src_type
== NX_LEARN_SRC_IMMEDIATE
378 && spec
->dst_type
== NX_LEARN_DST_MATCH
) {
379 mf_write_subfield(&spec
->dst
, &spec
->src_imm
, &match
);
383 ofpact_finish_LEARN(ofpacts
, &learn
);
388 /* Parses 'arg' as a set of arguments to the "learn" action and appends a
389 * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
392 * Returns NULL if successful, otherwise a malloc()'d string describing the
393 * error. The caller is responsible for freeing the returned string.
395 * If 'flow' is nonnull, then it should be the flow from a struct match that is
396 * the matching rule for the learning action. This helps to better validate
397 * the action's arguments.
400 char * OVS_WARN_UNUSED_RESULT
401 learn_parse(char *arg
, struct ofpbuf
*ofpacts
)
403 char *orig
= xstrdup(arg
);
404 char *error
= learn_parse__(orig
, arg
, ofpacts
);
409 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
412 learn_format(const struct ofpact_learn
*learn
, struct ds
*s
)
414 const struct ofpact_learn_spec
*spec
;
417 match_init_catchall(&match
);
419 ds_put_format(s
, "%slearn(%s%stable=%s%"PRIu8
,
420 colors
.learn
, colors
.end
, colors
.special
, colors
.end
,
422 if (learn
->idle_timeout
!= OFP_FLOW_PERMANENT
) {
423 ds_put_format(s
, ",%sidle_timeout=%s%"PRIu16
,
424 colors
.param
, colors
.end
, learn
->idle_timeout
);
426 if (learn
->hard_timeout
!= OFP_FLOW_PERMANENT
) {
427 ds_put_format(s
, ",%shard_timeout=%s%"PRIu16
,
428 colors
.param
, colors
.end
, learn
->hard_timeout
);
430 if (learn
->fin_idle_timeout
) {
431 ds_put_format(s
, ",%sfin_idle_timeout=%s%"PRIu16
,
432 colors
.param
, colors
.end
, learn
->fin_idle_timeout
);
434 if (learn
->fin_hard_timeout
) {
435 ds_put_format(s
, "%s,fin_hard_timeout=%s%"PRIu16
,
436 colors
.param
, colors
.end
, learn
->fin_hard_timeout
);
438 if (learn
->priority
!= OFP_DEFAULT_PRIORITY
) {
439 ds_put_format(s
, "%s,priority=%s%"PRIu16
,
440 colors
.special
, colors
.end
, learn
->priority
);
442 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
443 ds_put_format(s
, ",%ssend_flow_rem%s", colors
.value
, colors
.end
);
445 if (learn
->flags
& NX_LEARN_F_DELETE_LEARNED
) {
446 ds_put_format(s
, ",%sdelete_learned%s", colors
.value
, colors
.end
);
448 if (learn
->cookie
!= 0) {
449 ds_put_format(s
, ",%scookie=%s%#"PRIx64
,
450 colors
.param
, colors
.end
, ntohll(learn
->cookie
));
453 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
456 switch (spec
->src_type
| spec
->dst_type
) {
457 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_MATCH
:
458 if (spec
->dst
.ofs
== 0
459 && spec
->dst
.n_bits
== spec
->dst
.field
->n_bits
) {
460 union mf_value value
;
462 memset(&value
, 0, sizeof value
);
463 bitwise_copy(&spec
->src_imm
, sizeof spec
->src_imm
, 0,
464 &value
, spec
->dst
.field
->n_bytes
, 0,
465 spec
->dst
.field
->n_bits
);
466 ds_put_format(s
, "%s%s=%s", colors
.param
,
467 spec
->dst
.field
->name
, colors
.end
);
468 mf_format(spec
->dst
.field
, &value
, NULL
, s
);
470 ds_put_format(s
, "%s", colors
.param
);
471 mf_format_subfield(&spec
->dst
, s
);
472 ds_put_format(s
, "=%s", colors
.end
);
473 mf_format_subvalue(&spec
->src_imm
, s
);
477 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_MATCH
:
478 ds_put_format(s
, "%s", colors
.param
);
479 mf_format_subfield(&spec
->dst
, s
);
480 ds_put_format(s
, "%s", colors
.end
);
481 if (spec
->src
.field
!= spec
->dst
.field
||
482 spec
->src
.ofs
!= spec
->dst
.ofs
) {
483 ds_put_format(s
, "%s=%s", colors
.param
, colors
.end
);
484 mf_format_subfield(&spec
->src
, s
);
488 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_LOAD
:
489 ds_put_format(s
, "%sload:%s", colors
.special
, colors
.end
);
490 mf_format_subvalue(&spec
->src_imm
, s
);
491 ds_put_format(s
, "%s->%s", colors
.special
, colors
.end
);
492 mf_format_subfield(&spec
->dst
, s
);
495 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_LOAD
:
496 ds_put_format(s
, "%sload:%s", colors
.special
, colors
.end
);
497 mf_format_subfield(&spec
->src
, s
);
498 ds_put_format(s
, "%s->%s", colors
.special
, colors
.end
);
499 mf_format_subfield(&spec
->dst
, s
);
502 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_OUTPUT
:
503 ds_put_format(s
, "%soutput:%s", colors
.special
, colors
.end
);
504 mf_format_subfield(&spec
->src
, s
);
508 ds_put_format(s
, "%s)%s", colors
.learn
, colors
.end
);