]> git.proxmox.com Git - mirror_ovs.git/blame - lib/learn.c
ofp-print: Print bucket ids of OpenFlow 1.5 group messages.
[mirror_ovs.git] / lib / learn.c
CommitLineData
75a75043 1/*
3d6832c2 2 * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
75a75043
BP
3 *
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:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <config.h>
18
19#include "learn.h"
20
21#include "byte-order.h"
22#include "dynamic-string.h"
81a76618 23#include "match.h"
75a75043
BP
24#include "meta-flow.h"
25#include "nx-match.h"
f25d0cf3 26#include "ofp-actions.h"
90bf1e07 27#include "ofp-errors.h"
75a75043
BP
28#include "ofp-util.h"
29#include "ofpbuf.h"
30#include "openflow/openflow.h"
31#include "unaligned.h"
32
75a75043 33
f25d0cf3
BP
34/* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid,
35 * otherwise an OFPERR_*. */
36enum ofperr
37learn_check(const struct ofpact_learn *learn, const struct flow *flow)
75a75043 38{
f25d0cf3 39 const struct ofpact_learn_spec *spec;
81a76618 40 struct match match;
75a75043 41
81a76618 42 match_init_catchall(&match);
f25d0cf3
BP
43 for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
44 enum ofperr error;
337b9cec 45
f25d0cf3
BP
46 /* Check the source. */
47 if (spec->src_type == NX_LEARN_SRC_FIELD) {
48 error = mf_check_src(&spec->src, flow);
49 if (error) {
50 return error;
51 }
75a75043
BP
52 }
53
f25d0cf3
BP
54 /* Check the destination. */
55 switch (spec->dst_type) {
75a75043 56 case NX_LEARN_DST_MATCH:
81a76618 57 error = mf_check_src(&spec->dst, &match.flow);
f25d0cf3
BP
58 if (error) {
59 return error;
60 }
61
81a76618 62 mf_write_subfield(&spec->dst, &spec->src_imm, &match);
75a75043
BP
63 break;
64
65 case NX_LEARN_DST_LOAD:
81a76618 66 error = mf_check_dst(&spec->dst, &match.flow);
f25d0cf3
BP
67 if (error) {
68 return error;
337b9cec 69 }
75a75043
BP
70 break;
71
72 case NX_LEARN_DST_OUTPUT:
f25d0cf3 73 /* Nothing to do. */
75a75043
BP
74 break;
75 }
76 }
f25d0cf3 77 return 0;
75a75043
BP
78}
79
f25d0cf3
BP
80/* Composes 'fm' so that executing it will implement 'learn' given that the
81 * packet being processed has 'flow' as its flow.
82 *
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
85 * 'ofpacts' buffer.
86 *
87 * The caller has to actually execute 'fm'. */
88void
89learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
90 struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
91{
92 const struct ofpact_learn_spec *spec;
75a75043 93
81a76618
BP
94 match_init_catchall(&fm->match);
95 fm->priority = learn->priority;
f25d0cf3
BP
96 fm->cookie = htonll(0);
97 fm->cookie_mask = htonll(0);
80771642 98 fm->new_cookie = learn->cookie;
b8266395 99 fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX;
f25d0cf3
BP
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;
ca26eb44 104 fm->importance = 0;
f25d0cf3
BP
105 fm->buffer_id = UINT32_MAX;
106 fm->out_port = OFPP_NONE;
35f48b8b
BP
107 fm->flags = 0;
108 if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) {
109 fm->flags |= OFPUTIL_FF_SEND_FLOW_REM;
110 }
f25d0cf3
BP
111 fm->ofpacts = NULL;
112 fm->ofpacts_len = 0;
cc40d06b 113 fm->delete_reason = OFPRR_DELETE;
75a75043 114
f25d0cf3
BP
115 if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
116 struct ofpact_fin_timeout *oft;
117
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;
121 }
122
123 for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
7eb4b1f1 124 struct ofpact_set_field *sf;
f25d0cf3 125 union mf_subvalue value;
f25d0cf3
BP
126
127 if (spec->src_type == NX_LEARN_SRC_FIELD) {
128 mf_read_subfield(&spec->src, flow, &value);
129 } else {
130 value = spec->src_imm;
131 }
132
133 switch (spec->dst_type) {
134 case NX_LEARN_DST_MATCH:
81a76618 135 mf_write_subfield(&spec->dst, &value, &fm->match);
f25d0cf3
BP
136 break;
137
138 case NX_LEARN_DST_LOAD:
7eb4b1f1
BP
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,
143 spec->n_bits);
144 bitwise_one(&sf->mask, spec->dst.field->n_bytes, spec->dst.ofs,
145 spec->n_bits);
f25d0cf3
BP
146 break;
147
148 case NX_LEARN_DST_OUTPUT:
149 if (spec->n_bits <= 16
150 || is_all_zeros(value.u8, sizeof value - 2)) {
f98e6e5b
MC
151 ovs_be16 *last_be16 = &value.be16[ARRAY_SIZE(value.be16) - 1];
152 ofp_port_t port = u16_to_ofp(ntohs(*last_be16));
f25d0cf3 153
4e022ec0 154 if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX)
f25d0cf3
BP
155 || port == OFPP_IN_PORT
156 || port == OFPP_FLOOD
157 || port == OFPP_LOCAL
158 || port == OFPP_ALL) {
159 ofpact_put_OUTPUT(ofpacts)->port = port;
160 }
161 }
162 break;
163 }
164 }
165 ofpact_pad(ofpacts);
166
1f317cb5
PS
167 fm->ofpacts = ofpbuf_data(ofpacts);
168 fm->ofpacts_len = ofpbuf_size(ofpacts);
f25d0cf3 169}
75a75043 170
bcd2633a
JP
171/* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
172 * the learn action 'learn'. */
173void
174learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc)
175{
176 const struct ofpact_learn_spec *spec;
177 union mf_subvalue value;
178
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);
183 }
184 }
185}
186
bdda5aca
BP
187/* Returns NULL if successful, otherwise a malloc()'d string describing the
188 * error. The caller is responsible for freeing the returned string. */
189static char * WARN_UNUSED_RESULT
f25d0cf3 190learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
3d792b70
BP
191{
192 const char *full_s = s;
193 const char *arrow = strstr(s, "->");
194 struct mf_subfield dst;
195 union mf_subvalue imm;
bdda5aca 196 char *error;
3d792b70
BP
197
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);
203 int i;
204
205 for (i = 0; i < n; i++) {
206 int hexit = hexit_value(in[-i]);
207 if (hexit < 0) {
bdda5aca 208 return xasprintf("%s: bad hex digit in value", full_s);
3d792b70
BP
209 }
210 out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit;
211 }
212 s = arrow;
213 } else {
f98e6e5b
MC
214 ovs_be64 *last_be64 = &imm.be64[ARRAY_SIZE(imm.be64) - 1];
215 *last_be64 = htonll(strtoull(s, (char **) &s, 0));
3d792b70
BP
216 }
217
218 if (strncmp(s, "->", 2)) {
bdda5aca 219 return xasprintf("%s: missing `->' following value", full_s);
3d792b70
BP
220 }
221 s += 2;
222
bdda5aca
BP
223 error = mf_parse_subfield(&dst, s);
224 if (error) {
225 return error;
3d792b70 226 }
bad8a439
BP
227 if (!mf_nxm_header(dst.field->id)) {
228 return xasprintf("%s: experimenter OXM field '%s' not supported",
229 full_s, s);
230 }
3d792b70
BP
231
232 if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits,
233 (8 * sizeof imm) - dst.n_bits)) {
bdda5aca
BP
234 return xasprintf("%s: value does not fit into %u bits",
235 full_s, dst.n_bits);
3d792b70
BP
236 }
237
238 spec->n_bits = dst.n_bits;
239 spec->src_type = NX_LEARN_SRC_IMMEDIATE;
240 spec->src_imm = imm;
241 spec->dst_type = NX_LEARN_DST_LOAD;
242 spec->dst = dst;
bdda5aca 243 return NULL;
3d792b70
BP
244}
245
bdda5aca
BP
246/* Returns NULL if successful, otherwise a malloc()'d string describing the
247 * error. The caller is responsible for freeing the returned string. */
248static char * WARN_UNUSED_RESULT
75a75043 249learn_parse_spec(const char *orig, char *name, char *value,
f25d0cf3 250 struct ofpact_learn_spec *spec)
75a75043
BP
251{
252 if (mf_from_name(name)) {
253 const struct mf_field *dst = mf_from_name(name);
254 union mf_value imm;
255 char *error;
256
257 error = mf_parse_value(dst, value, &imm);
258 if (error) {
bdda5aca 259 return error;
75a75043
BP
260 }
261
262 spec->n_bits = dst->n_bits;
263 spec->src_type = NX_LEARN_SRC_IMMEDIATE;
3d792b70
BP
264 memset(&spec->src_imm, 0, sizeof spec->src_imm);
265 memcpy(&spec->src_imm.u8[sizeof spec->src_imm - dst->n_bytes],
266 &imm, dst->n_bytes);
75a75043 267 spec->dst_type = NX_LEARN_DST_MATCH;
816fd533
BP
268 spec->dst.field = dst;
269 spec->dst.ofs = 0;
270 spec->dst.n_bits = dst->n_bits;
75a75043 271 } else if (strchr(name, '[')) {
75a75043 272 /* Parse destination and check prerequisites. */
bdda5aca
BP
273 char *error;
274
275 error = mf_parse_subfield(&spec->dst, name);
276 if (error) {
277 return error;
75a75043 278 }
bad8a439
BP
279 if (!mf_nxm_header(spec->dst.field->id)) {
280 return xasprintf("%s: experimenter OXM field '%s' not supported",
281 orig, name);
282 }
75a75043
BP
283
284 /* Parse source and check prerequisites. */
285 if (value[0] != '\0') {
bdda5aca
BP
286 error = mf_parse_subfield(&spec->src, value);
287 if (error) {
288 return error;
75a75043 289 }
816fd533 290 if (spec->src.n_bits != spec->dst.n_bits) {
bdda5aca
BP
291 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
292 "differ", orig, name, spec->src.n_bits, value,
293 spec->dst.n_bits);
75a75043
BP
294 }
295 } else {
816fd533 296 spec->src = spec->dst;
75a75043
BP
297 }
298
816fd533 299 spec->n_bits = spec->src.n_bits;
75a75043 300 spec->src_type = NX_LEARN_SRC_FIELD;
75a75043 301 spec->dst_type = NX_LEARN_DST_MATCH;
75a75043
BP
302 } else if (!strcmp(name, "load")) {
303 if (value[strcspn(value, "[-")] == '-') {
bdda5aca
BP
304 char *error = learn_parse_load_immediate(value, spec);
305 if (error) {
306 return error;
307 }
75a75043 308 } else {
f25d0cf3 309 struct ofpact_reg_move move;
bdda5aca 310 char *error;
75a75043 311
bdda5aca
BP
312 error = nxm_parse_reg_move(&move, value);
313 if (error) {
314 return error;
315 }
75a75043 316
f25d0cf3 317 spec->n_bits = move.src.n_bits;
75a75043 318 spec->src_type = NX_LEARN_SRC_FIELD;
f25d0cf3 319 spec->src = move.src;
75a75043 320 spec->dst_type = NX_LEARN_DST_LOAD;
f25d0cf3 321 spec->dst = move.dst;
75a75043
BP
322 }
323 } else if (!strcmp(name, "output")) {
bdda5aca
BP
324 char *error = mf_parse_subfield(&spec->src, value);
325 if (error) {
326 return error;
75a75043
BP
327 }
328
816fd533 329 spec->n_bits = spec->src.n_bits;
75a75043 330 spec->src_type = NX_LEARN_SRC_FIELD;
75a75043 331 spec->dst_type = NX_LEARN_DST_OUTPUT;
75a75043 332 } else {
bdda5aca 333 return xasprintf("%s: unknown keyword %s", orig, name);
75a75043 334 }
bdda5aca
BP
335
336 return NULL;
75a75043
BP
337}
338
bdda5aca
BP
339/* Returns NULL if successful, otherwise a malloc()'d string describing the
340 * error. The caller is responsible for freeing the returned string. */
341static char * WARN_UNUSED_RESULT
342learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts)
75a75043 343{
f25d0cf3 344 struct ofpact_learn *learn;
81a76618 345 struct match match;
bdda5aca 346 char *name, *value;
75a75043 347
f25d0cf3
BP
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;
75a75043
BP
352 learn->table_id = 1;
353
81a76618 354 match_init_catchall(&match);
75a75043 355 while (ofputil_parse_key_value(&arg, &name, &value)) {
75a75043
BP
356 if (!strcmp(name, "table")) {
357 learn->table_id = atoi(value);
358 if (learn->table_id == 255) {
bdda5aca
BP
359 return xasprintf("%s: table id 255 not valid for `learn' "
360 "action", orig);
75a75043
BP
361 }
362 } else if (!strcmp(name, "priority")) {
f25d0cf3 363 learn->priority = atoi(value);
75a75043 364 } else if (!strcmp(name, "idle_timeout")) {
f25d0cf3 365 learn->idle_timeout = atoi(value);
75a75043 366 } else if (!strcmp(name, "hard_timeout")) {
f25d0cf3 367 learn->hard_timeout = atoi(value);
0e553d9c 368 } else if (!strcmp(name, "fin_idle_timeout")) {
f25d0cf3 369 learn->fin_idle_timeout = atoi(value);
0e553d9c 370 } else if (!strcmp(name, "fin_hard_timeout")) {
f25d0cf3 371 learn->fin_hard_timeout = atoi(value);
75a75043 372 } else if (!strcmp(name, "cookie")) {
80771642 373 learn->cookie = htonll(strtoull(value, NULL, 0));
3d6832c2 374 } else if (!strcmp(name, "send_flow_rem")) {
35f48b8b
BP
375 learn->flags |= NX_LEARN_F_SEND_FLOW_REM;
376 } else if (!strcmp(name, "delete_learned")) {
377 learn->flags |= NX_LEARN_F_DELETE_LEARNED;
75a75043 378 } else {
f25d0cf3 379 struct ofpact_learn_spec *spec;
bdda5aca 380 char *error;
f25d0cf3
BP
381
382 spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
cf3b7538 383 learn = ofpacts->frame;
f25d0cf3 384 learn->n_specs++;
75a75043 385
bdda5aca
BP
386 error = learn_parse_spec(orig, name, value, spec);
387 if (error) {
388 return error;
389 }
75a75043 390
81a76618 391 /* Update 'match' to allow for satisfying destination
75a75043 392 * prerequisites. */
f25d0cf3
BP
393 if (spec->src_type == NX_LEARN_SRC_IMMEDIATE
394 && spec->dst_type == NX_LEARN_DST_MATCH) {
81a76618 395 mf_write_subfield(&spec->dst, &spec->src_imm, &match);
75a75043
BP
396 }
397 }
398 }
f25d0cf3 399 ofpact_update_len(ofpacts, &learn->ofpact);
4cb3fde7 400
bdda5aca
BP
401 return NULL;
402}
403
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
406 * format parsed.
407 *
408 * Returns NULL if successful, otherwise a malloc()'d string describing the
409 * error. The caller is responsible for freeing the returned string.
410 *
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.
414 *
415 * Modifies 'arg'. */
416char * WARN_UNUSED_RESULT
417learn_parse(char *arg, struct ofpbuf *ofpacts)
418{
419 char *orig = xstrdup(arg);
420 char *error = learn_parse__(orig, arg, ofpacts);
4cb3fde7 421 free(orig);
bdda5aca 422 return error;
75a75043
BP
423}
424
f25d0cf3
BP
425/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
426 * describes. */
75a75043 427void
f25d0cf3 428learn_format(const struct ofpact_learn *learn, struct ds *s)
75a75043 429{
f25d0cf3 430 const struct ofpact_learn_spec *spec;
81a76618 431 struct match match;
75a75043 432
81a76618 433 match_init_catchall(&match);
75a75043
BP
434
435 ds_put_format(s, "learn(table=%"PRIu8, learn->table_id);
f25d0cf3
BP
436 if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
437 ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout);
75a75043 438 }
f25d0cf3
BP
439 if (learn->hard_timeout != OFP_FLOW_PERMANENT) {
440 ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout);
75a75043 441 }
0e553d9c 442 if (learn->fin_idle_timeout) {
f25d0cf3 443 ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout);
0e553d9c
BP
444 }
445 if (learn->fin_hard_timeout) {
f25d0cf3 446 ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout);
0e553d9c 447 }
f25d0cf3
BP
448 if (learn->priority != OFP_DEFAULT_PRIORITY) {
449 ds_put_format(s, ",priority=%"PRIu16, learn->priority);
75a75043 450 }
35f48b8b 451 if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) {
3d6832c2 452 ds_put_cstr(s, ",send_flow_rem");
75a75043 453 }
35f48b8b
BP
454 if (learn->flags & NX_LEARN_F_DELETE_LEARNED) {
455 ds_put_cstr(s, ",delete_learned");
456 }
f25d0cf3 457 if (learn->cookie != 0) {
80771642 458 ds_put_format(s, ",cookie=%#"PRIx64, ntohll(learn->cookie));
75a75043 459 }
75a75043 460
f25d0cf3 461 for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
75a75043
BP
462 ds_put_char(s, ',');
463
f25d0cf3 464 switch (spec->src_type | spec->dst_type) {
75a75043 465 case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH:
f25d0cf3
BP
466 if (spec->dst.ofs == 0
467 && spec->dst.n_bits == spec->dst.field->n_bits) {
75a75043 468 union mf_value value;
f299fbeb 469
75a75043 470 memset(&value, 0, sizeof value);
f25d0cf3
BP
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);
75a75043 476 } else {
f25d0cf3
BP
477 mf_format_subfield(&spec->dst, s);
478 ds_put_char(s, '=');
9bab681f 479 mf_format_subvalue(&spec->src_imm, s);
75a75043
BP
480 }
481 break;
482
483 case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH:
f25d0cf3
BP
484 mf_format_subfield(&spec->dst, s);
485 if (spec->src.field != spec->dst.field ||
486 spec->src.ofs != spec->dst.ofs) {
75a75043 487 ds_put_char(s, '=');
f25d0cf3 488 mf_format_subfield(&spec->src, s);
75a75043
BP
489 }
490 break;
491
492 case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD:
f25d0cf3 493 ds_put_format(s, "load:");
9bab681f 494 mf_format_subvalue(&spec->src_imm, s);
75a75043 495 ds_put_cstr(s, "->");
f25d0cf3 496 mf_format_subfield(&spec->dst, s);
75a75043
BP
497 break;
498
499 case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD:
500 ds_put_cstr(s, "load:");
f25d0cf3 501 mf_format_subfield(&spec->src, s);
75a75043 502 ds_put_cstr(s, "->");
f25d0cf3 503 mf_format_subfield(&spec->dst, s);
75a75043
BP
504 break;
505
506 case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT:
507 ds_put_cstr(s, "output:");
f25d0cf3 508 mf_format_subfield(&spec->src, s);
75a75043
BP
509 break;
510 }
511 }
75a75043
BP
512 ds_put_char(s, ')');
513}