]>
Commit | Line | Data |
---|---|---|
f22716dc | 1 | /* |
8368c090 | 2 | * Copyright (c) 2010, 2011 Nicira Networks. |
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 | ||
3b6a2571 | 25 | #include "autopath.h" |
daff3353 | 26 | #include "bundle.h" |
10a24935 | 27 | #include "byte-order.h" |
15f1f1b6 | 28 | #include "dynamic-string.h" |
f22716dc | 29 | #include "netdev.h" |
53ddd40a | 30 | #include "multipath.h" |
f393f81e | 31 | #include "nx-match.h" |
f22716dc JP |
32 | #include "ofp-util.h" |
33 | #include "ofpbuf.h" | |
34 | #include "openflow/openflow.h" | |
35 | #include "packets.h" | |
36 | #include "socket-util.h" | |
37 | #include "vconn.h" | |
38 | #include "vlog.h" | |
f22716dc | 39 | |
d98e6007 | 40 | VLOG_DEFINE_THIS_MODULE(ofp_parse); |
f22716dc | 41 | |
f22716dc JP |
42 | static uint32_t |
43 | str_to_u32(const char *str) | |
44 | { | |
45 | char *tail; | |
46 | uint32_t value; | |
47 | ||
c4894ed4 | 48 | if (!str[0]) { |
ce5452cf EJ |
49 | ovs_fatal(0, "missing required numeric argument"); |
50 | } | |
51 | ||
f22716dc JP |
52 | errno = 0; |
53 | value = strtoul(str, &tail, 0); | |
54 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
55 | ovs_fatal(0, "invalid numeric format %s", str); | |
56 | } | |
57 | return value; | |
58 | } | |
59 | ||
60 | static uint64_t | |
61 | str_to_u64(const char *str) | |
62 | { | |
63 | char *tail; | |
64 | uint64_t value; | |
65 | ||
c4894ed4 BP |
66 | if (!str[0]) { |
67 | ovs_fatal(0, "missing required numeric argument"); | |
68 | } | |
69 | ||
f22716dc JP |
70 | errno = 0; |
71 | value = strtoull(str, &tail, 0); | |
72 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
73 | ovs_fatal(0, "invalid numeric format %s", str); | |
74 | } | |
75 | return value; | |
76 | } | |
77 | ||
78 | static void | |
79 | str_to_mac(const char *str, uint8_t mac[6]) | |
80 | { | |
81 | if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) | |
82 | != ETH_ADDR_SCAN_COUNT) { | |
83 | ovs_fatal(0, "invalid mac address %s", str); | |
84 | } | |
85 | } | |
86 | ||
cb8ca532 BP |
87 | static void |
88 | str_to_eth_dst(const char *str, | |
89 | uint8_t mac[ETH_ADDR_LEN], uint8_t mask[ETH_ADDR_LEN]) | |
90 | { | |
91 | if (sscanf(str, ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT, | |
92 | ETH_ADDR_SCAN_ARGS(mac), ETH_ADDR_SCAN_ARGS(mask)) | |
93 | == ETH_ADDR_SCAN_COUNT * 2) { | |
94 | if (!flow_wildcards_is_dl_dst_mask_valid(mask)) { | |
95 | ovs_fatal(0, "%s: invalid Ethernet destination mask (only " | |
96 | "00:00:00:00:00:00, 01:00:00:00:00:00, " | |
97 | "fe:ff:ff:ff:ff:ff, and ff:ff:ff:ff:ff:ff are allowed)", | |
98 | str); | |
99 | } | |
100 | } else if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) | |
101 | == ETH_ADDR_SCAN_COUNT) { | |
102 | memset(mask, 0xff, ETH_ADDR_LEN); | |
103 | } else { | |
104 | ovs_fatal(0, "invalid mac address %s", str); | |
105 | } | |
106 | } | |
107 | ||
8050b31d BP |
108 | static void |
109 | str_to_ip(const char *str_, ovs_be32 *ip, ovs_be32 *maskp) | |
f22716dc JP |
110 | { |
111 | char *str = xstrdup(str_); | |
112 | char *save_ptr = NULL; | |
113 | const char *name, *netmask; | |
114 | struct in_addr in_addr; | |
8050b31d BP |
115 | ovs_be32 mask; |
116 | int retval; | |
f22716dc JP |
117 | |
118 | name = strtok_r(str, "/", &save_ptr); | |
119 | retval = name ? lookup_ip(name, &in_addr) : EINVAL; | |
120 | if (retval) { | |
121 | ovs_fatal(0, "%s: could not convert to IP address", str); | |
122 | } | |
123 | *ip = in_addr.s_addr; | |
124 | ||
125 | netmask = strtok_r(NULL, "/", &save_ptr); | |
126 | if (netmask) { | |
127 | uint8_t o[4]; | |
128 | if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8, | |
129 | &o[0], &o[1], &o[2], &o[3]) == 4) { | |
8050b31d | 130 | mask = htonl((o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3]); |
f22716dc JP |
131 | } else { |
132 | int prefix = atoi(netmask); | |
133 | if (prefix <= 0 || prefix > 32) { | |
134 | ovs_fatal(0, "%s: network prefix bits not between 1 and 32", | |
135 | str); | |
8050b31d BP |
136 | } else if (prefix == 32) { |
137 | mask = htonl(UINT32_MAX); | |
138 | } else { | |
139 | mask = htonl(((1u << prefix) - 1) << (32 - prefix)); | |
f22716dc | 140 | } |
f22716dc JP |
141 | } |
142 | } else { | |
8050b31d BP |
143 | mask = htonl(UINT32_MAX); |
144 | } | |
145 | *ip &= mask; | |
146 | ||
147 | if (maskp) { | |
148 | *maskp = mask; | |
149 | } else { | |
150 | if (mask != htonl(UINT32_MAX)) { | |
151 | ovs_fatal(0, "%s: netmask not allowed here", str_); | |
152 | } | |
f22716dc JP |
153 | } |
154 | ||
155 | free(str); | |
f22716dc JP |
156 | } |
157 | ||
8368c090 BP |
158 | static void |
159 | str_to_tun_id(const char *str, ovs_be64 *tun_idp, ovs_be64 *maskp) | |
160 | { | |
161 | uint64_t tun_id, mask; | |
162 | char *tail; | |
163 | ||
164 | errno = 0; | |
165 | tun_id = strtoull(str, &tail, 0); | |
166 | if (errno || (*tail != '\0' && *tail != '/')) { | |
167 | goto error; | |
168 | } | |
169 | ||
170 | if (*tail == '/') { | |
171 | mask = strtoull(tail + 1, &tail, 0); | |
172 | if (errno || *tail != '\0') { | |
173 | goto error; | |
174 | } | |
175 | } else { | |
176 | mask = UINT64_MAX; | |
177 | } | |
178 | ||
179 | *tun_idp = htonll(tun_id); | |
180 | *maskp = htonll(mask); | |
181 | return; | |
182 | ||
183 | error: | |
184 | ovs_fatal(0, "%s: bad syntax for tunnel id", str); | |
185 | } | |
186 | ||
33d8c6b4 BP |
187 | static void |
188 | str_to_vlan_tci(const char *str, ovs_be16 *vlan_tcip, ovs_be16 *maskp) | |
189 | { | |
190 | uint16_t vlan_tci, mask; | |
191 | char *tail; | |
192 | ||
193 | errno = 0; | |
194 | vlan_tci = strtol(str, &tail, 0); | |
195 | if (errno || (*tail != '\0' && *tail != '/')) { | |
196 | goto error; | |
197 | } | |
198 | ||
199 | if (*tail == '/') { | |
200 | mask = strtol(tail + 1, &tail, 0); | |
201 | if (errno || *tail != '\0') { | |
202 | goto error; | |
203 | } | |
204 | } else { | |
205 | mask = UINT16_MAX; | |
206 | } | |
207 | ||
208 | *vlan_tcip = htons(vlan_tci); | |
209 | *maskp = htons(mask); | |
210 | return; | |
211 | ||
212 | error: | |
213 | ovs_fatal(0, "%s: bad syntax for vlan_tci", str); | |
214 | } | |
215 | ||
d31f1109 JP |
216 | static void |
217 | str_to_ipv6(const char *str_, struct in6_addr *addrp, struct in6_addr *maskp) | |
218 | { | |
219 | char *str = xstrdup(str_); | |
220 | char *save_ptr = NULL; | |
221 | const char *name, *netmask; | |
222 | struct in6_addr addr, mask; | |
223 | int retval; | |
224 | ||
225 | name = strtok_r(str, "/", &save_ptr); | |
226 | retval = name ? lookup_ipv6(name, &addr) : EINVAL; | |
227 | if (retval) { | |
228 | ovs_fatal(0, "%s: could not convert to IPv6 address", str); | |
229 | } | |
230 | ||
231 | netmask = strtok_r(NULL, "/", &save_ptr); | |
232 | if (netmask) { | |
233 | int prefix = atoi(netmask); | |
234 | if (prefix <= 0 || prefix > 128) { | |
235 | ovs_fatal(0, "%s: network prefix bits not between 1 and 128", | |
236 | str); | |
237 | } else { | |
238 | mask = ipv6_create_mask(prefix); | |
239 | } | |
240 | } else { | |
241 | mask = in6addr_exact; | |
242 | } | |
243 | *addrp = ipv6_addr_bitand(&addr, &mask); | |
244 | ||
245 | if (maskp) { | |
246 | *maskp = mask; | |
247 | } else { | |
248 | if (!ipv6_mask_is_exact(&mask)) { | |
249 | ovs_fatal(0, "%s: netmask not allowed here", str_); | |
250 | } | |
251 | } | |
252 | ||
253 | free(str); | |
254 | } | |
255 | ||
f22716dc JP |
256 | static void * |
257 | put_action(struct ofpbuf *b, size_t size, uint16_t type) | |
258 | { | |
259 | struct ofp_action_header *ah = ofpbuf_put_zeros(b, size); | |
260 | ah->type = htons(type); | |
261 | ah->len = htons(size); | |
262 | return ah; | |
263 | } | |
264 | ||
265 | static struct ofp_action_output * | |
266 | put_output_action(struct ofpbuf *b, uint16_t port) | |
267 | { | |
268 | struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT); | |
269 | oao->port = htons(port); | |
270 | return oao; | |
271 | } | |
272 | ||
5682f723 BP |
273 | static void |
274 | put_enqueue_action(struct ofpbuf *b, uint16_t port, uint32_t queue) | |
275 | { | |
276 | struct ofp_action_enqueue *oae = put_action(b, sizeof *oae, OFPAT_ENQUEUE); | |
277 | oae->port = htons(port); | |
278 | oae->queue_id = htonl(queue); | |
279 | } | |
280 | ||
f22716dc JP |
281 | static void |
282 | put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr) | |
283 | { | |
284 | struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type); | |
285 | str_to_mac(addr, oada->dl_addr); | |
286 | } | |
287 | ||
f22716dc JP |
288 | static bool |
289 | parse_port_name(const char *name, uint16_t *port) | |
290 | { | |
291 | struct pair { | |
292 | const char *name; | |
293 | uint16_t value; | |
294 | }; | |
295 | static const struct pair pairs[] = { | |
296 | #define DEF_PAIR(NAME) {#NAME, OFPP_##NAME} | |
297 | DEF_PAIR(IN_PORT), | |
298 | DEF_PAIR(TABLE), | |
299 | DEF_PAIR(NORMAL), | |
300 | DEF_PAIR(FLOOD), | |
301 | DEF_PAIR(ALL), | |
302 | DEF_PAIR(CONTROLLER), | |
303 | DEF_PAIR(LOCAL), | |
304 | DEF_PAIR(NONE), | |
305 | #undef DEF_PAIR | |
306 | }; | |
307 | static const int n_pairs = ARRAY_SIZE(pairs); | |
308 | size_t i; | |
309 | ||
310 | for (i = 0; i < n_pairs; i++) { | |
311 | if (!strcasecmp(name, pairs[i].name)) { | |
312 | *port = pairs[i].value; | |
313 | return true; | |
314 | } | |
315 | } | |
316 | return false; | |
317 | } | |
318 | ||
29901626 BP |
319 | static void |
320 | parse_resubmit(struct nx_action_resubmit *nar, char *arg) | |
321 | { | |
322 | char *in_port_s, *table_s; | |
323 | uint16_t in_port; | |
324 | uint8_t table; | |
325 | ||
326 | in_port_s = strsep(&arg, ","); | |
327 | if (in_port_s && in_port_s[0]) { | |
328 | if (!parse_port_name(in_port_s, &in_port)) { | |
329 | in_port = str_to_u32(in_port_s); | |
330 | } | |
331 | } else { | |
332 | in_port = OFPP_IN_PORT; | |
333 | } | |
334 | ||
335 | table_s = strsep(&arg, ","); | |
336 | table = table_s && table_s[0] ? str_to_u32(table_s) : 255; | |
337 | ||
338 | if (in_port == OFPP_IN_PORT && table == 255) { | |
339 | ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " | |
340 | " on resubmit"); | |
341 | } | |
342 | ||
343 | nar->vendor = htonl(NX_VENDOR_ID); | |
344 | nar->in_port = htons(in_port); | |
345 | if (in_port != OFPP_IN_PORT && table == 255) { | |
346 | nar->subtype = htons(NXAST_RESUBMIT); | |
347 | } else { | |
348 | nar->subtype = htons(NXAST_RESUBMIT_TABLE); | |
349 | nar->table = table; | |
350 | } | |
351 | } | |
352 | ||
f22716dc JP |
353 | static void |
354 | str_to_action(char *str, struct ofpbuf *b) | |
355 | { | |
f22716dc JP |
356 | bool drop = false; |
357 | int n_actions; | |
53ddd40a | 358 | char *pos; |
f22716dc | 359 | |
53ddd40a | 360 | pos = str; |
d13803eb | 361 | n_actions = 0; |
53ddd40a | 362 | for (;;) { |
c4894ed4 | 363 | char empty_string[] = ""; |
53ddd40a BP |
364 | char *act, *arg; |
365 | size_t actlen; | |
f22716dc JP |
366 | uint16_t port; |
367 | ||
53ddd40a BP |
368 | pos += strspn(pos, ", \t\r\n"); |
369 | if (*pos == '\0') { | |
370 | break; | |
371 | } | |
372 | ||
f22716dc JP |
373 | if (drop) { |
374 | ovs_fatal(0, "Drop actions must not be followed by other actions"); | |
375 | } | |
376 | ||
53ddd40a BP |
377 | act = pos; |
378 | actlen = strcspn(pos, ":(, \t\r\n"); | |
379 | if (act[actlen] == ':') { | |
380 | /* The argument can be separated by a colon. */ | |
381 | size_t arglen; | |
382 | ||
383 | arg = act + actlen + 1; | |
384 | arglen = strcspn(arg, ", \t\r\n"); | |
385 | pos = arg + arglen + (arg[arglen] != '\0'); | |
386 | arg[arglen] = '\0'; | |
387 | } else if (act[actlen] == '(') { | |
388 | /* The argument can be surrounded by balanced parentheses. The | |
389 | * outermost set of parentheses is removed. */ | |
390 | int level = 1; | |
391 | size_t arglen; | |
392 | ||
393 | arg = act + actlen + 1; | |
394 | for (arglen = 0; level > 0; arglen++) { | |
395 | switch (arg[arglen]) { | |
396 | case '\0': | |
397 | ovs_fatal(0, "unbalanced parentheses in argument to %s " | |
398 | "action", act); | |
399 | ||
400 | case '(': | |
401 | level++; | |
402 | break; | |
403 | ||
404 | case ')': | |
405 | level--; | |
406 | break; | |
407 | } | |
408 | } | |
409 | arg[arglen - 1] = '\0'; | |
410 | pos = arg + arglen; | |
411 | } else { | |
412 | /* There might be no argument at all. */ | |
c4894ed4 | 413 | arg = empty_string; |
53ddd40a | 414 | pos = act + actlen + (act[actlen] != '\0'); |
f22716dc | 415 | } |
53ddd40a | 416 | act[actlen] = '\0'; |
f22716dc JP |
417 | |
418 | if (!strcasecmp(act, "mod_vlan_vid")) { | |
419 | struct ofp_action_vlan_vid *va; | |
420 | va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID); | |
421 | va->vlan_vid = htons(str_to_u32(arg)); | |
422 | } else if (!strcasecmp(act, "mod_vlan_pcp")) { | |
423 | struct ofp_action_vlan_pcp *va; | |
424 | va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP); | |
425 | va->vlan_pcp = str_to_u32(arg); | |
426 | } else if (!strcasecmp(act, "strip_vlan")) { | |
427 | struct ofp_action_header *ah; | |
428 | ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN); | |
429 | ah->type = htons(OFPAT_STRIP_VLAN); | |
430 | } else if (!strcasecmp(act, "mod_dl_src")) { | |
431 | put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg); | |
432 | } else if (!strcasecmp(act, "mod_dl_dst")) { | |
433 | put_dl_addr_action(b, OFPAT_SET_DL_DST, arg); | |
434 | } else if (!strcasecmp(act, "mod_nw_src")) { | |
435 | struct ofp_action_nw_addr *na; | |
436 | na = put_action(b, sizeof *na, OFPAT_SET_NW_SRC); | |
8050b31d | 437 | str_to_ip(arg, &na->nw_addr, NULL); |
f22716dc JP |
438 | } else if (!strcasecmp(act, "mod_nw_dst")) { |
439 | struct ofp_action_nw_addr *na; | |
440 | na = put_action(b, sizeof *na, OFPAT_SET_NW_DST); | |
8050b31d | 441 | str_to_ip(arg, &na->nw_addr, NULL); |
f22716dc JP |
442 | } else if (!strcasecmp(act, "mod_tp_src")) { |
443 | struct ofp_action_tp_port *ta; | |
444 | ta = put_action(b, sizeof *ta, OFPAT_SET_TP_SRC); | |
445 | ta->tp_port = htons(str_to_u32(arg)); | |
446 | } else if (!strcasecmp(act, "mod_tp_dst")) { | |
447 | struct ofp_action_tp_port *ta; | |
448 | ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST); | |
449 | ta->tp_port = htons(str_to_u32(arg)); | |
450 | } else if (!strcasecmp(act, "mod_nw_tos")) { | |
451 | struct ofp_action_nw_tos *nt; | |
452 | nt = put_action(b, sizeof *nt, OFPAT_SET_NW_TOS); | |
453 | nt->nw_tos = str_to_u32(arg); | |
454 | } else if (!strcasecmp(act, "resubmit")) { | |
455 | struct nx_action_resubmit *nar; | |
456 | nar = put_action(b, sizeof *nar, OFPAT_VENDOR); | |
29901626 | 457 | parse_resubmit(nar, arg); |
b9298d3f BP |
458 | } else if (!strcasecmp(act, "set_tunnel") |
459 | || !strcasecmp(act, "set_tunnel64")) { | |
460 | uint64_t tun_id = str_to_u64(arg); | |
461 | if (!strcasecmp(act, "set_tunnel64") || tun_id > UINT32_MAX) { | |
462 | struct nx_action_set_tunnel64 *nast64; | |
463 | nast64 = put_action(b, sizeof *nast64, OFPAT_VENDOR); | |
464 | nast64->vendor = htonl(NX_VENDOR_ID); | |
465 | nast64->subtype = htons(NXAST_SET_TUNNEL64); | |
466 | nast64->tun_id = htonll(tun_id); | |
467 | } else { | |
468 | struct nx_action_set_tunnel *nast; | |
469 | nast = put_action(b, sizeof *nast, OFPAT_VENDOR); | |
470 | nast->vendor = htonl(NX_VENDOR_ID); | |
471 | nast->subtype = htons(NXAST_SET_TUNNEL); | |
472 | nast->tun_id = htonl(tun_id); | |
473 | } | |
eedc0097 JP |
474 | } else if (!strcasecmp(act, "set_queue")) { |
475 | struct nx_action_set_queue *nasq; | |
476 | nasq = put_action(b, sizeof *nasq, OFPAT_VENDOR); | |
477 | nasq->vendor = htonl(NX_VENDOR_ID); | |
478 | nasq->subtype = htons(NXAST_SET_QUEUE); | |
479 | nasq->queue_id = htonl(str_to_u32(arg)); | |
480 | } else if (!strcasecmp(act, "pop_queue")) { | |
481 | struct nx_action_header *nah; | |
482 | nah = put_action(b, sizeof *nah, OFPAT_VENDOR); | |
483 | nah->vendor = htonl(NX_VENDOR_ID); | |
484 | nah->subtype = htons(NXAST_POP_QUEUE); | |
96fc46e8 BP |
485 | } else if (!strcasecmp(act, "note")) { |
486 | size_t start_ofs = b->size; | |
487 | struct nx_action_note *nan; | |
488 | int remainder; | |
489 | size_t len; | |
490 | ||
491 | nan = put_action(b, sizeof *nan, OFPAT_VENDOR); | |
492 | nan->vendor = htonl(NX_VENDOR_ID); | |
493 | nan->subtype = htons(NXAST_NOTE); | |
494 | ||
495 | b->size -= sizeof nan->note; | |
c4894ed4 | 496 | while (*arg != '\0') { |
96fc46e8 | 497 | uint8_t byte; |
bf971267 | 498 | bool ok; |
96fc46e8 BP |
499 | |
500 | if (*arg == '.') { | |
501 | arg++; | |
502 | } | |
503 | if (*arg == '\0') { | |
504 | break; | |
505 | } | |
506 | ||
bf971267 BP |
507 | byte = hexits_value(arg, 2, &ok); |
508 | if (!ok) { | |
96fc46e8 BP |
509 | ovs_fatal(0, "bad hex digit in `note' argument"); |
510 | } | |
96fc46e8 | 511 | ofpbuf_put(b, &byte, 1); |
bf971267 BP |
512 | |
513 | arg += 2; | |
96fc46e8 BP |
514 | } |
515 | ||
516 | len = b->size - start_ofs; | |
517 | remainder = len % OFP_ACTION_ALIGN; | |
518 | if (remainder) { | |
519 | ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder); | |
520 | } | |
fb05ba46 | 521 | nan = (struct nx_action_note *)((char *)b->data + start_ofs); |
96fc46e8 | 522 | nan->len = htons(b->size - start_ofs); |
f393f81e BP |
523 | } else if (!strcasecmp(act, "move")) { |
524 | struct nx_action_reg_move *move; | |
525 | move = ofpbuf_put_uninit(b, sizeof *move); | |
526 | nxm_parse_reg_move(move, arg); | |
527 | } else if (!strcasecmp(act, "load")) { | |
528 | struct nx_action_reg_load *load; | |
529 | load = ofpbuf_put_uninit(b, sizeof *load); | |
530 | nxm_parse_reg_load(load, arg); | |
53ddd40a BP |
531 | } else if (!strcasecmp(act, "multipath")) { |
532 | struct nx_action_multipath *nam; | |
533 | nam = ofpbuf_put_uninit(b, sizeof *nam); | |
534 | multipath_parse(nam, arg); | |
3b6a2571 EJ |
535 | } else if (!strcasecmp(act, "autopath")) { |
536 | struct nx_action_autopath *naa; | |
537 | naa = ofpbuf_put_uninit(b, sizeof *naa); | |
538 | autopath_parse(naa, arg); | |
daff3353 EJ |
539 | } else if (!strcasecmp(act, "bundle")) { |
540 | bundle_parse(b, arg); | |
a368bb53 EJ |
541 | } else if (!strcasecmp(act, "bundle_load")) { |
542 | bundle_parse_load(b, arg); | |
f22716dc JP |
543 | } else if (!strcasecmp(act, "output")) { |
544 | put_output_action(b, str_to_u32(arg)); | |
5682f723 BP |
545 | } else if (!strcasecmp(act, "enqueue")) { |
546 | char *sp = NULL; | |
2a022368 | 547 | char *port_s = strtok_r(arg, ":q", &sp); |
5682f723 | 548 | char *queue = strtok_r(NULL, "", &sp); |
2a022368 | 549 | if (port_s == NULL || queue == NULL) { |
5682f723 BP |
550 | ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); |
551 | } | |
2a022368 | 552 | put_enqueue_action(b, str_to_u32(port_s), str_to_u32(queue)); |
f22716dc JP |
553 | } else if (!strcasecmp(act, "drop")) { |
554 | /* A drop action in OpenFlow occurs by just not setting | |
555 | * an action. */ | |
556 | drop = true; | |
557 | if (n_actions) { | |
558 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
559 | "actions"); | |
560 | } | |
561 | } else if (!strcasecmp(act, "CONTROLLER")) { | |
562 | struct ofp_action_output *oao; | |
563 | oao = put_output_action(b, OFPP_CONTROLLER); | |
564 | ||
565 | /* Unless a numeric argument is specified, we send the whole | |
566 | * packet to the controller. */ | |
c4894ed4 | 567 | if (arg[0] && (strspn(arg, "0123456789") == strlen(arg))) { |
f22716dc JP |
568 | oao->max_len = htons(str_to_u32(arg)); |
569 | } else { | |
570 | oao->max_len = htons(UINT16_MAX); | |
571 | } | |
572 | } else if (parse_port_name(act, &port)) { | |
573 | put_output_action(b, port); | |
574 | } else if (strspn(act, "0123456789") == strlen(act)) { | |
575 | put_output_action(b, str_to_u32(act)); | |
576 | } else { | |
577 | ovs_fatal(0, "Unknown action: %s", act); | |
578 | } | |
d13803eb | 579 | n_actions++; |
f22716dc JP |
580 | } |
581 | } | |
582 | ||
583 | struct protocol { | |
584 | const char *name; | |
585 | uint16_t dl_type; | |
586 | uint8_t nw_proto; | |
587 | }; | |
588 | ||
589 | static bool | |
590 | parse_protocol(const char *name, const struct protocol **p_out) | |
591 | { | |
592 | static const struct protocol protocols[] = { | |
593 | { "ip", ETH_TYPE_IP, 0 }, | |
594 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
595 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
596 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
597 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
598 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
599 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
600 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
601 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
602 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
f22716dc JP |
603 | }; |
604 | const struct protocol *p; | |
605 | ||
606 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
607 | if (!strcmp(p->name, name)) { | |
608 | *p_out = p; | |
609 | return true; | |
610 | } | |
611 | } | |
612 | *p_out = NULL; | |
613 | return false; | |
614 | } | |
615 | ||
8050b31d | 616 | #define FIELDS \ |
8368c090 | 617 | FIELD(F_TUN_ID, "tun_id", 0) \ |
d8ae4d67 | 618 | FIELD(F_IN_PORT, "in_port", FWW_IN_PORT) \ |
66642cb4 BP |
619 | FIELD(F_DL_VLAN, "dl_vlan", 0) \ |
620 | FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", 0) \ | |
33d8c6b4 | 621 | FIELD(F_VLAN_TCI, "vlan_tci", 0) \ |
d8ae4d67 | 622 | FIELD(F_DL_SRC, "dl_src", FWW_DL_SRC) \ |
009853d8 | 623 | FIELD(F_DL_DST, "dl_dst", FWW_DL_DST | FWW_ETH_MCAST) \ |
d8ae4d67 | 624 | FIELD(F_DL_TYPE, "dl_type", FWW_DL_TYPE) \ |
8050b31d BP |
625 | FIELD(F_NW_SRC, "nw_src", 0) \ |
626 | FIELD(F_NW_DST, "nw_dst", 0) \ | |
d8ae4d67 BP |
627 | FIELD(F_NW_PROTO, "nw_proto", FWW_NW_PROTO) \ |
628 | FIELD(F_NW_TOS, "nw_tos", FWW_NW_TOS) \ | |
629 | FIELD(F_TP_SRC, "tp_src", FWW_TP_SRC) \ | |
630 | FIELD(F_TP_DST, "tp_dst", FWW_TP_DST) \ | |
631 | FIELD(F_ICMP_TYPE, "icmp_type", FWW_TP_SRC) \ | |
bad68a99 JP |
632 | FIELD(F_ICMP_CODE, "icmp_code", FWW_TP_DST) \ |
633 | FIELD(F_ARP_SHA, "arp_sha", FWW_ARP_SHA) \ | |
d31f1109 JP |
634 | FIELD(F_ARP_THA, "arp_tha", FWW_ARP_THA) \ |
635 | FIELD(F_IPV6_SRC, "ipv6_src", 0) \ | |
685a51a5 JP |
636 | FIELD(F_IPV6_DST, "ipv6_dst", 0) \ |
637 | FIELD(F_ND_TARGET, "nd_target", FWW_ND_TARGET) \ | |
638 | FIELD(F_ND_SLL, "nd_sll", FWW_ARP_SHA) \ | |
639 | FIELD(F_ND_TLL, "nd_tll", FWW_ARP_THA) | |
8050b31d BP |
640 | |
641 | enum field_index { | |
642 | #define FIELD(ENUM, NAME, WILDCARD) ENUM, | |
643 | FIELDS | |
644 | #undef FIELD | |
645 | N_FIELDS | |
646 | }; | |
647 | ||
f22716dc | 648 | struct field { |
8050b31d | 649 | enum field_index index; |
f22716dc | 650 | const char *name; |
d8ae4d67 | 651 | flow_wildcards_t wildcard; /* FWW_* bit. */ |
f22716dc JP |
652 | }; |
653 | ||
ec610b7b AE |
654 | static void |
655 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
656 | { | |
657 | va_list args; | |
658 | ||
659 | if (verbose) { | |
660 | fprintf(stderr, "%s:\n", flow); | |
661 | } | |
662 | ||
663 | va_start(args, format); | |
664 | ovs_fatal_valist(0, format, args); | |
665 | } | |
666 | ||
f22716dc | 667 | static bool |
8050b31d | 668 | parse_field_name(const char *name, const struct field **f_out) |
f22716dc | 669 | { |
8050b31d BP |
670 | static const struct field fields[N_FIELDS] = { |
671 | #define FIELD(ENUM, NAME, WILDCARD) { ENUM, NAME, WILDCARD }, | |
672 | FIELDS | |
673 | #undef FIELD | |
f22716dc JP |
674 | }; |
675 | const struct field *f; | |
676 | ||
677 | for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) { | |
678 | if (!strcmp(f->name, name)) { | |
679 | *f_out = f; | |
680 | return true; | |
681 | } | |
682 | } | |
683 | *f_out = NULL; | |
684 | return false; | |
685 | } | |
686 | ||
8050b31d BP |
687 | static void |
688 | parse_field_value(struct cls_rule *rule, enum field_index index, | |
689 | const char *value) | |
690 | { | |
cb8ca532 | 691 | uint8_t mac[ETH_ADDR_LEN], mac_mask[ETH_ADDR_LEN]; |
8368c090 | 692 | ovs_be64 tun_id, tun_mask; |
8050b31d | 693 | ovs_be32 ip, mask; |
33d8c6b4 | 694 | ovs_be16 tci, tci_mask; |
d31f1109 | 695 | struct in6_addr ipv6, ipv6_mask; |
8050b31d BP |
696 | uint16_t port_no; |
697 | ||
698 | switch (index) { | |
4c5df7f7 | 699 | case F_TUN_ID: |
8368c090 BP |
700 | str_to_tun_id(value, &tun_id, &tun_mask); |
701 | cls_rule_set_tun_id_masked(rule, tun_id, tun_mask); | |
4c5df7f7 BP |
702 | break; |
703 | ||
8050b31d BP |
704 | case F_IN_PORT: |
705 | if (!parse_port_name(value, &port_no)) { | |
706 | port_no = atoi(value); | |
707 | } | |
8050b31d BP |
708 | cls_rule_set_in_port(rule, port_no); |
709 | break; | |
710 | ||
711 | case F_DL_VLAN: | |
712 | cls_rule_set_dl_vlan(rule, htons(str_to_u32(value))); | |
713 | break; | |
714 | ||
715 | case F_DL_VLAN_PCP: | |
716 | cls_rule_set_dl_vlan_pcp(rule, str_to_u32(value)); | |
717 | break; | |
718 | ||
33d8c6b4 BP |
719 | case F_VLAN_TCI: |
720 | str_to_vlan_tci(value, &tci, &tci_mask); | |
721 | cls_rule_set_dl_tci_masked(rule, tci, tci_mask); | |
722 | break; | |
723 | ||
8050b31d BP |
724 | case F_DL_SRC: |
725 | str_to_mac(value, mac); | |
726 | cls_rule_set_dl_src(rule, mac); | |
727 | break; | |
728 | ||
729 | case F_DL_DST: | |
cb8ca532 BP |
730 | str_to_eth_dst(value, mac, mac_mask); |
731 | cls_rule_set_dl_dst_masked(rule, mac, mac_mask); | |
8050b31d BP |
732 | break; |
733 | ||
734 | case F_DL_TYPE: | |
735 | cls_rule_set_dl_type(rule, htons(str_to_u32(value))); | |
736 | break; | |
737 | ||
738 | case F_NW_SRC: | |
739 | str_to_ip(value, &ip, &mask); | |
740 | cls_rule_set_nw_src_masked(rule, ip, mask); | |
741 | break; | |
742 | ||
743 | case F_NW_DST: | |
744 | str_to_ip(value, &ip, &mask); | |
745 | cls_rule_set_nw_dst_masked(rule, ip, mask); | |
746 | break; | |
747 | ||
748 | case F_NW_PROTO: | |
749 | cls_rule_set_nw_proto(rule, str_to_u32(value)); | |
750 | break; | |
751 | ||
752 | case F_NW_TOS: | |
753 | cls_rule_set_nw_tos(rule, str_to_u32(value)); | |
754 | break; | |
755 | ||
756 | case F_TP_SRC: | |
757 | cls_rule_set_tp_src(rule, htons(str_to_u32(value))); | |
758 | break; | |
759 | ||
760 | case F_TP_DST: | |
761 | cls_rule_set_tp_dst(rule, htons(str_to_u32(value))); | |
762 | break; | |
763 | ||
764 | case F_ICMP_TYPE: | |
765 | cls_rule_set_icmp_type(rule, str_to_u32(value)); | |
766 | break; | |
767 | ||
768 | case F_ICMP_CODE: | |
769 | cls_rule_set_icmp_code(rule, str_to_u32(value)); | |
770 | break; | |
771 | ||
bad68a99 JP |
772 | case F_ARP_SHA: |
773 | str_to_mac(value, mac); | |
774 | cls_rule_set_arp_sha(rule, mac); | |
775 | break; | |
776 | ||
777 | case F_ARP_THA: | |
778 | str_to_mac(value, mac); | |
779 | cls_rule_set_arp_tha(rule, mac); | |
780 | break; | |
781 | ||
d31f1109 JP |
782 | case F_IPV6_SRC: |
783 | str_to_ipv6(value, &ipv6, &ipv6_mask); | |
784 | cls_rule_set_ipv6_src_masked(rule, &ipv6, &ipv6_mask); | |
785 | break; | |
786 | ||
787 | case F_IPV6_DST: | |
788 | str_to_ipv6(value, &ipv6, &ipv6_mask); | |
789 | cls_rule_set_ipv6_dst_masked(rule, &ipv6, &ipv6_mask); | |
790 | break; | |
791 | ||
685a51a5 JP |
792 | case F_ND_TARGET: |
793 | str_to_ipv6(value, &ipv6, NULL); | |
794 | cls_rule_set_nd_target(rule, ipv6); | |
795 | break; | |
796 | ||
797 | case F_ND_SLL: | |
798 | str_to_mac(value, mac); | |
799 | cls_rule_set_arp_sha(rule, mac); | |
800 | break; | |
801 | ||
802 | case F_ND_TLL: | |
803 | str_to_mac(value, mac); | |
804 | cls_rule_set_arp_tha(rule, mac); | |
805 | break; | |
806 | ||
8050b31d BP |
807 | case N_FIELDS: |
808 | NOT_REACHED(); | |
809 | } | |
810 | } | |
811 | ||
00b1c62f BP |
812 | static void |
813 | parse_reg_value(struct cls_rule *rule, int reg_idx, const char *value) | |
814 | { | |
815 | uint32_t reg_value, reg_mask; | |
816 | ||
817 | if (!strcmp(value, "ANY") || !strcmp(value, "*")) { | |
818 | cls_rule_set_reg_masked(rule, reg_idx, 0, 0); | |
819 | } else if (sscanf(value, "%"SCNi32"/%"SCNi32, | |
820 | ®_value, ®_mask) == 2) { | |
821 | cls_rule_set_reg_masked(rule, reg_idx, reg_value, reg_mask); | |
822 | } else if (sscanf(value, "%"SCNi32, ®_value)) { | |
823 | cls_rule_set_reg(rule, reg_idx, reg_value); | |
824 | } else { | |
825 | ovs_fatal(0, "register fields must take the form <value> " | |
826 | "or <value>/<mask>"); | |
827 | } | |
828 | } | |
829 | ||
c821124b BP |
830 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
831 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
832 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
833 | * or reallocated. | |
834 | * | |
835 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
836 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
837 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 838 | void |
a9a2da38 BP |
839 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
840 | bool verbose) | |
f22716dc | 841 | { |
c821124b BP |
842 | enum { |
843 | F_OUT_PORT = 1 << 0, | |
844 | F_ACTIONS = 1 << 1, | |
845 | F_COOKIE = 1 << 2, | |
846 | F_TIMEOUT = 1 << 3, | |
847 | F_PRIORITY = 1 << 4 | |
848 | } fields; | |
ec610b7b | 849 | char *string = xstrdup(str_); |
f22716dc JP |
850 | char *save_ptr = NULL; |
851 | char *name; | |
f22716dc | 852 | |
c821124b BP |
853 | switch (command) { |
854 | case -1: | |
855 | fields = F_OUT_PORT; | |
856 | break; | |
857 | ||
858 | case OFPFC_ADD: | |
859 | fields = F_ACTIONS | F_COOKIE | F_TIMEOUT | F_PRIORITY; | |
860 | break; | |
861 | ||
862 | case OFPFC_DELETE: | |
863 | fields = F_OUT_PORT; | |
864 | break; | |
865 | ||
866 | case OFPFC_DELETE_STRICT: | |
867 | fields = F_OUT_PORT | F_PRIORITY; | |
868 | break; | |
869 | ||
870 | case OFPFC_MODIFY: | |
871 | fields = F_ACTIONS | F_COOKIE; | |
872 | break; | |
873 | ||
874 | case OFPFC_MODIFY_STRICT: | |
875 | fields = F_ACTIONS | F_COOKIE | F_PRIORITY; | |
876 | break; | |
877 | ||
878 | default: | |
879 | NOT_REACHED(); | |
880 | } | |
881 | ||
88ca35ee BP |
882 | cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY); |
883 | fm->cookie = htonll(0); | |
6c1491fb | 884 | fm->table_id = 0xff; |
c821124b | 885 | fm->command = command; |
88ca35ee BP |
886 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
887 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
888 | fm->buffer_id = UINT32_MAX; | |
889 | fm->out_port = OFPP_NONE; | |
890 | fm->flags = 0; | |
c821124b BP |
891 | if (fields & F_ACTIONS) { |
892 | struct ofpbuf actions; | |
893 | char *act_str; | |
894 | ||
895 | act_str = strstr(string, "action"); | |
f22716dc | 896 | if (!act_str) { |
ec610b7b | 897 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
898 | } |
899 | *act_str = '\0'; | |
900 | ||
901 | act_str = strchr(act_str + 1, '='); | |
902 | if (!act_str) { | |
ec610b7b | 903 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
904 | } |
905 | ||
906 | act_str++; | |
907 | ||
c821124b BP |
908 | ofpbuf_init(&actions, sizeof(union ofp_action)); |
909 | str_to_action(act_str, &actions); | |
910 | fm->actions = ofpbuf_steal_data(&actions); | |
911 | fm->n_actions = actions.size / sizeof(union ofp_action); | |
88ca35ee BP |
912 | } else { |
913 | fm->actions = NULL; | |
914 | fm->n_actions = 0; | |
f22716dc | 915 | } |
f22716dc JP |
916 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
917 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
918 | const struct protocol *p; | |
919 | ||
920 | if (parse_protocol(name, &p)) { | |
88ca35ee | 921 | cls_rule_set_dl_type(&fm->cr, htons(p->dl_type)); |
f22716dc | 922 | if (p->nw_proto) { |
88ca35ee | 923 | cls_rule_set_nw_proto(&fm->cr, p->nw_proto); |
f22716dc JP |
924 | } |
925 | } else { | |
926 | const struct field *f; | |
927 | char *value; | |
928 | ||
929 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
930 | if (!value) { | |
ec610b7b | 931 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
932 | } |
933 | ||
6c1491fb BP |
934 | if (!strcmp(name, "table")) { |
935 | fm->table_id = atoi(value); | |
8050b31d | 936 | } else if (!strcmp(name, "out_port")) { |
88ca35ee | 937 | fm->out_port = atoi(value); |
c821124b | 938 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
88ca35ee | 939 | fm->cr.priority = atoi(value); |
c821124b | 940 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
88ca35ee | 941 | fm->idle_timeout = atoi(value); |
c821124b | 942 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
88ca35ee | 943 | fm->hard_timeout = atoi(value); |
c821124b | 944 | } else if (fields & F_COOKIE && !strcmp(name, "cookie")) { |
88ca35ee | 945 | fm->cookie = htonll(str_to_u64(value)); |
8050b31d | 946 | } else if (parse_field_name(name, &f)) { |
f22716dc | 947 | if (!strcmp(value, "*") || !strcmp(value, "ANY")) { |
8050b31d | 948 | if (f->wildcard) { |
88ca35ee BP |
949 | fm->cr.wc.wildcards |= f->wildcard; |
950 | cls_rule_zero_wildcarded_fields(&fm->cr); | |
8050b31d | 951 | } else if (f->index == F_NW_SRC) { |
88ca35ee | 952 | cls_rule_set_nw_src_masked(&fm->cr, 0, 0); |
8050b31d | 953 | } else if (f->index == F_NW_DST) { |
88ca35ee | 954 | cls_rule_set_nw_dst_masked(&fm->cr, 0, 0); |
d31f1109 JP |
955 | } else if (f->index == F_IPV6_SRC) { |
956 | cls_rule_set_ipv6_src_masked(&fm->cr, | |
957 | &in6addr_any, &in6addr_any); | |
958 | } else if (f->index == F_IPV6_DST) { | |
959 | cls_rule_set_ipv6_dst_masked(&fm->cr, | |
960 | &in6addr_any, &in6addr_any); | |
66642cb4 | 961 | } else if (f->index == F_DL_VLAN) { |
88ca35ee | 962 | cls_rule_set_any_vid(&fm->cr); |
66642cb4 | 963 | } else if (f->index == F_DL_VLAN_PCP) { |
88ca35ee | 964 | cls_rule_set_any_pcp(&fm->cr); |
f22716dc JP |
965 | } else { |
966 | NOT_REACHED(); | |
967 | } | |
8050b31d | 968 | } else { |
88ca35ee | 969 | parse_field_value(&fm->cr, f->index, value); |
f22716dc | 970 | } |
858f2852 BP |
971 | } else if (!strncmp(name, "reg", 3) |
972 | && isdigit((unsigned char) name[3])) { | |
00b1c62f BP |
973 | unsigned int reg_idx = atoi(name + 3); |
974 | if (reg_idx >= FLOW_N_REGS) { | |
ec610b7b AE |
975 | if (verbose) { |
976 | fprintf(stderr, "%s:\n", str_); | |
977 | } | |
978 | ofp_fatal(str_, verbose, "only %d registers supported", FLOW_N_REGS); | |
00b1c62f | 979 | } |
88ca35ee | 980 | parse_reg_value(&fm->cr, reg_idx, value); |
2c6d8411 BP |
981 | } else if (!strcmp(name, "duration") |
982 | || !strcmp(name, "n_packets") | |
983 | || !strcmp(name, "n_bytes")) { | |
984 | /* Ignore these, so that users can feed the output of | |
985 | * "ovs-ofctl dump-flows" back into commands that parse | |
986 | * flows. */ | |
f22716dc | 987 | } else { |
ec610b7b | 988 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
989 | } |
990 | } | |
991 | } | |
ec610b7b AE |
992 | |
993 | free(string); | |
f22716dc | 994 | } |
15f1f1b6 | 995 | |
88ca35ee BP |
996 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
997 | * (one of OFPFC_*) and appends the parsed OpenFlow message to 'packets'. | |
998 | * '*cur_format' should initially contain the flow format currently configured | |
999 | * on the connection; this function will add a message to change the flow | |
1000 | * format and update '*cur_format', if this is necessary to add the parsed | |
1001 | * flow. */ | |
1002 | void | |
1003 | parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format, | |
ec610b7b AE |
1004 | bool *flow_mod_table_id, char *string, uint16_t command, |
1005 | bool verbose) | |
15f1f1b6 | 1006 | { |
88ca35ee | 1007 | enum nx_flow_format min_format, next_format; |
01b389b1 | 1008 | struct cls_rule rule_copy; |
88ca35ee BP |
1009 | struct ofpbuf actions; |
1010 | struct ofpbuf *ofm; | |
a9a2da38 | 1011 | struct ofputil_flow_mod fm; |
88ca35ee BP |
1012 | |
1013 | ofpbuf_init(&actions, 64); | |
c821124b | 1014 | parse_ofp_str(&fm, command, string, verbose); |
88ca35ee | 1015 | |
b78f6b77 | 1016 | min_format = ofputil_min_flow_format(&fm.cr); |
88ca35ee BP |
1017 | next_format = MAX(*cur_format, min_format); |
1018 | if (next_format != *cur_format) { | |
1019 | struct ofpbuf *sff = ofputil_make_set_flow_format(next_format); | |
1020 | list_push_back(packets, &sff->list_node); | |
1021 | *cur_format = next_format; | |
1022 | } | |
1023 | ||
01b389b1 BP |
1024 | /* Normalize a copy of the rule. This ensures that non-normalized flows |
1025 | * get logged but doesn't affect what gets sent to the switch, so that the | |
1026 | * switch can do whatever it likes with the flow. */ | |
1027 | rule_copy = fm.cr; | |
1028 | ofputil_normalize_rule(&rule_copy, next_format); | |
1029 | ||
6c1491fb BP |
1030 | if (fm.table_id != 0xff && !*flow_mod_table_id) { |
1031 | struct ofpbuf *sff = ofputil_make_flow_mod_table_id(true); | |
1032 | list_push_back(packets, &sff->list_node); | |
1033 | *flow_mod_table_id = true; | |
1034 | } | |
1035 | ||
1036 | ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id); | |
88ca35ee BP |
1037 | list_push_back(packets, &ofm->list_node); |
1038 | ||
1039 | ofpbuf_uninit(&actions); | |
15f1f1b6 BP |
1040 | } |
1041 | ||
88ca35ee BP |
1042 | /* Similar to parse_ofp_flow_mod_str(), except that the string is read from |
1043 | * 'stream' and the command is always OFPFC_ADD. Returns false if end-of-file | |
1044 | * is reached before reading a flow, otherwise true. */ | |
1045 | bool | |
6c1491fb BP |
1046 | parse_ofp_flow_mod_file(struct list *packets, |
1047 | enum nx_flow_format *cur, bool *flow_mod_table_id, | |
02c5617b | 1048 | FILE *stream, uint16_t command) |
15f1f1b6 | 1049 | { |
dd8101bc BP |
1050 | struct ds s; |
1051 | bool ok; | |
15f1f1b6 | 1052 | |
dd8101bc BP |
1053 | ds_init(&s); |
1054 | ok = ds_get_preprocessed_line(&s, stream) == 0; | |
1055 | if (ok) { | |
6c1491fb | 1056 | parse_ofp_flow_mod_str(packets, cur, flow_mod_table_id, |
ec610b7b | 1057 | ds_cstr(&s), command, true); |
15f1f1b6 BP |
1058 | } |
1059 | ds_destroy(&s); | |
1060 | ||
88ca35ee BP |
1061 | return ok; |
1062 | } | |
1063 | ||
1064 | void | |
1065 | parse_ofp_flow_stats_request_str(struct flow_stats_request *fsr, | |
1066 | bool aggregate, char *string) | |
1067 | { | |
a9a2da38 | 1068 | struct ofputil_flow_mod fm; |
88ca35ee | 1069 | |
c821124b | 1070 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee BP |
1071 | fsr->aggregate = aggregate; |
1072 | fsr->match = fm.cr; | |
1073 | fsr->out_port = fm.out_port; | |
6c1491fb | 1074 | fsr->table_id = fm.table_id; |
15f1f1b6 | 1075 | } |