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
= ofpbuf_data(ofpacts
);
168 fm
->ofpacts_len
= ofpbuf_size(ofpacts
);
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 * WARN_UNUSED_RESULT
190 learn_parse_load_immediate(const char *s
, struct ofpact_learn_spec
*spec
)
192 const char *full_s
= s
;
193 const char *arrow
= strstr(s
, "->");
194 struct mf_subfield dst
;
195 union mf_subvalue imm
;
198 memset(&imm
, 0, sizeof imm
);
199 if (s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X') && arrow
) {
200 const char *in
= arrow
- 1;
201 uint8_t *out
= imm
.u8
+ sizeof imm
.u8
- 1;
202 int n
= arrow
- (s
+ 2);
205 for (i
= 0; i
< n
; i
++) {
206 int hexit
= hexit_value(in
[-i
]);
208 return xasprintf("%s: bad hex digit in value", full_s
);
210 out
[-(i
/ 2)] |= i
% 2 ? hexit
<< 4 : hexit
;
214 ovs_be64
*last_be64
= &imm
.be64
[ARRAY_SIZE(imm
.be64
) - 1];
215 *last_be64
= htonll(strtoull(s
, (char **) &s
, 0));
218 if (strncmp(s
, "->", 2)) {
219 return xasprintf("%s: missing `->' following value", full_s
);
223 error
= mf_parse_subfield(&dst
, s
);
227 if (!mf_nxm_header(dst
.field
->id
)) {
228 return xasprintf("%s: experimenter OXM field '%s' not supported",
232 if (!bitwise_is_all_zeros(&imm
, sizeof imm
, dst
.n_bits
,
233 (8 * sizeof imm
) - dst
.n_bits
)) {
234 return xasprintf("%s: value does not fit into %u bits",
238 spec
->n_bits
= dst
.n_bits
;
239 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
241 spec
->dst_type
= NX_LEARN_DST_LOAD
;
246 /* Returns NULL if successful, otherwise a malloc()'d string describing the
247 * error. The caller is responsible for freeing the returned string. */
248 static char * WARN_UNUSED_RESULT
249 learn_parse_spec(const char *orig
, char *name
, char *value
,
250 struct ofpact_learn_spec
*spec
)
252 if (mf_from_name(name
)) {
253 const struct mf_field
*dst
= mf_from_name(name
);
257 error
= mf_parse_value(dst
, value
, &imm
);
262 spec
->n_bits
= dst
->n_bits
;
263 spec
->src_type
= NX_LEARN_SRC_IMMEDIATE
;
264 memset(&spec
->src_imm
, 0, sizeof spec
->src_imm
);
265 memcpy(&spec
->src_imm
.u8
[sizeof spec
->src_imm
- dst
->n_bytes
],
267 spec
->dst_type
= NX_LEARN_DST_MATCH
;
268 spec
->dst
.field
= dst
;
270 spec
->dst
.n_bits
= dst
->n_bits
;
271 } else if (strchr(name
, '[')) {
272 /* Parse destination and check prerequisites. */
275 error
= mf_parse_subfield(&spec
->dst
, name
);
279 if (!mf_nxm_header(spec
->dst
.field
->id
)) {
280 return xasprintf("%s: experimenter OXM field '%s' not supported",
284 /* Parse source and check prerequisites. */
285 if (value
[0] != '\0') {
286 error
= mf_parse_subfield(&spec
->src
, value
);
290 if (spec
->src
.n_bits
!= spec
->dst
.n_bits
) {
291 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
292 "differ", orig
, name
, spec
->src
.n_bits
, value
,
296 spec
->src
= spec
->dst
;
299 spec
->n_bits
= spec
->src
.n_bits
;
300 spec
->src_type
= NX_LEARN_SRC_FIELD
;
301 spec
->dst_type
= NX_LEARN_DST_MATCH
;
302 } else if (!strcmp(name
, "load")) {
303 if (value
[strcspn(value
, "[-")] == '-') {
304 char *error
= learn_parse_load_immediate(value
, spec
);
309 struct ofpact_reg_move move
;
312 error
= nxm_parse_reg_move(&move
, value
);
317 spec
->n_bits
= move
.src
.n_bits
;
318 spec
->src_type
= NX_LEARN_SRC_FIELD
;
319 spec
->src
= move
.src
;
320 spec
->dst_type
= NX_LEARN_DST_LOAD
;
321 spec
->dst
= move
.dst
;
323 } else if (!strcmp(name
, "output")) {
324 char *error
= mf_parse_subfield(&spec
->src
, value
);
329 spec
->n_bits
= spec
->src
.n_bits
;
330 spec
->src_type
= NX_LEARN_SRC_FIELD
;
331 spec
->dst_type
= NX_LEARN_DST_OUTPUT
;
333 return xasprintf("%s: unknown keyword %s", orig
, name
);
339 /* Returns NULL if successful, otherwise a malloc()'d string describing the
340 * error. The caller is responsible for freeing the returned string. */
341 static char * WARN_UNUSED_RESULT
342 learn_parse__(char *orig
, char *arg
, struct ofpbuf
*ofpacts
)
344 struct ofpact_learn
*learn
;
348 learn
= ofpact_put_LEARN(ofpacts
);
349 learn
->idle_timeout
= OFP_FLOW_PERMANENT
;
350 learn
->hard_timeout
= OFP_FLOW_PERMANENT
;
351 learn
->priority
= OFP_DEFAULT_PRIORITY
;
354 match_init_catchall(&match
);
355 while (ofputil_parse_key_value(&arg
, &name
, &value
)) {
356 if (!strcmp(name
, "table")) {
357 learn
->table_id
= atoi(value
);
358 if (learn
->table_id
== 255) {
359 return xasprintf("%s: table id 255 not valid for `learn' "
362 } else if (!strcmp(name
, "priority")) {
363 learn
->priority
= atoi(value
);
364 } else if (!strcmp(name
, "idle_timeout")) {
365 learn
->idle_timeout
= atoi(value
);
366 } else if (!strcmp(name
, "hard_timeout")) {
367 learn
->hard_timeout
= atoi(value
);
368 } else if (!strcmp(name
, "fin_idle_timeout")) {
369 learn
->fin_idle_timeout
= atoi(value
);
370 } else if (!strcmp(name
, "fin_hard_timeout")) {
371 learn
->fin_hard_timeout
= atoi(value
);
372 } else if (!strcmp(name
, "cookie")) {
373 learn
->cookie
= htonll(strtoull(value
, NULL
, 0));
374 } else if (!strcmp(name
, "send_flow_rem")) {
375 learn
->flags
|= NX_LEARN_F_SEND_FLOW_REM
;
376 } else if (!strcmp(name
, "delete_learned")) {
377 learn
->flags
|= NX_LEARN_F_DELETE_LEARNED
;
379 struct ofpact_learn_spec
*spec
;
382 spec
= ofpbuf_put_zeros(ofpacts
, sizeof *spec
);
383 learn
= ofpacts
->frame
;
386 error
= learn_parse_spec(orig
, name
, value
, spec
);
391 /* Update 'match' to allow for satisfying destination
393 if (spec
->src_type
== NX_LEARN_SRC_IMMEDIATE
394 && spec
->dst_type
== NX_LEARN_DST_MATCH
) {
395 mf_write_subfield(&spec
->dst
, &spec
->src_imm
, &match
);
399 ofpact_update_len(ofpacts
, &learn
->ofpact
);
404 /* Parses 'arg' as a set of arguments to the "learn" action and appends a
405 * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
408 * Returns NULL if successful, otherwise a malloc()'d string describing the
409 * error. The caller is responsible for freeing the returned string.
411 * If 'flow' is nonnull, then it should be the flow from a struct match that is
412 * the matching rule for the learning action. This helps to better validate
413 * the action's arguments.
416 char * WARN_UNUSED_RESULT
417 learn_parse(char *arg
, struct ofpbuf
*ofpacts
)
419 char *orig
= xstrdup(arg
);
420 char *error
= learn_parse__(orig
, arg
, ofpacts
);
425 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
428 learn_format(const struct ofpact_learn
*learn
, struct ds
*s
)
430 const struct ofpact_learn_spec
*spec
;
433 match_init_catchall(&match
);
435 ds_put_format(s
, "learn(table=%"PRIu8
, learn
->table_id
);
436 if (learn
->idle_timeout
!= OFP_FLOW_PERMANENT
) {
437 ds_put_format(s
, ",idle_timeout=%"PRIu16
, learn
->idle_timeout
);
439 if (learn
->hard_timeout
!= OFP_FLOW_PERMANENT
) {
440 ds_put_format(s
, ",hard_timeout=%"PRIu16
, learn
->hard_timeout
);
442 if (learn
->fin_idle_timeout
) {
443 ds_put_format(s
, ",fin_idle_timeout=%"PRIu16
, learn
->fin_idle_timeout
);
445 if (learn
->fin_hard_timeout
) {
446 ds_put_format(s
, ",fin_hard_timeout=%"PRIu16
, learn
->fin_hard_timeout
);
448 if (learn
->priority
!= OFP_DEFAULT_PRIORITY
) {
449 ds_put_format(s
, ",priority=%"PRIu16
, learn
->priority
);
451 if (learn
->flags
& NX_LEARN_F_SEND_FLOW_REM
) {
452 ds_put_cstr(s
, ",send_flow_rem");
454 if (learn
->flags
& NX_LEARN_F_DELETE_LEARNED
) {
455 ds_put_cstr(s
, ",delete_learned");
457 if (learn
->cookie
!= 0) {
458 ds_put_format(s
, ",cookie=%#"PRIx64
, ntohll(learn
->cookie
));
461 for (spec
= learn
->specs
; spec
< &learn
->specs
[learn
->n_specs
]; spec
++) {
464 switch (spec
->src_type
| spec
->dst_type
) {
465 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_MATCH
:
466 if (spec
->dst
.ofs
== 0
467 && spec
->dst
.n_bits
== spec
->dst
.field
->n_bits
) {
468 union mf_value value
;
470 memset(&value
, 0, sizeof value
);
471 bitwise_copy(&spec
->src_imm
, sizeof spec
->src_imm
, 0,
472 &value
, spec
->dst
.field
->n_bytes
, 0,
473 spec
->dst
.field
->n_bits
);
474 ds_put_format(s
, "%s=", spec
->dst
.field
->name
);
475 mf_format(spec
->dst
.field
, &value
, NULL
, s
);
477 mf_format_subfield(&spec
->dst
, s
);
479 mf_format_subvalue(&spec
->src_imm
, s
);
483 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_MATCH
:
484 mf_format_subfield(&spec
->dst
, s
);
485 if (spec
->src
.field
!= spec
->dst
.field
||
486 spec
->src
.ofs
!= spec
->dst
.ofs
) {
488 mf_format_subfield(&spec
->src
, s
);
492 case NX_LEARN_SRC_IMMEDIATE
| NX_LEARN_DST_LOAD
:
493 ds_put_format(s
, "load:");
494 mf_format_subvalue(&spec
->src_imm
, s
);
495 ds_put_cstr(s
, "->");
496 mf_format_subfield(&spec
->dst
, s
);
499 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_LOAD
:
500 ds_put_cstr(s
, "load:");
501 mf_format_subfield(&spec
->src
, s
);
502 ds_put_cstr(s
, "->");
503 mf_format_subfield(&spec
->dst
, s
);
506 case NX_LEARN_SRC_FIELD
| NX_LEARN_DST_OUTPUT
:
507 ds_put_cstr(s
, "output:");
508 mf_format_subfield(&spec
->src
, s
);