]>
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 | ||
3b6a2571 | 25 | #include "autopath.h" |
daff3353 | 26 | #include "bundle.h" |
10a24935 | 27 | #include "byte-order.h" |
15f1f1b6 | 28 | #include "dynamic-string.h" |
75a75043 | 29 | #include "learn.h" |
6a885fd0 | 30 | #include "meta-flow.h" |
f22716dc | 31 | #include "netdev.h" |
53ddd40a | 32 | #include "multipath.h" |
f393f81e | 33 | #include "nx-match.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 | |
c3636ffc BP |
44 | static uint8_t |
45 | str_to_table_id(const char *str) | |
46 | { | |
47 | int table_id; | |
48 | ||
49 | if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) { | |
50 | ovs_fatal(0, "invalid table \"%s\"", str); | |
51 | } | |
52 | return table_id; | |
53 | } | |
54 | ||
55 | static uint16_t | |
56 | str_to_u16(const char *str, const char *name) | |
57 | { | |
58 | int value; | |
59 | ||
60 | if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) { | |
61 | ovs_fatal(0, "invalid %s \"%s\"", name, str); | |
62 | } | |
63 | return value; | |
64 | } | |
65 | ||
f22716dc JP |
66 | static uint32_t |
67 | str_to_u32(const char *str) | |
68 | { | |
69 | char *tail; | |
70 | uint32_t value; | |
71 | ||
c4894ed4 | 72 | if (!str[0]) { |
ce5452cf EJ |
73 | ovs_fatal(0, "missing required numeric argument"); |
74 | } | |
75 | ||
f22716dc JP |
76 | errno = 0; |
77 | value = strtoul(str, &tail, 0); | |
78 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
79 | ovs_fatal(0, "invalid numeric format %s", str); | |
80 | } | |
81 | return value; | |
82 | } | |
83 | ||
84 | static uint64_t | |
85 | str_to_u64(const char *str) | |
86 | { | |
87 | char *tail; | |
88 | uint64_t value; | |
89 | ||
c4894ed4 BP |
90 | if (!str[0]) { |
91 | ovs_fatal(0, "missing required numeric argument"); | |
92 | } | |
93 | ||
f22716dc JP |
94 | errno = 0; |
95 | value = strtoull(str, &tail, 0); | |
96 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
97 | ovs_fatal(0, "invalid numeric format %s", str); | |
98 | } | |
99 | return value; | |
100 | } | |
101 | ||
102 | static void | |
103 | str_to_mac(const char *str, uint8_t mac[6]) | |
104 | { | |
105 | if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) | |
106 | != ETH_ADDR_SCAN_COUNT) { | |
107 | ovs_fatal(0, "invalid mac address %s", str); | |
108 | } | |
109 | } | |
110 | ||
cb8ca532 | 111 | static void |
6a885fd0 | 112 | str_to_ip(const char *str, ovs_be32 *ip) |
cb8ca532 | 113 | { |
f22716dc | 114 | struct in_addr in_addr; |
f22716dc | 115 | |
6a885fd0 | 116 | if (lookup_ip(str, &in_addr)) { |
f22716dc JP |
117 | ovs_fatal(0, "%s: could not convert to IP address", str); |
118 | } | |
119 | *ip = in_addr.s_addr; | |
d31f1109 JP |
120 | } |
121 | ||
f22716dc JP |
122 | static struct ofp_action_output * |
123 | put_output_action(struct ofpbuf *b, uint16_t port) | |
124 | { | |
93996add BP |
125 | struct ofp_action_output *oao; |
126 | ||
08f94c0e | 127 | oao = ofputil_put_OFPAT10_OUTPUT(b); |
f22716dc JP |
128 | oao->port = htons(port); |
129 | return oao; | |
130 | } | |
131 | ||
5682f723 | 132 | static void |
333eba21 | 133 | parse_enqueue(struct ofpbuf *b, char *arg) |
5682f723 | 134 | { |
333eba21 BP |
135 | char *sp = NULL; |
136 | char *port = strtok_r(arg, ":q", &sp); | |
137 | char *queue = strtok_r(NULL, "", &sp); | |
138 | struct ofp_action_enqueue *oae; | |
139 | ||
140 | if (port == NULL || queue == NULL) { | |
141 | ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); | |
142 | } | |
143 | ||
08f94c0e | 144 | oae = ofputil_put_OFPAT10_ENQUEUE(b); |
333eba21 BP |
145 | oae->port = htons(str_to_u32(port)); |
146 | oae->queue_id = htonl(str_to_u32(queue)); | |
5682f723 BP |
147 | } |
148 | ||
f694937d EJ |
149 | static void |
150 | parse_output(struct ofpbuf *b, char *arg) | |
151 | { | |
152 | if (strchr(arg, '[')) { | |
153 | struct nx_action_output_reg *naor; | |
816fd533 | 154 | struct mf_subfield src; |
f694937d | 155 | |
816fd533 | 156 | mf_parse_subfield(&src, arg); |
f694937d | 157 | |
93996add | 158 | naor = ofputil_put_NXAST_OUTPUT_REG(b); |
816fd533 BP |
159 | naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits); |
160 | naor->src = htonl(src.field->nxm_header); | |
f694937d EJ |
161 | naor->max_len = htons(UINT16_MAX); |
162 | } else { | |
163 | put_output_action(b, str_to_u32(arg)); | |
164 | } | |
165 | } | |
166 | ||
29901626 | 167 | static void |
93996add | 168 | parse_resubmit(struct ofpbuf *b, char *arg) |
29901626 | 169 | { |
93996add | 170 | struct nx_action_resubmit *nar; |
29901626 BP |
171 | char *in_port_s, *table_s; |
172 | uint16_t in_port; | |
173 | uint8_t table; | |
174 | ||
175 | in_port_s = strsep(&arg, ","); | |
176 | if (in_port_s && in_port_s[0]) { | |
39dc9082 | 177 | if (!ofputil_port_from_string(in_port_s, &in_port)) { |
29901626 BP |
178 | in_port = str_to_u32(in_port_s); |
179 | } | |
180 | } else { | |
181 | in_port = OFPP_IN_PORT; | |
182 | } | |
183 | ||
184 | table_s = strsep(&arg, ","); | |
185 | table = table_s && table_s[0] ? str_to_u32(table_s) : 255; | |
186 | ||
187 | if (in_port == OFPP_IN_PORT && table == 255) { | |
188 | ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " | |
189 | " on resubmit"); | |
190 | } | |
191 | ||
29901626 | 192 | if (in_port != OFPP_IN_PORT && table == 255) { |
93996add | 193 | nar = ofputil_put_NXAST_RESUBMIT(b); |
29901626 | 194 | } else { |
93996add | 195 | nar = ofputil_put_NXAST_RESUBMIT_TABLE(b); |
29901626 BP |
196 | nar->table = table; |
197 | } | |
93996add | 198 | nar->in_port = htons(in_port); |
29901626 BP |
199 | } |
200 | ||
333eba21 | 201 | static void |
93996add | 202 | parse_set_tunnel(struct ofpbuf *b, const char *arg) |
333eba21 BP |
203 | { |
204 | uint64_t tun_id = str_to_u64(arg); | |
93996add BP |
205 | if (tun_id > UINT32_MAX) { |
206 | ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id); | |
333eba21 | 207 | } else { |
93996add | 208 | ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id); |
333eba21 BP |
209 | } |
210 | } | |
211 | ||
212 | static void | |
213 | parse_note(struct ofpbuf *b, const char *arg) | |
214 | { | |
215 | size_t start_ofs = b->size; | |
216 | struct nx_action_note *nan; | |
217 | int remainder; | |
218 | size_t len; | |
219 | ||
93996add | 220 | nan = ofputil_put_NXAST_NOTE(b); |
333eba21 BP |
221 | |
222 | b->size -= sizeof nan->note; | |
223 | while (*arg != '\0') { | |
224 | uint8_t byte; | |
225 | bool ok; | |
226 | ||
227 | if (*arg == '.') { | |
228 | arg++; | |
229 | } | |
230 | if (*arg == '\0') { | |
231 | break; | |
232 | } | |
233 | ||
234 | byte = hexits_value(arg, 2, &ok); | |
235 | if (!ok) { | |
236 | ovs_fatal(0, "bad hex digit in `note' argument"); | |
237 | } | |
238 | ofpbuf_put(b, &byte, 1); | |
239 | ||
240 | arg += 2; | |
241 | } | |
242 | ||
243 | len = b->size - start_ofs; | |
244 | remainder = len % OFP_ACTION_ALIGN; | |
245 | if (remainder) { | |
246 | ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder); | |
247 | } | |
248 | nan = (struct nx_action_note *)((char *)b->data + start_ofs); | |
249 | nan->len = htons(b->size - start_ofs); | |
250 | } | |
251 | ||
0e553d9c BP |
252 | static void |
253 | parse_fin_timeout(struct ofpbuf *b, char *arg) | |
254 | { | |
255 | struct nx_action_fin_timeout *naft; | |
256 | char *key, *value; | |
257 | ||
258 | naft = ofputil_put_NXAST_FIN_TIMEOUT(b); | |
259 | while (ofputil_parse_key_value(&arg, &key, &value)) { | |
260 | if (!strcmp(key, "idle_timeout")) { | |
261 | naft->fin_idle_timeout = htons(str_to_u16(value, key)); | |
262 | } else if (!strcmp(key, "hard_timeout")) { | |
263 | naft->fin_hard_timeout = htons(str_to_u16(value, key)); | |
264 | } else { | |
265 | ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); | |
266 | } | |
267 | } | |
268 | } | |
269 | ||
a7349929 BP |
270 | static void |
271 | parse_controller(struct ofpbuf *b, char *arg) | |
272 | { | |
273 | enum ofp_packet_in_reason reason = OFPR_ACTION; | |
274 | uint16_t controller_id = 0; | |
275 | uint16_t max_len = UINT16_MAX; | |
276 | ||
277 | if (!arg[0]) { | |
278 | /* Use defaults. */ | |
279 | } else if (strspn(arg, "0123456789") == strlen(arg)) { | |
280 | max_len = str_to_u16(arg, "max_len"); | |
281 | } else { | |
282 | char *name, *value; | |
283 | ||
284 | while (ofputil_parse_key_value(&arg, &name, &value)) { | |
285 | if (!strcmp(name, "reason")) { | |
286 | if (!ofputil_packet_in_reason_from_string(value, &reason)) { | |
287 | ovs_fatal(0, "unknown reason \"%s\"", value); | |
288 | } | |
289 | } else if (!strcmp(name, "max_len")) { | |
290 | max_len = str_to_u16(value, "max_len"); | |
291 | } else if (!strcmp(name, "id")) { | |
292 | controller_id = str_to_u16(value, "id"); | |
293 | } else { | |
294 | ovs_fatal(0, "unknown key \"%s\" parsing controller action", | |
295 | name); | |
296 | } | |
297 | } | |
298 | } | |
299 | ||
300 | if (reason == OFPR_ACTION && controller_id == 0) { | |
301 | put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len); | |
302 | } else { | |
303 | struct nx_action_controller *nac; | |
304 | ||
305 | nac = ofputil_put_NXAST_CONTROLLER(b); | |
306 | nac->max_len = htons(max_len); | |
307 | nac->reason = reason; | |
308 | nac->controller_id = htons(controller_id); | |
309 | } | |
310 | } | |
311 | ||
333eba21 | 312 | static void |
75a75043 BP |
313 | parse_named_action(enum ofputil_action_code code, const struct flow *flow, |
314 | struct ofpbuf *b, char *arg) | |
333eba21 | 315 | { |
93996add | 316 | struct ofp_action_dl_addr *oada; |
333eba21 BP |
317 | struct ofp_action_vlan_pcp *oavp; |
318 | struct ofp_action_vlan_vid *oavv; | |
319 | struct ofp_action_nw_addr *oana; | |
333eba21 | 320 | struct ofp_action_tp_port *oata; |
333eba21 BP |
321 | |
322 | switch (code) { | |
08f94c0e | 323 | case OFPUTIL_OFPAT10_OUTPUT: |
333eba21 BP |
324 | parse_output(b, arg); |
325 | break; | |
326 | ||
08f94c0e BP |
327 | case OFPUTIL_OFPAT10_SET_VLAN_VID: |
328 | oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b); | |
333eba21 BP |
329 | oavv->vlan_vid = htons(str_to_u32(arg)); |
330 | break; | |
331 | ||
08f94c0e BP |
332 | case OFPUTIL_OFPAT10_SET_VLAN_PCP: |
333 | oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b); | |
333eba21 BP |
334 | oavp->vlan_pcp = str_to_u32(arg); |
335 | break; | |
336 | ||
08f94c0e BP |
337 | case OFPUTIL_OFPAT10_STRIP_VLAN: |
338 | ofputil_put_OFPAT10_STRIP_VLAN(b); | |
333eba21 BP |
339 | break; |
340 | ||
08f94c0e BP |
341 | case OFPUTIL_OFPAT10_SET_DL_SRC: |
342 | case OFPUTIL_OFPAT10_SET_DL_DST: | |
93996add BP |
343 | oada = ofputil_put_action(code, b); |
344 | str_to_mac(arg, oada->dl_addr); | |
333eba21 BP |
345 | break; |
346 | ||
08f94c0e BP |
347 | case OFPUTIL_OFPAT10_SET_NW_SRC: |
348 | case OFPUTIL_OFPAT10_SET_NW_DST: | |
93996add | 349 | oana = ofputil_put_action(code, b); |
6a885fd0 | 350 | str_to_ip(arg, &oana->nw_addr); |
333eba21 BP |
351 | break; |
352 | ||
08f94c0e BP |
353 | case OFPUTIL_OFPAT10_SET_NW_TOS: |
354 | ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg); | |
333eba21 BP |
355 | break; |
356 | ||
08f94c0e BP |
357 | case OFPUTIL_OFPAT10_SET_TP_SRC: |
358 | case OFPUTIL_OFPAT10_SET_TP_DST: | |
93996add | 359 | oata = ofputil_put_action(code, b); |
333eba21 BP |
360 | oata->tp_port = htons(str_to_u32(arg)); |
361 | break; | |
362 | ||
08f94c0e | 363 | case OFPUTIL_OFPAT10_ENQUEUE: |
333eba21 BP |
364 | parse_enqueue(b, arg); |
365 | break; | |
366 | ||
367 | case OFPUTIL_NXAST_RESUBMIT: | |
93996add | 368 | parse_resubmit(b, arg); |
333eba21 BP |
369 | break; |
370 | ||
371 | case OFPUTIL_NXAST_SET_TUNNEL: | |
93996add | 372 | parse_set_tunnel(b, arg); |
333eba21 BP |
373 | break; |
374 | ||
375 | case OFPUTIL_NXAST_SET_QUEUE: | |
93996add | 376 | ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg)); |
333eba21 BP |
377 | break; |
378 | ||
379 | case OFPUTIL_NXAST_POP_QUEUE: | |
93996add | 380 | ofputil_put_NXAST_POP_QUEUE(b); |
333eba21 BP |
381 | break; |
382 | ||
383 | case OFPUTIL_NXAST_REG_MOVE: | |
93996add | 384 | nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg); |
333eba21 BP |
385 | break; |
386 | ||
387 | case OFPUTIL_NXAST_REG_LOAD: | |
93996add | 388 | nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg); |
333eba21 BP |
389 | break; |
390 | ||
391 | case OFPUTIL_NXAST_NOTE: | |
392 | parse_note(b, arg); | |
393 | break; | |
394 | ||
93996add BP |
395 | case OFPUTIL_NXAST_SET_TUNNEL64: |
396 | ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg)); | |
397 | break; | |
398 | ||
333eba21 | 399 | case OFPUTIL_NXAST_MULTIPATH: |
93996add | 400 | multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg); |
333eba21 BP |
401 | break; |
402 | ||
403 | case OFPUTIL_NXAST_AUTOPATH: | |
93996add | 404 | autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg); |
333eba21 BP |
405 | break; |
406 | ||
407 | case OFPUTIL_NXAST_BUNDLE: | |
408 | bundle_parse(b, arg); | |
409 | break; | |
410 | ||
411 | case OFPUTIL_NXAST_BUNDLE_LOAD: | |
412 | bundle_parse_load(b, arg); | |
413 | break; | |
414 | ||
415 | case OFPUTIL_NXAST_RESUBMIT_TABLE: | |
416 | case OFPUTIL_NXAST_OUTPUT_REG: | |
417 | NOT_REACHED(); | |
75a75043 BP |
418 | |
419 | case OFPUTIL_NXAST_LEARN: | |
420 | learn_parse(b, arg, flow); | |
421 | break; | |
a61680c6 | 422 | |
848e8809 EJ |
423 | case OFPUTIL_NXAST_EXIT: |
424 | ofputil_put_NXAST_EXIT(b); | |
425 | break; | |
f0fd1a17 PS |
426 | |
427 | case OFPUTIL_NXAST_DEC_TTL: | |
428 | ofputil_put_NXAST_DEC_TTL(b); | |
429 | break; | |
0e553d9c BP |
430 | |
431 | case OFPUTIL_NXAST_FIN_TIMEOUT: | |
432 | parse_fin_timeout(b, arg); | |
433 | break; | |
a7349929 BP |
434 | |
435 | case OFPUTIL_NXAST_CONTROLLER: | |
436 | parse_controller(b, arg); | |
437 | break; | |
333eba21 BP |
438 | } |
439 | } | |
440 | ||
f22716dc | 441 | static void |
75a75043 | 442 | str_to_action(const struct flow *flow, char *str, struct ofpbuf *b) |
f22716dc | 443 | { |
0ff22822 | 444 | char *pos, *act, *arg; |
f22716dc JP |
445 | int n_actions; |
446 | ||
53ddd40a | 447 | pos = str; |
d13803eb | 448 | n_actions = 0; |
0ff22822 | 449 | while (ofputil_parse_key_value(&pos, &act, &arg)) { |
f22716dc | 450 | uint16_t port; |
333eba21 | 451 | int code; |
f22716dc | 452 | |
333eba21 BP |
453 | code = ofputil_action_code_from_name(act); |
454 | if (code >= 0) { | |
75a75043 | 455 | parse_named_action(code, flow, b, arg); |
f22716dc JP |
456 | } else if (!strcasecmp(act, "drop")) { |
457 | /* A drop action in OpenFlow occurs by just not setting | |
458 | * an action. */ | |
f22716dc JP |
459 | if (n_actions) { |
460 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
461 | "actions"); | |
0ff22822 BP |
462 | } else if (ofputil_parse_key_value(&pos, &act, &arg)) { |
463 | ovs_fatal(0, "Drop actions must not be followed by other " | |
464 | "actions"); | |
f22716dc | 465 | } |
0ff22822 | 466 | break; |
39dc9082 | 467 | } else if (ofputil_port_from_string(act, &port)) { |
f22716dc | 468 | put_output_action(b, port); |
f22716dc JP |
469 | } else { |
470 | ovs_fatal(0, "Unknown action: %s", act); | |
471 | } | |
d13803eb | 472 | n_actions++; |
f22716dc JP |
473 | } |
474 | } | |
475 | ||
476 | struct protocol { | |
477 | const char *name; | |
478 | uint16_t dl_type; | |
479 | uint8_t nw_proto; | |
480 | }; | |
481 | ||
482 | static bool | |
483 | parse_protocol(const char *name, const struct protocol **p_out) | |
484 | { | |
485 | static const struct protocol protocols[] = { | |
486 | { "ip", ETH_TYPE_IP, 0 }, | |
487 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
488 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
489 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
490 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
491 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
492 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
493 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
494 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
495 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
f22716dc JP |
496 | }; |
497 | const struct protocol *p; | |
498 | ||
499 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
500 | if (!strcmp(p->name, name)) { | |
501 | *p_out = p; | |
502 | return true; | |
503 | } | |
504 | } | |
505 | *p_out = NULL; | |
506 | return false; | |
507 | } | |
508 | ||
ec610b7b AE |
509 | static void |
510 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
511 | { | |
512 | va_list args; | |
513 | ||
514 | if (verbose) { | |
515 | fprintf(stderr, "%s:\n", flow); | |
516 | } | |
517 | ||
518 | va_start(args, format); | |
519 | ovs_fatal_valist(0, format, args); | |
520 | } | |
521 | ||
8050b31d | 522 | static void |
6a885fd0 | 523 | parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule) |
8050b31d | 524 | { |
6a885fd0 BP |
525 | union mf_value value, mask; |
526 | char *error; | |
bad68a99 | 527 | |
6a885fd0 BP |
528 | error = mf_parse(mf, s, &value, &mask); |
529 | if (error) { | |
530 | ovs_fatal(0, "%s", error); | |
8050b31d | 531 | } |
8050b31d | 532 | |
6a885fd0 | 533 | mf_set(mf, &value, &mask, rule); |
00b1c62f BP |
534 | } |
535 | ||
c821124b BP |
536 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
537 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
538 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
539 | * or reallocated. | |
540 | * | |
541 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
542 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
543 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 544 | void |
a9a2da38 BP |
545 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
546 | bool verbose) | |
f22716dc | 547 | { |
c821124b BP |
548 | enum { |
549 | F_OUT_PORT = 1 << 0, | |
550 | F_ACTIONS = 1 << 1, | |
c821124b | 551 | F_TIMEOUT = 1 << 3, |
a993007b BP |
552 | F_PRIORITY = 1 << 4, |
553 | F_FLAGS = 1 << 5, | |
c821124b | 554 | } fields; |
ec610b7b | 555 | char *string = xstrdup(str_); |
f22716dc | 556 | char *save_ptr = NULL; |
75a75043 | 557 | char *act_str = NULL; |
f22716dc | 558 | char *name; |
f22716dc | 559 | |
c821124b BP |
560 | switch (command) { |
561 | case -1: | |
562 | fields = F_OUT_PORT; | |
563 | break; | |
564 | ||
565 | case OFPFC_ADD: | |
a993007b | 566 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
567 | break; |
568 | ||
569 | case OFPFC_DELETE: | |
570 | fields = F_OUT_PORT; | |
571 | break; | |
572 | ||
573 | case OFPFC_DELETE_STRICT: | |
574 | fields = F_OUT_PORT | F_PRIORITY; | |
575 | break; | |
576 | ||
577 | case OFPFC_MODIFY: | |
a993007b | 578 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
579 | break; |
580 | ||
581 | case OFPFC_MODIFY_STRICT: | |
a993007b | 582 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
583 | break; |
584 | ||
585 | default: | |
586 | NOT_REACHED(); | |
587 | } | |
588 | ||
88ca35ee BP |
589 | cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY); |
590 | fm->cookie = htonll(0); | |
e729e793 | 591 | fm->cookie_mask = htonll(0); |
6c1491fb | 592 | fm->table_id = 0xff; |
c821124b | 593 | fm->command = command; |
88ca35ee BP |
594 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
595 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
596 | fm->buffer_id = UINT32_MAX; | |
597 | fm->out_port = OFPP_NONE; | |
598 | fm->flags = 0; | |
c821124b | 599 | if (fields & F_ACTIONS) { |
c821124b | 600 | act_str = strstr(string, "action"); |
f22716dc | 601 | if (!act_str) { |
ec610b7b | 602 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
603 | } |
604 | *act_str = '\0'; | |
605 | ||
606 | act_str = strchr(act_str + 1, '='); | |
607 | if (!act_str) { | |
ec610b7b | 608 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
609 | } |
610 | ||
611 | act_str++; | |
f22716dc | 612 | } |
f22716dc JP |
613 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
614 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
615 | const struct protocol *p; | |
616 | ||
617 | if (parse_protocol(name, &p)) { | |
88ca35ee | 618 | cls_rule_set_dl_type(&fm->cr, htons(p->dl_type)); |
f22716dc | 619 | if (p->nw_proto) { |
88ca35ee | 620 | cls_rule_set_nw_proto(&fm->cr, p->nw_proto); |
f22716dc | 621 | } |
a993007b BP |
622 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
623 | fm->flags |= OFPFF_SEND_FLOW_REM; | |
624 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { | |
625 | fm->flags |= OFPFF_CHECK_OVERLAP; | |
f22716dc | 626 | } else { |
f22716dc JP |
627 | char *value; |
628 | ||
629 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
630 | if (!value) { | |
ec610b7b | 631 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
632 | } |
633 | ||
6c1491fb | 634 | if (!strcmp(name, "table")) { |
c3636ffc | 635 | fm->table_id = str_to_table_id(value); |
8050b31d | 636 | } else if (!strcmp(name, "out_port")) { |
88ca35ee | 637 | fm->out_port = atoi(value); |
c821124b | 638 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
c3636ffc | 639 | fm->cr.priority = str_to_u16(value, name); |
c821124b | 640 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
c3636ffc | 641 | fm->idle_timeout = str_to_u16(value, name); |
c821124b | 642 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
c3636ffc | 643 | fm->hard_timeout = str_to_u16(value, name); |
e729e793 JP |
644 | } else if (!strcmp(name, "cookie")) { |
645 | char *mask = strchr(value, '/'); | |
646 | if (mask) { | |
647 | if (command == OFPFC_ADD) { | |
648 | ofp_fatal(str_, verbose, "flow additions cannot use " | |
649 | "a cookie mask"); | |
650 | } | |
651 | *mask = '\0'; | |
652 | fm->cookie_mask = htonll(str_to_u64(mask+1)); | |
653 | } else { | |
654 | fm->cookie_mask = htonll(UINT64_MAX); | |
655 | } | |
88ca35ee | 656 | fm->cookie = htonll(str_to_u64(value)); |
6a885fd0 BP |
657 | } else if (mf_from_name(name)) { |
658 | parse_field(mf_from_name(name), value, &fm->cr); | |
2c6d8411 BP |
659 | } else if (!strcmp(name, "duration") |
660 | || !strcmp(name, "n_packets") | |
661 | || !strcmp(name, "n_bytes")) { | |
662 | /* Ignore these, so that users can feed the output of | |
663 | * "ovs-ofctl dump-flows" back into commands that parse | |
664 | * flows. */ | |
f22716dc | 665 | } else { |
ec610b7b | 666 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
667 | } |
668 | } | |
669 | } | |
75a75043 BP |
670 | if (fields & F_ACTIONS) { |
671 | struct ofpbuf actions; | |
672 | ||
673 | ofpbuf_init(&actions, sizeof(union ofp_action)); | |
674 | str_to_action(&fm->cr.flow, act_str, &actions); | |
675 | fm->actions = ofpbuf_steal_data(&actions); | |
676 | fm->n_actions = actions.size / sizeof(union ofp_action); | |
677 | } else { | |
678 | fm->actions = NULL; | |
679 | fm->n_actions = 0; | |
680 | } | |
ec610b7b AE |
681 | |
682 | free(string); | |
f22716dc | 683 | } |
15f1f1b6 | 684 | |
0c3d5fc8 BP |
685 | /* Parses 's' as a set of OpenFlow actions and appends the actions to |
686 | * 'actions'. | |
687 | * | |
688 | * Prints an error on stderr and aborts the program if 's' syntax is | |
689 | * invalid. */ | |
690 | void | |
691 | parse_ofp_actions(const char *s_, struct ofpbuf *actions) | |
692 | { | |
693 | char *s = xstrdup(s_); | |
694 | str_to_action(NULL, s, actions); | |
695 | free(s); | |
696 | } | |
697 | ||
88ca35ee | 698 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
27527aa0 | 699 | * (one of OFPFC_*) into 'fm'. */ |
88ca35ee | 700 | void |
27527aa0 | 701 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
a7fc1744 | 702 | uint16_t command, bool verbose) |
15f1f1b6 | 703 | { |
01b389b1 | 704 | struct cls_rule rule_copy; |
88ca35ee | 705 | |
27527aa0 | 706 | parse_ofp_str(fm, command, string, verbose); |
88ca35ee | 707 | |
01b389b1 BP |
708 | /* Normalize a copy of the rule. This ensures that non-normalized flows |
709 | * get logged but doesn't affect what gets sent to the switch, so that the | |
710 | * switch can do whatever it likes with the flow. */ | |
27527aa0 BP |
711 | rule_copy = fm->cr; |
712 | ofputil_normalize_rule(&rule_copy); | |
15f1f1b6 BP |
713 | } |
714 | ||
27527aa0 BP |
715 | void |
716 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, | |
717 | struct ofputil_flow_mod **fms, size_t *n_fms) | |
15f1f1b6 | 718 | { |
27527aa0 BP |
719 | size_t allocated_fms; |
720 | FILE *stream; | |
dd8101bc | 721 | struct ds s; |
15f1f1b6 | 722 | |
27527aa0 BP |
723 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
724 | if (stream == NULL) { | |
725 | ovs_fatal(errno, "%s: open", file_name); | |
726 | } | |
727 | ||
728 | allocated_fms = *n_fms; | |
dd8101bc | 729 | ds_init(&s); |
27527aa0 BP |
730 | while (!ds_get_preprocessed_line(&s, stream)) { |
731 | if (*n_fms >= allocated_fms) { | |
732 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
733 | } | |
734 | parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false); | |
735 | *n_fms += 1; | |
15f1f1b6 BP |
736 | } |
737 | ds_destroy(&s); | |
738 | ||
27527aa0 BP |
739 | if (stream != stdin) { |
740 | fclose(stream); | |
741 | } | |
88ca35ee BP |
742 | } |
743 | ||
744 | void | |
81d1ea94 | 745 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
a7fc1744 | 746 | bool aggregate, const char *string) |
88ca35ee | 747 | { |
a9a2da38 | 748 | struct ofputil_flow_mod fm; |
88ca35ee | 749 | |
c821124b | 750 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee | 751 | fsr->aggregate = aggregate; |
e729e793 JP |
752 | fsr->cookie = fm.cookie; |
753 | fsr->cookie_mask = fm.cookie_mask; | |
88ca35ee BP |
754 | fsr->match = fm.cr; |
755 | fsr->out_port = fm.out_port; | |
6c1491fb | 756 | fsr->table_id = fm.table_id; |
15f1f1b6 | 757 | } |