]>
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]) { | |
8010100b | 171 | if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { |
c6100d92 | 172 | ovs_fatal(0, "%s: resubmit to unknown port", 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 | 281 | static void |
99086062 | 282 | parse_noargs_dec_ttl(struct ofpbuf *b) |
c2d967a5 MM |
283 | { |
284 | struct ofpact_cnt_ids *ids; | |
7bcb1506 | 285 | uint16_t id = 0; |
c2d967a5 MM |
286 | |
287 | ids = ofpact_put_DEC_TTL(b); | |
7bcb1506 IY |
288 | ofpbuf_put(b, &id, sizeof id); |
289 | ids = b->l2; | |
290 | ids->n_controllers++; | |
291 | ofpact_update_len(b, &ids->ofpact); | |
292 | } | |
c2d967a5 | 293 | |
7bcb1506 | 294 | static void |
99086062 | 295 | parse_dec_ttl(struct ofpbuf *b, char *arg) |
7bcb1506 | 296 | { |
c2d967a5 | 297 | if (*arg == '\0') { |
99086062 | 298 | parse_noargs_dec_ttl(b); |
c2d967a5 | 299 | } else { |
7bcb1506 | 300 | struct ofpact_cnt_ids *ids; |
c2d967a5 MM |
301 | char *cntr; |
302 | ||
7bcb1506 | 303 | ids = ofpact_put_DEC_TTL(b); |
c2d967a5 MM |
304 | ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; |
305 | for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; | |
306 | cntr = strtok_r(NULL, ", ", &arg)) { | |
307 | uint16_t id = atoi(cntr); | |
308 | ||
309 | ofpbuf_put(b, &id, sizeof id); | |
310 | ids = b->l2; | |
311 | ids->n_controllers++; | |
312 | } | |
313 | if (!ids->n_controllers) { | |
314 | ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller " | |
315 | "id."); | |
316 | } | |
7bcb1506 | 317 | ofpact_update_len(b, &ids->ofpact); |
c2d967a5 | 318 | } |
c2d967a5 MM |
319 | } |
320 | ||
f5c45121 SH |
321 | static void |
322 | set_field_parse(const char *arg, struct ofpbuf *ofpacts) | |
323 | { | |
324 | char *orig = xstrdup(arg); | |
325 | struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts); | |
326 | char *value; | |
327 | char *delim; | |
328 | char *key; | |
329 | const struct mf_field *mf; | |
330 | const char *error; | |
331 | union mf_value mf_value; | |
332 | ||
333 | value = orig; | |
334 | delim = strstr(orig, "->"); | |
335 | if (!delim) { | |
336 | ovs_fatal(0, "%s: missing `->'", orig); | |
337 | } | |
338 | if (strlen(delim) <= strlen("->")) { | |
339 | ovs_fatal(0, "%s: missing field name following `->'", orig); | |
340 | } | |
341 | ||
342 | key = delim + strlen("->"); | |
343 | mf = mf_from_name(key); | |
344 | if (!mf) { | |
345 | ovs_fatal(0, "%s is not valid oxm field name", key); | |
346 | } | |
347 | if (!mf->writable) { | |
348 | ovs_fatal(0, "%s is not allowed to set", key); | |
349 | } | |
350 | ||
351 | delim[0] = '\0'; | |
352 | error = mf_parse_value(mf, value, &mf_value); | |
353 | if (error) { | |
354 | ovs_fatal(0, "%s", error); | |
355 | } | |
356 | if (!mf_is_value_valid(mf, &mf_value)) { | |
357 | ovs_fatal(0, "%s is not valid valid for field %s", value, key); | |
358 | } | |
359 | ofpact_set_field_init(load, mf, &mf_value); | |
360 | free(orig); | |
361 | } | |
362 | ||
4cceacb9 JS |
363 | static void |
364 | parse_metadata(struct ofpbuf *b, char *arg) | |
365 | { | |
366 | struct ofpact_metadata *om; | |
367 | char *mask = strchr(arg, '/'); | |
368 | ||
369 | om = ofpact_put_WRITE_METADATA(b); | |
370 | ||
371 | if (mask) { | |
372 | *mask = '\0'; | |
373 | om->mask = htonll(str_to_u64(mask + 1)); | |
374 | } else { | |
375 | om->mask = htonll(UINT64_MAX); | |
376 | } | |
377 | ||
378 | om->metadata = htonll(str_to_u64(arg)); | |
379 | } | |
380 | ||
333eba21 | 381 | static void |
75a75043 | 382 | parse_named_action(enum ofputil_action_code code, const struct flow *flow, |
f25d0cf3 | 383 | char *arg, struct ofpbuf *ofpacts) |
333eba21 | 384 | { |
f25d0cf3 BP |
385 | struct ofpact_tunnel *tunnel; |
386 | uint16_t vid; | |
3e34fbdd | 387 | uint16_t ethertype; |
f25d0cf3 BP |
388 | ovs_be32 ip; |
389 | uint8_t pcp; | |
390 | uint8_t tos; | |
333eba21 BP |
391 | |
392 | switch (code) { | |
690a61c5 BP |
393 | case OFPUTIL_ACTION_INVALID: |
394 | NOT_REACHED(); | |
395 | ||
08f94c0e | 396 | case OFPUTIL_OFPAT10_OUTPUT: |
d01c980f | 397 | case OFPUTIL_OFPAT11_OUTPUT: |
f25d0cf3 | 398 | parse_output(arg, ofpacts); |
333eba21 BP |
399 | break; |
400 | ||
08f94c0e | 401 | case OFPUTIL_OFPAT10_SET_VLAN_VID: |
d01c980f | 402 | case OFPUTIL_OFPAT11_SET_VLAN_VID: |
f25d0cf3 BP |
403 | vid = str_to_u32(arg); |
404 | if (vid & ~VLAN_VID_MASK) { | |
405 | ovs_fatal(0, "%s: not a valid VLAN VID", arg); | |
406 | } | |
407 | ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; | |
333eba21 BP |
408 | break; |
409 | ||
08f94c0e | 410 | case OFPUTIL_OFPAT10_SET_VLAN_PCP: |
d01c980f | 411 | case OFPUTIL_OFPAT11_SET_VLAN_PCP: |
f25d0cf3 BP |
412 | pcp = str_to_u32(arg); |
413 | if (pcp & ~7) { | |
414 | ovs_fatal(0, "%s: not a valid VLAN PCP", arg); | |
415 | } | |
416 | ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; | |
333eba21 BP |
417 | break; |
418 | ||
78a3fff6 | 419 | case OFPUTIL_OFPAT12_SET_FIELD: |
f5c45121 | 420 | set_field_parse(arg, ofpacts); |
78a3fff6 SH |
421 | break; |
422 | ||
08f94c0e | 423 | case OFPUTIL_OFPAT10_STRIP_VLAN: |
8e61c110 | 424 | case OFPUTIL_OFPAT11_POP_VLAN: |
f25d0cf3 | 425 | ofpact_put_STRIP_VLAN(ofpacts); |
333eba21 BP |
426 | break; |
427 | ||
3e34fbdd IY |
428 | case OFPUTIL_OFPAT11_PUSH_VLAN: |
429 | ethertype = str_to_u16(arg, "ethertype"); | |
430 | if (ethertype != ETH_TYPE_VLAN_8021Q) { | |
431 | /* TODO:XXXX ETH_TYPE_VLAN_8021AD case isn't supported */ | |
432 | ovs_fatal(0, "%s: not a valid VLAN ethertype", arg); | |
433 | } | |
434 | ofpact_put_PUSH_VLAN(ofpacts); | |
435 | break; | |
436 | ||
08f94c0e | 437 | case OFPUTIL_OFPAT10_SET_DL_SRC: |
d01c980f | 438 | case OFPUTIL_OFPAT11_SET_DL_SRC: |
f25d0cf3 BP |
439 | str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); |
440 | break; | |
441 | ||
08f94c0e | 442 | case OFPUTIL_OFPAT10_SET_DL_DST: |
d01c980f | 443 | case OFPUTIL_OFPAT11_SET_DL_DST: |
f25d0cf3 | 444 | str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); |
333eba21 BP |
445 | break; |
446 | ||
08f94c0e | 447 | case OFPUTIL_OFPAT10_SET_NW_SRC: |
d01c980f | 448 | case OFPUTIL_OFPAT11_SET_NW_SRC: |
f25d0cf3 BP |
449 | str_to_ip(arg, &ip); |
450 | ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; | |
451 | break; | |
452 | ||
08f94c0e | 453 | case OFPUTIL_OFPAT10_SET_NW_DST: |
d01c980f | 454 | case OFPUTIL_OFPAT11_SET_NW_DST: |
f25d0cf3 BP |
455 | str_to_ip(arg, &ip); |
456 | ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; | |
333eba21 BP |
457 | break; |
458 | ||
08f94c0e | 459 | case OFPUTIL_OFPAT10_SET_NW_TOS: |
d01c980f | 460 | case OFPUTIL_OFPAT11_SET_NW_TOS: |
f25d0cf3 BP |
461 | tos = str_to_u32(arg); |
462 | if (tos & ~IP_DSCP_MASK) { | |
463 | ovs_fatal(0, "%s: not a valid TOS", arg); | |
464 | } | |
465 | ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; | |
333eba21 BP |
466 | break; |
467 | ||
7bcb1506 | 468 | case OFPUTIL_OFPAT11_DEC_NW_TTL: |
68194f84 | 469 | NOT_REACHED(); |
7bcb1506 | 470 | |
08f94c0e | 471 | case OFPUTIL_OFPAT10_SET_TP_SRC: |
d01c980f | 472 | case OFPUTIL_OFPAT11_SET_TP_SRC: |
f25d0cf3 BP |
473 | ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); |
474 | break; | |
475 | ||
08f94c0e | 476 | case OFPUTIL_OFPAT10_SET_TP_DST: |
d01c980f | 477 | case OFPUTIL_OFPAT11_SET_TP_DST: |
f25d0cf3 | 478 | ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); |
333eba21 BP |
479 | break; |
480 | ||
08f94c0e | 481 | case OFPUTIL_OFPAT10_ENQUEUE: |
f25d0cf3 | 482 | parse_enqueue(arg, ofpacts); |
333eba21 BP |
483 | break; |
484 | ||
485 | case OFPUTIL_NXAST_RESUBMIT: | |
f25d0cf3 | 486 | parse_resubmit(arg, ofpacts); |
333eba21 BP |
487 | break; |
488 | ||
489 | case OFPUTIL_NXAST_SET_TUNNEL: | |
f25d0cf3 BP |
490 | case OFPUTIL_NXAST_SET_TUNNEL64: |
491 | tunnel = ofpact_put_SET_TUNNEL(ofpacts); | |
492 | tunnel->ofpact.compat = code; | |
493 | tunnel->tun_id = str_to_u64(arg); | |
333eba21 BP |
494 | break; |
495 | ||
4cceacb9 JS |
496 | case OFPUTIL_NXAST_WRITE_METADATA: |
497 | parse_metadata(ofpacts, arg); | |
498 | break; | |
499 | ||
333eba21 | 500 | case OFPUTIL_NXAST_SET_QUEUE: |
f25d0cf3 | 501 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); |
333eba21 BP |
502 | break; |
503 | ||
504 | case OFPUTIL_NXAST_POP_QUEUE: | |
f25d0cf3 | 505 | ofpact_put_POP_QUEUE(ofpacts); |
333eba21 BP |
506 | break; |
507 | ||
508 | case OFPUTIL_NXAST_REG_MOVE: | |
f25d0cf3 | 509 | nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); |
333eba21 BP |
510 | break; |
511 | ||
512 | case OFPUTIL_NXAST_REG_LOAD: | |
f25d0cf3 | 513 | nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); |
333eba21 BP |
514 | break; |
515 | ||
516 | case OFPUTIL_NXAST_NOTE: | |
f25d0cf3 | 517 | parse_note(arg, ofpacts); |
93996add BP |
518 | break; |
519 | ||
333eba21 | 520 | case OFPUTIL_NXAST_MULTIPATH: |
f25d0cf3 | 521 | multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); |
333eba21 BP |
522 | break; |
523 | ||
c51c638a | 524 | case OFPUTIL_NXAST_AUTOPATH__DEPRECATED: |
f25d0cf3 | 525 | autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg); |
333eba21 BP |
526 | break; |
527 | ||
528 | case OFPUTIL_NXAST_BUNDLE: | |
f25d0cf3 | 529 | bundle_parse(arg, ofpacts); |
333eba21 BP |
530 | break; |
531 | ||
532 | case OFPUTIL_NXAST_BUNDLE_LOAD: | |
f25d0cf3 | 533 | bundle_parse_load(arg, ofpacts); |
333eba21 BP |
534 | break; |
535 | ||
536 | case OFPUTIL_NXAST_RESUBMIT_TABLE: | |
537 | case OFPUTIL_NXAST_OUTPUT_REG: | |
c2d967a5 | 538 | case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: |
333eba21 | 539 | NOT_REACHED(); |
75a75043 BP |
540 | |
541 | case OFPUTIL_NXAST_LEARN: | |
f25d0cf3 | 542 | learn_parse(arg, flow, ofpacts); |
75a75043 | 543 | break; |
a61680c6 | 544 | |
848e8809 | 545 | case OFPUTIL_NXAST_EXIT: |
f25d0cf3 | 546 | ofpact_put_EXIT(ofpacts); |
848e8809 | 547 | break; |
f0fd1a17 PS |
548 | |
549 | case OFPUTIL_NXAST_DEC_TTL: | |
99086062 | 550 | parse_dec_ttl(ofpacts, arg); |
f0fd1a17 | 551 | break; |
0e553d9c BP |
552 | |
553 | case OFPUTIL_NXAST_FIN_TIMEOUT: | |
f25d0cf3 | 554 | parse_fin_timeout(ofpacts, arg); |
0e553d9c | 555 | break; |
a7349929 BP |
556 | |
557 | case OFPUTIL_NXAST_CONTROLLER: | |
f25d0cf3 | 558 | parse_controller(ofpacts, arg); |
a7349929 | 559 | break; |
333eba21 BP |
560 | } |
561 | } | |
562 | ||
8dd54666 IY |
563 | static bool |
564 | str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg, | |
565 | struct ofpbuf *ofpacts, int n_actions) | |
566 | { | |
567 | int code = ofputil_action_code_from_name(act); | |
568 | if (code >= 0) { | |
569 | parse_named_action(code, flow, arg, ofpacts); | |
570 | } else if (!strcasecmp(act, "drop")) { | |
571 | if (n_actions) { | |
572 | ovs_fatal(0, "Drop actions must not be preceded by other " | |
573 | "actions"); | |
574 | } else if (ofputil_parse_key_value(&pos, &act, &arg)) { | |
575 | ovs_fatal(0, "Drop actions must not be followed by other " | |
576 | "actions"); | |
577 | } | |
578 | return false; | |
579 | } else { | |
8010100b BP |
580 | uint16_t port; |
581 | if (ofputil_port_from_string(act, &port)) { | |
8dd54666 IY |
582 | ofpact_put_OUTPUT(ofpacts)->port = port; |
583 | } else { | |
584 | ovs_fatal(0, "Unknown action: %s", act); | |
585 | } | |
586 | } | |
587 | ||
588 | return true; | |
589 | } | |
590 | ||
f22716dc | 591 | static void |
f25d0cf3 | 592 | str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) |
f22716dc | 593 | { |
0ff22822 | 594 | char *pos, *act, *arg; |
4cceacb9 | 595 | enum ofperr error; |
f22716dc JP |
596 | int n_actions; |
597 | ||
53ddd40a | 598 | pos = str; |
d13803eb | 599 | n_actions = 0; |
0ff22822 | 600 | while (ofputil_parse_key_value(&pos, &act, &arg)) { |
8dd54666 | 601 | if (!str_to_ofpact__(flow, pos, act, arg, ofpacts, n_actions)) { |
0ff22822 | 602 | break; |
8dd54666 IY |
603 | } |
604 | n_actions++; | |
605 | } | |
4cceacb9 JS |
606 | |
607 | error = ofpacts_verify(ofpacts->data, ofpacts->size); | |
608 | if (error) { | |
609 | ovs_fatal(0, "Incorrect action ordering"); | |
610 | } | |
611 | ||
8dd54666 IY |
612 | ofpact_pad(ofpacts); |
613 | } | |
614 | ||
615 | static void | |
616 | parse_named_instruction(enum ovs_instruction_type type, | |
617 | char *arg, struct ofpbuf *ofpacts) | |
618 | { | |
4cceacb9 JS |
619 | enum ofperr error; |
620 | ||
8dd54666 IY |
621 | switch (type) { |
622 | case OVSINST_OFPIT11_APPLY_ACTIONS: | |
623 | NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */ | |
624 | break; | |
625 | ||
626 | case OVSINST_OFPIT11_WRITE_ACTIONS: | |
627 | /* TODO:XXX */ | |
628 | ovs_fatal(0, "instruction write-actions is not supported yet"); | |
629 | break; | |
630 | ||
631 | case OVSINST_OFPIT11_CLEAR_ACTIONS: | |
b19e8793 | 632 | ofpact_put_CLEAR_ACTIONS(ofpacts); |
8dd54666 IY |
633 | break; |
634 | ||
635 | case OVSINST_OFPIT11_WRITE_METADATA: | |
4cceacb9 | 636 | parse_metadata(ofpacts, arg); |
8dd54666 IY |
637 | break; |
638 | ||
639 | case OVSINST_OFPIT11_GOTO_TABLE: { | |
640 | struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); | |
641 | char *table_s = strsep(&arg, ","); | |
642 | if (!table_s || !table_s[0]) { | |
643 | ovs_fatal(0, "instruction goto-table needs table id"); | |
644 | } | |
645 | ogt->table_id = str_to_table_id(table_s); | |
646 | break; | |
647 | } | |
648 | } | |
4cceacb9 JS |
649 | |
650 | /* If write_metadata is specified as an action AND an instruction, ofpacts | |
651 | could be invalid. */ | |
652 | error = ofpacts_verify(ofpacts->data, ofpacts->size); | |
653 | if (error) { | |
654 | ovs_fatal(0, "Incorrect instruction ordering"); | |
655 | } | |
8dd54666 IY |
656 | } |
657 | ||
658 | static void | |
659 | str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) | |
660 | { | |
661 | char *pos, *inst, *arg; | |
662 | int type; | |
663 | const char *prev_inst = NULL; | |
664 | int prev_type = -1; | |
665 | int n_actions = 0; | |
666 | ||
667 | pos = str; | |
668 | while (ofputil_parse_key_value(&pos, &inst, &arg)) { | |
669 | type = ofpact_instruction_type_from_name(inst); | |
670 | if (type < 0) { | |
671 | if (!str_to_ofpact__(flow, pos, inst, arg, ofpacts, n_actions)) { | |
672 | break; | |
673 | } | |
674 | ||
675 | type = OVSINST_OFPIT11_APPLY_ACTIONS; | |
676 | if (prev_type == type) { | |
677 | n_actions++; | |
678 | continue; | |
c6100d92 | 679 | } |
8dd54666 IY |
680 | } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) { |
681 | ovs_fatal(0, "%s isn't supported. Just write actions then " | |
682 | "it is interpreted as apply_actions", inst); | |
683 | } else { | |
684 | parse_named_instruction(type, arg, ofpacts); | |
685 | } | |
686 | ||
687 | if (type == prev_type) { | |
688 | ovs_fatal(0, "instruction can be specified at most once: %s", | |
689 | inst); | |
f22716dc | 690 | } |
8dd54666 IY |
691 | if (type <= prev_type) { |
692 | ovs_fatal(0, "Instruction %s must be specified before %s", | |
693 | inst, prev_inst); | |
694 | } | |
695 | ||
696 | prev_inst = inst; | |
697 | prev_type = type; | |
d13803eb | 698 | n_actions++; |
f22716dc | 699 | } |
f25d0cf3 | 700 | ofpact_pad(ofpacts); |
f22716dc JP |
701 | } |
702 | ||
703 | struct protocol { | |
704 | const char *name; | |
705 | uint16_t dl_type; | |
706 | uint8_t nw_proto; | |
707 | }; | |
708 | ||
709 | static bool | |
710 | parse_protocol(const char *name, const struct protocol **p_out) | |
711 | { | |
712 | static const struct protocol protocols[] = { | |
713 | { "ip", ETH_TYPE_IP, 0 }, | |
714 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
715 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
716 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
717 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
d31f1109 JP |
718 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
719 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
720 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
721 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
722 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
8087f5ff MM |
723 | { "rarp", ETH_TYPE_RARP, 0}, |
724 | }; | |
f22716dc JP |
725 | const struct protocol *p; |
726 | ||
727 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
728 | if (!strcmp(p->name, name)) { | |
729 | *p_out = p; | |
730 | return true; | |
731 | } | |
732 | } | |
733 | *p_out = NULL; | |
734 | return false; | |
735 | } | |
736 | ||
ec610b7b AE |
737 | static void |
738 | ofp_fatal(const char *flow, bool verbose, const char *format, ...) | |
739 | { | |
740 | va_list args; | |
741 | ||
742 | if (verbose) { | |
743 | fprintf(stderr, "%s:\n", flow); | |
744 | } | |
745 | ||
746 | va_start(args, format); | |
747 | ovs_fatal_valist(0, format, args); | |
748 | } | |
749 | ||
8050b31d | 750 | static void |
81a76618 | 751 | parse_field(const struct mf_field *mf, const char *s, struct match *match) |
8050b31d | 752 | { |
6a885fd0 BP |
753 | union mf_value value, mask; |
754 | char *error; | |
bad68a99 | 755 | |
6a885fd0 BP |
756 | error = mf_parse(mf, s, &value, &mask); |
757 | if (error) { | |
758 | ovs_fatal(0, "%s", error); | |
8050b31d | 759 | } |
8050b31d | 760 | |
81a76618 | 761 | mf_set(mf, &value, &mask, match); |
00b1c62f BP |
762 | } |
763 | ||
c821124b BP |
764 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
765 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. | |
766 | * If 'actions' is specified, an action must be in 'string' and may be expanded | |
767 | * or reallocated. | |
768 | * | |
769 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
770 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
771 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */ | |
0199c526 | 772 | void |
a9a2da38 BP |
773 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
774 | bool verbose) | |
f22716dc | 775 | { |
c821124b BP |
776 | enum { |
777 | F_OUT_PORT = 1 << 0, | |
778 | F_ACTIONS = 1 << 1, | |
c821124b | 779 | F_TIMEOUT = 1 << 3, |
a993007b BP |
780 | F_PRIORITY = 1 << 4, |
781 | F_FLAGS = 1 << 5, | |
c821124b | 782 | } fields; |
ec610b7b | 783 | char *string = xstrdup(str_); |
f22716dc | 784 | char *save_ptr = NULL; |
75a75043 | 785 | char *act_str = NULL; |
f22716dc | 786 | char *name; |
f22716dc | 787 | |
c821124b BP |
788 | switch (command) { |
789 | case -1: | |
790 | fields = F_OUT_PORT; | |
791 | break; | |
792 | ||
793 | case OFPFC_ADD: | |
a993007b | 794 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
795 | break; |
796 | ||
797 | case OFPFC_DELETE: | |
798 | fields = F_OUT_PORT; | |
799 | break; | |
800 | ||
801 | case OFPFC_DELETE_STRICT: | |
802 | fields = F_OUT_PORT | F_PRIORITY; | |
803 | break; | |
804 | ||
805 | case OFPFC_MODIFY: | |
a993007b | 806 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
807 | break; |
808 | ||
809 | case OFPFC_MODIFY_STRICT: | |
a993007b | 810 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
811 | break; |
812 | ||
813 | default: | |
814 | NOT_REACHED(); | |
815 | } | |
816 | ||
81a76618 BP |
817 | match_init_catchall(&fm->match); |
818 | fm->priority = OFP_DEFAULT_PRIORITY; | |
88ca35ee | 819 | fm->cookie = htonll(0); |
e729e793 | 820 | fm->cookie_mask = htonll(0); |
623e1caf JP |
821 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
822 | /* For modify, by default, don't update the cookie. */ | |
823 | fm->new_cookie = htonll(UINT64_MAX); | |
824 | } else{ | |
825 | fm->new_cookie = htonll(0); | |
826 | } | |
6c1491fb | 827 | fm->table_id = 0xff; |
c821124b | 828 | fm->command = command; |
88ca35ee BP |
829 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
830 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
831 | fm->buffer_id = UINT32_MAX; | |
7f05e7ab | 832 | fm->out_port = OFPP_ANY; |
88ca35ee | 833 | fm->flags = 0; |
c821124b | 834 | if (fields & F_ACTIONS) { |
c821124b | 835 | act_str = strstr(string, "action"); |
f22716dc | 836 | if (!act_str) { |
ec610b7b | 837 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
838 | } |
839 | *act_str = '\0'; | |
840 | ||
841 | act_str = strchr(act_str + 1, '='); | |
842 | if (!act_str) { | |
ec610b7b | 843 | ofp_fatal(str_, verbose, "must specify an action"); |
f22716dc JP |
844 | } |
845 | ||
846 | act_str++; | |
f22716dc | 847 | } |
f22716dc JP |
848 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
849 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
850 | const struct protocol *p; | |
851 | ||
852 | if (parse_protocol(name, &p)) { | |
81a76618 | 853 | match_set_dl_type(&fm->match, htons(p->dl_type)); |
f22716dc | 854 | if (p->nw_proto) { |
81a76618 | 855 | match_set_nw_proto(&fm->match, p->nw_proto); |
f22716dc | 856 | } |
a993007b BP |
857 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
858 | fm->flags |= OFPFF_SEND_FLOW_REM; | |
859 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { | |
860 | fm->flags |= OFPFF_CHECK_OVERLAP; | |
2e1ae200 JR |
861 | } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { |
862 | fm->flags |= OFPFF12_RESET_COUNTS; | |
863 | } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { | |
864 | fm->flags |= OFPFF13_NO_PKT_COUNTS; | |
865 | } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { | |
866 | fm->flags |= OFPFF13_NO_BYT_COUNTS; | |
f22716dc | 867 | } else { |
f22716dc JP |
868 | char *value; |
869 | ||
870 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
871 | if (!value) { | |
ec610b7b | 872 | ofp_fatal(str_, verbose, "field %s missing value", name); |
f22716dc JP |
873 | } |
874 | ||
6c1491fb | 875 | if (!strcmp(name, "table")) { |
c3636ffc | 876 | fm->table_id = str_to_table_id(value); |
8050b31d | 877 | } else if (!strcmp(name, "out_port")) { |
8010100b | 878 | if (!ofputil_port_from_string(name, &fm->out_port)) { |
c6100d92 BP |
879 | ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port", |
880 | name); | |
881 | } | |
c821124b | 882 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
81a76618 | 883 | fm->priority = str_to_u16(value, name); |
c821124b | 884 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
c3636ffc | 885 | fm->idle_timeout = str_to_u16(value, name); |
c821124b | 886 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
c3636ffc | 887 | fm->hard_timeout = str_to_u16(value, name); |
e729e793 JP |
888 | } else if (!strcmp(name, "cookie")) { |
889 | char *mask = strchr(value, '/'); | |
623e1caf | 890 | |
e729e793 | 891 | if (mask) { |
623e1caf | 892 | /* A mask means we're searching for a cookie. */ |
e729e793 JP |
893 | if (command == OFPFC_ADD) { |
894 | ofp_fatal(str_, verbose, "flow additions cannot use " | |
895 | "a cookie mask"); | |
896 | } | |
897 | *mask = '\0'; | |
623e1caf | 898 | fm->cookie = htonll(str_to_u64(value)); |
e729e793 JP |
899 | fm->cookie_mask = htonll(str_to_u64(mask+1)); |
900 | } else { | |
623e1caf JP |
901 | /* No mask means that the cookie is being set. */ |
902 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
903 | && command != OFPFC_MODIFY_STRICT) { | |
904 | ofp_fatal(str_, verbose, "cannot set cookie"); | |
905 | } | |
906 | fm->new_cookie = htonll(str_to_u64(value)); | |
e729e793 | 907 | } |
6a885fd0 | 908 | } else if (mf_from_name(name)) { |
81a76618 | 909 | parse_field(mf_from_name(name), value, &fm->match); |
2c6d8411 BP |
910 | } else if (!strcmp(name, "duration") |
911 | || !strcmp(name, "n_packets") | |
912 | || !strcmp(name, "n_bytes")) { | |
913 | /* Ignore these, so that users can feed the output of | |
914 | * "ovs-ofctl dump-flows" back into commands that parse | |
915 | * flows. */ | |
f22716dc | 916 | } else { |
ec610b7b | 917 | ofp_fatal(str_, verbose, "unknown keyword %s", name); |
f22716dc JP |
918 | } |
919 | } | |
920 | } | |
623e1caf JP |
921 | if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX) |
922 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { | |
923 | /* On modifies without a mask, we are supposed to add a flow if | |
924 | * one does not exist. If a cookie wasn't been specified, use a | |
925 | * default of zero. */ | |
926 | fm->new_cookie = htonll(0); | |
927 | } | |
75a75043 | 928 | if (fields & F_ACTIONS) { |
f25d0cf3 | 929 | struct ofpbuf ofpacts; |
75a75043 | 930 | |
f25d0cf3 | 931 | ofpbuf_init(&ofpacts, 32); |
8dd54666 | 932 | str_to_inst_ofpacts(&fm->match.flow, act_str, &ofpacts); |
f25d0cf3 BP |
933 | fm->ofpacts_len = ofpacts.size; |
934 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); | |
75a75043 | 935 | } else { |
f25d0cf3 BP |
936 | fm->ofpacts_len = 0; |
937 | fm->ofpacts = NULL; | |
75a75043 | 938 | } |
ec610b7b AE |
939 | |
940 | free(string); | |
f22716dc | 941 | } |
15f1f1b6 | 942 | |
2b07c8b1 BP |
943 | /* Convert 'str_' (as described in the documentation for the "monitor" command |
944 | * in the ovs-ofctl man page) into 'fmr'. */ | |
945 | void | |
946 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
947 | const char *str_) | |
948 | { | |
949 | static uint32_t id; | |
950 | ||
951 | char *string = xstrdup(str_); | |
952 | char *save_ptr = NULL; | |
953 | char *name; | |
954 | ||
955 | fmr->id = id++; | |
956 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | |
957 | | NXFMF_OWN | NXFMF_ACTIONS); | |
958 | fmr->out_port = OFPP_NONE; | |
959 | fmr->table_id = 0xff; | |
81a76618 | 960 | match_init_catchall(&fmr->match); |
2b07c8b1 BP |
961 | |
962 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
963 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
964 | const struct protocol *p; | |
965 | ||
966 | if (!strcmp(name, "!initial")) { | |
967 | fmr->flags &= ~NXFMF_INITIAL; | |
968 | } else if (!strcmp(name, "!add")) { | |
969 | fmr->flags &= ~NXFMF_ADD; | |
970 | } else if (!strcmp(name, "!delete")) { | |
971 | fmr->flags &= ~NXFMF_DELETE; | |
972 | } else if (!strcmp(name, "!modify")) { | |
973 | fmr->flags &= ~NXFMF_MODIFY; | |
974 | } else if (!strcmp(name, "!actions")) { | |
975 | fmr->flags &= ~NXFMF_ACTIONS; | |
976 | } else if (!strcmp(name, "!own")) { | |
977 | fmr->flags &= ~NXFMF_OWN; | |
978 | } else if (parse_protocol(name, &p)) { | |
81a76618 | 979 | match_set_dl_type(&fmr->match, htons(p->dl_type)); |
2b07c8b1 | 980 | if (p->nw_proto) { |
81a76618 | 981 | match_set_nw_proto(&fmr->match, p->nw_proto); |
2b07c8b1 BP |
982 | } |
983 | } else { | |
984 | char *value; | |
985 | ||
986 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
987 | if (!value) { | |
988 | ovs_fatal(0, "%s: field %s missing value", str_, name); | |
989 | } | |
990 | ||
991 | if (!strcmp(name, "table")) { | |
992 | fmr->table_id = str_to_table_id(value); | |
993 | } else if (!strcmp(name, "out_port")) { | |
994 | fmr->out_port = atoi(value); | |
995 | } else if (mf_from_name(name)) { | |
996 | parse_field(mf_from_name(name), value, &fmr->match); | |
997 | } else { | |
998 | ovs_fatal(0, "%s: unknown keyword %s", str_, name); | |
999 | } | |
1000 | } | |
1001 | } | |
1002 | free(string); | |
1003 | } | |
1004 | ||
0c3d5fc8 BP |
1005 | /* Parses 's' as a set of OpenFlow actions and appends the actions to |
1006 | * 'actions'. | |
1007 | * | |
1008 | * Prints an error on stderr and aborts the program if 's' syntax is | |
1009 | * invalid. */ | |
1010 | void | |
f25d0cf3 | 1011 | parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) |
0c3d5fc8 BP |
1012 | { |
1013 | char *s = xstrdup(s_); | |
f25d0cf3 | 1014 | str_to_ofpacts(NULL, s, ofpacts); |
0c3d5fc8 BP |
1015 | free(s); |
1016 | } | |
1017 | ||
88ca35ee | 1018 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
27527aa0 | 1019 | * (one of OFPFC_*) into 'fm'. */ |
88ca35ee | 1020 | void |
27527aa0 | 1021 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
a7fc1744 | 1022 | uint16_t command, bool verbose) |
15f1f1b6 | 1023 | { |
81a76618 | 1024 | struct match match_copy; |
88ca35ee | 1025 | |
27527aa0 | 1026 | parse_ofp_str(fm, command, string, verbose); |
88ca35ee | 1027 | |
81a76618 | 1028 | /* Normalize a copy of the match. This ensures that non-normalized flows |
01b389b1 BP |
1029 | * get logged but doesn't affect what gets sent to the switch, so that the |
1030 | * switch can do whatever it likes with the flow. */ | |
81a76618 BP |
1031 | match_copy = fm->match; |
1032 | ofputil_normalize_match(&match_copy); | |
15f1f1b6 BP |
1033 | } |
1034 | ||
27527aa0 BP |
1035 | void |
1036 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, | |
1037 | struct ofputil_flow_mod **fms, size_t *n_fms) | |
15f1f1b6 | 1038 | { |
27527aa0 BP |
1039 | size_t allocated_fms; |
1040 | FILE *stream; | |
dd8101bc | 1041 | struct ds s; |
15f1f1b6 | 1042 | |
27527aa0 BP |
1043 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
1044 | if (stream == NULL) { | |
1045 | ovs_fatal(errno, "%s: open", file_name); | |
1046 | } | |
1047 | ||
1048 | allocated_fms = *n_fms; | |
dd8101bc | 1049 | ds_init(&s); |
27527aa0 BP |
1050 | while (!ds_get_preprocessed_line(&s, stream)) { |
1051 | if (*n_fms >= allocated_fms) { | |
1052 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
1053 | } | |
1054 | parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false); | |
1055 | *n_fms += 1; | |
15f1f1b6 BP |
1056 | } |
1057 | ds_destroy(&s); | |
1058 | ||
27527aa0 BP |
1059 | if (stream != stdin) { |
1060 | fclose(stream); | |
1061 | } | |
88ca35ee BP |
1062 | } |
1063 | ||
1064 | void | |
81d1ea94 | 1065 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
a7fc1744 | 1066 | bool aggregate, const char *string) |
88ca35ee | 1067 | { |
a9a2da38 | 1068 | struct ofputil_flow_mod fm; |
88ca35ee | 1069 | |
c821124b | 1070 | parse_ofp_str(&fm, -1, string, false); |
88ca35ee | 1071 | fsr->aggregate = aggregate; |
e729e793 JP |
1072 | fsr->cookie = fm.cookie; |
1073 | fsr->cookie_mask = fm.cookie_mask; | |
81a76618 | 1074 | fsr->match = fm.match; |
88ca35ee | 1075 | fsr->out_port = fm.out_port; |
6c1491fb | 1076 | fsr->table_id = fm.table_id; |
15f1f1b6 | 1077 | } |
ccbe50f8 BP |
1078 | |
1079 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
1080 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
1081 | * mf_field. Fields must be specified in a natural order for satisfying | |
1082 | * prerequisites. | |
1083 | * | |
1084 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
1085 | * problem. */ | |
1086 | char * | |
1087 | parse_ofp_exact_flow(struct flow *flow, const char *s) | |
1088 | { | |
1089 | char *pos, *key, *value_s; | |
1090 | char *error = NULL; | |
1091 | char *copy; | |
1092 | ||
1093 | memset(flow, 0, sizeof *flow); | |
1094 | ||
1095 | pos = copy = xstrdup(s); | |
1096 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
1097 | const struct protocol *p; | |
1098 | if (parse_protocol(key, &p)) { | |
1099 | if (flow->dl_type) { | |
1100 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
1101 | goto exit; | |
1102 | } | |
1103 | flow->dl_type = htons(p->dl_type); | |
1104 | ||
1105 | if (p->nw_proto) { | |
1106 | if (flow->nw_proto) { | |
1107 | error = xasprintf("%s: network protocol set " | |
1108 | "multiple times", s); | |
1109 | goto exit; | |
1110 | } | |
1111 | flow->nw_proto = p->nw_proto; | |
1112 | } | |
1113 | } else { | |
1114 | const struct mf_field *mf; | |
1115 | union mf_value value; | |
1116 | char *field_error; | |
1117 | ||
1118 | mf = mf_from_name(key); | |
1119 | if (!mf) { | |
1120 | error = xasprintf("%s: unknown field %s", s, key); | |
1121 | goto exit; | |
1122 | } | |
1123 | ||
1124 | if (!mf_are_prereqs_ok(mf, flow)) { | |
1125 | error = xasprintf("%s: prerequisites not met for setting %s", | |
1126 | s, key); | |
1127 | goto exit; | |
1128 | } | |
1129 | ||
1130 | if (!mf_is_zero(mf, flow)) { | |
1131 | error = xasprintf("%s: field %s set multiple times", s, key); | |
1132 | goto exit; | |
1133 | } | |
1134 | ||
1135 | field_error = mf_parse_value(mf, value_s, &value); | |
1136 | if (field_error) { | |
1137 | error = xasprintf("%s: bad value for %s (%s)", | |
1138 | s, key, field_error); | |
1139 | free(field_error); | |
1140 | goto exit; | |
1141 | } | |
1142 | ||
1143 | mf_set_flow_value(mf, &value, flow); | |
1144 | } | |
1145 | } | |
1146 | ||
1147 | exit: | |
1148 | free(copy); | |
1149 | ||
1150 | if (error) { | |
1151 | memset(flow, 0, sizeof *flow); | |
1152 | } | |
1153 | return error; | |
1154 | } |