]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
3476fce3 | 2 | * Copyright (c) 2009, 2010, 2011 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
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: | |
064af421 | 7 | * |
a14bc59f BP |
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. | |
064af421 BP |
15 | */ |
16 | ||
d31f1109 | 17 | #include <arpa/inet.h> |
064af421 BP |
18 | #include <config.h> |
19 | #include "odp-util.h" | |
36956a7d | 20 | #include <errno.h> |
064af421 | 21 | #include <inttypes.h> |
685a51a5 | 22 | #include <netinet/icmp6.h> |
064af421 BP |
23 | #include <stdlib.h> |
24 | #include <string.h> | |
cdee00fd | 25 | #include "byte-order.h" |
064af421 BP |
26 | #include "coverage.h" |
27 | #include "dynamic-string.h" | |
28 | #include "flow.h" | |
cdee00fd | 29 | #include "netlink.h" |
3bffc610 | 30 | #include "ofpbuf.h" |
0ae60917 | 31 | #include "openvswitch/tunnel.h" |
064af421 BP |
32 | #include "packets.h" |
33 | #include "timeval.h" | |
34 | #include "util.h" | |
35 | ||
df2c07f4 JP |
36 | /* The interface between userspace and kernel uses an "OVS_*" prefix. |
37 | * Since this is fairly non-specific for the OVS userspace components, | |
38 | * "ODP_*" (Open vSwitch Datapath) is used as the prefix for | |
39 | * interactions with the datapath. | |
40 | */ | |
41 | ||
cdee00fd BP |
42 | int |
43 | odp_action_len(uint16_t type) | |
44 | { | |
df2c07f4 | 45 | if (type > OVS_ACTION_ATTR_MAX) { |
cdee00fd BP |
46 | return -1; |
47 | } | |
48 | ||
df2c07f4 JP |
49 | switch ((enum ovs_action_type) type) { |
50 | case OVS_ACTION_ATTR_OUTPUT: return 4; | |
51 | case OVS_ACTION_ATTR_USERSPACE: return 8; | |
52 | case OVS_ACTION_ATTR_SET_DL_TCI: return 2; | |
53 | case OVS_ACTION_ATTR_STRIP_VLAN: return 0; | |
54 | case OVS_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN; | |
55 | case OVS_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN; | |
56 | case OVS_ACTION_ATTR_SET_NW_SRC: return 4; | |
57 | case OVS_ACTION_ATTR_SET_NW_DST: return 4; | |
58 | case OVS_ACTION_ATTR_SET_NW_TOS: return 1; | |
59 | case OVS_ACTION_ATTR_SET_TP_SRC: return 2; | |
60 | case OVS_ACTION_ATTR_SET_TP_DST: return 2; | |
61 | case OVS_ACTION_ATTR_SET_TUNNEL: return 8; | |
62 | case OVS_ACTION_ATTR_SET_PRIORITY: return 4; | |
63 | case OVS_ACTION_ATTR_POP_PRIORITY: return 0; | |
64 | ||
65 | case OVS_ACTION_ATTR_UNSPEC: | |
66 | case __OVS_ACTION_ATTR_MAX: | |
cdee00fd BP |
67 | return -1; |
68 | } | |
69 | ||
70 | return -1; | |
71 | } | |
72 | ||
73 | static void | |
74 | format_generic_odp_action(struct ds *ds, const struct nlattr *a) | |
75 | { | |
8ba43fbd BP |
76 | size_t len = nl_attr_get_size(a); |
77 | ||
cdee00fd | 78 | ds_put_format(ds, "action%"PRId16, nl_attr_type(a)); |
8ba43fbd | 79 | if (len) { |
cdee00fd BP |
80 | const uint8_t *unspec; |
81 | unsigned int i; | |
82 | ||
83 | unspec = nl_attr_get(a); | |
8ba43fbd | 84 | for (i = 0; i < len; i++) { |
cdee00fd BP |
85 | ds_put_char(ds, i ? ' ': '('); |
86 | ds_put_format(ds, "%02x", unspec[i]); | |
87 | } | |
88 | ds_put_char(ds, ')'); | |
89 | } | |
90 | } | |
91 | ||
064af421 | 92 | void |
cdee00fd | 93 | format_odp_action(struct ds *ds, const struct nlattr *a) |
064af421 | 94 | { |
cdee00fd BP |
95 | const uint8_t *eth; |
96 | ovs_be32 ip; | |
97 | ||
b7b0c620 | 98 | if (nl_attr_get_size(a) != odp_action_len(nl_attr_type(a))) { |
8ba43fbd | 99 | ds_put_format(ds, "bad length %zu, expected %d for: ", |
b7b0c620 | 100 | nl_attr_get_size(a), odp_action_len(nl_attr_type(a))); |
cdee00fd BP |
101 | format_generic_odp_action(ds, a); |
102 | return; | |
103 | } | |
104 | ||
105 | switch (nl_attr_type(a)) { | |
df2c07f4 | 106 | case OVS_ACTION_ATTR_OUTPUT: |
cdee00fd | 107 | ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a)); |
064af421 | 108 | break; |
df2c07f4 | 109 | case OVS_ACTION_ATTR_USERSPACE: |
b85d8d61 | 110 | ds_put_format(ds, "userspace(%"PRIu64")", nl_attr_get_u64(a)); |
064af421 | 111 | break; |
df2c07f4 | 112 | case OVS_ACTION_ATTR_SET_TUNNEL: |
b9298d3f BP |
113 | ds_put_format(ds, "set_tunnel(%#"PRIx64")", |
114 | ntohll(nl_attr_get_be64(a))); | |
659586ef | 115 | break; |
df2c07f4 | 116 | case OVS_ACTION_ATTR_SET_DL_TCI: |
27bcf966 | 117 | ds_put_format(ds, "set_tci(vid=%"PRIu16",pcp=%d)", |
cdee00fd BP |
118 | vlan_tci_to_vid(nl_attr_get_be16(a)), |
119 | vlan_tci_to_pcp(nl_attr_get_be16(a))); | |
064af421 | 120 | break; |
df2c07f4 | 121 | case OVS_ACTION_ATTR_STRIP_VLAN: |
064af421 BP |
122 | ds_put_format(ds, "strip_vlan"); |
123 | break; | |
df2c07f4 | 124 | case OVS_ACTION_ATTR_SET_DL_SRC: |
cdee00fd BP |
125 | eth = nl_attr_get_unspec(a, ETH_ADDR_LEN); |
126 | ds_put_format(ds, "set_dl_src("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth)); | |
064af421 | 127 | break; |
df2c07f4 | 128 | case OVS_ACTION_ATTR_SET_DL_DST: |
cdee00fd BP |
129 | eth = nl_attr_get_unspec(a, ETH_ADDR_LEN); |
130 | ds_put_format(ds, "set_dl_dst("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth)); | |
064af421 | 131 | break; |
df2c07f4 | 132 | case OVS_ACTION_ATTR_SET_NW_SRC: |
cdee00fd BP |
133 | ip = nl_attr_get_be32(a); |
134 | ds_put_format(ds, "set_nw_src("IP_FMT")", IP_ARGS(&ip)); | |
064af421 | 135 | break; |
df2c07f4 | 136 | case OVS_ACTION_ATTR_SET_NW_DST: |
cdee00fd BP |
137 | ip = nl_attr_get_be32(a); |
138 | ds_put_format(ds, "set_nw_dst("IP_FMT")", IP_ARGS(&ip)); | |
064af421 | 139 | break; |
df2c07f4 | 140 | case OVS_ACTION_ATTR_SET_NW_TOS: |
cdee00fd | 141 | ds_put_format(ds, "set_nw_tos(%"PRIu8")", nl_attr_get_u8(a)); |
959a2ecd | 142 | break; |
df2c07f4 | 143 | case OVS_ACTION_ATTR_SET_TP_SRC: |
cdee00fd | 144 | ds_put_format(ds, "set_tp_src(%"PRIu16")", ntohs(nl_attr_get_be16(a))); |
064af421 | 145 | break; |
df2c07f4 | 146 | case OVS_ACTION_ATTR_SET_TP_DST: |
cdee00fd | 147 | ds_put_format(ds, "set_tp_dst(%"PRIu16")", ntohs(nl_attr_get_be16(a))); |
064af421 | 148 | break; |
df2c07f4 | 149 | case OVS_ACTION_ATTR_SET_PRIORITY: |
cdee00fd | 150 | ds_put_format(ds, "set_priority(%#"PRIx32")", nl_attr_get_u32(a)); |
c4d279ab | 151 | break; |
df2c07f4 | 152 | case OVS_ACTION_ATTR_POP_PRIORITY: |
c4d279ab | 153 | ds_put_cstr(ds, "pop_priority"); |
a77d89b8 | 154 | break; |
064af421 | 155 | default: |
cdee00fd | 156 | format_generic_odp_action(ds, a); |
064af421 BP |
157 | break; |
158 | } | |
159 | } | |
160 | ||
161 | void | |
cdee00fd | 162 | format_odp_actions(struct ds *ds, const struct nlattr *actions, |
cf22f8cb | 163 | size_t actions_len) |
064af421 | 164 | { |
cdee00fd BP |
165 | if (actions_len) { |
166 | const struct nlattr *a; | |
167 | unsigned int left; | |
168 | ||
169 | NL_ATTR_FOR_EACH (a, left, actions, actions_len) { | |
170 | if (a != actions) { | |
171 | ds_put_char(ds, ','); | |
172 | } | |
173 | format_odp_action(ds, a); | |
064af421 | 174 | } |
cdee00fd | 175 | if (left) { |
3476fce3 BP |
176 | if (left == actions_len) { |
177 | ds_put_cstr(ds, "<empty>"); | |
178 | } | |
36956a7d | 179 | ds_put_format(ds, ",***%u leftover bytes***", left); |
cdee00fd BP |
180 | } |
181 | } else { | |
064af421 BP |
182 | ds_put_cstr(ds, "drop"); |
183 | } | |
184 | } | |
14608a15 | 185 | \f |
36956a7d BP |
186 | /* Returns the correct length of the payload for a flow key attribute of the |
187 | * specified 'type', or -1 if 'type' is unknown. */ | |
188 | static int | |
189 | odp_flow_key_attr_len(uint16_t type) | |
190 | { | |
df2c07f4 | 191 | if (type > OVS_KEY_ATTR_MAX) { |
36956a7d BP |
192 | return -1; |
193 | } | |
194 | ||
df2c07f4 JP |
195 | switch ((enum ovs_key_type) type) { |
196 | case OVS_KEY_ATTR_TUN_ID: return 8; | |
197 | case OVS_KEY_ATTR_IN_PORT: return 4; | |
198 | case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet); | |
199 | case OVS_KEY_ATTR_8021Q: return sizeof(struct ovs_key_8021q); | |
200 | case OVS_KEY_ATTR_ETHERTYPE: return 2; | |
201 | case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4); | |
202 | case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6); | |
203 | case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp); | |
204 | case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp); | |
205 | case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp); | |
206 | case OVS_KEY_ATTR_ICMPV6: return sizeof(struct ovs_key_icmpv6); | |
207 | case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp); | |
208 | case OVS_KEY_ATTR_ND: return sizeof(struct ovs_key_nd); | |
209 | ||
210 | case OVS_KEY_ATTR_UNSPEC: | |
211 | case __OVS_KEY_ATTR_MAX: | |
36956a7d BP |
212 | return -1; |
213 | } | |
214 | ||
215 | return -1; | |
216 | } | |
217 | ||
218 | ||
219 | static void | |
220 | format_generic_odp_key(const struct nlattr *a, struct ds *ds) | |
221 | { | |
222 | size_t len = nl_attr_get_size(a); | |
223 | ||
224 | ds_put_format(ds, "key%"PRId16, nl_attr_type(a)); | |
225 | if (len) { | |
226 | const uint8_t *unspec; | |
227 | unsigned int i; | |
228 | ||
229 | unspec = nl_attr_get(a); | |
230 | for (i = 0; i < len; i++) { | |
231 | ds_put_char(ds, i ? ' ': '('); | |
232 | ds_put_format(ds, "%02x", unspec[i]); | |
233 | } | |
234 | ds_put_char(ds, ')'); | |
235 | } | |
236 | } | |
237 | ||
238 | static void | |
239 | format_odp_key_attr(const struct nlattr *a, struct ds *ds) | |
240 | { | |
df2c07f4 JP |
241 | const struct ovs_key_ethernet *eth_key; |
242 | const struct ovs_key_8021q *q_key; | |
243 | const struct ovs_key_ipv4 *ipv4_key; | |
244 | const struct ovs_key_ipv6 *ipv6_key; | |
245 | const struct ovs_key_tcp *tcp_key; | |
246 | const struct ovs_key_udp *udp_key; | |
247 | const struct ovs_key_icmp *icmp_key; | |
248 | const struct ovs_key_icmpv6 *icmpv6_key; | |
249 | const struct ovs_key_arp *arp_key; | |
250 | const struct ovs_key_nd *nd_key; | |
36956a7d BP |
251 | |
252 | if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) { | |
253 | ds_put_format(ds, "bad length %zu, expected %d for: ", | |
254 | nl_attr_get_size(a), | |
255 | odp_flow_key_attr_len(nl_attr_type(a))); | |
256 | format_generic_odp_key(a, ds); | |
257 | return; | |
258 | } | |
259 | ||
260 | switch (nl_attr_type(a)) { | |
df2c07f4 | 261 | case OVS_KEY_ATTR_TUN_ID: |
602ef4b2 | 262 | ds_put_format(ds, "tun_id(%#"PRIx64")", ntohll(nl_attr_get_be64(a))); |
36956a7d BP |
263 | break; |
264 | ||
df2c07f4 | 265 | case OVS_KEY_ATTR_IN_PORT: |
36956a7d BP |
266 | ds_put_format(ds, "in_port(%"PRIu32")", nl_attr_get_u32(a)); |
267 | break; | |
268 | ||
df2c07f4 | 269 | case OVS_KEY_ATTR_ETHERNET: |
36956a7d BP |
270 | eth_key = nl_attr_get(a); |
271 | ds_put_format(ds, "eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")", | |
272 | ETH_ADDR_ARGS(eth_key->eth_src), | |
273 | ETH_ADDR_ARGS(eth_key->eth_dst)); | |
274 | break; | |
275 | ||
df2c07f4 | 276 | case OVS_KEY_ATTR_8021Q: |
36956a7d BP |
277 | q_key = nl_attr_get(a); |
278 | ds_put_cstr(ds, "vlan("); | |
279 | if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { | |
074d69f5 | 280 | ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(q_key->q_tpid)); |
36956a7d | 281 | } |
6adf1730 | 282 | ds_put_format(ds, "vid=%"PRIu16",pcp=%d)", |
36956a7d BP |
283 | vlan_tci_to_vid(q_key->q_tci), |
284 | vlan_tci_to_pcp(q_key->q_tci)); | |
285 | break; | |
286 | ||
df2c07f4 | 287 | case OVS_KEY_ATTR_ETHERTYPE: |
074d69f5 | 288 | ds_put_format(ds, "eth_type(0x%04"PRIx16")", |
36956a7d BP |
289 | ntohs(nl_attr_get_be16(a))); |
290 | break; | |
291 | ||
df2c07f4 | 292 | case OVS_KEY_ATTR_IPV4: |
36956a7d BP |
293 | ipv4_key = nl_attr_get(a); |
294 | ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT"," | |
295 | "proto=%"PRId8",tos=%"PRIu8")", | |
296 | IP_ARGS(&ipv4_key->ipv4_src), | |
297 | IP_ARGS(&ipv4_key->ipv4_dst), | |
298 | ipv4_key->ipv4_proto, ipv4_key->ipv4_tos); | |
299 | break; | |
300 | ||
df2c07f4 | 301 | case OVS_KEY_ATTR_IPV6: { |
d31f1109 JP |
302 | char src_str[INET6_ADDRSTRLEN]; |
303 | char dst_str[INET6_ADDRSTRLEN]; | |
304 | ||
305 | ipv6_key = nl_attr_get(a); | |
306 | inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str); | |
307 | inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str); | |
308 | ||
309 | ds_put_format(ds, "ipv6(src=%s,dst=%s,proto=%"PRId8",tos=%"PRIu8")", | |
310 | src_str, dst_str, ipv6_key->ipv6_proto, | |
311 | ipv6_key->ipv6_tos); | |
312 | break; | |
313 | } | |
314 | ||
df2c07f4 | 315 | case OVS_KEY_ATTR_TCP: |
36956a7d BP |
316 | tcp_key = nl_attr_get(a); |
317 | ds_put_format(ds, "tcp(src=%"PRIu16",dst=%"PRIu16")", | |
318 | ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst)); | |
319 | break; | |
320 | ||
df2c07f4 | 321 | case OVS_KEY_ATTR_UDP: |
36956a7d BP |
322 | udp_key = nl_attr_get(a); |
323 | ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16")", | |
324 | ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst)); | |
325 | break; | |
326 | ||
df2c07f4 | 327 | case OVS_KEY_ATTR_ICMP: |
36956a7d BP |
328 | icmp_key = nl_attr_get(a); |
329 | ds_put_format(ds, "icmp(type=%"PRIu8",code=%"PRIu8")", | |
330 | icmp_key->icmp_type, icmp_key->icmp_code); | |
331 | break; | |
332 | ||
df2c07f4 | 333 | case OVS_KEY_ATTR_ICMPV6: |
d31f1109 JP |
334 | icmpv6_key = nl_attr_get(a); |
335 | ds_put_format(ds, "icmpv6(type=%"PRIu8",code=%"PRIu8")", | |
336 | icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code); | |
337 | break; | |
338 | ||
df2c07f4 | 339 | case OVS_KEY_ATTR_ARP: |
36956a7d | 340 | arp_key = nl_attr_get(a); |
bad68a99 JP |
341 | ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16"," |
342 | "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT")", | |
36956a7d | 343 | IP_ARGS(&arp_key->arp_sip), IP_ARGS(&arp_key->arp_tip), |
bad68a99 JP |
344 | ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha), |
345 | ETH_ADDR_ARGS(arp_key->arp_tha)); | |
36956a7d BP |
346 | break; |
347 | ||
df2c07f4 | 348 | case OVS_KEY_ATTR_ND: { |
685a51a5 JP |
349 | char target[INET6_ADDRSTRLEN]; |
350 | ||
351 | nd_key = nl_attr_get(a); | |
352 | inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target); | |
353 | ||
354 | ds_put_format(ds, "nd(target=%s", target); | |
355 | if (!eth_addr_is_zero(nd_key->nd_sll)) { | |
356 | ds_put_format(ds, ",sll="ETH_ADDR_FMT, | |
357 | ETH_ADDR_ARGS(nd_key->nd_sll)); | |
358 | } | |
359 | if (!eth_addr_is_zero(nd_key->nd_tll)) { | |
360 | ds_put_format(ds, ",tll="ETH_ADDR_FMT, | |
361 | ETH_ADDR_ARGS(nd_key->nd_tll)); | |
362 | } | |
363 | ds_put_char(ds, ')'); | |
364 | break; | |
365 | } | |
366 | ||
36956a7d BP |
367 | default: |
368 | format_generic_odp_key(a, ds); | |
369 | break; | |
370 | } | |
371 | } | |
372 | ||
373 | /* Appends to 'ds' a string representation of the 'key_len' bytes of | |
df2c07f4 | 374 | * OVS_KEY_ATTR_* attributes in 'key'. */ |
14608a15 | 375 | void |
36956a7d | 376 | odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds) |
14608a15 | 377 | { |
36956a7d BP |
378 | if (key_len) { |
379 | const struct nlattr *a; | |
380 | unsigned int left; | |
381 | ||
382 | NL_ATTR_FOR_EACH (a, left, key, key_len) { | |
383 | if (a != key) { | |
384 | ds_put_char(ds, ','); | |
385 | } | |
386 | format_odp_key_attr(a, ds); | |
387 | } | |
388 | if (left) { | |
389 | if (left == key_len) { | |
390 | ds_put_cstr(ds, "<empty>"); | |
391 | } | |
392 | ds_put_format(ds, ",***%u leftover bytes***", left); | |
393 | } | |
394 | } else { | |
395 | ds_put_cstr(ds, "<empty>"); | |
396 | } | |
14608a15 | 397 | } |
064af421 | 398 | |
3bffc610 BP |
399 | static int |
400 | put_nd_key(int n, const char *nd_target_s, | |
401 | const uint8_t *nd_sll, const uint8_t *nd_tll, struct ofpbuf *key) | |
402 | { | |
df2c07f4 | 403 | struct ovs_key_nd nd_key; |
3bffc610 BP |
404 | |
405 | memset(&nd_key, 0, sizeof nd_key); | |
406 | if (inet_pton(AF_INET6, nd_target_s, nd_key.nd_target) != 1) { | |
407 | return -EINVAL; | |
408 | } | |
409 | if (nd_sll) { | |
410 | memcpy(nd_key.nd_sll, nd_sll, ETH_ADDR_LEN); | |
411 | } | |
412 | if (nd_tll) { | |
413 | memcpy(nd_key.nd_tll, nd_tll, ETH_ADDR_LEN); | |
414 | } | |
df2c07f4 | 415 | nl_msg_put_unspec(key, OVS_KEY_ATTR_ND, &nd_key, sizeof nd_key); |
3bffc610 BP |
416 | return n; |
417 | } | |
418 | ||
419 | static int | |
420 | parse_odp_key_attr(const char *s, struct ofpbuf *key) | |
421 | { | |
422 | /* Many of the sscanf calls in this function use oversized destination | |
423 | * fields because some sscanf() implementations truncate the range of %i | |
424 | * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a | |
425 | * value of 0x7fff. The other alternatives are to allow only a single | |
426 | * radix (e.g. decimal or hexadecimal) or to write more sophisticated | |
427 | * parsers. | |
428 | * | |
429 | * The tun_id parser has to use an alternative approach because there is no | |
430 | * type larger than 64 bits. */ | |
431 | ||
432 | { | |
433 | char tun_id_s[32]; | |
434 | int n = -1; | |
435 | ||
436 | if (sscanf(s, "tun_id(%31[x0123456789abcdefABCDEF])%n", | |
437 | tun_id_s, &n) > 0 && n > 0) { | |
438 | uint64_t tun_id = strtoull(tun_id_s, NULL, 0); | |
df2c07f4 | 439 | nl_msg_put_be64(key, OVS_KEY_ATTR_TUN_ID, htonll(tun_id)); |
3bffc610 BP |
440 | return n; |
441 | } | |
442 | } | |
443 | ||
444 | { | |
445 | unsigned long long int in_port; | |
446 | int n = -1; | |
447 | ||
448 | if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) { | |
df2c07f4 | 449 | nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port); |
3bffc610 BP |
450 | return n; |
451 | } | |
452 | } | |
453 | ||
454 | { | |
df2c07f4 | 455 | struct ovs_key_ethernet eth_key; |
3bffc610 BP |
456 | int n = -1; |
457 | ||
458 | if (sscanf(s, | |
459 | "eth(src="ETH_ADDR_SCAN_FMT",dst="ETH_ADDR_SCAN_FMT")%n", | |
460 | ETH_ADDR_SCAN_ARGS(eth_key.eth_src), | |
461 | ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n) > 0 && n > 0) { | |
df2c07f4 | 462 | nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET, |
3bffc610 BP |
463 | ð_key, sizeof eth_key); |
464 | return n; | |
465 | } | |
466 | } | |
467 | ||
468 | { | |
469 | uint16_t tpid = ETH_TYPE_VLAN; | |
470 | uint16_t vid; | |
471 | int pcp; | |
472 | int n = -1; | |
473 | ||
474 | if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", | |
475 | &vid, &pcp, &n) > 0 && n > 0) || | |
476 | (sscanf(s, "vlan(tpid=%"SCNi16",vid=%"SCNi16",pcp=%i)%n", | |
477 | &tpid, &vid, &pcp, &n) > 0 && n > 0)) { | |
df2c07f4 | 478 | struct ovs_key_8021q q_key; |
3bffc610 BP |
479 | |
480 | q_key.q_tpid = htons(tpid); | |
481 | q_key.q_tci = htons((vid << VLAN_VID_SHIFT) | | |
482 | (pcp << VLAN_PCP_SHIFT)); | |
df2c07f4 | 483 | nl_msg_put_unspec(key, OVS_KEY_ATTR_8021Q, &q_key, sizeof q_key); |
3bffc610 BP |
484 | return n; |
485 | } | |
486 | } | |
487 | ||
488 | { | |
fac2e516 | 489 | int eth_type; |
3bffc610 BP |
490 | int n = -1; |
491 | ||
fac2e516 | 492 | if (sscanf(s, "eth_type(%i)%n", ð_type, &n) > 0 && n > 0) { |
df2c07f4 | 493 | nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type)); |
3bffc610 BP |
494 | return n; |
495 | } | |
496 | } | |
497 | ||
498 | { | |
499 | ovs_be32 ipv4_src; | |
500 | ovs_be32 ipv4_dst; | |
501 | int ipv4_proto; | |
502 | int ipv4_tos; | |
503 | int n = -1; | |
504 | ||
505 | if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT"," | |
506 | "proto=%i,tos=%i)%n", | |
507 | IP_SCAN_ARGS(&ipv4_src), | |
508 | IP_SCAN_ARGS(&ipv4_dst), &ipv4_proto, &ipv4_tos, &n) > 0 | |
509 | && n > 0) { | |
df2c07f4 | 510 | struct ovs_key_ipv4 ipv4_key; |
3bffc610 BP |
511 | |
512 | memset(&ipv4_key, 0, sizeof ipv4_key); | |
513 | ipv4_key.ipv4_src = ipv4_src; | |
514 | ipv4_key.ipv4_dst = ipv4_dst; | |
515 | ipv4_key.ipv4_proto = ipv4_proto; | |
516 | ipv4_key.ipv4_tos = ipv4_tos; | |
df2c07f4 | 517 | nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4, |
3bffc610 BP |
518 | &ipv4_key, sizeof ipv4_key); |
519 | return n; | |
520 | } | |
521 | } | |
522 | ||
523 | { | |
524 | char ipv6_src_s[IPV6_SCAN_LEN + 1]; | |
525 | char ipv6_dst_s[IPV6_SCAN_LEN + 1]; | |
526 | int ipv6_proto; | |
527 | int ipv6_tos; | |
528 | int n = -1; | |
529 | ||
530 | if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT"," | |
531 | "proto=%i,tos=%i)%n", | |
532 | ipv6_src_s, ipv6_dst_s, | |
533 | &ipv6_proto, &ipv6_tos, &n) > 0 && n > 0) { | |
df2c07f4 | 534 | struct ovs_key_ipv6 ipv6_key; |
3bffc610 BP |
535 | |
536 | memset(&ipv6_key, 0, sizeof ipv6_key); | |
537 | if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 || | |
538 | inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1) { | |
539 | return -EINVAL; | |
540 | } | |
541 | ipv6_key.ipv6_proto = ipv6_proto; | |
542 | ipv6_key.ipv6_tos = ipv6_tos; | |
df2c07f4 | 543 | nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6, |
3bffc610 BP |
544 | &ipv6_key, sizeof ipv6_key); |
545 | return n; | |
546 | } | |
547 | } | |
548 | ||
549 | { | |
550 | int tcp_src; | |
551 | int tcp_dst; | |
552 | int n = -1; | |
553 | ||
554 | if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0 | |
555 | && n > 0) { | |
df2c07f4 | 556 | struct ovs_key_tcp tcp_key; |
3bffc610 BP |
557 | |
558 | tcp_key.tcp_src = htons(tcp_src); | |
559 | tcp_key.tcp_dst = htons(tcp_dst); | |
df2c07f4 | 560 | nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key); |
3bffc610 BP |
561 | return n; |
562 | } | |
563 | } | |
564 | ||
565 | { | |
566 | int udp_src; | |
567 | int udp_dst; | |
568 | int n = -1; | |
569 | ||
570 | if (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0 | |
571 | && n > 0) { | |
df2c07f4 | 572 | struct ovs_key_udp udp_key; |
3bffc610 BP |
573 | |
574 | udp_key.udp_src = htons(udp_src); | |
575 | udp_key.udp_dst = htons(udp_dst); | |
df2c07f4 | 576 | nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key); |
3bffc610 BP |
577 | return n; |
578 | } | |
579 | } | |
580 | ||
581 | { | |
582 | int icmp_type; | |
583 | int icmp_code; | |
584 | int n = -1; | |
585 | ||
586 | if (sscanf(s, "icmp(type=%i,code=%i)%n", | |
587 | &icmp_type, &icmp_code, &n) > 0 | |
588 | && n > 0) { | |
df2c07f4 | 589 | struct ovs_key_icmp icmp_key; |
3bffc610 BP |
590 | |
591 | icmp_key.icmp_type = icmp_type; | |
592 | icmp_key.icmp_code = icmp_code; | |
df2c07f4 | 593 | nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP, |
3bffc610 BP |
594 | &icmp_key, sizeof icmp_key); |
595 | return n; | |
596 | } | |
597 | } | |
598 | ||
599 | { | |
df2c07f4 | 600 | struct ovs_key_icmpv6 icmpv6_key; |
3bffc610 BP |
601 | int n = -1; |
602 | ||
603 | if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n", | |
604 | &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,&n) > 0 | |
605 | && n > 0) { | |
df2c07f4 | 606 | nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6, |
3bffc610 BP |
607 | &icmpv6_key, sizeof icmpv6_key); |
608 | return n; | |
609 | } | |
610 | } | |
611 | ||
612 | { | |
613 | ovs_be32 arp_sip; | |
614 | ovs_be32 arp_tip; | |
615 | int arp_op; | |
616 | uint8_t arp_sha[ETH_ADDR_LEN]; | |
617 | uint8_t arp_tha[ETH_ADDR_LEN]; | |
618 | int n = -1; | |
619 | ||
620 | if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT"," | |
621 | "op=%i,sha="ETH_ADDR_SCAN_FMT",tha="ETH_ADDR_SCAN_FMT")%n", | |
622 | IP_SCAN_ARGS(&arp_sip), | |
623 | IP_SCAN_ARGS(&arp_tip), | |
624 | &arp_op, | |
625 | ETH_ADDR_SCAN_ARGS(arp_sha), | |
626 | ETH_ADDR_SCAN_ARGS(arp_tha), &n) > 0 && n > 0) { | |
df2c07f4 | 627 | struct ovs_key_arp arp_key; |
3bffc610 BP |
628 | |
629 | memset(&arp_key, 0, sizeof arp_key); | |
630 | arp_key.arp_sip = arp_sip; | |
631 | arp_key.arp_tip = arp_tip; | |
632 | arp_key.arp_op = htons(arp_op); | |
633 | memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN); | |
634 | memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN); | |
df2c07f4 | 635 | nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key); |
3bffc610 BP |
636 | return n; |
637 | } | |
638 | } | |
639 | ||
640 | { | |
641 | char nd_target_s[IPV6_SCAN_LEN + 1]; | |
642 | uint8_t nd_sll[ETH_ADDR_LEN]; | |
643 | uint8_t nd_tll[ETH_ADDR_LEN]; | |
644 | int n = -1; | |
645 | ||
646 | if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n", | |
647 | nd_target_s, &n) > 0 && n > 0) { | |
648 | return put_nd_key(n, nd_target_s, NULL, NULL, key); | |
649 | } | |
650 | if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n", | |
651 | nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n) > 0 | |
652 | && n > 0) { | |
653 | return put_nd_key(n, nd_target_s, nd_sll, NULL, key); | |
654 | } | |
655 | if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n", | |
656 | nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0 | |
657 | && n > 0) { | |
658 | return put_nd_key(n, nd_target_s, NULL, nd_tll, key); | |
659 | } | |
660 | if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT"," | |
661 | "tll="ETH_ADDR_SCAN_FMT")%n", | |
662 | nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), | |
663 | ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0 | |
664 | && n > 0) { | |
665 | return put_nd_key(n, nd_target_s, nd_sll, nd_tll, key); | |
666 | } | |
667 | } | |
668 | ||
669 | return -EINVAL; | |
670 | } | |
671 | ||
df2c07f4 JP |
672 | /* Parses the string representation of a datapath flow key, in the |
673 | * format output by odp_flow_key_format(). Returns 0 if successful, | |
674 | * otherwise a positive errno value. On success, the flow key is | |
675 | * appended to 'key' as a series of Netlink attributes. On failure, no | |
676 | * data is appended to 'key'. Either way, 'key''s data might be | |
677 | * reallocated. | |
3bffc610 BP |
678 | * |
679 | * On success, the attributes appended to 'key' are individually syntactically | |
680 | * valid, but they may not be valid as a sequence. 'key' might, for example, | |
681 | * be missing an "in_port" key, have duplicated keys, or have keys in the wrong | |
682 | * order. odp_flow_key_to_flow() will detect those errors. */ | |
683 | int | |
684 | odp_flow_key_from_string(const char *s, struct ofpbuf *key) | |
685 | { | |
686 | const size_t old_size = key->size; | |
687 | for (;;) { | |
688 | int retval; | |
689 | ||
690 | s += strspn(s, ", \t\r\n"); | |
691 | if (!*s) { | |
692 | return 0; | |
693 | } | |
694 | ||
695 | retval = parse_odp_key_attr(s, key); | |
696 | if (retval < 0) { | |
697 | key->size = old_size; | |
698 | return -retval; | |
699 | } | |
700 | s += retval; | |
701 | } | |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
df2c07f4 | 706 | /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'. */ |
14608a15 | 707 | void |
36956a7d | 708 | odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) |
14608a15 | 709 | { |
df2c07f4 | 710 | struct ovs_key_ethernet *eth_key; |
36956a7d BP |
711 | |
712 | if (flow->tun_id != htonll(0)) { | |
df2c07f4 | 713 | nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tun_id); |
36956a7d BP |
714 | } |
715 | ||
df2c07f4 | 716 | nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT, |
abe529af | 717 | ofp_port_to_odp_port(flow->in_port)); |
36956a7d | 718 | |
df2c07f4 | 719 | eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET, |
36956a7d BP |
720 | sizeof *eth_key); |
721 | memcpy(eth_key->eth_src, flow->dl_src, ETH_ADDR_LEN); | |
722 | memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN); | |
723 | ||
724 | if (flow->vlan_tci != htons(0)) { | |
df2c07f4 | 725 | struct ovs_key_8021q *q_key; |
36956a7d | 726 | |
df2c07f4 | 727 | q_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_8021Q, |
36956a7d BP |
728 | sizeof *q_key); |
729 | q_key->q_tpid = htons(ETH_TYPE_VLAN); | |
730 | q_key->q_tci = flow->vlan_tci & ~htons(VLAN_CFI); | |
731 | } | |
732 | ||
733 | if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { | |
734 | return; | |
735 | } | |
736 | ||
df2c07f4 | 737 | nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type); |
36956a7d BP |
738 | |
739 | if (flow->dl_type == htons(ETH_TYPE_IP)) { | |
df2c07f4 | 740 | struct ovs_key_ipv4 *ipv4_key; |
36956a7d | 741 | |
df2c07f4 | 742 | ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4, |
36956a7d | 743 | sizeof *ipv4_key); |
535d6987 | 744 | memset(ipv4_key, 0, sizeof *ipv4_key); |
36956a7d BP |
745 | ipv4_key->ipv4_src = flow->nw_src; |
746 | ipv4_key->ipv4_dst = flow->nw_dst; | |
747 | ipv4_key->ipv4_proto = flow->nw_proto; | |
748 | ipv4_key->ipv4_tos = flow->nw_tos; | |
d31f1109 | 749 | } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { |
df2c07f4 | 750 | struct ovs_key_ipv6 *ipv6_key; |
d31f1109 | 751 | |
df2c07f4 | 752 | ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6, |
d31f1109 | 753 | sizeof *ipv6_key); |
535d6987 | 754 | memset(ipv6_key, 0, sizeof *ipv6_key); |
d31f1109 JP |
755 | memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src); |
756 | memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst); | |
757 | ipv6_key->ipv6_proto = flow->nw_proto; | |
758 | ipv6_key->ipv6_tos = flow->nw_tos; | |
759 | } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { | |
df2c07f4 | 760 | struct ovs_key_arp *arp_key; |
d31f1109 | 761 | |
df2c07f4 | 762 | arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP, |
d31f1109 | 763 | sizeof *arp_key); |
535d6987 | 764 | memset(arp_key, 0, sizeof *arp_key); |
d31f1109 JP |
765 | arp_key->arp_sip = flow->nw_src; |
766 | arp_key->arp_tip = flow->nw_dst; | |
767 | arp_key->arp_op = htons(flow->nw_proto); | |
768 | memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN); | |
769 | memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN); | |
770 | } | |
b53055f4 | 771 | |
d31f1109 JP |
772 | if (flow->dl_type == htons(ETH_TYPE_IP) |
773 | || flow->dl_type == htons(ETH_TYPE_IPV6)) { | |
36956a7d | 774 | |
6767a2cc | 775 | if (flow->nw_proto == IPPROTO_TCP) { |
df2c07f4 | 776 | struct ovs_key_tcp *tcp_key; |
36956a7d | 777 | |
df2c07f4 | 778 | tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP, |
36956a7d BP |
779 | sizeof *tcp_key); |
780 | tcp_key->tcp_src = flow->tp_src; | |
781 | tcp_key->tcp_dst = flow->tp_dst; | |
6767a2cc | 782 | } else if (flow->nw_proto == IPPROTO_UDP) { |
df2c07f4 | 783 | struct ovs_key_udp *udp_key; |
36956a7d | 784 | |
df2c07f4 | 785 | udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP, |
36956a7d BP |
786 | sizeof *udp_key); |
787 | udp_key->udp_src = flow->tp_src; | |
788 | udp_key->udp_dst = flow->tp_dst; | |
d31f1109 JP |
789 | } else if (flow->dl_type == htons(ETH_TYPE_IP) |
790 | && flow->nw_proto == IPPROTO_ICMP) { | |
df2c07f4 | 791 | struct ovs_key_icmp *icmp_key; |
36956a7d | 792 | |
df2c07f4 | 793 | icmp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMP, |
36956a7d BP |
794 | sizeof *icmp_key); |
795 | icmp_key->icmp_type = ntohs(flow->tp_src); | |
796 | icmp_key->icmp_code = ntohs(flow->tp_dst); | |
d31f1109 JP |
797 | } else if (flow->dl_type == htons(ETH_TYPE_IPV6) |
798 | && flow->nw_proto == IPPROTO_ICMPV6) { | |
df2c07f4 | 799 | struct ovs_key_icmpv6 *icmpv6_key; |
d31f1109 | 800 | |
df2c07f4 | 801 | icmpv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMPV6, |
d31f1109 JP |
802 | sizeof *icmpv6_key); |
803 | icmpv6_key->icmpv6_type = ntohs(flow->tp_src); | |
804 | icmpv6_key->icmpv6_code = ntohs(flow->tp_dst); | |
685a51a5 JP |
805 | |
806 | if (icmpv6_key->icmpv6_type == ND_NEIGHBOR_SOLICIT | |
807 | || icmpv6_key->icmpv6_type == ND_NEIGHBOR_ADVERT) { | |
df2c07f4 | 808 | struct ovs_key_nd *nd_key; |
685a51a5 | 809 | |
df2c07f4 | 810 | nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND, |
685a51a5 JP |
811 | sizeof *nd_key); |
812 | memcpy(nd_key->nd_target, &flow->nd_target, | |
813 | sizeof nd_key->nd_target); | |
814 | memcpy(nd_key->nd_sll, flow->arp_sha, ETH_ADDR_LEN); | |
815 | memcpy(nd_key->nd_tll, flow->arp_tha, ETH_ADDR_LEN); | |
816 | } | |
36956a7d | 817 | } |
36956a7d BP |
818 | } |
819 | } | |
820 | ||
df2c07f4 | 821 | /* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow |
36956a7d BP |
822 | * structure in 'flow'. Returns 0 if successful, otherwise EINVAL. */ |
823 | int | |
824 | odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, | |
825 | struct flow *flow) | |
826 | { | |
827 | const struct nlattr *nla; | |
df2c07f4 | 828 | enum ovs_key_type prev_type; |
36956a7d BP |
829 | size_t left; |
830 | ||
831 | memset(flow, 0, sizeof *flow); | |
832 | flow->dl_type = htons(FLOW_DL_TYPE_NONE); | |
833 | ||
df2c07f4 | 834 | prev_type = OVS_KEY_ATTR_UNSPEC; |
36956a7d | 835 | NL_ATTR_FOR_EACH (nla, left, key, key_len) { |
df2c07f4 JP |
836 | const struct ovs_key_ethernet *eth_key; |
837 | const struct ovs_key_8021q *q_key; | |
838 | const struct ovs_key_ipv4 *ipv4_key; | |
839 | const struct ovs_key_ipv6 *ipv6_key; | |
840 | const struct ovs_key_tcp *tcp_key; | |
841 | const struct ovs_key_udp *udp_key; | |
842 | const struct ovs_key_icmp *icmp_key; | |
843 | const struct ovs_key_icmpv6 *icmpv6_key; | |
844 | const struct ovs_key_arp *arp_key; | |
845 | const struct ovs_key_nd *nd_key; | |
36956a7d BP |
846 | |
847 | uint16_t type = nl_attr_type(nla); | |
848 | int len = odp_flow_key_attr_len(type); | |
849 | ||
850 | if (nl_attr_get_size(nla) != len && len != -1) { | |
851 | return EINVAL; | |
852 | } | |
853 | ||
854 | #define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE)) | |
855 | switch (TRANSITION(prev_type, type)) { | |
df2c07f4 | 856 | case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_TUN_ID): |
36956a7d BP |
857 | flow->tun_id = nl_attr_get_be64(nla); |
858 | break; | |
859 | ||
df2c07f4 JP |
860 | case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_IN_PORT): |
861 | case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_IN_PORT): | |
36956a7d BP |
862 | if (nl_attr_get_u32(nla) >= UINT16_MAX) { |
863 | return EINVAL; | |
864 | } | |
abe529af | 865 | flow->in_port = odp_port_to_ofp_port(nl_attr_get_u32(nla)); |
36956a7d BP |
866 | break; |
867 | ||
df2c07f4 | 868 | case TRANSITION(OVS_KEY_ATTR_IN_PORT, OVS_KEY_ATTR_ETHERNET): |
36956a7d BP |
869 | eth_key = nl_attr_get(nla); |
870 | memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN); | |
871 | memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN); | |
872 | break; | |
873 | ||
df2c07f4 | 874 | case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_8021Q): |
36956a7d BP |
875 | q_key = nl_attr_get(nla); |
876 | if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { | |
877 | /* Only standard 0x8100 VLANs currently supported. */ | |
878 | return EINVAL; | |
879 | } | |
880 | if (q_key->q_tci & htons(VLAN_CFI)) { | |
881 | return EINVAL; | |
882 | } | |
883 | flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI); | |
884 | break; | |
885 | ||
df2c07f4 JP |
886 | case TRANSITION(OVS_KEY_ATTR_8021Q, OVS_KEY_ATTR_ETHERTYPE): |
887 | case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_ETHERTYPE): | |
36956a7d BP |
888 | flow->dl_type = nl_attr_get_be16(nla); |
889 | if (ntohs(flow->dl_type) < 1536) { | |
890 | return EINVAL; | |
891 | } | |
892 | break; | |
893 | ||
df2c07f4 | 894 | case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV4): |
36956a7d BP |
895 | if (flow->dl_type != htons(ETH_TYPE_IP)) { |
896 | return EINVAL; | |
897 | } | |
898 | ipv4_key = nl_attr_get(nla); | |
899 | flow->nw_src = ipv4_key->ipv4_src; | |
900 | flow->nw_dst = ipv4_key->ipv4_dst; | |
901 | flow->nw_proto = ipv4_key->ipv4_proto; | |
902 | flow->nw_tos = ipv4_key->ipv4_tos; | |
903 | if (flow->nw_tos & IP_ECN_MASK) { | |
904 | return EINVAL; | |
905 | } | |
906 | break; | |
907 | ||
df2c07f4 | 908 | case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV6): |
d31f1109 JP |
909 | if (flow->dl_type != htons(ETH_TYPE_IPV6)) { |
910 | return EINVAL; | |
911 | } | |
912 | ipv6_key = nl_attr_get(nla); | |
913 | memcpy(&flow->ipv6_src, ipv6_key->ipv6_src, sizeof flow->ipv6_src); | |
914 | memcpy(&flow->ipv6_dst, ipv6_key->ipv6_dst, sizeof flow->ipv6_dst); | |
915 | flow->nw_proto = ipv6_key->ipv6_proto; | |
916 | flow->nw_tos = ipv6_key->ipv6_tos; | |
917 | if (flow->nw_tos & IP_ECN_MASK) { | |
918 | return EINVAL; | |
919 | } | |
920 | break; | |
921 | ||
df2c07f4 JP |
922 | case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_TCP): |
923 | case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_TCP): | |
6767a2cc | 924 | if (flow->nw_proto != IPPROTO_TCP) { |
36956a7d BP |
925 | return EINVAL; |
926 | } | |
927 | tcp_key = nl_attr_get(nla); | |
928 | flow->tp_src = tcp_key->tcp_src; | |
929 | flow->tp_dst = tcp_key->tcp_dst; | |
930 | break; | |
931 | ||
df2c07f4 JP |
932 | case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_UDP): |
933 | case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_UDP): | |
6767a2cc | 934 | if (flow->nw_proto != IPPROTO_UDP) { |
36956a7d BP |
935 | return EINVAL; |
936 | } | |
937 | udp_key = nl_attr_get(nla); | |
938 | flow->tp_src = udp_key->udp_src; | |
939 | flow->tp_dst = udp_key->udp_dst; | |
940 | break; | |
941 | ||
df2c07f4 | 942 | case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_ICMP): |
6767a2cc | 943 | if (flow->nw_proto != IPPROTO_ICMP) { |
36956a7d BP |
944 | return EINVAL; |
945 | } | |
946 | icmp_key = nl_attr_get(nla); | |
947 | flow->tp_src = htons(icmp_key->icmp_type); | |
948 | flow->tp_dst = htons(icmp_key->icmp_code); | |
949 | break; | |
950 | ||
df2c07f4 | 951 | case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_ICMPV6): |
d31f1109 JP |
952 | if (flow->nw_proto != IPPROTO_ICMPV6) { |
953 | return EINVAL; | |
954 | } | |
955 | icmpv6_key = nl_attr_get(nla); | |
956 | flow->tp_src = htons(icmpv6_key->icmpv6_type); | |
957 | flow->tp_dst = htons(icmpv6_key->icmpv6_code); | |
958 | break; | |
959 | ||
df2c07f4 | 960 | case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_ARP): |
36956a7d BP |
961 | if (flow->dl_type != htons(ETH_TYPE_ARP)) { |
962 | return EINVAL; | |
963 | } | |
964 | arp_key = nl_attr_get(nla); | |
965 | flow->nw_src = arp_key->arp_sip; | |
966 | flow->nw_dst = arp_key->arp_tip; | |
967 | if (arp_key->arp_op & htons(0xff00)) { | |
968 | return EINVAL; | |
969 | } | |
970 | flow->nw_proto = ntohs(arp_key->arp_op); | |
bad68a99 JP |
971 | memcpy(flow->arp_sha, arp_key->arp_sha, ETH_ADDR_LEN); |
972 | memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN); | |
36956a7d BP |
973 | break; |
974 | ||
df2c07f4 | 975 | case TRANSITION(OVS_KEY_ATTR_ICMPV6, OVS_KEY_ATTR_ND): |
685a51a5 JP |
976 | if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT) |
977 | && flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) { | |
978 | return EINVAL; | |
979 | } | |
980 | nd_key = nl_attr_get(nla); | |
981 | memcpy(&flow->nd_target, nd_key->nd_target, sizeof flow->nd_target); | |
982 | memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN); | |
983 | memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN); | |
984 | break; | |
985 | ||
36956a7d | 986 | default: |
df2c07f4 JP |
987 | if (type == OVS_KEY_ATTR_UNSPEC |
988 | || prev_type == OVS_KEY_ATTR_UNSPEC) { | |
36956a7d BP |
989 | return EINVAL; |
990 | } | |
991 | return EINVAL; | |
992 | } | |
993 | ||
994 | prev_type = type; | |
995 | } | |
996 | if (left) { | |
997 | return EINVAL; | |
998 | } | |
999 | ||
1000 | switch (prev_type) { | |
df2c07f4 | 1001 | case OVS_KEY_ATTR_UNSPEC: |
36956a7d BP |
1002 | return EINVAL; |
1003 | ||
df2c07f4 JP |
1004 | case OVS_KEY_ATTR_TUN_ID: |
1005 | case OVS_KEY_ATTR_IN_PORT: | |
36956a7d BP |
1006 | return EINVAL; |
1007 | ||
df2c07f4 JP |
1008 | case OVS_KEY_ATTR_ETHERNET: |
1009 | case OVS_KEY_ATTR_8021Q: | |
36956a7d BP |
1010 | return 0; |
1011 | ||
df2c07f4 | 1012 | case OVS_KEY_ATTR_ETHERTYPE: |
36956a7d | 1013 | if (flow->dl_type == htons(ETH_TYPE_IP) |
d31f1109 | 1014 | || flow->dl_type == htons(ETH_TYPE_IPV6) |
36956a7d BP |
1015 | || flow->dl_type == htons(ETH_TYPE_ARP)) { |
1016 | return EINVAL; | |
1017 | } | |
1018 | return 0; | |
1019 | ||
df2c07f4 | 1020 | case OVS_KEY_ATTR_IPV4: |
6767a2cc JP |
1021 | if (flow->nw_proto == IPPROTO_TCP |
1022 | || flow->nw_proto == IPPROTO_UDP | |
1023 | || flow->nw_proto == IPPROTO_ICMP) { | |
36956a7d BP |
1024 | return EINVAL; |
1025 | } | |
1026 | return 0; | |
1027 | ||
df2c07f4 | 1028 | case OVS_KEY_ATTR_IPV6: |
d31f1109 JP |
1029 | if (flow->nw_proto == IPPROTO_TCP |
1030 | || flow->nw_proto == IPPROTO_UDP | |
1031 | || flow->nw_proto == IPPROTO_ICMPV6) { | |
1032 | return EINVAL; | |
1033 | } | |
1034 | return 0; | |
1035 | ||
df2c07f4 | 1036 | case OVS_KEY_ATTR_ICMPV6: |
685a51a5 JP |
1037 | if (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT) |
1038 | || flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)) { | |
1039 | return EINVAL; | |
1040 | } | |
1041 | return 0; | |
1042 | ||
df2c07f4 JP |
1043 | case OVS_KEY_ATTR_TCP: |
1044 | case OVS_KEY_ATTR_UDP: | |
1045 | case OVS_KEY_ATTR_ICMP: | |
1046 | case OVS_KEY_ATTR_ARP: | |
1047 | case OVS_KEY_ATTR_ND: | |
36956a7d BP |
1048 | return 0; |
1049 | ||
df2c07f4 | 1050 | case __OVS_KEY_ATTR_MAX: |
36956a7d BP |
1051 | default: |
1052 | NOT_REACHED(); | |
1053 | } | |
14608a15 | 1054 | } |