]>
Commit | Line | Data |
---|---|---|
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_*. */ | |
36 | enum ofperr | |
37 | learn_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'. */ | |
88 | void | |
89 | learn_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'. */ | |
173 | void | |
174 | learn_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. */ | |
189 | static char * WARN_UNUSED_RESULT | |
f25d0cf3 | 190 | learn_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. */ | |
248 | static char * WARN_UNUSED_RESULT | |
75a75043 | 249 | learn_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. */ | |
341 | static char * WARN_UNUSED_RESULT | |
342 | learn_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'. */ | |
416 | char * WARN_UNUSED_RESULT | |
417 | learn_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 | 427 | void |
f25d0cf3 | 428 | learn_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 | } |