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
;
104 fm
->buffer_id
= UINT32_MAX
;
105 fm
->out_port
= OFPP_NONE
;
107 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
108 fm
->flags
|= OFPUTIL_FF_SEND_FLOW_REM
;
112 fm
->delete_reason
= OFPRR_DELETE
;
114 if (learn
->fin_idle_timeout
|| learn
->fin_hard_timeout
) {
115 struct ofpact_fin_timeout
*oft
;
117 oft
= ofpact_put_FIN_TIMEOUT(ofpacts
);
118 oft
->fin_idle_timeout
= learn
->fin_idle_timeout
;
119 oft
->fin_hard_timeout
= learn
->fin_hard_timeout
;
122 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
123 struct ofpact_set_field
*sf
;
124 union mf_subvalue value
;
126 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
127 mf_read_subfield(&spec
->src
, flow
, &value
);
129 value
= spec
->src_imm
;
132 switch (spec
->dst_type
) {
133 case NX_LEARN_DST_MATCH
:
134 mf_write_subfield(&spec
->dst
, &value
, &fm
->match
);
137 case NX_LEARN_DST_LOAD
:
138 sf
= ofpact_put_reg_load(ofpacts
);
139 sf
->field
= spec
->dst
.field
;
140 bitwise_copy(&value
, sizeof value
, 0,
141 &sf
->value
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
143 bitwise_one(&sf
->mask
, spec
->dst
.field
->n_bytes
, spec
->dst
.ofs
,
147 case NX_LEARN_DST_OUTPUT
:
148 if (spec
->n_bits
<= 16
149 || is_all_zeros(value
.u8
, sizeof value
- 2)) {
150 ofp_port_t port
= u16_to_ofp(ntohs(value
.be16
[7]));
152 if (ofp_to_u16(port
) < ofp_to_u16(OFPP_MAX
)
153 || port
== OFPP_IN_PORT
154 || port
== OFPP_FLOOD
155 || port
== OFPP_LOCAL
156 || port
== OFPP_ALL
) {
157 ofpact_put_OUTPUT(ofpacts
)->port
= port
;
165 fm
->ofpacts
= ofpbuf_data(ofpacts
);
166 fm
->ofpacts_len
= ofpbuf_size(ofpacts
);
169 /* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
170 * the learn action 'learn'. */
172 learn_mask(const struct ofpact_learn
*learn
, struct flow_wildcards
*wc
)
174 const struct ofpact_learn_spec
*spec
;
175 union mf_subvalue value
;
177 memset(&value
, 0xff, sizeof value
);
178 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
179 if (spec
->src_type
== NX_LEARN_SRC_FIELD
) {
180 mf_write_subfield_flow(&spec
->src
, &value
, &wc
->masks
);
185 /* Returns NULL if successful, otherwise a malloc()'d string describing the
186 * error. The caller is responsible for freeing the returned string. */
187 static char * WARN_UNUSED_RESULT
188 learn_parse_load_immediate(const char *s
, struct ofpact_learn_spec
*spec
)
190 const char *full_s
= s
;
191 const char *arrow
= strstr(s
, "->");
192 struct mf_subfield dst
;
193 union mf_subvalue imm
;
196 memset(&imm
, 0, sizeof imm
);
197 if (s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X') && arrow
) {
198 const char *in
= arrow
- 1;
199 uint8_t *out
= imm
.u8
+ sizeof imm
.u8
- 1;
200 int n
= arrow
- (s
+ 2);
203 for (i
= 0; i
< n
; i
++) {
204 int hexit
= hexit_value(in
[-i
]);
206 return xasprintf("%s: bad hex digit in value", full_s
);
208 out
[-(i
/ 2)] |= i
% 2 ? hexit
<< 4 : hexit
;
212 imm
.be64
[1] = htonll(strtoull(s
, (char **) &s
, 0));
215 if (strncmp(s
, "->", 2)) {
216 return xasprintf("%s: missing `->' following value", full_s
);
220 error
= mf_parse_subfield(&dst
, s
);
224 if (!mf_nxm_header(dst
.field
->id
)) {
225 return xasprintf("%s: experimenter OXM field '%s' not supported",
229 if (!bitwise_is_all_zeros(&imm
, sizeof imm
, dst
.n_bits
,
230 (8 * sizeof imm
) - dst
.n_bits
)) {
231 return xasprintf("%s: value does not fit into %u bits",
235 spec
->n_bits
= dst
.n_bits
;
236 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
238 spec
->dst_type
= NX_LEARN_DST_LOAD
;
243 /* Returns NULL if successful, otherwise a malloc()'d string describing the
244 * error. The caller is responsible for freeing the returned string. */
245 static char * WARN_UNUSED_RESULT
246 learn_parse_spec(const char *orig
, char *name
, char *value
,
247 struct ofpact_learn_spec
*spec
)
249 if (mf_from_name(name
)) {
250 const struct mf_field
*dst
= mf_from_name(name
);
254 error
= mf_parse_value(dst
, value
, &imm
);
259 spec
->n_bits
= dst
->n_bits
;
260 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
261 memset(&spec
->src_imm
, 0, sizeof spec
->src_imm
);
262 memcpy(&spec
->src_imm
.u8
[sizeof spec
->src_imm
- dst
->n_bytes
],
264 spec
->dst_type
= NX_LEARN_DST_MATCH
;
265 spec
->dst
.field
= dst
;
267 spec
->dst
.n_bits
= dst
->n_bits
;
268 } else if (strchr(name
, '[')) {
269 /* Parse destination and check prerequisites. */
272 error
= mf_parse_subfield(&spec
->dst
, name
);
276 if (!mf_nxm_header(spec
->dst
.field
->id
)) {
277 return xasprintf("%s: experimenter OXM field '%s' not supported",
281 /* Parse source and check prerequisites. */
282 if (value
[0] != '\0') {
283 error
= mf_parse_subfield(&spec
->src
, value
);
287 if (spec
->src
.n_bits
!= spec
->dst
.n_bits
) {
288 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
289 "differ", orig
, name
, spec
->src
.n_bits
, value
,
293 spec
->src
= spec
->dst
;
296 spec
->n_bits
= spec
->src
.n_bits
;
297 spec
->src_type
= NX_LEARN_SRC_FIELD
;
298 spec
->dst_type
= NX_LEARN_DST_MATCH
;
299 } else if (!strcmp(name
, "load")) {
300 if (value
[strcspn(value
, "[-")] == '-') {
301 char *error
= learn_parse_load_immediate(value
, spec
);
306 struct ofpact_reg_move move
;
309 error
= nxm_parse_reg_move(&move
, value
);
314 spec
->n_bits
= move
.src
.n_bits
;
315 spec
->src_type
= NX_LEARN_SRC_FIELD
;
316 spec
->src
= move
.src
;
317 spec
->dst_type
= NX_LEARN_DST_LOAD
;
318 spec
->dst
= move
.dst
;
320 } else if (!strcmp(name
, "output")) {
321 char *error
= mf_parse_subfield(&spec
->src
, value
);
326 spec
->n_bits
= spec
->src
.n_bits
;
327 spec
->src_type
= NX_LEARN_SRC_FIELD
;
328 spec
->dst_type
= NX_LEARN_DST_OUTPUT
;
330 return xasprintf("%s: unknown keyword %s", orig
, name
);
336 /* Returns NULL if successful, otherwise a malloc()'d string describing the
337 * error. The caller is responsible for freeing the returned string. */
338 static char * WARN_UNUSED_RESULT
339 learn_parse__(char *orig
, char *arg
, struct ofpbuf
*ofpacts
)
341 struct ofpact_learn
*learn
;
345 learn
= ofpact_put_LEARN(ofpacts
);
346 learn
->idle_timeout
= OFP_FLOW_PERMANENT
;
347 learn
->hard_timeout
= OFP_FLOW_PERMANENT
;
348 learn
->priority
= OFP_DEFAULT_PRIORITY
;
351 match_init_catchall(&match
);
352 while (ofputil_parse_key_value(&arg
, &name
, &value
)) {
353 if (!strcmp(name
, "table")) {
354 learn
->table_id
= atoi(value
);
355 if (learn
->table_id
== 255) {
356 return xasprintf("%s: table id 255 not valid for `learn' "
359 } else if (!strcmp(name
, "priority")) {
360 learn
->priority
= atoi(value
);
361 } else if (!strcmp(name
, "idle_timeout")) {
362 learn
->idle_timeout
= atoi(value
);
363 } else if (!strcmp(name
, "hard_timeout")) {
364 learn
->hard_timeout
= atoi(value
);
365 } else if (!strcmp(name
, "fin_idle_timeout")) {
366 learn
->fin_idle_timeout
= atoi(value
);
367 } else if (!strcmp(name
, "fin_hard_timeout")) {
368 learn
->fin_hard_timeout
= atoi(value
);
369 } else if (!strcmp(name
, "cookie")) {
370 learn
->cookie
= htonll(strtoull(value
, NULL
, 0));
371 } else if (!strcmp(name
, "send_flow_rem")) {
372 learn
->flags
|= NX_LEARN_F_SEND_FLOW_REM
;
373 } else if (!strcmp(name
, "delete_learned")) {
374 learn
->flags
|= NX_LEARN_F_DELETE_LEARNED
;
376 struct ofpact_learn_spec
*spec
;
379 spec
= ofpbuf_put_zeros(ofpacts
, sizeof *spec
);
380 learn
= ofpacts
->frame
;
383 error
= learn_parse_spec(orig
, name
, value
, spec
);
388 /* Update 'match' to allow for satisfying destination
390 if (spec
->src_type
== NX_LEARN_SRC_IMMEDIATE
391 && spec
->dst_type
== NX_LEARN_DST_MATCH
) {
392 mf_write_subfield(&spec
->dst
, &spec
->src_imm
, &match
);
396 ofpact_update_len(ofpacts
, &learn
->ofpact
);
401 /* Parses 'arg' as a set of arguments to the "learn" action and appends a
402 * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
405 * Returns NULL if successful, otherwise a malloc()'d string describing the
406 * error. The caller is responsible for freeing the returned string.
408 * If 'flow' is nonnull, then it should be the flow from a struct match that is
409 * the matching rule for the learning action. This helps to better validate
410 * the action's arguments.
413 char * WARN_UNUSED_RESULT
414 learn_parse(char *arg
, struct ofpbuf
*ofpacts
)
416 char *orig
= xstrdup(arg
);
417 char *error
= learn_parse__(orig
, arg
, ofpacts
);
422 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
425 learn_format(const struct ofpact_learn
*learn
, struct ds
*s
)
427 const struct ofpact_learn_spec
*spec
;
430 match_init_catchall(&match
);
432 ds_put_format(s
, "learn(table=%"PRIu8
, learn
->table_id
);
433 if (learn
->idle_timeout
!= OFP_FLOW_PERMANENT
) {
434 ds_put_format(s
, ",idle_timeout=%"PRIu16
, learn
->idle_timeout
);
436 if (learn
->hard_timeout
!= OFP_FLOW_PERMANENT
) {
437 ds_put_format(s
, ",hard_timeout=%"PRIu16
, learn
->hard_timeout
);
439 if (learn
->fin_idle_timeout
) {
440 ds_put_format(s
, ",fin_idle_timeout=%"PRIu16
, learn
->fin_idle_timeout
);
442 if (learn
->fin_hard_timeout
) {
443 ds_put_format(s
, ",fin_hard_timeout=%"PRIu16
, learn
->fin_hard_timeout
);
445 if (learn
->priority
!= OFP_DEFAULT_PRIORITY
) {
446 ds_put_format(s
, ",priority=%"PRIu16
, learn
->priority
);
448 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
449 ds_put_cstr(s
, ",send_flow_rem");
451 if (learn
->flags
& NX_LEARN_F_DELETE_LEARNED
) {
452 ds_put_cstr(s
, ",delete_learned");
454 if (learn
->cookie
!= 0) {
455 ds_put_format(s
, ",cookie=%#"PRIx64
, ntohll(learn
->cookie
));
458 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
461 switch (spec
->src_type
| spec
->dst_type
) {
462 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_MATCH
:
463 if (spec
->dst
.ofs
== 0
464 && spec
->dst
.n_bits
== spec
->dst
.field
->n_bits
) {
465 union mf_value value
;
467 memset(&value
, 0, sizeof value
);
468 bitwise_copy(&spec
->src_imm
, sizeof spec
->src_imm
, 0,
469 &value
, spec
->dst
.field
->n_bytes
, 0,
470 spec
->dst
.field
->n_bits
);
471 ds_put_format(s
, "%s=", spec
->dst
.field
->name
);
472 mf_format(spec
->dst
.field
, &value
, NULL
, s
);
474 mf_format_subfield(&spec
->dst
, s
);
476 mf_format_subvalue(&spec
->src_imm
, s
);
480 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_MATCH
:
481 mf_format_subfield(&spec
->dst
, s
);
482 if (spec
->src
.field
!= spec
->dst
.field
||
483 spec
->src
.ofs
!= spec
->dst
.ofs
) {
485 mf_format_subfield(&spec
->src
, s
);
489 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_LOAD
:
490 ds_put_format(s
, "load:");
491 mf_format_subvalue(&spec
->src_imm
, s
);
492 ds_put_cstr(s
, "->");
493 mf_format_subfield(&spec
->dst
, s
);
496 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_LOAD
:
497 ds_put_cstr(s
, "load:");
498 mf_format_subfield(&spec
->src
, s
);
499 ds_put_cstr(s
, "->");
500 mf_format_subfield(&spec
->dst
, s
);
503 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_OUTPUT
:
504 ds_put_cstr(s
, "output:");
505 mf_format_subfield(&spec
->src
, s
);