]>
Commit | Line | Data |
---|---|---|
f22716dc | 1 | /* |
cc40d06b | 2 | * Copyright (c) 2010, 2011, 2012, 2013, 2014 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" | |
a944ef40 | 37 | #include "ovs-thread.h" |
f22716dc | 38 | #include "packets.h" |
5a0a5702 | 39 | #include "simap.h" |
f22716dc JP |
40 | #include "socket-util.h" |
41 | #include "vconn.h" | |
f22716dc | 42 | |
bdda5aca BP |
43 | /* Parses 'str' as an 8-bit unsigned integer into '*valuep'. |
44 | * | |
45 | * 'name' describes the value parsed in an error message, if any. | |
46 | * | |
47 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
48 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 49 | char * WARN_UNUSED_RESULT |
bdda5aca | 50 | str_to_u8(const char *str, const char *name, uint8_t *valuep) |
c3636ffc | 51 | { |
638a19b0 | 52 | int value; |
c3636ffc | 53 | |
bdda5aca BP |
54 | if (!str_to_int(str, 0, &value) || value < 0 || value > 255) { |
55 | return xasprintf("invalid %s \"%s\"", name, str); | |
c3636ffc | 56 | } |
bdda5aca BP |
57 | *valuep = value; |
58 | return NULL; | |
c3636ffc BP |
59 | } |
60 | ||
bdda5aca BP |
61 | /* Parses 'str' as a 16-bit unsigned integer into '*valuep'. |
62 | * | |
63 | * 'name' describes the value parsed in an error message, if any. | |
64 | * | |
65 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
66 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 67 | char * WARN_UNUSED_RESULT |
bdda5aca | 68 | str_to_u16(const char *str, const char *name, uint16_t *valuep) |
c3636ffc BP |
69 | { |
70 | int value; | |
71 | ||
72 | if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) { | |
bdda5aca | 73 | return xasprintf("invalid %s \"%s\"", name, str); |
c3636ffc | 74 | } |
bdda5aca BP |
75 | *valuep = value; |
76 | return NULL; | |
c3636ffc BP |
77 | } |
78 | ||
bdda5aca BP |
79 | /* Parses 'str' as a 32-bit unsigned integer into '*valuep'. |
80 | * | |
81 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
82 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 83 | char * WARN_UNUSED_RESULT |
bdda5aca | 84 | str_to_u32(const char *str, uint32_t *valuep) |
f22716dc JP |
85 | { |
86 | char *tail; | |
87 | uint32_t value; | |
88 | ||
c4894ed4 | 89 | if (!str[0]) { |
bdda5aca | 90 | return xstrdup("missing required numeric argument"); |
ce5452cf EJ |
91 | } |
92 | ||
f22716dc JP |
93 | errno = 0; |
94 | value = strtoul(str, &tail, 0); | |
95 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
bdda5aca | 96 | return xasprintf("invalid numeric format %s", str); |
f22716dc | 97 | } |
bdda5aca BP |
98 | *valuep = value; |
99 | return NULL; | |
f22716dc JP |
100 | } |
101 | ||
bdda5aca BP |
102 | /* Parses 'str' as an 64-bit unsigned integer into '*valuep'. |
103 | * | |
104 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
105 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 106 | char * WARN_UNUSED_RESULT |
bdda5aca | 107 | str_to_u64(const char *str, uint64_t *valuep) |
f22716dc JP |
108 | { |
109 | char *tail; | |
110 | uint64_t value; | |
111 | ||
c4894ed4 | 112 | if (!str[0]) { |
bdda5aca | 113 | return xstrdup("missing required numeric argument"); |
c4894ed4 BP |
114 | } |
115 | ||
f22716dc JP |
116 | errno = 0; |
117 | value = strtoull(str, &tail, 0); | |
118 | if (errno == EINVAL || errno == ERANGE || *tail) { | |
bdda5aca | 119 | return xasprintf("invalid numeric format %s", str); |
f22716dc | 120 | } |
bdda5aca BP |
121 | *valuep = value; |
122 | return NULL; | |
f22716dc JP |
123 | } |
124 | ||
bdda5aca BP |
125 | /* Parses 'str' as an 64-bit unsigned integer in network byte order into |
126 | * '*valuep'. | |
127 | * | |
128 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
129 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 130 | char * WARN_UNUSED_RESULT |
bdda5aca BP |
131 | str_to_be64(const char *str, ovs_be64 *valuep) |
132 | { | |
4be17953 | 133 | uint64_t value = 0; |
bdda5aca BP |
134 | char *error; |
135 | ||
136 | error = str_to_u64(str, &value); | |
137 | if (!error) { | |
138 | *valuep = htonll(value); | |
139 | } | |
140 | return error; | |
141 | } | |
142 | ||
143 | /* Parses 'str' as an Ethernet address into 'mac'. | |
144 | * | |
145 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
146 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 147 | char * WARN_UNUSED_RESULT |
f22716dc JP |
148 | str_to_mac(const char *str, uint8_t mac[6]) |
149 | { | |
c2c28dfd | 150 | if (!ovs_scan(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) { |
bdda5aca | 151 | return xasprintf("invalid mac address %s", str); |
f22716dc | 152 | } |
bdda5aca | 153 | return NULL; |
f22716dc JP |
154 | } |
155 | ||
bdda5aca BP |
156 | /* Parses 'str' as an IP address into '*ip'. |
157 | * | |
158 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
159 | * error. The caller is responsible for freeing the returned string. */ | |
4caacc1e | 160 | char * WARN_UNUSED_RESULT |
6a885fd0 | 161 | str_to_ip(const char *str, ovs_be32 *ip) |
cb8ca532 | 162 | { |
f22716dc | 163 | struct in_addr in_addr; |
f22716dc | 164 | |
6a885fd0 | 165 | if (lookup_ip(str, &in_addr)) { |
bdda5aca | 166 | return xasprintf("%s: could not convert to IP address", str); |
f22716dc JP |
167 | } |
168 | *ip = in_addr.s_addr; | |
bdda5aca | 169 | return NULL; |
d31f1109 JP |
170 | } |
171 | ||
f22716dc JP |
172 | struct protocol { |
173 | const char *name; | |
174 | uint16_t dl_type; | |
175 | uint8_t nw_proto; | |
176 | }; | |
177 | ||
178 | static bool | |
179 | parse_protocol(const char *name, const struct protocol **p_out) | |
180 | { | |
181 | static const struct protocol protocols[] = { | |
182 | { "ip", ETH_TYPE_IP, 0 }, | |
183 | { "arp", ETH_TYPE_ARP, 0 }, | |
6767a2cc JP |
184 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
185 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
186 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
0d56eaf2 | 187 | { "sctp", ETH_TYPE_IP, IPPROTO_SCTP }, |
d31f1109 JP |
188 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
189 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
190 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
191 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
192 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
0d56eaf2 | 193 | { "sctp6", ETH_TYPE_IPV6, IPPROTO_SCTP }, |
8087f5ff | 194 | { "rarp", ETH_TYPE_RARP, 0}, |
b02475c5 SH |
195 | { "mpls", ETH_TYPE_MPLS, 0 }, |
196 | { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, | |
197 | }; | |
f22716dc JP |
198 | const struct protocol *p; |
199 | ||
200 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
201 | if (!strcmp(p->name, name)) { | |
202 | *p_out = p; | |
203 | return true; | |
204 | } | |
205 | } | |
206 | *p_out = NULL; | |
207 | return false; | |
208 | } | |
209 | ||
bdda5aca | 210 | /* Parses 's' as the (possibly masked) value of field 'mf', and updates |
db0b6c29 JR |
211 | * 'match' appropriately. Restricts the set of usable protocols to ones |
212 | * supporting the parsed field. | |
bdda5aca BP |
213 | * |
214 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
215 | * error. The caller is responsible for freeing the returned string. */ | |
216 | static char * WARN_UNUSED_RESULT | |
db0b6c29 JR |
217 | parse_field(const struct mf_field *mf, const char *s, struct match *match, |
218 | enum ofputil_protocol *usable_protocols) | |
8050b31d | 219 | { |
6a885fd0 BP |
220 | union mf_value value, mask; |
221 | char *error; | |
bad68a99 | 222 | |
6a885fd0 | 223 | error = mf_parse(mf, s, &value, &mask); |
bdda5aca | 224 | if (!error) { |
db0b6c29 | 225 | *usable_protocols &= mf_set(mf, &value, &mask, match); |
8050b31d | 226 | } |
bdda5aca | 227 | return error; |
00b1c62f BP |
228 | } |
229 | ||
c2d936a4 BP |
230 | static char * |
231 | extract_actions(char *s) | |
232 | { | |
233 | s = strstr(s, "action"); | |
234 | if (s) { | |
235 | *s = '\0'; | |
236 | s = strchr(s + 1, '='); | |
237 | return s ? s + 1 : NULL; | |
238 | } else { | |
239 | return NULL; | |
240 | } | |
241 | } | |
242 | ||
243 | ||
bdda5aca | 244 | static char * WARN_UNUSED_RESULT |
db0b6c29 | 245 | parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, |
ba2fe8e9 | 246 | enum ofputil_protocol *usable_protocols) |
f22716dc | 247 | { |
c821124b BP |
248 | enum { |
249 | F_OUT_PORT = 1 << 0, | |
250 | F_ACTIONS = 1 << 1, | |
c821124b | 251 | F_TIMEOUT = 1 << 3, |
a993007b BP |
252 | F_PRIORITY = 1 << 4, |
253 | F_FLAGS = 1 << 5, | |
c821124b | 254 | } fields; |
f22716dc | 255 | char *save_ptr = NULL; |
75a75043 | 256 | char *act_str = NULL; |
f22716dc | 257 | char *name; |
f22716dc | 258 | |
db0b6c29 JR |
259 | *usable_protocols = OFPUTIL_P_ANY; |
260 | ||
c821124b BP |
261 | switch (command) { |
262 | case -1: | |
263 | fields = F_OUT_PORT; | |
264 | break; | |
265 | ||
266 | case OFPFC_ADD: | |
a993007b | 267 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
268 | break; |
269 | ||
270 | case OFPFC_DELETE: | |
271 | fields = F_OUT_PORT; | |
272 | break; | |
273 | ||
274 | case OFPFC_DELETE_STRICT: | |
275 | fields = F_OUT_PORT | F_PRIORITY; | |
276 | break; | |
277 | ||
278 | case OFPFC_MODIFY: | |
a993007b | 279 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
280 | break; |
281 | ||
282 | case OFPFC_MODIFY_STRICT: | |
a993007b | 283 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
284 | break; |
285 | ||
286 | default: | |
428b2edd | 287 | OVS_NOT_REACHED(); |
c821124b BP |
288 | } |
289 | ||
81a76618 BP |
290 | match_init_catchall(&fm->match); |
291 | fm->priority = OFP_DEFAULT_PRIORITY; | |
88ca35ee | 292 | fm->cookie = htonll(0); |
e729e793 | 293 | fm->cookie_mask = htonll(0); |
623e1caf JP |
294 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
295 | /* For modify, by default, don't update the cookie. */ | |
b8266395 | 296 | fm->new_cookie = OVS_BE64_MAX; |
623e1caf JP |
297 | } else{ |
298 | fm->new_cookie = htonll(0); | |
299 | } | |
23342857 | 300 | fm->modify_cookie = false; |
6c1491fb | 301 | fm->table_id = 0xff; |
c821124b | 302 | fm->command = command; |
88ca35ee BP |
303 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
304 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
305 | fm->buffer_id = UINT32_MAX; | |
7f05e7ab | 306 | fm->out_port = OFPP_ANY; |
88ca35ee | 307 | fm->flags = 0; |
7395c052 | 308 | fm->out_group = OFPG11_ANY; |
cc40d06b | 309 | fm->delete_reason = OFPRR_DELETE; |
c821124b | 310 | if (fields & F_ACTIONS) { |
c2d936a4 | 311 | act_str = extract_actions(string); |
f22716dc | 312 | if (!act_str) { |
bdda5aca | 313 | return xstrdup("must specify an action"); |
f22716dc | 314 | } |
f22716dc | 315 | } |
f22716dc JP |
316 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; |
317 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
318 | const struct protocol *p; | |
bdda5aca | 319 | char *error = NULL; |
f22716dc JP |
320 | |
321 | if (parse_protocol(name, &p)) { | |
81a76618 | 322 | match_set_dl_type(&fm->match, htons(p->dl_type)); |
f22716dc | 323 | if (p->nw_proto) { |
81a76618 | 324 | match_set_nw_proto(&fm->match, p->nw_proto); |
f22716dc | 325 | } |
a993007b | 326 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
0fb88c18 | 327 | fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; |
a993007b | 328 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { |
0fb88c18 | 329 | fm->flags |= OFPUTIL_FF_CHECK_OVERLAP; |
2e1ae200 | 330 | } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { |
0fb88c18 | 331 | fm->flags |= OFPUTIL_FF_RESET_COUNTS; |
db0b6c29 | 332 | *usable_protocols &= OFPUTIL_P_OF12_UP; |
2e1ae200 | 333 | } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { |
0fb88c18 | 334 | fm->flags |= OFPUTIL_FF_NO_PKT_COUNTS; |
db0b6c29 | 335 | *usable_protocols &= OFPUTIL_P_OF13_UP; |
2e1ae200 | 336 | } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { |
0fb88c18 | 337 | fm->flags |= OFPUTIL_FF_NO_BYT_COUNTS; |
db0b6c29 | 338 | *usable_protocols &= OFPUTIL_P_OF13_UP; |
adcf00ba AZ |
339 | } else if (!strcmp(name, "no_readonly_table") |
340 | || !strcmp(name, "allow_hidden_fields")) { | |
341 | /* ignore these fields. */ | |
f22716dc | 342 | } else { |
f22716dc JP |
343 | char *value; |
344 | ||
345 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
346 | if (!value) { | |
bdda5aca | 347 | return xasprintf("field %s missing value", name); |
f22716dc JP |
348 | } |
349 | ||
6c1491fb | 350 | if (!strcmp(name, "table")) { |
bdda5aca | 351 | error = str_to_u8(value, "table", &fm->table_id); |
db0b6c29 JR |
352 | if (fm->table_id != 0xff) { |
353 | *usable_protocols &= OFPUTIL_P_TID; | |
354 | } | |
8050b31d | 355 | } else if (!strcmp(name, "out_port")) { |
be3f512a | 356 | if (!ofputil_port_from_string(value, &fm->out_port)) { |
bdda5aca BP |
357 | error = xasprintf("%s is not a valid OpenFlow port", |
358 | value); | |
c6100d92 | 359 | } |
c821124b | 360 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
4be17953 | 361 | uint16_t priority = 0; |
bdda5aca BP |
362 | |
363 | error = str_to_u16(value, name, &priority); | |
364 | fm->priority = priority; | |
c821124b | 365 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
bdda5aca | 366 | error = str_to_u16(value, name, &fm->idle_timeout); |
c821124b | 367 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
bdda5aca | 368 | error = str_to_u16(value, name, &fm->hard_timeout); |
e729e793 JP |
369 | } else if (!strcmp(name, "cookie")) { |
370 | char *mask = strchr(value, '/'); | |
623e1caf | 371 | |
e729e793 | 372 | if (mask) { |
623e1caf | 373 | /* A mask means we're searching for a cookie. */ |
e729e793 | 374 | if (command == OFPFC_ADD) { |
bdda5aca BP |
375 | return xstrdup("flow additions cannot use " |
376 | "a cookie mask"); | |
e729e793 JP |
377 | } |
378 | *mask = '\0'; | |
bdda5aca BP |
379 | error = str_to_be64(value, &fm->cookie); |
380 | if (error) { | |
381 | return error; | |
382 | } | |
383 | error = str_to_be64(mask + 1, &fm->cookie_mask); | |
db0b6c29 JR |
384 | |
385 | /* Matching of the cookie is only supported through NXM or | |
386 | * OF1.1+. */ | |
387 | if (fm->cookie_mask != htonll(0)) { | |
388 | *usable_protocols &= OFPUTIL_P_NXM_OF11_UP; | |
389 | } | |
e729e793 | 390 | } else { |
623e1caf JP |
391 | /* No mask means that the cookie is being set. */ |
392 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
bdda5aca BP |
393 | && command != OFPFC_MODIFY_STRICT) { |
394 | return xstrdup("cannot set cookie"); | |
623e1caf | 395 | } |
bdda5aca | 396 | error = str_to_be64(value, &fm->new_cookie); |
23342857 | 397 | fm->modify_cookie = true; |
e729e793 | 398 | } |
6a885fd0 | 399 | } else if (mf_from_name(name)) { |
db0b6c29 JR |
400 | error = parse_field(mf_from_name(name), value, &fm->match, |
401 | usable_protocols); | |
2c6d8411 BP |
402 | } else if (!strcmp(name, "duration") |
403 | || !strcmp(name, "n_packets") | |
146356e9 JP |
404 | || !strcmp(name, "n_bytes") |
405 | || !strcmp(name, "idle_age") | |
406 | || !strcmp(name, "hard_age")) { | |
2c6d8411 BP |
407 | /* Ignore these, so that users can feed the output of |
408 | * "ovs-ofctl dump-flows" back into commands that parse | |
409 | * flows. */ | |
f22716dc | 410 | } else { |
bdda5aca BP |
411 | error = xasprintf("unknown keyword %s", name); |
412 | } | |
413 | ||
414 | if (error) { | |
415 | return error; | |
f22716dc JP |
416 | } |
417 | } | |
418 | } | |
db0b6c29 JR |
419 | /* Check for usable protocol interdependencies between match fields. */ |
420 | if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) { | |
421 | const struct flow_wildcards *wc = &fm->match.wc; | |
422 | /* Only NXM and OXM support matching L3 and L4 fields within IPv6. | |
423 | * | |
424 | * (IPv6 specific fields as well as arp_sha, arp_tha, nw_frag, and | |
425 | * nw_ttl are covered elsewhere so they don't need to be included in | |
426 | * this test too.) | |
427 | */ | |
428 | if (wc->masks.nw_proto || wc->masks.nw_tos | |
429 | || wc->masks.tp_src || wc->masks.tp_dst) { | |
430 | *usable_protocols &= OFPUTIL_P_NXM_OXM_ANY; | |
431 | } | |
432 | } | |
b8266395 | 433 | if (!fm->cookie_mask && fm->new_cookie == OVS_BE64_MAX |
bdda5aca | 434 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { |
623e1caf JP |
435 | /* On modifies without a mask, we are supposed to add a flow if |
436 | * one does not exist. If a cookie wasn't been specified, use a | |
437 | * default of zero. */ | |
438 | fm->new_cookie = htonll(0); | |
439 | } | |
75a75043 | 440 | if (fields & F_ACTIONS) { |
c2d936a4 | 441 | enum ofputil_protocol action_usable_protocols; |
f25d0cf3 | 442 | struct ofpbuf ofpacts; |
bdda5aca | 443 | char *error; |
75a75043 | 444 | |
f25d0cf3 | 445 | ofpbuf_init(&ofpacts, 32); |
c2d936a4 BP |
446 | error = ofpacts_parse_instructions(act_str, &ofpacts, |
447 | &action_usable_protocols); | |
448 | *usable_protocols &= action_usable_protocols; | |
bdda5aca BP |
449 | if (!error) { |
450 | enum ofperr err; | |
451 | ||
1f317cb5 | 452 | err = ofpacts_check(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &fm->match.flow, |
ba2fe8e9 BP |
453 | OFPP_MAX, fm->table_id, 255, usable_protocols); |
454 | if (!err && !usable_protocols) { | |
455 | err = OFPERR_OFPBAC_MATCH_INCONSISTENT; | |
456 | } | |
bdda5aca | 457 | if (err) { |
ba2fe8e9 BP |
458 | error = xasprintf("actions are invalid with specified match " |
459 | "(%s)", ofperr_to_string(err)); | |
bdda5aca | 460 | } |
ba2fe8e9 | 461 | |
bdda5aca BP |
462 | } |
463 | if (error) { | |
464 | ofpbuf_uninit(&ofpacts); | |
465 | return error; | |
b019d34d SH |
466 | } |
467 | ||
1f317cb5 | 468 | fm->ofpacts_len = ofpbuf_size(&ofpacts); |
bdda5aca | 469 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); |
75a75043 | 470 | } else { |
f25d0cf3 BP |
471 | fm->ofpacts_len = 0; |
472 | fm->ofpacts = NULL; | |
75a75043 | 473 | } |
ec610b7b | 474 | |
bdda5aca | 475 | return NULL; |
f22716dc | 476 | } |
15f1f1b6 | 477 | |
638a19b0 | 478 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
bdda5aca | 479 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. |
db0b6c29 | 480 | * Returns the set of usable protocols in '*usable_protocols'. |
bdda5aca BP |
481 | * |
482 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
483 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
484 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. | |
485 | * | |
486 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
487 | * error. The caller is responsible for freeing the returned string. */ | |
488 | char * WARN_UNUSED_RESULT | |
db0b6c29 | 489 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
ba2fe8e9 | 490 | enum ofputil_protocol *usable_protocols) |
bdda5aca BP |
491 | { |
492 | char *string = xstrdup(str_); | |
493 | char *error; | |
494 | ||
ba2fe8e9 | 495 | error = parse_ofp_str__(fm, command, string, usable_protocols); |
bdda5aca BP |
496 | if (error) { |
497 | fm->ofpacts = NULL; | |
498 | fm->ofpacts_len = 0; | |
499 | } | |
500 | ||
501 | free(string); | |
502 | return error; | |
503 | } | |
504 | ||
505 | static char * WARN_UNUSED_RESULT | |
506 | parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string, | |
db0b6c29 JR |
507 | struct ofpbuf *bands, int command, |
508 | enum ofputil_protocol *usable_protocols) | |
638a19b0 JR |
509 | { |
510 | enum { | |
511 | F_METER = 1 << 0, | |
512 | F_FLAGS = 1 << 1, | |
513 | F_BANDS = 1 << 2, | |
514 | } fields; | |
638a19b0 JR |
515 | char *save_ptr = NULL; |
516 | char *band_str = NULL; | |
517 | char *name; | |
518 | ||
db0b6c29 | 519 | /* Meters require at least OF 1.3. */ |
040f4db8 | 520 | *usable_protocols = OFPUTIL_P_OF13_UP; |
db0b6c29 | 521 | |
638a19b0 JR |
522 | switch (command) { |
523 | case -1: | |
524 | fields = F_METER; | |
525 | break; | |
526 | ||
527 | case OFPMC13_ADD: | |
528 | fields = F_METER | F_FLAGS | F_BANDS; | |
529 | break; | |
530 | ||
531 | case OFPMC13_DELETE: | |
532 | fields = F_METER; | |
533 | break; | |
534 | ||
535 | case OFPMC13_MODIFY: | |
536 | fields = F_METER | F_FLAGS | F_BANDS; | |
537 | break; | |
538 | ||
539 | default: | |
428b2edd | 540 | OVS_NOT_REACHED(); |
638a19b0 JR |
541 | } |
542 | ||
543 | mm->command = command; | |
544 | mm->meter.meter_id = 0; | |
545 | mm->meter.flags = 0; | |
546 | if (fields & F_BANDS) { | |
547 | band_str = strstr(string, "band"); | |
548 | if (!band_str) { | |
bdda5aca | 549 | return xstrdup("must specify bands"); |
638a19b0 JR |
550 | } |
551 | *band_str = '\0'; | |
552 | ||
553 | band_str = strchr(band_str + 1, '='); | |
554 | if (!band_str) { | |
bdda5aca | 555 | return xstrdup("must specify bands"); |
638a19b0 JR |
556 | } |
557 | ||
558 | band_str++; | |
559 | } | |
560 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
561 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
562 | ||
563 | if (fields & F_FLAGS && !strcmp(name, "kbps")) { | |
564 | mm->meter.flags |= OFPMF13_KBPS; | |
565 | } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { | |
566 | mm->meter.flags |= OFPMF13_PKTPS; | |
567 | } else if (fields & F_FLAGS && !strcmp(name, "burst")) { | |
568 | mm->meter.flags |= OFPMF13_BURST; | |
569 | } else if (fields & F_FLAGS && !strcmp(name, "stats")) { | |
570 | mm->meter.flags |= OFPMF13_STATS; | |
571 | } else { | |
572 | char *value; | |
573 | ||
574 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
575 | if (!value) { | |
bdda5aca | 576 | return xasprintf("field %s missing value", name); |
638a19b0 JR |
577 | } |
578 | ||
579 | if (!strcmp(name, "meter")) { | |
580 | if (!strcmp(value, "all")) { | |
581 | mm->meter.meter_id = OFPM13_ALL; | |
582 | } else if (!strcmp(value, "controller")) { | |
583 | mm->meter.meter_id = OFPM13_CONTROLLER; | |
584 | } else if (!strcmp(value, "slowpath")) { | |
585 | mm->meter.meter_id = OFPM13_SLOWPATH; | |
586 | } else { | |
bdda5aca BP |
587 | char *error = str_to_u32(value, &mm->meter.meter_id); |
588 | if (error) { | |
589 | return error; | |
590 | } | |
638a19b0 | 591 | if (mm->meter.meter_id > OFPM13_MAX) { |
bdda5aca | 592 | return xasprintf("invalid value for %s", name); |
638a19b0 JR |
593 | } |
594 | } | |
595 | } else { | |
bdda5aca | 596 | return xasprintf("unknown keyword %s", name); |
638a19b0 JR |
597 | } |
598 | } | |
599 | } | |
600 | if (fields & F_METER && !mm->meter.meter_id) { | |
bdda5aca | 601 | return xstrdup("must specify 'meter'"); |
638a19b0 JR |
602 | } |
603 | if (fields & F_FLAGS && !mm->meter.flags) { | |
bdda5aca | 604 | return xstrdup("meter must specify either 'kbps' or 'pktps'"); |
638a19b0 JR |
605 | } |
606 | ||
607 | if (fields & F_BANDS) { | |
638a19b0 JR |
608 | uint16_t n_bands = 0; |
609 | struct ofputil_meter_band *band = NULL; | |
610 | int i; | |
611 | ||
638a19b0 JR |
612 | for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; |
613 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
614 | ||
615 | char *value; | |
616 | ||
617 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
618 | if (!value) { | |
bdda5aca | 619 | return xasprintf("field %s missing value", name); |
638a19b0 JR |
620 | } |
621 | ||
622 | if (!strcmp(name, "type")) { | |
623 | /* Start a new band */ | |
bdda5aca | 624 | band = ofpbuf_put_zeros(bands, sizeof *band); |
638a19b0 JR |
625 | n_bands++; |
626 | ||
627 | if (!strcmp(value, "drop")) { | |
628 | band->type = OFPMBT13_DROP; | |
629 | } else if (!strcmp(value, "dscp_remark")) { | |
630 | band->type = OFPMBT13_DSCP_REMARK; | |
631 | } else { | |
bdda5aca | 632 | return xasprintf("field %s unknown value %s", name, value); |
638a19b0 JR |
633 | } |
634 | } else if (!band || !band->type) { | |
bdda5aca | 635 | return xstrdup("band must start with the 'type' keyword"); |
638a19b0 | 636 | } else if (!strcmp(name, "rate")) { |
bdda5aca BP |
637 | char *error = str_to_u32(value, &band->rate); |
638 | if (error) { | |
639 | return error; | |
640 | } | |
638a19b0 | 641 | } else if (!strcmp(name, "burst_size")) { |
bdda5aca BP |
642 | char *error = str_to_u32(value, &band->burst_size); |
643 | if (error) { | |
644 | return error; | |
645 | } | |
638a19b0 | 646 | } else if (!strcmp(name, "prec_level")) { |
bdda5aca BP |
647 | char *error = str_to_u8(value, name, &band->prec_level); |
648 | if (error) { | |
649 | return error; | |
650 | } | |
638a19b0 | 651 | } else { |
bdda5aca | 652 | return xasprintf("unknown keyword %s", name); |
638a19b0 JR |
653 | } |
654 | } | |
655 | /* validate bands */ | |
656 | if (!n_bands) { | |
bdda5aca | 657 | return xstrdup("meter must have bands"); |
638a19b0 JR |
658 | } |
659 | ||
660 | mm->meter.n_bands = n_bands; | |
bdda5aca | 661 | mm->meter.bands = ofpbuf_steal_data(bands); |
638a19b0 JR |
662 | |
663 | for (i = 0; i < n_bands; ++i) { | |
664 | band = &mm->meter.bands[i]; | |
665 | ||
666 | if (!band->type) { | |
bdda5aca | 667 | return xstrdup("band must have 'type'"); |
638a19b0 JR |
668 | } |
669 | if (band->type == OFPMBT13_DSCP_REMARK) { | |
670 | if (!band->prec_level) { | |
bdda5aca BP |
671 | return xstrdup("'dscp_remark' band must have" |
672 | " 'prec_level'"); | |
638a19b0 JR |
673 | } |
674 | } else { | |
675 | if (band->prec_level) { | |
bdda5aca BP |
676 | return xstrdup("Only 'dscp_remark' band may have" |
677 | " 'prec_level'"); | |
638a19b0 JR |
678 | } |
679 | } | |
680 | if (!band->rate) { | |
bdda5aca | 681 | return xstrdup("band must have 'rate'"); |
638a19b0 JR |
682 | } |
683 | if (mm->meter.flags & OFPMF13_BURST) { | |
684 | if (!band->burst_size) { | |
bdda5aca BP |
685 | return xstrdup("band must have 'burst_size' " |
686 | "when 'burst' flag is set"); | |
638a19b0 JR |
687 | } |
688 | } else { | |
689 | if (band->burst_size) { | |
bdda5aca BP |
690 | return xstrdup("band may have 'burst_size' only " |
691 | "when 'burst' flag is set"); | |
638a19b0 JR |
692 | } |
693 | } | |
694 | } | |
695 | } else { | |
696 | mm->meter.n_bands = 0; | |
697 | mm->meter.bands = NULL; | |
698 | } | |
699 | ||
bdda5aca BP |
700 | return NULL; |
701 | } | |
702 | ||
703 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man | |
704 | * page) into 'mm' for sending the specified meter_mod 'command' to a switch. | |
705 | * | |
706 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
707 | * error. The caller is responsible for freeing the returned string. */ | |
708 | char * WARN_UNUSED_RESULT | |
709 | parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, | |
db0b6c29 | 710 | int command, enum ofputil_protocol *usable_protocols) |
bdda5aca BP |
711 | { |
712 | struct ofpbuf bands; | |
713 | char *string; | |
714 | char *error; | |
715 | ||
716 | ofpbuf_init(&bands, 64); | |
717 | string = xstrdup(str_); | |
718 | ||
db0b6c29 JR |
719 | error = parse_ofp_meter_mod_str__(mm, string, &bands, command, |
720 | usable_protocols); | |
bdda5aca | 721 | |
638a19b0 | 722 | free(string); |
bdda5aca BP |
723 | ofpbuf_uninit(&bands); |
724 | ||
725 | return error; | |
638a19b0 JR |
726 | } |
727 | ||
bdda5aca BP |
728 | static char * WARN_UNUSED_RESULT |
729 | parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, | |
db0b6c29 JR |
730 | const char *str_, char *string, |
731 | enum ofputil_protocol *usable_protocols) | |
2b07c8b1 | 732 | { |
97be1538 | 733 | static atomic_uint32_t id = ATOMIC_VAR_INIT(0); |
2b07c8b1 BP |
734 | char *save_ptr = NULL; |
735 | char *name; | |
736 | ||
97be1538 | 737 | atomic_add(&id, 1, &fmr->id); |
a944ef40 | 738 | |
2b07c8b1 BP |
739 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY |
740 | | NXFMF_OWN | NXFMF_ACTIONS); | |
741 | fmr->out_port = OFPP_NONE; | |
742 | fmr->table_id = 0xff; | |
81a76618 | 743 | match_init_catchall(&fmr->match); |
2b07c8b1 BP |
744 | |
745 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
746 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
747 | const struct protocol *p; | |
748 | ||
749 | if (!strcmp(name, "!initial")) { | |
750 | fmr->flags &= ~NXFMF_INITIAL; | |
751 | } else if (!strcmp(name, "!add")) { | |
752 | fmr->flags &= ~NXFMF_ADD; | |
753 | } else if (!strcmp(name, "!delete")) { | |
754 | fmr->flags &= ~NXFMF_DELETE; | |
755 | } else if (!strcmp(name, "!modify")) { | |
756 | fmr->flags &= ~NXFMF_MODIFY; | |
757 | } else if (!strcmp(name, "!actions")) { | |
758 | fmr->flags &= ~NXFMF_ACTIONS; | |
759 | } else if (!strcmp(name, "!own")) { | |
760 | fmr->flags &= ~NXFMF_OWN; | |
761 | } else if (parse_protocol(name, &p)) { | |
81a76618 | 762 | match_set_dl_type(&fmr->match, htons(p->dl_type)); |
2b07c8b1 | 763 | if (p->nw_proto) { |
81a76618 | 764 | match_set_nw_proto(&fmr->match, p->nw_proto); |
2b07c8b1 BP |
765 | } |
766 | } else { | |
767 | char *value; | |
768 | ||
769 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
770 | if (!value) { | |
bdda5aca | 771 | return xasprintf("%s: field %s missing value", str_, name); |
2b07c8b1 BP |
772 | } |
773 | ||
774 | if (!strcmp(name, "table")) { | |
bdda5aca BP |
775 | char *error = str_to_u8(value, "table", &fmr->table_id); |
776 | if (error) { | |
777 | return error; | |
778 | } | |
2b07c8b1 | 779 | } else if (!strcmp(name, "out_port")) { |
4e022ec0 | 780 | fmr->out_port = u16_to_ofp(atoi(value)); |
2b07c8b1 | 781 | } else if (mf_from_name(name)) { |
bdda5aca BP |
782 | char *error; |
783 | ||
db0b6c29 JR |
784 | error = parse_field(mf_from_name(name), value, &fmr->match, |
785 | usable_protocols); | |
bdda5aca BP |
786 | if (error) { |
787 | return error; | |
788 | } | |
2b07c8b1 | 789 | } else { |
bdda5aca | 790 | return xasprintf("%s: unknown keyword %s", str_, name); |
2b07c8b1 BP |
791 | } |
792 | } | |
793 | } | |
bdda5aca BP |
794 | return NULL; |
795 | } | |
796 | ||
797 | /* Convert 'str_' (as described in the documentation for the "monitor" command | |
798 | * in the ovs-ofctl man page) into 'fmr'. | |
799 | * | |
800 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
801 | * error. The caller is responsible for freeing the returned string. */ | |
802 | char * WARN_UNUSED_RESULT | |
803 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
db0b6c29 JR |
804 | const char *str_, |
805 | enum ofputil_protocol *usable_protocols) | |
bdda5aca BP |
806 | { |
807 | char *string = xstrdup(str_); | |
db0b6c29 JR |
808 | char *error = parse_flow_monitor_request__(fmr, str_, string, |
809 | usable_protocols); | |
2b07c8b1 | 810 | free(string); |
bdda5aca | 811 | return error; |
2b07c8b1 BP |
812 | } |
813 | ||
88ca35ee | 814 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
bdda5aca BP |
815 | * (one of OFPFC_*) into 'fm'. |
816 | * | |
817 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
818 | * error. The caller is responsible for freeing the returned string. */ | |
819 | char * WARN_UNUSED_RESULT | |
27527aa0 | 820 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
db0b6c29 | 821 | uint16_t command, |
ba2fe8e9 | 822 | enum ofputil_protocol *usable_protocols) |
15f1f1b6 | 823 | { |
ba2fe8e9 | 824 | char *error = parse_ofp_str(fm, command, string, usable_protocols); |
bdda5aca BP |
825 | if (!error) { |
826 | /* Normalize a copy of the match. This ensures that non-normalized | |
827 | * flows get logged but doesn't affect what gets sent to the switch, so | |
828 | * that the switch can do whatever it likes with the flow. */ | |
829 | struct match match_copy = fm->match; | |
830 | ofputil_normalize_match(&match_copy); | |
831 | } | |
88ca35ee | 832 | |
bdda5aca | 833 | return error; |
15f1f1b6 BP |
834 | } |
835 | ||
918f2b82 AZ |
836 | /* Convert 'table_id' and 'flow_miss_handling' (as described for the |
837 | * "mod-table" command in the ovs-ofctl man page) into 'tm' for sending the | |
838 | * specified table_mod 'command' to a switch. | |
839 | * | |
840 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
841 | * error. The caller is responsible for freeing the returned string. */ | |
842 | char * WARN_UNUSED_RESULT | |
843 | parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, | |
844 | const char *flow_miss_handling, | |
845 | enum ofputil_protocol *usable_protocols) | |
846 | { | |
847 | /* Table mod requires at least OF 1.1. */ | |
848 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
849 | ||
850 | if (!strcasecmp(table_id, "all")) { | |
083761ad | 851 | tm->table_id = OFPTT_ALL; |
918f2b82 AZ |
852 | } else { |
853 | char *error = str_to_u8(table_id, "table_id", &tm->table_id); | |
854 | if (error) { | |
855 | return error; | |
856 | } | |
857 | } | |
858 | ||
859 | if (strcmp(flow_miss_handling, "controller") == 0) { | |
3c1bb396 | 860 | tm->miss_config = OFPUTIL_TABLE_MISS_CONTROLLER; |
918f2b82 | 861 | } else if (strcmp(flow_miss_handling, "continue") == 0) { |
3c1bb396 | 862 | tm->miss_config = OFPUTIL_TABLE_MISS_CONTINUE; |
918f2b82 | 863 | } else if (strcmp(flow_miss_handling, "drop") == 0) { |
3c1bb396 | 864 | tm->miss_config = OFPUTIL_TABLE_MISS_DROP; |
918f2b82 AZ |
865 | } else { |
866 | return xasprintf("invalid flow_miss_handling %s", flow_miss_handling); | |
867 | } | |
868 | ||
3c1bb396 BP |
869 | if (tm->table_id == 0xfe |
870 | && tm->miss_config == OFPUTIL_TABLE_MISS_CONTINUE) { | |
918f2b82 AZ |
871 | return xstrdup("last table's flow miss handling can not be continue"); |
872 | } | |
873 | ||
874 | return NULL; | |
875 | } | |
876 | ||
877 | ||
bdda5aca BP |
878 | /* Opens file 'file_name' and reads each line as a flow_mod of the specified |
879 | * type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated | |
880 | * on the caller's behalf, and the number of flow_mods in '*n_fms'. | |
881 | * | |
882 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
883 | * error. The caller is responsible for freeing the returned string. */ | |
884 | char * WARN_UNUSED_RESULT | |
27527aa0 | 885 | parse_ofp_flow_mod_file(const char *file_name, uint16_t command, |
db0b6c29 | 886 | struct ofputil_flow_mod **fms, size_t *n_fms, |
ba2fe8e9 | 887 | enum ofputil_protocol *usable_protocols) |
15f1f1b6 | 888 | { |
27527aa0 | 889 | size_t allocated_fms; |
bdda5aca | 890 | int line_number; |
27527aa0 | 891 | FILE *stream; |
dd8101bc | 892 | struct ds s; |
15f1f1b6 | 893 | |
db0b6c29 JR |
894 | *usable_protocols = OFPUTIL_P_ANY; |
895 | ||
bdda5aca BP |
896 | *fms = NULL; |
897 | *n_fms = 0; | |
898 | ||
27527aa0 BP |
899 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
900 | if (stream == NULL) { | |
bdda5aca BP |
901 | return xasprintf("%s: open failed (%s)", |
902 | file_name, ovs_strerror(errno)); | |
27527aa0 BP |
903 | } |
904 | ||
905 | allocated_fms = *n_fms; | |
dd8101bc | 906 | ds_init(&s); |
bdda5aca BP |
907 | line_number = 0; |
908 | while (!ds_get_preprocessed_line(&s, stream, &line_number)) { | |
909 | char *error; | |
db0b6c29 | 910 | enum ofputil_protocol usable; |
bdda5aca | 911 | |
27527aa0 BP |
912 | if (*n_fms >= allocated_fms) { |
913 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
914 | } | |
db0b6c29 | 915 | error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, |
ba2fe8e9 | 916 | &usable); |
bdda5aca BP |
917 | if (error) { |
918 | size_t i; | |
919 | ||
920 | for (i = 0; i < *n_fms; i++) { | |
dc723c44 | 921 | free(CONST_CAST(struct ofpact *, (*fms)[i].ofpacts)); |
bdda5aca BP |
922 | } |
923 | free(*fms); | |
924 | *fms = NULL; | |
925 | *n_fms = 0; | |
926 | ||
927 | ds_destroy(&s); | |
928 | if (stream != stdin) { | |
929 | fclose(stream); | |
930 | } | |
931 | ||
932 | return xasprintf("%s:%d: %s", file_name, line_number, error); | |
933 | } | |
db0b6c29 | 934 | *usable_protocols &= usable; /* Each line can narrow the set. */ |
27527aa0 | 935 | *n_fms += 1; |
15f1f1b6 | 936 | } |
15f1f1b6 | 937 | |
bdda5aca | 938 | ds_destroy(&s); |
27527aa0 BP |
939 | if (stream != stdin) { |
940 | fclose(stream); | |
941 | } | |
bdda5aca | 942 | return NULL; |
88ca35ee BP |
943 | } |
944 | ||
bdda5aca | 945 | char * WARN_UNUSED_RESULT |
81d1ea94 | 946 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
db0b6c29 | 947 | bool aggregate, const char *string, |
ba2fe8e9 | 948 | enum ofputil_protocol *usable_protocols) |
88ca35ee | 949 | { |
a9a2da38 | 950 | struct ofputil_flow_mod fm; |
bdda5aca BP |
951 | char *error; |
952 | ||
ba2fe8e9 | 953 | error = parse_ofp_str(&fm, -1, string, usable_protocols); |
bdda5aca BP |
954 | if (error) { |
955 | return error; | |
956 | } | |
88ca35ee | 957 | |
db0b6c29 JR |
958 | /* Special table ID support not required for stats requests. */ |
959 | if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) { | |
960 | *usable_protocols |= OFPUTIL_P_OF10_STD; | |
961 | } | |
962 | if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) { | |
963 | *usable_protocols |= OFPUTIL_P_OF10_NXM; | |
964 | } | |
965 | ||
88ca35ee | 966 | fsr->aggregate = aggregate; |
e729e793 JP |
967 | fsr->cookie = fm.cookie; |
968 | fsr->cookie_mask = fm.cookie_mask; | |
81a76618 | 969 | fsr->match = fm.match; |
88ca35ee | 970 | fsr->out_port = fm.out_port; |
7395c052 | 971 | fsr->out_group = fm.out_group; |
6c1491fb | 972 | fsr->table_id = fm.table_id; |
bdda5aca | 973 | return NULL; |
15f1f1b6 | 974 | } |
ccbe50f8 BP |
975 | |
976 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
977 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
978 | * mf_field. Fields must be specified in a natural order for satisfying | |
5a0a5702 GS |
979 | * prerequisites. If 'mask' is specified, fills the mask field for each of the |
980 | * field specified in flow. If the map, 'names_portno' is specfied, converts | |
981 | * the in_port name into port no while setting the 'flow'. | |
ccbe50f8 BP |
982 | * |
983 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
984 | * problem. */ | |
985 | char * | |
5a0a5702 GS |
986 | parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, |
987 | const struct simap *portno_names) | |
ccbe50f8 BP |
988 | { |
989 | char *pos, *key, *value_s; | |
990 | char *error = NULL; | |
991 | char *copy; | |
992 | ||
993 | memset(flow, 0, sizeof *flow); | |
5a0a5702 GS |
994 | if (mask) { |
995 | memset(mask, 0, sizeof *mask); | |
996 | } | |
ccbe50f8 BP |
997 | |
998 | pos = copy = xstrdup(s); | |
999 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
1000 | const struct protocol *p; | |
1001 | if (parse_protocol(key, &p)) { | |
1002 | if (flow->dl_type) { | |
1003 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
1004 | goto exit; | |
1005 | } | |
1006 | flow->dl_type = htons(p->dl_type); | |
5a0a5702 GS |
1007 | if (mask) { |
1008 | mask->dl_type = OVS_BE16_MAX; | |
1009 | } | |
ccbe50f8 BP |
1010 | |
1011 | if (p->nw_proto) { | |
1012 | if (flow->nw_proto) { | |
1013 | error = xasprintf("%s: network protocol set " | |
1014 | "multiple times", s); | |
1015 | goto exit; | |
1016 | } | |
1017 | flow->nw_proto = p->nw_proto; | |
5a0a5702 GS |
1018 | if (mask) { |
1019 | mask->nw_proto = UINT8_MAX; | |
1020 | } | |
ccbe50f8 BP |
1021 | } |
1022 | } else { | |
1023 | const struct mf_field *mf; | |
1024 | union mf_value value; | |
1025 | char *field_error; | |
1026 | ||
1027 | mf = mf_from_name(key); | |
1028 | if (!mf) { | |
1029 | error = xasprintf("%s: unknown field %s", s, key); | |
1030 | goto exit; | |
1031 | } | |
1032 | ||
1033 | if (!mf_are_prereqs_ok(mf, flow)) { | |
1034 | error = xasprintf("%s: prerequisites not met for setting %s", | |
1035 | s, key); | |
1036 | goto exit; | |
1037 | } | |
1038 | ||
1039 | if (!mf_is_zero(mf, flow)) { | |
1040 | error = xasprintf("%s: field %s set multiple times", s, key); | |
1041 | goto exit; | |
1042 | } | |
1043 | ||
5a0a5702 GS |
1044 | if (!strcmp(key, "in_port") |
1045 | && portno_names | |
1046 | && simap_contains(portno_names, value_s)) { | |
1047 | flow->in_port.ofp_port = u16_to_ofp( | |
1048 | simap_get(portno_names, value_s)); | |
1049 | if (mask) { | |
1050 | mask->in_port.ofp_port = u16_to_ofp(ntohs(OVS_BE16_MAX)); | |
1051 | } | |
1052 | } else { | |
1053 | field_error = mf_parse_value(mf, value_s, &value); | |
1054 | if (field_error) { | |
1055 | error = xasprintf("%s: bad value for %s (%s)", | |
1056 | s, key, field_error); | |
1057 | free(field_error); | |
1058 | goto exit; | |
1059 | } | |
ccbe50f8 | 1060 | |
5a0a5702 GS |
1061 | mf_set_flow_value(mf, &value, flow); |
1062 | if (mask) { | |
1063 | mf_mask_field(mf, mask); | |
1064 | } | |
1065 | } | |
ccbe50f8 BP |
1066 | } |
1067 | } | |
1068 | ||
4e022ec0 AW |
1069 | if (!flow->in_port.ofp_port) { |
1070 | flow->in_port.ofp_port = OFPP_NONE; | |
72d64e33 EJ |
1071 | } |
1072 | ||
ccbe50f8 BP |
1073 | exit: |
1074 | free(copy); | |
1075 | ||
1076 | if (error) { | |
1077 | memset(flow, 0, sizeof *flow); | |
5a0a5702 GS |
1078 | if (mask) { |
1079 | memset(mask, 0, sizeof *mask); | |
1080 | } | |
ccbe50f8 BP |
1081 | } |
1082 | return error; | |
1083 | } | |
7395c052 NZ |
1084 | |
1085 | static char * WARN_UNUSED_RESULT | |
1086 | parse_bucket_str(struct ofputil_bucket *bucket, char *str_, | |
1087 | enum ofputil_protocol *usable_protocols) | |
1088 | { | |
c2d936a4 | 1089 | char *pos, *key, *value; |
7395c052 | 1090 | struct ofpbuf ofpacts; |
c2d936a4 BP |
1091 | struct ds actions; |
1092 | char *error; | |
7395c052 NZ |
1093 | |
1094 | bucket->weight = 1; | |
1095 | bucket->watch_port = OFPP_ANY; | |
1096 | bucket->watch_group = OFPG11_ANY; | |
1097 | ||
c2d936a4 | 1098 | ds_init(&actions); |
7395c052 | 1099 | |
c2d936a4 BP |
1100 | pos = str_; |
1101 | error = NULL; | |
1102 | while (ofputil_parse_key_value(&pos, &key, &value)) { | |
1103 | if (!strcasecmp(key, "weight")) { | |
1104 | error = str_to_u16(value, "weight", &bucket->weight); | |
1105 | } else if (!strcasecmp(key, "watch_port")) { | |
1106 | if (!ofputil_port_from_string(value, &bucket->watch_port) | |
7395c052 NZ |
1107 | || (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX) |
1108 | && bucket->watch_port != OFPP_ANY)) { | |
c2d936a4 | 1109 | error = xasprintf("%s: invalid watch_port", value); |
7395c052 | 1110 | } |
c2d936a4 BP |
1111 | } else if (!strcasecmp(key, "watch_group")) { |
1112 | error = str_to_u32(value, &bucket->watch_group); | |
7395c052 NZ |
1113 | if (!error && bucket->watch_group > OFPG_MAX) { |
1114 | error = xasprintf("invalid watch_group id %"PRIu32, | |
1115 | bucket->watch_group); | |
1116 | } | |
c2d936a4 BP |
1117 | } else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) { |
1118 | ds_put_format(&actions, "%s,", value); | |
7395c052 | 1119 | } else { |
c2d936a4 | 1120 | ds_put_format(&actions, "%s(%s),", key, value); |
7395c052 NZ |
1121 | } |
1122 | ||
1123 | if (error) { | |
c2d936a4 | 1124 | ds_destroy(&actions); |
7395c052 NZ |
1125 | return error; |
1126 | } | |
1127 | } | |
1128 | ||
c2d936a4 BP |
1129 | if (!actions.length) { |
1130 | return xstrdup("bucket must specify actions"); | |
1131 | } | |
1132 | ds_chomp(&actions, ','); | |
1133 | ||
1134 | ofpbuf_init(&ofpacts, 0); | |
1135 | error = ofpacts_parse_actions(ds_cstr(&actions), &ofpacts, | |
1136 | usable_protocols); | |
1137 | ds_destroy(&actions); | |
1138 | if (error) { | |
1139 | ofpbuf_uninit(&ofpacts); | |
1140 | return error; | |
1141 | } | |
1f317cb5 PS |
1142 | bucket->ofpacts = ofpbuf_data(&ofpacts); |
1143 | bucket->ofpacts_len = ofpbuf_size(&ofpacts); | |
7395c052 NZ |
1144 | |
1145 | return NULL; | |
1146 | } | |
1147 | ||
1148 | static char * WARN_UNUSED_RESULT | |
1149 | parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command, | |
1150 | char *string, | |
1151 | enum ofputil_protocol *usable_protocols) | |
1152 | { | |
1153 | enum { | |
1154 | F_GROUP_TYPE = 1 << 0, | |
1155 | F_BUCKETS = 1 << 1, | |
1156 | } fields; | |
1157 | char *save_ptr = NULL; | |
1158 | bool had_type = false; | |
1159 | char *name; | |
1160 | struct ofputil_bucket *bucket; | |
1161 | char *error = NULL; | |
1162 | ||
1163 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
1164 | ||
1165 | switch (command) { | |
1166 | case OFPGC11_ADD: | |
1167 | fields = F_GROUP_TYPE | F_BUCKETS; | |
1168 | break; | |
1169 | ||
1170 | case OFPGC11_DELETE: | |
1171 | fields = 0; | |
1172 | break; | |
1173 | ||
1174 | case OFPGC11_MODIFY: | |
1175 | fields = F_GROUP_TYPE | F_BUCKETS; | |
1176 | break; | |
1177 | ||
1178 | default: | |
428b2edd | 1179 | OVS_NOT_REACHED(); |
7395c052 NZ |
1180 | } |
1181 | ||
1182 | memset(gm, 0, sizeof *gm); | |
1183 | gm->command = command; | |
1184 | gm->group_id = OFPG_ANY; | |
1185 | list_init(&gm->buckets); | |
1186 | if (command == OFPGC11_DELETE && string[0] == '\0') { | |
1187 | gm->group_id = OFPG_ALL; | |
1188 | return NULL; | |
1189 | } | |
1190 | ||
1191 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
1192 | ||
1193 | if (fields & F_BUCKETS) { | |
1194 | char *bkt_str = strstr(string, "bucket"); | |
1195 | ||
1196 | if (bkt_str) { | |
1197 | *bkt_str = '\0'; | |
1198 | } | |
1199 | ||
1200 | while (bkt_str) { | |
1201 | char *next_bkt_str; | |
1202 | ||
1203 | bkt_str = strchr(bkt_str + 1, '='); | |
1204 | if (!bkt_str) { | |
1205 | error = xstrdup("must specify bucket content"); | |
1206 | goto out; | |
1207 | } | |
1208 | bkt_str++; | |
1209 | ||
1210 | next_bkt_str = strstr(bkt_str, "bucket"); | |
1211 | if (next_bkt_str) { | |
1212 | *next_bkt_str = '\0'; | |
1213 | } | |
1214 | ||
1215 | bucket = xzalloc(sizeof(struct ofputil_bucket)); | |
1216 | error = parse_bucket_str(bucket, bkt_str, usable_protocols); | |
1217 | if (error) { | |
1218 | free(bucket); | |
1219 | goto out; | |
1220 | } | |
1221 | list_push_back(&gm->buckets, &bucket->list_node); | |
1222 | ||
1223 | bkt_str = next_bkt_str; | |
1224 | } | |
1225 | } | |
1226 | ||
1227 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
1228 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
1229 | char *value; | |
1230 | ||
1231 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
1232 | if (!value) { | |
1233 | error = xasprintf("field %s missing value", name); | |
1234 | goto out; | |
1235 | } | |
1236 | ||
1237 | if (!strcmp(name, "group_id")) { | |
1238 | if(!strcmp(value, "all")) { | |
1239 | gm->group_id = OFPG_ALL; | |
1240 | } else { | |
1241 | char *error = str_to_u32(value, &gm->group_id); | |
1242 | if (error) { | |
1243 | goto out; | |
1244 | } | |
1245 | if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) { | |
1246 | error = xasprintf("invalid group id %"PRIu32, | |
1247 | gm->group_id); | |
1248 | goto out; | |
1249 | } | |
1250 | } | |
1251 | } else if (!strcmp(name, "type")){ | |
1252 | if (!(fields & F_GROUP_TYPE)) { | |
1253 | error = xstrdup("type is not needed"); | |
1254 | goto out; | |
1255 | } | |
1256 | if (!strcmp(value, "all")) { | |
1257 | gm->type = OFPGT11_ALL; | |
1258 | } else if (!strcmp(value, "select")) { | |
1259 | gm->type = OFPGT11_SELECT; | |
1260 | } else if (!strcmp(value, "indirect")) { | |
1261 | gm->type = OFPGT11_INDIRECT; | |
1262 | } else if (!strcmp(value, "ff") || | |
1263 | !strcmp(value, "fast_failover")) { | |
1264 | gm->type = OFPGT11_FF; | |
1265 | } else { | |
1266 | error = xasprintf("invalid group type %s", value); | |
1267 | goto out; | |
1268 | } | |
1269 | had_type = true; | |
1270 | } else if (!strcmp(name, "bucket")) { | |
1271 | error = xstrdup("bucket is not needed"); | |
1272 | goto out; | |
1273 | } else { | |
1274 | error = xasprintf("unknown keyword %s", name); | |
1275 | goto out; | |
1276 | } | |
1277 | } | |
1278 | if (gm->group_id == OFPG_ANY) { | |
1279 | error = xstrdup("must specify a group_id"); | |
1280 | goto out; | |
1281 | } | |
1282 | if (fields & F_GROUP_TYPE && !had_type) { | |
1283 | error = xstrdup("must specify a type"); | |
1284 | goto out; | |
1285 | } | |
1286 | ||
1287 | /* Validate buckets. */ | |
1288 | LIST_FOR_EACH (bucket, list_node, &gm->buckets) { | |
1289 | if (bucket->weight != 1 && gm->type != OFPGT11_SELECT) { | |
1290 | error = xstrdup("Only select groups can have bucket weights."); | |
1291 | goto out; | |
1292 | } | |
1293 | } | |
1294 | if (gm->type == OFPGT11_INDIRECT && !list_is_short(&gm->buckets)) { | |
1295 | error = xstrdup("Indirect groups can have at most one bucket."); | |
1296 | goto out; | |
1297 | } | |
1298 | ||
1299 | return NULL; | |
1300 | out: | |
1301 | ofputil_bucket_list_destroy(&gm->buckets); | |
1302 | return error; | |
1303 | } | |
1304 | ||
1305 | char * WARN_UNUSED_RESULT | |
1306 | parse_ofp_group_mod_str(struct ofputil_group_mod *gm, uint16_t command, | |
1307 | const char *str_, | |
1308 | enum ofputil_protocol *usable_protocols) | |
1309 | { | |
1310 | char *string = xstrdup(str_); | |
1311 | char *error = parse_ofp_group_mod_str__(gm, command, string, | |
1312 | usable_protocols); | |
1313 | free(string); | |
1314 | ||
1315 | if (error) { | |
1316 | ofputil_bucket_list_destroy(&gm->buckets); | |
1317 | } | |
1318 | return error; | |
1319 | } | |
1320 | ||
1321 | char * WARN_UNUSED_RESULT | |
1322 | parse_ofp_group_mod_file(const char *file_name, uint16_t command, | |
1323 | struct ofputil_group_mod **gms, size_t *n_gms, | |
1324 | enum ofputil_protocol *usable_protocols) | |
1325 | { | |
1326 | size_t allocated_gms; | |
1327 | int line_number; | |
1328 | FILE *stream; | |
1329 | struct ds s; | |
1330 | ||
1331 | *gms = NULL; | |
1332 | *n_gms = 0; | |
1333 | ||
1334 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); | |
1335 | if (stream == NULL) { | |
1336 | return xasprintf("%s: open failed (%s)", | |
1337 | file_name, ovs_strerror(errno)); | |
1338 | } | |
1339 | ||
1340 | allocated_gms = *n_gms; | |
1341 | ds_init(&s); | |
1342 | line_number = 0; | |
1343 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
1344 | while (!ds_get_preprocessed_line(&s, stream, &line_number)) { | |
1345 | enum ofputil_protocol usable; | |
1346 | char *error; | |
1347 | ||
1348 | if (*n_gms >= allocated_gms) { | |
2134b5ec SH |
1349 | size_t i; |
1350 | ||
7395c052 | 1351 | *gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms); |
2134b5ec SH |
1352 | for (i = 0; i < *n_gms; i++) { |
1353 | list_moved(&(*gms)[i].buckets); | |
1354 | } | |
7395c052 NZ |
1355 | } |
1356 | error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s), | |
1357 | &usable); | |
1358 | if (error) { | |
1359 | size_t i; | |
1360 | ||
1361 | for (i = 0; i < *n_gms; i++) { | |
1362 | ofputil_bucket_list_destroy(&(*gms)[i].buckets); | |
1363 | } | |
1364 | free(*gms); | |
1365 | *gms = NULL; | |
1366 | *n_gms = 0; | |
1367 | ||
1368 | ds_destroy(&s); | |
1369 | if (stream != stdin) { | |
1370 | fclose(stream); | |
1371 | } | |
1372 | ||
1373 | return xasprintf("%s:%d: %s", file_name, line_number, error); | |
1374 | } | |
1375 | *usable_protocols &= usable; | |
1376 | *n_gms += 1; | |
1377 | } | |
1378 | ||
1379 | ds_destroy(&s); | |
1380 | if (stream != stdin) { | |
1381 | fclose(stream); | |
1382 | } | |
1383 | return NULL; | |
1384 | } |