]>
Commit | Line | Data |
---|---|---|
75a75043 | 1 | /* |
816fd533 | 2 | * Copyright (c) 2011, 2012 Nicira Networks. |
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" | |
23 | #include "meta-flow.h" | |
24 | #include "nx-match.h" | |
90bf1e07 | 25 | #include "ofp-errors.h" |
75a75043 BP |
26 | #include "ofp-util.h" |
27 | #include "ofpbuf.h" | |
28 | #include "openflow/openflow.h" | |
29 | #include "unaligned.h" | |
30 | ||
31 | static ovs_be16 | |
32 | get_be16(const void **pp) | |
33 | { | |
34 | const ovs_be16 *p = *pp; | |
35 | ovs_be16 value = *p; | |
36 | *pp = p + 1; | |
37 | return value; | |
38 | } | |
39 | ||
40 | static ovs_be32 | |
41 | get_be32(const void **pp) | |
42 | { | |
43 | const ovs_be32 *p = *pp; | |
44 | ovs_be32 value = get_unaligned_be32(p); | |
45 | *pp = p + 1; | |
46 | return value; | |
47 | } | |
48 | ||
49 | static uint64_t | |
50 | get_bits(int n_bits, const void **p) | |
51 | { | |
52 | int n_segs = DIV_ROUND_UP(n_bits, 16); | |
53 | uint64_t value; | |
54 | ||
55 | value = 0; | |
56 | while (n_segs-- > 0) { | |
57 | value = (value << 16) | ntohs(get_be16(p)); | |
58 | } | |
59 | return value; | |
60 | } | |
61 | ||
816fd533 BP |
62 | static void |
63 | get_subfield(int n_bits, const void **p, struct mf_subfield *sf) | |
64 | { | |
65 | sf->field = mf_from_nxm_header(ntohl(get_be32(p))); | |
66 | sf->ofs = ntohs(get_be16(p)); | |
67 | sf->n_bits = n_bits; | |
68 | } | |
69 | ||
75a75043 BP |
70 | static unsigned int |
71 | learn_min_len(uint16_t header) | |
72 | { | |
73 | int n_bits = header & NX_LEARN_N_BITS_MASK; | |
74 | int src_type = header & NX_LEARN_SRC_MASK; | |
75 | int dst_type = header & NX_LEARN_DST_MASK; | |
76 | unsigned int min_len; | |
77 | ||
78 | min_len = 0; | |
79 | if (src_type == NX_LEARN_SRC_FIELD) { | |
80 | min_len += sizeof(ovs_be32); /* src_field */ | |
81 | min_len += sizeof(ovs_be16); /* src_ofs */ | |
82 | } else { | |
83 | min_len += DIV_ROUND_UP(n_bits, 16); | |
84 | } | |
85 | if (dst_type == NX_LEARN_DST_MATCH || | |
86 | dst_type == NX_LEARN_DST_LOAD) { | |
87 | min_len += sizeof(ovs_be32); /* dst_field */ | |
88 | min_len += sizeof(ovs_be16); /* dst_ofs */ | |
89 | } | |
90 | return min_len; | |
91 | } | |
92 | ||
90bf1e07 | 93 | static enum ofperr |
75a75043 BP |
94 | learn_check_header(uint16_t header, size_t len) |
95 | { | |
96 | int src_type = header & NX_LEARN_SRC_MASK; | |
97 | int dst_type = header & NX_LEARN_DST_MASK; | |
98 | ||
99 | /* Check for valid src and dst type combination. */ | |
100 | if (dst_type == NX_LEARN_DST_MATCH || | |
101 | dst_type == NX_LEARN_DST_LOAD || | |
102 | (dst_type == NX_LEARN_DST_OUTPUT && | |
103 | src_type == NX_LEARN_SRC_FIELD)) { | |
104 | /* OK. */ | |
105 | } else { | |
90bf1e07 | 106 | return OFPERR_OFPBAC_BAD_ARGUMENT; |
75a75043 BP |
107 | } |
108 | ||
109 | /* Check that the arguments don't overrun the end of the action. */ | |
110 | if (len < learn_min_len(header)) { | |
90bf1e07 | 111 | return OFPERR_OFPBAC_BAD_LEN; |
75a75043 BP |
112 | } |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | /* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a | |
118 | * valid action on 'flow'. */ | |
90bf1e07 | 119 | enum ofperr |
75a75043 BP |
120 | learn_check(const struct nx_action_learn *learn, const struct flow *flow) |
121 | { | |
122 | struct cls_rule rule; | |
123 | const void *p, *end; | |
124 | ||
125 | cls_rule_init_catchall(&rule, 0); | |
126 | ||
127 | if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM) | |
0e553d9c | 128 | || learn->pad |
75a75043 | 129 | || learn->table_id == 0xff) { |
90bf1e07 | 130 | return OFPERR_OFPBAC_BAD_ARGUMENT; |
75a75043 BP |
131 | } |
132 | ||
133 | end = (char *) learn + ntohs(learn->len); | |
134 | for (p = learn + 1; p != end; ) { | |
135 | uint16_t header = ntohs(get_be16(&p)); | |
136 | int n_bits = header & NX_LEARN_N_BITS_MASK; | |
137 | int src_type = header & NX_LEARN_SRC_MASK; | |
138 | int dst_type = header & NX_LEARN_DST_MASK; | |
139 | ||
90bf1e07 | 140 | enum ofperr error; |
75a75043 | 141 | uint64_t value; |
75a75043 BP |
142 | |
143 | if (!header) { | |
144 | break; | |
145 | } | |
146 | ||
147 | error = learn_check_header(header, (char *) end - (char *) p); | |
148 | if (error) { | |
149 | return error; | |
150 | } | |
151 | ||
152 | /* Check the source. */ | |
153 | if (src_type == NX_LEARN_SRC_FIELD) { | |
816fd533 | 154 | struct mf_subfield src; |
75a75043 | 155 | |
816fd533 BP |
156 | get_subfield(n_bits, &p, &src); |
157 | error = mf_check_src(&src, flow); | |
75a75043 BP |
158 | if (error) { |
159 | return error; | |
160 | } | |
161 | value = 0; | |
162 | } else { | |
163 | value = get_bits(n_bits, &p); | |
164 | } | |
165 | ||
166 | /* Check the destination. */ | |
167 | if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { | |
816fd533 | 168 | struct mf_subfield dst; |
75a75043 | 169 | |
816fd533 | 170 | get_subfield(n_bits, &p, &dst); |
db0a4375 | 171 | error = (dst_type == NX_LEARN_DST_LOAD |
816fd533 BP |
172 | ? mf_check_dst(&dst, &rule.flow) |
173 | : mf_check_src(&dst, &rule.flow)); | |
75a75043 BP |
174 | if (error) { |
175 | return error; | |
176 | } | |
177 | ||
178 | if (dst_type == NX_LEARN_DST_MATCH | |
179 | && src_type == NX_LEARN_SRC_IMMEDIATE) { | |
816fd533 | 180 | mf_set_subfield(&dst, value, &rule); |
75a75043 BP |
181 | } |
182 | } | |
183 | } | |
184 | if (!is_all_zeros(p, (char *) end - (char *) p)) { | |
90bf1e07 | 185 | return OFPERR_OFPBAC_BAD_ARGUMENT; |
75a75043 BP |
186 | } |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | void | |
192 | learn_execute(const struct nx_action_learn *learn, const struct flow *flow, | |
193 | struct ofputil_flow_mod *fm) | |
194 | { | |
195 | const void *p, *end; | |
196 | struct ofpbuf actions; | |
197 | ||
198 | cls_rule_init_catchall(&fm->cr, ntohs(learn->priority)); | |
199 | fm->cookie = learn->cookie; | |
200 | fm->table_id = learn->table_id; | |
201 | fm->command = OFPFC_MODIFY_STRICT; | |
202 | fm->idle_timeout = ntohs(learn->idle_timeout); | |
203 | fm->hard_timeout = ntohs(learn->hard_timeout); | |
204 | fm->buffer_id = UINT32_MAX; | |
205 | fm->out_port = OFPP_NONE; | |
206 | fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM; | |
207 | fm->actions = NULL; | |
208 | fm->n_actions = 0; | |
209 | ||
210 | ofpbuf_init(&actions, 64); | |
211 | ||
0e553d9c BP |
212 | if (learn->fin_idle_timeout || learn->fin_hard_timeout) { |
213 | struct nx_action_fin_timeout *naft; | |
214 | ||
215 | naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions); | |
216 | naft->fin_idle_timeout = learn->fin_idle_timeout; | |
217 | naft->fin_hard_timeout = learn->fin_hard_timeout; | |
218 | } | |
219 | ||
75a75043 BP |
220 | for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) { |
221 | uint16_t header = ntohs(get_be16(&p)); | |
222 | int n_bits = header & NX_LEARN_N_BITS_MASK; | |
223 | int src_type = header & NX_LEARN_SRC_MASK; | |
224 | int dst_type = header & NX_LEARN_DST_MASK; | |
225 | uint64_t value; | |
226 | ||
227 | struct nx_action_reg_load *load; | |
816fd533 | 228 | struct mf_subfield dst; |
75a75043 BP |
229 | |
230 | if (!header) { | |
231 | break; | |
232 | } | |
233 | ||
234 | if (src_type == NX_LEARN_SRC_FIELD) { | |
816fd533 | 235 | struct mf_subfield src; |
75a75043 | 236 | |
816fd533 BP |
237 | get_subfield(n_bits, &p, &src); |
238 | value = mf_get_subfield(&src, flow); | |
75a75043 BP |
239 | } else { |
240 | value = get_bits(n_bits, &p); | |
241 | } | |
242 | ||
243 | switch (dst_type) { | |
244 | case NX_LEARN_DST_MATCH: | |
816fd533 BP |
245 | get_subfield(n_bits, &p, &dst); |
246 | mf_set_subfield(&dst, value, &fm->cr); | |
75a75043 BP |
247 | break; |
248 | ||
249 | case NX_LEARN_DST_LOAD: | |
816fd533 | 250 | get_subfield(n_bits, &p, &dst); |
75a75043 | 251 | load = ofputil_put_NXAST_REG_LOAD(&actions); |
816fd533 BP |
252 | load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); |
253 | load->dst = htonl(dst.field->nxm_header); | |
75a75043 BP |
254 | load->value = htonll(value); |
255 | break; | |
256 | ||
257 | case NX_LEARN_DST_OUTPUT: | |
258 | ofputil_put_OFPAT_OUTPUT(&actions)->port = htons(value); | |
259 | break; | |
260 | } | |
261 | } | |
262 | ||
263 | fm->actions = ofpbuf_steal_data(&actions); | |
264 | fm->n_actions = actions.size / sizeof(struct ofp_action_header); | |
265 | } | |
266 | ||
267 | static void | |
268 | put_be16(struct ofpbuf *b, ovs_be16 x) | |
269 | { | |
270 | ofpbuf_put(b, &x, sizeof x); | |
271 | } | |
272 | ||
273 | static void | |
274 | put_be32(struct ofpbuf *b, ovs_be32 x) | |
275 | { | |
276 | ofpbuf_put(b, &x, sizeof x); | |
277 | } | |
278 | ||
279 | static void | |
280 | put_u16(struct ofpbuf *b, uint16_t x) | |
281 | { | |
282 | put_be16(b, htons(x)); | |
283 | } | |
284 | ||
285 | static void | |
286 | put_u32(struct ofpbuf *b, uint32_t x) | |
287 | { | |
288 | put_be32(b, htonl(x)); | |
289 | } | |
290 | ||
291 | struct learn_spec { | |
292 | int n_bits; | |
293 | ||
294 | int src_type; | |
816fd533 | 295 | struct mf_subfield src; |
75a75043 BP |
296 | uint8_t src_imm[sizeof(union mf_value)]; |
297 | ||
298 | int dst_type; | |
816fd533 | 299 | struct mf_subfield dst; |
75a75043 BP |
300 | }; |
301 | ||
302 | static void | |
303 | learn_parse_spec(const char *orig, char *name, char *value, | |
304 | struct learn_spec *spec) | |
305 | { | |
816fd533 | 306 | memset(spec, 0, sizeof *spec); |
75a75043 BP |
307 | if (mf_from_name(name)) { |
308 | const struct mf_field *dst = mf_from_name(name); | |
309 | union mf_value imm; | |
310 | char *error; | |
311 | ||
312 | error = mf_parse_value(dst, value, &imm); | |
313 | if (error) { | |
314 | ovs_fatal(0, "%s", error); | |
315 | } | |
316 | ||
317 | spec->n_bits = dst->n_bits; | |
318 | spec->src_type = NX_LEARN_SRC_IMMEDIATE; | |
75a75043 BP |
319 | memcpy(spec->src_imm, &imm, dst->n_bytes); |
320 | spec->dst_type = NX_LEARN_DST_MATCH; | |
816fd533 BP |
321 | spec->dst.field = dst; |
322 | spec->dst.ofs = 0; | |
323 | spec->dst.n_bits = dst->n_bits; | |
75a75043 | 324 | } else if (strchr(name, '[')) { |
75a75043 | 325 | /* Parse destination and check prerequisites. */ |
816fd533 | 326 | if (mf_parse_subfield(&spec->dst, name)[0] != '\0') { |
75a75043 BP |
327 | ovs_fatal(0, "%s: syntax error after NXM field name `%s'", |
328 | orig, name); | |
329 | } | |
330 | ||
331 | /* Parse source and check prerequisites. */ | |
332 | if (value[0] != '\0') { | |
816fd533 | 333 | if (mf_parse_subfield(&spec->src, value)[0] != '\0') { |
75a75043 BP |
334 | ovs_fatal(0, "%s: syntax error after NXM field name `%s'", |
335 | orig, value); | |
336 | } | |
816fd533 BP |
337 | if (spec->src.n_bits != spec->dst.n_bits) { |
338 | ovs_fatal(0, "%s: bit widths of %s (%u) and %s (%u) differ", | |
339 | orig, name, spec->src.n_bits, value, | |
340 | spec->dst.n_bits); | |
75a75043 BP |
341 | } |
342 | } else { | |
816fd533 | 343 | spec->src = spec->dst; |
75a75043 BP |
344 | } |
345 | ||
816fd533 | 346 | spec->n_bits = spec->src.n_bits; |
75a75043 | 347 | spec->src_type = NX_LEARN_SRC_FIELD; |
75a75043 | 348 | spec->dst_type = NX_LEARN_DST_MATCH; |
75a75043 BP |
349 | } else if (!strcmp(name, "load")) { |
350 | if (value[strcspn(value, "[-")] == '-') { | |
351 | struct nx_action_reg_load load; | |
352 | int nbits, imm_bytes; | |
353 | uint64_t imm; | |
354 | int i; | |
355 | ||
356 | nxm_parse_reg_load(&load, value); | |
357 | nbits = nxm_decode_n_bits(load.ofs_nbits); | |
358 | imm_bytes = DIV_ROUND_UP(nbits, 8); | |
359 | imm = ntohll(load.value); | |
360 | ||
361 | spec->n_bits = nbits; | |
362 | spec->src_type = NX_LEARN_SRC_IMMEDIATE; | |
75a75043 BP |
363 | for (i = 0; i < imm_bytes; i++) { |
364 | spec->src_imm[i] = imm >> ((imm_bytes - i - 1) * 8); | |
365 | } | |
366 | spec->dst_type = NX_LEARN_DST_LOAD; | |
816fd533 | 367 | nxm_decode(&spec->dst, load.dst, load.ofs_nbits); |
75a75043 BP |
368 | } else { |
369 | struct nx_action_reg_move move; | |
370 | ||
371 | nxm_parse_reg_move(&move, value); | |
372 | ||
373 | spec->n_bits = ntohs(move.n_bits); | |
374 | spec->src_type = NX_LEARN_SRC_FIELD; | |
816fd533 BP |
375 | nxm_decode_discrete(&spec->src, |
376 | move.src, move.src_ofs, move.n_bits); | |
75a75043 | 377 | spec->dst_type = NX_LEARN_DST_LOAD; |
816fd533 BP |
378 | nxm_decode_discrete(&spec->dst, |
379 | move.dst, move.dst_ofs, move.n_bits); | |
75a75043 BP |
380 | } |
381 | } else if (!strcmp(name, "output")) { | |
816fd533 | 382 | if (mf_parse_subfield(&spec->src, value)[0] != '\0') { |
75a75043 BP |
383 | ovs_fatal(0, "%s: syntax error after NXM field name `%s'", |
384 | orig, name); | |
385 | } | |
386 | ||
816fd533 | 387 | spec->n_bits = spec->src.n_bits; |
75a75043 | 388 | spec->src_type = NX_LEARN_SRC_FIELD; |
75a75043 | 389 | spec->dst_type = NX_LEARN_DST_OUTPUT; |
75a75043 BP |
390 | } else { |
391 | ovs_fatal(0, "%s: unknown keyword %s", orig, name); | |
392 | } | |
393 | } | |
394 | ||
0c3d5fc8 BP |
395 | /* Parses 'arg' as a set of arguments to the "learn" action and appends a |
396 | * matching NXAST_LEARN action to 'b'. The format parsed is described in | |
397 | * ovs-ofctl(8). | |
398 | * | |
399 | * Prints an error on stderr and aborts the program if 'arg' syntax is invalid. | |
400 | * | |
401 | * If 'flow' is nonnull, then it should be the flow from a cls_rule that is | |
402 | * the matching rule for the learning action. This helps to better validate | |
403 | * the action's arguments. | |
404 | * | |
405 | * Modifies 'arg'. */ | |
75a75043 BP |
406 | void |
407 | learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow) | |
408 | { | |
409 | char *orig = xstrdup(arg); | |
410 | char *name, *value; | |
90bf1e07 | 411 | enum ofperr error; |
75a75043 BP |
412 | size_t learn_ofs; |
413 | size_t len; | |
414 | ||
415 | struct nx_action_learn *learn; | |
416 | struct cls_rule rule; | |
417 | ||
418 | learn_ofs = b->size; | |
419 | learn = ofputil_put_NXAST_LEARN(b); | |
420 | learn->idle_timeout = htons(OFP_FLOW_PERMANENT); | |
421 | learn->hard_timeout = htons(OFP_FLOW_PERMANENT); | |
422 | learn->priority = htons(OFP_DEFAULT_PRIORITY); | |
423 | learn->cookie = htonll(0); | |
424 | learn->flags = htons(0); | |
425 | learn->table_id = 1; | |
426 | ||
427 | cls_rule_init_catchall(&rule, 0); | |
428 | while (ofputil_parse_key_value(&arg, &name, &value)) { | |
429 | learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); | |
430 | if (!strcmp(name, "table")) { | |
431 | learn->table_id = atoi(value); | |
432 | if (learn->table_id == 255) { | |
433 | ovs_fatal(0, "%s: table id 255 not valid for `learn' action", | |
434 | orig); | |
435 | } | |
436 | } else if (!strcmp(name, "priority")) { | |
437 | learn->priority = htons(atoi(value)); | |
438 | } else if (!strcmp(name, "idle_timeout")) { | |
439 | learn->idle_timeout = htons(atoi(value)); | |
440 | } else if (!strcmp(name, "hard_timeout")) { | |
441 | learn->hard_timeout = htons(atoi(value)); | |
0e553d9c BP |
442 | } else if (!strcmp(name, "fin_idle_timeout")) { |
443 | learn->fin_idle_timeout = htons(atoi(value)); | |
444 | } else if (!strcmp(name, "fin_hard_timeout")) { | |
445 | learn->fin_hard_timeout = htons(atoi(value)); | |
75a75043 BP |
446 | } else if (!strcmp(name, "cookie")) { |
447 | learn->cookie = htonll(strtoull(value, NULL, 0)); | |
448 | } else { | |
449 | struct learn_spec spec; | |
450 | ||
451 | learn_parse_spec(orig, name, value, &spec); | |
452 | ||
453 | /* Check prerequisites. */ | |
454 | if (spec.src_type == NX_LEARN_SRC_FIELD | |
0c3d5fc8 | 455 | && flow && !mf_are_prereqs_ok(spec.src.field, flow)) { |
75a75043 BP |
456 | ovs_fatal(0, "%s: cannot specify source field %s because " |
457 | "prerequisites are not satisfied", | |
816fd533 | 458 | orig, spec.src.field->name); |
75a75043 BP |
459 | } |
460 | if ((spec.dst_type == NX_LEARN_DST_MATCH | |
461 | || spec.dst_type == NX_LEARN_DST_LOAD) | |
816fd533 | 462 | && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) { |
75a75043 BP |
463 | ovs_fatal(0, "%s: cannot specify destination field %s because " |
464 | "prerequisites are not satisfied", | |
816fd533 | 465 | orig, spec.dst.field->name); |
75a75043 BP |
466 | } |
467 | ||
468 | /* Update 'rule' to allow for satisfying destination | |
469 | * prerequisites. */ | |
470 | if (spec.src_type == NX_LEARN_SRC_IMMEDIATE | |
471 | && spec.dst_type == NX_LEARN_DST_MATCH | |
816fd533 BP |
472 | && spec.dst.ofs == 0 |
473 | && spec.n_bits == spec.dst.field->n_bytes * 8) { | |
75a75043 BP |
474 | union mf_value imm; |
475 | ||
816fd533 BP |
476 | memcpy(&imm, spec.src_imm, spec.dst.field->n_bytes); |
477 | mf_set_value(spec.dst.field, &imm, &rule); | |
75a75043 BP |
478 | } |
479 | ||
480 | /* Output the flow_mod_spec. */ | |
481 | put_u16(b, spec.n_bits | spec.src_type | spec.dst_type); | |
482 | if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) { | |
483 | int n_bytes = DIV_ROUND_UP(spec.n_bits, 8); | |
484 | if (n_bytes % 2) { | |
485 | ofpbuf_put_zeros(b, 1); | |
486 | } | |
487 | ofpbuf_put(b, spec.src_imm, n_bytes); | |
488 | } else { | |
816fd533 BP |
489 | put_u32(b, spec.src.field->nxm_header); |
490 | put_u16(b, spec.src.ofs); | |
75a75043 BP |
491 | } |
492 | if (spec.dst_type == NX_LEARN_DST_MATCH || | |
493 | spec.dst_type == NX_LEARN_DST_LOAD) { | |
816fd533 BP |
494 | put_u32(b, spec.dst.field->nxm_header); |
495 | put_u16(b, spec.dst.ofs); | |
75a75043 BP |
496 | } else { |
497 | assert(spec.dst_type == NX_LEARN_DST_OUTPUT); | |
498 | } | |
499 | } | |
500 | } | |
75a75043 BP |
501 | |
502 | put_u16(b, 0); | |
503 | ||
504 | len = b->size - learn_ofs; | |
505 | if (len % 8) { | |
506 | ofpbuf_put_zeros(b, 8 - len % 8); | |
507 | } | |
508 | ||
509 | learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); | |
510 | learn->len = htons(b->size - learn_ofs); | |
4cb3fde7 BP |
511 | |
512 | /* In theory the above should have caught any errors, but... */ | |
0c3d5fc8 BP |
513 | if (flow) { |
514 | error = learn_check(learn, flow); | |
515 | if (error) { | |
516 | ovs_fatal(0, "%s: %s", orig, ofperr_to_string(error)); | |
517 | } | |
4cb3fde7 BP |
518 | } |
519 | free(orig); | |
75a75043 BP |
520 | } |
521 | ||
522 | void | |
523 | learn_format(const struct nx_action_learn *learn, struct ds *s) | |
524 | { | |
525 | struct cls_rule rule; | |
526 | const void *p, *end; | |
527 | ||
528 | cls_rule_init_catchall(&rule, 0); | |
529 | ||
530 | ds_put_format(s, "learn(table=%"PRIu8, learn->table_id); | |
531 | if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) { | |
532 | ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout)); | |
533 | } | |
534 | if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) { | |
535 | ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout)); | |
536 | } | |
0e553d9c BP |
537 | if (learn->fin_idle_timeout) { |
538 | ds_put_format(s, ",fin_idle_timeout=%"PRIu16, | |
539 | ntohs(learn->fin_idle_timeout)); | |
540 | } | |
541 | if (learn->fin_hard_timeout) { | |
542 | ds_put_format(s, ",fin_hard_timeout=%"PRIu16, | |
543 | ntohs(learn->fin_hard_timeout)); | |
544 | } | |
75a75043 BP |
545 | if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) { |
546 | ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority)); | |
547 | } | |
548 | if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) { | |
549 | ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM"); | |
550 | } | |
551 | if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) { | |
552 | ds_put_format(s, ",***flags=%"PRIu16"***", | |
553 | ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM); | |
554 | } | |
555 | if (learn->cookie != htonll(0)) { | |
556 | ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie)); | |
557 | } | |
0e553d9c | 558 | if (learn->pad != 0) { |
75a75043 BP |
559 | ds_put_cstr(s, ",***nonzero pad***"); |
560 | } | |
561 | ||
562 | end = (char *) learn + ntohs(learn->len); | |
563 | for (p = learn + 1; p != end; ) { | |
564 | uint16_t header = ntohs(get_be16(&p)); | |
565 | int n_bits = header & NX_LEARN_N_BITS_MASK; | |
566 | ||
567 | int src_type = header & NX_LEARN_SRC_MASK; | |
816fd533 | 568 | struct mf_subfield src; |
75a75043 BP |
569 | const uint8_t *src_value; |
570 | int src_value_bytes; | |
571 | ||
572 | int dst_type = header & NX_LEARN_DST_MASK; | |
816fd533 | 573 | struct mf_subfield dst; |
75a75043 | 574 | |
90bf1e07 | 575 | enum ofperr error; |
75a75043 BP |
576 | int i; |
577 | ||
578 | if (!header) { | |
579 | break; | |
580 | } | |
581 | ||
582 | error = learn_check_header(header, (char *) end - (char *) p); | |
90bf1e07 | 583 | if (error == OFPERR_OFPBAC_BAD_ARGUMENT) { |
75a75043 BP |
584 | ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)", |
585 | header); | |
586 | return; | |
90bf1e07 | 587 | } else if (error == OFPERR_OFPBAC_BAD_LEN) { |
75a75043 BP |
588 | ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes " |
589 | "long but only %td bytes are left***)", | |
590 | (char *) p - (char *) (learn + 1) - 2, | |
591 | learn_min_len(header) + 2, | |
592 | (char *) end - (char *) p + 2); | |
593 | return; | |
594 | } | |
595 | assert(!error); | |
596 | ||
597 | /* Get the source. */ | |
598 | if (src_type == NX_LEARN_SRC_FIELD) { | |
816fd533 | 599 | get_subfield(n_bits, &p, &src); |
75a75043 BP |
600 | src_value_bytes = 0; |
601 | src_value = NULL; | |
602 | } else { | |
816fd533 BP |
603 | src.field = NULL; |
604 | src.ofs = 0; | |
605 | src.n_bits = 0; | |
75a75043 BP |
606 | src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16); |
607 | src_value = p; | |
608 | p = (const void *) ((const uint8_t *) p + src_value_bytes); | |
609 | } | |
610 | ||
611 | /* Get the destination. */ | |
612 | if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { | |
816fd533 | 613 | get_subfield(n_bits, &p, &dst); |
75a75043 | 614 | } else { |
816fd533 BP |
615 | dst.field = NULL; |
616 | dst.ofs = 0; | |
617 | dst.n_bits = 0; | |
75a75043 BP |
618 | } |
619 | ||
620 | ds_put_char(s, ','); | |
621 | ||
622 | switch (src_type | dst_type) { | |
623 | case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: | |
816fd533 | 624 | if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) { |
75a75043 BP |
625 | union mf_value value; |
626 | uint8_t *bytes = (uint8_t *) &value; | |
627 | ||
816fd533 | 628 | if (src_value_bytes > dst.field->n_bytes) { |
f299fbeb BP |
629 | /* The destination field is an odd number of bytes, which |
630 | * got rounded up to a multiple of 2 to be put into the | |
631 | * learning action. Skip over the leading byte, which | |
632 | * should be zero anyway. Otherwise the memcpy() below | |
633 | * will overrun the start of 'value'. */ | |
816fd533 | 634 | int diff = src_value_bytes - dst.field->n_bytes; |
f299fbeb BP |
635 | src_value += diff; |
636 | src_value_bytes -= diff; | |
637 | } | |
638 | ||
75a75043 | 639 | memset(&value, 0, sizeof value); |
816fd533 | 640 | memcpy(&bytes[dst.field->n_bytes - src_value_bytes], |
75a75043 | 641 | src_value, src_value_bytes); |
816fd533 BP |
642 | ds_put_format(s, "%s=", dst.field->name); |
643 | mf_format(dst.field, &value, NULL, s); | |
75a75043 | 644 | } else { |
816fd533 | 645 | mf_format_subfield(&dst, s); |
75a75043 BP |
646 | ds_put_cstr(s, "=0x"); |
647 | for (i = 0; i < src_value_bytes; i++) { | |
648 | ds_put_format(s, "%02"PRIx8, src_value[i]); | |
649 | } | |
650 | } | |
651 | break; | |
652 | ||
653 | case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: | |
816fd533 BP |
654 | mf_format_subfield(&dst, s); |
655 | if (src.field != dst.field || src.ofs != dst.ofs) { | |
75a75043 | 656 | ds_put_char(s, '='); |
816fd533 | 657 | mf_format_subfield(&src, s); |
75a75043 BP |
658 | } |
659 | break; | |
660 | ||
661 | case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: | |
662 | ds_put_cstr(s, "load:0x"); | |
663 | for (i = 0; i < src_value_bytes; i++) { | |
664 | ds_put_format(s, "%02"PRIx8, src_value[i]); | |
665 | } | |
666 | ds_put_cstr(s, "->"); | |
816fd533 | 667 | mf_format_subfield(&dst, s); |
75a75043 BP |
668 | break; |
669 | ||
670 | case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: | |
671 | ds_put_cstr(s, "load:"); | |
816fd533 | 672 | mf_format_subfield(&src, s); |
75a75043 | 673 | ds_put_cstr(s, "->"); |
816fd533 | 674 | mf_format_subfield(&dst, s); |
75a75043 BP |
675 | break; |
676 | ||
677 | case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: | |
678 | ds_put_cstr(s, "output:"); | |
816fd533 | 679 | mf_format_subfield(&src, s); |
75a75043 BP |
680 | break; |
681 | } | |
682 | } | |
683 | if (!is_all_zeros(p, (char *) end - (char *) p)) { | |
684 | ds_put_cstr(s, ",***nonzero trailer***"); | |
685 | } | |
686 | ds_put_char(s, ')'); | |
687 | } |