]>
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" |
53ddd40a | 31 | #include "multipath.h" |
f25d0cf3 | 32 | #include "netdev.h" |
f393f81e | 33 | #include "nx-match.h" |
f25d0cf3 | 34 | #include "ofp-actions.h" |
f22716dc JP |
35 | #include "ofp-util.h" |
36 | #include "ofpbuf.h" | |
37 | #include "openflow/openflow.h" | |
38 | #include "packets.h" | |
39 | #include "socket-util.h" | |
40 | #include "vconn.h" | |
41 | #include "vlog.h" | |
f22716dc | 42 | |
d98e6007 | 43 | VLOG_DEFINE_THIS_MODULE(ofp_parse); |
f22716dc | 44 | |
99b67183 BP |
45 | static void ofp_fatal(const char *flow, bool verbose, const char *format, ...) |
46 | NO_RETURN; | |
47 | ||
c3636ffc BP |
48 | static uint8_t |
49 | str_to_table_id(const char *str) | |
50 | { | |
51 | int table_id; | |
52 | ||
53 | if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) { | |
54 | ovs_fatal(0, "invalid table \"%s\"", str); | |
55 | } | |
56 | return table_id; | |
57 | } | |
58 | ||
59 | static uint16_t | |
60 | str_to_u16(const char *str, const char *name) | |
61 | { | |
62 | int value; | |
63 | ||
64 | if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) { | |
65 | ovs_fatal(0, "invalid %s \"%s\"", name, str); | |
66 | } | |
67 | return value; | |
68 | } | |
69 | ||
f22716dc JP |
70 | static uint32_t |
71 | str_to_u32(const char *str) | |
72 | { | |
73 | char *tail; | |
74 | uint32_t value; | |
75 | ||
c4894ed4 | 76 | if (!str[0]) { |
ce5452cf EJ |
77 | ovs_fatal(0, "missing required numeric argument"); |
78 | } | |
79 | ||
f22716dc JP |
80 | errno = 0; |
81 | value = strtoul(str, &tail, 0); | |
82 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
83 | ovs_fatal(0, "invalid numeric format %s", str); | |
84 | } | |
85 | return value; | |
86 | } | |
87 | ||
88 | static uint64_t | |
89 | str_to_u64(const char *str) | |
90 | { | |
91 | char *tail; | |
92 | uint64_t value; | |
93 | ||
c4894ed4 BP |
94 | if (!str[0]) { |
95 | ovs_fatal(0, "missing required numeric argument"); | |
96 | } | |
97 | ||
f22716dc JP |
98 | errno = 0; |
99 | value = strtoull(str, &tail, 0); | |
100 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
101 | ovs_fatal(0, "invalid numeric format %s", str); | |
102 | } | |
103 | return value; | |
104 | } | |
105 | ||
106 | static void | |
107 | str_to_mac(const char *str, uint8_t mac[6]) | |
108 | { | |
109 | if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) | |
110 | != ETH_ADDR_SCAN_COUNT) { | |
111 | ovs_fatal(0, "invalid mac address %s", str); | |
112 | } | |
113 | } | |
114 | ||
cb8ca532 | 115 | static void |
6a885fd0 | 116 | str_to_ip(const char *str, ovs_be32 *ip) |
cb8ca532 | 117 | { |
f22716dc | 118 | struct in_addr in_addr; |
f22716dc | 119 | |
6a885fd0 | 120 | if (lookup_ip(str, &in_addr)) { |
f22716dc JP |
121 | ovs_fatal(0, "%s: could not convert to IP address", str); |
122 | } | |
123 | *ip = in_addr.s_addr; | |
d31f1109 JP |
124 | } |
125 | ||
5682f723 | 126 | static void |
f25d0cf3 | 127 | parse_enqueue(char *arg, struct ofpbuf *ofpacts) |
5682f723 | 128 | { |
333eba21 BP |
129 | char *sp = NULL; |
130 | char *port = strtok_r(arg, ":q", &sp); | |
131 | char *queue = strtok_r(NULL, "", &sp); | |
f25d0cf3 | 132 | struct ofpact_enqueue *enqueue; |
333eba21 BP |
133 | |
134 | if (port == NULL || queue == NULL) { | |
135 | ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); | |
136 | } | |
137 | ||
f25d0cf3 BP |
138 | enqueue = ofpact_put_ENQUEUE(ofpacts); |
139 | enqueue->port = str_to_u32(port); | |
140 | enqueue->queue = str_to_u32(queue); | |
5682f723 BP |
141 | } |
142 | ||
f694937d | 143 | static void |
f25d0cf3 | 144 | parse_output(char *arg, struct ofpbuf *ofpacts) |
f694937d EJ |
145 | { |
146 | if (strchr(arg, '[')) { | |
f25d0cf3 | 147 | struct ofpact_output_reg *output_reg; |
f694937d | 148 | |
f25d0cf3 BP |
149 | output_reg = ofpact_put_OUTPUT_REG(ofpacts); |
150 | mf_parse_subfield(&output_reg->src, arg); | |
151 | output_reg->max_len = UINT16_MAX; | |
f694937d | 152 | } else { |
f25d0cf3 BP |
153 | struct ofpact_output *output; |
154 | ||
155 | output = ofpact_put_OUTPUT(ofpacts); | |
156 | output->port = str_to_u32(arg); | |
157 | output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; | |
f694937d EJ |
158 | } |
159 | } | |
160 | ||
29901626 | 161 | static void |
f25d0cf3 | 162 | parse_resubmit(char *arg, struct ofpbuf *ofpacts) |
29901626 | 163 | { |
f25d0cf3 | 164 | struct ofpact_resubmit *resubmit; |
29901626 | 165 | char *in_port_s, *table_s; |
f25d0cf3 BP |
166 | |
167 | resubmit = ofpact_put_RESUBMIT(ofpacts); | |
29901626 BP |
168 | |
169 | in_port_s = strsep(&arg, ","); | |
170 | if (in_port_s && in_port_s[0]) { | |
f25d0cf3 BP |
171 | if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { |
172 | resubmit->in_port = str_to_u32(in_port_s); | |
29901626 BP |
173 | } |
174 | } else { | |
f25d0cf3 | 175 | resubmit->in_port = OFPP_IN_PORT; |
29901626 BP |
176 | } |
177 | ||
178 | table_s = strsep(&arg, ","); | |
f25d0cf3 | 179 | resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255; |
29901626 | 180 | |
f25d0cf3 | 181 | if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { |
29901626 BP |
182 | ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " |
183 | " on resubmit"); | |
184 | } | |
333eba21 BP |
185 | } |
186 | ||
187 | static void | |
f25d0cf3 | 188 | parse_note(const char *arg, struct ofpbuf *ofpacts) |
333eba21 | 189 | { |
f25d0cf3 | 190 | struct ofpact_note *note; |
333eba21 | 191 | |
f25d0cf3 | 192 | note = ofpact_put_NOTE(ofpacts); |
333eba21 BP |
193 | while (*arg != '\0') { |
194 | uint8_t byte; | |
195 | bool ok; | |
196 | ||
197 | if (*arg == '.') { | |
198 | arg++; | |
199 | } | |
200 | if (*arg == '\0') { | |
201 | break; | |
202 | } | |
203 | ||
204 | byte = hexits_value(arg, 2, &ok); | |
205 | if (!ok) { | |
206 | ovs_fatal(0, "bad hex digit in `note' argument"); | |
207 | } | |
f25d0cf3 | 208 | ofpbuf_put(ofpacts, &byte, 1); |
333eba21 | 209 | |
f25d0cf3 BP |
210 | note = ofpacts->l2; |
211 | note->length++; | |
333eba21 | 212 | |
f25d0cf3 | 213 | arg += 2; |
333eba21 | 214 | } |
f25d0cf3 | 215 | ofpact_update_len(ofpacts, ¬e->ofpact); |
333eba21 BP |
216 | } |
217 | ||
0e553d9c BP |
218 | static void |
219 | parse_fin_timeout(struct ofpbuf *b, char *arg) | |
220 | { | |
f25d0cf3 | 221 | struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); |
0e553d9c BP |
222 | char *key, *value; |
223 | ||
0e553d9c BP |
224 | while (ofputil_parse_key_value(&arg, &key, &value)) { |
225 | if (!strcmp(key, "idle_timeout")) { | |
f25d0cf3 | 226 | oft->fin_idle_timeout = str_to_u16(value, key); |
0e553d9c | 227 | } else if (!strcmp(key, "hard_timeout")) { |
f25d0cf3 | 228 | oft->fin_hard_timeout = str_to_u16(value, key); |
0e553d9c BP |
229 | } else { |
230 | ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); | |
231 | } | |
232 | } | |
233 | } | |
234 | ||
a7349929 BP |
235 | static void |
236 | parse_controller(struct ofpbuf *b, char *arg) | |
237 | { | |
238 | enum ofp_packet_in_reason reason = OFPR_ACTION; | |
239 | uint16_t controller_id = 0; | |
240 | uint16_t max_len = UINT16_MAX; | |
241 | ||
242 | if (!arg[0]) { | |
243 | /* Use defaults. */ | |
244 | } else if (strspn(arg, "0123456789") == strlen(arg)) { | |
245 | max_len = str_to_u16(arg, "max_len"); | |
246 | } else { | |
247 | char *name, *value; | |
248 | ||
249 | while (ofputil_parse_key_value(&arg, &name, &value)) { | |
250 | if (!strcmp(name, "reason")) { | |
251 | if (!ofputil_packet_in_reason_from_string(value, &reason)) { | |
252 | ovs_fatal(0, "unknown reason \"%s\"", value); | |
253 | } | |
254 | } else if (!strcmp(name, "max_len")) { | |
255 | max_len = str_to_u16(value, "max_len"); | |
256 | } else if (!strcmp(name, "id")) { | |
257 | controller_id = str_to_u16(value, "id"); | |
258 | } else { | |
259 | ovs_fatal(0, "unknown key \"%s\" parsing controller action", | |
260 | name); | |
261 | } | |
262 | } | |
263 | } | |
264 | ||
265 | if (reason == OFPR_ACTION && controller_id == 0) { | |
f25d0cf3 BP |
266 | struct ofpact_output *output; |
267 | ||
268 | output = ofpact_put_OUTPUT(b); | |
269 | output->port = OFPP_CONTROLLER; | |
270 | output->max_len = max_len; | |
a7349929 | 271 | } else { |
f25d0cf3 | 272 | struct ofpact_controller *controller; |
a7349929 | 273 | |
f25d0cf3 BP |
274 | controller = ofpact_put_CONTROLLER(b); |
275 | controller->max_len = max_len; | |
276 | controller->reason = reason; | |
277 | controller->controller_id = controller_id; | |
a7349929 BP |
278 | } |
279 | } | |
280 | ||
c2d967a5 MM |
281 | static void |
282 | parse_dec_ttl(struct ofpbuf *b, char *arg) | |
283 | { | |
284 | struct ofpact_cnt_ids *ids; | |
285 | ||
286 | ids = ofpact_put_DEC_TTL(b); | |
287 | ||
288 | if (*arg == '\0') { | |
289 | uint16_t id = 0; | |
290 | ||
291 | ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL; | |
292 | ofpbuf_put(b, &id, sizeof id); | |
293 | ids = b->l2; | |
294 | ids->n_controllers++; | |
295 | } else { | |
296 | char *cntr; | |
297 | ||
298 | ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; | |
299 | for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; | |
300 | cntr = strtok_r(NULL, ", ", &arg)) { | |
301 | uint16_t id = atoi(cntr); | |
302 | ||
303 | ofpbuf_put(b, &id, sizeof id); | |
304 | ids = b->l2; | |
305 | ids->n_controllers++; | |
306 | } | |
307 | if (!ids->n_controllers) { | |
308 | ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller " | |
309 | "id."); | |
310 | } | |
311 | ||
312 | } | |
313 | ofpact_update_len(b, &ids->ofpact); | |
314 | } | |
315 | ||
333eba21 | 316 | static void |
75a75043 | 317 | parse_named_action(enum ofputil_action_code code, const struct flow *flow, |
f25d0cf3 | 318 | char *arg, struct ofpbuf *ofpacts) |
333eba21 | 319 | { |
f25d0cf3 BP |
320 | struct ofpact_tunnel *tunnel; |
321 | uint16_t vid; | |
322 | ovs_be32 ip; | |
323 | uint8_t pcp; | |
324 | uint8_t tos; | |
333eba21 BP |
325 | |
326 | switch (code) { | |
690a61c5 BP |
327 | case OFPUTIL_ACTION_INVALID: |
328 | NOT_REACHED(); | |
329 | ||
08f94c0e | 330 | case OFPUTIL_OFPAT10_OUTPUT: |
d01c980f | 331 | case OFPUTIL_OFPAT11_OUTPUT: |
f25d0cf3 | 332 | parse_output(arg, ofpacts); |
333eba21 BP |
333 | break; |
334 | ||
08f94c0e | 335 | case OFPUTIL_OFPAT10_SET_VLAN_VID: |
d01c980f | 336 | case OFPUTIL_OFPAT11_SET_VLAN_VID: |
f25d0cf3 BP |
337 | vid = str_to_u32(arg); |
338 | if (vid & ~VLAN_VID_MASK) { | |
339 | ovs_fatal(0, "%s: not a valid VLAN VID", arg); | |
340 | } | |
341 | ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; | |
333eba21 BP |
342 | break; |
343 | ||
08f94c0e | 344 | case OFPUTIL_OFPAT10_SET_VLAN_PCP: |
d01c980f | 345 | case OFPUTIL_OFPAT11_SET_VLAN_PCP: |
f25d0cf3 BP |
346 | pcp = str_to_u32(arg); |
347 | if (pcp & ~7) { | |
348 | ovs_fatal(0, "%s: not a valid VLAN PCP", arg); | |
349 | } | |
350 | ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; | |
333eba21 BP |
351 | break; |
352 | ||
08f94c0e | 353 | case OFPUTIL_OFPAT10_STRIP_VLAN: |
f25d0cf3 | 354 | ofpact_put_STRIP_VLAN(ofpacts); |
333eba21 BP |
355 | break; |
356 | ||
08f94c0e | 357 | case OFPUTIL_OFPAT10_SET_DL_SRC: |
d01c980f | 358 | case OFPUTIL_OFPAT11_SET_DL_SRC: |
f25d0cf3 BP |
359 | str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); |
360 | break; | |
361 | ||
08f94c0e | 362 | case OFPUTIL_OFPAT10_SET_DL_DST: |
d01c980f | 363 | case OFPUTIL_OFPAT11_SET_DL_DST: |
f25d0cf3 | 364 | str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); |
333eba21 BP |
365 | break; |
366 | ||
08f94c0e | 367 | case OFPUTIL_OFPAT10_SET_NW_SRC: |
d01c980f | 368 | case OFPUTIL_OFPAT11_SET_NW_SRC: |
f25d0cf3 BP |
369 | str_to_ip(arg, &ip); |
370 | ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; | |
371 | break; | |
372 | ||
08f94c0e | 373 | case OFPUTIL_OFPAT10_SET_NW_DST: |
d01c980f | 374 | case OFPUTIL_OFPAT11_SET_NW_DST: |
f25d0cf3 BP |
375 | str_to_ip(arg, &ip); |
376 | ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; | |
333eba21 BP |
377 | break; |
378 | ||
08f94c0e | 379 | case OFPUTIL_OFPAT10_SET_NW_TOS: |
d01c980f | 380 | case OFPUTIL_OFPAT11_SET_NW_TOS: |
f25d0cf3 BP |
381 | tos = str_to_u32(arg); |
382 | if (tos & ~IP_DSCP_MASK) { | |
383 | ovs_fatal(0, "%s: not a valid TOS", arg); | |
384 | } | |
385 | ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; | |
333eba21 BP |
386 | break; |
387 | ||
08f94c0e | 388 | case OFPUTIL_OFPAT10_SET_TP_SRC: |
d01c980f | 389 | case OFPUTIL_OFPAT11_SET_TP_SRC: |
f25d0cf3 BP |
390 | ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); |
391 | break; | |
392 | ||
08f94c0e | 393 | case OFPUTIL_OFPAT10_SET_TP_DST: |
d01c980f | 394 | case OFPUTIL_OFPAT11_SET_TP_DST: |
f25d0cf3 | 395 | ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); |
333eba21 BP |
396 | break; |
397 | ||
08f94c0e | 398 | case OFPUTIL_OFPAT10_ENQUEUE: |
f25d0cf3 | 399 | parse_enqueue(arg, ofpacts); |
333eba21 BP |
400 | break; |
401 | ||
402 | case OFPUTIL_NXAST_RESUBMIT: | |
f25d0cf3 | 403 | parse_resubmit(arg, ofpacts); |
333eba21 BP |
404 | break; |
405 | ||
406 | case OFPUTIL_NXAST_SET_TUNNEL: | |
f25d0cf3 BP |
407 | case OFPUTIL_NXAST_SET_TUNNEL64: |
408 | tunnel = ofpact_put_SET_TUNNEL(ofpacts); | |
409 | tunnel->ofpact.compat = code; | |
410 | tunnel->tun_id = str_to_u64(arg); | |
333eba21 BP |
411 | break; |
412 | ||
413 | case OFPUTIL_NXAST_SET_QUEUE: | |
f25d0cf3 | 414 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); |
333eba21 BP |
415 | break; |
416 | ||
417 | case OFPUTIL_NXAST_POP_QUEUE: | |
f25d0cf3 | 418 | ofpact_put_POP_QUEUE(ofpacts); |
333eba21 BP |
419 | break; |
420 | ||
421 | case OFPUTIL_NXAST_REG_MOVE: | |
f25d0cf3 | 422 | nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); |
333eba21 BP |
423 | break; |
424 | ||
425 | case OFPUTIL_NXAST_REG_LOAD: | |
f25d0cf3 | 426 | nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); |
333eba21 BP |
427 | break; |
428 | ||
429 | case OFPUTIL_NXAST_NOTE: | |
f25d0cf3 | 430 | parse_note(arg, ofpacts); |
93996add BP |
431 | break; |
432 | ||
333eba21 | 433 | case OFPUTIL_NXAST_MULTIPATH: |
f25d0cf3 | 434 | multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); |
333eba21 BP |
435 | break; |
436 | ||
c51c638a | 437 | case OFPUTIL_NXAST_AUTOPATH__DEPRECATED: |
f25d0cf3 | 438 | autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg); |
333eba21 BP |
439 | break; |
440 | ||
441 | case OFPUTIL_NXAST_BUNDLE: | |
f25d0cf3 | 442 | bundle_parse(arg, ofpacts); |
333eba21 BP |
443 | break; |
444 | ||
445 | case OFPUTIL_NXAST_BUNDLE_LOAD: | |
f25d0cf3 | 446 | bundle_parse_load(arg, ofpacts); |
333eba21 BP |
447 | break; |
448 | ||
449 | case OFPUTIL_NXAST_RESUBMIT_TABLE: | |
450 | case OFPUTIL_NXAST_OUTPUT_REG: | |
c2d967a5 | 451 | case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: |
333eba21 | 452 | NOT_REACHED(); |
75a75043 BP |
453 | |
454 | case OFPUTIL_NXAST_LEARN: | |
f25d0cf3 | 455 | learn_parse(arg, flow, ofpacts); |
75a75043 | 456 | break; |
a61680c6 | 457 | |
848e8809 | 458 | case OFPUTIL_NXAST_EXIT: |
f25d0cf3 | 459 | ofpact_put_EXIT(ofpacts); |
848e8809 | 460 | break; |
f0fd1a17 PS |
461 | |
462 | case OFPUTIL_NXAST_DEC_TTL: | |
c2d967a5 | 463 | parse_dec_ttl(ofpacts, arg); |
f0fd1a17 | 464 | break; |
0e553d9c BP |
465 | |
466 | case OFPUTIL_NXAST_FIN_TIMEOUT: | |
f25d0cf3 | 467 | parse_fin_timeout(ofpacts, arg); |
0e553d9c | 468 | break; |
a7349929 BP |
469 | |
470 | case OFPUTIL_NXAST_CONTROLLER: | |
f25d0cf3 | 471 | parse_controller(ofpacts, arg); |
a7349929 | 472 | break; |
333eba21 BP |
473 | } |
474 | } | |
475 | ||
f22716dc | 476 | static void |
f25d0cf3 | 477 | str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) |
f22716dc | 478 | { |
0ff22822 | 479 | char *pos, *act, *arg; |
f22716dc JP |
480 | int n_actions; |
481 | ||
53ddd40a | 482 | pos = str; |
d13803eb | 483 | n_actions = 0; |
0ff22822 | 484 | while (ofputil_parse_key_value(&pos, &act, &arg)) { |
f22716dc | 485 | uint16_t port; |
333eba21 | 486 | int code; |
f22716dc | 487 | |
333eba21 BP |
488 | code = ofputil_action_code_from_name(act); |
489 | if (code >= 0) { | |
f25d0cf3 | 490 | parse_named_action(code, flow, arg, ofpacts); |
f22716dc | 491 | } else if (!strcasecmp(act, "drop")) { |
f22716dc JP |
492 | if (n_actions) { |
493 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
494 | "actions"); | |
0ff22822 BP |
495 | } else if (ofputil_parse_key_value(&pos, &act, &arg)) { |
496 | ovs_fatal(0, "Drop actions must not be followed by other " | |
497 | "actions"); | |
f22716dc | 498 | } |
0ff22822 | 499 | break; |
39dc9082 | 500 | } else if (ofputil_port_from_string(act, &port)) { |
f25d0cf3 | 501 | ofpact_put_OUTPUT(ofpacts)->port = port; |
f22716dc JP |
502 | } else { |
503 | ovs_fatal(0, "Unknown action: %s", act); | |
504 | } | |
d13803eb | 505 | n_actions++; |
f22716dc | 506 | } |
f25d0cf3 | 507 | ofpact_pad(ofpacts); |
f22716dc JP |
508 | } |
509 | ||
510 | struct protocol { | |
511 | const char *name; | |
512 | uint16_t dl_type; | |
513 | uint8_t nw_proto; | |
514 | }; | |
515 | ||
516 | static bool | |
517 | parse_protocol(const char *name, const struct protocol **p_out) | |
518 | { | |
519 | static const struct protocol protocols[] = { | |
520 | { "ip", ETH_TYPE_IP, 0 }, | |
521 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
522 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
523 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
524 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
525 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
526 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
527 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
528 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
529 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
f22716dc JP |
530 | }; |
531 | const struct protocol *p; | |
532 | ||
533 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
534 | if (!strcmp(p->name, name)) { | |
535 | *p_out = p; | |
536 | return true; | |
537 | } | |
538 | } | |
539 | *p_out = NULL; | |
540 | return false; | |
541 | } | |
542 | ||
ec610b7b AE |
543 | static void |
544 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
545 | { | |
546 | va_list args; | |
547 | ||
548 | if (verbose) { | |
549 | fprintf(stderr, "%s:\n", flow); | |
550 | } | |
551 | ||
552 | va_start(args, format); | |
553 | ovs_fatal_valist(0, format, args); | |
554 | } | |
555 | ||
8050b31d | 556 | static void |
81a76618 | 557 | parse_field(const struct mf_field *mf, const char *s, struct match *match) |
8050b31d | 558 | { |
6a885fd0 BP |
559 | union mf_value value, mask; |
560 | char *error; | |
bad68a99 | 561 | |
6a885fd0 BP |
562 | error = mf_parse(mf, s, &value, &mask); |
563 | if (error) { | |
564 | ovs_fatal(0, "%s", error); | |
8050b31d | 565 | } |
8050b31d | 566 | |
81a76618 | 567 | mf_set(mf, &value, &mask, match); |
00b1c62f BP |
568 | } |
569 | ||
c821124b BP |
570 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
571 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
572 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
573 | * or reallocated. | |
574 | * | |
575 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
576 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
577 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 578 | void |
a9a2da38 BP |
579 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
580 | bool verbose) | |
f22716dc | 581 | { |
c821124b BP |
582 | enum { |
583 | F_OUT_PORT = 1 << 0, | |
584 | F_ACTIONS = 1 << 1, | |
c821124b | 585 | F_TIMEOUT = 1 << 3, |
a993007b BP |
586 | F_PRIORITY = 1 << 4, |
587 | F_FLAGS = 1 << 5, | |
c821124b | 588 | } fields; |
ec610b7b | 589 | char *string = xstrdup(str_); |
f22716dc | 590 | char *save_ptr = NULL; |
75a75043 | 591 | char *act_str = NULL; |
f22716dc | 592 | char *name; |
f22716dc | 593 | |
c821124b BP |
594 | switch (command) { |
595 | case -1: | |
596 | fields = F_OUT_PORT; | |
597 | break; | |
598 | ||
599 | case OFPFC_ADD: | |
a993007b | 600 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
601 | break; |
602 | ||
603 | case OFPFC_DELETE: | |
604 | fields = F_OUT_PORT; | |
605 | break; | |
606 | ||
607 | case OFPFC_DELETE_STRICT: | |
608 | fields = F_OUT_PORT | F_PRIORITY; | |
609 | break; | |
610 | ||
611 | case OFPFC_MODIFY: | |
a993007b | 612 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
613 | break; |
614 | ||
615 | case OFPFC_MODIFY_STRICT: | |
a993007b | 616 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
617 | break; |
618 | ||
619 | default: | |
620 | NOT_REACHED(); | |
621 | } | |
622 | ||
81a76618 BP |
623 | match_init_catchall(&fm->match); |
624 | fm->priority = OFP_DEFAULT_PRIORITY; | |
88ca35ee | 625 | fm->cookie = htonll(0); |
e729e793 | 626 | fm->cookie_mask = htonll(0); |
623e1caf JP |
627 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
628 | /* For modify, by default, don't update the cookie. */ | |
629 | fm->new_cookie = htonll(UINT64_MAX); | |
630 | } else{ | |
631 | fm->new_cookie = htonll(0); | |
632 | } | |
6c1491fb | 633 | fm->table_id = 0xff; |
c821124b | 634 | fm->command = command; |
88ca35ee BP |
635 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
636 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
637 | fm->buffer_id = UINT32_MAX; | |
638 | fm->out_port = OFPP_NONE; | |
639 | fm->flags = 0; | |
c821124b | 640 | if (fields & F_ACTIONS) { |
c821124b | 641 | act_str = strstr(string, "action"); |
f22716dc | 642 | if (!act_str) { |
ec610b7b | 643 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
644 | } |
645 | *act_str = '\0'; | |
646 | ||
647 | act_str = strchr(act_str + 1, '='); | |
648 | if (!act_str) { | |
ec610b7b | 649 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
650 | } |
651 | ||
652 | act_str++; | |
f22716dc | 653 | } |
f22716dc JP |
654 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
655 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
656 | const struct protocol *p; | |
657 | ||
658 | if (parse_protocol(name, &p)) { | |
81a76618 | 659 | match_set_dl_type(&fm->match, htons(p->dl_type)); |
f22716dc | 660 | if (p->nw_proto) { |
81a76618 | 661 | match_set_nw_proto(&fm->match, p->nw_proto); |
f22716dc | 662 | } |
a993007b BP |
663 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
664 | fm->flags |= OFPFF_SEND_FLOW_REM; | |
665 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { | |
666 | fm->flags |= OFPFF_CHECK_OVERLAP; | |
f22716dc | 667 | } else { |
f22716dc JP |
668 | char *value; |
669 | ||
670 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
671 | if (!value) { | |
ec610b7b | 672 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
673 | } |
674 | ||
6c1491fb | 675 | if (!strcmp(name, "table")) { |
c3636ffc | 676 | fm->table_id = str_to_table_id(value); |
8050b31d | 677 | } else if (!strcmp(name, "out_port")) { |
88ca35ee | 678 | fm->out_port = atoi(value); |
c821124b | 679 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
81a76618 | 680 | fm->priority = str_to_u16(value, name); |
c821124b | 681 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
c3636ffc | 682 | fm->idle_timeout = str_to_u16(value, name); |
c821124b | 683 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
c3636ffc | 684 | fm->hard_timeout = str_to_u16(value, name); |
e729e793 JP |
685 | } else if (!strcmp(name, "cookie")) { |
686 | char *mask = strchr(value, '/'); | |
623e1caf | 687 | |
e729e793 | 688 | if (mask) { |
623e1caf | 689 | /* A mask means we're searching for a cookie. */ |
e729e793 JP |
690 | if (command == OFPFC_ADD) { |
691 | ofp_fatal(str_, verbose, "flow additions cannot use " | |
692 | "a cookie mask"); | |
693 | } | |
694 | *mask = '\0'; | |
623e1caf | 695 | fm->cookie = htonll(str_to_u64(value)); |
e729e793 JP |
696 | fm->cookie_mask = htonll(str_to_u64(mask+1)); |
697 | } else { | |
623e1caf JP |
698 | /* No mask means that the cookie is being set. */ |
699 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
700 | && command != OFPFC_MODIFY_STRICT) { | |
701 | ofp_fatal(str_, verbose, "cannot set cookie"); | |
702 | } | |
703 | fm->new_cookie = htonll(str_to_u64(value)); | |
e729e793 | 704 | } |
6a885fd0 | 705 | } else if (mf_from_name(name)) { |
81a76618 | 706 | parse_field(mf_from_name(name), value, &fm->match); |
2c6d8411 BP |
707 | } else if (!strcmp(name, "duration") |
708 | || !strcmp(name, "n_packets") | |
709 | || !strcmp(name, "n_bytes")) { | |
710 | /* Ignore these, so that users can feed the output of | |
711 | * "ovs-ofctl dump-flows" back into commands that parse | |
712 | * flows. */ | |
f22716dc | 713 | } else { |
ec610b7b | 714 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
715 | } |
716 | } | |
717 | } | |
623e1caf JP |
718 | if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX) |
719 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { | |
720 | /* On modifies without a mask, we are supposed to add a flow if | |
721 | * one does not exist. If a cookie wasn't been specified, use a | |
722 | * default of zero. */ | |
723 | fm->new_cookie = htonll(0); | |
724 | } | |
75a75043 | 725 | if (fields & F_ACTIONS) { |
f25d0cf3 | 726 | struct ofpbuf ofpacts; |
75a75043 | 727 | |
f25d0cf3 | 728 | ofpbuf_init(&ofpacts, 32); |
81a76618 | 729 | str_to_ofpacts(&fm->match.flow, act_str, &ofpacts); |
f25d0cf3 BP |
730 | fm->ofpacts_len = ofpacts.size; |
731 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); | |
75a75043 | 732 | } else { |
f25d0cf3 BP |
733 | fm->ofpacts_len = 0; |
734 | fm->ofpacts = NULL; | |
75a75043 | 735 | } |
ec610b7b AE |
736 | |
737 | free(string); | |
f22716dc | 738 | } |
15f1f1b6 | 739 | |
2b07c8b1 BP |
740 | /* Convert 'str_' (as described in the documentation for the "monitor" command |
741 | * in the ovs-ofctl man page) into 'fmr'. */ | |
742 | void | |
743 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
744 | const char *str_) | |
745 | { | |
746 | static uint32_t id; | |
747 | ||
748 | char *string = xstrdup(str_); | |
749 | char *save_ptr = NULL; | |
750 | char *name; | |
751 | ||
752 | fmr->id = id++; | |
753 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | |
754 | | NXFMF_OWN | NXFMF_ACTIONS); | |
755 | fmr->out_port = OFPP_NONE; | |
756 | fmr->table_id = 0xff; | |
81a76618 | 757 | match_init_catchall(&fmr->match); |
2b07c8b1 BP |
758 | |
759 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
760 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
761 | const struct protocol *p; | |
762 | ||
763 | if (!strcmp(name, "!initial")) { | |
764 | fmr->flags &= ~NXFMF_INITIAL; | |
765 | } else if (!strcmp(name, "!add")) { | |
766 | fmr->flags &= ~NXFMF_ADD; | |
767 | } else if (!strcmp(name, "!delete")) { | |
768 | fmr->flags &= ~NXFMF_DELETE; | |
769 | } else if (!strcmp(name, "!modify")) { | |
770 | fmr->flags &= ~NXFMF_MODIFY; | |
771 | } else if (!strcmp(name, "!actions")) { | |
772 | fmr->flags &= ~NXFMF_ACTIONS; | |
773 | } else if (!strcmp(name, "!own")) { | |
774 | fmr->flags &= ~NXFMF_OWN; | |
775 | } else if (parse_protocol(name, &p)) { | |
81a76618 | 776 | match_set_dl_type(&fmr->match, htons(p->dl_type)); |
2b07c8b1 | 777 | if (p->nw_proto) { |
81a76618 | 778 | match_set_nw_proto(&fmr->match, p->nw_proto); |
2b07c8b1 BP |
779 | } |
780 | } else { | |
781 | char *value; | |
782 | ||
783 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
784 | if (!value) { | |
785 | ovs_fatal(0, "%s: field %s missing value", str_, name); | |
786 | } | |
787 | ||
788 | if (!strcmp(name, "table")) { | |
789 | fmr->table_id = str_to_table_id(value); | |
790 | } else if (!strcmp(name, "out_port")) { | |
791 | fmr->out_port = atoi(value); | |
792 | } else if (mf_from_name(name)) { | |
793 | parse_field(mf_from_name(name), value, &fmr->match); | |
794 | } else { | |
795 | ovs_fatal(0, "%s: unknown keyword %s", str_, name); | |
796 | } | |
797 | } | |
798 | } | |
799 | free(string); | |
800 | } | |
801 | ||
0c3d5fc8 BP |
802 | /* Parses 's' as a set of OpenFlow actions and appends the actions to |
803 | * 'actions'. | |
804 | * | |
805 | * Prints an error on stderr and aborts the program if 's' syntax is | |
806 | * invalid. */ | |
807 | void | |
f25d0cf3 | 808 | parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) |
0c3d5fc8 BP |
809 | { |
810 | char *s = xstrdup(s_); | |
f25d0cf3 | 811 | str_to_ofpacts(NULL, s, ofpacts); |
0c3d5fc8 BP |
812 | free(s); |
813 | } | |
814 | ||
88ca35ee | 815 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
27527aa0 | 816 | * (one of OFPFC_*) into 'fm'. */ |
88ca35ee | 817 | void |
27527aa0 | 818 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
a7fc1744 | 819 | uint16_t command, bool verbose) |
15f1f1b6 | 820 | { |
81a76618 | 821 | struct match match_copy; |
88ca35ee | 822 | |
27527aa0 | 823 | parse_ofp_str(fm, command, string, verbose); |
88ca35ee | 824 | |
81a76618 | 825 | /* Normalize a copy of the match. This ensures that non-normalized flows |
01b389b1 BP |
826 | * get logged but doesn't affect what gets sent to the switch, so that the |
827 | * switch can do whatever it likes with the flow. */ | |
81a76618 BP |
828 | match_copy = fm->match; |
829 | ofputil_normalize_match(&match_copy); | |
15f1f1b6 BP |
830 | } |
831 | ||
27527aa0 BP |
832 | void |
833 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, | |
834 | struct ofputil_flow_mod **fms, size_t *n_fms) | |
15f1f1b6 | 835 | { |
27527aa0 BP |
836 | size_t allocated_fms; |
837 | FILE *stream; | |
dd8101bc | 838 | struct ds s; |
15f1f1b6 | 839 | |
27527aa0 BP |
840 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
841 | if (stream == NULL) { | |
842 | ovs_fatal(errno, "%s: open", file_name); | |
843 | } | |
844 | ||
845 | allocated_fms = *n_fms; | |
dd8101bc | 846 | ds_init(&s); |
27527aa0 BP |
847 | while (!ds_get_preprocessed_line(&s, stream)) { |
848 | if (*n_fms >= allocated_fms) { | |
849 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
850 | } | |
851 | parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false); | |
852 | *n_fms += 1; | |
15f1f1b6 BP |
853 | } |
854 | ds_destroy(&s); | |
855 | ||
27527aa0 BP |
856 | if (stream != stdin) { |
857 | fclose(stream); | |
858 | } | |
88ca35ee BP |
859 | } |
860 | ||
861 | void | |
81d1ea94 | 862 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
a7fc1744 | 863 | bool aggregate, const char *string) |
88ca35ee | 864 | { |
a9a2da38 | 865 | struct ofputil_flow_mod fm; |
88ca35ee | 866 | |
c821124b | 867 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee | 868 | fsr->aggregate = aggregate; |
e729e793 JP |
869 | fsr->cookie = fm.cookie; |
870 | fsr->cookie_mask = fm.cookie_mask; | |
81a76618 | 871 | fsr->match = fm.match; |
88ca35ee | 872 | fsr->out_port = fm.out_port; |
6c1491fb | 873 | fsr->table_id = fm.table_id; |
15f1f1b6 | 874 | } |
ccbe50f8 BP |
875 | |
876 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
877 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
878 | * mf_field. Fields must be specified in a natural order for satisfying | |
879 | * prerequisites. | |
880 | * | |
881 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
882 | * problem. */ | |
883 | char * | |
884 | parse_ofp_exact_flow(struct flow *flow, const char *s) | |
885 | { | |
886 | char *pos, *key, *value_s; | |
887 | char *error = NULL; | |
888 | char *copy; | |
889 | ||
890 | memset(flow, 0, sizeof *flow); | |
891 | ||
892 | pos = copy = xstrdup(s); | |
893 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
894 | const struct protocol *p; | |
895 | if (parse_protocol(key, &p)) { | |
896 | if (flow->dl_type) { | |
897 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
898 | goto exit; | |
899 | } | |
900 | flow->dl_type = htons(p->dl_type); | |
901 | ||
902 | if (p->nw_proto) { | |
903 | if (flow->nw_proto) { | |
904 | error = xasprintf("%s: network protocol set " | |
905 | "multiple times", s); | |
906 | goto exit; | |
907 | } | |
908 | flow->nw_proto = p->nw_proto; | |
909 | } | |
910 | } else { | |
911 | const struct mf_field *mf; | |
912 | union mf_value value; | |
913 | char *field_error; | |
914 | ||
915 | mf = mf_from_name(key); | |
916 | if (!mf) { | |
917 | error = xasprintf("%s: unknown field %s", s, key); | |
918 | goto exit; | |
919 | } | |
920 | ||
921 | if (!mf_are_prereqs_ok(mf, flow)) { | |
922 | error = xasprintf("%s: prerequisites not met for setting %s", | |
923 | s, key); | |
924 | goto exit; | |
925 | } | |
926 | ||
927 | if (!mf_is_zero(mf, flow)) { | |
928 | error = xasprintf("%s: field %s set multiple times", s, key); | |
929 | goto exit; | |
930 | } | |
931 | ||
932 | field_error = mf_parse_value(mf, value_s, &value); | |
933 | if (field_error) { | |
934 | error = xasprintf("%s: bad value for %s (%s)", | |
935 | s, key, field_error); | |
936 | free(field_error); | |
937 | goto exit; | |
938 | } | |
939 | ||
940 | mf_set_flow_value(mf, &value, flow); | |
941 | } | |
942 | } | |
943 | ||
944 | exit: | |
945 | free(copy); | |
946 | ||
947 | if (error) { | |
948 | memset(flow, 0, sizeof *flow); | |
949 | } | |
950 | return error; | |
951 | } |