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