2 * Copyright (c) 2011, 2012, 2013, 2014 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"
22 #include "dynamic-string.h"
24 #include "meta-flow.h"
26 #include "ofp-actions.h"
27 #include "ofp-errors.h"
30 #include "openflow/openflow.h"
31 #include "unaligned.h"
34 /* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid,
35 * otherwise an OFPERR_*. */
37 learn_check(const struct ofpact_learn
*learn
, const struct flow
*flow
)
39 const struct ofpact_learn_spec
*spec
;
42 match_init_catchall(&match
);
43 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
46 /* Check the source. */
47 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
48 error
= mf_check_src(&spec
->src
, flow
);
54 /* Check the destination. */
55 switch (spec
->dst_type
) {
56 case NX_LEARN_DST_MATCH
:
57 error
= mf_check_src(&spec
->dst
, &match
.flow
);
62 mf_write_subfield(&spec
->dst
, &spec
->src_imm
, &match
);
65 case NX_LEARN_DST_LOAD
:
66 error
= mf_check_dst(&spec
->dst
, &match
.flow
);
72 case NX_LEARN_DST_OUTPUT
:
80 /* Composes 'fm' so that executing it will implement 'learn' given that the
81 * packet being processed has 'flow' as its flow.
83 * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize
84 * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the
87 * The caller has to actually execute 'fm'. */
89 learn_execute(const struct ofpact_learn
*learn
, const struct flow
*flow
,
90 struct ofputil_flow_mod
*fm
, struct ofpbuf
*ofpacts
)
92 const struct ofpact_learn_spec
*spec
;
94 match_init_catchall(&fm
->match
);
95 fm
->priority
= learn
->priority
;
96 fm
->cookie
= htonll(0);
97 fm
->cookie_mask
= htonll(0);
98 fm
->new_cookie
= learn
->cookie
;
99 fm
->modify_cookie
= fm
->new_cookie
!= OVS_BE64_MAX
;
100 fm
->table_id
= learn
->table_id
;
101 fm
->command
= OFPFC_MODIFY_STRICT
;
102 fm
->idle_timeout
= learn
->idle_timeout
;
103 fm
->hard_timeout
= learn
->hard_timeout
;
105 fm
->buffer_id
= UINT32_MAX
;
106 fm
->out_port
= OFPP_NONE
;
108 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
109 fm
->flags
|= OFPUTIL_FF_SEND_FLOW_REM
;
113 fm
->delete_reason
= OFPRR_DELETE
;
115 if (learn
->fin_idle_timeout
|| learn
->fin_hard_timeout
) {
116 struct ofpact_fin_timeout
*oft
;
118 oft
= ofpact_put_FIN_TIMEOUT(ofpacts
);
119 oft
->fin_idle_timeout
= learn
->fin_idle_timeout
;
120 oft
->fin_hard_timeout
= learn
->fin_hard_timeout
;
123 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
124 struct ofpact_set_field
*sf
;
125 union mf_subvalue value
;
127 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
128 mf_read_subfield(&spec
->src
, flow
, &value
);
130 value
= spec
->src_imm
;
133 switch (spec
->dst_type
) {
134 case NX_LEARN_DST_MATCH
:
135 mf_write_subfield(&spec
->dst
, &value
, &fm
->match
);
138 case NX_LEARN_DST_LOAD
:
139 sf
= ofpact_put_reg_load(ofpacts
);
140 sf
->field
= spec
->dst
.field
;
141 bitwise_copy(&value
, sizeof value
, 0,
142 &sf
->value
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
144 bitwise_one(&sf
->mask
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
148 case NX_LEARN_DST_OUTPUT
:
149 if (spec
->n_bits
<= 16
150 || is_all_zeros(value
.u8
, sizeof value
- 2)) {
151 ovs_be16
*last_be16
= &value
.be16
[ARRAY_SIZE(value
.be16
) - 1];
152 ofp_port_t port
= u16_to_ofp(ntohs(*last_be16
));
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
;
167 fm
->ofpacts
= ofpacts
->data
;
168 fm
->ofpacts_len
= ofpacts
->size
;
171 /* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
172 * the learn action 'learn'. */
174 learn_mask(const struct ofpact_learn
*learn
, struct flow_wildcards
*wc
)
176 const struct ofpact_learn_spec
*spec
;
177 union mf_subvalue value
;
179 memset(&value
, 0xff, sizeof value
);
180 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
181 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
182 mf_write_subfield_flow(&spec
->src
, &value
, &wc
->masks
);
187 /* Returns NULL if successful, otherwise a malloc()'d string describing the
188 * error. The caller is responsible for freeing the returned string. */
189 static char * OVS_WARN_UNUSED_RESULT
190 learn_parse_load_immediate(const char *s
, struct ofpact_learn_spec
*spec
)
192 const char *full_s
= s
;
193 struct mf_subfield dst
;
194 union mf_subvalue imm
;
198 err
= parse_int_string(s
, imm
.u8
, sizeof imm
.u8
, (char **) &s
);
200 return xasprintf("%s: bad hex digit in value", full_s
);
203 if (strncmp(s
, "->", 2)) {
204 return xasprintf("%s: missing `->' following value", full_s
);
208 error
= mf_parse_subfield(&dst
, s
);
212 if (!mf_nxm_header(dst
.field
->id
)) {
213 return xasprintf("%s: experimenter OXM field '%s' not supported",
217 if (!bitwise_is_all_zeros(&imm
, sizeof imm
, dst
.n_bits
,
218 (8 * sizeof imm
) - dst
.n_bits
)) {
219 return xasprintf("%s: value does not fit into %u bits",
223 spec
->n_bits
= dst
.n_bits
;
224 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
226 spec
->dst_type
= NX_LEARN_DST_LOAD
;
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
)
237 if (mf_from_name(name
)) {
238 const struct mf_field
*dst
= mf_from_name(name
);
242 error
= mf_parse_value(dst
, value
, &imm
);
247 spec
->n_bits
= dst
->n_bits
;
248 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
249 memset(&spec
->src_imm
, 0, sizeof spec
->src_imm
);
250 memcpy(&spec
->src_imm
.u8
[sizeof spec
->src_imm
- dst
->n_bytes
],
252 spec
->dst_type
= NX_LEARN_DST_MATCH
;
253 spec
->dst
.field
= dst
;
255 spec
->dst
.n_bits
= dst
->n_bits
;
256 } else if (strchr(name
, '[')) {
257 /* Parse destination and check prerequisites. */
260 error
= mf_parse_subfield(&spec
->dst
, name
);
264 if (!mf_nxm_header(spec
->dst
.field
->id
)) {
265 return xasprintf("%s: experimenter OXM field '%s' not supported",
269 /* Parse source and check prerequisites. */
270 if (value
[0] != '\0') {
271 error
= mf_parse_subfield(&spec
->src
, value
);
275 if (spec
->src
.n_bits
!= spec
->dst
.n_bits
) {
276 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
277 "differ", orig
, name
, spec
->src
.n_bits
, value
,
281 spec
->src
= spec
->dst
;
284 spec
->n_bits
= spec
->src
.n_bits
;
285 spec
->src_type
= NX_LEARN_SRC_FIELD
;
286 spec
->dst_type
= NX_LEARN_DST_MATCH
;
287 } else if (!strcmp(name
, "load")) {
288 if (value
[strcspn(value
, "[-")] == '-') {
289 char *error
= learn_parse_load_immediate(value
, spec
);
294 struct ofpact_reg_move move
;
297 error
= nxm_parse_reg_move(&move
, value
);
302 spec
->n_bits
= move
.src
.n_bits
;
303 spec
->src_type
= NX_LEARN_SRC_FIELD
;
304 spec
->src
= move
.src
;
305 spec
->dst_type
= NX_LEARN_DST_LOAD
;
306 spec
->dst
= move
.dst
;
308 } else if (!strcmp(name
, "output")) {
309 char *error
= mf_parse_subfield(&spec
->src
, value
);
314 spec
->n_bits
= spec
->src
.n_bits
;
315 spec
->src_type
= NX_LEARN_SRC_FIELD
;
316 spec
->dst_type
= NX_LEARN_DST_OUTPUT
;
318 return xasprintf("%s: unknown keyword %s", orig
, name
);
324 /* Returns NULL if successful, otherwise a malloc()'d string describing the
325 * error. The caller is responsible for freeing the returned string. */
326 static char * OVS_WARN_UNUSED_RESULT
327 learn_parse__(char *orig
, char *arg
, struct ofpbuf
*ofpacts
)
329 struct ofpact_learn
*learn
;
333 learn
= ofpact_put_LEARN(ofpacts
);
334 learn
->idle_timeout
= OFP_FLOW_PERMANENT
;
335 learn
->hard_timeout
= OFP_FLOW_PERMANENT
;
336 learn
->priority
= OFP_DEFAULT_PRIORITY
;
339 match_init_catchall(&match
);
340 while (ofputil_parse_key_value(&arg
, &name
, &value
)) {
341 if (!strcmp(name
, "table")) {
342 learn
->table_id
= atoi(value
);
343 if (learn
->table_id
== 255) {
344 return xasprintf("%s: table id 255 not valid for `learn' "
347 } else if (!strcmp(name
, "priority")) {
348 learn
->priority
= atoi(value
);
349 } else if (!strcmp(name
, "idle_timeout")) {
350 learn
->idle_timeout
= atoi(value
);
351 } else if (!strcmp(name
, "hard_timeout")) {
352 learn
->hard_timeout
= atoi(value
);
353 } else if (!strcmp(name
, "fin_idle_timeout")) {
354 learn
->fin_idle_timeout
= atoi(value
);
355 } else if (!strcmp(name
, "fin_hard_timeout")) {
356 learn
->fin_hard_timeout
= atoi(value
);
357 } else if (!strcmp(name
, "cookie")) {
358 learn
->cookie
= htonll(strtoull(value
, NULL
, 0));
359 } else if (!strcmp(name
, "send_flow_rem")) {
360 learn
->flags
|= NX_LEARN_F_SEND_FLOW_REM
;
361 } else if (!strcmp(name
, "delete_learned")) {
362 learn
->flags
|= NX_LEARN_F_DELETE_LEARNED
;
364 struct ofpact_learn_spec
*spec
;
367 spec
= ofpbuf_put_zeros(ofpacts
, sizeof *spec
);
368 learn
= ofpacts
->header
;
371 error
= learn_parse_spec(orig
, name
, value
, spec
);
376 /* Update 'match' to allow for satisfying destination
378 if (spec
->src_type
== NX_LEARN_SRC_IMMEDIATE
379 && spec
->dst_type
== NX_LEARN_DST_MATCH
) {
380 mf_write_subfield(&spec
->dst
, &spec
->src_imm
, &match
);
384 ofpact_update_len(ofpacts
, &learn
->ofpact
);
389 /* Parses 'arg' as a set of arguments to the "learn" action and appends a
390 * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
393 * Returns NULL if successful, otherwise a malloc()'d string describing the
394 * error. The caller is responsible for freeing the returned string.
396 * If 'flow' is nonnull, then it should be the flow from a struct match that is
397 * the matching rule for the learning action. This helps to better validate
398 * the action's arguments.
401 char * OVS_WARN_UNUSED_RESULT
402 learn_parse(char *arg
, struct ofpbuf
*ofpacts
)
404 char *orig
= xstrdup(arg
);
405 char *error
= learn_parse__(orig
, arg
, ofpacts
);
410 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
413 learn_format(const struct ofpact_learn
*learn
, struct ds
*s
)
415 const struct ofpact_learn_spec
*spec
;
418 match_init_catchall(&match
);
420 ds_put_format(s
, "learn(table=%"PRIu8
, learn
->table_id
);
421 if (learn
->idle_timeout
!= OFP_FLOW_PERMANENT
) {
422 ds_put_format(s
, ",idle_timeout=%"PRIu16
, learn
->idle_timeout
);
424 if (learn
->hard_timeout
!= OFP_FLOW_PERMANENT
) {
425 ds_put_format(s
, ",hard_timeout=%"PRIu16
, learn
->hard_timeout
);
427 if (learn
->fin_idle_timeout
) {
428 ds_put_format(s
, ",fin_idle_timeout=%"PRIu16
, learn
->fin_idle_timeout
);
430 if (learn
->fin_hard_timeout
) {
431 ds_put_format(s
, ",fin_hard_timeout=%"PRIu16
, learn
->fin_hard_timeout
);
433 if (learn
->priority
!= OFP_DEFAULT_PRIORITY
) {
434 ds_put_format(s
, ",priority=%"PRIu16
, learn
->priority
);
436 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
437 ds_put_cstr(s
, ",send_flow_rem");
439 if (learn
->flags
& NX_LEARN_F_DELETE_LEARNED
) {
440 ds_put_cstr(s
, ",delete_learned");
442 if (learn
->cookie
!= 0) {
443 ds_put_format(s
, ",cookie=%#"PRIx64
, ntohll(learn
->cookie
));
446 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
449 switch (spec
->src_type
| spec
->dst_type
) {
450 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_MATCH
:
451 if (spec
->dst
.ofs
== 0
452 && spec
->dst
.n_bits
== spec
->dst
.field
->n_bits
) {
453 union mf_value value
;
455 memset(&value
, 0, sizeof value
);
456 bitwise_copy(&spec
->src_imm
, sizeof spec
->src_imm
, 0,
457 &value
, spec
->dst
.field
->n_bytes
, 0,
458 spec
->dst
.field
->n_bits
);
459 ds_put_format(s
, "%s=", spec
->dst
.field
->name
);
460 mf_format(spec
->dst
.field
, &value
, NULL
, s
);
462 mf_format_subfield(&spec
->dst
, s
);
464 mf_format_subvalue(&spec
->src_imm
, s
);
468 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_MATCH
:
469 mf_format_subfield(&spec
->dst
, s
);
470 if (spec
->src
.field
!= spec
->dst
.field
||
471 spec
->src
.ofs
!= spec
->dst
.ofs
) {
473 mf_format_subfield(&spec
->src
, s
);
477 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_LOAD
:
478 ds_put_format(s
, "load:");
479 mf_format_subvalue(&spec
->src_imm
, s
);
480 ds_put_cstr(s
, "->");
481 mf_format_subfield(&spec
->dst
, s
);
484 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_LOAD
:
485 ds_put_cstr(s
, "load:");
486 mf_format_subfield(&spec
->src
, s
);
487 ds_put_cstr(s
, "->");
488 mf_format_subfield(&spec
->dst
, s
);
491 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_OUTPUT
:
492 ds_put_cstr(s
, "output:");
493 mf_format_subfield(&spec
->src
, s
);