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