]>
Commit | Line | Data |
---|---|---|
75a75043 | 1 | /* |
ffa4d73b | 2 | * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 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" | |
b1c5bf1f | 22 | #include "colors.h" |
b598f214 BW |
23 | #include "nx-match.h" |
24 | #include "openflow/openflow.h" | |
3e8a2ad1 | 25 | #include "openvswitch/dynamic-string.h" |
e29747e4 | 26 | #include "openvswitch/match.h" |
064d7f84 | 27 | #include "openvswitch/meta-flow.h" |
b598f214 BW |
28 | #include "openvswitch/ofp-actions.h" |
29 | #include "openvswitch/ofp-errors.h" | |
f4248336 | 30 | #include "openvswitch/ofp-util.h" |
64c96779 | 31 | #include "openvswitch/ofpbuf.h" |
5c7c16d8 | 32 | #include "vl-mff-map.h" |
75a75043 BP |
33 | #include "unaligned.h" |
34 | ||
75a75043 | 35 | |
f25d0cf3 BP |
36 | /* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid, |
37 | * otherwise an OFPERR_*. */ | |
38 | enum ofperr | |
67210a55 | 39 | learn_check(const struct ofpact_learn *learn, const struct match *src_match) |
75a75043 | 40 | { |
f25d0cf3 | 41 | const struct ofpact_learn_spec *spec; |
67210a55 | 42 | struct match dst_match; |
75a75043 | 43 | |
67210a55 | 44 | match_init_catchall(&dst_match); |
c65a31e2 | 45 | OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) { |
f25d0cf3 | 46 | enum ofperr error; |
337b9cec | 47 | |
f25d0cf3 BP |
48 | /* Check the source. */ |
49 | if (spec->src_type == NX_LEARN_SRC_FIELD) { | |
67210a55 | 50 | error = mf_check_src(&spec->src, src_match); |
f25d0cf3 BP |
51 | if (error) { |
52 | return error; | |
53 | } | |
75a75043 BP |
54 | } |
55 | ||
f25d0cf3 BP |
56 | /* Check the destination. */ |
57 | switch (spec->dst_type) { | |
75a75043 | 58 | case NX_LEARN_DST_MATCH: |
67210a55 | 59 | error = mf_check_src(&spec->dst, &dst_match); |
f25d0cf3 BP |
60 | if (error) { |
61 | return error; | |
62 | } | |
507a9a16 JR |
63 | if (spec->src_type & NX_LEARN_SRC_IMMEDIATE) { |
64 | mf_write_subfield_value(&spec->dst, | |
67210a55 JR |
65 | ofpact_learn_spec_imm(spec), |
66 | &dst_match); | |
507a9a16 | 67 | } |
75a75043 BP |
68 | break; |
69 | ||
70 | case NX_LEARN_DST_LOAD: | |
67210a55 | 71 | error = mf_check_dst(&spec->dst, &dst_match); |
f25d0cf3 BP |
72 | if (error) { |
73 | return error; | |
337b9cec | 74 | } |
75a75043 BP |
75 | break; |
76 | ||
77 | case NX_LEARN_DST_OUTPUT: | |
f25d0cf3 | 78 | /* Nothing to do. */ |
75a75043 BP |
79 | break; |
80 | } | |
81 | } | |
f25d0cf3 | 82 | return 0; |
75a75043 BP |
83 | } |
84 | ||
f25d0cf3 BP |
85 | /* Composes 'fm' so that executing it will implement 'learn' given that the |
86 | * packet being processed has 'flow' as its flow. | |
87 | * | |
88 | * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize | |
89 | * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the | |
90 | * 'ofpacts' buffer. | |
91 | * | |
92 | * The caller has to actually execute 'fm'. */ | |
93 | void | |
94 | learn_execute(const struct ofpact_learn *learn, const struct flow *flow, | |
95 | struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) | |
96 | { | |
97 | const struct ofpact_learn_spec *spec; | |
75a75043 | 98 | |
81a76618 BP |
99 | match_init_catchall(&fm->match); |
100 | fm->priority = learn->priority; | |
f25d0cf3 BP |
101 | fm->cookie = htonll(0); |
102 | fm->cookie_mask = htonll(0); | |
80771642 | 103 | fm->new_cookie = learn->cookie; |
b8266395 | 104 | fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; |
f25d0cf3 BP |
105 | fm->table_id = learn->table_id; |
106 | fm->command = OFPFC_MODIFY_STRICT; | |
107 | fm->idle_timeout = learn->idle_timeout; | |
108 | fm->hard_timeout = learn->hard_timeout; | |
ca26eb44 | 109 | fm->importance = 0; |
f25d0cf3 BP |
110 | fm->buffer_id = UINT32_MAX; |
111 | fm->out_port = OFPP_NONE; | |
5c7c16d8 | 112 | fm->ofpacts_tlv_bitmap = 0; |
35f48b8b BP |
113 | fm->flags = 0; |
114 | if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { | |
115 | fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; | |
116 | } | |
f25d0cf3 BP |
117 | fm->ofpacts = NULL; |
118 | fm->ofpacts_len = 0; | |
75a75043 | 119 | |
f25d0cf3 BP |
120 | if (learn->fin_idle_timeout || learn->fin_hard_timeout) { |
121 | struct ofpact_fin_timeout *oft; | |
122 | ||
123 | oft = ofpact_put_FIN_TIMEOUT(ofpacts); | |
124 | oft->fin_idle_timeout = learn->fin_idle_timeout; | |
125 | oft->fin_hard_timeout = learn->fin_hard_timeout; | |
126 | } | |
127 | ||
c65a31e2 | 128 | OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) { |
7eb4b1f1 | 129 | struct ofpact_set_field *sf; |
f25d0cf3 | 130 | union mf_subvalue value; |
f25d0cf3 BP |
131 | |
132 | if (spec->src_type == NX_LEARN_SRC_FIELD) { | |
133 | mf_read_subfield(&spec->src, flow, &value); | |
134 | } else { | |
507a9a16 JR |
135 | mf_subvalue_from_value(&spec->dst, &value, |
136 | ofpact_learn_spec_imm(spec)); | |
f25d0cf3 BP |
137 | } |
138 | ||
139 | switch (spec->dst_type) { | |
140 | case NX_LEARN_DST_MATCH: | |
81a76618 | 141 | mf_write_subfield(&spec->dst, &value, &fm->match); |
3d4b2e6e | 142 | match_add_ethernet_prereq(&fm->match, spec->dst.field); |
5c7c16d8 YHW |
143 | mf_vl_mff_set_tlv_bitmap( |
144 | spec->dst.field, &fm->match.flow.tunnel.metadata.present.map); | |
f25d0cf3 BP |
145 | break; |
146 | ||
147 | case NX_LEARN_DST_LOAD: | |
128684a6 | 148 | sf = ofpact_put_reg_load(ofpacts, spec->dst.field, NULL, NULL); |
7eb4b1f1 | 149 | bitwise_copy(&value, sizeof value, 0, |
128684a6 | 150 | sf->value, spec->dst.field->n_bytes, spec->dst.ofs, |
7eb4b1f1 | 151 | spec->n_bits); |
128684a6 JR |
152 | bitwise_one(ofpact_set_field_mask(sf), spec->dst.field->n_bytes, |
153 | spec->dst.ofs, spec->n_bits); | |
5c7c16d8 | 154 | mf_vl_mff_set_tlv_bitmap(spec->dst.field, &fm->ofpacts_tlv_bitmap); |
f25d0cf3 BP |
155 | break; |
156 | ||
157 | case NX_LEARN_DST_OUTPUT: | |
158 | if (spec->n_bits <= 16 | |
159 | || is_all_zeros(value.u8, sizeof value - 2)) { | |
5d867be0 | 160 | ofp_port_t port = u16_to_ofp(ntohll(value.integer)); |
f25d0cf3 | 161 | |
4e022ec0 | 162 | if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) |
f25d0cf3 BP |
163 | || port == OFPP_IN_PORT |
164 | || port == OFPP_FLOOD | |
165 | || port == OFPP_LOCAL | |
166 | || port == OFPP_ALL) { | |
167 | ofpact_put_OUTPUT(ofpacts)->port = port; | |
168 | } | |
169 | } | |
170 | break; | |
171 | } | |
172 | } | |
f25d0cf3 | 173 | |
6fd6ed71 PS |
174 | fm->ofpacts = ofpacts->data; |
175 | fm->ofpacts_len = ofpacts->size; | |
f25d0cf3 | 176 | } |
75a75043 | 177 | |
bcd2633a JP |
178 | /* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in |
179 | * the learn action 'learn'. */ | |
180 | void | |
181 | learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc) | |
182 | { | |
183 | const struct ofpact_learn_spec *spec; | |
184 | union mf_subvalue value; | |
185 | ||
186 | memset(&value, 0xff, sizeof value); | |
c65a31e2 | 187 | OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) { |
bcd2633a JP |
188 | if (spec->src_type == NX_LEARN_SRC_FIELD) { |
189 | mf_write_subfield_flow(&spec->src, &value, &wc->masks); | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
bdda5aca BP |
194 | /* Returns NULL if successful, otherwise a malloc()'d string describing the |
195 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 196 | static char * OVS_WARN_UNUSED_RESULT |
21b2fa61 JR |
197 | learn_parse_load_immediate(union mf_subvalue *imm, const char *s, |
198 | const char *full_s, struct ofpact_learn_spec *spec, | |
dfe191d5 | 199 | struct ofpbuf *ofpacts) |
3d792b70 | 200 | { |
3d792b70 | 201 | struct mf_subfield dst; |
bdda5aca | 202 | char *error; |
3d792b70 | 203 | |
bdda5aca BP |
204 | error = mf_parse_subfield(&dst, s); |
205 | if (error) { | |
206 | return error; | |
3d792b70 | 207 | } |
bad8a439 BP |
208 | if (!mf_nxm_header(dst.field->id)) { |
209 | return xasprintf("%s: experimenter OXM field '%s' not supported", | |
210 | full_s, s); | |
211 | } | |
3d792b70 | 212 | |
21b2fa61 JR |
213 | if (!bitwise_is_all_zeros(imm, sizeof *imm, dst.n_bits, |
214 | (8 * sizeof *imm) - dst.n_bits)) { | |
bdda5aca BP |
215 | return xasprintf("%s: value does not fit into %u bits", |
216 | full_s, dst.n_bits); | |
3d792b70 BP |
217 | } |
218 | ||
219 | spec->n_bits = dst.n_bits; | |
220 | spec->src_type = NX_LEARN_SRC_IMMEDIATE; | |
3d792b70 BP |
221 | spec->dst_type = NX_LEARN_DST_LOAD; |
222 | spec->dst = dst; | |
dfe191d5 JR |
223 | |
224 | /* Push value last, as this may reallocate 'spec'! */ | |
225 | unsigned int n_bytes = DIV_ROUND_UP(dst.n_bits, 8); | |
226 | uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(n_bytes)); | |
21b2fa61 | 227 | memcpy(src_imm, &imm->u8[sizeof imm->u8 - n_bytes], n_bytes); |
dfe191d5 | 228 | |
bdda5aca | 229 | return NULL; |
3d792b70 BP |
230 | } |
231 | ||
bdda5aca BP |
232 | /* Returns NULL if successful, otherwise a malloc()'d string describing the |
233 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 234 | static char * OVS_WARN_UNUSED_RESULT |
75a75043 | 235 | learn_parse_spec(const char *orig, char *name, char *value, |
50f96b10 | 236 | const struct ofputil_port_map *port_map, |
dfe191d5 JR |
237 | struct ofpact_learn_spec *spec, |
238 | struct ofpbuf *ofpacts, struct match *match) | |
75a75043 | 239 | { |
21b2fa61 JR |
240 | /* Parse destination and check prerequisites. */ |
241 | struct mf_subfield dst; | |
75a75043 | 242 | |
ffa4d73b YHW |
243 | char *error = mf_parse_subfield(&dst, name); |
244 | bool parse_error = error != NULL; | |
245 | free(error); | |
246 | ||
247 | if (!parse_error) { | |
21b2fa61 | 248 | if (!mf_nxm_header(dst.field->id)) { |
bad8a439 BP |
249 | return xasprintf("%s: experimenter OXM field '%s' not supported", |
250 | orig, name); | |
251 | } | |
21b2fa61 JR |
252 | spec->dst = dst; |
253 | spec->n_bits = dst.n_bits; | |
254 | spec->dst_type = NX_LEARN_DST_MATCH; | |
75a75043 BP |
255 | |
256 | /* Parse source and check prerequisites. */ | |
257 | if (value[0] != '\0') { | |
21b2fa61 JR |
258 | struct mf_subfield src; |
259 | error = mf_parse_subfield(&src, value); | |
bdda5aca | 260 | if (error) { |
21b2fa61 JR |
261 | union mf_value imm; |
262 | char *imm_error = NULL; | |
263 | ||
264 | /* Try an immediate value. */ | |
265 | if (dst.ofs == 0 && dst.n_bits == dst.field->n_bits) { | |
266 | /* Full field value. */ | |
50f96b10 BP |
267 | imm_error = mf_parse_value(dst.field, value, port_map, |
268 | &imm); | |
21b2fa61 JR |
269 | } else { |
270 | char *tail; | |
271 | /* Partial field value. */ | |
272 | if (parse_int_string(value, (uint8_t *)&imm, | |
273 | dst.field->n_bytes, &tail) | |
274 | || *tail != 0) { | |
275 | imm_error = xasprintf("%s: cannot parse integer value", orig); | |
276 | } | |
277 | ||
278 | if (!imm_error && | |
279 | !bitwise_is_all_zeros(&imm, dst.field->n_bytes, | |
280 | dst.n_bits, | |
281 | dst.field->n_bytes * 8 - dst.n_bits)) { | |
282 | struct ds ds; | |
283 | ||
284 | ds_init(&ds); | |
50f96b10 | 285 | mf_format(dst.field, &imm, NULL, NULL, &ds); |
21b2fa61 JR |
286 | imm_error = xasprintf("%s: value %s does not fit into %d bits", |
287 | orig, ds_cstr(&ds), dst.n_bits); | |
288 | ds_destroy(&ds); | |
289 | } | |
290 | } | |
291 | if (imm_error) { | |
292 | char *err = xasprintf("%s: %s value %s cannot be parsed as a subfield (%s) or an immediate value (%s)", | |
293 | orig, name, value, error, imm_error); | |
294 | free(error); | |
295 | free(imm_error); | |
296 | return err; | |
297 | } | |
298 | ||
299 | spec->src_type = NX_LEARN_SRC_IMMEDIATE; | |
300 | ||
301 | /* Update 'match' to allow for satisfying destination | |
302 | * prerequisites. */ | |
303 | mf_write_subfield_value(&dst, &imm, match); | |
304 | ||
305 | /* Push value last, as this may reallocate 'spec'! */ | |
306 | unsigned int imm_bytes = DIV_ROUND_UP(dst.n_bits, 8); | |
307 | uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, | |
308 | OFPACT_ALIGN(imm_bytes)); | |
309 | memcpy(src_imm, &imm, imm_bytes); | |
310 | ||
311 | free(error); | |
312 | return NULL; | |
75a75043 | 313 | } |
21b2fa61 | 314 | spec->src = src; |
816fd533 | 315 | if (spec->src.n_bits != spec->dst.n_bits) { |
bdda5aca BP |
316 | return xasprintf("%s: bit widths of %s (%u) and %s (%u) " |
317 | "differ", orig, name, spec->src.n_bits, value, | |
318 | spec->dst.n_bits); | |
75a75043 BP |
319 | } |
320 | } else { | |
816fd533 | 321 | spec->src = spec->dst; |
75a75043 BP |
322 | } |
323 | ||
75a75043 | 324 | spec->src_type = NX_LEARN_SRC_FIELD; |
75a75043 | 325 | } else if (!strcmp(name, "load")) { |
21b2fa61 JR |
326 | union mf_subvalue imm; |
327 | char *tail; | |
328 | char *dst_value = strstr(value, "->"); | |
329 | ||
330 | if (dst_value == value) { | |
331 | return xasprintf("%s: missing source before `->' in `%s'", name, | |
332 | value); | |
333 | } | |
334 | if (!dst_value) { | |
335 | return xasprintf("%s: missing `->' in `%s'", name, value); | |
336 | } | |
337 | ||
338 | if (!parse_int_string(value, imm.u8, sizeof imm.u8, (char **) &tail) | |
339 | && tail != value) { | |
340 | if (tail != dst_value) { | |
341 | return xasprintf("%s: garbage before `->' in `%s'", | |
342 | name, value); | |
343 | } | |
344 | ||
71f21279 BP |
345 | error = learn_parse_load_immediate(&imm, dst_value + 2, value, spec, |
346 | ofpacts); | |
bdda5aca BP |
347 | if (error) { |
348 | return error; | |
349 | } | |
75a75043 | 350 | } else { |
f25d0cf3 | 351 | struct ofpact_reg_move move; |
75a75043 | 352 | |
bdda5aca BP |
353 | error = nxm_parse_reg_move(&move, value); |
354 | if (error) { | |
355 | return error; | |
356 | } | |
75a75043 | 357 | |
f25d0cf3 | 358 | spec->n_bits = move.src.n_bits; |
75a75043 | 359 | spec->src_type = NX_LEARN_SRC_FIELD; |
f25d0cf3 | 360 | spec->src = move.src; |
75a75043 | 361 | spec->dst_type = NX_LEARN_DST_LOAD; |
f25d0cf3 | 362 | spec->dst = move.dst; |
75a75043 BP |
363 | } |
364 | } else if (!strcmp(name, "output")) { | |
71f21279 | 365 | error = mf_parse_subfield(&spec->src, value); |
bdda5aca BP |
366 | if (error) { |
367 | return error; | |
75a75043 BP |
368 | } |
369 | ||
816fd533 | 370 | spec->n_bits = spec->src.n_bits; |
75a75043 | 371 | spec->src_type = NX_LEARN_SRC_FIELD; |
75a75043 | 372 | spec->dst_type = NX_LEARN_DST_OUTPUT; |
75a75043 | 373 | } else { |
bdda5aca | 374 | return xasprintf("%s: unknown keyword %s", orig, name); |
75a75043 | 375 | } |
bdda5aca BP |
376 | |
377 | return NULL; | |
75a75043 BP |
378 | } |
379 | ||
bdda5aca BP |
380 | /* Returns NULL if successful, otherwise a malloc()'d string describing the |
381 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 382 | static char * OVS_WARN_UNUSED_RESULT |
50f96b10 BP |
383 | learn_parse__(char *orig, char *arg, const struct ofputil_port_map *port_map, |
384 | struct ofpbuf *ofpacts) | |
75a75043 | 385 | { |
f25d0cf3 | 386 | struct ofpact_learn *learn; |
81a76618 | 387 | struct match match; |
bdda5aca | 388 | char *name, *value; |
75a75043 | 389 | |
f25d0cf3 BP |
390 | learn = ofpact_put_LEARN(ofpacts); |
391 | learn->idle_timeout = OFP_FLOW_PERMANENT; | |
392 | learn->hard_timeout = OFP_FLOW_PERMANENT; | |
393 | learn->priority = OFP_DEFAULT_PRIORITY; | |
75a75043 BP |
394 | learn->table_id = 1; |
395 | ||
81a76618 | 396 | match_init_catchall(&match); |
75a75043 | 397 | while (ofputil_parse_key_value(&arg, &name, &value)) { |
75a75043 BP |
398 | if (!strcmp(name, "table")) { |
399 | learn->table_id = atoi(value); | |
400 | if (learn->table_id == 255) { | |
bdda5aca BP |
401 | return xasprintf("%s: table id 255 not valid for `learn' " |
402 | "action", orig); | |
75a75043 BP |
403 | } |
404 | } else if (!strcmp(name, "priority")) { | |
f25d0cf3 | 405 | learn->priority = atoi(value); |
75a75043 | 406 | } else if (!strcmp(name, "idle_timeout")) { |
f25d0cf3 | 407 | learn->idle_timeout = atoi(value); |
75a75043 | 408 | } else if (!strcmp(name, "hard_timeout")) { |
f25d0cf3 | 409 | learn->hard_timeout = atoi(value); |
0e553d9c | 410 | } else if (!strcmp(name, "fin_idle_timeout")) { |
f25d0cf3 | 411 | learn->fin_idle_timeout = atoi(value); |
0e553d9c | 412 | } else if (!strcmp(name, "fin_hard_timeout")) { |
f25d0cf3 | 413 | learn->fin_hard_timeout = atoi(value); |
75a75043 | 414 | } else if (!strcmp(name, "cookie")) { |
80771642 | 415 | learn->cookie = htonll(strtoull(value, NULL, 0)); |
3d6832c2 | 416 | } else if (!strcmp(name, "send_flow_rem")) { |
35f48b8b BP |
417 | learn->flags |= NX_LEARN_F_SEND_FLOW_REM; |
418 | } else if (!strcmp(name, "delete_learned")) { | |
419 | learn->flags |= NX_LEARN_F_DELETE_LEARNED; | |
4c71600d DDP |
420 | } else if (!strcmp(name, "limit")) { |
421 | learn->limit = atoi(value); | |
422 | } else if (!strcmp(name, "result_dst")) { | |
423 | char *error; | |
424 | learn->flags |= NX_LEARN_F_WRITE_RESULT; | |
425 | error = mf_parse_subfield(&learn->result_dst, value); | |
426 | if (error) { | |
427 | return error; | |
428 | } | |
429 | if (!learn->result_dst.field->writable) { | |
430 | return xasprintf("%s is read-only", value); | |
431 | } | |
432 | if (learn->result_dst.n_bits != 1) { | |
433 | return xasprintf("result_dst in 'learn' action must be a " | |
434 | "single bit"); | |
435 | } | |
75a75043 | 436 | } else { |
f25d0cf3 | 437 | struct ofpact_learn_spec *spec; |
bdda5aca | 438 | char *error; |
f25d0cf3 BP |
439 | |
440 | spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); | |
50f96b10 BP |
441 | error = learn_parse_spec(orig, name, value, port_map, |
442 | spec, ofpacts, &match); | |
bdda5aca BP |
443 | if (error) { |
444 | return error; | |
445 | } | |
dfe191d5 | 446 | learn = ofpacts->header; |
75a75043 BP |
447 | } |
448 | } | |
ce058104 | 449 | ofpact_finish_LEARN(ofpacts, &learn); |
4cb3fde7 | 450 | |
bdda5aca BP |
451 | return NULL; |
452 | } | |
453 | ||
454 | /* Parses 'arg' as a set of arguments to the "learn" action and appends a | |
455 | * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the | |
456 | * format parsed. | |
457 | * | |
458 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
459 | * error. The caller is responsible for freeing the returned string. | |
460 | * | |
461 | * If 'flow' is nonnull, then it should be the flow from a struct match that is | |
462 | * the matching rule for the learning action. This helps to better validate | |
463 | * the action's arguments. | |
464 | * | |
465 | * Modifies 'arg'. */ | |
cab50449 | 466 | char * OVS_WARN_UNUSED_RESULT |
50f96b10 BP |
467 | learn_parse(char *arg, const struct ofputil_port_map *port_map, |
468 | struct ofpbuf *ofpacts) | |
bdda5aca BP |
469 | { |
470 | char *orig = xstrdup(arg); | |
50f96b10 | 471 | char *error = learn_parse__(orig, arg, port_map, ofpacts); |
4cb3fde7 | 472 | free(orig); |
bdda5aca | 473 | return error; |
75a75043 BP |
474 | } |
475 | ||
f25d0cf3 BP |
476 | /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8) |
477 | * describes. */ | |
75a75043 | 478 | void |
50f96b10 BP |
479 | learn_format(const struct ofpact_learn *learn, |
480 | const struct ofputil_port_map *port_map, struct ds *s) | |
75a75043 | 481 | { |
f25d0cf3 | 482 | const struct ofpact_learn_spec *spec; |
81a76618 | 483 | struct match match; |
75a75043 | 484 | |
81a76618 | 485 | match_init_catchall(&match); |
75a75043 | 486 | |
b1c5bf1f QM |
487 | ds_put_format(s, "%slearn(%s%stable=%s%"PRIu8, |
488 | colors.learn, colors.end, colors.special, colors.end, | |
489 | learn->table_id); | |
f25d0cf3 | 490 | if (learn->idle_timeout != OFP_FLOW_PERMANENT) { |
b1c5bf1f QM |
491 | ds_put_format(s, ",%sidle_timeout=%s%"PRIu16, |
492 | colors.param, colors.end, learn->idle_timeout); | |
75a75043 | 493 | } |
f25d0cf3 | 494 | if (learn->hard_timeout != OFP_FLOW_PERMANENT) { |
b1c5bf1f QM |
495 | ds_put_format(s, ",%shard_timeout=%s%"PRIu16, |
496 | colors.param, colors.end, learn->hard_timeout); | |
75a75043 | 497 | } |
0e553d9c | 498 | if (learn->fin_idle_timeout) { |
b1c5bf1f QM |
499 | ds_put_format(s, ",%sfin_idle_timeout=%s%"PRIu16, |
500 | colors.param, colors.end, learn->fin_idle_timeout); | |
0e553d9c BP |
501 | } |
502 | if (learn->fin_hard_timeout) { | |
b1c5bf1f QM |
503 | ds_put_format(s, "%s,fin_hard_timeout=%s%"PRIu16, |
504 | colors.param, colors.end, learn->fin_hard_timeout); | |
0e553d9c | 505 | } |
f25d0cf3 | 506 | if (learn->priority != OFP_DEFAULT_PRIORITY) { |
b1c5bf1f QM |
507 | ds_put_format(s, "%s,priority=%s%"PRIu16, |
508 | colors.special, colors.end, learn->priority); | |
75a75043 | 509 | } |
35f48b8b | 510 | if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { |
b1c5bf1f | 511 | ds_put_format(s, ",%ssend_flow_rem%s", colors.value, colors.end); |
75a75043 | 512 | } |
35f48b8b | 513 | if (learn->flags & NX_LEARN_F_DELETE_LEARNED) { |
b1c5bf1f | 514 | ds_put_format(s, ",%sdelete_learned%s", colors.value, colors.end); |
35f48b8b | 515 | } |
f25d0cf3 | 516 | if (learn->cookie != 0) { |
b1c5bf1f QM |
517 | ds_put_format(s, ",%scookie=%s%#"PRIx64, |
518 | colors.param, colors.end, ntohll(learn->cookie)); | |
75a75043 | 519 | } |
4c71600d DDP |
520 | if (learn->limit != 0) { |
521 | ds_put_format(s, ",%slimit=%s%"PRIu32, | |
522 | colors.param, colors.end, learn->limit); | |
523 | } | |
524 | if (learn->flags & NX_LEARN_F_WRITE_RESULT) { | |
525 | ds_put_format(s, ",%sresult_dst=%s", colors.param, colors.end); | |
526 | mf_format_subfield(&learn->result_dst, s); | |
527 | } | |
75a75043 | 528 | |
c65a31e2 | 529 | OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) { |
507a9a16 | 530 | unsigned int n_bytes = DIV_ROUND_UP(spec->n_bits, 8); |
75a75043 BP |
531 | ds_put_char(s, ','); |
532 | ||
f25d0cf3 | 533 | switch (spec->src_type | spec->dst_type) { |
dfe191d5 | 534 | case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: { |
f25d0cf3 BP |
535 | if (spec->dst.ofs == 0 |
536 | && spec->dst.n_bits == spec->dst.field->n_bits) { | |
75a75043 | 537 | union mf_value value; |
f299fbeb | 538 | |
75a75043 | 539 | memset(&value, 0, sizeof value); |
dfe191d5 | 540 | memcpy(&value.b[spec->dst.field->n_bytes - n_bytes], |
507a9a16 | 541 | ofpact_learn_spec_imm(spec), n_bytes); |
b1c5bf1f QM |
542 | ds_put_format(s, "%s%s=%s", colors.param, |
543 | spec->dst.field->name, colors.end); | |
50f96b10 | 544 | mf_format(spec->dst.field, &value, NULL, port_map, s); |
75a75043 | 545 | } else { |
b1c5bf1f | 546 | ds_put_format(s, "%s", colors.param); |
f25d0cf3 | 547 | mf_format_subfield(&spec->dst, s); |
b1c5bf1f | 548 | ds_put_format(s, "=%s", colors.end); |
507a9a16 | 549 | ds_put_hex(s, ofpact_learn_spec_imm(spec), n_bytes); |
75a75043 BP |
550 | } |
551 | break; | |
dfe191d5 | 552 | } |
75a75043 | 553 | case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: |
b1c5bf1f | 554 | ds_put_format(s, "%s", colors.param); |
f25d0cf3 | 555 | mf_format_subfield(&spec->dst, s); |
b1c5bf1f | 556 | ds_put_format(s, "%s", colors.end); |
f25d0cf3 BP |
557 | if (spec->src.field != spec->dst.field || |
558 | spec->src.ofs != spec->dst.ofs) { | |
b1c5bf1f | 559 | ds_put_format(s, "%s=%s", colors.param, colors.end); |
f25d0cf3 | 560 | mf_format_subfield(&spec->src, s); |
75a75043 BP |
561 | } |
562 | break; | |
563 | ||
564 | case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: | |
b1c5bf1f | 565 | ds_put_format(s, "%sload:%s", colors.special, colors.end); |
507a9a16 | 566 | ds_put_hex(s, ofpact_learn_spec_imm(spec), n_bytes); |
b1c5bf1f | 567 | ds_put_format(s, "%s->%s", colors.special, colors.end); |
f25d0cf3 | 568 | mf_format_subfield(&spec->dst, s); |
75a75043 BP |
569 | break; |
570 | ||
571 | case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: | |
b1c5bf1f | 572 | ds_put_format(s, "%sload:%s", colors.special, colors.end); |
f25d0cf3 | 573 | mf_format_subfield(&spec->src, s); |
b1c5bf1f | 574 | ds_put_format(s, "%s->%s", colors.special, colors.end); |
f25d0cf3 | 575 | mf_format_subfield(&spec->dst, s); |
75a75043 BP |
576 | break; |
577 | ||
578 | case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: | |
b1c5bf1f | 579 | ds_put_format(s, "%soutput:%s", colors.special, colors.end); |
f25d0cf3 | 580 | mf_format_subfield(&spec->src, s); |
75a75043 BP |
581 | break; |
582 | } | |
583 | } | |
b1c5bf1f | 584 | ds_put_format(s, "%s)%s", colors.learn, colors.end); |
75a75043 | 585 | } |