]>
Commit | Line | Data |
---|---|---|
f22716dc | 1 | /* |
c7ecbf1e | 2 | * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 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> | |
d787ad39 | 24 | #include <netinet/in.h> |
f22716dc | 25 | |
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 | 40 | #include "socket-util.h" |
4a1f523f | 41 | #include "openvswitch/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. */ | |
cab50449 | 49 | char * OVS_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. */ | |
cab50449 | 67 | char * OVS_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. */ | |
cab50449 | 83 | char * OVS_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. */ | |
cab50449 | 106 | char * OVS_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. */ | |
cab50449 | 130 | char * OVS_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. */ | |
cab50449 | 147 | char * OVS_WARN_UNUSED_RESULT |
74ff3298 | 148 | str_to_mac(const char *str, struct eth_addr *mac) |
f22716dc | 149 | { |
74ff3298 | 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. */ | |
cab50449 | 160 | char * OVS_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 | ||
d787ad39 JS |
172 | /* Parses 'str' as a conntrack helper into 'alg'. |
173 | * | |
174 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
175 | * error. The caller is responsible for freeing the returned string. */ | |
176 | char * OVS_WARN_UNUSED_RESULT | |
177 | str_to_connhelper(const char *str, uint16_t *alg) | |
178 | { | |
179 | if (!strcmp(str, "ftp")) { | |
180 | *alg = IPPORT_FTP; | |
181 | return NULL; | |
182 | } | |
183 | return xasprintf("invalid conntrack helper \"%s\"", str); | |
184 | } | |
185 | ||
f22716dc JP |
186 | struct protocol { |
187 | const char *name; | |
188 | uint16_t dl_type; | |
189 | uint8_t nw_proto; | |
190 | }; | |
191 | ||
192 | static bool | |
193 | parse_protocol(const char *name, const struct protocol **p_out) | |
194 | { | |
195 | static const struct protocol protocols[] = { | |
196 | { "ip", ETH_TYPE_IP, 0 }, | |
64cd4a05 JP |
197 | { "ipv4", ETH_TYPE_IP, 0 }, |
198 | { "ip4", ETH_TYPE_IP, 0 }, | |
f22716dc | 199 | { "arp", ETH_TYPE_ARP, 0 }, |
6767a2cc JP |
200 | { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, |
201 | { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, | |
202 | { "udp", ETH_TYPE_IP, IPPROTO_UDP }, | |
0d56eaf2 | 203 | { "sctp", ETH_TYPE_IP, IPPROTO_SCTP }, |
d31f1109 JP |
204 | { "ipv6", ETH_TYPE_IPV6, 0 }, |
205 | { "ip6", ETH_TYPE_IPV6, 0 }, | |
206 | { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, | |
207 | { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, | |
208 | { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, | |
0d56eaf2 | 209 | { "sctp6", ETH_TYPE_IPV6, IPPROTO_SCTP }, |
8087f5ff | 210 | { "rarp", ETH_TYPE_RARP, 0}, |
b02475c5 SH |
211 | { "mpls", ETH_TYPE_MPLS, 0 }, |
212 | { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, | |
213 | }; | |
f22716dc JP |
214 | const struct protocol *p; |
215 | ||
216 | for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { | |
217 | if (!strcmp(p->name, name)) { | |
218 | *p_out = p; | |
219 | return true; | |
220 | } | |
221 | } | |
222 | *p_out = NULL; | |
223 | return false; | |
224 | } | |
225 | ||
bdda5aca | 226 | /* Parses 's' as the (possibly masked) value of field 'mf', and updates |
db0b6c29 JR |
227 | * 'match' appropriately. Restricts the set of usable protocols to ones |
228 | * supporting the parsed field. | |
bdda5aca BP |
229 | * |
230 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
231 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 232 | static char * OVS_WARN_UNUSED_RESULT |
db0b6c29 JR |
233 | parse_field(const struct mf_field *mf, const char *s, struct match *match, |
234 | enum ofputil_protocol *usable_protocols) | |
8050b31d | 235 | { |
6a885fd0 BP |
236 | union mf_value value, mask; |
237 | char *error; | |
bad68a99 | 238 | |
0eede6b6 JG |
239 | if (!*s) { |
240 | /* If there's no string, we're just trying to match on the | |
241 | * existence of the field, so use a no-op value. */ | |
242 | s = "0/0"; | |
243 | } | |
244 | ||
6a885fd0 | 245 | error = mf_parse(mf, s, &value, &mask); |
bdda5aca | 246 | if (!error) { |
4f7b100c | 247 | *usable_protocols &= mf_set(mf, &value, &mask, match, &error); |
8050b31d | 248 | } |
bdda5aca | 249 | return error; |
00b1c62f BP |
250 | } |
251 | ||
c2d936a4 BP |
252 | static char * |
253 | extract_actions(char *s) | |
254 | { | |
255 | s = strstr(s, "action"); | |
256 | if (s) { | |
257 | *s = '\0'; | |
258 | s = strchr(s + 1, '='); | |
259 | return s ? s + 1 : NULL; | |
260 | } else { | |
261 | return NULL; | |
262 | } | |
263 | } | |
264 | ||
265 | ||
cab50449 | 266 | static char * OVS_WARN_UNUSED_RESULT |
db0b6c29 | 267 | parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, |
ba2fe8e9 | 268 | enum ofputil_protocol *usable_protocols) |
f22716dc | 269 | { |
c821124b BP |
270 | enum { |
271 | F_OUT_PORT = 1 << 0, | |
272 | F_ACTIONS = 1 << 1, | |
ca26eb44 | 273 | F_IMPORTANCE = 1 << 2, |
c821124b | 274 | F_TIMEOUT = 1 << 3, |
a993007b BP |
275 | F_PRIORITY = 1 << 4, |
276 | F_FLAGS = 1 << 5, | |
c821124b | 277 | } fields; |
75a75043 | 278 | char *act_str = NULL; |
bc78455b | 279 | char *name, *value; |
f22716dc | 280 | |
db0b6c29 JR |
281 | *usable_protocols = OFPUTIL_P_ANY; |
282 | ||
db5076ee JR |
283 | if (command == -2) { |
284 | size_t len; | |
285 | ||
286 | string += strspn(string, " \t\r\n"); /* Skip white space. */ | |
287 | len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */ | |
288 | ||
289 | if (!strncmp(string, "add", len)) { | |
290 | command = OFPFC_ADD; | |
291 | } else if (!strncmp(string, "delete", len)) { | |
292 | command = OFPFC_DELETE; | |
293 | } else if (!strncmp(string, "delete_strict", len)) { | |
294 | command = OFPFC_DELETE_STRICT; | |
295 | } else if (!strncmp(string, "modify", len)) { | |
296 | command = OFPFC_MODIFY; | |
297 | } else if (!strncmp(string, "modify_strict", len)) { | |
298 | command = OFPFC_MODIFY_STRICT; | |
299 | } else { | |
300 | len = 0; | |
301 | command = OFPFC_ADD; | |
302 | } | |
303 | string += len; | |
304 | } | |
305 | ||
c821124b BP |
306 | switch (command) { |
307 | case -1: | |
308 | fields = F_OUT_PORT; | |
309 | break; | |
310 | ||
311 | case OFPFC_ADD: | |
ca26eb44 | 312 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS | F_IMPORTANCE; |
c821124b BP |
313 | break; |
314 | ||
315 | case OFPFC_DELETE: | |
316 | fields = F_OUT_PORT; | |
317 | break; | |
318 | ||
319 | case OFPFC_DELETE_STRICT: | |
320 | fields = F_OUT_PORT | F_PRIORITY; | |
321 | break; | |
322 | ||
323 | case OFPFC_MODIFY: | |
a993007b | 324 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
325 | break; |
326 | ||
327 | case OFPFC_MODIFY_STRICT: | |
a993007b | 328 | fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; |
c821124b BP |
329 | break; |
330 | ||
331 | default: | |
428b2edd | 332 | OVS_NOT_REACHED(); |
c821124b BP |
333 | } |
334 | ||
81a76618 BP |
335 | match_init_catchall(&fm->match); |
336 | fm->priority = OFP_DEFAULT_PRIORITY; | |
88ca35ee | 337 | fm->cookie = htonll(0); |
e729e793 | 338 | fm->cookie_mask = htonll(0); |
623e1caf JP |
339 | if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { |
340 | /* For modify, by default, don't update the cookie. */ | |
b8266395 | 341 | fm->new_cookie = OVS_BE64_MAX; |
623e1caf JP |
342 | } else{ |
343 | fm->new_cookie = htonll(0); | |
344 | } | |
23342857 | 345 | fm->modify_cookie = false; |
6c1491fb | 346 | fm->table_id = 0xff; |
c821124b | 347 | fm->command = command; |
88ca35ee BP |
348 | fm->idle_timeout = OFP_FLOW_PERMANENT; |
349 | fm->hard_timeout = OFP_FLOW_PERMANENT; | |
350 | fm->buffer_id = UINT32_MAX; | |
7f05e7ab | 351 | fm->out_port = OFPP_ANY; |
88ca35ee | 352 | fm->flags = 0; |
ca26eb44 | 353 | fm->importance = 0; |
30ef36c6 | 354 | fm->out_group = OFPG_ANY; |
cc40d06b | 355 | fm->delete_reason = OFPRR_DELETE; |
c821124b | 356 | if (fields & F_ACTIONS) { |
c2d936a4 | 357 | act_str = extract_actions(string); |
f22716dc | 358 | if (!act_str) { |
bdda5aca | 359 | return xstrdup("must specify an action"); |
f22716dc | 360 | } |
f22716dc | 361 | } |
bc78455b JG |
362 | |
363 | while (ofputil_parse_key_value(&string, &name, &value)) { | |
f22716dc | 364 | const struct protocol *p; |
bdda5aca | 365 | char *error = NULL; |
f22716dc JP |
366 | |
367 | if (parse_protocol(name, &p)) { | |
81a76618 | 368 | match_set_dl_type(&fm->match, htons(p->dl_type)); |
f22716dc | 369 | if (p->nw_proto) { |
81a76618 | 370 | match_set_nw_proto(&fm->match, p->nw_proto); |
f22716dc | 371 | } |
a993007b | 372 | } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { |
0fb88c18 | 373 | fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; |
a993007b | 374 | } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { |
0fb88c18 | 375 | fm->flags |= OFPUTIL_FF_CHECK_OVERLAP; |
2e1ae200 | 376 | } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { |
0fb88c18 | 377 | fm->flags |= OFPUTIL_FF_RESET_COUNTS; |
db0b6c29 | 378 | *usable_protocols &= OFPUTIL_P_OF12_UP; |
2e1ae200 | 379 | } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { |
0fb88c18 | 380 | fm->flags |= OFPUTIL_FF_NO_PKT_COUNTS; |
db0b6c29 | 381 | *usable_protocols &= OFPUTIL_P_OF13_UP; |
2e1ae200 | 382 | } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { |
0fb88c18 | 383 | fm->flags |= OFPUTIL_FF_NO_BYT_COUNTS; |
db0b6c29 | 384 | *usable_protocols &= OFPUTIL_P_OF13_UP; |
adcf00ba AZ |
385 | } else if (!strcmp(name, "no_readonly_table") |
386 | || !strcmp(name, "allow_hidden_fields")) { | |
387 | /* ignore these fields. */ | |
1cb20095 | 388 | } else if (mf_from_name(name)) { |
1cb20095 JG |
389 | error = parse_field(mf_from_name(name), value, &fm->match, |
390 | usable_protocols); | |
f22716dc | 391 | } else { |
bc78455b | 392 | if (!*value) { |
bdda5aca | 393 | return xasprintf("field %s missing value", name); |
f22716dc JP |
394 | } |
395 | ||
6c1491fb | 396 | if (!strcmp(name, "table")) { |
bdda5aca | 397 | error = str_to_u8(value, "table", &fm->table_id); |
db0b6c29 JR |
398 | if (fm->table_id != 0xff) { |
399 | *usable_protocols &= OFPUTIL_P_TID; | |
400 | } | |
19ba1422 | 401 | } else if (fields & F_OUT_PORT && !strcmp(name, "out_port")) { |
be3f512a | 402 | if (!ofputil_port_from_string(value, &fm->out_port)) { |
bdda5aca BP |
403 | error = xasprintf("%s is not a valid OpenFlow port", |
404 | value); | |
c6100d92 | 405 | } |
6d5d1f3b BP |
406 | } else if (fields & F_OUT_PORT && !strcmp(name, "out_group")) { |
407 | *usable_protocols &= OFPUTIL_P_OF11_UP; | |
408 | if (!ofputil_group_from_string(value, &fm->out_group)) { | |
409 | error = xasprintf("%s is not a valid OpenFlow group", | |
410 | value); | |
411 | } | |
c821124b | 412 | } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { |
4be17953 | 413 | uint16_t priority = 0; |
bdda5aca BP |
414 | |
415 | error = str_to_u16(value, name, &priority); | |
416 | fm->priority = priority; | |
c821124b | 417 | } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { |
bdda5aca | 418 | error = str_to_u16(value, name, &fm->idle_timeout); |
c821124b | 419 | } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { |
bdda5aca | 420 | error = str_to_u16(value, name, &fm->hard_timeout); |
ca26eb44 RB |
421 | } else if (fields & F_IMPORTANCE && !strcmp(name, "importance")) { |
422 | error = str_to_u16(value, name, &fm->importance); | |
e729e793 JP |
423 | } else if (!strcmp(name, "cookie")) { |
424 | char *mask = strchr(value, '/'); | |
623e1caf | 425 | |
e729e793 | 426 | if (mask) { |
623e1caf | 427 | /* A mask means we're searching for a cookie. */ |
e729e793 | 428 | if (command == OFPFC_ADD) { |
bdda5aca BP |
429 | return xstrdup("flow additions cannot use " |
430 | "a cookie mask"); | |
e729e793 JP |
431 | } |
432 | *mask = '\0'; | |
bdda5aca BP |
433 | error = str_to_be64(value, &fm->cookie); |
434 | if (error) { | |
435 | return error; | |
436 | } | |
437 | error = str_to_be64(mask + 1, &fm->cookie_mask); | |
db0b6c29 JR |
438 | |
439 | /* Matching of the cookie is only supported through NXM or | |
440 | * OF1.1+. */ | |
441 | if (fm->cookie_mask != htonll(0)) { | |
442 | *usable_protocols &= OFPUTIL_P_NXM_OF11_UP; | |
443 | } | |
e729e793 | 444 | } else { |
623e1caf JP |
445 | /* No mask means that the cookie is being set. */ |
446 | if (command != OFPFC_ADD && command != OFPFC_MODIFY | |
bdda5aca BP |
447 | && command != OFPFC_MODIFY_STRICT) { |
448 | return xstrdup("cannot set cookie"); | |
623e1caf | 449 | } |
bdda5aca | 450 | error = str_to_be64(value, &fm->new_cookie); |
23342857 | 451 | fm->modify_cookie = true; |
e729e793 | 452 | } |
2c6d8411 BP |
453 | } else if (!strcmp(name, "duration") |
454 | || !strcmp(name, "n_packets") | |
146356e9 JP |
455 | || !strcmp(name, "n_bytes") |
456 | || !strcmp(name, "idle_age") | |
457 | || !strcmp(name, "hard_age")) { | |
2c6d8411 BP |
458 | /* Ignore these, so that users can feed the output of |
459 | * "ovs-ofctl dump-flows" back into commands that parse | |
460 | * flows. */ | |
f22716dc | 461 | } else { |
bdda5aca BP |
462 | error = xasprintf("unknown keyword %s", name); |
463 | } | |
bc78455b | 464 | } |
bdda5aca | 465 | |
bc78455b JG |
466 | if (error) { |
467 | return error; | |
f22716dc JP |
468 | } |
469 | } | |
db0b6c29 JR |
470 | /* Check for usable protocol interdependencies between match fields. */ |
471 | if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) { | |
472 | const struct flow_wildcards *wc = &fm->match.wc; | |
473 | /* Only NXM and OXM support matching L3 and L4 fields within IPv6. | |
474 | * | |
475 | * (IPv6 specific fields as well as arp_sha, arp_tha, nw_frag, and | |
476 | * nw_ttl are covered elsewhere so they don't need to be included in | |
477 | * this test too.) | |
478 | */ | |
479 | if (wc->masks.nw_proto || wc->masks.nw_tos | |
480 | || wc->masks.tp_src || wc->masks.tp_dst) { | |
481 | *usable_protocols &= OFPUTIL_P_NXM_OXM_ANY; | |
482 | } | |
483 | } | |
b8266395 | 484 | if (!fm->cookie_mask && fm->new_cookie == OVS_BE64_MAX |
bdda5aca | 485 | && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { |
623e1caf JP |
486 | /* On modifies without a mask, we are supposed to add a flow if |
487 | * one does not exist. If a cookie wasn't been specified, use a | |
488 | * default of zero. */ | |
489 | fm->new_cookie = htonll(0); | |
490 | } | |
75a75043 | 491 | if (fields & F_ACTIONS) { |
c2d936a4 | 492 | enum ofputil_protocol action_usable_protocols; |
f25d0cf3 | 493 | struct ofpbuf ofpacts; |
bdda5aca | 494 | char *error; |
75a75043 | 495 | |
f25d0cf3 | 496 | ofpbuf_init(&ofpacts, 32); |
c2d936a4 BP |
497 | error = ofpacts_parse_instructions(act_str, &ofpacts, |
498 | &action_usable_protocols); | |
499 | *usable_protocols &= action_usable_protocols; | |
bdda5aca BP |
500 | if (!error) { |
501 | enum ofperr err; | |
502 | ||
6fd6ed71 | 503 | err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow, |
ba2fe8e9 | 504 | OFPP_MAX, fm->table_id, 255, usable_protocols); |
a805558c | 505 | if (!err && !*usable_protocols) { |
ba2fe8e9 BP |
506 | err = OFPERR_OFPBAC_MATCH_INCONSISTENT; |
507 | } | |
bdda5aca | 508 | if (err) { |
ba2fe8e9 BP |
509 | error = xasprintf("actions are invalid with specified match " |
510 | "(%s)", ofperr_to_string(err)); | |
bdda5aca | 511 | } |
ba2fe8e9 | 512 | |
bdda5aca BP |
513 | } |
514 | if (error) { | |
515 | ofpbuf_uninit(&ofpacts); | |
516 | return error; | |
b019d34d SH |
517 | } |
518 | ||
6fd6ed71 | 519 | fm->ofpacts_len = ofpacts.size; |
bdda5aca | 520 | fm->ofpacts = ofpbuf_steal_data(&ofpacts); |
75a75043 | 521 | } else { |
f25d0cf3 BP |
522 | fm->ofpacts_len = 0; |
523 | fm->ofpacts = NULL; | |
75a75043 | 524 | } |
ec610b7b | 525 | |
bdda5aca | 526 | return NULL; |
f22716dc | 527 | } |
15f1f1b6 | 528 | |
638a19b0 | 529 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man |
bdda5aca | 530 | * page) into 'fm' for sending the specified flow_mod 'command' to a switch. |
db0b6c29 | 531 | * Returns the set of usable protocols in '*usable_protocols'. |
bdda5aca BP |
532 | * |
533 | * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* | |
534 | * constant for 'command'. To parse syntax for an OFPST_FLOW or | |
535 | * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. | |
536 | * | |
db5076ee JR |
537 | * If 'command' is given as -2, 'str_' may begin with a command name ("add", |
538 | * "modify", "delete", "modify_strict", or "delete_strict"). A missing command | |
539 | * name is treated as "add". | |
540 | * | |
bdda5aca BP |
541 | * Returns NULL if successful, otherwise a malloc()'d string describing the |
542 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 543 | char * OVS_WARN_UNUSED_RESULT |
db0b6c29 | 544 | parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, |
ba2fe8e9 | 545 | enum ofputil_protocol *usable_protocols) |
bdda5aca BP |
546 | { |
547 | char *string = xstrdup(str_); | |
548 | char *error; | |
549 | ||
ba2fe8e9 | 550 | error = parse_ofp_str__(fm, command, string, usable_protocols); |
bdda5aca BP |
551 | if (error) { |
552 | fm->ofpacts = NULL; | |
553 | fm->ofpacts_len = 0; | |
554 | } | |
555 | ||
556 | free(string); | |
557 | return error; | |
558 | } | |
559 | ||
cab50449 | 560 | static char * OVS_WARN_UNUSED_RESULT |
bdda5aca | 561 | parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string, |
db0b6c29 JR |
562 | struct ofpbuf *bands, int command, |
563 | enum ofputil_protocol *usable_protocols) | |
638a19b0 JR |
564 | { |
565 | enum { | |
566 | F_METER = 1 << 0, | |
567 | F_FLAGS = 1 << 1, | |
568 | F_BANDS = 1 << 2, | |
569 | } fields; | |
638a19b0 JR |
570 | char *save_ptr = NULL; |
571 | char *band_str = NULL; | |
572 | char *name; | |
573 | ||
db0b6c29 | 574 | /* Meters require at least OF 1.3. */ |
040f4db8 | 575 | *usable_protocols = OFPUTIL_P_OF13_UP; |
db0b6c29 | 576 | |
638a19b0 JR |
577 | switch (command) { |
578 | case -1: | |
579 | fields = F_METER; | |
580 | break; | |
581 | ||
582 | case OFPMC13_ADD: | |
583 | fields = F_METER | F_FLAGS | F_BANDS; | |
584 | break; | |
585 | ||
586 | case OFPMC13_DELETE: | |
587 | fields = F_METER; | |
588 | break; | |
589 | ||
590 | case OFPMC13_MODIFY: | |
591 | fields = F_METER | F_FLAGS | F_BANDS; | |
592 | break; | |
593 | ||
594 | default: | |
428b2edd | 595 | OVS_NOT_REACHED(); |
638a19b0 JR |
596 | } |
597 | ||
598 | mm->command = command; | |
599 | mm->meter.meter_id = 0; | |
600 | mm->meter.flags = 0; | |
601 | if (fields & F_BANDS) { | |
602 | band_str = strstr(string, "band"); | |
603 | if (!band_str) { | |
bdda5aca | 604 | return xstrdup("must specify bands"); |
638a19b0 JR |
605 | } |
606 | *band_str = '\0'; | |
607 | ||
608 | band_str = strchr(band_str + 1, '='); | |
609 | if (!band_str) { | |
bdda5aca | 610 | return xstrdup("must specify bands"); |
638a19b0 JR |
611 | } |
612 | ||
613 | band_str++; | |
614 | } | |
615 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
616 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
617 | ||
618 | if (fields & F_FLAGS && !strcmp(name, "kbps")) { | |
619 | mm->meter.flags |= OFPMF13_KBPS; | |
620 | } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { | |
621 | mm->meter.flags |= OFPMF13_PKTPS; | |
622 | } else if (fields & F_FLAGS && !strcmp(name, "burst")) { | |
623 | mm->meter.flags |= OFPMF13_BURST; | |
624 | } else if (fields & F_FLAGS && !strcmp(name, "stats")) { | |
625 | mm->meter.flags |= OFPMF13_STATS; | |
626 | } else { | |
627 | char *value; | |
628 | ||
629 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
630 | if (!value) { | |
bdda5aca | 631 | return xasprintf("field %s missing value", name); |
638a19b0 JR |
632 | } |
633 | ||
634 | if (!strcmp(name, "meter")) { | |
635 | if (!strcmp(value, "all")) { | |
636 | mm->meter.meter_id = OFPM13_ALL; | |
637 | } else if (!strcmp(value, "controller")) { | |
638 | mm->meter.meter_id = OFPM13_CONTROLLER; | |
639 | } else if (!strcmp(value, "slowpath")) { | |
640 | mm->meter.meter_id = OFPM13_SLOWPATH; | |
641 | } else { | |
bdda5aca BP |
642 | char *error = str_to_u32(value, &mm->meter.meter_id); |
643 | if (error) { | |
644 | return error; | |
645 | } | |
dc8a858f JS |
646 | if (mm->meter.meter_id > OFPM13_MAX |
647 | || !mm->meter.meter_id) { | |
bdda5aca | 648 | return xasprintf("invalid value for %s", name); |
638a19b0 JR |
649 | } |
650 | } | |
651 | } else { | |
bdda5aca | 652 | return xasprintf("unknown keyword %s", name); |
638a19b0 JR |
653 | } |
654 | } | |
655 | } | |
656 | if (fields & F_METER && !mm->meter.meter_id) { | |
bdda5aca | 657 | return xstrdup("must specify 'meter'"); |
638a19b0 JR |
658 | } |
659 | if (fields & F_FLAGS && !mm->meter.flags) { | |
bdda5aca | 660 | return xstrdup("meter must specify either 'kbps' or 'pktps'"); |
638a19b0 JR |
661 | } |
662 | ||
663 | if (fields & F_BANDS) { | |
638a19b0 JR |
664 | uint16_t n_bands = 0; |
665 | struct ofputil_meter_band *band = NULL; | |
666 | int i; | |
667 | ||
638a19b0 JR |
668 | for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; |
669 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
670 | ||
671 | char *value; | |
672 | ||
673 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
674 | if (!value) { | |
bdda5aca | 675 | return xasprintf("field %s missing value", name); |
638a19b0 JR |
676 | } |
677 | ||
678 | if (!strcmp(name, "type")) { | |
679 | /* Start a new band */ | |
bdda5aca | 680 | band = ofpbuf_put_zeros(bands, sizeof *band); |
638a19b0 JR |
681 | n_bands++; |
682 | ||
683 | if (!strcmp(value, "drop")) { | |
684 | band->type = OFPMBT13_DROP; | |
685 | } else if (!strcmp(value, "dscp_remark")) { | |
686 | band->type = OFPMBT13_DSCP_REMARK; | |
687 | } else { | |
bdda5aca | 688 | return xasprintf("field %s unknown value %s", name, value); |
638a19b0 JR |
689 | } |
690 | } else if (!band || !band->type) { | |
bdda5aca | 691 | return xstrdup("band must start with the 'type' keyword"); |
638a19b0 | 692 | } else if (!strcmp(name, "rate")) { |
bdda5aca BP |
693 | char *error = str_to_u32(value, &band->rate); |
694 | if (error) { | |
695 | return error; | |
696 | } | |
638a19b0 | 697 | } else if (!strcmp(name, "burst_size")) { |
bdda5aca BP |
698 | char *error = str_to_u32(value, &band->burst_size); |
699 | if (error) { | |
700 | return error; | |
701 | } | |
638a19b0 | 702 | } else if (!strcmp(name, "prec_level")) { |
bdda5aca BP |
703 | char *error = str_to_u8(value, name, &band->prec_level); |
704 | if (error) { | |
705 | return error; | |
706 | } | |
638a19b0 | 707 | } else { |
bdda5aca | 708 | return xasprintf("unknown keyword %s", name); |
638a19b0 JR |
709 | } |
710 | } | |
711 | /* validate bands */ | |
712 | if (!n_bands) { | |
bdda5aca | 713 | return xstrdup("meter must have bands"); |
638a19b0 JR |
714 | } |
715 | ||
716 | mm->meter.n_bands = n_bands; | |
bdda5aca | 717 | mm->meter.bands = ofpbuf_steal_data(bands); |
638a19b0 JR |
718 | |
719 | for (i = 0; i < n_bands; ++i) { | |
720 | band = &mm->meter.bands[i]; | |
721 | ||
722 | if (!band->type) { | |
bdda5aca | 723 | return xstrdup("band must have 'type'"); |
638a19b0 JR |
724 | } |
725 | if (band->type == OFPMBT13_DSCP_REMARK) { | |
726 | if (!band->prec_level) { | |
bdda5aca BP |
727 | return xstrdup("'dscp_remark' band must have" |
728 | " 'prec_level'"); | |
638a19b0 JR |
729 | } |
730 | } else { | |
731 | if (band->prec_level) { | |
bdda5aca BP |
732 | return xstrdup("Only 'dscp_remark' band may have" |
733 | " 'prec_level'"); | |
638a19b0 JR |
734 | } |
735 | } | |
736 | if (!band->rate) { | |
bdda5aca | 737 | return xstrdup("band must have 'rate'"); |
638a19b0 JR |
738 | } |
739 | if (mm->meter.flags & OFPMF13_BURST) { | |
740 | if (!band->burst_size) { | |
bdda5aca BP |
741 | return xstrdup("band must have 'burst_size' " |
742 | "when 'burst' flag is set"); | |
638a19b0 JR |
743 | } |
744 | } else { | |
745 | if (band->burst_size) { | |
bdda5aca BP |
746 | return xstrdup("band may have 'burst_size' only " |
747 | "when 'burst' flag is set"); | |
638a19b0 JR |
748 | } |
749 | } | |
750 | } | |
751 | } else { | |
752 | mm->meter.n_bands = 0; | |
753 | mm->meter.bands = NULL; | |
754 | } | |
755 | ||
bdda5aca BP |
756 | return NULL; |
757 | } | |
758 | ||
759 | /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man | |
760 | * page) into 'mm' for sending the specified meter_mod 'command' to a switch. | |
761 | * | |
762 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
763 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 764 | char * OVS_WARN_UNUSED_RESULT |
bdda5aca | 765 | parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, |
db0b6c29 | 766 | int command, enum ofputil_protocol *usable_protocols) |
bdda5aca BP |
767 | { |
768 | struct ofpbuf bands; | |
769 | char *string; | |
770 | char *error; | |
771 | ||
772 | ofpbuf_init(&bands, 64); | |
773 | string = xstrdup(str_); | |
774 | ||
db0b6c29 JR |
775 | error = parse_ofp_meter_mod_str__(mm, string, &bands, command, |
776 | usable_protocols); | |
bdda5aca | 777 | |
638a19b0 | 778 | free(string); |
bdda5aca BP |
779 | ofpbuf_uninit(&bands); |
780 | ||
781 | return error; | |
638a19b0 JR |
782 | } |
783 | ||
cab50449 | 784 | static char * OVS_WARN_UNUSED_RESULT |
bdda5aca | 785 | parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, |
db0b6c29 JR |
786 | const char *str_, char *string, |
787 | enum ofputil_protocol *usable_protocols) | |
2b07c8b1 | 788 | { |
ca4fbdfe | 789 | static atomic_count id = ATOMIC_COUNT_INIT(0); |
bc78455b | 790 | char *name, *value; |
2b07c8b1 | 791 | |
ca4fbdfe | 792 | fmr->id = atomic_count_inc(&id); |
a944ef40 | 793 | |
2b07c8b1 BP |
794 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY |
795 | | NXFMF_OWN | NXFMF_ACTIONS); | |
796 | fmr->out_port = OFPP_NONE; | |
797 | fmr->table_id = 0xff; | |
81a76618 | 798 | match_init_catchall(&fmr->match); |
2b07c8b1 | 799 | |
bc78455b | 800 | while (ofputil_parse_key_value(&string, &name, &value)) { |
2b07c8b1 | 801 | const struct protocol *p; |
0eede6b6 | 802 | char *error = NULL; |
2b07c8b1 BP |
803 | |
804 | if (!strcmp(name, "!initial")) { | |
805 | fmr->flags &= ~NXFMF_INITIAL; | |
806 | } else if (!strcmp(name, "!add")) { | |
807 | fmr->flags &= ~NXFMF_ADD; | |
808 | } else if (!strcmp(name, "!delete")) { | |
809 | fmr->flags &= ~NXFMF_DELETE; | |
810 | } else if (!strcmp(name, "!modify")) { | |
811 | fmr->flags &= ~NXFMF_MODIFY; | |
812 | } else if (!strcmp(name, "!actions")) { | |
813 | fmr->flags &= ~NXFMF_ACTIONS; | |
814 | } else if (!strcmp(name, "!own")) { | |
815 | fmr->flags &= ~NXFMF_OWN; | |
816 | } else if (parse_protocol(name, &p)) { | |
81a76618 | 817 | match_set_dl_type(&fmr->match, htons(p->dl_type)); |
2b07c8b1 | 818 | if (p->nw_proto) { |
81a76618 | 819 | match_set_nw_proto(&fmr->match, p->nw_proto); |
2b07c8b1 | 820 | } |
0eede6b6 JG |
821 | } else if (mf_from_name(name)) { |
822 | error = parse_field(mf_from_name(name), value, &fmr->match, | |
823 | usable_protocols); | |
2b07c8b1 | 824 | } else { |
bc78455b | 825 | if (!*value) { |
bdda5aca | 826 | return xasprintf("%s: field %s missing value", str_, name); |
2b07c8b1 BP |
827 | } |
828 | ||
829 | if (!strcmp(name, "table")) { | |
0eede6b6 | 830 | error = str_to_u8(value, "table", &fmr->table_id); |
2b07c8b1 | 831 | } else if (!strcmp(name, "out_port")) { |
4e022ec0 | 832 | fmr->out_port = u16_to_ofp(atoi(value)); |
2b07c8b1 | 833 | } else { |
bdda5aca | 834 | return xasprintf("%s: unknown keyword %s", str_, name); |
2b07c8b1 BP |
835 | } |
836 | } | |
0eede6b6 JG |
837 | |
838 | if (error) { | |
839 | return error; | |
840 | } | |
2b07c8b1 | 841 | } |
bdda5aca BP |
842 | return NULL; |
843 | } | |
844 | ||
845 | /* Convert 'str_' (as described in the documentation for the "monitor" command | |
846 | * in the ovs-ofctl man page) into 'fmr'. | |
847 | * | |
848 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
849 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 850 | char * OVS_WARN_UNUSED_RESULT |
bdda5aca | 851 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, |
db0b6c29 JR |
852 | const char *str_, |
853 | enum ofputil_protocol *usable_protocols) | |
bdda5aca BP |
854 | { |
855 | char *string = xstrdup(str_); | |
db0b6c29 JR |
856 | char *error = parse_flow_monitor_request__(fmr, str_, string, |
857 | usable_protocols); | |
2b07c8b1 | 858 | free(string); |
bdda5aca | 859 | return error; |
2b07c8b1 BP |
860 | } |
861 | ||
88ca35ee | 862 | /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' |
bdda5aca BP |
863 | * (one of OFPFC_*) into 'fm'. |
864 | * | |
db5076ee JR |
865 | * If 'command' is given as -2, 'string' may begin with a command name ("add", |
866 | * "modify", "delete", "modify_strict", or "delete_strict"). A missing command | |
867 | * name is treated as "add". | |
868 | * | |
bdda5aca BP |
869 | * Returns NULL if successful, otherwise a malloc()'d string describing the |
870 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 871 | char * OVS_WARN_UNUSED_RESULT |
27527aa0 | 872 | parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, |
db5076ee | 873 | int command, |
ba2fe8e9 | 874 | enum ofputil_protocol *usable_protocols) |
15f1f1b6 | 875 | { |
ba2fe8e9 | 876 | char *error = parse_ofp_str(fm, command, string, usable_protocols); |
db5076ee | 877 | |
bdda5aca BP |
878 | if (!error) { |
879 | /* Normalize a copy of the match. This ensures that non-normalized | |
880 | * flows get logged but doesn't affect what gets sent to the switch, so | |
881 | * that the switch can do whatever it likes with the flow. */ | |
882 | struct match match_copy = fm->match; | |
883 | ofputil_normalize_match(&match_copy); | |
884 | } | |
88ca35ee | 885 | |
bdda5aca | 886 | return error; |
15f1f1b6 BP |
887 | } |
888 | ||
de7d3c07 SJ |
889 | /* Convert 'setting' (as described for the "mod-table" command |
890 | * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and | |
891 | * 'tm->table_vacancy->vacancy_down' threshold values. | |
892 | * For the two threshold values, value of vacancy_up is always greater | |
893 | * than value of vacancy_down. | |
894 | * | |
895 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
896 | * error. The caller is responsible for freeing the returned string. */ | |
897 | char * OVS_WARN_UNUSED_RESULT | |
898 | parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting) | |
899 | { | |
900 | char *save_ptr = NULL; | |
901 | char *vac_up, *vac_down; | |
902 | char *value = strdup(setting); | |
903 | int vacancy_up, vacancy_down; | |
904 | ||
905 | strtok_r(value, ":", &save_ptr); | |
906 | vac_down = strtok_r(NULL, ",", &save_ptr); | |
907 | if (!vac_down) { | |
908 | return xasprintf("Vacancy down value missing"); | |
909 | } | |
910 | if (!str_to_int(vac_down, 0, &vacancy_down) || | |
911 | vacancy_down < 0 || vacancy_down > 100) { | |
912 | return xasprintf("Invalid vacancy down value \"%s\"", vac_down); | |
913 | } | |
914 | vac_up = strtok_r(NULL, ",", &save_ptr); | |
915 | if (!vac_up) { | |
916 | return xasprintf("Vacancy up value missing"); | |
917 | } | |
918 | if (!str_to_int(vac_up, 0, &vacancy_up) || | |
919 | vacancy_up < 0 || vacancy_up > 100) { | |
920 | return xasprintf("Invalid vacancy up value \"%s\"", vac_up); | |
921 | } | |
922 | if (vacancy_down > vacancy_up) { | |
923 | return xasprintf("Invalid vacancy range, vacancy up should be greater" | |
924 | " than vacancy down ""(%s)", | |
925 | ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE)); | |
926 | } | |
927 | tm->table_vacancy.vacancy_down = vacancy_down; | |
928 | tm->table_vacancy.vacancy_up = vacancy_up; | |
929 | return NULL; | |
930 | } | |
931 | ||
82c22d34 BP |
932 | /* Convert 'table_id' and 'setting' (as described for the "mod-table" command |
933 | * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a | |
934 | * switch. | |
935 | * | |
936 | * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into | |
937 | * '*usable_versions'. | |
918f2b82 AZ |
938 | * |
939 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
940 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 941 | char * OVS_WARN_UNUSED_RESULT |
918f2b82 | 942 | parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, |
82c22d34 | 943 | const char *setting, uint32_t *usable_versions) |
918f2b82 | 944 | { |
82c22d34 | 945 | *usable_versions = 0; |
918f2b82 | 946 | if (!strcasecmp(table_id, "all")) { |
083761ad | 947 | tm->table_id = OFPTT_ALL; |
918f2b82 AZ |
948 | } else { |
949 | char *error = str_to_u8(table_id, "table_id", &tm->table_id); | |
950 | if (error) { | |
951 | return error; | |
952 | } | |
953 | } | |
954 | ||
82c22d34 BP |
955 | tm->miss = OFPUTIL_TABLE_MISS_DEFAULT; |
956 | tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; | |
957 | tm->eviction_flags = UINT32_MAX; | |
de7d3c07 SJ |
958 | tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; |
959 | tm->table_vacancy.vacancy_down = 0; | |
960 | tm->table_vacancy.vacancy_up = 0; | |
961 | tm->table_vacancy.vacancy = 0; | |
82c22d34 | 962 | /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod. |
de7d3c07 SJ |
963 | * Only OpenFlow 1.4+ can configure eviction and vacancy events |
964 | * via table_mod. | |
82c22d34 BP |
965 | */ |
966 | if (!strcmp(setting, "controller")) { | |
967 | tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER; | |
968 | *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); | |
969 | } else if (!strcmp(setting, "continue")) { | |
970 | tm->miss = OFPUTIL_TABLE_MISS_CONTINUE; | |
971 | *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); | |
972 | } else if (!strcmp(setting, "drop")) { | |
973 | tm->miss = OFPUTIL_TABLE_MISS_DROP; | |
974 | *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); | |
975 | } else if (!strcmp(setting, "evict")) { | |
976 | tm->eviction = OFPUTIL_TABLE_EVICTION_ON; | |
977 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
978 | } else if (!strcmp(setting, "noevict")) { | |
979 | tm->eviction = OFPUTIL_TABLE_EVICTION_OFF; | |
980 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
de7d3c07 SJ |
981 | } else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) { |
982 | tm->vacancy = OFPUTIL_TABLE_VACANCY_ON; | |
983 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
984 | char *error = parse_ofp_table_vacancy(tm, setting); | |
985 | if (error) { | |
986 | return error; | |
987 | } | |
988 | } else if (!strcmp(setting, "novacancy")) { | |
989 | tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF; | |
990 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
918f2b82 | 991 | } else { |
82c22d34 | 992 | return xasprintf("invalid table_mod setting %s", setting); |
918f2b82 AZ |
993 | } |
994 | ||
3c1bb396 | 995 | if (tm->table_id == 0xfe |
82c22d34 | 996 | && tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) { |
918f2b82 AZ |
997 | return xstrdup("last table's flow miss handling can not be continue"); |
998 | } | |
999 | ||
1000 | return NULL; | |
1001 | } | |
1002 | ||
1003 | ||
bdda5aca BP |
1004 | /* Opens file 'file_name' and reads each line as a flow_mod of the specified |
1005 | * type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated | |
1006 | * on the caller's behalf, and the number of flow_mods in '*n_fms'. | |
1007 | * | |
db5076ee JR |
1008 | * If 'command' is given as -2, each line may start with a command name |
1009 | * ("add", "modify", "delete", "modify_strict", or "delete_strict"). A missing | |
1010 | * command name is treated as "add". | |
1011 | * | |
bdda5aca BP |
1012 | * Returns NULL if successful, otherwise a malloc()'d string describing the |
1013 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 1014 | char * OVS_WARN_UNUSED_RESULT |
db5076ee | 1015 | parse_ofp_flow_mod_file(const char *file_name, int command, |
db0b6c29 | 1016 | struct ofputil_flow_mod **fms, size_t *n_fms, |
ba2fe8e9 | 1017 | enum ofputil_protocol *usable_protocols) |
15f1f1b6 | 1018 | { |
27527aa0 | 1019 | size_t allocated_fms; |
bdda5aca | 1020 | int line_number; |
27527aa0 | 1021 | FILE *stream; |
dd8101bc | 1022 | struct ds s; |
15f1f1b6 | 1023 | |
db0b6c29 JR |
1024 | *usable_protocols = OFPUTIL_P_ANY; |
1025 | ||
bdda5aca BP |
1026 | *fms = NULL; |
1027 | *n_fms = 0; | |
1028 | ||
27527aa0 BP |
1029 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); |
1030 | if (stream == NULL) { | |
bdda5aca BP |
1031 | return xasprintf("%s: open failed (%s)", |
1032 | file_name, ovs_strerror(errno)); | |
27527aa0 BP |
1033 | } |
1034 | ||
1035 | allocated_fms = *n_fms; | |
dd8101bc | 1036 | ds_init(&s); |
bdda5aca BP |
1037 | line_number = 0; |
1038 | while (!ds_get_preprocessed_line(&s, stream, &line_number)) { | |
1039 | char *error; | |
db0b6c29 | 1040 | enum ofputil_protocol usable; |
bdda5aca | 1041 | |
27527aa0 BP |
1042 | if (*n_fms >= allocated_fms) { |
1043 | *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); | |
1044 | } | |
db0b6c29 | 1045 | error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, |
ba2fe8e9 | 1046 | &usable); |
bdda5aca BP |
1047 | if (error) { |
1048 | size_t i; | |
1049 | ||
1050 | for (i = 0; i < *n_fms; i++) { | |
dc723c44 | 1051 | free(CONST_CAST(struct ofpact *, (*fms)[i].ofpacts)); |
bdda5aca BP |
1052 | } |
1053 | free(*fms); | |
1054 | *fms = NULL; | |
1055 | *n_fms = 0; | |
1056 | ||
1057 | ds_destroy(&s); | |
1058 | if (stream != stdin) { | |
1059 | fclose(stream); | |
1060 | } | |
1061 | ||
1062 | return xasprintf("%s:%d: %s", file_name, line_number, error); | |
1063 | } | |
db0b6c29 | 1064 | *usable_protocols &= usable; /* Each line can narrow the set. */ |
27527aa0 | 1065 | *n_fms += 1; |
15f1f1b6 | 1066 | } |
15f1f1b6 | 1067 | |
bdda5aca | 1068 | ds_destroy(&s); |
27527aa0 BP |
1069 | if (stream != stdin) { |
1070 | fclose(stream); | |
1071 | } | |
bdda5aca | 1072 | return NULL; |
88ca35ee BP |
1073 | } |
1074 | ||
cab50449 | 1075 | char * OVS_WARN_UNUSED_RESULT |
81d1ea94 | 1076 | parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, |
db0b6c29 | 1077 | bool aggregate, const char *string, |
ba2fe8e9 | 1078 | enum ofputil_protocol *usable_protocols) |
88ca35ee | 1079 | { |
a9a2da38 | 1080 | struct ofputil_flow_mod fm; |
bdda5aca BP |
1081 | char *error; |
1082 | ||
ba2fe8e9 | 1083 | error = parse_ofp_str(&fm, -1, string, usable_protocols); |
bdda5aca BP |
1084 | if (error) { |
1085 | return error; | |
1086 | } | |
88ca35ee | 1087 | |
db0b6c29 JR |
1088 | /* Special table ID support not required for stats requests. */ |
1089 | if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) { | |
1090 | *usable_protocols |= OFPUTIL_P_OF10_STD; | |
1091 | } | |
1092 | if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) { | |
1093 | *usable_protocols |= OFPUTIL_P_OF10_NXM; | |
1094 | } | |
1095 | ||
88ca35ee | 1096 | fsr->aggregate = aggregate; |
e729e793 JP |
1097 | fsr->cookie = fm.cookie; |
1098 | fsr->cookie_mask = fm.cookie_mask; | |
81a76618 | 1099 | fsr->match = fm.match; |
88ca35ee | 1100 | fsr->out_port = fm.out_port; |
7395c052 | 1101 | fsr->out_group = fm.out_group; |
6c1491fb | 1102 | fsr->table_id = fm.table_id; |
bdda5aca | 1103 | return NULL; |
15f1f1b6 | 1104 | } |
ccbe50f8 BP |
1105 | |
1106 | /* Parses a specification of a flow from 's' into 'flow'. 's' must take the | |
1107 | * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a | |
1108 | * mf_field. Fields must be specified in a natural order for satisfying | |
5a0a5702 GS |
1109 | * prerequisites. If 'mask' is specified, fills the mask field for each of the |
1110 | * field specified in flow. If the map, 'names_portno' is specfied, converts | |
1111 | * the in_port name into port no while setting the 'flow'. | |
ccbe50f8 BP |
1112 | * |
1113 | * Returns NULL on success, otherwise a malloc()'d string that explains the | |
1114 | * problem. */ | |
1115 | char * | |
5a0a5702 GS |
1116 | parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, |
1117 | const struct simap *portno_names) | |
ccbe50f8 BP |
1118 | { |
1119 | char *pos, *key, *value_s; | |
1120 | char *error = NULL; | |
1121 | char *copy; | |
1122 | ||
1123 | memset(flow, 0, sizeof *flow); | |
5a0a5702 GS |
1124 | if (mask) { |
1125 | memset(mask, 0, sizeof *mask); | |
1126 | } | |
ccbe50f8 BP |
1127 | |
1128 | pos = copy = xstrdup(s); | |
1129 | while (ofputil_parse_key_value(&pos, &key, &value_s)) { | |
1130 | const struct protocol *p; | |
1131 | if (parse_protocol(key, &p)) { | |
1132 | if (flow->dl_type) { | |
1133 | error = xasprintf("%s: Ethernet type set multiple times", s); | |
1134 | goto exit; | |
1135 | } | |
1136 | flow->dl_type = htons(p->dl_type); | |
5a0a5702 GS |
1137 | if (mask) { |
1138 | mask->dl_type = OVS_BE16_MAX; | |
1139 | } | |
ccbe50f8 BP |
1140 | |
1141 | if (p->nw_proto) { | |
1142 | if (flow->nw_proto) { | |
1143 | error = xasprintf("%s: network protocol set " | |
1144 | "multiple times", s); | |
1145 | goto exit; | |
1146 | } | |
1147 | flow->nw_proto = p->nw_proto; | |
5a0a5702 GS |
1148 | if (mask) { |
1149 | mask->nw_proto = UINT8_MAX; | |
1150 | } | |
ccbe50f8 BP |
1151 | } |
1152 | } else { | |
1153 | const struct mf_field *mf; | |
1154 | union mf_value value; | |
1155 | char *field_error; | |
1156 | ||
1157 | mf = mf_from_name(key); | |
1158 | if (!mf) { | |
1159 | error = xasprintf("%s: unknown field %s", s, key); | |
1160 | goto exit; | |
1161 | } | |
1162 | ||
1163 | if (!mf_are_prereqs_ok(mf, flow)) { | |
1164 | error = xasprintf("%s: prerequisites not met for setting %s", | |
1165 | s, key); | |
1166 | goto exit; | |
1167 | } | |
1168 | ||
1cb20095 | 1169 | if (mf_is_set(mf, flow)) { |
ccbe50f8 BP |
1170 | error = xasprintf("%s: field %s set multiple times", s, key); |
1171 | goto exit; | |
1172 | } | |
1173 | ||
5a0a5702 GS |
1174 | if (!strcmp(key, "in_port") |
1175 | && portno_names | |
1176 | && simap_contains(portno_names, value_s)) { | |
1177 | flow->in_port.ofp_port = u16_to_ofp( | |
1178 | simap_get(portno_names, value_s)); | |
1179 | if (mask) { | |
1180 | mask->in_port.ofp_port = u16_to_ofp(ntohs(OVS_BE16_MAX)); | |
1181 | } | |
1182 | } else { | |
1183 | field_error = mf_parse_value(mf, value_s, &value); | |
1184 | if (field_error) { | |
1185 | error = xasprintf("%s: bad value for %s (%s)", | |
1186 | s, key, field_error); | |
1187 | free(field_error); | |
1188 | goto exit; | |
1189 | } | |
ccbe50f8 | 1190 | |
5a0a5702 GS |
1191 | mf_set_flow_value(mf, &value, flow); |
1192 | if (mask) { | |
1193 | mf_mask_field(mf, mask); | |
1194 | } | |
1195 | } | |
ccbe50f8 BP |
1196 | } |
1197 | } | |
1198 | ||
4e022ec0 AW |
1199 | if (!flow->in_port.ofp_port) { |
1200 | flow->in_port.ofp_port = OFPP_NONE; | |
72d64e33 EJ |
1201 | } |
1202 | ||
ccbe50f8 BP |
1203 | exit: |
1204 | free(copy); | |
1205 | ||
1206 | if (error) { | |
1207 | memset(flow, 0, sizeof *flow); | |
5a0a5702 GS |
1208 | if (mask) { |
1209 | memset(mask, 0, sizeof *mask); | |
1210 | } | |
ccbe50f8 BP |
1211 | } |
1212 | return error; | |
1213 | } | |
7395c052 | 1214 | |
cab50449 | 1215 | static char * OVS_WARN_UNUSED_RESULT |
64e8c446 | 1216 | parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type, |
7395c052 NZ |
1217 | enum ofputil_protocol *usable_protocols) |
1218 | { | |
c2d936a4 | 1219 | char *pos, *key, *value; |
7395c052 | 1220 | struct ofpbuf ofpacts; |
c2d936a4 BP |
1221 | struct ds actions; |
1222 | char *error; | |
7395c052 | 1223 | |
64e8c446 | 1224 | bucket->weight = group_type == OFPGT11_SELECT ? 1 : 0; |
18ac06d3 | 1225 | bucket->bucket_id = OFPG15_BUCKET_ALL; |
7395c052 | 1226 | bucket->watch_port = OFPP_ANY; |
30ef36c6 | 1227 | bucket->watch_group = OFPG_ANY; |
7395c052 | 1228 | |
c2d936a4 | 1229 | ds_init(&actions); |
7395c052 | 1230 | |
c2d936a4 BP |
1231 | pos = str_; |
1232 | error = NULL; | |
1233 | while (ofputil_parse_key_value(&pos, &key, &value)) { | |
1234 | if (!strcasecmp(key, "weight")) { | |
1235 | error = str_to_u16(value, "weight", &bucket->weight); | |
1236 | } else if (!strcasecmp(key, "watch_port")) { | |
1237 | if (!ofputil_port_from_string(value, &bucket->watch_port) | |
7395c052 NZ |
1238 | || (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX) |
1239 | && bucket->watch_port != OFPP_ANY)) { | |
c2d936a4 | 1240 | error = xasprintf("%s: invalid watch_port", value); |
7395c052 | 1241 | } |
c2d936a4 BP |
1242 | } else if (!strcasecmp(key, "watch_group")) { |
1243 | error = str_to_u32(value, &bucket->watch_group); | |
7395c052 NZ |
1244 | if (!error && bucket->watch_group > OFPG_MAX) { |
1245 | error = xasprintf("invalid watch_group id %"PRIu32, | |
1246 | bucket->watch_group); | |
1247 | } | |
2d5d050c SH |
1248 | } else if (!strcasecmp(key, "bucket_id")) { |
1249 | error = str_to_u32(value, &bucket->bucket_id); | |
1250 | if (!error && bucket->bucket_id > OFPG15_BUCKET_MAX) { | |
1251 | error = xasprintf("invalid bucket_id id %"PRIu32, | |
1252 | bucket->bucket_id); | |
1253 | } | |
1254 | *usable_protocols &= OFPUTIL_P_OF15_UP; | |
c2d936a4 BP |
1255 | } else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) { |
1256 | ds_put_format(&actions, "%s,", value); | |
7395c052 | 1257 | } else { |
c2d936a4 | 1258 | ds_put_format(&actions, "%s(%s),", key, value); |
7395c052 NZ |
1259 | } |
1260 | ||
1261 | if (error) { | |
c2d936a4 | 1262 | ds_destroy(&actions); |
7395c052 NZ |
1263 | return error; |
1264 | } | |
1265 | } | |
1266 | ||
c2d936a4 BP |
1267 | if (!actions.length) { |
1268 | return xstrdup("bucket must specify actions"); | |
1269 | } | |
1270 | ds_chomp(&actions, ','); | |
1271 | ||
1272 | ofpbuf_init(&ofpacts, 0); | |
1273 | error = ofpacts_parse_actions(ds_cstr(&actions), &ofpacts, | |
1274 | usable_protocols); | |
1275 | ds_destroy(&actions); | |
1276 | if (error) { | |
1277 | ofpbuf_uninit(&ofpacts); | |
1278 | return error; | |
1279 | } | |
6fd6ed71 PS |
1280 | bucket->ofpacts = ofpacts.data; |
1281 | bucket->ofpacts_len = ofpacts.size; | |
7395c052 NZ |
1282 | |
1283 | return NULL; | |
1284 | } | |
1285 | ||
b879391e SH |
1286 | static char * OVS_WARN_UNUSED_RESULT |
1287 | parse_select_group_field(char *s, struct field_array *fa, | |
1288 | enum ofputil_protocol *usable_protocols) | |
1289 | { | |
68dfc25b | 1290 | char *name, *value_str; |
b879391e | 1291 | |
68dfc25b | 1292 | while (ofputil_parse_key_value(&s, &name, &value_str)) { |
b879391e SH |
1293 | const struct mf_field *mf = mf_from_name(name); |
1294 | ||
1295 | if (mf) { | |
1296 | char *error; | |
b879391e SH |
1297 | union mf_value value; |
1298 | ||
1299 | if (bitmap_is_set(fa->used.bm, mf->id)) { | |
1300 | return xasprintf("%s: duplicate field", name); | |
1301 | } | |
1302 | ||
68dfc25b | 1303 | if (*value_str) { |
b879391e SH |
1304 | error = mf_parse_value(mf, value_str, &value); |
1305 | if (error) { | |
1306 | return error; | |
1307 | } | |
1308 | ||
1309 | /* The mask cannot be all-zeros */ | |
1cb20095 JG |
1310 | if (!mf_is_tun_metadata(mf) && |
1311 | is_all_zeros(&value, mf->n_bytes)) { | |
b879391e SH |
1312 | return xasprintf("%s: values are wildcards here " |
1313 | "and must not be all-zeros", s); | |
1314 | } | |
1315 | ||
1316 | /* The values parsed are masks for fields used | |
1317 | * by the selection method */ | |
1318 | if (!mf_is_mask_valid(mf, &value)) { | |
1319 | return xasprintf("%s: invalid mask for field %s", | |
1320 | value_str, mf->name); | |
1321 | } | |
1322 | } else { | |
1323 | memset(&value, 0xff, mf->n_bytes); | |
1324 | } | |
1325 | ||
1326 | field_array_set(mf->id, &value, fa); | |
1327 | ||
1328 | if (is_all_ones(&value, mf->n_bytes)) { | |
1329 | *usable_protocols &= mf->usable_protocols_exact; | |
1330 | } else if (mf->usable_protocols_bitwise == mf->usable_protocols_cidr | |
1331 | || ip_is_cidr(value.be32)) { | |
1332 | *usable_protocols &= mf->usable_protocols_cidr; | |
1333 | } else { | |
1334 | *usable_protocols &= mf->usable_protocols_bitwise; | |
1335 | } | |
1336 | } else { | |
1337 | return xasprintf("%s: unknown field %s", s, name); | |
1338 | } | |
1339 | } | |
1340 | ||
1341 | return NULL; | |
1342 | } | |
1343 | ||
cab50449 | 1344 | static char * OVS_WARN_UNUSED_RESULT |
7395c052 NZ |
1345 | parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command, |
1346 | char *string, | |
1347 | enum ofputil_protocol *usable_protocols) | |
1348 | { | |
1349 | enum { | |
45952551 SH |
1350 | F_GROUP_TYPE = 1 << 0, |
1351 | F_BUCKETS = 1 << 1, | |
1352 | F_COMMAND_BUCKET_ID = 1 << 2, | |
1353 | F_COMMAND_BUCKET_ID_ALL = 1 << 3, | |
7395c052 | 1354 | } fields; |
7395c052 | 1355 | bool had_type = false; |
45952551 | 1356 | bool had_command_bucket_id = false; |
7395c052 NZ |
1357 | struct ofputil_bucket *bucket; |
1358 | char *error = NULL; | |
1359 | ||
1360 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
1361 | ||
1362 | switch (command) { | |
1363 | case OFPGC11_ADD: | |
1364 | fields = F_GROUP_TYPE | F_BUCKETS; | |
1365 | break; | |
1366 | ||
1367 | case OFPGC11_DELETE: | |
1368 | fields = 0; | |
1369 | break; | |
1370 | ||
1371 | case OFPGC11_MODIFY: | |
1372 | fields = F_GROUP_TYPE | F_BUCKETS; | |
1373 | break; | |
1374 | ||
45952551 SH |
1375 | case OFPGC15_INSERT_BUCKET: |
1376 | fields = F_BUCKETS | F_COMMAND_BUCKET_ID; | |
1377 | *usable_protocols &= OFPUTIL_P_OF15_UP; | |
1378 | break; | |
1379 | ||
1380 | case OFPGC15_REMOVE_BUCKET: | |
1381 | fields = F_COMMAND_BUCKET_ID | F_COMMAND_BUCKET_ID_ALL; | |
1382 | *usable_protocols &= OFPUTIL_P_OF15_UP; | |
1383 | break; | |
1384 | ||
7395c052 | 1385 | default: |
428b2edd | 1386 | OVS_NOT_REACHED(); |
7395c052 NZ |
1387 | } |
1388 | ||
1389 | memset(gm, 0, sizeof *gm); | |
1390 | gm->command = command; | |
1391 | gm->group_id = OFPG_ANY; | |
18ac06d3 | 1392 | gm->command_bucket_id = OFPG15_BUCKET_ALL; |
7395c052 NZ |
1393 | list_init(&gm->buckets); |
1394 | if (command == OFPGC11_DELETE && string[0] == '\0') { | |
1395 | gm->group_id = OFPG_ALL; | |
1396 | return NULL; | |
1397 | } | |
1398 | ||
1399 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
1400 | ||
64e8c446 BP |
1401 | /* Strip the buckets off the end of 'string', if there are any, saving a |
1402 | * pointer for later. We want to parse the buckets last because the bucket | |
1403 | * type influences bucket defaults. */ | |
1404 | char *bkt_str = strstr(string, "bucket="); | |
1405 | if (bkt_str) { | |
1406 | if (!(fields & F_BUCKETS)) { | |
1407 | error = xstrdup("bucket is not needed"); | |
1408 | goto out; | |
7395c052 | 1409 | } |
64e8c446 | 1410 | *bkt_str = '\0'; |
7395c052 NZ |
1411 | } |
1412 | ||
64e8c446 | 1413 | /* Parse everything before the buckets. */ |
68dfc25b BP |
1414 | char *pos = string; |
1415 | char *name, *value; | |
1416 | while (ofputil_parse_key_value(&pos, &name, &value)) { | |
45952551 SH |
1417 | if (!strcmp(name, "command_bucket_id")) { |
1418 | if (!(fields & F_COMMAND_BUCKET_ID)) { | |
1419 | error = xstrdup("command bucket id is not needed"); | |
1420 | goto out; | |
1421 | } | |
1422 | if (!strcmp(value, "all")) { | |
1423 | gm->command_bucket_id = OFPG15_BUCKET_ALL; | |
1424 | } else if (!strcmp(value, "first")) { | |
1425 | gm->command_bucket_id = OFPG15_BUCKET_FIRST; | |
1426 | } else if (!strcmp(value, "last")) { | |
1427 | gm->command_bucket_id = OFPG15_BUCKET_LAST; | |
1428 | } else { | |
908995c5 | 1429 | error = str_to_u32(value, &gm->command_bucket_id); |
45952551 SH |
1430 | if (error) { |
1431 | goto out; | |
1432 | } | |
1433 | if (gm->command_bucket_id > OFPG15_BUCKET_MAX | |
1434 | && (gm->command_bucket_id != OFPG15_BUCKET_FIRST | |
1435 | && gm->command_bucket_id != OFPG15_BUCKET_LAST | |
1436 | && gm->command_bucket_id != OFPG15_BUCKET_ALL)) { | |
1437 | error = xasprintf("invalid command bucket id %"PRIu32, | |
1438 | gm->command_bucket_id); | |
1439 | goto out; | |
1440 | } | |
1441 | } | |
1442 | if (gm->command_bucket_id == OFPG15_BUCKET_ALL | |
1443 | && !(fields & F_COMMAND_BUCKET_ID_ALL)) { | |
1444 | error = xstrdup("command_bucket_id=all is not permitted"); | |
1445 | goto out; | |
1446 | } | |
1447 | had_command_bucket_id = true; | |
1448 | } else if (!strcmp(name, "group_id")) { | |
7395c052 NZ |
1449 | if(!strcmp(value, "all")) { |
1450 | gm->group_id = OFPG_ALL; | |
1451 | } else { | |
aa6fb077 | 1452 | error = str_to_u32(value, &gm->group_id); |
7395c052 NZ |
1453 | if (error) { |
1454 | goto out; | |
1455 | } | |
1456 | if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) { | |
1457 | error = xasprintf("invalid group id %"PRIu32, | |
1458 | gm->group_id); | |
1459 | goto out; | |
1460 | } | |
1461 | } | |
1462 | } else if (!strcmp(name, "type")){ | |
1463 | if (!(fields & F_GROUP_TYPE)) { | |
1464 | error = xstrdup("type is not needed"); | |
1465 | goto out; | |
1466 | } | |
1467 | if (!strcmp(value, "all")) { | |
1468 | gm->type = OFPGT11_ALL; | |
1469 | } else if (!strcmp(value, "select")) { | |
1470 | gm->type = OFPGT11_SELECT; | |
1471 | } else if (!strcmp(value, "indirect")) { | |
1472 | gm->type = OFPGT11_INDIRECT; | |
1473 | } else if (!strcmp(value, "ff") || | |
1474 | !strcmp(value, "fast_failover")) { | |
1475 | gm->type = OFPGT11_FF; | |
1476 | } else { | |
1477 | error = xasprintf("invalid group type %s", value); | |
1478 | goto out; | |
1479 | } | |
1480 | had_type = true; | |
b879391e SH |
1481 | } else if (!strcmp(name, "selection_method")) { |
1482 | if (!(fields & F_GROUP_TYPE)) { | |
1483 | error = xstrdup("selection method is not needed"); | |
1484 | goto out; | |
1485 | } | |
1486 | if (strlen(value) >= NTR_MAX_SELECTION_METHOD_LEN) { | |
1487 | error = xasprintf("selection method is longer than %u" | |
1488 | " bytes long", | |
1489 | NTR_MAX_SELECTION_METHOD_LEN - 1); | |
1490 | goto out; | |
1491 | } | |
1492 | memset(gm->props.selection_method, '\0', | |
1493 | NTR_MAX_SELECTION_METHOD_LEN); | |
1494 | strcpy(gm->props.selection_method, value); | |
1495 | *usable_protocols &= OFPUTIL_P_OF15_UP; | |
1496 | } else if (!strcmp(name, "selection_method_param")) { | |
1497 | if (!(fields & F_GROUP_TYPE)) { | |
1498 | error = xstrdup("selection method param is not needed"); | |
1499 | goto out; | |
1500 | } | |
1501 | error = str_to_u64(value, &gm->props.selection_method_param); | |
596be0ad BP |
1502 | if (error) { |
1503 | goto out; | |
1504 | } | |
b879391e SH |
1505 | *usable_protocols &= OFPUTIL_P_OF15_UP; |
1506 | } else if (!strcmp(name, "fields")) { | |
1507 | if (!(fields & F_GROUP_TYPE)) { | |
1508 | error = xstrdup("fields are not needed"); | |
1509 | goto out; | |
1510 | } | |
1511 | error = parse_select_group_field(value, &gm->props.fields, | |
1512 | usable_protocols); | |
1513 | if (error) { | |
1514 | goto out; | |
1515 | } | |
1516 | *usable_protocols &= OFPUTIL_P_OF15_UP; | |
7395c052 NZ |
1517 | } else { |
1518 | error = xasprintf("unknown keyword %s", name); | |
1519 | goto out; | |
1520 | } | |
1521 | } | |
1522 | if (gm->group_id == OFPG_ANY) { | |
1523 | error = xstrdup("must specify a group_id"); | |
1524 | goto out; | |
1525 | } | |
1526 | if (fields & F_GROUP_TYPE && !had_type) { | |
1527 | error = xstrdup("must specify a type"); | |
1528 | goto out; | |
1529 | } | |
1530 | ||
45952551 SH |
1531 | if (fields & F_COMMAND_BUCKET_ID) { |
1532 | if (!(fields & F_COMMAND_BUCKET_ID_ALL || had_command_bucket_id)) { | |
1533 | error = xstrdup("must specify a command bucket id"); | |
1534 | goto out; | |
1535 | } | |
1536 | } else if (had_command_bucket_id) { | |
1537 | error = xstrdup("command bucket id is not needed"); | |
1538 | goto out; | |
1539 | } | |
1540 | ||
64e8c446 BP |
1541 | /* Now parse the buckets, if any. */ |
1542 | while (bkt_str) { | |
1543 | char *next_bkt_str; | |
1544 | ||
1545 | bkt_str = strchr(bkt_str + 1, '='); | |
1546 | if (!bkt_str) { | |
1547 | error = xstrdup("must specify bucket content"); | |
1548 | goto out; | |
1549 | } | |
1550 | bkt_str++; | |
1551 | ||
1552 | next_bkt_str = strstr(bkt_str, "bucket="); | |
1553 | if (next_bkt_str) { | |
1554 | *next_bkt_str = '\0'; | |
1555 | } | |
1556 | ||
1557 | bucket = xzalloc(sizeof(struct ofputil_bucket)); | |
1558 | error = parse_bucket_str(bucket, bkt_str, gm->type, usable_protocols); | |
1559 | if (error) { | |
1560 | free(bucket); | |
1561 | goto out; | |
1562 | } | |
1563 | list_push_back(&gm->buckets, &bucket->list_node); | |
1564 | ||
1565 | if (gm->type != OFPGT11_SELECT && bucket->weight) { | |
7395c052 NZ |
1566 | error = xstrdup("Only select groups can have bucket weights."); |
1567 | goto out; | |
1568 | } | |
64e8c446 BP |
1569 | |
1570 | bkt_str = next_bkt_str; | |
7395c052 NZ |
1571 | } |
1572 | if (gm->type == OFPGT11_INDIRECT && !list_is_short(&gm->buckets)) { | |
1573 | error = xstrdup("Indirect groups can have at most one bucket."); | |
1574 | goto out; | |
1575 | } | |
1576 | ||
1577 | return NULL; | |
1578 | out: | |
1579 | ofputil_bucket_list_destroy(&gm->buckets); | |
1580 | return error; | |
1581 | } | |
1582 | ||
cab50449 | 1583 | char * OVS_WARN_UNUSED_RESULT |
7395c052 NZ |
1584 | parse_ofp_group_mod_str(struct ofputil_group_mod *gm, uint16_t command, |
1585 | const char *str_, | |
1586 | enum ofputil_protocol *usable_protocols) | |
1587 | { | |
1588 | char *string = xstrdup(str_); | |
1589 | char *error = parse_ofp_group_mod_str__(gm, command, string, | |
1590 | usable_protocols); | |
1591 | free(string); | |
1592 | ||
1593 | if (error) { | |
1594 | ofputil_bucket_list_destroy(&gm->buckets); | |
1595 | } | |
1596 | return error; | |
1597 | } | |
1598 | ||
cab50449 | 1599 | char * OVS_WARN_UNUSED_RESULT |
7395c052 NZ |
1600 | parse_ofp_group_mod_file(const char *file_name, uint16_t command, |
1601 | struct ofputil_group_mod **gms, size_t *n_gms, | |
1602 | enum ofputil_protocol *usable_protocols) | |
1603 | { | |
1604 | size_t allocated_gms; | |
1605 | int line_number; | |
1606 | FILE *stream; | |
1607 | struct ds s; | |
1608 | ||
1609 | *gms = NULL; | |
1610 | *n_gms = 0; | |
1611 | ||
1612 | stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); | |
1613 | if (stream == NULL) { | |
1614 | return xasprintf("%s: open failed (%s)", | |
1615 | file_name, ovs_strerror(errno)); | |
1616 | } | |
1617 | ||
1618 | allocated_gms = *n_gms; | |
1619 | ds_init(&s); | |
1620 | line_number = 0; | |
1621 | *usable_protocols = OFPUTIL_P_OF11_UP; | |
1622 | while (!ds_get_preprocessed_line(&s, stream, &line_number)) { | |
1623 | enum ofputil_protocol usable; | |
1624 | char *error; | |
1625 | ||
1626 | if (*n_gms >= allocated_gms) { | |
c7ecbf1e | 1627 | struct ofputil_group_mod *new_gms; |
2134b5ec SH |
1628 | size_t i; |
1629 | ||
c7ecbf1e | 1630 | new_gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms); |
2134b5ec | 1631 | for (i = 0; i < *n_gms; i++) { |
c7ecbf1e | 1632 | list_moved(&new_gms[i].buckets, &(*gms)[i].buckets); |
2134b5ec | 1633 | } |
c7ecbf1e | 1634 | *gms = new_gms; |
7395c052 NZ |
1635 | } |
1636 | error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s), | |
1637 | &usable); | |
1638 | if (error) { | |
1639 | size_t i; | |
1640 | ||
1641 | for (i = 0; i < *n_gms; i++) { | |
1642 | ofputil_bucket_list_destroy(&(*gms)[i].buckets); | |
1643 | } | |
1644 | free(*gms); | |
1645 | *gms = NULL; | |
1646 | *n_gms = 0; | |
1647 | ||
1648 | ds_destroy(&s); | |
1649 | if (stream != stdin) { | |
1650 | fclose(stream); | |
1651 | } | |
1652 | ||
1653 | return xasprintf("%s:%d: %s", file_name, line_number, error); | |
1654 | } | |
1655 | *usable_protocols &= usable; | |
1656 | *n_gms += 1; | |
1657 | } | |
1658 | ||
1659 | ds_destroy(&s); | |
1660 | if (stream != stdin) { | |
1661 | fclose(stream); | |
1662 | } | |
1663 | return NULL; | |
1664 | } | |
6159c531 JG |
1665 | |
1666 | char * OVS_WARN_UNUSED_RESULT | |
1667 | parse_ofp_geneve_table_mod_str(struct ofputil_geneve_table_mod *gtm, | |
1668 | uint16_t command, const char *s, | |
1669 | enum ofputil_protocol *usable_protocols) | |
1670 | { | |
1671 | *usable_protocols = OFPUTIL_P_NXM_OXM_ANY; | |
1672 | ||
1673 | gtm->command = command; | |
1674 | list_init(>m->mappings); | |
1675 | ||
1676 | while (*s) { | |
1677 | struct ofputil_geneve_map *map = xmalloc(sizeof *map); | |
1678 | int n; | |
1679 | ||
1680 | if (*s == ',') { | |
1681 | s++; | |
1682 | } | |
1683 | ||
1684 | list_push_back(>m->mappings, &map->list_node); | |
1685 | ||
1686 | if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}->tun_metadata%"SCNi16"%n", | |
1687 | &map->option_class, &map->option_type, &map->option_len, | |
1688 | &map->index, &n)) { | |
1689 | ofputil_uninit_geneve_table(>m->mappings); | |
1690 | return xstrdup("invalid geneve mapping"); | |
1691 | } | |
1692 | ||
1693 | s += n; | |
1694 | } | |
1695 | ||
1696 | return NULL; | |
1697 | } |