]>
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 | ||
daff3353 | 25 | #include "bundle.h" |
10a24935 | 26 | #include "byte-order.h" |
15f1f1b6 | 27 | #include "dynamic-string.h" |
75a75043 | 28 | #include "learn.h" |
6a885fd0 | 29 | #include "meta-flow.h" |
53ddd40a | 30 | #include "multipath.h" |
f25d0cf3 | 31 | #include "netdev.h" |
f393f81e | 32 | #include "nx-match.h" |
f25d0cf3 | 33 | #include "ofp-actions.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 | |
99b67183 BP |
44 | static void ofp_fatal(const char *flow, bool verbose, const char *format, ...) |
45 | NO_RETURN; | |
46 | ||
c3636ffc | 47 | static uint8_t |
638a19b0 | 48 | str_to_u8(const char *str, const char *name) |
c3636ffc | 49 | { |
638a19b0 | 50 | int value; |
c3636ffc | 51 | |
638a19b0 JR |
52 | if (!str_to_int(str, 10, &value) || value < 0 || value > 255) { |
53 | ovs_fatal(0, "invalid %s \"%s\"", name, str); | |
c3636ffc | 54 | } |
638a19b0 | 55 | return value; |
c3636ffc BP |
56 | } |
57 | ||
58 | static uint16_t | |
59 | str_to_u16(const char *str, const char *name) | |
60 | { | |
61 | int value; | |
62 | ||
63 | if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) { | |
64 | ovs_fatal(0, "invalid %s \"%s\"", name, str); | |
65 | } | |
66 | return value; | |
67 | } | |
68 | ||
f22716dc JP |
69 | static uint32_t |
70 | str_to_u32(const char *str) | |
71 | { | |
72 | char *tail; | |
73 | uint32_t value; | |
74 | ||
c4894ed4 | 75 | if (!str[0]) { |
ce5452cf EJ |
76 | ovs_fatal(0, "missing required numeric argument"); |
77 | } | |
78 | ||
f22716dc JP |
79 | errno = 0; |
80 | value = strtoul(str, &tail, 0); | |
81 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
82 | ovs_fatal(0, "invalid numeric format %s", str); | |
83 | } | |
84 | return value; | |
85 | } | |
86 | ||
87 | static uint64_t | |
88 | str_to_u64(const char *str) | |
89 | { | |
90 | char *tail; | |
91 | uint64_t value; | |
92 | ||
c4894ed4 BP |
93 | if (!str[0]) { |
94 | ovs_fatal(0, "missing required numeric argument"); | |
95 | } | |
96 | ||
f22716dc JP |
97 | errno = 0; |
98 | value = strtoull(str, &tail, 0); | |
99 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
100 | ovs_fatal(0, "invalid numeric format %s", str); | |
101 | } | |
102 | return value; | |
103 | } | |
104 | ||
105 | static void | |
106 | str_to_mac(const char *str, uint8_t mac[6]) | |
107 | { | |
108 | if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac)) | |
109 | != ETH_ADDR_SCAN_COUNT) { | |
110 | ovs_fatal(0, "invalid mac address %s", str); | |
111 | } | |
112 | } | |
113 | ||
cb8ca532 | 114 | static void |
6a885fd0 | 115 | str_to_ip(const char *str, ovs_be32 *ip) |
cb8ca532 | 116 | { |
f22716dc | 117 | struct in_addr in_addr; |
f22716dc | 118 | |
6a885fd0 | 119 | if (lookup_ip(str, &in_addr)) { |
f22716dc JP |
120 | ovs_fatal(0, "%s: could not convert to IP address", str); |
121 | } | |
122 | *ip = in_addr.s_addr; | |
d31f1109 JP |
123 | } |
124 | ||
5682f723 | 125 | static void |
f25d0cf3 | 126 | parse_enqueue(char *arg, struct ofpbuf *ofpacts) |
5682f723 | 127 | { |
333eba21 BP |
128 | char *sp = NULL; |
129 | char *port = strtok_r(arg, ":q", &sp); | |
130 | char *queue = strtok_r(NULL, "", &sp); | |
f25d0cf3 | 131 | struct ofpact_enqueue *enqueue; |
333eba21 BP |
132 | |
133 | if (port == NULL || queue == NULL) { | |
134 | ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); | |
135 | } | |
136 | ||
f25d0cf3 | 137 | enqueue = ofpact_put_ENQUEUE(ofpacts); |
4e022ec0 | 138 | enqueue->port = u16_to_ofp(str_to_u32(port)); |
f25d0cf3 | 139 | enqueue->queue = str_to_u32(queue); |
5682f723 BP |
140 | } |
141 | ||
f694937d | 142 | static void |
f25d0cf3 | 143 | parse_output(char *arg, struct ofpbuf *ofpacts) |
f694937d EJ |
144 | { |
145 | if (strchr(arg, '[')) { | |
f25d0cf3 | 146 | struct ofpact_output_reg *output_reg; |
f694937d | 147 | |
f25d0cf3 BP |
148 | output_reg = ofpact_put_OUTPUT_REG(ofpacts); |
149 | mf_parse_subfield(&output_reg->src, arg); | |
150 | output_reg->max_len = UINT16_MAX; | |
f694937d | 151 | } else { |
f25d0cf3 BP |
152 | struct ofpact_output *output; |
153 | ||
154 | output = ofpact_put_OUTPUT(ofpacts); | |
4e022ec0 | 155 | output->port = u16_to_ofp(str_to_u32(arg)); |
f25d0cf3 | 156 | output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; |
f694937d EJ |
157 | } |
158 | } | |
159 | ||
29901626 | 160 | static void |
f25d0cf3 | 161 | parse_resubmit(char *arg, struct ofpbuf *ofpacts) |
29901626 | 162 | { |
f25d0cf3 | 163 | struct ofpact_resubmit *resubmit; |
29901626 | 164 | char *in_port_s, *table_s; |
f25d0cf3 BP |
165 | |
166 | resubmit = ofpact_put_RESUBMIT(ofpacts); | |
29901626 BP |
167 | |
168 | in_port_s = strsep(&arg, ","); | |
169 | if (in_port_s && in_port_s[0]) { | |
8010100b | 170 | if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { |
c6100d92 | 171 | ovs_fatal(0, "%s: resubmit to unknown port", in_port_s); |
29901626 BP |
172 | } |
173 | } else { | |
f25d0cf3 | 174 | resubmit->in_port = OFPP_IN_PORT; |
29901626 BP |
175 | } |
176 | ||
177 | table_s = strsep(&arg, ","); | |
f25d0cf3 | 178 | resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255; |
29901626 | 179 | |
f25d0cf3 | 180 | if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { |
29901626 BP |
181 | ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " |
182 | " on resubmit"); | |
183 | } | |
333eba21 BP |
184 | } |
185 | ||
186 | static void | |
f25d0cf3 | 187 | parse_note(const char *arg, struct ofpbuf *ofpacts) |
333eba21 | 188 | { |
f25d0cf3 | 189 | struct ofpact_note *note; |
333eba21 | 190 | |
f25d0cf3 | 191 | note = ofpact_put_NOTE(ofpacts); |
333eba21 BP |
192 | while (*arg != '\0') { |
193 | uint8_t byte; | |
194 | bool ok; | |
195 | ||
196 | if (*arg == '.') { | |
197 | arg++; | |
198 | } | |
199 | if (*arg == '\0') { | |
200 | break; | |
201 | } | |
202 | ||
203 | byte = hexits_value(arg, 2, &ok); | |
204 | if (!ok) { | |
205 | ovs_fatal(0, "bad hex digit in `note' argument"); | |
206 | } | |
f25d0cf3 | 207 | ofpbuf_put(ofpacts, &byte, 1); |
333eba21 | 208 | |
f25d0cf3 BP |
209 | note = ofpacts->l2; |
210 | note->length++; | |
333eba21 | 211 | |
f25d0cf3 | 212 | arg += 2; |
333eba21 | 213 | } |
f25d0cf3 | 214 | ofpact_update_len(ofpacts, ¬e->ofpact); |
333eba21 BP |
215 | } |
216 | ||
0e553d9c BP |
217 | static void |
218 | parse_fin_timeout(struct ofpbuf *b, char *arg) | |
219 | { | |
f25d0cf3 | 220 | struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); |
0e553d9c BP |
221 | char *key, *value; |
222 | ||
0e553d9c BP |
223 | while (ofputil_parse_key_value(&arg, &key, &value)) { |
224 | if (!strcmp(key, "idle_timeout")) { | |
f25d0cf3 | 225 | oft->fin_idle_timeout = str_to_u16(value, key); |
0e553d9c | 226 | } else if (!strcmp(key, "hard_timeout")) { |
f25d0cf3 | 227 | oft->fin_hard_timeout = str_to_u16(value, key); |
0e553d9c BP |
228 | } else { |
229 | ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); | |
230 | } | |
231 | } | |
232 | } | |
233 | ||
a7349929 BP |
234 | static void |
235 | parse_controller(struct ofpbuf *b, char *arg) | |
236 | { | |
237 | enum ofp_packet_in_reason reason = OFPR_ACTION; | |
238 | uint16_t controller_id = 0; | |
239 | uint16_t max_len = UINT16_MAX; | |
240 | ||
241 | if (!arg[0]) { | |
242 | /* Use defaults. */ | |
243 | } else if (strspn(arg, "0123456789") == strlen(arg)) { | |
244 | max_len = str_to_u16(arg, "max_len"); | |
245 | } else { | |
246 | char *name, *value; | |
247 | ||
248 | while (ofputil_parse_key_value(&arg, &name, &value)) { | |
249 | if (!strcmp(name, "reason")) { | |
250 | if (!ofputil_packet_in_reason_from_string(value, &reason)) { | |
251 | ovs_fatal(0, "unknown reason \"%s\"", value); | |
252 | } | |
253 | } else if (!strcmp(name, "max_len")) { | |
254 | max_len = str_to_u16(value, "max_len"); | |
255 | } else if (!strcmp(name, "id")) { | |
256 | controller_id = str_to_u16(value, "id"); | |
257 | } else { | |
258 | ovs_fatal(0, "unknown key \"%s\" parsing controller action", | |
259 | name); | |
260 | } | |
261 | } | |
262 | } | |
263 | ||
264 | if (reason == OFPR_ACTION && controller_id == 0) { | |
f25d0cf3 BP |
265 | struct ofpact_output *output; |
266 | ||
267 | output = ofpact_put_OUTPUT(b); | |
268 | output->port = OFPP_CONTROLLER; | |
269 | output->max_len = max_len; | |
a7349929 | 270 | } else { |
f25d0cf3 | 271 | struct ofpact_controller *controller; |
a7349929 | 272 | |
f25d0cf3 BP |
273 | controller = ofpact_put_CONTROLLER(b); |
274 | controller->max_len = max_len; | |
275 | controller->reason = reason; | |
276 | controller->controller_id = controller_id; | |
a7349929 BP |
277 | } |
278 | } | |
279 | ||
c2d967a5 | 280 | static void |
99086062 | 281 | parse_noargs_dec_ttl(struct ofpbuf *b) |
c2d967a5 MM |
282 | { |
283 | struct ofpact_cnt_ids *ids; | |
7bcb1506 | 284 | uint16_t id = 0; |
c2d967a5 MM |
285 | |
286 | ids = ofpact_put_DEC_TTL(b); | |
7bcb1506 IY |
287 | ofpbuf_put(b, &id, sizeof id); |
288 | ids = b->l2; | |
289 | ids->n_controllers++; | |
290 | ofpact_update_len(b, &ids->ofpact); | |
291 | } | |
c2d967a5 | 292 | |
7bcb1506 | 293 | static void |
99086062 | 294 | parse_dec_ttl(struct ofpbuf *b, char *arg) |
7bcb1506 | 295 | { |
c2d967a5 | 296 | if (*arg == '\0') { |
99086062 | 297 | parse_noargs_dec_ttl(b); |
c2d967a5 | 298 | } else { |
7bcb1506 | 299 | struct ofpact_cnt_ids *ids; |
c2d967a5 MM |
300 | char *cntr; |
301 | ||
7bcb1506 | 302 | ids = ofpact_put_DEC_TTL(b); |
c2d967a5 MM |
303 | ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; |
304 | for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; | |
305 | cntr = strtok_r(NULL, ", ", &arg)) { | |
306 | uint16_t id = atoi(cntr); | |
307 | ||
308 | ofpbuf_put(b, &id, sizeof id); | |
309 | ids = b->l2; | |
310 | ids->n_controllers++; | |
311 | } | |
312 | if (!ids->n_controllers) { | |
313 | ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller " | |
314 | "id."); | |
315 | } | |
7bcb1506 | 316 | ofpact_update_len(b, &ids->ofpact); |
c2d967a5 | 317 | } |
c2d967a5 MM |
318 | } |
319 | ||
0f3f3c3d SH |
320 | static void |
321 | parse_set_mpls_ttl(struct ofpbuf *b, const char *arg) | |
322 | { | |
323 | struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b); | |
324 | ||
325 | if (*arg == '\0') { | |
326 | ovs_fatal(0, "parse_set_mpls_ttl: expected ttl."); | |
327 | } | |
328 | ||
329 | mpls_ttl->ttl = atoi(arg); | |
330 | } | |
331 | ||
f5c45121 SH |
332 | static void |
333 | set_field_parse(const char *arg, struct ofpbuf *ofpacts) | |
334 | { | |
335 | char *orig = xstrdup(arg); | |
336 | struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts); | |
337 | char *value; | |
338 | char *delim; | |
339 | char *key; | |
340 | const struct mf_field *mf; | |
341 | const char *error; | |
342 | union mf_value mf_value; | |
343 | ||
344 | value = orig; | |
345 | delim = strstr(orig, "->"); | |
346 | if (!delim) { | |
347 | ovs_fatal(0, "%s: missing `->'", orig); | |
348 | } | |
349 | if (strlen(delim) <= strlen("->")) { | |
350 | ovs_fatal(0, "%s: missing field name following `->'", orig); | |
351 | } | |
352 | ||
353 | key = delim + strlen("->"); | |
354 | mf = mf_from_name(key); | |
355 | if (!mf) { | |
356 | ovs_fatal(0, "%s is not valid oxm field name", key); | |
357 | } | |
358 | if (!mf->writable) { | |
359 | ovs_fatal(0, "%s is not allowed to set", key); | |
360 | } | |
361 | ||
362 | delim[0] = '\0'; | |
363 | error = mf_parse_value(mf, value, &mf_value); | |
364 | if (error) { | |
365 | ovs_fatal(0, "%s", error); | |
366 | } | |
367 | if (!mf_is_value_valid(mf, &mf_value)) { | |
368 | ovs_fatal(0, "%s is not valid valid for field %s", value, key); | |
369 | } | |
370 | ofpact_set_field_init(load, mf, &mf_value); | |
371 | free(orig); | |
372 | } | |
373 | ||
4cceacb9 JS |
374 | static void |
375 | parse_metadata(struct ofpbuf *b, char *arg) | |
376 | { | |
377 | struct ofpact_metadata *om; | |
378 | char *mask = strchr(arg, '/'); | |
379 | ||
380 | om = ofpact_put_WRITE_METADATA(b); | |
381 | ||
382 | if (mask) { | |
383 | *mask = '\0'; | |
384 | om->mask = htonll(str_to_u64(mask + 1)); | |
385 | } else { | |
386 | om->mask = htonll(UINT64_MAX); | |
387 | } | |
388 | ||
389 | om->metadata = htonll(str_to_u64(arg)); | |
390 | } | |
391 | ||
29089a54 RL |
392 | static void |
393 | parse_sample(struct ofpbuf *b, char *arg) | |
394 | { | |
395 | struct ofpact_sample *os = ofpact_put_SAMPLE(b); | |
396 | char *key, *value; | |
397 | ||
398 | while (ofputil_parse_key_value(&arg, &key, &value)) { | |
399 | if (!strcmp(key, "probability")) { | |
400 | os->probability = str_to_u16(value, "probability"); | |
401 | if (os->probability == 0) { | |
402 | ovs_fatal(0, "invalid probability value \"%s\"", value); | |
403 | } | |
404 | } else if (!strcmp(key, "collector_set_id")) { | |
405 | os->collector_set_id = str_to_u32(value); | |
406 | } else if (!strcmp(key, "obs_domain_id")) { | |
407 | os->obs_domain_id = str_to_u32(value); | |
408 | } else if (!strcmp(key, "obs_point_id")) { | |
409 | os->obs_point_id = str_to_u32(value); | |
410 | } else { | |
411 | ovs_fatal(0, "invalid key \"%s\" in \"sample\" argument", | |
412 | key); | |
413 | } | |
414 | } | |
415 | if (os->probability == 0) { | |
416 | ovs_fatal(0, "non-zero \"probability\" must be specified on sample"); | |
417 | } | |
418 | } | |
419 | ||
333eba21 | 420 | static void |
dd43a558 | 421 | parse_named_action(enum ofputil_action_code code, |
f25d0cf3 | 422 | char *arg, struct ofpbuf *ofpacts) |
333eba21 | 423 | { |
f25d0cf3 BP |
424 | struct ofpact_tunnel *tunnel; |
425 | uint16_t vid; | |
3e34fbdd | 426 | uint16_t ethertype; |
f25d0cf3 BP |
427 | ovs_be32 ip; |
428 | uint8_t pcp; | |
429 | uint8_t tos; | |
333eba21 BP |
430 | |
431 | switch (code) { | |
690a61c5 BP |
432 | case OFPUTIL_ACTION_INVALID: |
433 | NOT_REACHED(); | |
434 | ||
08f94c0e | 435 | case OFPUTIL_OFPAT10_OUTPUT: |
d01c980f | 436 | case OFPUTIL_OFPAT11_OUTPUT: |
f25d0cf3 | 437 | parse_output(arg, ofpacts); |
333eba21 BP |
438 | break; |
439 | ||
08f94c0e | 440 | case OFPUTIL_OFPAT10_SET_VLAN_VID: |
d01c980f | 441 | case OFPUTIL_OFPAT11_SET_VLAN_VID: |
f25d0cf3 BP |
442 | vid = str_to_u32(arg); |
443 | if (vid & ~VLAN_VID_MASK) { | |
444 | ovs_fatal(0, "%s: not a valid VLAN VID", arg); | |
445 | } | |
446 | ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; | |
333eba21 BP |
447 | break; |
448 | ||
08f94c0e | 449 | case OFPUTIL_OFPAT10_SET_VLAN_PCP: |
d01c980f | 450 | case OFPUTIL_OFPAT11_SET_VLAN_PCP: |
f25d0cf3 BP |
451 | pcp = str_to_u32(arg); |
452 | if (pcp & ~7) { | |
453 | ovs_fatal(0, "%s: not a valid VLAN PCP", arg); | |
454 | } | |
455 | ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; | |
333eba21 BP |
456 | break; |
457 | ||
78a3fff6 | 458 | case OFPUTIL_OFPAT12_SET_FIELD: |
f5c45121 | 459 | set_field_parse(arg, ofpacts); |
78a3fff6 SH |
460 | break; |
461 | ||
08f94c0e | 462 | case OFPUTIL_OFPAT10_STRIP_VLAN: |
8e61c110 | 463 | case OFPUTIL_OFPAT11_POP_VLAN: |
f25d0cf3 | 464 | ofpact_put_STRIP_VLAN(ofpacts); |
333eba21 BP |
465 | break; |
466 | ||
3e34fbdd IY |
467 | case OFPUTIL_OFPAT11_PUSH_VLAN: |
468 | ethertype = str_to_u16(arg, "ethertype"); | |
469 | if (ethertype != ETH_TYPE_VLAN_8021Q) { | |
5dca28b5 | 470 | /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ |
3e34fbdd IY |
471 | ovs_fatal(0, "%s: not a valid VLAN ethertype", arg); |
472 | } | |
473 | ofpact_put_PUSH_VLAN(ofpacts); | |
474 | break; | |
475 | ||
276c4e7a SH |
476 | case OFPUTIL_OFPAT11_SET_QUEUE: |
477 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); | |
478 | break; | |
479 | ||
480 | ||
08f94c0e | 481 | case OFPUTIL_OFPAT10_SET_DL_SRC: |
d01c980f | 482 | case OFPUTIL_OFPAT11_SET_DL_SRC: |
f25d0cf3 BP |
483 | str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); |
484 | break; | |
485 | ||
08f94c0e | 486 | case OFPUTIL_OFPAT10_SET_DL_DST: |
d01c980f | 487 | case OFPUTIL_OFPAT11_SET_DL_DST: |
f25d0cf3 | 488 | str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); |
333eba21 BP |
489 | break; |
490 | ||
08f94c0e | 491 | case OFPUTIL_OFPAT10_SET_NW_SRC: |
d01c980f | 492 | case OFPUTIL_OFPAT11_SET_NW_SRC: |
f25d0cf3 BP |
493 | str_to_ip(arg, &ip); |
494 | ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; | |
495 | break; | |
496 | ||
08f94c0e | 497 | case OFPUTIL_OFPAT10_SET_NW_DST: |
d01c980f | 498 | case OFPUTIL_OFPAT11_SET_NW_DST: |
f25d0cf3 BP |
499 | str_to_ip(arg, &ip); |
500 | ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; | |
333eba21 BP |
501 | break; |
502 | ||
08f94c0e | 503 | case OFPUTIL_OFPAT10_SET_NW_TOS: |
d01c980f | 504 | case OFPUTIL_OFPAT11_SET_NW_TOS: |
f25d0cf3 BP |
505 | tos = str_to_u32(arg); |
506 | if (tos & ~IP_DSCP_MASK) { | |
507 | ovs_fatal(0, "%s: not a valid TOS", arg); | |
508 | } | |
509 | ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; | |
333eba21 BP |
510 | break; |
511 | ||
7bcb1506 | 512 | case OFPUTIL_OFPAT11_DEC_NW_TTL: |
68194f84 | 513 | NOT_REACHED(); |
7bcb1506 | 514 | |
08f94c0e | 515 | case OFPUTIL_OFPAT10_SET_TP_SRC: |
d01c980f | 516 | case OFPUTIL_OFPAT11_SET_TP_SRC: |
f25d0cf3 BP |
517 | ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); |
518 | break; | |
519 | ||
08f94c0e | 520 | case OFPUTIL_OFPAT10_SET_TP_DST: |
d01c980f | 521 | case OFPUTIL_OFPAT11_SET_TP_DST: |
f25d0cf3 | 522 | ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); |
333eba21 BP |
523 | break; |
524 | ||
08f94c0e | 525 | case OFPUTIL_OFPAT10_ENQUEUE: |
f25d0cf3 | 526 | parse_enqueue(arg, ofpacts); |
333eba21 BP |
527 | break; |
528 | ||
529 | case OFPUTIL_NXAST_RESUBMIT: | |
f25d0cf3 | 530 | parse_resubmit(arg, ofpacts); |
333eba21 BP |
531 | break; |
532 | ||
533 | case OFPUTIL_NXAST_SET_TUNNEL: | |
f25d0cf3 BP |
534 | case OFPUTIL_NXAST_SET_TUNNEL64: |
535 | tunnel = ofpact_put_SET_TUNNEL(ofpacts); | |
536 | tunnel->ofpact.compat = code; | |
537 | tunnel->tun_id = str_to_u64(arg); | |
333eba21 BP |
538 | break; |
539 | ||
4cceacb9 JS |
540 | case OFPUTIL_NXAST_WRITE_METADATA: |
541 | parse_metadata(ofpacts, arg); | |
542 | break; | |
543 | ||
333eba21 | 544 | case OFPUTIL_NXAST_SET_QUEUE: |
f25d0cf3 | 545 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); |
333eba21 BP |
546 | break; |
547 | ||
548 | case OFPUTIL_NXAST_POP_QUEUE: | |
f25d0cf3 | 549 | ofpact_put_POP_QUEUE(ofpacts); |
333eba21 BP |
550 | break; |
551 | ||
552 | case OFPUTIL_NXAST_REG_MOVE: | |
f25d0cf3 | 553 | nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); |
333eba21 BP |
554 | break; |
555 | ||
556 | case OFPUTIL_NXAST_REG_LOAD: | |
f25d0cf3 | 557 | nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); |
333eba21 BP |
558 | break; |
559 | ||
560 | case OFPUTIL_NXAST_NOTE: | |
f25d0cf3 | 561 | parse_note(arg, ofpacts); |
93996add BP |
562 | break; |
563 | ||
333eba21 | 564 | case OFPUTIL_NXAST_MULTIPATH: |
f25d0cf3 | 565 | multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); |
333eba21 BP |
566 | break; |
567 | ||
333eba21 | 568 | case OFPUTIL_NXAST_BUNDLE: |
f25d0cf3 | 569 | bundle_parse(arg, ofpacts); |
333eba21 BP |
570 | break; |
571 | ||
572 | case OFPUTIL_NXAST_BUNDLE_LOAD: | |
f25d0cf3 | 573 | bundle_parse_load(arg, ofpacts); |
333eba21 BP |
574 | break; |
575 | ||
576 | case OFPUTIL_NXAST_RESUBMIT_TABLE: | |
577 | case OFPUTIL_NXAST_OUTPUT_REG: | |
c2d967a5 | 578 | case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: |
333eba21 | 579 | NOT_REACHED(); |
75a75043 BP |
580 | |
581 | case OFPUTIL_NXAST_LEARN: | |
dd43a558 | 582 | learn_parse(arg, ofpacts); |
75a75043 | 583 | break; |
a61680c6 | 584 | |
848e8809 | 585 | case OFPUTIL_NXAST_EXIT: |
f25d0cf3 | 586 | ofpact_put_EXIT(ofpacts); |
848e8809 | 587 | break; |
f0fd1a17 PS |
588 | |
589 | case OFPUTIL_NXAST_DEC_TTL: | |
99086062 | 590 | parse_dec_ttl(ofpacts, arg); |
f0fd1a17 | 591 | break; |
0e553d9c | 592 | |
0f3f3c3d SH |
593 | case OFPUTIL_NXAST_SET_MPLS_TTL: |
594 | case OFPUTIL_OFPAT11_SET_MPLS_TTL: | |
595 | parse_set_mpls_ttl(ofpacts, arg); | |
596 | break; | |
597 | ||
b676167a SH |
598 | case OFPUTIL_OFPAT11_DEC_MPLS_TTL: |
599 | case OFPUTIL_NXAST_DEC_MPLS_TTL: | |
600 | ofpact_put_DEC_MPLS_TTL(ofpacts); | |
601 | break; | |
602 | ||
0e553d9c | 603 | case OFPUTIL_NXAST_FIN_TIMEOUT: |
f25d0cf3 | 604 | parse_fin_timeout(ofpacts, arg); |
0e553d9c | 605 | break; |
a7349929 BP |
606 | |
607 | case OFPUTIL_NXAST_CONTROLLER: | |
f25d0cf3 | 608 | parse_controller(ofpacts, arg); |
a7349929 | 609 | break; |
b02475c5 SH |
610 | |
611 | case OFPUTIL_OFPAT11_PUSH_MPLS: | |
612 | case OFPUTIL_NXAST_PUSH_MPLS: | |
613 | ofpact_put_PUSH_MPLS(ofpacts)->ethertype = | |
614 | htons(str_to_u16(arg, "push_mpls")); | |
615 | break; | |
616 | ||
617 | case OFPUTIL_OFPAT11_POP_MPLS: | |
618 | case OFPUTIL_NXAST_POP_MPLS: | |
619 | ofpact_put_POP_MPLS(ofpacts)->ethertype = | |
620 | htons(str_to_u16(arg, "pop_mpls")); | |
621 | break; | |
29089a54 | 622 | |
bd85dac1 AZ |
623 | case OFPUTIL_NXAST_STACK_PUSH: |
624 | nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); | |
625 | break; | |
626 | case OFPUTIL_NXAST_STACK_POP: | |
627 | nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); | |
628 | break; | |
29089a54 RL |
629 | |
630 | case OFPUTIL_NXAST_SAMPLE: | |
631 | parse_sample(ofpacts, arg); | |
632 | break; | |
333eba21 BP |
633 | } |
634 | } | |
635 | ||
8dd54666 | 636 | static bool |
dd43a558 | 637 | str_to_ofpact__(char *pos, char *act, char *arg, |
8dd54666 IY |
638 | struct ofpbuf *ofpacts, int n_actions) |
639 | { | |
640 | int code = ofputil_action_code_from_name(act); | |
641 | if (code >= 0) { | |
dd43a558 | 642 | parse_named_action(code, arg, ofpacts); |
8dd54666 IY |
643 | } else if (!strcasecmp(act, "drop")) { |
644 | if (n_actions) { | |
645 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
646 | "actions"); | |
647 | } else if (ofputil_parse_key_value(&pos, &act, &arg)) { | |
648 | ovs_fatal(0, "Drop actions must not be followed by other " | |
649 | "actions"); | |
650 | } | |
651 | return false; | |
652 | } else { | |
4e022ec0 | 653 | ofp_port_t port; |
8010100b | 654 | if (ofputil_port_from_string(act, &port)) { |
8dd54666 IY |
655 | ofpact_put_OUTPUT(ofpacts)->port = port; |
656 | } else { | |
657 | ovs_fatal(0, "Unknown action: %s", act); | |
658 | } | |
659 | } | |
660 | ||
661 | return true; | |
662 | } | |
663 | ||
f22716dc | 664 | static void |
dd43a558 | 665 | str_to_ofpacts(char *str, struct ofpbuf *ofpacts) |
f22716dc | 666 | { |
0ff22822 | 667 | char *pos, *act, *arg; |
4cceacb9 | 668 | enum ofperr error; |
f22716dc JP |
669 | int n_actions; |
670 | ||
53ddd40a | 671 | pos = str; |
d13803eb | 672 | n_actions = 0; |
0ff22822 | 673 | while (ofputil_parse_key_value(&pos, &act, &arg)) { |
dd43a558 | 674 | if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) { |
0ff22822 | 675 | break; |
8dd54666 IY |
676 | } |
677 | n_actions++; | |
678 | } | |
4cceacb9 JS |
679 | |
680 | error = ofpacts_verify(ofpacts->data, ofpacts->size); | |
681 | if (error) { | |
682 | ovs_fatal(0, "Incorrect action ordering"); | |
683 | } | |
684 | ||
8dd54666 IY |
685 | ofpact_pad(ofpacts); |
686 | } | |
687 | ||
688 | static void | |
689 | parse_named_instruction(enum ovs_instruction_type type, | |
690 | char *arg, struct ofpbuf *ofpacts) | |
691 | { | |
4cceacb9 JS |
692 | enum ofperr error; |
693 | ||
8dd54666 IY |
694 | switch (type) { |
695 | case OVSINST_OFPIT11_APPLY_ACTIONS: | |
696 | NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */ | |
697 | break; | |
698 | ||
699 | case OVSINST_OFPIT11_WRITE_ACTIONS: | |
5dca28b5 | 700 | /* XXX */ |
8dd54666 IY |
701 | ovs_fatal(0, "instruction write-actions is not supported yet"); |
702 | break; | |
703 | ||
704 | case OVSINST_OFPIT11_CLEAR_ACTIONS: | |
b19e8793 | 705 | ofpact_put_CLEAR_ACTIONS(ofpacts); |
8dd54666 IY |
706 | break; |
707 | ||
638a19b0 JR |
708 | case OVSINST_OFPIT13_METER: |
709 | ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg); | |
710 | break; | |
711 | ||
8dd54666 | 712 | case OVSINST_OFPIT11_WRITE_METADATA: |
4cceacb9 | 713 | parse_metadata(ofpacts, arg); |
8dd54666 IY |
714 | break; |
715 | ||
716 | case OVSINST_OFPIT11_GOTO_TABLE: { | |
717 | struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); | |
718 | char *table_s = strsep(&arg, ","); | |
719 | if (!table_s || !table_s[0]) { | |
720 | ovs_fatal(0, "instruction goto-table needs table id"); | |
721 | } | |
638a19b0 | 722 | ogt->table_id = str_to_u8(table_s, "table"); |
8dd54666 IY |
723 | break; |
724 | } | |
725 | } | |
4cceacb9 JS |
726 | |
727 | /* If write_metadata is specified as an action AND an instruction, ofpacts | |
728 | could be invalid. */ | |
729 | error = ofpacts_verify(ofpacts->data, ofpacts->size); | |
730 | if (error) { | |
731 | ovs_fatal(0, "Incorrect instruction ordering"); | |
732 | } | |
8dd54666 IY |
733 | } |
734 | ||
735 | static void | |
dd43a558 | 736 | str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts) |
8dd54666 IY |
737 | { |
738 | char *pos, *inst, *arg; | |
739 | int type; | |
740 | const char *prev_inst = NULL; | |
741 | int prev_type = -1; | |
742 | int n_actions = 0; | |
743 | ||
744 | pos = str; | |
745 | while (ofputil_parse_key_value(&pos, &inst, &arg)) { | |
746 | type = ofpact_instruction_type_from_name(inst); | |
747 | if (type < 0) { | |
dd43a558 | 748 | if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) { |
8dd54666 IY |
749 | break; |
750 | } | |
751 | ||
752 | type = OVSINST_OFPIT11_APPLY_ACTIONS; | |
753 | if (prev_type == type) { | |
754 | n_actions++; | |
755 | continue; | |
c6100d92 | 756 | } |
8dd54666 IY |
757 | } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) { |
758 | ovs_fatal(0, "%s isn't supported. Just write actions then " | |
759 | "it is interpreted as apply_actions", inst); | |
760 | } else { | |
761 | parse_named_instruction(type, arg, ofpacts); | |
762 | } | |
763 | ||
764 | if (type == prev_type) { | |
765 | ovs_fatal(0, "instruction can be specified at most once: %s", | |
766 | inst); | |
f22716dc | 767 | } |
8dd54666 IY |
768 | if (type <= prev_type) { |
769 | ovs_fatal(0, "Instruction %s must be specified before %s", | |
770 | inst, prev_inst); | |
771 | } | |
772 | ||
773 | prev_inst = inst; | |
774 | prev_type = type; | |
d13803eb | 775 | n_actions++; |
f22716dc | 776 | } |
f25d0cf3 | 777 | ofpact_pad(ofpacts); |
f22716dc JP |
778 | } |
779 | ||
780 | struct protocol { | |
781 | const char *name; | |
782 | uint16_t dl_type; | |
783 | uint8_t nw_proto; | |
784 | }; | |
785 | ||
786 | static bool | |
787 | parse_protocol(const char *name, const struct protocol **p_out) | |
788 | { | |
789 | static const struct protocol protocols[] = { | |
790 | { "ip", ETH_TYPE_IP, 0 }, | |
791 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
792 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
793 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
794 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
795 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
796 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
797 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
798 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
799 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
8087f5ff | 800 | { "rarp", ETH_TYPE_RARP, 0}, |
b02475c5 SH |
801 | { "mpls", ETH_TYPE_MPLS, 0 }, |
802 | { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, | |
803 | }; | |
f22716dc JP |
804 | const struct protocol *p; |
805 | ||
806 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
807 | if (!strcmp(p->name, name)) { | |
808 | *p_out = p; | |
809 | return true; | |
810 | } | |
811 | } | |
812 | *p_out = NULL; | |
813 | return false; | |
814 | } | |
815 | ||
ec610b7b AE |
816 | static void |
817 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
818 | { | |
819 | va_list args; | |
820 | ||
821 | if (verbose) { | |
822 | fprintf(stderr, "%s:\n", flow); | |
823 | } | |
824 | ||
825 | va_start(args, format); | |
826 | ovs_fatal_valist(0, format, args); | |
827 | } | |
828 | ||
8050b31d | 829 | static void |
81a76618 | 830 | parse_field(const struct mf_field *mf, const char *s, struct match *match) |
8050b31d | 831 | { |
6a885fd0 BP |
832 | union mf_value value, mask; |
833 | char *error; | |
bad68a99 | 834 | |
6a885fd0 BP |
835 | error = mf_parse(mf, s, &value, &mask); |
836 | if (error) { | |
837 | ovs_fatal(0, "%s", error); | |
8050b31d | 838 | } |
8050b31d | 839 | |
81a76618 | 840 | mf_set(mf, &value, &mask, match); |
00b1c62f BP |
841 | } |
842 | ||
c821124b BP |
843 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
844 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
845 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
846 | * or reallocated. | |
847 | * | |
848 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
849 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
850 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 851 | void |
a9a2da38 BP |
852 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
853 | bool verbose) | |
f22716dc | 854 | { |
c821124b BP |
855 | enum { |
856 | F_OUT_PORT = 1 << 0, | |
857 | F_ACTIONS = 1 << 1, | |
c821124b | 858 | F_TIMEOUT = 1 << 3, |
a993007b BP |
859 | F_PRIORITY = 1 << 4, |
860 | F_FLAGS = 1 << 5, | |
c821124b | 861 | } fields; |
ec610b7b | 862 | char *string = xstrdup(str_); |
f22716dc | 863 | char *save_ptr = NULL; |
75a75043 | 864 | char *act_str = NULL; |
f22716dc | 865 | char *name; |
f22716dc | 866 | |
c821124b BP |
867 | switch (command) { |
868 | case -1: | |
869 | fields = F_OUT_PORT; | |
870 | break; | |
871 | ||
872 | case OFPFC_ADD: | |
a993007b | 873 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
874 | break; |
875 | ||
876 | case OFPFC_DELETE: | |
877 | fields = F_OUT_PORT; | |
878 | break; | |
879 | ||
880 | case OFPFC_DELETE_STRICT: | |
881 | fields = F_OUT_PORT | F_PRIORITY; | |
882 | break; | |
883 | ||
884 | case OFPFC_MODIFY: | |
a993007b | 885 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
886 | break; |
887 | ||
888 | case OFPFC_MODIFY_STRICT: | |
a993007b | 889 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
890 | break; |
891 | ||
892 | default: | |
893 | NOT_REACHED(); | |
894 | } | |
895 | ||
81a76618 BP |
896 | match_init_catchall(&fm->match); |
897 | fm->priority = OFP_DEFAULT_PRIORITY; | |
88ca35ee | 898 | fm->cookie = htonll(0); |
e729e793 | 899 | fm->cookie_mask = htonll(0); |
623e1caf JP |
900 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
901 | /* For modify, by default, don't update the cookie. */ | |
902 | fm->new_cookie = htonll(UINT64_MAX); | |
903 | } else{ | |
904 | fm->new_cookie = htonll(0); | |
905 | } | |
6c1491fb | 906 | fm->table_id = 0xff; |
c821124b | 907 | fm->command = command; |
88ca35ee BP |
908 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
909 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
910 | fm->buffer_id = UINT32_MAX; | |
7f05e7ab | 911 | fm->out_port = OFPP_ANY; |
88ca35ee | 912 | fm->flags = 0; |
c821124b | 913 | if (fields & F_ACTIONS) { |
c821124b | 914 | act_str = strstr(string, "action"); |
f22716dc | 915 | if (!act_str) { |
ec610b7b | 916 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
917 | } |
918 | *act_str = '\0'; | |
919 | ||
920 | act_str = strchr(act_str + 1, '='); | |
921 | if (!act_str) { | |
ec610b7b | 922 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
923 | } |
924 | ||
925 | act_str++; | |
f22716dc | 926 | } |
f22716dc JP |
927 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
928 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
929 | const struct protocol *p; | |
930 | ||
931 | if (parse_protocol(name, &p)) { | |
81a76618 | 932 | match_set_dl_type(&fm->match, htons(p->dl_type)); |
f22716dc | 933 | if (p->nw_proto) { |
81a76618 | 934 | match_set_nw_proto(&fm->match, p->nw_proto); |
f22716dc | 935 | } |
a993007b BP |
936 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
937 | fm->flags |= OFPFF_SEND_FLOW_REM; | |
938 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { | |
939 | fm->flags |= OFPFF_CHECK_OVERLAP; | |
2e1ae200 JR |
940 | } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { |
941 | fm->flags |= OFPFF12_RESET_COUNTS; | |
942 | } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { | |
943 | fm->flags |= OFPFF13_NO_PKT_COUNTS; | |
944 | } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { | |
945 | fm->flags |= OFPFF13_NO_BYT_COUNTS; | |
f22716dc | 946 | } else { |
f22716dc JP |
947 | char *value; |
948 | ||
949 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
950 | if (!value) { | |
ec610b7b | 951 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
952 | } |
953 | ||
6c1491fb | 954 | if (!strcmp(name, "table")) { |
638a19b0 | 955 | fm->table_id = str_to_u8(value, name); |
8050b31d | 956 | } else if (!strcmp(name, "out_port")) { |
8010100b | 957 | if (!ofputil_port_from_string(name, &fm->out_port)) { |
c6100d92 BP |
958 | ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port", |
959 | name); | |
960 | } | |
c821124b | 961 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
81a76618 | 962 | fm->priority = str_to_u16(value, name); |
c821124b | 963 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
c3636ffc | 964 | fm->idle_timeout = str_to_u16(value, name); |
c821124b | 965 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
c3636ffc | 966 | fm->hard_timeout = str_to_u16(value, name); |
e729e793 JP |
967 | } else if (!strcmp(name, "cookie")) { |
968 | char *mask = strchr(value, '/'); | |
623e1caf | 969 | |
e729e793 | 970 | if (mask) { |
623e1caf | 971 | /* A mask means we're searching for a cookie. */ |
e729e793 JP |
972 | if (command == OFPFC_ADD) { |
973 | ofp_fatal(str_, verbose, "flow additions cannot use " | |
974 | "a cookie mask"); | |
975 | } | |
976 | *mask = '\0'; | |
623e1caf | 977 | fm->cookie = htonll(str_to_u64(value)); |
e729e793 JP |
978 | fm->cookie_mask = htonll(str_to_u64(mask+1)); |
979 | } else { | |
623e1caf JP |
980 | /* No mask means that the cookie is being set. */ |
981 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
982 | && command != OFPFC_MODIFY_STRICT) { | |
983 | ofp_fatal(str_, verbose, "cannot set cookie"); | |
984 | } | |
985 | fm->new_cookie = htonll(str_to_u64(value)); | |
e729e793 | 986 | } |
6a885fd0 | 987 | } else if (mf_from_name(name)) { |
81a76618 | 988 | parse_field(mf_from_name(name), value, &fm->match); |
2c6d8411 BP |
989 | } else if (!strcmp(name, "duration") |
990 | || !strcmp(name, "n_packets") | |
146356e9 JP |
991 | || !strcmp(name, "n_bytes") |
992 | || !strcmp(name, "idle_age") | |
993 | || !strcmp(name, "hard_age")) { | |
2c6d8411 BP |
994 | /* Ignore these, so that users can feed the output of |
995 | * "ovs-ofctl dump-flows" back into commands that parse | |
996 | * flows. */ | |
f22716dc | 997 | } else { |
ec610b7b | 998 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
999 | } |
1000 | } | |
1001 | } | |
623e1caf JP |
1002 | if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX) |
1003 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { | |
1004 | /* On modifies without a mask, we are supposed to add a flow if | |
1005 | * one does not exist. If a cookie wasn't been specified, use a | |
1006 | * default of zero. */ | |
1007 | fm->new_cookie = htonll(0); | |
1008 | } | |
75a75043 | 1009 | if (fields & F_ACTIONS) { |
f25d0cf3 | 1010 | struct ofpbuf ofpacts; |
b019d34d | 1011 | enum ofperr err; |
75a75043 | 1012 | |
f25d0cf3 | 1013 | ofpbuf_init(&ofpacts, 32); |
dd43a558 | 1014 | str_to_inst_ofpacts(act_str, &ofpacts); |
f25d0cf3 BP |
1015 | fm->ofpacts_len = ofpacts.size; |
1016 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); | |
b019d34d SH |
1017 | |
1018 | err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow, | |
1019 | OFPP_MAX); | |
1020 | if (err) { | |
1021 | exit(EXIT_FAILURE); | |
1022 | } | |
1023 | ||
75a75043 | 1024 | } else { |
f25d0cf3 BP |
1025 | fm->ofpacts_len = 0; |
1026 | fm->ofpacts = NULL; | |
75a75043 | 1027 | } |
ec610b7b AE |
1028 | |
1029 | free(string); | |
f22716dc | 1030 | } |
15f1f1b6 | 1031 | |
638a19b0 JR |
1032 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
1033 | * page) into 'mm' for sending the specified meter_mod 'command' to a switch. | |
1034 | */ | |
1035 | void | |
1036 | parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, | |
1037 | int command, bool verbose) | |
1038 | { | |
1039 | enum { | |
1040 | F_METER = 1 << 0, | |
1041 | F_FLAGS = 1 << 1, | |
1042 | F_BANDS = 1 << 2, | |
1043 | } fields; | |
1044 | char *string = xstrdup(str_); | |
1045 | char *save_ptr = NULL; | |
1046 | char *band_str = NULL; | |
1047 | char *name; | |
1048 | ||
1049 | switch (command) { | |
1050 | case -1: | |
1051 | fields = F_METER; | |
1052 | break; | |
1053 | ||
1054 | case OFPMC13_ADD: | |
1055 | fields = F_METER | F_FLAGS | F_BANDS; | |
1056 | break; | |
1057 | ||
1058 | case OFPMC13_DELETE: | |
1059 | fields = F_METER; | |
1060 | break; | |
1061 | ||
1062 | case OFPMC13_MODIFY: | |
1063 | fields = F_METER | F_FLAGS | F_BANDS; | |
1064 | break; | |
1065 | ||
1066 | default: | |
1067 | NOT_REACHED(); | |
1068 | } | |
1069 | ||
1070 | mm->command = command; | |
1071 | mm->meter.meter_id = 0; | |
1072 | mm->meter.flags = 0; | |
1073 | if (fields & F_BANDS) { | |
1074 | band_str = strstr(string, "band"); | |
1075 | if (!band_str) { | |
1076 | ofp_fatal(str_, verbose, "must specify bands"); | |
1077 | } | |
1078 | *band_str = '\0'; | |
1079 | ||
1080 | band_str = strchr(band_str + 1, '='); | |
1081 | if (!band_str) { | |
1082 | ofp_fatal(str_, verbose, "must specify bands"); | |
1083 | } | |
1084 | ||
1085 | band_str++; | |
1086 | } | |
1087 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
1088 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
1089 | ||
1090 | if (fields & F_FLAGS && !strcmp(name, "kbps")) { | |
1091 | mm->meter.flags |= OFPMF13_KBPS; | |
1092 | } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { | |
1093 | mm->meter.flags |= OFPMF13_PKTPS; | |
1094 | } else if (fields & F_FLAGS && !strcmp(name, "burst")) { | |
1095 | mm->meter.flags |= OFPMF13_BURST; | |
1096 | } else if (fields & F_FLAGS && !strcmp(name, "stats")) { | |
1097 | mm->meter.flags |= OFPMF13_STATS; | |
1098 | } else { | |
1099 | char *value; | |
1100 | ||
1101 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
1102 | if (!value) { | |
1103 | ofp_fatal(str_, verbose, "field %s missing value", name); | |
1104 | } | |
1105 | ||
1106 | if (!strcmp(name, "meter")) { | |
1107 | if (!strcmp(value, "all")) { | |
1108 | mm->meter.meter_id = OFPM13_ALL; | |
1109 | } else if (!strcmp(value, "controller")) { | |
1110 | mm->meter.meter_id = OFPM13_CONTROLLER; | |
1111 | } else if (!strcmp(value, "slowpath")) { | |
1112 | mm->meter.meter_id = OFPM13_SLOWPATH; | |
1113 | } else { | |
1114 | mm->meter.meter_id = str_to_u32(value); | |
1115 | if (mm->meter.meter_id > OFPM13_MAX) { | |
1116 | ofp_fatal(str_, verbose, "invalid value for %s", name); | |
1117 | } | |
1118 | } | |
1119 | } else { | |
1120 | ofp_fatal(str_, verbose, "unknown keyword %s", name); | |
1121 | } | |
1122 | } | |
1123 | } | |
1124 | if (fields & F_METER && !mm->meter.meter_id) { | |
1125 | ofp_fatal(str_, verbose, "must specify 'meter'"); | |
1126 | } | |
1127 | if (fields & F_FLAGS && !mm->meter.flags) { | |
1128 | ofp_fatal(str_, verbose, | |
1129 | "meter must specify either 'kbps' or 'pktps'"); | |
1130 | } | |
1131 | ||
1132 | if (fields & F_BANDS) { | |
1133 | struct ofpbuf bands; | |
1134 | uint16_t n_bands = 0; | |
1135 | struct ofputil_meter_band *band = NULL; | |
1136 | int i; | |
1137 | ||
1138 | ofpbuf_init(&bands, 64); | |
1139 | ||
1140 | for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; | |
1141 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
1142 | ||
1143 | char *value; | |
1144 | ||
1145 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
1146 | if (!value) { | |
1147 | ofp_fatal(str_, verbose, "field %s missing value", name); | |
1148 | } | |
1149 | ||
1150 | if (!strcmp(name, "type")) { | |
1151 | /* Start a new band */ | |
1152 | band = ofpbuf_put_zeros(&bands, sizeof *band); | |
1153 | n_bands++; | |
1154 | ||
1155 | if (!strcmp(value, "drop")) { | |
1156 | band->type = OFPMBT13_DROP; | |
1157 | } else if (!strcmp(value, "dscp_remark")) { | |
1158 | band->type = OFPMBT13_DSCP_REMARK; | |
1159 | } else { | |
1160 | ofp_fatal(str_, verbose, "field %s unknown value %s", name, | |
1161 | value); | |
1162 | } | |
1163 | } else if (!band || !band->type) { | |
1164 | ofp_fatal(str_, verbose, | |
1165 | "band must start with the 'type' keyword"); | |
1166 | } else if (!strcmp(name, "rate")) { | |
1167 | band->rate = str_to_u32(value); | |
1168 | } else if (!strcmp(name, "burst_size")) { | |
1169 | band->burst_size = str_to_u32(value); | |
1170 | } else if (!strcmp(name, "prec_level")) { | |
1171 | band->prec_level = str_to_u8(value, name); | |
1172 | } else { | |
1173 | ofp_fatal(str_, verbose, "unknown keyword %s", name); | |
1174 | } | |
1175 | } | |
1176 | /* validate bands */ | |
1177 | if (!n_bands) { | |
1178 | ofp_fatal(str_, verbose, "meter must have bands"); | |
1179 | } | |
1180 | ||
1181 | mm->meter.n_bands = n_bands; | |
1182 | mm->meter.bands = ofpbuf_steal_data(&bands); | |
1183 | ||
1184 | for (i = 0; i < n_bands; ++i) { | |
1185 | band = &mm->meter.bands[i]; | |
1186 | ||
1187 | if (!band->type) { | |
1188 | ofp_fatal(str_, verbose, "band must have 'type'"); | |
1189 | } | |
1190 | if (band->type == OFPMBT13_DSCP_REMARK) { | |
1191 | if (!band->prec_level) { | |
1192 | ofp_fatal(str_, verbose, "'dscp_remark' band must have" | |
1193 | " 'prec_level'"); | |
1194 | } | |
1195 | } else { | |
1196 | if (band->prec_level) { | |
1197 | ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have" | |
1198 | " 'prec_level'"); | |
1199 | } | |
1200 | } | |
1201 | if (!band->rate) { | |
1202 | ofp_fatal(str_, verbose, "band must have 'rate'"); | |
1203 | } | |
1204 | if (mm->meter.flags & OFPMF13_BURST) { | |
1205 | if (!band->burst_size) { | |
1206 | ofp_fatal(str_, verbose, "band must have 'burst_size' " | |
1207 | "when 'burst' flag is set"); | |
1208 | } | |
1209 | } else { | |
1210 | if (band->burst_size) { | |
1211 | ofp_fatal(str_, verbose, "band may have 'burst_size' only " | |
1212 | "when 'burst' flag is set"); | |
1213 | } | |
1214 | } | |
1215 | } | |
1216 | } else { | |
1217 | mm->meter.n_bands = 0; | |
1218 | mm->meter.bands = NULL; | |
1219 | } | |
1220 | ||
1221 | free(string); | |
1222 | } | |
1223 | ||
2b07c8b1 BP |
1224 | /* Convert 'str_' (as described in the documentation for the "monitor" command |
1225 | * in the ovs-ofctl man page) into 'fmr'. */ | |
1226 | void | |
1227 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
1228 | const char *str_) | |
1229 | { | |
1230 | static uint32_t id; | |
1231 | ||
1232 | char *string = xstrdup(str_); | |
1233 | char *save_ptr = NULL; | |
1234 | char *name; | |
1235 | ||
1236 | fmr->id = id++; | |
1237 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | |
1238 | | NXFMF_OWN | NXFMF_ACTIONS); | |
1239 | fmr->out_port = OFPP_NONE; | |
1240 | fmr->table_id = 0xff; | |
81a76618 | 1241 | match_init_catchall(&fmr->match); |
2b07c8b1 BP |
1242 | |
1243 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
1244 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
1245 | const struct protocol *p; | |
1246 | ||
1247 | if (!strcmp(name, "!initial")) { | |
1248 | fmr->flags &= ~NXFMF_INITIAL; | |
1249 | } else if (!strcmp(name, "!add")) { | |
1250 | fmr->flags &= ~NXFMF_ADD; | |
1251 | } else if (!strcmp(name, "!delete")) { | |
1252 | fmr->flags &= ~NXFMF_DELETE; | |
1253 | } else if (!strcmp(name, "!modify")) { | |
1254 | fmr->flags &= ~NXFMF_MODIFY; | |
1255 | } else if (!strcmp(name, "!actions")) { | |
1256 | fmr->flags &= ~NXFMF_ACTIONS; | |
1257 | } else if (!strcmp(name, "!own")) { | |
1258 | fmr->flags &= ~NXFMF_OWN; | |
1259 | } else if (parse_protocol(name, &p)) { | |
81a76618 | 1260 | match_set_dl_type(&fmr->match, htons(p->dl_type)); |
2b07c8b1 | 1261 | if (p->nw_proto) { |
81a76618 | 1262 | match_set_nw_proto(&fmr->match, p->nw_proto); |
2b07c8b1 BP |
1263 | } |
1264 | } else { | |
1265 | char *value; | |
1266 | ||
1267 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
1268 | if (!value) { | |
1269 | ovs_fatal(0, "%s: field %s missing value", str_, name); | |
1270 | } | |
1271 | ||
1272 | if (!strcmp(name, "table")) { | |
638a19b0 | 1273 | fmr->table_id = str_to_u8(value, name); |
2b07c8b1 | 1274 | } else if (!strcmp(name, "out_port")) { |
4e022ec0 | 1275 | fmr->out_port = u16_to_ofp(atoi(value)); |
2b07c8b1 BP |
1276 | } else if (mf_from_name(name)) { |
1277 | parse_field(mf_from_name(name), value, &fmr->match); | |
1278 | } else { | |
1279 | ovs_fatal(0, "%s: unknown keyword %s", str_, name); | |
1280 | } | |
1281 | } | |
1282 | } | |
1283 | free(string); | |
1284 | } | |
1285 | ||
0c3d5fc8 BP |
1286 | /* Parses 's' as a set of OpenFlow actions and appends the actions to |
1287 | * 'actions'. | |
1288 | * | |
1289 | * Prints an error on stderr and aborts the program if 's' syntax is | |
1290 | * invalid. */ | |
1291 | void | |
f25d0cf3 | 1292 | parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) |
0c3d5fc8 BP |
1293 | { |
1294 | char *s = xstrdup(s_); | |
dd43a558 | 1295 | str_to_ofpacts(s, ofpacts); |
0c3d5fc8 BP |
1296 | free(s); |
1297 | } | |
1298 | ||
88ca35ee | 1299 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
27527aa0 | 1300 | * (one of OFPFC_*) into 'fm'. */ |
88ca35ee | 1301 | void |
27527aa0 | 1302 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
a7fc1744 | 1303 | uint16_t command, bool verbose) |
15f1f1b6 | 1304 | { |
81a76618 | 1305 | struct match match_copy; |
88ca35ee | 1306 | |
27527aa0 | 1307 | parse_ofp_str(fm, command, string, verbose); |
88ca35ee | 1308 | |
81a76618 | 1309 | /* Normalize a copy of the match. This ensures that non-normalized flows |
01b389b1 BP |
1310 | * get logged but doesn't affect what gets sent to the switch, so that the |
1311 | * switch can do whatever it likes with the flow. */ | |
81a76618 BP |
1312 | match_copy = fm->match; |
1313 | ofputil_normalize_match(&match_copy); | |
15f1f1b6 BP |
1314 | } |
1315 | ||
27527aa0 BP |
1316 | void |
1317 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, | |
1318 | struct ofputil_flow_mod **fms, size_t *n_fms) | |
15f1f1b6 | 1319 | { |
27527aa0 BP |
1320 | size_t allocated_fms; |
1321 | FILE *stream; | |
dd8101bc | 1322 | struct ds s; |
15f1f1b6 | 1323 | |
27527aa0 BP |
1324 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
1325 | if (stream == NULL) { | |
1326 | ovs_fatal(errno, "%s: open", file_name); | |
1327 | } | |
1328 | ||
1329 | allocated_fms = *n_fms; | |
dd8101bc | 1330 | ds_init(&s); |
27527aa0 BP |
1331 | while (!ds_get_preprocessed_line(&s, stream)) { |
1332 | if (*n_fms >= allocated_fms) { | |
1333 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
1334 | } | |
1335 | parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false); | |
1336 | *n_fms += 1; | |
15f1f1b6 BP |
1337 | } |
1338 | ds_destroy(&s); | |
1339 | ||
27527aa0 BP |
1340 | if (stream != stdin) { |
1341 | fclose(stream); | |
1342 | } | |
88ca35ee BP |
1343 | } |
1344 | ||
1345 | void | |
81d1ea94 | 1346 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
a7fc1744 | 1347 | bool aggregate, const char *string) |
88ca35ee | 1348 | { |
a9a2da38 | 1349 | struct ofputil_flow_mod fm; |
88ca35ee | 1350 | |
c821124b | 1351 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee | 1352 | fsr->aggregate = aggregate; |
e729e793 JP |
1353 | fsr->cookie = fm.cookie; |
1354 | fsr->cookie_mask = fm.cookie_mask; | |
81a76618 | 1355 | fsr->match = fm.match; |
88ca35ee | 1356 | fsr->out_port = fm.out_port; |
6c1491fb | 1357 | fsr->table_id = fm.table_id; |
15f1f1b6 | 1358 | } |
ccbe50f8 BP |
1359 | |
1360 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
1361 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
1362 | * mf_field. Fields must be specified in a natural order for satisfying | |
1363 | * prerequisites. | |
1364 | * | |
1365 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
1366 | * problem. */ | |
1367 | char * | |
1368 | parse_ofp_exact_flow(struct flow *flow, const char *s) | |
1369 | { | |
1370 | char *pos, *key, *value_s; | |
1371 | char *error = NULL; | |
1372 | char *copy; | |
1373 | ||
1374 | memset(flow, 0, sizeof *flow); | |
1375 | ||
1376 | pos = copy = xstrdup(s); | |
1377 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
1378 | const struct protocol *p; | |
1379 | if (parse_protocol(key, &p)) { | |
1380 | if (flow->dl_type) { | |
1381 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
1382 | goto exit; | |
1383 | } | |
1384 | flow->dl_type = htons(p->dl_type); | |
1385 | ||
1386 | if (p->nw_proto) { | |
1387 | if (flow->nw_proto) { | |
1388 | error = xasprintf("%s: network protocol set " | |
1389 | "multiple times", s); | |
1390 | goto exit; | |
1391 | } | |
1392 | flow->nw_proto = p->nw_proto; | |
1393 | } | |
1394 | } else { | |
1395 | const struct mf_field *mf; | |
1396 | union mf_value value; | |
1397 | char *field_error; | |
1398 | ||
1399 | mf = mf_from_name(key); | |
1400 | if (!mf) { | |
1401 | error = xasprintf("%s: unknown field %s", s, key); | |
1402 | goto exit; | |
1403 | } | |
1404 | ||
1405 | if (!mf_are_prereqs_ok(mf, flow)) { | |
1406 | error = xasprintf("%s: prerequisites not met for setting %s", | |
1407 | s, key); | |
1408 | goto exit; | |
1409 | } | |
1410 | ||
1411 | if (!mf_is_zero(mf, flow)) { | |
1412 | error = xasprintf("%s: field %s set multiple times", s, key); | |
1413 | goto exit; | |
1414 | } | |
1415 | ||
1416 | field_error = mf_parse_value(mf, value_s, &value); | |
1417 | if (field_error) { | |
1418 | error = xasprintf("%s: bad value for %s (%s)", | |
1419 | s, key, field_error); | |
1420 | free(field_error); | |
1421 | goto exit; | |
1422 | } | |
1423 | ||
1424 | mf_set_flow_value(mf, &value, flow); | |
1425 | } | |
1426 | } | |
1427 | ||
4e022ec0 AW |
1428 | if (!flow->in_port.ofp_port) { |
1429 | flow->in_port.ofp_port = OFPP_NONE; | |
72d64e33 EJ |
1430 | } |
1431 | ||
ccbe50f8 BP |
1432 | exit: |
1433 | free(copy); | |
1434 | ||
1435 | if (error) { | |
1436 | memset(flow, 0, sizeof *flow); | |
1437 | } | |
1438 | return error; | |
1439 | } |