]>
Commit | Line | Data |
---|---|---|
f22716dc | 1 | /* |
e0edde6f | 2 | * Copyright (c) 2010, 2011, 2012 Nicira, Inc. |
f22716dc JP |
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 "ofp-parse.h" | |
20 | ||
00b1c62f | 21 | #include <ctype.h> |
f22716dc JP |
22 | #include <errno.h> |
23 | #include <stdlib.h> | |
24 | ||
daff3353 | 25 | #include "bundle.h" |
10a24935 | 26 | #include "byte-order.h" |
15f1f1b6 | 27 | #include "dynamic-string.h" |
75a75043 | 28 | #include "learn.h" |
6a885fd0 | 29 | #include "meta-flow.h" |
53ddd40a | 30 | #include "multipath.h" |
f25d0cf3 | 31 | #include "netdev.h" |
f393f81e | 32 | #include "nx-match.h" |
f25d0cf3 | 33 | #include "ofp-actions.h" |
f22716dc JP |
34 | #include "ofp-util.h" |
35 | #include "ofpbuf.h" | |
36 | #include "openflow/openflow.h" | |
37 | #include "packets.h" | |
38 | #include "socket-util.h" | |
39 | #include "vconn.h" | |
40 | #include "vlog.h" | |
f22716dc | 41 | |
d98e6007 | 42 | VLOG_DEFINE_THIS_MODULE(ofp_parse); |
f22716dc | 43 | |
99b67183 BP |
44 | static void ofp_fatal(const char *flow, bool verbose, const char *format, ...) |
45 | NO_RETURN; | |
46 | ||
c3636ffc BP |
47 | static uint8_t |
48 | str_to_table_id(const char *str) | |
49 | { | |
50 | int table_id; | |
51 | ||
52 | if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) { | |
53 | ovs_fatal(0, "invalid table \"%s\"", str); | |
54 | } | |
55 | return table_id; | |
56 | } | |
57 | ||
58 | static uint16_t | |
59 | str_to_u16(const char *str, const char *name) | |
60 | { | |
61 | int value; | |
62 | ||
63 | if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) { | |
64 | ovs_fatal(0, "invalid %s \"%s\"", name, str); | |
65 | } | |
66 | return value; | |
67 | } | |
68 | ||
f22716dc JP |
69 | static uint32_t |
70 | str_to_u32(const char *str) | |
71 | { | |
72 | char *tail; | |
73 | uint32_t value; | |
74 | ||
c4894ed4 | 75 | if (!str[0]) { |
ce5452cf EJ |
76 | ovs_fatal(0, "missing required numeric argument"); |
77 | } | |
78 | ||
f22716dc JP |
79 | errno = 0; |
80 | value = strtoul(str, &tail, 0); | |
81 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
82 | ovs_fatal(0, "invalid numeric format %s", str); | |
83 | } | |
84 | return value; | |
85 | } | |
86 | ||
87 | static uint64_t | |
88 | str_to_u64(const char *str) | |
89 | { | |
90 | char *tail; | |
91 | uint64_t value; | |
92 | ||
c4894ed4 BP |
93 | if (!str[0]) { |
94 | ovs_fatal(0, "missing required numeric argument"); | |
95 | } | |
96 | ||
f22716dc JP |
97 | errno = 0; |
98 | value = strtoull(str, &tail, 0); | |
99 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
100 | ovs_fatal(0, "invalid numeric format %s", str); | |
101 | } | |
102 | return value; | |
103 | } | |
104 | ||
105 | static void | |
106 | str_to_mac(const char *str, uint8_t mac[6]) | |
107 | { | |
108 | if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) | |
109 | != ETH_ADDR_SCAN_COUNT) { | |
110 | ovs_fatal(0, "invalid mac address %s", str); | |
111 | } | |
112 | } | |
113 | ||
cb8ca532 | 114 | static void |
6a885fd0 | 115 | str_to_ip(const char *str, ovs_be32 *ip) |
cb8ca532 | 116 | { |
f22716dc | 117 | struct in_addr in_addr; |
f22716dc | 118 | |
6a885fd0 | 119 | if (lookup_ip(str, &in_addr)) { |
f22716dc JP |
120 | ovs_fatal(0, "%s: could not convert to IP address", str); |
121 | } | |
122 | *ip = in_addr.s_addr; | |
d31f1109 JP |
123 | } |
124 | ||
5682f723 | 125 | static void |
f25d0cf3 | 126 | parse_enqueue(char *arg, struct ofpbuf *ofpacts) |
5682f723 | 127 | { |
333eba21 BP |
128 | char *sp = NULL; |
129 | char *port = strtok_r(arg, ":q", &sp); | |
130 | char *queue = strtok_r(NULL, "", &sp); | |
f25d0cf3 | 131 | struct ofpact_enqueue *enqueue; |
333eba21 BP |
132 | |
133 | if (port == NULL || queue == NULL) { | |
134 | ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); | |
135 | } | |
136 | ||
f25d0cf3 BP |
137 | enqueue = ofpact_put_ENQUEUE(ofpacts); |
138 | enqueue->port = str_to_u32(port); | |
139 | enqueue->queue = str_to_u32(queue); | |
5682f723 BP |
140 | } |
141 | ||
f694937d | 142 | static void |
f25d0cf3 | 143 | parse_output(char *arg, struct ofpbuf *ofpacts) |
f694937d EJ |
144 | { |
145 | if (strchr(arg, '[')) { | |
f25d0cf3 | 146 | struct ofpact_output_reg *output_reg; |
f694937d | 147 | |
f25d0cf3 BP |
148 | output_reg = ofpact_put_OUTPUT_REG(ofpacts); |
149 | mf_parse_subfield(&output_reg->src, arg); | |
150 | output_reg->max_len = UINT16_MAX; | |
f694937d | 151 | } else { |
f25d0cf3 BP |
152 | struct ofpact_output *output; |
153 | ||
154 | output = ofpact_put_OUTPUT(ofpacts); | |
155 | output->port = str_to_u32(arg); | |
156 | output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; | |
f694937d EJ |
157 | } |
158 | } | |
159 | ||
29901626 | 160 | static void |
f25d0cf3 | 161 | parse_resubmit(char *arg, struct ofpbuf *ofpacts) |
29901626 | 162 | { |
f25d0cf3 | 163 | struct ofpact_resubmit *resubmit; |
29901626 | 164 | char *in_port_s, *table_s; |
f25d0cf3 BP |
165 | |
166 | resubmit = ofpact_put_RESUBMIT(ofpacts); | |
29901626 BP |
167 | |
168 | in_port_s = strsep(&arg, ","); | |
169 | if (in_port_s && in_port_s[0]) { | |
8010100b | 170 | if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { |
c6100d92 | 171 | ovs_fatal(0, "%s: resubmit to unknown port", in_port_s); |
29901626 BP |
172 | } |
173 | } else { | |
f25d0cf3 | 174 | resubmit->in_port = OFPP_IN_PORT; |
29901626 BP |
175 | } |
176 | ||
177 | table_s = strsep(&arg, ","); | |
f25d0cf3 | 178 | resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255; |
29901626 | 179 | |
f25d0cf3 | 180 | if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { |
29901626 BP |
181 | ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " |
182 | " on resubmit"); | |
183 | } | |
333eba21 BP |
184 | } |
185 | ||
186 | static void | |
f25d0cf3 | 187 | parse_note(const char *arg, struct ofpbuf *ofpacts) |
333eba21 | 188 | { |
f25d0cf3 | 189 | struct ofpact_note *note; |
333eba21 | 190 | |
f25d0cf3 | 191 | note = ofpact_put_NOTE(ofpacts); |
333eba21 BP |
192 | while (*arg != '\0') { |
193 | uint8_t byte; | |
194 | bool ok; | |
195 | ||
196 | if (*arg == '.') { | |
197 | arg++; | |
198 | } | |
199 | if (*arg == '\0') { | |
200 | break; | |
201 | } | |
202 | ||
203 | byte = hexits_value(arg, 2, &ok); | |
204 | if (!ok) { | |
205 | ovs_fatal(0, "bad hex digit in `note' argument"); | |
206 | } | |
f25d0cf3 | 207 | ofpbuf_put(ofpacts, &byte, 1); |
333eba21 | 208 | |
f25d0cf3 BP |
209 | note = ofpacts->l2; |
210 | note->length++; | |
333eba21 | 211 | |
f25d0cf3 | 212 | arg += 2; |
333eba21 | 213 | } |
f25d0cf3 | 214 | ofpact_update_len(ofpacts, ¬e->ofpact); |
333eba21 BP |
215 | } |
216 | ||
0e553d9c BP |
217 | static void |
218 | parse_fin_timeout(struct ofpbuf *b, char *arg) | |
219 | { | |
f25d0cf3 | 220 | struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); |
0e553d9c BP |
221 | char *key, *value; |
222 | ||
0e553d9c BP |
223 | while (ofputil_parse_key_value(&arg, &key, &value)) { |
224 | if (!strcmp(key, "idle_timeout")) { | |
f25d0cf3 | 225 | oft->fin_idle_timeout = str_to_u16(value, key); |
0e553d9c | 226 | } else if (!strcmp(key, "hard_timeout")) { |
f25d0cf3 | 227 | oft->fin_hard_timeout = str_to_u16(value, key); |
0e553d9c BP |
228 | } else { |
229 | ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); | |
230 | } | |
231 | } | |
232 | } | |
233 | ||
a7349929 BP |
234 | static void |
235 | parse_controller(struct ofpbuf *b, char *arg) | |
236 | { | |
237 | enum ofp_packet_in_reason reason = OFPR_ACTION; | |
238 | uint16_t controller_id = 0; | |
239 | uint16_t max_len = UINT16_MAX; | |
240 | ||
241 | if (!arg[0]) { | |
242 | /* Use defaults. */ | |
243 | } else if (strspn(arg, "0123456789") == strlen(arg)) { | |
244 | max_len = str_to_u16(arg, "max_len"); | |
245 | } else { | |
246 | char *name, *value; | |
247 | ||
248 | while (ofputil_parse_key_value(&arg, &name, &value)) { | |
249 | if (!strcmp(name, "reason")) { | |
250 | if (!ofputil_packet_in_reason_from_string(value, &reason)) { | |
251 | ovs_fatal(0, "unknown reason \"%s\"", value); | |
252 | } | |
253 | } else if (!strcmp(name, "max_len")) { | |
254 | max_len = str_to_u16(value, "max_len"); | |
255 | } else if (!strcmp(name, "id")) { | |
256 | controller_id = str_to_u16(value, "id"); | |
257 | } else { | |
258 | ovs_fatal(0, "unknown key \"%s\" parsing controller action", | |
259 | name); | |
260 | } | |
261 | } | |
262 | } | |
263 | ||
264 | if (reason == OFPR_ACTION && controller_id == 0) { | |
f25d0cf3 BP |
265 | struct ofpact_output *output; |
266 | ||
267 | output = ofpact_put_OUTPUT(b); | |
268 | output->port = OFPP_CONTROLLER; | |
269 | output->max_len = max_len; | |
a7349929 | 270 | } else { |
f25d0cf3 | 271 | struct ofpact_controller *controller; |
a7349929 | 272 | |
f25d0cf3 BP |
273 | controller = ofpact_put_CONTROLLER(b); |
274 | controller->max_len = max_len; | |
275 | controller->reason = reason; | |
276 | controller->controller_id = controller_id; | |
a7349929 BP |
277 | } |
278 | } | |
279 | ||
c2d967a5 | 280 | static void |
99086062 | 281 | parse_noargs_dec_ttl(struct ofpbuf *b) |
c2d967a5 MM |
282 | { |
283 | struct ofpact_cnt_ids *ids; | |
7bcb1506 | 284 | uint16_t id = 0; |
c2d967a5 MM |
285 | |
286 | ids = ofpact_put_DEC_TTL(b); | |
7bcb1506 IY |
287 | ofpbuf_put(b, &id, sizeof id); |
288 | ids = b->l2; | |
289 | ids->n_controllers++; | |
290 | ofpact_update_len(b, &ids->ofpact); | |
291 | } | |
c2d967a5 | 292 | |
7bcb1506 | 293 | static void |
99086062 | 294 | parse_dec_ttl(struct ofpbuf *b, char *arg) |
7bcb1506 | 295 | { |
c2d967a5 | 296 | if (*arg == '\0') { |
99086062 | 297 | parse_noargs_dec_ttl(b); |
c2d967a5 | 298 | } else { |
7bcb1506 | 299 | struct ofpact_cnt_ids *ids; |
c2d967a5 MM |
300 | char *cntr; |
301 | ||
7bcb1506 | 302 | ids = ofpact_put_DEC_TTL(b); |
c2d967a5 MM |
303 | ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; |
304 | for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; | |
305 | cntr = strtok_r(NULL, ", ", &arg)) { | |
306 | uint16_t id = atoi(cntr); | |
307 | ||
308 | ofpbuf_put(b, &id, sizeof id); | |
309 | ids = b->l2; | |
310 | ids->n_controllers++; | |
311 | } | |
312 | if (!ids->n_controllers) { | |
313 | ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller " | |
314 | "id."); | |
315 | } | |
7bcb1506 | 316 | ofpact_update_len(b, &ids->ofpact); |
c2d967a5 | 317 | } |
c2d967a5 MM |
318 | } |
319 | ||
0f3f3c3d SH |
320 | static void |
321 | parse_set_mpls_ttl(struct ofpbuf *b, const char *arg) | |
322 | { | |
323 | struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b); | |
324 | ||
325 | if (*arg == '\0') { | |
326 | ovs_fatal(0, "parse_set_mpls_ttl: expected ttl."); | |
327 | } | |
328 | ||
329 | mpls_ttl->ttl = atoi(arg); | |
330 | } | |
331 | ||
f5c45121 SH |
332 | static void |
333 | set_field_parse(const char *arg, struct ofpbuf *ofpacts) | |
334 | { | |
335 | char *orig = xstrdup(arg); | |
336 | struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts); | |
337 | char *value; | |
338 | char *delim; | |
339 | char *key; | |
340 | const struct mf_field *mf; | |
341 | const char *error; | |
342 | union mf_value mf_value; | |
343 | ||
344 | value = orig; | |
345 | delim = strstr(orig, "->"); | |
346 | if (!delim) { | |
347 | ovs_fatal(0, "%s: missing `->'", orig); | |
348 | } | |
349 | if (strlen(delim) <= strlen("->")) { | |
350 | ovs_fatal(0, "%s: missing field name following `->'", orig); | |
351 | } | |
352 | ||
353 | key = delim + strlen("->"); | |
354 | mf = mf_from_name(key); | |
355 | if (!mf) { | |
356 | ovs_fatal(0, "%s is not valid oxm field name", key); | |
357 | } | |
358 | if (!mf->writable) { | |
359 | ovs_fatal(0, "%s is not allowed to set", key); | |
360 | } | |
361 | ||
362 | delim[0] = '\0'; | |
363 | error = mf_parse_value(mf, value, &mf_value); | |
364 | if (error) { | |
365 | ovs_fatal(0, "%s", error); | |
366 | } | |
367 | if (!mf_is_value_valid(mf, &mf_value)) { | |
368 | ovs_fatal(0, "%s is not valid valid for field %s", value, key); | |
369 | } | |
370 | ofpact_set_field_init(load, mf, &mf_value); | |
371 | free(orig); | |
372 | } | |
373 | ||
4cceacb9 JS |
374 | static void |
375 | parse_metadata(struct ofpbuf *b, char *arg) | |
376 | { | |
377 | struct ofpact_metadata *om; | |
378 | char *mask = strchr(arg, '/'); | |
379 | ||
380 | om = ofpact_put_WRITE_METADATA(b); | |
381 | ||
382 | if (mask) { | |
383 | *mask = '\0'; | |
384 | om->mask = htonll(str_to_u64(mask + 1)); | |
385 | } else { | |
386 | om->mask = htonll(UINT64_MAX); | |
387 | } | |
388 | ||
389 | om->metadata = htonll(str_to_u64(arg)); | |
390 | } | |
391 | ||
333eba21 | 392 | static void |
75a75043 | 393 | parse_named_action(enum ofputil_action_code code, const struct flow *flow, |
f25d0cf3 | 394 | char *arg, struct ofpbuf *ofpacts) |
333eba21 | 395 | { |
f25d0cf3 BP |
396 | struct ofpact_tunnel *tunnel; |
397 | uint16_t vid; | |
3e34fbdd | 398 | uint16_t ethertype; |
f25d0cf3 BP |
399 | ovs_be32 ip; |
400 | uint8_t pcp; | |
401 | uint8_t tos; | |
333eba21 BP |
402 | |
403 | switch (code) { | |
690a61c5 BP |
404 | case OFPUTIL_ACTION_INVALID: |
405 | NOT_REACHED(); | |
406 | ||
08f94c0e | 407 | case OFPUTIL_OFPAT10_OUTPUT: |
d01c980f | 408 | case OFPUTIL_OFPAT11_OUTPUT: |
f25d0cf3 | 409 | parse_output(arg, ofpacts); |
333eba21 BP |
410 | break; |
411 | ||
08f94c0e | 412 | case OFPUTIL_OFPAT10_SET_VLAN_VID: |
d01c980f | 413 | case OFPUTIL_OFPAT11_SET_VLAN_VID: |
f25d0cf3 BP |
414 | vid = str_to_u32(arg); |
415 | if (vid & ~VLAN_VID_MASK) { | |
416 | ovs_fatal(0, "%s: not a valid VLAN VID", arg); | |
417 | } | |
418 | ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; | |
333eba21 BP |
419 | break; |
420 | ||
08f94c0e | 421 | case OFPUTIL_OFPAT10_SET_VLAN_PCP: |
d01c980f | 422 | case OFPUTIL_OFPAT11_SET_VLAN_PCP: |
f25d0cf3 BP |
423 | pcp = str_to_u32(arg); |
424 | if (pcp & ~7) { | |
425 | ovs_fatal(0, "%s: not a valid VLAN PCP", arg); | |
426 | } | |
427 | ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; | |
333eba21 BP |
428 | break; |
429 | ||
78a3fff6 | 430 | case OFPUTIL_OFPAT12_SET_FIELD: |
f5c45121 | 431 | set_field_parse(arg, ofpacts); |
78a3fff6 SH |
432 | break; |
433 | ||
08f94c0e | 434 | case OFPUTIL_OFPAT10_STRIP_VLAN: |
8e61c110 | 435 | case OFPUTIL_OFPAT11_POP_VLAN: |
f25d0cf3 | 436 | ofpact_put_STRIP_VLAN(ofpacts); |
333eba21 BP |
437 | break; |
438 | ||
3e34fbdd IY |
439 | case OFPUTIL_OFPAT11_PUSH_VLAN: |
440 | ethertype = str_to_u16(arg, "ethertype"); | |
441 | if (ethertype != ETH_TYPE_VLAN_8021Q) { | |
5dca28b5 | 442 | /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ |
3e34fbdd IY |
443 | ovs_fatal(0, "%s: not a valid VLAN ethertype", arg); |
444 | } | |
445 | ofpact_put_PUSH_VLAN(ofpacts); | |
446 | break; | |
447 | ||
276c4e7a SH |
448 | case OFPUTIL_OFPAT11_SET_QUEUE: |
449 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); | |
450 | break; | |
451 | ||
452 | ||
08f94c0e | 453 | case OFPUTIL_OFPAT10_SET_DL_SRC: |
d01c980f | 454 | case OFPUTIL_OFPAT11_SET_DL_SRC: |
f25d0cf3 BP |
455 | str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); |
456 | break; | |
457 | ||
08f94c0e | 458 | case OFPUTIL_OFPAT10_SET_DL_DST: |
d01c980f | 459 | case OFPUTIL_OFPAT11_SET_DL_DST: |
f25d0cf3 | 460 | str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); |
333eba21 BP |
461 | break; |
462 | ||
08f94c0e | 463 | case OFPUTIL_OFPAT10_SET_NW_SRC: |
d01c980f | 464 | case OFPUTIL_OFPAT11_SET_NW_SRC: |
f25d0cf3 BP |
465 | str_to_ip(arg, &ip); |
466 | ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; | |
467 | break; | |
468 | ||
08f94c0e | 469 | case OFPUTIL_OFPAT10_SET_NW_DST: |
d01c980f | 470 | case OFPUTIL_OFPAT11_SET_NW_DST: |
f25d0cf3 BP |
471 | str_to_ip(arg, &ip); |
472 | ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; | |
333eba21 BP |
473 | break; |
474 | ||
08f94c0e | 475 | case OFPUTIL_OFPAT10_SET_NW_TOS: |
d01c980f | 476 | case OFPUTIL_OFPAT11_SET_NW_TOS: |
f25d0cf3 BP |
477 | tos = str_to_u32(arg); |
478 | if (tos & ~IP_DSCP_MASK) { | |
479 | ovs_fatal(0, "%s: not a valid TOS", arg); | |
480 | } | |
481 | ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; | |
333eba21 BP |
482 | break; |
483 | ||
7bcb1506 | 484 | case OFPUTIL_OFPAT11_DEC_NW_TTL: |
68194f84 | 485 | NOT_REACHED(); |
7bcb1506 | 486 | |
08f94c0e | 487 | case OFPUTIL_OFPAT10_SET_TP_SRC: |
d01c980f | 488 | case OFPUTIL_OFPAT11_SET_TP_SRC: |
f25d0cf3 BP |
489 | ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); |
490 | break; | |
491 | ||
08f94c0e | 492 | case OFPUTIL_OFPAT10_SET_TP_DST: |
d01c980f | 493 | case OFPUTIL_OFPAT11_SET_TP_DST: |
f25d0cf3 | 494 | ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); |
333eba21 BP |
495 | break; |
496 | ||
08f94c0e | 497 | case OFPUTIL_OFPAT10_ENQUEUE: |
f25d0cf3 | 498 | parse_enqueue(arg, ofpacts); |
333eba21 BP |
499 | break; |
500 | ||
501 | case OFPUTIL_NXAST_RESUBMIT: | |
f25d0cf3 | 502 | parse_resubmit(arg, ofpacts); |
333eba21 BP |
503 | break; |
504 | ||
505 | case OFPUTIL_NXAST_SET_TUNNEL: | |
f25d0cf3 BP |
506 | case OFPUTIL_NXAST_SET_TUNNEL64: |
507 | tunnel = ofpact_put_SET_TUNNEL(ofpacts); | |
508 | tunnel->ofpact.compat = code; | |
509 | tunnel->tun_id = str_to_u64(arg); | |
333eba21 BP |
510 | break; |
511 | ||
4cceacb9 JS |
512 | case OFPUTIL_NXAST_WRITE_METADATA: |
513 | parse_metadata(ofpacts, arg); | |
514 | break; | |
515 | ||
333eba21 | 516 | case OFPUTIL_NXAST_SET_QUEUE: |
f25d0cf3 | 517 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); |
333eba21 BP |
518 | break; |
519 | ||
520 | case OFPUTIL_NXAST_POP_QUEUE: | |
f25d0cf3 | 521 | ofpact_put_POP_QUEUE(ofpacts); |
333eba21 BP |
522 | break; |
523 | ||
524 | case OFPUTIL_NXAST_REG_MOVE: | |
f25d0cf3 | 525 | nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); |
333eba21 BP |
526 | break; |
527 | ||
528 | case OFPUTIL_NXAST_REG_LOAD: | |
f25d0cf3 | 529 | nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); |
333eba21 BP |
530 | break; |
531 | ||
532 | case OFPUTIL_NXAST_NOTE: | |
f25d0cf3 | 533 | parse_note(arg, ofpacts); |
93996add BP |
534 | break; |
535 | ||
333eba21 | 536 | case OFPUTIL_NXAST_MULTIPATH: |
f25d0cf3 | 537 | multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); |
333eba21 BP |
538 | break; |
539 | ||
333eba21 | 540 | case OFPUTIL_NXAST_BUNDLE: |
f25d0cf3 | 541 | bundle_parse(arg, ofpacts); |
333eba21 BP |
542 | break; |
543 | ||
544 | case OFPUTIL_NXAST_BUNDLE_LOAD: | |
f25d0cf3 | 545 | bundle_parse_load(arg, ofpacts); |
333eba21 BP |
546 | break; |
547 | ||
548 | case OFPUTIL_NXAST_RESUBMIT_TABLE: | |
549 | case OFPUTIL_NXAST_OUTPUT_REG: | |
c2d967a5 | 550 | case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: |
333eba21 | 551 | NOT_REACHED(); |
75a75043 BP |
552 | |
553 | case OFPUTIL_NXAST_LEARN: | |
f25d0cf3 | 554 | learn_parse(arg, flow, ofpacts); |
75a75043 | 555 | break; |
a61680c6 | 556 | |
848e8809 | 557 | case OFPUTIL_NXAST_EXIT: |
f25d0cf3 | 558 | ofpact_put_EXIT(ofpacts); |
848e8809 | 559 | break; |
f0fd1a17 PS |
560 | |
561 | case OFPUTIL_NXAST_DEC_TTL: | |
99086062 | 562 | parse_dec_ttl(ofpacts, arg); |
f0fd1a17 | 563 | break; |
0e553d9c | 564 | |
0f3f3c3d SH |
565 | case OFPUTIL_NXAST_SET_MPLS_TTL: |
566 | case OFPUTIL_OFPAT11_SET_MPLS_TTL: | |
567 | parse_set_mpls_ttl(ofpacts, arg); | |
568 | break; | |
569 | ||
b676167a SH |
570 | case OFPUTIL_OFPAT11_DEC_MPLS_TTL: |
571 | case OFPUTIL_NXAST_DEC_MPLS_TTL: | |
572 | ofpact_put_DEC_MPLS_TTL(ofpacts); | |
573 | break; | |
574 | ||
0e553d9c | 575 | case OFPUTIL_NXAST_FIN_TIMEOUT: |
f25d0cf3 | 576 | parse_fin_timeout(ofpacts, arg); |
0e553d9c | 577 | break; |
a7349929 BP |
578 | |
579 | case OFPUTIL_NXAST_CONTROLLER: | |
f25d0cf3 | 580 | parse_controller(ofpacts, arg); |
a7349929 | 581 | break; |
b02475c5 SH |
582 | |
583 | case OFPUTIL_OFPAT11_PUSH_MPLS: | |
584 | case OFPUTIL_NXAST_PUSH_MPLS: | |
585 | ofpact_put_PUSH_MPLS(ofpacts)->ethertype = | |
586 | htons(str_to_u16(arg, "push_mpls")); | |
587 | break; | |
588 | ||
589 | case OFPUTIL_OFPAT11_POP_MPLS: | |
590 | case OFPUTIL_NXAST_POP_MPLS: | |
591 | ofpact_put_POP_MPLS(ofpacts)->ethertype = | |
592 | htons(str_to_u16(arg, "pop_mpls")); | |
593 | break; | |
bd85dac1 AZ |
594 | case OFPUTIL_NXAST_STACK_PUSH: |
595 | nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); | |
596 | break; | |
597 | case OFPUTIL_NXAST_STACK_POP: | |
598 | nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); | |
599 | break; | |
333eba21 BP |
600 | } |
601 | } | |
602 | ||
8dd54666 IY |
603 | static bool |
604 | str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg, | |
605 | struct ofpbuf *ofpacts, int n_actions) | |
606 | { | |
607 | int code = ofputil_action_code_from_name(act); | |
608 | if (code >= 0) { | |
609 | parse_named_action(code, flow, arg, ofpacts); | |
610 | } else if (!strcasecmp(act, "drop")) { | |
611 | if (n_actions) { | |
612 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
613 | "actions"); | |
614 | } else if (ofputil_parse_key_value(&pos, &act, &arg)) { | |
615 | ovs_fatal(0, "Drop actions must not be followed by other " | |
616 | "actions"); | |
617 | } | |
618 | return false; | |
619 | } else { | |
8010100b BP |
620 | uint16_t port; |
621 | if (ofputil_port_from_string(act, &port)) { | |
8dd54666 IY |
622 | ofpact_put_OUTPUT(ofpacts)->port = port; |
623 | } else { | |
624 | ovs_fatal(0, "Unknown action: %s", act); | |
625 | } | |
626 | } | |
627 | ||
628 | return true; | |
629 | } | |
630 | ||
f22716dc | 631 | static void |
f25d0cf3 | 632 | str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) |
f22716dc | 633 | { |
0ff22822 | 634 | char *pos, *act, *arg; |
4cceacb9 | 635 | enum ofperr error; |
f22716dc JP |
636 | int n_actions; |
637 | ||
53ddd40a | 638 | pos = str; |
d13803eb | 639 | n_actions = 0; |
0ff22822 | 640 | while (ofputil_parse_key_value(&pos, &act, &arg)) { |
8dd54666 | 641 | if (!str_to_ofpact__(flow, pos, act, arg, ofpacts, n_actions)) { |
0ff22822 | 642 | break; |
8dd54666 IY |
643 | } |
644 | n_actions++; | |
645 | } | |
4cceacb9 JS |
646 | |
647 | error = ofpacts_verify(ofpacts->data, ofpacts->size); | |
648 | if (error) { | |
649 | ovs_fatal(0, "Incorrect action ordering"); | |
650 | } | |
651 | ||
8dd54666 IY |
652 | ofpact_pad(ofpacts); |
653 | } | |
654 | ||
655 | static void | |
656 | parse_named_instruction(enum ovs_instruction_type type, | |
657 | char *arg, struct ofpbuf *ofpacts) | |
658 | { | |
4cceacb9 JS |
659 | enum ofperr error; |
660 | ||
8dd54666 IY |
661 | switch (type) { |
662 | case OVSINST_OFPIT11_APPLY_ACTIONS: | |
663 | NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */ | |
664 | break; | |
665 | ||
666 | case OVSINST_OFPIT11_WRITE_ACTIONS: | |
5dca28b5 | 667 | /* XXX */ |
8dd54666 IY |
668 | ovs_fatal(0, "instruction write-actions is not supported yet"); |
669 | break; | |
670 | ||
671 | case OVSINST_OFPIT11_CLEAR_ACTIONS: | |
b19e8793 | 672 | ofpact_put_CLEAR_ACTIONS(ofpacts); |
8dd54666 IY |
673 | break; |
674 | ||
675 | case OVSINST_OFPIT11_WRITE_METADATA: | |
4cceacb9 | 676 | parse_metadata(ofpacts, arg); |
8dd54666 IY |
677 | break; |
678 | ||
679 | case OVSINST_OFPIT11_GOTO_TABLE: { | |
680 | struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); | |
681 | char *table_s = strsep(&arg, ","); | |
682 | if (!table_s || !table_s[0]) { | |
683 | ovs_fatal(0, "instruction goto-table needs table id"); | |
684 | } | |
685 | ogt->table_id = str_to_table_id(table_s); | |
686 | break; | |
687 | } | |
688 | } | |
4cceacb9 JS |
689 | |
690 | /* If write_metadata is specified as an action AND an instruction, ofpacts | |
691 | could be invalid. */ | |
692 | error = ofpacts_verify(ofpacts->data, ofpacts->size); | |
693 | if (error) { | |
694 | ovs_fatal(0, "Incorrect instruction ordering"); | |
695 | } | |
8dd54666 IY |
696 | } |
697 | ||
698 | static void | |
699 | str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) | |
700 | { | |
701 | char *pos, *inst, *arg; | |
702 | int type; | |
703 | const char *prev_inst = NULL; | |
704 | int prev_type = -1; | |
705 | int n_actions = 0; | |
706 | ||
707 | pos = str; | |
708 | while (ofputil_parse_key_value(&pos, &inst, &arg)) { | |
709 | type = ofpact_instruction_type_from_name(inst); | |
710 | if (type < 0) { | |
711 | if (!str_to_ofpact__(flow, pos, inst, arg, ofpacts, n_actions)) { | |
712 | break; | |
713 | } | |
714 | ||
715 | type = OVSINST_OFPIT11_APPLY_ACTIONS; | |
716 | if (prev_type == type) { | |
717 | n_actions++; | |
718 | continue; | |
c6100d92 | 719 | } |
8dd54666 IY |
720 | } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) { |
721 | ovs_fatal(0, "%s isn't supported. Just write actions then " | |
722 | "it is interpreted as apply_actions", inst); | |
723 | } else { | |
724 | parse_named_instruction(type, arg, ofpacts); | |
725 | } | |
726 | ||
727 | if (type == prev_type) { | |
728 | ovs_fatal(0, "instruction can be specified at most once: %s", | |
729 | inst); | |
f22716dc | 730 | } |
8dd54666 IY |
731 | if (type <= prev_type) { |
732 | ovs_fatal(0, "Instruction %s must be specified before %s", | |
733 | inst, prev_inst); | |
734 | } | |
735 | ||
736 | prev_inst = inst; | |
737 | prev_type = type; | |
d13803eb | 738 | n_actions++; |
f22716dc | 739 | } |
f25d0cf3 | 740 | ofpact_pad(ofpacts); |
f22716dc JP |
741 | } |
742 | ||
743 | struct protocol { | |
744 | const char *name; | |
745 | uint16_t dl_type; | |
746 | uint8_t nw_proto; | |
747 | }; | |
748 | ||
749 | static bool | |
750 | parse_protocol(const char *name, const struct protocol **p_out) | |
751 | { | |
752 | static const struct protocol protocols[] = { | |
753 | { "ip", ETH_TYPE_IP, 0 }, | |
754 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
755 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
756 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
757 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
758 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
759 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
760 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
761 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
762 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
8087f5ff | 763 | { "rarp", ETH_TYPE_RARP, 0}, |
b02475c5 SH |
764 | { "mpls", ETH_TYPE_MPLS, 0 }, |
765 | { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, | |
766 | }; | |
f22716dc JP |
767 | const struct protocol *p; |
768 | ||
769 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
770 | if (!strcmp(p->name, name)) { | |
771 | *p_out = p; | |
772 | return true; | |
773 | } | |
774 | } | |
775 | *p_out = NULL; | |
776 | return false; | |
777 | } | |
778 | ||
ec610b7b AE |
779 | static void |
780 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
781 | { | |
782 | va_list args; | |
783 | ||
784 | if (verbose) { | |
785 | fprintf(stderr, "%s:\n", flow); | |
786 | } | |
787 | ||
788 | va_start(args, format); | |
789 | ovs_fatal_valist(0, format, args); | |
790 | } | |
791 | ||
8050b31d | 792 | static void |
81a76618 | 793 | parse_field(const struct mf_field *mf, const char *s, struct match *match) |
8050b31d | 794 | { |
6a885fd0 BP |
795 | union mf_value value, mask; |
796 | char *error; | |
bad68a99 | 797 | |
6a885fd0 BP |
798 | error = mf_parse(mf, s, &value, &mask); |
799 | if (error) { | |
800 | ovs_fatal(0, "%s", error); | |
8050b31d | 801 | } |
8050b31d | 802 | |
81a76618 | 803 | mf_set(mf, &value, &mask, match); |
00b1c62f BP |
804 | } |
805 | ||
c821124b BP |
806 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
807 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
808 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
809 | * or reallocated. | |
810 | * | |
811 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
812 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
813 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 814 | void |
a9a2da38 BP |
815 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
816 | bool verbose) | |
f22716dc | 817 | { |
c821124b BP |
818 | enum { |
819 | F_OUT_PORT = 1 << 0, | |
820 | F_ACTIONS = 1 << 1, | |
c821124b | 821 | F_TIMEOUT = 1 << 3, |
a993007b BP |
822 | F_PRIORITY = 1 << 4, |
823 | F_FLAGS = 1 << 5, | |
c821124b | 824 | } fields; |
ec610b7b | 825 | char *string = xstrdup(str_); |
f22716dc | 826 | char *save_ptr = NULL; |
75a75043 | 827 | char *act_str = NULL; |
f22716dc | 828 | char *name; |
f22716dc | 829 | |
c821124b BP |
830 | switch (command) { |
831 | case -1: | |
832 | fields = F_OUT_PORT; | |
833 | break; | |
834 | ||
835 | case OFPFC_ADD: | |
a993007b | 836 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
837 | break; |
838 | ||
839 | case OFPFC_DELETE: | |
840 | fields = F_OUT_PORT; | |
841 | break; | |
842 | ||
843 | case OFPFC_DELETE_STRICT: | |
844 | fields = F_OUT_PORT | F_PRIORITY; | |
845 | break; | |
846 | ||
847 | case OFPFC_MODIFY: | |
a993007b | 848 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
849 | break; |
850 | ||
851 | case OFPFC_MODIFY_STRICT: | |
a993007b | 852 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
853 | break; |
854 | ||
855 | default: | |
856 | NOT_REACHED(); | |
857 | } | |
858 | ||
81a76618 BP |
859 | match_init_catchall(&fm->match); |
860 | fm->priority = OFP_DEFAULT_PRIORITY; | |
88ca35ee | 861 | fm->cookie = htonll(0); |
e729e793 | 862 | fm->cookie_mask = htonll(0); |
623e1caf JP |
863 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
864 | /* For modify, by default, don't update the cookie. */ | |
865 | fm->new_cookie = htonll(UINT64_MAX); | |
866 | } else{ | |
867 | fm->new_cookie = htonll(0); | |
868 | } | |
6c1491fb | 869 | fm->table_id = 0xff; |
c821124b | 870 | fm->command = command; |
88ca35ee BP |
871 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
872 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
873 | fm->buffer_id = UINT32_MAX; | |
7f05e7ab | 874 | fm->out_port = OFPP_ANY; |
88ca35ee | 875 | fm->flags = 0; |
c821124b | 876 | if (fields & F_ACTIONS) { |
c821124b | 877 | act_str = strstr(string, "action"); |
f22716dc | 878 | if (!act_str) { |
ec610b7b | 879 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
880 | } |
881 | *act_str = '\0'; | |
882 | ||
883 | act_str = strchr(act_str + 1, '='); | |
884 | if (!act_str) { | |
ec610b7b | 885 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
886 | } |
887 | ||
888 | act_str++; | |
f22716dc | 889 | } |
f22716dc JP |
890 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
891 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
892 | const struct protocol *p; | |
893 | ||
894 | if (parse_protocol(name, &p)) { | |
81a76618 | 895 | match_set_dl_type(&fm->match, htons(p->dl_type)); |
f22716dc | 896 | if (p->nw_proto) { |
81a76618 | 897 | match_set_nw_proto(&fm->match, p->nw_proto); |
f22716dc | 898 | } |
a993007b BP |
899 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
900 | fm->flags |= OFPFF_SEND_FLOW_REM; | |
901 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { | |
902 | fm->flags |= OFPFF_CHECK_OVERLAP; | |
2e1ae200 JR |
903 | } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { |
904 | fm->flags |= OFPFF12_RESET_COUNTS; | |
905 | } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { | |
906 | fm->flags |= OFPFF13_NO_PKT_COUNTS; | |
907 | } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { | |
908 | fm->flags |= OFPFF13_NO_BYT_COUNTS; | |
f22716dc | 909 | } else { |
f22716dc JP |
910 | char *value; |
911 | ||
912 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
913 | if (!value) { | |
ec610b7b | 914 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
915 | } |
916 | ||
6c1491fb | 917 | if (!strcmp(name, "table")) { |
c3636ffc | 918 | fm->table_id = str_to_table_id(value); |
8050b31d | 919 | } else if (!strcmp(name, "out_port")) { |
8010100b | 920 | if (!ofputil_port_from_string(name, &fm->out_port)) { |
c6100d92 BP |
921 | ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port", |
922 | name); | |
923 | } | |
c821124b | 924 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
81a76618 | 925 | fm->priority = str_to_u16(value, name); |
c821124b | 926 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
c3636ffc | 927 | fm->idle_timeout = str_to_u16(value, name); |
c821124b | 928 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
c3636ffc | 929 | fm->hard_timeout = str_to_u16(value, name); |
e729e793 JP |
930 | } else if (!strcmp(name, "cookie")) { |
931 | char *mask = strchr(value, '/'); | |
623e1caf | 932 | |
e729e793 | 933 | if (mask) { |
623e1caf | 934 | /* A mask means we're searching for a cookie. */ |
e729e793 JP |
935 | if (command == OFPFC_ADD) { |
936 | ofp_fatal(str_, verbose, "flow additions cannot use " | |
937 | "a cookie mask"); | |
938 | } | |
939 | *mask = '\0'; | |
623e1caf | 940 | fm->cookie = htonll(str_to_u64(value)); |
e729e793 JP |
941 | fm->cookie_mask = htonll(str_to_u64(mask+1)); |
942 | } else { | |
623e1caf JP |
943 | /* No mask means that the cookie is being set. */ |
944 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
945 | && command != OFPFC_MODIFY_STRICT) { | |
946 | ofp_fatal(str_, verbose, "cannot set cookie"); | |
947 | } | |
948 | fm->new_cookie = htonll(str_to_u64(value)); | |
e729e793 | 949 | } |
6a885fd0 | 950 | } else if (mf_from_name(name)) { |
81a76618 | 951 | parse_field(mf_from_name(name), value, &fm->match); |
2c6d8411 BP |
952 | } else if (!strcmp(name, "duration") |
953 | || !strcmp(name, "n_packets") | |
146356e9 JP |
954 | || !strcmp(name, "n_bytes") |
955 | || !strcmp(name, "idle_age") | |
956 | || !strcmp(name, "hard_age")) { | |
2c6d8411 BP |
957 | /* Ignore these, so that users can feed the output of |
958 | * "ovs-ofctl dump-flows" back into commands that parse | |
959 | * flows. */ | |
f22716dc | 960 | } else { |
ec610b7b | 961 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
962 | } |
963 | } | |
964 | } | |
623e1caf JP |
965 | if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX) |
966 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { | |
967 | /* On modifies without a mask, we are supposed to add a flow if | |
968 | * one does not exist. If a cookie wasn't been specified, use a | |
969 | * default of zero. */ | |
970 | fm->new_cookie = htonll(0); | |
971 | } | |
75a75043 | 972 | if (fields & F_ACTIONS) { |
f25d0cf3 | 973 | struct ofpbuf ofpacts; |
75a75043 | 974 | |
f25d0cf3 | 975 | ofpbuf_init(&ofpacts, 32); |
8dd54666 | 976 | str_to_inst_ofpacts(&fm->match.flow, act_str, &ofpacts); |
f25d0cf3 BP |
977 | fm->ofpacts_len = ofpacts.size; |
978 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); | |
75a75043 | 979 | } else { |
f25d0cf3 BP |
980 | fm->ofpacts_len = 0; |
981 | fm->ofpacts = NULL; | |
75a75043 | 982 | } |
ec610b7b AE |
983 | |
984 | free(string); | |
f22716dc | 985 | } |
15f1f1b6 | 986 | |
2b07c8b1 BP |
987 | /* Convert 'str_' (as described in the documentation for the "monitor" command |
988 | * in the ovs-ofctl man page) into 'fmr'. */ | |
989 | void | |
990 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
991 | const char *str_) | |
992 | { | |
993 | static uint32_t id; | |
994 | ||
995 | char *string = xstrdup(str_); | |
996 | char *save_ptr = NULL; | |
997 | char *name; | |
998 | ||
999 | fmr->id = id++; | |
1000 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | |
1001 | | NXFMF_OWN | NXFMF_ACTIONS); | |
1002 | fmr->out_port = OFPP_NONE; | |
1003 | fmr->table_id = 0xff; | |
81a76618 | 1004 | match_init_catchall(&fmr->match); |
2b07c8b1 BP |
1005 | |
1006 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
1007 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
1008 | const struct protocol *p; | |
1009 | ||
1010 | if (!strcmp(name, "!initial")) { | |
1011 | fmr->flags &= ~NXFMF_INITIAL; | |
1012 | } else if (!strcmp(name, "!add")) { | |
1013 | fmr->flags &= ~NXFMF_ADD; | |
1014 | } else if (!strcmp(name, "!delete")) { | |
1015 | fmr->flags &= ~NXFMF_DELETE; | |
1016 | } else if (!strcmp(name, "!modify")) { | |
1017 | fmr->flags &= ~NXFMF_MODIFY; | |
1018 | } else if (!strcmp(name, "!actions")) { | |
1019 | fmr->flags &= ~NXFMF_ACTIONS; | |
1020 | } else if (!strcmp(name, "!own")) { | |
1021 | fmr->flags &= ~NXFMF_OWN; | |
1022 | } else if (parse_protocol(name, &p)) { | |
81a76618 | 1023 | match_set_dl_type(&fmr->match, htons(p->dl_type)); |
2b07c8b1 | 1024 | if (p->nw_proto) { |
81a76618 | 1025 | match_set_nw_proto(&fmr->match, p->nw_proto); |
2b07c8b1 BP |
1026 | } |
1027 | } else { | |
1028 | char *value; | |
1029 | ||
1030 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
1031 | if (!value) { | |
1032 | ovs_fatal(0, "%s: field %s missing value", str_, name); | |
1033 | } | |
1034 | ||
1035 | if (!strcmp(name, "table")) { | |
1036 | fmr->table_id = str_to_table_id(value); | |
1037 | } else if (!strcmp(name, "out_port")) { | |
1038 | fmr->out_port = atoi(value); | |
1039 | } else if (mf_from_name(name)) { | |
1040 | parse_field(mf_from_name(name), value, &fmr->match); | |
1041 | } else { | |
1042 | ovs_fatal(0, "%s: unknown keyword %s", str_, name); | |
1043 | } | |
1044 | } | |
1045 | } | |
1046 | free(string); | |
1047 | } | |
1048 | ||
0c3d5fc8 BP |
1049 | /* Parses 's' as a set of OpenFlow actions and appends the actions to |
1050 | * 'actions'. | |
1051 | * | |
1052 | * Prints an error on stderr and aborts the program if 's' syntax is | |
1053 | * invalid. */ | |
1054 | void | |
f25d0cf3 | 1055 | parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) |
0c3d5fc8 BP |
1056 | { |
1057 | char *s = xstrdup(s_); | |
f25d0cf3 | 1058 | str_to_ofpacts(NULL, s, ofpacts); |
0c3d5fc8 BP |
1059 | free(s); |
1060 | } | |
1061 | ||
88ca35ee | 1062 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
27527aa0 | 1063 | * (one of OFPFC_*) into 'fm'. */ |
88ca35ee | 1064 | void |
27527aa0 | 1065 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
a7fc1744 | 1066 | uint16_t command, bool verbose) |
15f1f1b6 | 1067 | { |
81a76618 | 1068 | struct match match_copy; |
88ca35ee | 1069 | |
27527aa0 | 1070 | parse_ofp_str(fm, command, string, verbose); |
88ca35ee | 1071 | |
81a76618 | 1072 | /* Normalize a copy of the match. This ensures that non-normalized flows |
01b389b1 BP |
1073 | * get logged but doesn't affect what gets sent to the switch, so that the |
1074 | * switch can do whatever it likes with the flow. */ | |
81a76618 BP |
1075 | match_copy = fm->match; |
1076 | ofputil_normalize_match(&match_copy); | |
15f1f1b6 BP |
1077 | } |
1078 | ||
27527aa0 BP |
1079 | void |
1080 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, | |
1081 | struct ofputil_flow_mod **fms, size_t *n_fms) | |
15f1f1b6 | 1082 | { |
27527aa0 BP |
1083 | size_t allocated_fms; |
1084 | FILE *stream; | |
dd8101bc | 1085 | struct ds s; |
15f1f1b6 | 1086 | |
27527aa0 BP |
1087 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
1088 | if (stream == NULL) { | |
1089 | ovs_fatal(errno, "%s: open", file_name); | |
1090 | } | |
1091 | ||
1092 | allocated_fms = *n_fms; | |
dd8101bc | 1093 | ds_init(&s); |
27527aa0 BP |
1094 | while (!ds_get_preprocessed_line(&s, stream)) { |
1095 | if (*n_fms >= allocated_fms) { | |
1096 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
1097 | } | |
1098 | parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false); | |
1099 | *n_fms += 1; | |
15f1f1b6 BP |
1100 | } |
1101 | ds_destroy(&s); | |
1102 | ||
27527aa0 BP |
1103 | if (stream != stdin) { |
1104 | fclose(stream); | |
1105 | } | |
88ca35ee BP |
1106 | } |
1107 | ||
1108 | void | |
81d1ea94 | 1109 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
a7fc1744 | 1110 | bool aggregate, const char *string) |
88ca35ee | 1111 | { |
a9a2da38 | 1112 | struct ofputil_flow_mod fm; |
88ca35ee | 1113 | |
c821124b | 1114 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee | 1115 | fsr->aggregate = aggregate; |
e729e793 JP |
1116 | fsr->cookie = fm.cookie; |
1117 | fsr->cookie_mask = fm.cookie_mask; | |
81a76618 | 1118 | fsr->match = fm.match; |
88ca35ee | 1119 | fsr->out_port = fm.out_port; |
6c1491fb | 1120 | fsr->table_id = fm.table_id; |
15f1f1b6 | 1121 | } |
ccbe50f8 BP |
1122 | |
1123 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
1124 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
1125 | * mf_field. Fields must be specified in a natural order for satisfying | |
1126 | * prerequisites. | |
1127 | * | |
1128 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
1129 | * problem. */ | |
1130 | char * | |
1131 | parse_ofp_exact_flow(struct flow *flow, const char *s) | |
1132 | { | |
1133 | char *pos, *key, *value_s; | |
1134 | char *error = NULL; | |
1135 | char *copy; | |
1136 | ||
1137 | memset(flow, 0, sizeof *flow); | |
1138 | ||
1139 | pos = copy = xstrdup(s); | |
1140 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
1141 | const struct protocol *p; | |
1142 | if (parse_protocol(key, &p)) { | |
1143 | if (flow->dl_type) { | |
1144 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
1145 | goto exit; | |
1146 | } | |
1147 | flow->dl_type = htons(p->dl_type); | |
1148 | ||
1149 | if (p->nw_proto) { | |
1150 | if (flow->nw_proto) { | |
1151 | error = xasprintf("%s: network protocol set " | |
1152 | "multiple times", s); | |
1153 | goto exit; | |
1154 | } | |
1155 | flow->nw_proto = p->nw_proto; | |
1156 | } | |
1157 | } else { | |
1158 | const struct mf_field *mf; | |
1159 | union mf_value value; | |
1160 | char *field_error; | |
1161 | ||
1162 | mf = mf_from_name(key); | |
1163 | if (!mf) { | |
1164 | error = xasprintf("%s: unknown field %s", s, key); | |
1165 | goto exit; | |
1166 | } | |
1167 | ||
1168 | if (!mf_are_prereqs_ok(mf, flow)) { | |
1169 | error = xasprintf("%s: prerequisites not met for setting %s", | |
1170 | s, key); | |
1171 | goto exit; | |
1172 | } | |
1173 | ||
1174 | if (!mf_is_zero(mf, flow)) { | |
1175 | error = xasprintf("%s: field %s set multiple times", s, key); | |
1176 | goto exit; | |
1177 | } | |
1178 | ||
1179 | field_error = mf_parse_value(mf, value_s, &value); | |
1180 | if (field_error) { | |
1181 | error = xasprintf("%s: bad value for %s (%s)", | |
1182 | s, key, field_error); | |
1183 | free(field_error); | |
1184 | goto exit; | |
1185 | } | |
1186 | ||
1187 | mf_set_flow_value(mf, &value, flow); | |
1188 | } | |
1189 | } | |
1190 | ||
72d64e33 EJ |
1191 | if (!flow->in_port) { |
1192 | flow->in_port = OFPP_NONE; | |
1193 | } | |
1194 | ||
ccbe50f8 BP |
1195 | exit: |
1196 | free(copy); | |
1197 | ||
1198 | if (error) { | |
1199 | memset(flow, 0, sizeof *flow); | |
1200 | } | |
1201 | return error; | |
1202 | } |