]>
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" |
0ae60917 | 30 | #include "openvswitch/tunnel.h" |
064af421 BP |
31 | #include "packets.h" |
32 | #include "timeval.h" | |
33 | #include "util.h" | |
34 | ||
cdee00fd BP |
35 | int |
36 | odp_action_len(uint16_t type) | |
37 | { | |
7aec165d | 38 | if (type > ODP_ACTION_ATTR_MAX) { |
cdee00fd BP |
39 | return -1; |
40 | } | |
41 | ||
42 | switch ((enum odp_action_type) type) { | |
7aec165d BP |
43 | case ODP_ACTION_ATTR_OUTPUT: return 4; |
44 | case ODP_ACTION_ATTR_CONTROLLER: return 8; | |
45 | case ODP_ACTION_ATTR_SET_DL_TCI: return 2; | |
46 | case ODP_ACTION_ATTR_STRIP_VLAN: return 0; | |
47 | case ODP_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN; | |
48 | case ODP_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN; | |
49 | case ODP_ACTION_ATTR_SET_NW_SRC: return 4; | |
50 | case ODP_ACTION_ATTR_SET_NW_DST: return 4; | |
51 | case ODP_ACTION_ATTR_SET_NW_TOS: return 1; | |
52 | case ODP_ACTION_ATTR_SET_TP_SRC: return 2; | |
53 | case ODP_ACTION_ATTR_SET_TP_DST: return 2; | |
54 | case ODP_ACTION_ATTR_SET_TUNNEL: return 8; | |
55 | case ODP_ACTION_ATTR_SET_PRIORITY: return 4; | |
56 | case ODP_ACTION_ATTR_POP_PRIORITY: return 0; | |
57 | case ODP_ACTION_ATTR_DROP_SPOOFED_ARP: return 0; | |
cdee00fd | 58 | |
7aec165d BP |
59 | case ODP_ACTION_ATTR_UNSPEC: |
60 | case __ODP_ACTION_ATTR_MAX: | |
cdee00fd BP |
61 | return -1; |
62 | } | |
63 | ||
64 | return -1; | |
65 | } | |
66 | ||
67 | static void | |
68 | format_generic_odp_action(struct ds *ds, const struct nlattr *a) | |
69 | { | |
8ba43fbd BP |
70 | size_t len = nl_attr_get_size(a); |
71 | ||
cdee00fd | 72 | ds_put_format(ds, "action%"PRId16, nl_attr_type(a)); |
8ba43fbd | 73 | if (len) { |
cdee00fd BP |
74 | const uint8_t *unspec; |
75 | unsigned int i; | |
76 | ||
77 | unspec = nl_attr_get(a); | |
8ba43fbd | 78 | for (i = 0; i < len; i++) { |
cdee00fd BP |
79 | ds_put_char(ds, i ? ' ': '('); |
80 | ds_put_format(ds, "%02x", unspec[i]); | |
81 | } | |
82 | ds_put_char(ds, ')'); | |
83 | } | |
84 | } | |
85 | ||
064af421 | 86 | void |
cdee00fd | 87 | format_odp_action(struct ds *ds, const struct nlattr *a) |
064af421 | 88 | { |
cdee00fd BP |
89 | const uint8_t *eth; |
90 | ovs_be32 ip; | |
91 | ||
b7b0c620 | 92 | if (nl_attr_get_size(a) != odp_action_len(nl_attr_type(a))) { |
8ba43fbd | 93 | ds_put_format(ds, "bad length %zu, expected %d for: ", |
b7b0c620 | 94 | nl_attr_get_size(a), odp_action_len(nl_attr_type(a))); |
cdee00fd BP |
95 | format_generic_odp_action(ds, a); |
96 | return; | |
97 | } | |
98 | ||
99 | switch (nl_attr_type(a)) { | |
7aec165d | 100 | case ODP_ACTION_ATTR_OUTPUT: |
cdee00fd | 101 | ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a)); |
064af421 | 102 | break; |
7aec165d | 103 | case ODP_ACTION_ATTR_CONTROLLER: |
b9298d3f | 104 | ds_put_format(ds, "ctl(%"PRIu64")", nl_attr_get_u64(a)); |
064af421 | 105 | break; |
7aec165d | 106 | case ODP_ACTION_ATTR_SET_TUNNEL: |
b9298d3f BP |
107 | ds_put_format(ds, "set_tunnel(%#"PRIx64")", |
108 | ntohll(nl_attr_get_be64(a))); | |
659586ef | 109 | break; |
7aec165d | 110 | case ODP_ACTION_ATTR_SET_DL_TCI: |
27bcf966 | 111 | ds_put_format(ds, "set_tci(vid=%"PRIu16",pcp=%d)", |
cdee00fd BP |
112 | vlan_tci_to_vid(nl_attr_get_be16(a)), |
113 | vlan_tci_to_pcp(nl_attr_get_be16(a))); | |
064af421 | 114 | break; |
7aec165d | 115 | case ODP_ACTION_ATTR_STRIP_VLAN: |
064af421 BP |
116 | ds_put_format(ds, "strip_vlan"); |
117 | break; | |
7aec165d | 118 | case ODP_ACTION_ATTR_SET_DL_SRC: |
cdee00fd BP |
119 | eth = nl_attr_get_unspec(a, ETH_ADDR_LEN); |
120 | ds_put_format(ds, "set_dl_src("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth)); | |
064af421 | 121 | break; |
7aec165d | 122 | case ODP_ACTION_ATTR_SET_DL_DST: |
cdee00fd BP |
123 | eth = nl_attr_get_unspec(a, ETH_ADDR_LEN); |
124 | ds_put_format(ds, "set_dl_dst("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth)); | |
064af421 | 125 | break; |
7aec165d | 126 | case ODP_ACTION_ATTR_SET_NW_SRC: |
cdee00fd BP |
127 | ip = nl_attr_get_be32(a); |
128 | ds_put_format(ds, "set_nw_src("IP_FMT")", IP_ARGS(&ip)); | |
064af421 | 129 | break; |
7aec165d | 130 | case ODP_ACTION_ATTR_SET_NW_DST: |
cdee00fd BP |
131 | ip = nl_attr_get_be32(a); |
132 | ds_put_format(ds, "set_nw_dst("IP_FMT")", IP_ARGS(&ip)); | |
064af421 | 133 | break; |
7aec165d | 134 | case ODP_ACTION_ATTR_SET_NW_TOS: |
cdee00fd | 135 | ds_put_format(ds, "set_nw_tos(%"PRIu8")", nl_attr_get_u8(a)); |
959a2ecd | 136 | break; |
7aec165d | 137 | case ODP_ACTION_ATTR_SET_TP_SRC: |
cdee00fd | 138 | ds_put_format(ds, "set_tp_src(%"PRIu16")", ntohs(nl_attr_get_be16(a))); |
064af421 | 139 | break; |
7aec165d | 140 | case ODP_ACTION_ATTR_SET_TP_DST: |
cdee00fd | 141 | ds_put_format(ds, "set_tp_dst(%"PRIu16")", ntohs(nl_attr_get_be16(a))); |
064af421 | 142 | break; |
7aec165d | 143 | case ODP_ACTION_ATTR_SET_PRIORITY: |
cdee00fd | 144 | ds_put_format(ds, "set_priority(%#"PRIx32")", nl_attr_get_u32(a)); |
c4d279ab | 145 | break; |
7aec165d | 146 | case ODP_ACTION_ATTR_POP_PRIORITY: |
c4d279ab BP |
147 | ds_put_cstr(ds, "pop_priority"); |
148 | break; | |
7aec165d | 149 | case ODP_ACTION_ATTR_DROP_SPOOFED_ARP: |
a77d89b8 BP |
150 | ds_put_cstr(ds, "drop_spoofed_arp"); |
151 | break; | |
064af421 | 152 | default: |
cdee00fd | 153 | format_generic_odp_action(ds, a); |
064af421 BP |
154 | break; |
155 | } | |
156 | } | |
157 | ||
158 | void | |
cdee00fd | 159 | format_odp_actions(struct ds *ds, const struct nlattr *actions, |
cf22f8cb | 160 | size_t actions_len) |
064af421 | 161 | { |
cdee00fd BP |
162 | if (actions_len) { |
163 | const struct nlattr *a; | |
164 | unsigned int left; | |
165 | ||
166 | NL_ATTR_FOR_EACH (a, left, actions, actions_len) { | |
167 | if (a != actions) { | |
168 | ds_put_char(ds, ','); | |
169 | } | |
170 | format_odp_action(ds, a); | |
064af421 | 171 | } |
cdee00fd | 172 | if (left) { |
3476fce3 BP |
173 | if (left == actions_len) { |
174 | ds_put_cstr(ds, "<empty>"); | |
175 | } | |
36956a7d | 176 | ds_put_format(ds, ",***%u leftover bytes***", left); |
cdee00fd BP |
177 | } |
178 | } else { | |
064af421 BP |
179 | ds_put_cstr(ds, "drop"); |
180 | } | |
181 | } | |
14608a15 | 182 | \f |
36956a7d BP |
183 | /* Returns the correct length of the payload for a flow key attribute of the |
184 | * specified 'type', or -1 if 'type' is unknown. */ | |
185 | static int | |
186 | odp_flow_key_attr_len(uint16_t type) | |
187 | { | |
188 | if (type > ODP_KEY_ATTR_MAX) { | |
189 | return -1; | |
190 | } | |
191 | ||
192 | switch ((enum odp_key_type) type) { | |
193 | case ODP_KEY_ATTR_TUN_ID: return 8; | |
194 | case ODP_KEY_ATTR_IN_PORT: return 4; | |
195 | case ODP_KEY_ATTR_ETHERNET: return sizeof(struct odp_key_ethernet); | |
196 | case ODP_KEY_ATTR_8021Q: return sizeof(struct odp_key_8021q); | |
197 | case ODP_KEY_ATTR_ETHERTYPE: return 2; | |
198 | case ODP_KEY_ATTR_IPV4: return sizeof(struct odp_key_ipv4); | |
d31f1109 | 199 | case ODP_KEY_ATTR_IPV6: return sizeof(struct odp_key_ipv6); |
36956a7d BP |
200 | case ODP_KEY_ATTR_TCP: return sizeof(struct odp_key_tcp); |
201 | case ODP_KEY_ATTR_UDP: return sizeof(struct odp_key_udp); | |
202 | case ODP_KEY_ATTR_ICMP: return sizeof(struct odp_key_icmp); | |
d31f1109 | 203 | case ODP_KEY_ATTR_ICMPV6: return sizeof(struct odp_key_icmpv6); |
36956a7d | 204 | case ODP_KEY_ATTR_ARP: return sizeof(struct odp_key_arp); |
685a51a5 | 205 | case ODP_KEY_ATTR_ND: return sizeof(struct odp_key_nd); |
36956a7d BP |
206 | |
207 | case ODP_KEY_ATTR_UNSPEC: | |
208 | case __ODP_KEY_ATTR_MAX: | |
209 | return -1; | |
210 | } | |
211 | ||
212 | return -1; | |
213 | } | |
214 | ||
215 | ||
216 | static void | |
217 | format_generic_odp_key(const struct nlattr *a, struct ds *ds) | |
218 | { | |
219 | size_t len = nl_attr_get_size(a); | |
220 | ||
221 | ds_put_format(ds, "key%"PRId16, nl_attr_type(a)); | |
222 | if (len) { | |
223 | const uint8_t *unspec; | |
224 | unsigned int i; | |
225 | ||
226 | unspec = nl_attr_get(a); | |
227 | for (i = 0; i < len; i++) { | |
228 | ds_put_char(ds, i ? ' ': '('); | |
229 | ds_put_format(ds, "%02x", unspec[i]); | |
230 | } | |
231 | ds_put_char(ds, ')'); | |
232 | } | |
233 | } | |
234 | ||
235 | static void | |
236 | format_odp_key_attr(const struct nlattr *a, struct ds *ds) | |
237 | { | |
238 | const struct odp_key_ethernet *eth_key; | |
239 | const struct odp_key_8021q *q_key; | |
240 | const struct odp_key_ipv4 *ipv4_key; | |
d31f1109 | 241 | const struct odp_key_ipv6 *ipv6_key; |
36956a7d BP |
242 | const struct odp_key_tcp *tcp_key; |
243 | const struct odp_key_udp *udp_key; | |
244 | const struct odp_key_icmp *icmp_key; | |
d31f1109 | 245 | const struct odp_key_icmpv6 *icmpv6_key; |
36956a7d | 246 | const struct odp_key_arp *arp_key; |
685a51a5 | 247 | const struct odp_key_nd *nd_key; |
36956a7d BP |
248 | |
249 | if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) { | |
250 | ds_put_format(ds, "bad length %zu, expected %d for: ", | |
251 | nl_attr_get_size(a), | |
252 | odp_flow_key_attr_len(nl_attr_type(a))); | |
253 | format_generic_odp_key(a, ds); | |
254 | return; | |
255 | } | |
256 | ||
257 | switch (nl_attr_type(a)) { | |
258 | case ODP_KEY_ATTR_TUN_ID: | |
259 | ds_put_format(ds, "tun_id(%#"PRIx64")", nl_attr_get_be64(a)); | |
260 | break; | |
261 | ||
262 | case ODP_KEY_ATTR_IN_PORT: | |
263 | ds_put_format(ds, "in_port(%"PRIu32")", nl_attr_get_u32(a)); | |
264 | break; | |
265 | ||
266 | case ODP_KEY_ATTR_ETHERNET: | |
267 | eth_key = nl_attr_get(a); | |
268 | ds_put_format(ds, "eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")", | |
269 | ETH_ADDR_ARGS(eth_key->eth_src), | |
270 | ETH_ADDR_ARGS(eth_key->eth_dst)); | |
271 | break; | |
272 | ||
273 | case ODP_KEY_ATTR_8021Q: | |
274 | q_key = nl_attr_get(a); | |
275 | ds_put_cstr(ds, "vlan("); | |
276 | if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { | |
074d69f5 | 277 | ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(q_key->q_tpid)); |
36956a7d BP |
278 | } |
279 | ds_put_format(ds, "vid%"PRIu16",pcp%d)", | |
280 | vlan_tci_to_vid(q_key->q_tci), | |
281 | vlan_tci_to_pcp(q_key->q_tci)); | |
282 | break; | |
283 | ||
284 | case ODP_KEY_ATTR_ETHERTYPE: | |
074d69f5 | 285 | ds_put_format(ds, "eth_type(0x%04"PRIx16")", |
36956a7d BP |
286 | ntohs(nl_attr_get_be16(a))); |
287 | break; | |
288 | ||
289 | case ODP_KEY_ATTR_IPV4: | |
290 | ipv4_key = nl_attr_get(a); | |
291 | ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT"," | |
292 | "proto=%"PRId8",tos=%"PRIu8")", | |
293 | IP_ARGS(&ipv4_key->ipv4_src), | |
294 | IP_ARGS(&ipv4_key->ipv4_dst), | |
295 | ipv4_key->ipv4_proto, ipv4_key->ipv4_tos); | |
296 | break; | |
297 | ||
d31f1109 JP |
298 | case ODP_KEY_ATTR_IPV6: { |
299 | char src_str[INET6_ADDRSTRLEN]; | |
300 | char dst_str[INET6_ADDRSTRLEN]; | |
301 | ||
302 | ipv6_key = nl_attr_get(a); | |
303 | inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str); | |
304 | inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str); | |
305 | ||
306 | ds_put_format(ds, "ipv6(src=%s,dst=%s,proto=%"PRId8",tos=%"PRIu8")", | |
307 | src_str, dst_str, ipv6_key->ipv6_proto, | |
308 | ipv6_key->ipv6_tos); | |
309 | break; | |
310 | } | |
311 | ||
36956a7d BP |
312 | case ODP_KEY_ATTR_TCP: |
313 | tcp_key = nl_attr_get(a); | |
314 | ds_put_format(ds, "tcp(src=%"PRIu16",dst=%"PRIu16")", | |
315 | ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst)); | |
316 | break; | |
317 | ||
318 | case ODP_KEY_ATTR_UDP: | |
319 | udp_key = nl_attr_get(a); | |
320 | ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16")", | |
321 | ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst)); | |
322 | break; | |
323 | ||
324 | case ODP_KEY_ATTR_ICMP: | |
325 | icmp_key = nl_attr_get(a); | |
326 | ds_put_format(ds, "icmp(type=%"PRIu8",code=%"PRIu8")", | |
327 | icmp_key->icmp_type, icmp_key->icmp_code); | |
328 | break; | |
329 | ||
d31f1109 JP |
330 | case ODP_KEY_ATTR_ICMPV6: |
331 | icmpv6_key = nl_attr_get(a); | |
332 | ds_put_format(ds, "icmpv6(type=%"PRIu8",code=%"PRIu8")", | |
333 | icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code); | |
334 | break; | |
335 | ||
36956a7d BP |
336 | case ODP_KEY_ATTR_ARP: |
337 | arp_key = nl_attr_get(a); | |
bad68a99 JP |
338 | ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16"," |
339 | "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT")", | |
36956a7d | 340 | IP_ARGS(&arp_key->arp_sip), IP_ARGS(&arp_key->arp_tip), |
bad68a99 JP |
341 | ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha), |
342 | ETH_ADDR_ARGS(arp_key->arp_tha)); | |
36956a7d BP |
343 | break; |
344 | ||
685a51a5 JP |
345 | case ODP_KEY_ATTR_ND: { |
346 | char target[INET6_ADDRSTRLEN]; | |
347 | ||
348 | nd_key = nl_attr_get(a); | |
349 | inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target); | |
350 | ||
351 | ds_put_format(ds, "nd(target=%s", target); | |
352 | if (!eth_addr_is_zero(nd_key->nd_sll)) { | |
353 | ds_put_format(ds, ",sll="ETH_ADDR_FMT, | |
354 | ETH_ADDR_ARGS(nd_key->nd_sll)); | |
355 | } | |
356 | if (!eth_addr_is_zero(nd_key->nd_tll)) { | |
357 | ds_put_format(ds, ",tll="ETH_ADDR_FMT, | |
358 | ETH_ADDR_ARGS(nd_key->nd_tll)); | |
359 | } | |
360 | ds_put_char(ds, ')'); | |
361 | break; | |
362 | } | |
363 | ||
36956a7d BP |
364 | default: |
365 | format_generic_odp_key(a, ds); | |
366 | break; | |
367 | } | |
368 | } | |
369 | ||
370 | /* Appends to 'ds' a string representation of the 'key_len' bytes of | |
371 | * ODP_KEY_ATTR_* attributes in 'key'. */ | |
14608a15 | 372 | void |
36956a7d | 373 | odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds) |
14608a15 | 374 | { |
36956a7d BP |
375 | if (key_len) { |
376 | const struct nlattr *a; | |
377 | unsigned int left; | |
378 | ||
379 | NL_ATTR_FOR_EACH (a, left, key, key_len) { | |
380 | if (a != key) { | |
381 | ds_put_char(ds, ','); | |
382 | } | |
383 | format_odp_key_attr(a, ds); | |
384 | } | |
385 | if (left) { | |
386 | if (left == key_len) { | |
387 | ds_put_cstr(ds, "<empty>"); | |
388 | } | |
389 | ds_put_format(ds, ",***%u leftover bytes***", left); | |
390 | } | |
391 | } else { | |
392 | ds_put_cstr(ds, "<empty>"); | |
393 | } | |
14608a15 | 394 | } |
064af421 | 395 | |
36956a7d | 396 | /* Appends a representation of 'flow' as ODP_KEY_ATTR_* attributes to 'buf'. */ |
14608a15 | 397 | void |
36956a7d | 398 | odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) |
14608a15 | 399 | { |
36956a7d BP |
400 | struct odp_key_ethernet *eth_key; |
401 | ||
402 | if (flow->tun_id != htonll(0)) { | |
403 | nl_msg_put_be64(buf, ODP_KEY_ATTR_TUN_ID, flow->tun_id); | |
404 | } | |
405 | ||
406 | nl_msg_put_u32(buf, ODP_KEY_ATTR_IN_PORT, flow->in_port); | |
407 | ||
408 | eth_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ETHERNET, | |
409 | sizeof *eth_key); | |
410 | memcpy(eth_key->eth_src, flow->dl_src, ETH_ADDR_LEN); | |
411 | memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN); | |
412 | ||
413 | if (flow->vlan_tci != htons(0)) { | |
414 | struct odp_key_8021q *q_key; | |
415 | ||
416 | q_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_8021Q, | |
417 | sizeof *q_key); | |
418 | q_key->q_tpid = htons(ETH_TYPE_VLAN); | |
419 | q_key->q_tci = flow->vlan_tci & ~htons(VLAN_CFI); | |
420 | } | |
421 | ||
422 | if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { | |
423 | return; | |
424 | } | |
425 | ||
426 | nl_msg_put_be16(buf, ODP_KEY_ATTR_ETHERTYPE, flow->dl_type); | |
427 | ||
428 | if (flow->dl_type == htons(ETH_TYPE_IP)) { | |
429 | struct odp_key_ipv4 *ipv4_key; | |
430 | ||
431 | ipv4_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_IPV4, | |
432 | sizeof *ipv4_key); | |
433 | ipv4_key->ipv4_src = flow->nw_src; | |
434 | ipv4_key->ipv4_dst = flow->nw_dst; | |
435 | ipv4_key->ipv4_proto = flow->nw_proto; | |
436 | ipv4_key->ipv4_tos = flow->nw_tos; | |
d31f1109 JP |
437 | } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { |
438 | struct odp_key_ipv6 *ipv6_key; | |
439 | ||
440 | ipv6_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_IPV6, | |
441 | sizeof *ipv6_key); | |
442 | memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src); | |
443 | memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst); | |
444 | ipv6_key->ipv6_proto = flow->nw_proto; | |
445 | ipv6_key->ipv6_tos = flow->nw_tos; | |
446 | } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { | |
447 | struct odp_key_arp *arp_key; | |
448 | ||
449 | arp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ARP, | |
450 | sizeof *arp_key); | |
451 | arp_key->arp_sip = flow->nw_src; | |
452 | arp_key->arp_tip = flow->nw_dst; | |
453 | arp_key->arp_op = htons(flow->nw_proto); | |
454 | memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN); | |
455 | memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN); | |
456 | } | |
457 | ||
458 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
459 | || flow->dl_type == htons(ETH_TYPE_IPV6)) { | |
36956a7d | 460 | |
6767a2cc | 461 | if (flow->nw_proto == IPPROTO_TCP) { |
36956a7d BP |
462 | struct odp_key_tcp *tcp_key; |
463 | ||
464 | tcp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_TCP, | |
465 | sizeof *tcp_key); | |
466 | tcp_key->tcp_src = flow->tp_src; | |
467 | tcp_key->tcp_dst = flow->tp_dst; | |
6767a2cc | 468 | } else if (flow->nw_proto == IPPROTO_UDP) { |
36956a7d BP |
469 | struct odp_key_udp *udp_key; |
470 | ||
471 | udp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_UDP, | |
472 | sizeof *udp_key); | |
473 | udp_key->udp_src = flow->tp_src; | |
474 | udp_key->udp_dst = flow->tp_dst; | |
d31f1109 JP |
475 | } else if (flow->dl_type == htons(ETH_TYPE_IP) |
476 | && flow->nw_proto == IPPROTO_ICMP) { | |
36956a7d BP |
477 | struct odp_key_icmp *icmp_key; |
478 | ||
479 | icmp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ICMP, | |
480 | sizeof *icmp_key); | |
481 | icmp_key->icmp_type = ntohs(flow->tp_src); | |
482 | icmp_key->icmp_code = ntohs(flow->tp_dst); | |
d31f1109 JP |
483 | } else if (flow->dl_type == htons(ETH_TYPE_IPV6) |
484 | && flow->nw_proto == IPPROTO_ICMPV6) { | |
485 | struct odp_key_icmpv6 *icmpv6_key; | |
486 | ||
487 | icmpv6_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ICMPV6, | |
488 | sizeof *icmpv6_key); | |
489 | icmpv6_key->icmpv6_type = ntohs(flow->tp_src); | |
490 | icmpv6_key->icmpv6_code = ntohs(flow->tp_dst); | |
685a51a5 JP |
491 | |
492 | if (icmpv6_key->icmpv6_type == ND_NEIGHBOR_SOLICIT | |
493 | || icmpv6_key->icmpv6_type == ND_NEIGHBOR_ADVERT) { | |
494 | struct odp_key_nd *nd_key; | |
495 | ||
496 | nd_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ND, | |
497 | sizeof *nd_key); | |
498 | memcpy(nd_key->nd_target, &flow->nd_target, | |
499 | sizeof nd_key->nd_target); | |
500 | memcpy(nd_key->nd_sll, flow->arp_sha, ETH_ADDR_LEN); | |
501 | memcpy(nd_key->nd_tll, flow->arp_tha, ETH_ADDR_LEN); | |
502 | } | |
36956a7d | 503 | } |
36956a7d BP |
504 | } |
505 | } | |
506 | ||
507 | /* Converts the 'key_len' bytes of ODP_KEY_ATTR_* attributes in 'key' to a flow | |
508 | * structure in 'flow'. Returns 0 if successful, otherwise EINVAL. */ | |
509 | int | |
510 | odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, | |
511 | struct flow *flow) | |
512 | { | |
513 | const struct nlattr *nla; | |
514 | enum odp_key_type prev_type; | |
515 | size_t left; | |
516 | ||
517 | memset(flow, 0, sizeof *flow); | |
518 | flow->dl_type = htons(FLOW_DL_TYPE_NONE); | |
519 | ||
520 | prev_type = ODP_KEY_ATTR_UNSPEC; | |
521 | NL_ATTR_FOR_EACH (nla, left, key, key_len) { | |
522 | const struct odp_key_ethernet *eth_key; | |
523 | const struct odp_key_8021q *q_key; | |
524 | const struct odp_key_ipv4 *ipv4_key; | |
d31f1109 | 525 | const struct odp_key_ipv6 *ipv6_key; |
36956a7d BP |
526 | const struct odp_key_tcp *tcp_key; |
527 | const struct odp_key_udp *udp_key; | |
528 | const struct odp_key_icmp *icmp_key; | |
d31f1109 | 529 | const struct odp_key_icmpv6 *icmpv6_key; |
36956a7d | 530 | const struct odp_key_arp *arp_key; |
685a51a5 | 531 | const struct odp_key_nd *nd_key; |
36956a7d BP |
532 | |
533 | uint16_t type = nl_attr_type(nla); | |
534 | int len = odp_flow_key_attr_len(type); | |
535 | ||
536 | if (nl_attr_get_size(nla) != len && len != -1) { | |
537 | return EINVAL; | |
538 | } | |
539 | ||
540 | #define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE)) | |
541 | switch (TRANSITION(prev_type, type)) { | |
542 | case TRANSITION(ODP_KEY_ATTR_UNSPEC, ODP_KEY_ATTR_TUN_ID): | |
543 | flow->tun_id = nl_attr_get_be64(nla); | |
544 | break; | |
545 | ||
546 | case TRANSITION(ODP_KEY_ATTR_UNSPEC, ODP_KEY_ATTR_IN_PORT): | |
547 | case TRANSITION(ODP_KEY_ATTR_TUN_ID, ODP_KEY_ATTR_IN_PORT): | |
548 | if (nl_attr_get_u32(nla) >= UINT16_MAX) { | |
549 | return EINVAL; | |
550 | } | |
551 | flow->in_port = nl_attr_get_u32(nla); | |
552 | break; | |
553 | ||
554 | case TRANSITION(ODP_KEY_ATTR_IN_PORT, ODP_KEY_ATTR_ETHERNET): | |
555 | eth_key = nl_attr_get(nla); | |
556 | memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN); | |
557 | memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN); | |
558 | break; | |
559 | ||
560 | case TRANSITION(ODP_KEY_ATTR_ETHERNET, ODP_KEY_ATTR_8021Q): | |
561 | q_key = nl_attr_get(nla); | |
562 | if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { | |
563 | /* Only standard 0x8100 VLANs currently supported. */ | |
564 | return EINVAL; | |
565 | } | |
566 | if (q_key->q_tci & htons(VLAN_CFI)) { | |
567 | return EINVAL; | |
568 | } | |
569 | flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI); | |
570 | break; | |
571 | ||
572 | case TRANSITION(ODP_KEY_ATTR_8021Q, ODP_KEY_ATTR_ETHERTYPE): | |
573 | case TRANSITION(ODP_KEY_ATTR_ETHERNET, ODP_KEY_ATTR_ETHERTYPE): | |
574 | flow->dl_type = nl_attr_get_be16(nla); | |
575 | if (ntohs(flow->dl_type) < 1536) { | |
576 | return EINVAL; | |
577 | } | |
578 | break; | |
579 | ||
580 | case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_IPV4): | |
581 | if (flow->dl_type != htons(ETH_TYPE_IP)) { | |
582 | return EINVAL; | |
583 | } | |
584 | ipv4_key = nl_attr_get(nla); | |
585 | flow->nw_src = ipv4_key->ipv4_src; | |
586 | flow->nw_dst = ipv4_key->ipv4_dst; | |
587 | flow->nw_proto = ipv4_key->ipv4_proto; | |
588 | flow->nw_tos = ipv4_key->ipv4_tos; | |
589 | if (flow->nw_tos & IP_ECN_MASK) { | |
590 | return EINVAL; | |
591 | } | |
592 | break; | |
593 | ||
d31f1109 JP |
594 | case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_IPV6): |
595 | if (flow->dl_type != htons(ETH_TYPE_IPV6)) { | |
596 | return EINVAL; | |
597 | } | |
598 | ipv6_key = nl_attr_get(nla); | |
599 | memcpy(&flow->ipv6_src, ipv6_key->ipv6_src, sizeof flow->ipv6_src); | |
600 | memcpy(&flow->ipv6_dst, ipv6_key->ipv6_dst, sizeof flow->ipv6_dst); | |
601 | flow->nw_proto = ipv6_key->ipv6_proto; | |
602 | flow->nw_tos = ipv6_key->ipv6_tos; | |
603 | if (flow->nw_tos & IP_ECN_MASK) { | |
604 | return EINVAL; | |
605 | } | |
606 | break; | |
607 | ||
36956a7d | 608 | case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_TCP): |
d31f1109 | 609 | case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_TCP): |
6767a2cc | 610 | if (flow->nw_proto != IPPROTO_TCP) { |
36956a7d BP |
611 | return EINVAL; |
612 | } | |
613 | tcp_key = nl_attr_get(nla); | |
614 | flow->tp_src = tcp_key->tcp_src; | |
615 | flow->tp_dst = tcp_key->tcp_dst; | |
616 | break; | |
617 | ||
618 | case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_UDP): | |
d31f1109 | 619 | case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_UDP): |
6767a2cc | 620 | if (flow->nw_proto != IPPROTO_UDP) { |
36956a7d BP |
621 | return EINVAL; |
622 | } | |
623 | udp_key = nl_attr_get(nla); | |
624 | flow->tp_src = udp_key->udp_src; | |
625 | flow->tp_dst = udp_key->udp_dst; | |
626 | break; | |
627 | ||
628 | case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_ICMP): | |
6767a2cc | 629 | if (flow->nw_proto != IPPROTO_ICMP) { |
36956a7d BP |
630 | return EINVAL; |
631 | } | |
632 | icmp_key = nl_attr_get(nla); | |
633 | flow->tp_src = htons(icmp_key->icmp_type); | |
634 | flow->tp_dst = htons(icmp_key->icmp_code); | |
635 | break; | |
636 | ||
d31f1109 JP |
637 | case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_ICMPV6): |
638 | if (flow->nw_proto != IPPROTO_ICMPV6) { | |
639 | return EINVAL; | |
640 | } | |
641 | icmpv6_key = nl_attr_get(nla); | |
642 | flow->tp_src = htons(icmpv6_key->icmpv6_type); | |
643 | flow->tp_dst = htons(icmpv6_key->icmpv6_code); | |
644 | break; | |
645 | ||
36956a7d BP |
646 | case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_ARP): |
647 | if (flow->dl_type != htons(ETH_TYPE_ARP)) { | |
648 | return EINVAL; | |
649 | } | |
650 | arp_key = nl_attr_get(nla); | |
651 | flow->nw_src = arp_key->arp_sip; | |
652 | flow->nw_dst = arp_key->arp_tip; | |
653 | if (arp_key->arp_op & htons(0xff00)) { | |
654 | return EINVAL; | |
655 | } | |
656 | flow->nw_proto = ntohs(arp_key->arp_op); | |
bad68a99 JP |
657 | memcpy(flow->arp_sha, arp_key->arp_sha, ETH_ADDR_LEN); |
658 | memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN); | |
36956a7d BP |
659 | break; |
660 | ||
685a51a5 JP |
661 | case TRANSITION(ODP_KEY_ATTR_ICMPV6, ODP_KEY_ATTR_ND): |
662 | if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT) | |
663 | && flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) { | |
664 | return EINVAL; | |
665 | } | |
666 | nd_key = nl_attr_get(nla); | |
667 | memcpy(&flow->nd_target, nd_key->nd_target, sizeof flow->nd_target); | |
668 | memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN); | |
669 | memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN); | |
670 | break; | |
671 | ||
36956a7d BP |
672 | default: |
673 | if (type == ODP_KEY_ATTR_UNSPEC | |
674 | || prev_type == ODP_KEY_ATTR_UNSPEC) { | |
675 | return EINVAL; | |
676 | } | |
677 | return EINVAL; | |
678 | } | |
679 | ||
680 | prev_type = type; | |
681 | } | |
682 | if (left) { | |
683 | return EINVAL; | |
684 | } | |
685 | ||
686 | switch (prev_type) { | |
687 | case ODP_KEY_ATTR_UNSPEC: | |
688 | return EINVAL; | |
689 | ||
690 | case ODP_KEY_ATTR_TUN_ID: | |
691 | case ODP_KEY_ATTR_IN_PORT: | |
692 | return EINVAL; | |
693 | ||
694 | case ODP_KEY_ATTR_ETHERNET: | |
695 | case ODP_KEY_ATTR_8021Q: | |
696 | return 0; | |
697 | ||
698 | case ODP_KEY_ATTR_ETHERTYPE: | |
699 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
d31f1109 | 700 | || flow->dl_type == htons(ETH_TYPE_IPV6) |
36956a7d BP |
701 | || flow->dl_type == htons(ETH_TYPE_ARP)) { |
702 | return EINVAL; | |
703 | } | |
704 | return 0; | |
705 | ||
706 | case ODP_KEY_ATTR_IPV4: | |
6767a2cc JP |
707 | if (flow->nw_proto == IPPROTO_TCP |
708 | || flow->nw_proto == IPPROTO_UDP | |
709 | || flow->nw_proto == IPPROTO_ICMP) { | |
36956a7d BP |
710 | return EINVAL; |
711 | } | |
712 | return 0; | |
713 | ||
d31f1109 JP |
714 | case ODP_KEY_ATTR_IPV6: |
715 | if (flow->nw_proto == IPPROTO_TCP | |
716 | || flow->nw_proto == IPPROTO_UDP | |
717 | || flow->nw_proto == IPPROTO_ICMPV6) { | |
718 | return EINVAL; | |
719 | } | |
720 | return 0; | |
721 | ||
685a51a5 JP |
722 | case ODP_KEY_ATTR_ICMPV6: |
723 | if (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT) | |
724 | || flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)) { | |
725 | return EINVAL; | |
726 | } | |
727 | return 0; | |
728 | ||
36956a7d BP |
729 | case ODP_KEY_ATTR_TCP: |
730 | case ODP_KEY_ATTR_UDP: | |
731 | case ODP_KEY_ATTR_ICMP: | |
732 | case ODP_KEY_ATTR_ARP: | |
685a51a5 | 733 | case ODP_KEY_ATTR_ND: |
36956a7d BP |
734 | return 0; |
735 | ||
736 | case __ODP_KEY_ATTR_MAX: | |
737 | default: | |
738 | NOT_REACHED(); | |
739 | } | |
14608a15 | 740 | } |