]>
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 | ||
333eba21 | 281 | static void |
75a75043 | 282 | parse_named_action(enum ofputil_action_code code, const struct flow *flow, |
f25d0cf3 | 283 | char *arg, struct ofpbuf *ofpacts) |
333eba21 | 284 | { |
f25d0cf3 BP |
285 | struct ofpact_tunnel *tunnel; |
286 | uint16_t vid; | |
287 | ovs_be32 ip; | |
288 | uint8_t pcp; | |
289 | uint8_t tos; | |
333eba21 BP |
290 | |
291 | switch (code) { | |
690a61c5 BP |
292 | case OFPUTIL_ACTION_INVALID: |
293 | NOT_REACHED(); | |
294 | ||
08f94c0e | 295 | case OFPUTIL_OFPAT10_OUTPUT: |
d01c980f | 296 | case OFPUTIL_OFPAT11_OUTPUT: |
f25d0cf3 | 297 | parse_output(arg, ofpacts); |
333eba21 BP |
298 | break; |
299 | ||
08f94c0e | 300 | case OFPUTIL_OFPAT10_SET_VLAN_VID: |
d01c980f | 301 | case OFPUTIL_OFPAT11_SET_VLAN_VID: |
f25d0cf3 BP |
302 | vid = str_to_u32(arg); |
303 | if (vid & ~VLAN_VID_MASK) { | |
304 | ovs_fatal(0, "%s: not a valid VLAN VID", arg); | |
305 | } | |
306 | ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; | |
333eba21 BP |
307 | break; |
308 | ||
08f94c0e | 309 | case OFPUTIL_OFPAT10_SET_VLAN_PCP: |
d01c980f | 310 | case OFPUTIL_OFPAT11_SET_VLAN_PCP: |
f25d0cf3 BP |
311 | pcp = str_to_u32(arg); |
312 | if (pcp & ~7) { | |
313 | ovs_fatal(0, "%s: not a valid VLAN PCP", arg); | |
314 | } | |
315 | ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; | |
333eba21 BP |
316 | break; |
317 | ||
08f94c0e | 318 | case OFPUTIL_OFPAT10_STRIP_VLAN: |
f25d0cf3 | 319 | ofpact_put_STRIP_VLAN(ofpacts); |
333eba21 BP |
320 | break; |
321 | ||
08f94c0e | 322 | case OFPUTIL_OFPAT10_SET_DL_SRC: |
d01c980f | 323 | case OFPUTIL_OFPAT11_SET_DL_SRC: |
f25d0cf3 BP |
324 | str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); |
325 | break; | |
326 | ||
08f94c0e | 327 | case OFPUTIL_OFPAT10_SET_DL_DST: |
d01c980f | 328 | case OFPUTIL_OFPAT11_SET_DL_DST: |
f25d0cf3 | 329 | str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); |
333eba21 BP |
330 | break; |
331 | ||
08f94c0e | 332 | case OFPUTIL_OFPAT10_SET_NW_SRC: |
d01c980f | 333 | case OFPUTIL_OFPAT11_SET_NW_SRC: |
f25d0cf3 BP |
334 | str_to_ip(arg, &ip); |
335 | ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; | |
336 | break; | |
337 | ||
08f94c0e | 338 | case OFPUTIL_OFPAT10_SET_NW_DST: |
d01c980f | 339 | case OFPUTIL_OFPAT11_SET_NW_DST: |
f25d0cf3 BP |
340 | str_to_ip(arg, &ip); |
341 | ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; | |
333eba21 BP |
342 | break; |
343 | ||
08f94c0e | 344 | case OFPUTIL_OFPAT10_SET_NW_TOS: |
d01c980f | 345 | case OFPUTIL_OFPAT11_SET_NW_TOS: |
f25d0cf3 BP |
346 | tos = str_to_u32(arg); |
347 | if (tos & ~IP_DSCP_MASK) { | |
348 | ovs_fatal(0, "%s: not a valid TOS", arg); | |
349 | } | |
350 | ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; | |
333eba21 BP |
351 | break; |
352 | ||
08f94c0e | 353 | case OFPUTIL_OFPAT10_SET_TP_SRC: |
d01c980f | 354 | case OFPUTIL_OFPAT11_SET_TP_SRC: |
f25d0cf3 BP |
355 | ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); |
356 | break; | |
357 | ||
08f94c0e | 358 | case OFPUTIL_OFPAT10_SET_TP_DST: |
d01c980f | 359 | case OFPUTIL_OFPAT11_SET_TP_DST: |
f25d0cf3 | 360 | ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); |
333eba21 BP |
361 | break; |
362 | ||
08f94c0e | 363 | case OFPUTIL_OFPAT10_ENQUEUE: |
f25d0cf3 | 364 | parse_enqueue(arg, ofpacts); |
333eba21 BP |
365 | break; |
366 | ||
367 | case OFPUTIL_NXAST_RESUBMIT: | |
f25d0cf3 | 368 | parse_resubmit(arg, ofpacts); |
333eba21 BP |
369 | break; |
370 | ||
371 | case OFPUTIL_NXAST_SET_TUNNEL: | |
f25d0cf3 BP |
372 | case OFPUTIL_NXAST_SET_TUNNEL64: |
373 | tunnel = ofpact_put_SET_TUNNEL(ofpacts); | |
374 | tunnel->ofpact.compat = code; | |
375 | tunnel->tun_id = str_to_u64(arg); | |
333eba21 BP |
376 | break; |
377 | ||
378 | case OFPUTIL_NXAST_SET_QUEUE: | |
f25d0cf3 | 379 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); |
333eba21 BP |
380 | break; |
381 | ||
382 | case OFPUTIL_NXAST_POP_QUEUE: | |
f25d0cf3 | 383 | ofpact_put_POP_QUEUE(ofpacts); |
333eba21 BP |
384 | break; |
385 | ||
386 | case OFPUTIL_NXAST_REG_MOVE: | |
f25d0cf3 | 387 | nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); |
333eba21 BP |
388 | break; |
389 | ||
390 | case OFPUTIL_NXAST_REG_LOAD: | |
f25d0cf3 | 391 | nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); |
333eba21 BP |
392 | break; |
393 | ||
394 | case OFPUTIL_NXAST_NOTE: | |
f25d0cf3 | 395 | parse_note(arg, ofpacts); |
93996add BP |
396 | break; |
397 | ||
333eba21 | 398 | case OFPUTIL_NXAST_MULTIPATH: |
f25d0cf3 | 399 | multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); |
333eba21 BP |
400 | break; |
401 | ||
402 | case OFPUTIL_NXAST_AUTOPATH: | |
f25d0cf3 | 403 | autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg); |
333eba21 BP |
404 | break; |
405 | ||
406 | case OFPUTIL_NXAST_BUNDLE: | |
f25d0cf3 | 407 | bundle_parse(arg, ofpacts); |
333eba21 BP |
408 | break; |
409 | ||
410 | case OFPUTIL_NXAST_BUNDLE_LOAD: | |
f25d0cf3 | 411 | bundle_parse_load(arg, ofpacts); |
333eba21 BP |
412 | break; |
413 | ||
414 | case OFPUTIL_NXAST_RESUBMIT_TABLE: | |
415 | case OFPUTIL_NXAST_OUTPUT_REG: | |
416 | NOT_REACHED(); | |
75a75043 BP |
417 | |
418 | case OFPUTIL_NXAST_LEARN: | |
f25d0cf3 | 419 | learn_parse(arg, flow, ofpacts); |
75a75043 | 420 | break; |
a61680c6 | 421 | |
848e8809 | 422 | case OFPUTIL_NXAST_EXIT: |
f25d0cf3 | 423 | ofpact_put_EXIT(ofpacts); |
848e8809 | 424 | break; |
f0fd1a17 PS |
425 | |
426 | case OFPUTIL_NXAST_DEC_TTL: | |
f25d0cf3 | 427 | ofpact_put_DEC_TTL(ofpacts); |
f0fd1a17 | 428 | break; |
0e553d9c BP |
429 | |
430 | case OFPUTIL_NXAST_FIN_TIMEOUT: | |
f25d0cf3 | 431 | parse_fin_timeout(ofpacts, arg); |
0e553d9c | 432 | break; |
a7349929 BP |
433 | |
434 | case OFPUTIL_NXAST_CONTROLLER: | |
f25d0cf3 | 435 | parse_controller(ofpacts, arg); |
a7349929 | 436 | break; |
333eba21 BP |
437 | } |
438 | } | |
439 | ||
f22716dc | 440 | static void |
f25d0cf3 | 441 | str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) |
f22716dc | 442 | { |
0ff22822 | 443 | char *pos, *act, *arg; |
f22716dc JP |
444 | int n_actions; |
445 | ||
53ddd40a | 446 | pos = str; |
d13803eb | 447 | n_actions = 0; |
0ff22822 | 448 | while (ofputil_parse_key_value(&pos, &act, &arg)) { |
f22716dc | 449 | uint16_t port; |
333eba21 | 450 | int code; |
f22716dc | 451 | |
333eba21 BP |
452 | code = ofputil_action_code_from_name(act); |
453 | if (code >= 0) { | |
f25d0cf3 | 454 | parse_named_action(code, flow, arg, ofpacts); |
f22716dc | 455 | } else if (!strcasecmp(act, "drop")) { |
f22716dc JP |
456 | if (n_actions) { |
457 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
458 | "actions"); | |
0ff22822 BP |
459 | } else if (ofputil_parse_key_value(&pos, &act, &arg)) { |
460 | ovs_fatal(0, "Drop actions must not be followed by other " | |
461 | "actions"); | |
f22716dc | 462 | } |
0ff22822 | 463 | break; |
39dc9082 | 464 | } else if (ofputil_port_from_string(act, &port)) { |
f25d0cf3 | 465 | ofpact_put_OUTPUT(ofpacts)->port = port; |
f22716dc JP |
466 | } else { |
467 | ovs_fatal(0, "Unknown action: %s", act); | |
468 | } | |
d13803eb | 469 | n_actions++; |
f22716dc | 470 | } |
f25d0cf3 | 471 | ofpact_pad(ofpacts); |
f22716dc JP |
472 | } |
473 | ||
474 | struct protocol { | |
475 | const char *name; | |
476 | uint16_t dl_type; | |
477 | uint8_t nw_proto; | |
478 | }; | |
479 | ||
480 | static bool | |
481 | parse_protocol(const char *name, const struct protocol **p_out) | |
482 | { | |
483 | static const struct protocol protocols[] = { | |
484 | { "ip", ETH_TYPE_IP, 0 }, | |
485 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
486 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
487 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
488 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
489 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
490 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
491 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
492 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
493 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
f22716dc JP |
494 | }; |
495 | const struct protocol *p; | |
496 | ||
497 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
498 | if (!strcmp(p->name, name)) { | |
499 | *p_out = p; | |
500 | return true; | |
501 | } | |
502 | } | |
503 | *p_out = NULL; | |
504 | return false; | |
505 | } | |
506 | ||
ec610b7b AE |
507 | static void |
508 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
509 | { | |
510 | va_list args; | |
511 | ||
512 | if (verbose) { | |
513 | fprintf(stderr, "%s:\n", flow); | |
514 | } | |
515 | ||
516 | va_start(args, format); | |
517 | ovs_fatal_valist(0, format, args); | |
518 | } | |
519 | ||
8050b31d | 520 | static void |
6a885fd0 | 521 | parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule) |
8050b31d | 522 | { |
6a885fd0 BP |
523 | union mf_value value, mask; |
524 | char *error; | |
bad68a99 | 525 | |
6a885fd0 BP |
526 | error = mf_parse(mf, s, &value, &mask); |
527 | if (error) { | |
528 | ovs_fatal(0, "%s", error); | |
8050b31d | 529 | } |
8050b31d | 530 | |
6a885fd0 | 531 | mf_set(mf, &value, &mask, rule); |
00b1c62f BP |
532 | } |
533 | ||
c821124b BP |
534 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
535 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
536 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
537 | * or reallocated. | |
538 | * | |
539 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
540 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
541 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 542 | void |
a9a2da38 BP |
543 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
544 | bool verbose) | |
f22716dc | 545 | { |
c821124b BP |
546 | enum { |
547 | F_OUT_PORT = 1 << 0, | |
548 | F_ACTIONS = 1 << 1, | |
c821124b | 549 | F_TIMEOUT = 1 << 3, |
a993007b BP |
550 | F_PRIORITY = 1 << 4, |
551 | F_FLAGS = 1 << 5, | |
c821124b | 552 | } fields; |
ec610b7b | 553 | char *string = xstrdup(str_); |
f22716dc | 554 | char *save_ptr = NULL; |
75a75043 | 555 | char *act_str = NULL; |
f22716dc | 556 | char *name; |
f22716dc | 557 | |
c821124b BP |
558 | switch (command) { |
559 | case -1: | |
560 | fields = F_OUT_PORT; | |
561 | break; | |
562 | ||
563 | case OFPFC_ADD: | |
a993007b | 564 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
565 | break; |
566 | ||
567 | case OFPFC_DELETE: | |
568 | fields = F_OUT_PORT; | |
569 | break; | |
570 | ||
571 | case OFPFC_DELETE_STRICT: | |
572 | fields = F_OUT_PORT | F_PRIORITY; | |
573 | break; | |
574 | ||
575 | case OFPFC_MODIFY: | |
a993007b | 576 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
577 | break; |
578 | ||
579 | case OFPFC_MODIFY_STRICT: | |
a993007b | 580 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
581 | break; |
582 | ||
583 | default: | |
584 | NOT_REACHED(); | |
585 | } | |
586 | ||
88ca35ee BP |
587 | cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY); |
588 | fm->cookie = htonll(0); | |
e729e793 | 589 | fm->cookie_mask = htonll(0); |
623e1caf JP |
590 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
591 | /* For modify, by default, don't update the cookie. */ | |
592 | fm->new_cookie = htonll(UINT64_MAX); | |
593 | } else{ | |
594 | fm->new_cookie = htonll(0); | |
595 | } | |
6c1491fb | 596 | fm->table_id = 0xff; |
c821124b | 597 | fm->command = command; |
88ca35ee BP |
598 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
599 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
600 | fm->buffer_id = UINT32_MAX; | |
601 | fm->out_port = OFPP_NONE; | |
602 | fm->flags = 0; | |
c821124b | 603 | if (fields & F_ACTIONS) { |
c821124b | 604 | act_str = strstr(string, "action"); |
f22716dc | 605 | if (!act_str) { |
ec610b7b | 606 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
607 | } |
608 | *act_str = '\0'; | |
609 | ||
610 | act_str = strchr(act_str + 1, '='); | |
611 | if (!act_str) { | |
ec610b7b | 612 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
613 | } |
614 | ||
615 | act_str++; | |
f22716dc | 616 | } |
f22716dc JP |
617 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
618 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
619 | const struct protocol *p; | |
620 | ||
621 | if (parse_protocol(name, &p)) { | |
88ca35ee | 622 | cls_rule_set_dl_type(&fm->cr, htons(p->dl_type)); |
f22716dc | 623 | if (p->nw_proto) { |
88ca35ee | 624 | cls_rule_set_nw_proto(&fm->cr, p->nw_proto); |
f22716dc | 625 | } |
a993007b BP |
626 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
627 | fm->flags |= OFPFF_SEND_FLOW_REM; | |
628 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { | |
629 | fm->flags |= OFPFF_CHECK_OVERLAP; | |
f22716dc | 630 | } else { |
f22716dc JP |
631 | char *value; |
632 | ||
633 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
634 | if (!value) { | |
ec610b7b | 635 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
636 | } |
637 | ||
6c1491fb | 638 | if (!strcmp(name, "table")) { |
c3636ffc | 639 | fm->table_id = str_to_table_id(value); |
8050b31d | 640 | } else if (!strcmp(name, "out_port")) { |
88ca35ee | 641 | fm->out_port = atoi(value); |
c821124b | 642 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
c3636ffc | 643 | fm->cr.priority = str_to_u16(value, name); |
c821124b | 644 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
c3636ffc | 645 | fm->idle_timeout = str_to_u16(value, name); |
c821124b | 646 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
c3636ffc | 647 | fm->hard_timeout = str_to_u16(value, name); |
e729e793 JP |
648 | } else if (!strcmp(name, "cookie")) { |
649 | char *mask = strchr(value, '/'); | |
623e1caf | 650 | |
e729e793 | 651 | if (mask) { |
623e1caf | 652 | /* A mask means we're searching for a cookie. */ |
e729e793 JP |
653 | if (command == OFPFC_ADD) { |
654 | ofp_fatal(str_, verbose, "flow additions cannot use " | |
655 | "a cookie mask"); | |
656 | } | |
657 | *mask = '\0'; | |
623e1caf | 658 | fm->cookie = htonll(str_to_u64(value)); |
e729e793 JP |
659 | fm->cookie_mask = htonll(str_to_u64(mask+1)); |
660 | } else { | |
623e1caf JP |
661 | /* No mask means that the cookie is being set. */ |
662 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
663 | && command != OFPFC_MODIFY_STRICT) { | |
664 | ofp_fatal(str_, verbose, "cannot set cookie"); | |
665 | } | |
666 | fm->new_cookie = htonll(str_to_u64(value)); | |
e729e793 | 667 | } |
6a885fd0 BP |
668 | } else if (mf_from_name(name)) { |
669 | parse_field(mf_from_name(name), value, &fm->cr); | |
2c6d8411 BP |
670 | } else if (!strcmp(name, "duration") |
671 | || !strcmp(name, "n_packets") | |
672 | || !strcmp(name, "n_bytes")) { | |
673 | /* Ignore these, so that users can feed the output of | |
674 | * "ovs-ofctl dump-flows" back into commands that parse | |
675 | * flows. */ | |
f22716dc | 676 | } else { |
ec610b7b | 677 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
678 | } |
679 | } | |
680 | } | |
623e1caf JP |
681 | if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX) |
682 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { | |
683 | /* On modifies without a mask, we are supposed to add a flow if | |
684 | * one does not exist. If a cookie wasn't been specified, use a | |
685 | * default of zero. */ | |
686 | fm->new_cookie = htonll(0); | |
687 | } | |
75a75043 | 688 | if (fields & F_ACTIONS) { |
f25d0cf3 | 689 | struct ofpbuf ofpacts; |
75a75043 | 690 | |
f25d0cf3 BP |
691 | ofpbuf_init(&ofpacts, 32); |
692 | str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts); | |
693 | fm->ofpacts_len = ofpacts.size; | |
694 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); | |
75a75043 | 695 | } else { |
f25d0cf3 BP |
696 | fm->ofpacts_len = 0; |
697 | fm->ofpacts = NULL; | |
75a75043 | 698 | } |
ec610b7b AE |
699 | |
700 | free(string); | |
f22716dc | 701 | } |
15f1f1b6 | 702 | |
2b07c8b1 BP |
703 | /* Convert 'str_' (as described in the documentation for the "monitor" command |
704 | * in the ovs-ofctl man page) into 'fmr'. */ | |
705 | void | |
706 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
707 | const char *str_) | |
708 | { | |
709 | static uint32_t id; | |
710 | ||
711 | char *string = xstrdup(str_); | |
712 | char *save_ptr = NULL; | |
713 | char *name; | |
714 | ||
715 | fmr->id = id++; | |
716 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | |
717 | | NXFMF_OWN | NXFMF_ACTIONS); | |
718 | fmr->out_port = OFPP_NONE; | |
719 | fmr->table_id = 0xff; | |
720 | cls_rule_init_catchall(&fmr->match, 0); | |
721 | ||
722 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
723 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
724 | const struct protocol *p; | |
725 | ||
726 | if (!strcmp(name, "!initial")) { | |
727 | fmr->flags &= ~NXFMF_INITIAL; | |
728 | } else if (!strcmp(name, "!add")) { | |
729 | fmr->flags &= ~NXFMF_ADD; | |
730 | } else if (!strcmp(name, "!delete")) { | |
731 | fmr->flags &= ~NXFMF_DELETE; | |
732 | } else if (!strcmp(name, "!modify")) { | |
733 | fmr->flags &= ~NXFMF_MODIFY; | |
734 | } else if (!strcmp(name, "!actions")) { | |
735 | fmr->flags &= ~NXFMF_ACTIONS; | |
736 | } else if (!strcmp(name, "!own")) { | |
737 | fmr->flags &= ~NXFMF_OWN; | |
738 | } else if (parse_protocol(name, &p)) { | |
739 | cls_rule_set_dl_type(&fmr->match, htons(p->dl_type)); | |
740 | if (p->nw_proto) { | |
741 | cls_rule_set_nw_proto(&fmr->match, p->nw_proto); | |
742 | } | |
743 | } else { | |
744 | char *value; | |
745 | ||
746 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
747 | if (!value) { | |
748 | ovs_fatal(0, "%s: field %s missing value", str_, name); | |
749 | } | |
750 | ||
751 | if (!strcmp(name, "table")) { | |
752 | fmr->table_id = str_to_table_id(value); | |
753 | } else if (!strcmp(name, "out_port")) { | |
754 | fmr->out_port = atoi(value); | |
755 | } else if (mf_from_name(name)) { | |
756 | parse_field(mf_from_name(name), value, &fmr->match); | |
757 | } else { | |
758 | ovs_fatal(0, "%s: unknown keyword %s", str_, name); | |
759 | } | |
760 | } | |
761 | } | |
762 | free(string); | |
763 | } | |
764 | ||
0c3d5fc8 BP |
765 | /* Parses 's' as a set of OpenFlow actions and appends the actions to |
766 | * 'actions'. | |
767 | * | |
768 | * Prints an error on stderr and aborts the program if 's' syntax is | |
769 | * invalid. */ | |
770 | void | |
f25d0cf3 | 771 | parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) |
0c3d5fc8 BP |
772 | { |
773 | char *s = xstrdup(s_); | |
f25d0cf3 | 774 | str_to_ofpacts(NULL, s, ofpacts); |
0c3d5fc8 BP |
775 | free(s); |
776 | } | |
777 | ||
88ca35ee | 778 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
27527aa0 | 779 | * (one of OFPFC_*) into 'fm'. */ |
88ca35ee | 780 | void |
27527aa0 | 781 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
a7fc1744 | 782 | uint16_t command, bool verbose) |
15f1f1b6 | 783 | { |
01b389b1 | 784 | struct cls_rule rule_copy; |
88ca35ee | 785 | |
27527aa0 | 786 | parse_ofp_str(fm, command, string, verbose); |
88ca35ee | 787 | |
01b389b1 BP |
788 | /* Normalize a copy of the rule. This ensures that non-normalized flows |
789 | * get logged but doesn't affect what gets sent to the switch, so that the | |
790 | * switch can do whatever it likes with the flow. */ | |
27527aa0 BP |
791 | rule_copy = fm->cr; |
792 | ofputil_normalize_rule(&rule_copy); | |
15f1f1b6 BP |
793 | } |
794 | ||
27527aa0 BP |
795 | void |
796 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, | |
797 | struct ofputil_flow_mod **fms, size_t *n_fms) | |
15f1f1b6 | 798 | { |
27527aa0 BP |
799 | size_t allocated_fms; |
800 | FILE *stream; | |
dd8101bc | 801 | struct ds s; |
15f1f1b6 | 802 | |
27527aa0 BP |
803 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
804 | if (stream == NULL) { | |
805 | ovs_fatal(errno, "%s: open", file_name); | |
806 | } | |
807 | ||
808 | allocated_fms = *n_fms; | |
dd8101bc | 809 | ds_init(&s); |
27527aa0 BP |
810 | while (!ds_get_preprocessed_line(&s, stream)) { |
811 | if (*n_fms >= allocated_fms) { | |
812 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
813 | } | |
814 | parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false); | |
815 | *n_fms += 1; | |
15f1f1b6 BP |
816 | } |
817 | ds_destroy(&s); | |
818 | ||
27527aa0 BP |
819 | if (stream != stdin) { |
820 | fclose(stream); | |
821 | } | |
88ca35ee BP |
822 | } |
823 | ||
824 | void | |
81d1ea94 | 825 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
a7fc1744 | 826 | bool aggregate, const char *string) |
88ca35ee | 827 | { |
a9a2da38 | 828 | struct ofputil_flow_mod fm; |
88ca35ee | 829 | |
c821124b | 830 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee | 831 | fsr->aggregate = aggregate; |
e729e793 JP |
832 | fsr->cookie = fm.cookie; |
833 | fsr->cookie_mask = fm.cookie_mask; | |
88ca35ee BP |
834 | fsr->match = fm.cr; |
835 | fsr->out_port = fm.out_port; | |
6c1491fb | 836 | fsr->table_id = fm.table_id; |
15f1f1b6 | 837 | } |
ccbe50f8 BP |
838 | |
839 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
840 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
841 | * mf_field. Fields must be specified in a natural order for satisfying | |
842 | * prerequisites. | |
843 | * | |
844 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
845 | * problem. */ | |
846 | char * | |
847 | parse_ofp_exact_flow(struct flow *flow, const char *s) | |
848 | { | |
849 | char *pos, *key, *value_s; | |
850 | char *error = NULL; | |
851 | char *copy; | |
852 | ||
853 | memset(flow, 0, sizeof *flow); | |
854 | ||
855 | pos = copy = xstrdup(s); | |
856 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
857 | const struct protocol *p; | |
858 | if (parse_protocol(key, &p)) { | |
859 | if (flow->dl_type) { | |
860 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
861 | goto exit; | |
862 | } | |
863 | flow->dl_type = htons(p->dl_type); | |
864 | ||
865 | if (p->nw_proto) { | |
866 | if (flow->nw_proto) { | |
867 | error = xasprintf("%s: network protocol set " | |
868 | "multiple times", s); | |
869 | goto exit; | |
870 | } | |
871 | flow->nw_proto = p->nw_proto; | |
872 | } | |
873 | } else { | |
874 | const struct mf_field *mf; | |
875 | union mf_value value; | |
876 | char *field_error; | |
877 | ||
878 | mf = mf_from_name(key); | |
879 | if (!mf) { | |
880 | error = xasprintf("%s: unknown field %s", s, key); | |
881 | goto exit; | |
882 | } | |
883 | ||
884 | if (!mf_are_prereqs_ok(mf, flow)) { | |
885 | error = xasprintf("%s: prerequisites not met for setting %s", | |
886 | s, key); | |
887 | goto exit; | |
888 | } | |
889 | ||
890 | if (!mf_is_zero(mf, flow)) { | |
891 | error = xasprintf("%s: field %s set multiple times", s, key); | |
892 | goto exit; | |
893 | } | |
894 | ||
895 | field_error = mf_parse_value(mf, value_s, &value); | |
896 | if (field_error) { | |
897 | error = xasprintf("%s: bad value for %s (%s)", | |
898 | s, key, field_error); | |
899 | free(field_error); | |
900 | goto exit; | |
901 | } | |
902 | ||
903 | mf_set_flow_value(mf, &value, flow); | |
904 | } | |
905 | } | |
906 | ||
907 | exit: | |
908 | free(copy); | |
909 | ||
910 | if (error) { | |
911 | memset(flow, 0, sizeof *flow); | |
912 | } | |
913 | return error; | |
914 | } |