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