]>
Commit | Line | Data |
---|---|---|
30eb304e JP |
1 | /* |
2 | * f_flower.c Flower Classifier | |
3 | * | |
4 | * This program is free software; you can distribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * Authors: Jiri Pirko <jiri@resnulli.us> | |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <unistd.h> | |
30eb304e JP |
15 | #include <string.h> |
16 | #include <net/if.h> | |
f888f4e2 | 17 | #include <linux/if_arp.h> |
30eb304e JP |
18 | #include <linux/if_ether.h> |
19 | #include <linux/ip.h> | |
745d9172 | 20 | #include <linux/tc_act/tc_vlan.h> |
7638ee13 | 21 | #include <linux/mpls.h> |
30eb304e JP |
22 | |
23 | #include "utils.h" | |
24 | #include "tc_util.h" | |
25 | #include "rt_names.h" | |
26 | ||
08f66c80 PB |
27 | enum flower_matching_flags { |
28 | FLOWER_IP_FLAGS, | |
29 | }; | |
30 | ||
6910d656 SH |
31 | enum flower_endpoint { |
32 | FLOWER_ENDPOINT_SRC, | |
33 | FLOWER_ENDPOINT_DST | |
34 | }; | |
35 | ||
eb3b5696 SH |
36 | enum flower_icmp_field { |
37 | FLOWER_ICMP_FIELD_TYPE, | |
38 | FLOWER_ICMP_FIELD_CODE | |
39 | }; | |
40 | ||
30eb304e JP |
41 | static void explain(void) |
42 | { | |
512caeb2 SH |
43 | fprintf(stderr, |
44 | "Usage: ... flower [ MATCH-LIST ]\n" | |
45 | " [ skip_sw | skip_hw ]\n" | |
46 | " [ action ACTION-SPEC ] [ classid CLASSID ]\n" | |
47 | "\n" | |
48 | "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n" | |
49 | " MATCH := { indev DEV-NAME |\n" | |
50 | " vlan_id VID |\n" | |
51 | " vlan_prio PRIORITY |\n" | |
52 | " vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n" | |
c2078f8d SH |
53 | " dst_mac MASKED-LLADDR |\n" |
54 | " src_mac MASKED-LLADDR |\n" | |
eb3b5696 | 55 | " ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n" |
6ea2c2b1 OG |
56 | " ip_tos MASKED-IP_TOS |\n" |
57 | " ip_ttl MASKED-IP_TTL |\n" | |
7638ee13 BL |
58 | " mpls_label LABEL |\n" |
59 | " mpls_tc TC |\n" | |
60 | " mpls_bos BOS |\n" | |
61 | " mpls_ttl TTL |\n" | |
b2a1f740 SH |
62 | " dst_ip PREFIX |\n" |
63 | " src_ip PREFIX |\n" | |
512caeb2 | 64 | " dst_port PORT-NUMBER |\n" |
bb9b63b1 | 65 | " src_port PORT-NUMBER |\n" |
0c30d14d | 66 | " tcp_flags MASKED-TCP_FLAGS |\n" |
6374961a SH |
67 | " type MASKED-ICMP-TYPE |\n" |
68 | " code MASKED-ICMP-CODE |\n" | |
c7ec052b SH |
69 | " arp_tip IPV4-PREFIX |\n" |
70 | " arp_sip IPV4-PREFIX |\n" | |
f888f4e2 SH |
71 | " arp_op [ request | reply | OP ] |\n" |
72 | " arp_tha MASKED-LLADDR |\n" | |
73 | " arp_sha MASKED-LLADDR |\n" | |
10da5528 SH |
74 | " enc_dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" |
75 | " enc_src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" | |
bf73c650 | 76 | " enc_key_id [ KEY-ID ] |\n" |
08f66c80 | 77 | " ip_flags IP-FLAGS | \n" |
a5ae170e | 78 | " enc_dst_port [ port_number ] }\n" |
512caeb2 | 79 | " FILTERID := X:Y:Z\n" |
c2078f8d | 80 | " MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n" |
512caeb2 SH |
81 | " ACTION-SPEC := ... look at individual actions\n" |
82 | "\n" | |
328374dc | 83 | "NOTE: CLASSID, IP-PROTO are parsed as hexadecimal input.\n" |
512caeb2 SH |
84 | "NOTE: There can be only used one mask per one prio. If user needs\n" |
85 | " to specify different mask, he has to use different prio.\n"); | |
30eb304e JP |
86 | } |
87 | ||
88 | static int flower_parse_eth_addr(char *str, int addr_type, int mask_type, | |
89 | struct nlmsghdr *n) | |
90 | { | |
c2078f8d SH |
91 | int ret, err = -1; |
92 | char addr[ETH_ALEN], *slash; | |
93 | ||
94 | slash = strchr(str, '/'); | |
95 | if (slash) | |
96 | *slash = '\0'; | |
30eb304e JP |
97 | |
98 | ret = ll_addr_a2n(addr, sizeof(addr), str); | |
99 | if (ret < 0) | |
c2078f8d | 100 | goto err; |
30eb304e | 101 | addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr)); |
c2078f8d SH |
102 | |
103 | if (slash) { | |
104 | unsigned bits; | |
105 | ||
106 | if (!get_unsigned(&bits, slash + 1, 10)) { | |
107 | uint64_t mask; | |
108 | ||
109 | /* Extra 16 bit shift to push mac address into | |
110 | * high bits of uint64_t | |
111 | */ | |
112 | mask = htonll(0xffffffffffffULL << (16 + 48 - bits)); | |
113 | memcpy(addr, &mask, ETH_ALEN); | |
114 | } else { | |
115 | ret = ll_addr_a2n(addr, sizeof(addr), slash + 1); | |
116 | if (ret < 0) | |
117 | goto err; | |
118 | } | |
119 | } else { | |
120 | memset(addr, 0xff, ETH_ALEN); | |
121 | } | |
30eb304e | 122 | addattr_l(n, MAX_MSG, mask_type, addr, sizeof(addr)); |
c2078f8d SH |
123 | |
124 | err = 0; | |
125 | err: | |
126 | if (slash) | |
127 | *slash = '/'; | |
128 | return err; | |
30eb304e JP |
129 | } |
130 | ||
745d9172 | 131 | static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type, |
512caeb2 SH |
132 | __be16 *p_vlan_eth_type, |
133 | struct nlmsghdr *n) | |
745d9172 HHZ |
134 | { |
135 | __be16 vlan_eth_type; | |
136 | ||
137 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
138 | fprintf(stderr, |
139 | "Can't set \"vlan_ethtype\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
140 | return -1; |
141 | } | |
142 | ||
143 | if (ll_proto_a2n(&vlan_eth_type, str)) | |
144 | invarg("invalid vlan_ethtype", str); | |
145 | addattr16(n, MAX_MSG, type, vlan_eth_type); | |
146 | *p_vlan_eth_type = vlan_eth_type; | |
147 | return 0; | |
148 | } | |
149 | ||
08f66c80 PB |
150 | struct flag_to_string { |
151 | int flag; | |
152 | enum flower_matching_flags type; | |
153 | char *string; | |
154 | }; | |
22a8f019 | 155 | |
08f66c80 PB |
156 | static struct flag_to_string flags_str[] = { |
157 | { TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOWER_IP_FLAGS, "frag" }, | |
158 | }; | |
22a8f019 | 159 | |
08f66c80 PB |
160 | static int flower_parse_matching_flags(char *str, |
161 | enum flower_matching_flags type, | |
162 | __u32 *mtf, __u32 *mtf_mask) | |
163 | { | |
164 | char *token; | |
165 | bool no; | |
166 | bool found; | |
167 | int i; | |
22a8f019 | 168 | |
08f66c80 PB |
169 | token = strtok(str, "/"); |
170 | ||
171 | while (token) { | |
172 | if (!strncmp(token, "no", 2)) { | |
173 | no = true; | |
174 | token += 2; | |
175 | } else | |
176 | no = false; | |
177 | ||
178 | found = false; | |
179 | for (i = 0; i < ARRAY_SIZE(flags_str); i++) { | |
180 | if (type != flags_str[i].type) | |
181 | continue; | |
182 | ||
183 | if (!strcmp(token, flags_str[i].string)) { | |
184 | if (no) | |
185 | *mtf &= ~flags_str[i].flag; | |
186 | else | |
187 | *mtf |= flags_str[i].flag; | |
188 | ||
189 | *mtf_mask |= flags_str[i].flag; | |
190 | found = true; | |
191 | break; | |
192 | } | |
193 | } | |
194 | if (!found) | |
22a8f019 | 195 | return -1; |
08f66c80 PB |
196 | |
197 | token = strtok(NULL, "/"); | |
22a8f019 PB |
198 | } |
199 | ||
22a8f019 PB |
200 | return 0; |
201 | } | |
202 | ||
30eb304e JP |
203 | static int flower_parse_ip_proto(char *str, __be16 eth_type, int type, |
204 | __u8 *p_ip_proto, struct nlmsghdr *n) | |
205 | { | |
206 | int ret; | |
207 | __u8 ip_proto; | |
208 | ||
eb3b5696 SH |
209 | if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) |
210 | goto err; | |
211 | ||
30eb304e JP |
212 | if (matches(str, "tcp") == 0) { |
213 | ip_proto = IPPROTO_TCP; | |
214 | } else if (matches(str, "udp") == 0) { | |
215 | ip_proto = IPPROTO_UDP; | |
a1fb0d48 SH |
216 | } else if (matches(str, "sctp") == 0) { |
217 | ip_proto = IPPROTO_SCTP; | |
eb3b5696 SH |
218 | } else if (matches(str, "icmp") == 0) { |
219 | if (eth_type != htons(ETH_P_IP)) | |
220 | goto err; | |
221 | ip_proto = IPPROTO_ICMP; | |
222 | } else if (matches(str, "icmpv6") == 0) { | |
223 | if (eth_type != htons(ETH_P_IPV6)) | |
224 | goto err; | |
225 | ip_proto = IPPROTO_ICMPV6; | |
30eb304e JP |
226 | } else { |
227 | ret = get_u8(&ip_proto, str, 16); | |
228 | if (ret) | |
229 | return -1; | |
230 | } | |
231 | addattr8(n, MAX_MSG, type, ip_proto); | |
232 | *p_ip_proto = ip_proto; | |
233 | return 0; | |
eb3b5696 SH |
234 | |
235 | err: | |
236 | fprintf(stderr, "Illegal \"eth_type\" for ip proto\n"); | |
237 | return -1; | |
30eb304e JP |
238 | } |
239 | ||
f888f4e2 SH |
240 | static int __flower_parse_ip_addr(char *str, int family, |
241 | int addr4_type, int mask4_type, | |
242 | int addr6_type, int mask6_type, | |
243 | struct nlmsghdr *n) | |
30eb304e JP |
244 | { |
245 | int ret; | |
246 | inet_prefix addr; | |
30eb304e JP |
247 | int bits; |
248 | int i; | |
249 | ||
30eb304e JP |
250 | ret = get_prefix(&addr, str, family); |
251 | if (ret) | |
252 | return -1; | |
253 | ||
bb9b63b1 AV |
254 | if (family && (addr.family != family)) { |
255 | fprintf(stderr, "Illegal \"eth_type\" for ip address\n"); | |
30eb304e | 256 | return -1; |
bb9b63b1 | 257 | } |
30eb304e JP |
258 | |
259 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? addr4_type : addr6_type, | |
260 | addr.data, addr.bytelen); | |
261 | ||
262 | memset(addr.data, 0xff, addr.bytelen); | |
263 | bits = addr.bitlen; | |
264 | for (i = 0; i < addr.bytelen / 4; i++) { | |
265 | if (!bits) { | |
266 | addr.data[i] = 0; | |
267 | } else if (bits / 32 >= 1) { | |
268 | bits -= 32; | |
269 | } else { | |
270 | addr.data[i] <<= 32 - bits; | |
271 | addr.data[i] = htonl(addr.data[i]); | |
272 | bits = 0; | |
273 | } | |
274 | } | |
275 | ||
276 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? mask4_type : mask6_type, | |
277 | addr.data, addr.bytelen); | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
f888f4e2 SH |
282 | static int flower_parse_ip_addr(char *str, __be16 eth_type, |
283 | int addr4_type, int mask4_type, | |
284 | int addr6_type, int mask6_type, | |
285 | struct nlmsghdr *n) | |
286 | { | |
287 | int family; | |
288 | ||
289 | if (eth_type == htons(ETH_P_IP)) { | |
290 | family = AF_INET; | |
291 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
292 | family = AF_INET6; | |
293 | } else if (!eth_type) { | |
294 | family = AF_UNSPEC; | |
295 | } else { | |
296 | return -1; | |
297 | } | |
298 | ||
164a9ff4 RD |
299 | return __flower_parse_ip_addr(str, family, addr4_type, mask4_type, |
300 | addr6_type, mask6_type, n); | |
f888f4e2 SH |
301 | } |
302 | ||
303 | static bool flower_eth_type_arp(__be16 eth_type) | |
304 | { | |
305 | return eth_type == htons(ETH_P_ARP) || eth_type == htons(ETH_P_RARP); | |
306 | } | |
307 | ||
308 | static int flower_parse_arp_ip_addr(char *str, __be16 eth_type, | |
309 | int addr_type, int mask_type, | |
310 | struct nlmsghdr *n) | |
311 | { | |
312 | if (!flower_eth_type_arp(eth_type)) | |
313 | return -1; | |
314 | ||
315 | return __flower_parse_ip_addr(str, AF_INET, addr_type, mask_type, | |
316 | TCA_FLOWER_UNSPEC, TCA_FLOWER_UNSPEC, n); | |
317 | } | |
318 | ||
180136e5 SH |
319 | static int flower_parse_u8(char *str, int value_type, int mask_type, |
320 | int (*value_from_name)(const char *str, | |
321 | __u8 *value), | |
322 | bool (*value_validate)(__u8 value), | |
323 | struct nlmsghdr *n) | |
f888f4e2 SH |
324 | { |
325 | char *slash; | |
326 | int ret, err = -1; | |
180136e5 | 327 | __u8 value, mask; |
f888f4e2 SH |
328 | |
329 | slash = strchr(str, '/'); | |
330 | if (slash) | |
331 | *slash = '\0'; | |
332 | ||
180136e5 SH |
333 | ret = value_from_name ? value_from_name(str, &value) : -1; |
334 | if (ret < 0) { | |
f888f4e2 SH |
335 | ret = get_u8(&value, str, 10); |
336 | if (ret) | |
337 | goto err; | |
f888f4e2 SH |
338 | } |
339 | ||
180136e5 SH |
340 | if (value_validate && !value_validate(value)) |
341 | goto err; | |
342 | ||
f888f4e2 SH |
343 | if (slash) { |
344 | ret = get_u8(&mask, slash + 1, 10); | |
345 | if (ret) | |
346 | goto err; | |
347 | } | |
348 | else { | |
349 | mask = UINT8_MAX; | |
350 | } | |
351 | ||
180136e5 | 352 | addattr8(n, MAX_MSG, value_type, value); |
f888f4e2 SH |
353 | addattr8(n, MAX_MSG, mask_type, mask); |
354 | ||
355 | err = 0; | |
356 | err: | |
357 | if (slash) | |
358 | *slash = '/'; | |
359 | return err; | |
360 | } | |
361 | ||
9d36e54f SH |
362 | static const char *flower_print_arp_op_to_name(__u8 op) |
363 | { | |
364 | switch (op) { | |
365 | case ARPOP_REQUEST: | |
366 | return "request"; | |
367 | case ARPOP_REPLY: | |
368 | return "reply"; | |
369 | default: | |
370 | return NULL; | |
371 | } | |
372 | } | |
373 | ||
180136e5 SH |
374 | static int flower_arp_op_from_name(const char *name, __u8 *op) |
375 | { | |
376 | if (!strcmp(name, "request")) | |
377 | *op = ARPOP_REQUEST; | |
378 | else if (!strcmp(name, "reply")) | |
379 | *op = ARPOP_REPLY; | |
380 | else | |
381 | return -1; | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
386 | static bool flow_arp_op_validate(__u8 op) | |
387 | { | |
388 | return !op || op == ARPOP_REQUEST || op == ARPOP_REPLY; | |
389 | } | |
390 | ||
391 | static int flower_parse_arp_op(char *str, __be16 eth_type, | |
392 | int op_type, int mask_type, | |
393 | struct nlmsghdr *n) | |
394 | { | |
395 | if (!flower_eth_type_arp(eth_type)) | |
396 | return -1; | |
397 | ||
398 | return flower_parse_u8(str, op_type, mask_type, flower_arp_op_from_name, | |
399 | flow_arp_op_validate, n); | |
400 | } | |
401 | ||
eb3b5696 SH |
402 | static int flower_icmp_attr_type(__be16 eth_type, __u8 ip_proto, |
403 | enum flower_icmp_field field) | |
404 | { | |
405 | if (eth_type == htons(ETH_P_IP) && ip_proto == IPPROTO_ICMP) | |
406 | return field == FLOWER_ICMP_FIELD_CODE ? | |
407 | TCA_FLOWER_KEY_ICMPV4_CODE : | |
408 | TCA_FLOWER_KEY_ICMPV4_TYPE; | |
409 | else if (eth_type == htons(ETH_P_IPV6) && ip_proto == IPPROTO_ICMPV6) | |
410 | return field == FLOWER_ICMP_FIELD_CODE ? | |
411 | TCA_FLOWER_KEY_ICMPV6_CODE : | |
412 | TCA_FLOWER_KEY_ICMPV6_TYPE; | |
413 | ||
414 | return -1; | |
415 | } | |
416 | ||
6374961a SH |
417 | static int flower_icmp_attr_mask_type(__be16 eth_type, __u8 ip_proto, |
418 | enum flower_icmp_field field) | |
419 | { | |
420 | if (eth_type == htons(ETH_P_IP) && ip_proto == IPPROTO_ICMP) | |
421 | return field == FLOWER_ICMP_FIELD_CODE ? | |
422 | TCA_FLOWER_KEY_ICMPV4_CODE_MASK : | |
423 | TCA_FLOWER_KEY_ICMPV4_TYPE_MASK; | |
424 | else if (eth_type == htons(ETH_P_IPV6) && ip_proto == IPPROTO_ICMPV6) | |
425 | return field == FLOWER_ICMP_FIELD_CODE ? | |
426 | TCA_FLOWER_KEY_ICMPV6_CODE_MASK : | |
427 | TCA_FLOWER_KEY_ICMPV6_TYPE_MASK; | |
428 | ||
429 | return -1; | |
430 | } | |
431 | ||
eb3b5696 SH |
432 | static int flower_parse_icmp(char *str, __u16 eth_type, __u8 ip_proto, |
433 | enum flower_icmp_field field, struct nlmsghdr *n) | |
434 | { | |
6374961a | 435 | int value_type, mask_type; |
eb3b5696 | 436 | |
6374961a SH |
437 | value_type = flower_icmp_attr_type(eth_type, ip_proto, field); |
438 | mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto, field); | |
439 | if (value_type < 0 || mask_type < 0) | |
eb3b5696 SH |
440 | return -1; |
441 | ||
6374961a | 442 | return flower_parse_u8(str, value_type, mask_type, NULL, NULL, n); |
eb3b5696 SH |
443 | } |
444 | ||
6910d656 | 445 | static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint) |
30eb304e | 446 | { |
6bd5b80c | 447 | if (ip_proto == IPPROTO_TCP) |
6910d656 SH |
448 | return endpoint == FLOWER_ENDPOINT_SRC ? |
449 | TCA_FLOWER_KEY_TCP_SRC : | |
a1fb0d48 | 450 | TCA_FLOWER_KEY_TCP_DST; |
6bd5b80c | 451 | else if (ip_proto == IPPROTO_UDP) |
6910d656 SH |
452 | return endpoint == FLOWER_ENDPOINT_SRC ? |
453 | TCA_FLOWER_KEY_UDP_SRC : | |
a1fb0d48 | 454 | TCA_FLOWER_KEY_UDP_DST; |
6bd5b80c | 455 | else if (ip_proto == IPPROTO_SCTP) |
6910d656 SH |
456 | return endpoint == FLOWER_ENDPOINT_SRC ? |
457 | TCA_FLOWER_KEY_SCTP_SRC : | |
a1fb0d48 | 458 | TCA_FLOWER_KEY_SCTP_DST; |
6bd5b80c | 459 | else |
30eb304e | 460 | return -1; |
a1fb0d48 SH |
461 | } |
462 | ||
6910d656 SH |
463 | static int flower_parse_port(char *str, __u8 ip_proto, |
464 | enum flower_endpoint endpoint, | |
a1fb0d48 SH |
465 | struct nlmsghdr *n) |
466 | { | |
467 | int ret; | |
468 | int type; | |
469 | __be16 port; | |
470 | ||
6910d656 | 471 | type = flower_port_attr_type(ip_proto, endpoint); |
a1fb0d48 SH |
472 | if (type < 0) |
473 | return -1; | |
30eb304e | 474 | |
9f7401fa | 475 | ret = get_be16(&port, str, 10); |
30eb304e JP |
476 | if (ret) |
477 | return -1; | |
478 | ||
9f7401fa | 479 | addattr16(n, MAX_MSG, type, port); |
30eb304e JP |
480 | |
481 | return 0; | |
482 | } | |
483 | ||
0c30d14d JP |
484 | #define TCP_FLAGS_MAX_MASK 0xfff |
485 | ||
486 | static int flower_parse_tcp_flags(char *str, int flags_type, int mask_type, | |
487 | struct nlmsghdr *n) | |
488 | { | |
489 | char *slash; | |
490 | int ret, err = -1; | |
491 | __u16 flags; | |
492 | ||
493 | slash = strchr(str, '/'); | |
494 | if (slash) | |
495 | *slash = '\0'; | |
496 | ||
497 | ret = get_u16(&flags, str, 16); | |
498 | if (ret < 0 || flags & ~TCP_FLAGS_MAX_MASK) | |
499 | goto err; | |
500 | ||
501 | addattr16(n, MAX_MSG, flags_type, htons(flags)); | |
502 | ||
503 | if (slash) { | |
504 | ret = get_u16(&flags, slash + 1, 16); | |
505 | if (ret < 0 || flags & ~TCP_FLAGS_MAX_MASK) | |
506 | goto err; | |
507 | } else { | |
508 | flags = TCP_FLAGS_MAX_MASK; | |
509 | } | |
510 | addattr16(n, MAX_MSG, mask_type, htons(flags)); | |
511 | ||
512 | err = 0; | |
513 | err: | |
514 | if (slash) | |
515 | *slash = '/'; | |
516 | return err; | |
517 | } | |
518 | ||
6ea2c2b1 OG |
519 | static int flower_parse_ip_tos_ttl(char *str, int key_type, int mask_type, |
520 | struct nlmsghdr *n) | |
521 | { | |
522 | char *slash; | |
523 | int ret, err = -1; | |
524 | __u8 tos_ttl; | |
525 | ||
526 | slash = strchr(str, '/'); | |
527 | if (slash) | |
528 | *slash = '\0'; | |
529 | ||
530 | ret = get_u8(&tos_ttl, str, 10); | |
531 | if (ret < 0) | |
532 | ret = get_u8(&tos_ttl, str, 16); | |
533 | if (ret < 0) | |
534 | goto err; | |
535 | ||
536 | addattr8(n, MAX_MSG, key_type, tos_ttl); | |
537 | ||
538 | if (slash) { | |
539 | ret = get_u8(&tos_ttl, slash + 1, 16); | |
540 | if (ret < 0) | |
541 | goto err; | |
542 | } else { | |
543 | tos_ttl = 0xff; | |
544 | } | |
545 | addattr8(n, MAX_MSG, mask_type, tos_ttl); | |
546 | ||
547 | err = 0; | |
548 | err: | |
549 | if (slash) | |
550 | *slash = '/'; | |
551 | return err; | |
552 | } | |
553 | ||
bb9b63b1 AV |
554 | static int flower_parse_key_id(const char *str, int type, struct nlmsghdr *n) |
555 | { | |
556 | int ret; | |
557 | __be32 key_id; | |
558 | ||
559 | ret = get_be32(&key_id, str, 10); | |
560 | if (!ret) | |
561 | addattr32(n, MAX_MSG, type, key_id); | |
562 | ||
563 | return ret; | |
564 | } | |
565 | ||
41aa17ff HHZ |
566 | static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n) |
567 | { | |
568 | int ret; | |
569 | __be16 port; | |
570 | ||
571 | ret = get_be16(&port, str, 10); | |
572 | if (ret) | |
573 | return -1; | |
574 | ||
575 | addattr16(n, MAX_MSG, type, port); | |
576 | ||
577 | return 0; | |
578 | } | |
579 | ||
30eb304e JP |
580 | static int flower_parse_opt(struct filter_util *qu, char *handle, |
581 | int argc, char **argv, struct nlmsghdr *n) | |
582 | { | |
583 | int ret; | |
584 | struct tcmsg *t = NLMSG_DATA(n); | |
585 | struct rtattr *tail; | |
488b41d0 | 586 | __be16 eth_type = TC_H_MIN(t->tcm_info); |
745d9172 | 587 | __be16 vlan_ethtype = 0; |
30eb304e | 588 | __u8 ip_proto = 0xff; |
cfcabf18 | 589 | __u32 flags = 0; |
08f66c80 PB |
590 | __u32 mtf = 0; |
591 | __u32 mtf_mask = 0; | |
30eb304e | 592 | |
30eb304e JP |
593 | if (handle) { |
594 | ret = get_u32(&t->tcm_handle, handle, 0); | |
595 | if (ret) { | |
596 | fprintf(stderr, "Illegal \"handle\"\n"); | |
597 | return -1; | |
598 | } | |
599 | } | |
600 | ||
601 | tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); | |
602 | addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); | |
603 | ||
488b41d0 JHS |
604 | if (argc == 0) { |
605 | /*at minimal we will match all ethertype packets */ | |
606 | goto parse_done; | |
607 | } | |
608 | ||
30eb304e JP |
609 | while (argc > 0) { |
610 | if (matches(*argv, "classid") == 0 || | |
611 | matches(*argv, "flowid") == 0) { | |
32a121cb | 612 | unsigned int handle; |
30eb304e JP |
613 | |
614 | NEXT_ARG(); | |
615 | ret = get_tc_classid(&handle, *argv); | |
616 | if (ret) { | |
617 | fprintf(stderr, "Illegal \"classid\"\n"); | |
618 | return -1; | |
619 | } | |
620 | addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); | |
0d575c4d AN |
621 | } else if (matches(*argv, "hw_tc") == 0) { |
622 | unsigned int handle; | |
623 | __u32 tc; | |
624 | char *end; | |
625 | ||
626 | NEXT_ARG(); | |
627 | tc = strtoul(*argv, &end, 0); | |
628 | if (*end) { | |
629 | fprintf(stderr, "Illegal TC index\n"); | |
630 | return -1; | |
631 | } | |
632 | if (tc >= TC_QOPT_MAX_QUEUE) { | |
633 | fprintf(stderr, "TC index exceeds max range\n"); | |
634 | return -1; | |
635 | } | |
636 | handle = TC_H_MAKE(TC_H_MAJ(t->tcm_parent), | |
637 | TC_H_MIN(tc + TC_H_MIN_PRIORITY)); | |
638 | addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, | |
639 | sizeof(handle)); | |
08f66c80 | 640 | } else if (matches(*argv, "ip_flags") == 0) { |
22a8f019 PB |
641 | NEXT_ARG(); |
642 | ret = flower_parse_matching_flags(*argv, | |
08f66c80 PB |
643 | FLOWER_IP_FLAGS, |
644 | &mtf, | |
645 | &mtf_mask); | |
22a8f019 | 646 | if (ret < 0) { |
08f66c80 | 647 | fprintf(stderr, "Illegal \"ip_flags\"\n"); |
22a8f019 PB |
648 | return -1; |
649 | } | |
cfcabf18 AV |
650 | } else if (matches(*argv, "skip_hw") == 0) { |
651 | flags |= TCA_CLS_FLAGS_SKIP_HW; | |
652 | } else if (matches(*argv, "skip_sw") == 0) { | |
653 | flags |= TCA_CLS_FLAGS_SKIP_SW; | |
30eb304e | 654 | } else if (matches(*argv, "indev") == 0) { |
30eb304e | 655 | NEXT_ARG(); |
625df645 PS |
656 | if (check_ifname(*argv)) |
657 | invarg("\"indev\" not a valid ifname", *argv); | |
ee474849 | 658 | addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, *argv); |
745d9172 HHZ |
659 | } else if (matches(*argv, "vlan_id") == 0) { |
660 | __u16 vid; | |
661 | ||
662 | NEXT_ARG(); | |
663 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
664 | fprintf(stderr, |
665 | "Can't set \"vlan_id\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
666 | return -1; |
667 | } | |
668 | ret = get_u16(&vid, *argv, 10); | |
669 | if (ret < 0 || vid & ~0xfff) { | |
670 | fprintf(stderr, "Illegal \"vlan_id\"\n"); | |
671 | return -1; | |
672 | } | |
673 | addattr16(n, MAX_MSG, TCA_FLOWER_KEY_VLAN_ID, vid); | |
674 | } else if (matches(*argv, "vlan_prio") == 0) { | |
675 | __u8 vlan_prio; | |
676 | ||
677 | NEXT_ARG(); | |
678 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
679 | fprintf(stderr, |
680 | "Can't set \"vlan_prio\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
681 | return -1; |
682 | } | |
683 | ret = get_u8(&vlan_prio, *argv, 10); | |
684 | if (ret < 0 || vlan_prio & ~0x7) { | |
685 | fprintf(stderr, "Illegal \"vlan_prio\"\n"); | |
686 | return -1; | |
687 | } | |
512caeb2 SH |
688 | addattr8(n, MAX_MSG, |
689 | TCA_FLOWER_KEY_VLAN_PRIO, vlan_prio); | |
745d9172 HHZ |
690 | } else if (matches(*argv, "vlan_ethtype") == 0) { |
691 | NEXT_ARG(); | |
692 | ret = flower_parse_vlan_eth_type(*argv, eth_type, | |
512caeb2 SH |
693 | TCA_FLOWER_KEY_VLAN_ETH_TYPE, |
694 | &vlan_ethtype, n); | |
745d9172 HHZ |
695 | if (ret < 0) |
696 | return -1; | |
7638ee13 BL |
697 | } else if (matches(*argv, "mpls_label") == 0) { |
698 | __u32 label; | |
699 | ||
700 | NEXT_ARG(); | |
701 | if (eth_type != htons(ETH_P_MPLS_UC) && | |
702 | eth_type != htons(ETH_P_MPLS_MC)) { | |
703 | fprintf(stderr, | |
704 | "Can't set \"mpls_label\" if ethertype isn't MPLS\n"); | |
705 | return -1; | |
706 | } | |
707 | ret = get_u32(&label, *argv, 10); | |
708 | if (ret < 0 || label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) { | |
709 | fprintf(stderr, "Illegal \"mpls_label\"\n"); | |
710 | return -1; | |
711 | } | |
712 | addattr32(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_LABEL, label); | |
713 | } else if (matches(*argv, "mpls_tc") == 0) { | |
714 | __u8 tc; | |
715 | ||
716 | NEXT_ARG(); | |
717 | if (eth_type != htons(ETH_P_MPLS_UC) && | |
718 | eth_type != htons(ETH_P_MPLS_MC)) { | |
719 | fprintf(stderr, | |
720 | "Can't set \"mpls_tc\" if ethertype isn't MPLS\n"); | |
721 | return -1; | |
722 | } | |
723 | ret = get_u8(&tc, *argv, 10); | |
724 | if (ret < 0 || tc & ~(MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT)) { | |
725 | fprintf(stderr, "Illegal \"mpls_tc\"\n"); | |
726 | return -1; | |
727 | } | |
728 | addattr8(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_TC, tc); | |
729 | } else if (matches(*argv, "mpls_bos") == 0) { | |
730 | __u8 bos; | |
731 | ||
732 | NEXT_ARG(); | |
733 | if (eth_type != htons(ETH_P_MPLS_UC) && | |
734 | eth_type != htons(ETH_P_MPLS_MC)) { | |
735 | fprintf(stderr, | |
736 | "Can't set \"mpls_bos\" if ethertype isn't MPLS\n"); | |
737 | return -1; | |
738 | } | |
739 | ret = get_u8(&bos, *argv, 10); | |
740 | if (ret < 0 || bos & ~(MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT)) { | |
741 | fprintf(stderr, "Illegal \"mpls_bos\"\n"); | |
742 | return -1; | |
743 | } | |
744 | addattr8(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_BOS, bos); | |
745 | } else if (matches(*argv, "mpls_ttl") == 0) { | |
746 | __u8 ttl; | |
747 | ||
748 | NEXT_ARG(); | |
749 | if (eth_type != htons(ETH_P_MPLS_UC) && | |
750 | eth_type != htons(ETH_P_MPLS_MC)) { | |
751 | fprintf(stderr, | |
752 | "Can't set \"mpls_ttl\" if ethertype isn't MPLS\n"); | |
753 | return -1; | |
754 | } | |
755 | ret = get_u8(&ttl, *argv, 10); | |
756 | if (ret < 0 || ttl & ~(MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT)) { | |
757 | fprintf(stderr, "Illegal \"mpls_ttl\"\n"); | |
758 | return -1; | |
759 | } | |
760 | addattr8(n, MAX_MSG, TCA_FLOWER_KEY_MPLS_TTL, ttl); | |
30eb304e JP |
761 | } else if (matches(*argv, "dst_mac") == 0) { |
762 | NEXT_ARG(); | |
763 | ret = flower_parse_eth_addr(*argv, | |
764 | TCA_FLOWER_KEY_ETH_DST, | |
765 | TCA_FLOWER_KEY_ETH_DST_MASK, | |
766 | n); | |
767 | if (ret < 0) { | |
768 | fprintf(stderr, "Illegal \"dst_mac\"\n"); | |
769 | return -1; | |
770 | } | |
771 | } else if (matches(*argv, "src_mac") == 0) { | |
772 | NEXT_ARG(); | |
773 | ret = flower_parse_eth_addr(*argv, | |
774 | TCA_FLOWER_KEY_ETH_SRC, | |
775 | TCA_FLOWER_KEY_ETH_SRC_MASK, | |
776 | n); | |
777 | if (ret < 0) { | |
778 | fprintf(stderr, "Illegal \"src_mac\"\n"); | |
779 | return -1; | |
780 | } | |
30eb304e JP |
781 | } else if (matches(*argv, "ip_proto") == 0) { |
782 | NEXT_ARG(); | |
745d9172 HHZ |
783 | ret = flower_parse_ip_proto(*argv, vlan_ethtype ? |
784 | vlan_ethtype : eth_type, | |
30eb304e JP |
785 | TCA_FLOWER_KEY_IP_PROTO, |
786 | &ip_proto, n); | |
787 | if (ret < 0) { | |
788 | fprintf(stderr, "Illegal \"ip_proto\"\n"); | |
789 | return -1; | |
790 | } | |
6ea2c2b1 OG |
791 | } else if (matches(*argv, "ip_tos") == 0) { |
792 | NEXT_ARG(); | |
793 | ret = flower_parse_ip_tos_ttl(*argv, | |
794 | TCA_FLOWER_KEY_IP_TOS, | |
795 | TCA_FLOWER_KEY_IP_TOS_MASK, | |
796 | n); | |
797 | if (ret < 0) { | |
798 | fprintf(stderr, "Illegal \"ip_tos\"\n"); | |
799 | return -1; | |
800 | } | |
801 | } else if (matches(*argv, "ip_ttl") == 0) { | |
802 | NEXT_ARG(); | |
803 | ret = flower_parse_ip_tos_ttl(*argv, | |
804 | TCA_FLOWER_KEY_IP_TTL, | |
805 | TCA_FLOWER_KEY_IP_TTL_MASK, | |
806 | n); | |
807 | if (ret < 0) { | |
808 | fprintf(stderr, "Illegal \"ip_ttl\"\n"); | |
809 | return -1; | |
810 | } | |
30eb304e JP |
811 | } else if (matches(*argv, "dst_ip") == 0) { |
812 | NEXT_ARG(); | |
745d9172 HHZ |
813 | ret = flower_parse_ip_addr(*argv, vlan_ethtype ? |
814 | vlan_ethtype : eth_type, | |
30eb304e JP |
815 | TCA_FLOWER_KEY_IPV4_DST, |
816 | TCA_FLOWER_KEY_IPV4_DST_MASK, | |
817 | TCA_FLOWER_KEY_IPV6_DST, | |
818 | TCA_FLOWER_KEY_IPV6_DST_MASK, | |
819 | n); | |
820 | if (ret < 0) { | |
821 | fprintf(stderr, "Illegal \"dst_ip\"\n"); | |
822 | return -1; | |
823 | } | |
824 | } else if (matches(*argv, "src_ip") == 0) { | |
825 | NEXT_ARG(); | |
745d9172 HHZ |
826 | ret = flower_parse_ip_addr(*argv, vlan_ethtype ? |
827 | vlan_ethtype : eth_type, | |
30eb304e JP |
828 | TCA_FLOWER_KEY_IPV4_SRC, |
829 | TCA_FLOWER_KEY_IPV4_SRC_MASK, | |
830 | TCA_FLOWER_KEY_IPV6_SRC, | |
831 | TCA_FLOWER_KEY_IPV6_SRC_MASK, | |
832 | n); | |
833 | if (ret < 0) { | |
834 | fprintf(stderr, "Illegal \"src_ip\"\n"); | |
835 | return -1; | |
836 | } | |
837 | } else if (matches(*argv, "dst_port") == 0) { | |
838 | NEXT_ARG(); | |
6910d656 SH |
839 | ret = flower_parse_port(*argv, ip_proto, |
840 | FLOWER_ENDPOINT_DST, n); | |
30eb304e JP |
841 | if (ret < 0) { |
842 | fprintf(stderr, "Illegal \"dst_port\"\n"); | |
843 | return -1; | |
844 | } | |
845 | } else if (matches(*argv, "src_port") == 0) { | |
846 | NEXT_ARG(); | |
6910d656 SH |
847 | ret = flower_parse_port(*argv, ip_proto, |
848 | FLOWER_ENDPOINT_SRC, n); | |
30eb304e JP |
849 | if (ret < 0) { |
850 | fprintf(stderr, "Illegal \"src_port\"\n"); | |
851 | return -1; | |
852 | } | |
0c30d14d JP |
853 | } else if (matches(*argv, "tcp_flags") == 0) { |
854 | NEXT_ARG(); | |
855 | ret = flower_parse_tcp_flags(*argv, | |
856 | TCA_FLOWER_KEY_TCP_FLAGS, | |
857 | TCA_FLOWER_KEY_TCP_FLAGS_MASK, | |
858 | n); | |
859 | if (ret < 0) { | |
860 | fprintf(stderr, "Illegal \"tcp_flags\"\n"); | |
861 | return -1; | |
862 | } | |
eb3b5696 SH |
863 | } else if (matches(*argv, "type") == 0) { |
864 | NEXT_ARG(); | |
865 | ret = flower_parse_icmp(*argv, eth_type, ip_proto, | |
866 | FLOWER_ICMP_FIELD_TYPE, n); | |
867 | if (ret < 0) { | |
868 | fprintf(stderr, "Illegal \"icmp type\"\n"); | |
869 | return -1; | |
870 | } | |
871 | } else if (matches(*argv, "code") == 0) { | |
872 | NEXT_ARG(); | |
873 | ret = flower_parse_icmp(*argv, eth_type, ip_proto, | |
874 | FLOWER_ICMP_FIELD_CODE, n); | |
875 | if (ret < 0) { | |
876 | fprintf(stderr, "Illegal \"icmp code\"\n"); | |
877 | return -1; | |
878 | } | |
f888f4e2 SH |
879 | } else if (matches(*argv, "arp_tip") == 0) { |
880 | NEXT_ARG(); | |
881 | ret = flower_parse_arp_ip_addr(*argv, vlan_ethtype ? | |
882 | vlan_ethtype : eth_type, | |
883 | TCA_FLOWER_KEY_ARP_TIP, | |
884 | TCA_FLOWER_KEY_ARP_TIP_MASK, | |
885 | n); | |
886 | if (ret < 0) { | |
887 | fprintf(stderr, "Illegal \"arp_tip\"\n"); | |
888 | return -1; | |
889 | } | |
890 | } else if (matches(*argv, "arp_sip") == 0) { | |
891 | NEXT_ARG(); | |
892 | ret = flower_parse_arp_ip_addr(*argv, vlan_ethtype ? | |
893 | vlan_ethtype : eth_type, | |
894 | TCA_FLOWER_KEY_ARP_SIP, | |
895 | TCA_FLOWER_KEY_ARP_SIP_MASK, | |
896 | n); | |
897 | if (ret < 0) { | |
898 | fprintf(stderr, "Illegal \"arp_sip\"\n"); | |
899 | return -1; | |
900 | } | |
901 | } else if (matches(*argv, "arp_op") == 0) { | |
902 | NEXT_ARG(); | |
903 | ret = flower_parse_arp_op(*argv, vlan_ethtype ? | |
904 | vlan_ethtype : eth_type, | |
905 | TCA_FLOWER_KEY_ARP_OP, | |
906 | TCA_FLOWER_KEY_ARP_OP_MASK, | |
907 | n); | |
908 | if (ret < 0) { | |
909 | fprintf(stderr, "Illegal \"arp_op\"\n"); | |
910 | return -1; | |
911 | } | |
912 | } else if (matches(*argv, "arp_tha") == 0) { | |
913 | NEXT_ARG(); | |
914 | ret = flower_parse_eth_addr(*argv, | |
915 | TCA_FLOWER_KEY_ARP_THA, | |
916 | TCA_FLOWER_KEY_ARP_THA_MASK, | |
917 | n); | |
918 | if (ret < 0) { | |
919 | fprintf(stderr, "Illegal \"arp_tha\"\n"); | |
920 | return -1; | |
921 | } | |
922 | } else if (matches(*argv, "arp_sha") == 0) { | |
923 | NEXT_ARG(); | |
924 | ret = flower_parse_eth_addr(*argv, | |
925 | TCA_FLOWER_KEY_ARP_SHA, | |
926 | TCA_FLOWER_KEY_ARP_SHA_MASK, | |
927 | n); | |
928 | if (ret < 0) { | |
929 | fprintf(stderr, "Illegal \"arp_sha\"\n"); | |
930 | return -1; | |
931 | } | |
bb9b63b1 AV |
932 | } else if (matches(*argv, "enc_dst_ip") == 0) { |
933 | NEXT_ARG(); | |
934 | ret = flower_parse_ip_addr(*argv, 0, | |
935 | TCA_FLOWER_KEY_ENC_IPV4_DST, | |
936 | TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, | |
937 | TCA_FLOWER_KEY_ENC_IPV6_DST, | |
938 | TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, | |
939 | n); | |
940 | if (ret < 0) { | |
941 | fprintf(stderr, "Illegal \"enc_dst_ip\"\n"); | |
942 | return -1; | |
943 | } | |
944 | } else if (matches(*argv, "enc_src_ip") == 0) { | |
945 | NEXT_ARG(); | |
946 | ret = flower_parse_ip_addr(*argv, 0, | |
947 | TCA_FLOWER_KEY_ENC_IPV4_SRC, | |
948 | TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, | |
949 | TCA_FLOWER_KEY_ENC_IPV6_SRC, | |
950 | TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, | |
951 | n); | |
952 | if (ret < 0) { | |
953 | fprintf(stderr, "Illegal \"enc_src_ip\"\n"); | |
954 | return -1; | |
955 | } | |
956 | } else if (matches(*argv, "enc_key_id") == 0) { | |
957 | NEXT_ARG(); | |
958 | ret = flower_parse_key_id(*argv, | |
959 | TCA_FLOWER_KEY_ENC_KEY_ID, n); | |
960 | if (ret < 0) { | |
961 | fprintf(stderr, "Illegal \"enc_key_id\"\n"); | |
962 | return -1; | |
963 | } | |
41aa17ff HHZ |
964 | } else if (matches(*argv, "enc_dst_port") == 0) { |
965 | NEXT_ARG(); | |
966 | ret = flower_parse_enc_port(*argv, | |
967 | TCA_FLOWER_KEY_ENC_UDP_DST_PORT, n); | |
968 | if (ret < 0) { | |
969 | fprintf(stderr, "Illegal \"enc_dst_port\"\n"); | |
970 | return -1; | |
971 | } | |
30eb304e JP |
972 | } else if (matches(*argv, "action") == 0) { |
973 | NEXT_ARG(); | |
974 | ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n); | |
975 | if (ret) { | |
976 | fprintf(stderr, "Illegal \"action\"\n"); | |
977 | return -1; | |
978 | } | |
979 | continue; | |
980 | } else if (strcmp(*argv, "help") == 0) { | |
981 | explain(); | |
982 | return -1; | |
983 | } else { | |
984 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
985 | explain(); | |
986 | return -1; | |
987 | } | |
988 | argc--; argv++; | |
989 | } | |
990 | ||
488b41d0 | 991 | parse_done: |
c85609b2 RD |
992 | ret = addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags); |
993 | if (ret) | |
994 | return ret; | |
cfcabf18 | 995 | |
08f66c80 PB |
996 | if (mtf_mask) { |
997 | ret = addattr32(n, MAX_MSG, TCA_FLOWER_KEY_FLAGS, htonl(mtf)); | |
998 | if (ret) | |
999 | return ret; | |
1000 | ||
1001 | ret = addattr32(n, MAX_MSG, TCA_FLOWER_KEY_FLAGS_MASK, htonl(mtf_mask)); | |
1002 | if (ret) | |
1003 | return ret; | |
1004 | } | |
1005 | ||
4f7d406f BL |
1006 | if (eth_type != htons(ETH_P_ALL)) { |
1007 | ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type); | |
1008 | if (ret) | |
1009 | return ret; | |
1010 | } | |
488b41d0 | 1011 | |
32a121cb | 1012 | tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; |
30eb304e JP |
1013 | |
1014 | return 0; | |
1015 | } | |
1016 | ||
1017 | static int __mask_bits(char *addr, size_t len) | |
1018 | { | |
1019 | int bits = 0; | |
1020 | bool hole = false; | |
1021 | int i; | |
1022 | int j; | |
1023 | ||
1024 | for (i = 0; i < len; i++, addr++) { | |
1025 | for (j = 7; j >= 0; j--) { | |
1026 | if (((*addr) >> j) & 0x1) { | |
1027 | if (hole) | |
1028 | return -1; | |
1029 | bits++; | |
1030 | } else if (bits) { | |
1031 | hole = true; | |
1032 | } else{ | |
1033 | return -1; | |
1034 | } | |
1035 | } | |
1036 | } | |
1037 | return bits; | |
1038 | } | |
1039 | ||
e28b88a4 | 1040 | static void flower_print_eth_addr(char *name, struct rtattr *addr_attr, |
30eb304e JP |
1041 | struct rtattr *mask_attr) |
1042 | { | |
e28b88a4 JP |
1043 | SPRINT_BUF(namefrm); |
1044 | SPRINT_BUF(out); | |
30eb304e | 1045 | SPRINT_BUF(b1); |
e28b88a4 | 1046 | size_t done; |
30eb304e JP |
1047 | int bits; |
1048 | ||
1049 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN) | |
1050 | return; | |
e28b88a4 JP |
1051 | done = sprintf(out, "%s", |
1052 | ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN, | |
1053 | 0, b1, sizeof(b1))); | |
1054 | if (mask_attr && RTA_PAYLOAD(mask_attr) == ETH_ALEN) { | |
1055 | bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN); | |
1056 | if (bits < 0) | |
1057 | sprintf(out + done, "/%s", | |
1058 | ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN, | |
1059 | 0, b1, sizeof(b1))); | |
1060 | else if (bits < ETH_ALEN * 8) | |
1061 | sprintf(out + done, "/%d", bits); | |
1062 | } | |
1063 | ||
1064 | sprintf(namefrm, "\n %s %%s", name); | |
1065 | print_string(PRINT_ANY, name, namefrm, out); | |
30eb304e JP |
1066 | } |
1067 | ||
e28b88a4 | 1068 | static void flower_print_eth_type(__be16 *p_eth_type, |
30eb304e JP |
1069 | struct rtattr *eth_type_attr) |
1070 | { | |
e28b88a4 | 1071 | SPRINT_BUF(out); |
30eb304e JP |
1072 | __be16 eth_type; |
1073 | ||
1074 | if (!eth_type_attr) | |
1075 | return; | |
1076 | ||
1077 | eth_type = rta_getattr_u16(eth_type_attr); | |
30eb304e | 1078 | if (eth_type == htons(ETH_P_IP)) |
e28b88a4 | 1079 | sprintf(out, "ipv4"); |
30eb304e | 1080 | else if (eth_type == htons(ETH_P_IPV6)) |
e28b88a4 | 1081 | sprintf(out, "ipv6"); |
f888f4e2 | 1082 | else if (eth_type == htons(ETH_P_ARP)) |
e28b88a4 | 1083 | sprintf(out, "arp"); |
f888f4e2 | 1084 | else if (eth_type == htons(ETH_P_RARP)) |
e28b88a4 | 1085 | sprintf(out, "rarp"); |
30eb304e | 1086 | else |
e28b88a4 JP |
1087 | sprintf(out, "%04x", ntohs(eth_type)); |
1088 | ||
1089 | print_string(PRINT_ANY, "eth_type", "\n eth_type %s", out); | |
30eb304e JP |
1090 | *p_eth_type = eth_type; |
1091 | } | |
1092 | ||
e28b88a4 | 1093 | static void flower_print_ip_proto(__u8 *p_ip_proto, |
30eb304e JP |
1094 | struct rtattr *ip_proto_attr) |
1095 | { | |
e28b88a4 | 1096 | SPRINT_BUF(out); |
30eb304e JP |
1097 | __u8 ip_proto; |
1098 | ||
1099 | if (!ip_proto_attr) | |
1100 | return; | |
1101 | ||
1102 | ip_proto = rta_getattr_u8(ip_proto_attr); | |
30eb304e | 1103 | if (ip_proto == IPPROTO_TCP) |
e28b88a4 | 1104 | sprintf(out, "tcp"); |
30eb304e | 1105 | else if (ip_proto == IPPROTO_UDP) |
e28b88a4 | 1106 | sprintf(out, "udp"); |
a1fb0d48 | 1107 | else if (ip_proto == IPPROTO_SCTP) |
e28b88a4 | 1108 | sprintf(out, "sctp"); |
eb3b5696 | 1109 | else if (ip_proto == IPPROTO_ICMP) |
e28b88a4 | 1110 | sprintf(out, "icmp"); |
eb3b5696 | 1111 | else if (ip_proto == IPPROTO_ICMPV6) |
e28b88a4 | 1112 | sprintf(out, "icmpv6"); |
30eb304e | 1113 | else |
e28b88a4 JP |
1114 | sprintf(out, "%02x", ip_proto); |
1115 | ||
1116 | print_string(PRINT_ANY, "ip_proto", "\n ip_proto %s", out); | |
30eb304e JP |
1117 | *p_ip_proto = ip_proto; |
1118 | } | |
1119 | ||
e28b88a4 | 1120 | static void flower_print_ip_attr(char *name, struct rtattr *key_attr, |
6ea2c2b1 OG |
1121 | struct rtattr *mask_attr) |
1122 | { | |
e28b88a4 JP |
1123 | SPRINT_BUF(namefrm); |
1124 | SPRINT_BUF(out); | |
1125 | size_t done; | |
1126 | ||
6ea2c2b1 OG |
1127 | if (!key_attr) |
1128 | return; | |
1129 | ||
e28b88a4 JP |
1130 | done = sprintf(out, "%x", rta_getattr_u8(key_attr)); |
1131 | if (mask_attr) | |
1132 | sprintf(out + done, "/%x", rta_getattr_u8(mask_attr)); | |
1133 | ||
1134 | sprintf(namefrm, "\n %s %%x", name); | |
1135 | print_string(PRINT_ANY, name, namefrm, out); | |
6ea2c2b1 OG |
1136 | } |
1137 | ||
e28b88a4 | 1138 | static void flower_print_matching_flags(char *name, |
08f66c80 | 1139 | enum flower_matching_flags type, |
22a8f019 PB |
1140 | struct rtattr *attr, |
1141 | struct rtattr *mask_attr) | |
1142 | { | |
08f66c80 PB |
1143 | int i; |
1144 | int count = 0; | |
1145 | __u32 mtf; | |
1146 | __u32 mtf_mask; | |
1147 | ||
22a8f019 PB |
1148 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != 4) |
1149 | return; | |
1150 | ||
08f66c80 PB |
1151 | mtf = ntohl(rta_getattr_u32(attr)); |
1152 | mtf_mask = ntohl(rta_getattr_u32(mask_attr)); | |
1153 | ||
1154 | for (i = 0; i < ARRAY_SIZE(flags_str); i++) { | |
1155 | if (type != flags_str[i].type) | |
1156 | continue; | |
1157 | if (mtf_mask & flags_str[i].flag) { | |
e28b88a4 JP |
1158 | if (++count == 1) { |
1159 | print_string(PRINT_FP, NULL, "\n %s ", name); | |
1160 | open_json_object(name); | |
1161 | } else { | |
1162 | print_string(PRINT_FP, NULL, "/", NULL); | |
1163 | } | |
08f66c80 | 1164 | |
e28b88a4 JP |
1165 | print_bool(PRINT_JSON, flags_str[i].string, NULL, |
1166 | mtf & flags_str[i].flag); | |
08f66c80 | 1167 | if (mtf & flags_str[i].flag) |
e28b88a4 JP |
1168 | print_string(PRINT_FP, NULL, "%s", |
1169 | flags_str[i].string); | |
08f66c80 | 1170 | else |
e28b88a4 JP |
1171 | print_string(PRINT_FP, NULL, "no%s", |
1172 | flags_str[i].string); | |
08f66c80 PB |
1173 | } |
1174 | } | |
e28b88a4 JP |
1175 | if (count) |
1176 | close_json_object(); | |
22a8f019 PB |
1177 | } |
1178 | ||
e28b88a4 | 1179 | static void flower_print_ip_addr(char *name, __be16 eth_type, |
30eb304e JP |
1180 | struct rtattr *addr4_attr, |
1181 | struct rtattr *mask4_attr, | |
1182 | struct rtattr *addr6_attr, | |
1183 | struct rtattr *mask6_attr) | |
1184 | { | |
30eb304e JP |
1185 | struct rtattr *addr_attr; |
1186 | struct rtattr *mask_attr; | |
e28b88a4 JP |
1187 | SPRINT_BUF(namefrm); |
1188 | SPRINT_BUF(out); | |
1189 | size_t done; | |
30eb304e JP |
1190 | int family; |
1191 | size_t len; | |
1192 | int bits; | |
1193 | ||
1194 | if (eth_type == htons(ETH_P_IP)) { | |
1195 | family = AF_INET; | |
1196 | addr_attr = addr4_attr; | |
1197 | mask_attr = mask4_attr; | |
1198 | len = 4; | |
1199 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
1200 | family = AF_INET6; | |
1201 | addr_attr = addr6_attr; | |
1202 | mask_attr = mask6_attr; | |
1203 | len = 16; | |
1204 | } else { | |
1205 | return; | |
1206 | } | |
1207 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != len) | |
1208 | return; | |
30eb304e JP |
1209 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != len) |
1210 | return; | |
e28b88a4 | 1211 | done = sprintf(out, "%s", rt_addr_n2a_rta(family, addr_attr)); |
30eb304e JP |
1212 | bits = __mask_bits(RTA_DATA(mask_attr), len); |
1213 | if (bits < 0) | |
e28b88a4 | 1214 | sprintf(out + done, "/%s", rt_addr_n2a_rta(family, mask_attr)); |
30eb304e | 1215 | else if (bits < len * 8) |
e28b88a4 JP |
1216 | sprintf(out + done, "/%d", bits); |
1217 | ||
1218 | sprintf(namefrm, "\n %s %%s", name); | |
1219 | print_string(PRINT_ANY, name, namefrm, out); | |
30eb304e | 1220 | } |
e28b88a4 | 1221 | static void flower_print_ip4_addr(char *name, struct rtattr *addr_attr, |
f888f4e2 SH |
1222 | struct rtattr *mask_attr) |
1223 | { | |
e28b88a4 | 1224 | return flower_print_ip_addr(name, htons(ETH_P_IP), |
f888f4e2 SH |
1225 | addr_attr, mask_attr, 0, 0); |
1226 | } | |
30eb304e | 1227 | |
e28b88a4 | 1228 | static void flower_print_port(char *name, struct rtattr *attr) |
30eb304e | 1229 | { |
e28b88a4 JP |
1230 | SPRINT_BUF(namefrm); |
1231 | ||
1232 | if (!attr) | |
1233 | return; | |
1234 | ||
1235 | sprintf(namefrm,"\n %s %%u", name); | |
1236 | print_uint(PRINT_ANY, name, namefrm, rta_getattr_be16(attr)); | |
30eb304e JP |
1237 | } |
1238 | ||
e28b88a4 JP |
1239 | static void flower_print_tcp_flags(char *name, struct rtattr *flags_attr, |
1240 | struct rtattr *mask_attr) | |
0c30d14d | 1241 | { |
e28b88a4 JP |
1242 | SPRINT_BUF(namefrm); |
1243 | SPRINT_BUF(out); | |
1244 | size_t done; | |
1245 | ||
0c30d14d JP |
1246 | if (!flags_attr) |
1247 | return; | |
e28b88a4 JP |
1248 | |
1249 | done = sprintf(out, "%x", rta_getattr_be16(flags_attr)); | |
1250 | if (mask_attr) | |
1251 | sprintf(out + done, "%x", rta_getattr_be16(flags_attr)); | |
1252 | ||
1253 | sprintf(namefrm, "\n %s %%s", name); | |
1254 | print_string(PRINT_ANY, name, namefrm, out); | |
0c30d14d JP |
1255 | } |
1256 | ||
1257 | ||
e28b88a4 | 1258 | static void flower_print_key_id(const char *name, struct rtattr *attr) |
bb9b63b1 | 1259 | { |
e28b88a4 JP |
1260 | SPRINT_BUF(namefrm); |
1261 | ||
1262 | if (!attr) | |
1263 | return; | |
1264 | ||
1265 | sprintf(namefrm,"\n %s %%u", name); | |
1266 | print_uint(PRINT_ANY, name, namefrm, rta_getattr_be32(attr)); | |
bb9b63b1 AV |
1267 | } |
1268 | ||
e28b88a4 | 1269 | static void flower_print_masked_u8(const char *name, struct rtattr *attr, |
9d36e54f SH |
1270 | struct rtattr *mask_attr, |
1271 | const char *(*value_to_str)(__u8 value)) | |
f888f4e2 | 1272 | { |
9d36e54f SH |
1273 | const char *value_str = NULL; |
1274 | __u8 value, mask; | |
e28b88a4 JP |
1275 | SPRINT_BUF(namefrm); |
1276 | SPRINT_BUF(out); | |
1277 | size_t done; | |
f888f4e2 | 1278 | |
9d36e54f | 1279 | if (!attr) |
f888f4e2 SH |
1280 | return; |
1281 | ||
9d36e54f | 1282 | value = rta_getattr_u8(attr); |
f888f4e2 | 1283 | mask = mask_attr ? rta_getattr_u8(mask_attr) : UINT8_MAX; |
9d36e54f SH |
1284 | if (mask == UINT8_MAX && value_to_str) |
1285 | value_str = value_to_str(value); | |
f888f4e2 | 1286 | |
9d36e54f | 1287 | if (value_str) |
e28b88a4 | 1288 | done = sprintf(out, "%s", value_str); |
f888f4e2 | 1289 | else |
e28b88a4 | 1290 | done = sprintf(out, "%d", value); |
f888f4e2 SH |
1291 | |
1292 | if (mask != UINT8_MAX) | |
e28b88a4 JP |
1293 | sprintf(out + done, "/%d", mask); |
1294 | ||
1295 | sprintf(namefrm,"\n %s %%s", name); | |
1296 | print_string(PRINT_ANY, name, namefrm, out); | |
f888f4e2 SH |
1297 | } |
1298 | ||
e28b88a4 | 1299 | static void flower_print_u8(const char *name, struct rtattr *attr) |
7638ee13 | 1300 | { |
e28b88a4 | 1301 | flower_print_masked_u8(name, attr, NULL, NULL); |
7638ee13 BL |
1302 | } |
1303 | ||
e28b88a4 | 1304 | static void flower_print_u32(const char *name, struct rtattr *attr) |
7638ee13 | 1305 | { |
e28b88a4 | 1306 | SPRINT_BUF(namefrm); |
7638ee13 BL |
1307 | |
1308 | if (!attr) | |
1309 | return; | |
1310 | ||
e28b88a4 JP |
1311 | sprintf(namefrm,"\n %s %%u", name); |
1312 | print_uint(PRINT_ANY, name, namefrm, rta_getattr_u32(attr)); | |
7638ee13 BL |
1313 | } |
1314 | ||
e28b88a4 | 1315 | static void flower_print_arp_op(const char *name, |
9d36e54f SH |
1316 | struct rtattr *op_attr, |
1317 | struct rtattr *mask_attr) | |
1318 | { | |
e28b88a4 | 1319 | flower_print_masked_u8(name, op_attr, mask_attr, |
9d36e54f SH |
1320 | flower_print_arp_op_to_name); |
1321 | } | |
1322 | ||
30eb304e JP |
1323 | static int flower_print_opt(struct filter_util *qu, FILE *f, |
1324 | struct rtattr *opt, __u32 handle) | |
1325 | { | |
1326 | struct rtattr *tb[TCA_FLOWER_MAX + 1]; | |
6374961a | 1327 | int nl_type, nl_mask_type; |
30eb304e JP |
1328 | __be16 eth_type = 0; |
1329 | __u8 ip_proto = 0xff; | |
1330 | ||
1331 | if (!opt) | |
1332 | return 0; | |
1333 | ||
1334 | parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt); | |
1335 | ||
1336 | if (handle) | |
e28b88a4 | 1337 | print_uint(PRINT_ANY, "handle", "handle 0x%x ", handle); |
30eb304e JP |
1338 | |
1339 | if (tb[TCA_FLOWER_CLASSID]) { | |
0d575c4d AN |
1340 | __u32 h = rta_getattr_u32(tb[TCA_FLOWER_CLASSID]); |
1341 | ||
1342 | if (TC_H_MIN(h) < TC_H_MIN_PRIORITY || | |
1343 | TC_H_MIN(h) > (TC_H_MIN_PRIORITY + TC_QOPT_MAX_QUEUE - 1)) { | |
1344 | SPRINT_BUF(b1); | |
e28b88a4 JP |
1345 | print_string(PRINT_ANY, "classid", "classid %s ", |
1346 | sprint_tc_classid(h, b1)); | |
0d575c4d | 1347 | } else { |
e28b88a4 JP |
1348 | print_uint(PRINT_ANY, "hw_tc", "hw_tc %u ", |
1349 | TC_H_MIN(h) - TC_H_MIN_PRIORITY); | |
0d575c4d | 1350 | } |
30eb304e JP |
1351 | } |
1352 | ||
1353 | if (tb[TCA_FLOWER_INDEV]) { | |
1354 | struct rtattr *attr = tb[TCA_FLOWER_INDEV]; | |
1355 | ||
e28b88a4 JP |
1356 | print_string(PRINT_ANY, "indev", "\n indev %s", |
1357 | rta_getattr_str(attr)); | |
30eb304e JP |
1358 | } |
1359 | ||
e28b88a4 JP |
1360 | open_json_object("keys"); |
1361 | ||
745d9172 HHZ |
1362 | if (tb[TCA_FLOWER_KEY_VLAN_ID]) { |
1363 | struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID]; | |
1364 | ||
e28b88a4 JP |
1365 | print_uint(PRINT_ANY, "vlan_id", "\n vlan_id %u", |
1366 | rta_getattr_u16(attr)); | |
745d9172 HHZ |
1367 | } |
1368 | ||
1369 | if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { | |
1370 | struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_PRIO]; | |
1371 | ||
e28b88a4 JP |
1372 | print_uint(PRINT_ANY, "vlan_prio", "\n vlan_prio %d", |
1373 | rta_getattr_u8(attr)); | |
745d9172 HHZ |
1374 | } |
1375 | ||
e28b88a4 | 1376 | flower_print_eth_addr("dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], |
30eb304e | 1377 | tb[TCA_FLOWER_KEY_ETH_DST_MASK]); |
e28b88a4 | 1378 | flower_print_eth_addr("src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], |
30eb304e JP |
1379 | tb[TCA_FLOWER_KEY_ETH_SRC_MASK]); |
1380 | ||
e28b88a4 JP |
1381 | flower_print_eth_type(ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]); |
1382 | flower_print_ip_proto(&ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]); | |
30eb304e | 1383 | |
e28b88a4 | 1384 | flower_print_ip_attr("ip_tos", tb[TCA_FLOWER_KEY_IP_TOS], |
6ea2c2b1 | 1385 | tb[TCA_FLOWER_KEY_IP_TOS_MASK]); |
e28b88a4 | 1386 | flower_print_ip_attr("ip_ttl", tb[TCA_FLOWER_KEY_IP_TTL], |
6ea2c2b1 OG |
1387 | tb[TCA_FLOWER_KEY_IP_TTL_MASK]); |
1388 | ||
e28b88a4 JP |
1389 | flower_print_u32("mpls_label", tb[TCA_FLOWER_KEY_MPLS_LABEL]); |
1390 | flower_print_u8("mpls_tc", tb[TCA_FLOWER_KEY_MPLS_TC]); | |
1391 | flower_print_u8("mpls_bos", tb[TCA_FLOWER_KEY_MPLS_BOS]); | |
1392 | flower_print_u8("mpls_ttl", tb[TCA_FLOWER_KEY_MPLS_TTL]); | |
7638ee13 | 1393 | |
e28b88a4 | 1394 | flower_print_ip_addr("dst_ip", eth_type, |
30eb304e JP |
1395 | tb[TCA_FLOWER_KEY_IPV4_DST], |
1396 | tb[TCA_FLOWER_KEY_IPV4_DST_MASK], | |
1397 | tb[TCA_FLOWER_KEY_IPV6_DST], | |
1398 | tb[TCA_FLOWER_KEY_IPV6_DST_MASK]); | |
1399 | ||
e28b88a4 | 1400 | flower_print_ip_addr("src_ip", eth_type, |
30eb304e JP |
1401 | tb[TCA_FLOWER_KEY_IPV4_SRC], |
1402 | tb[TCA_FLOWER_KEY_IPV4_SRC_MASK], | |
1403 | tb[TCA_FLOWER_KEY_IPV6_SRC], | |
1404 | tb[TCA_FLOWER_KEY_IPV6_SRC_MASK]); | |
1405 | ||
b2141de1 | 1406 | nl_type = flower_port_attr_type(ip_proto, FLOWER_ENDPOINT_DST); |
6bd5b80c | 1407 | if (nl_type >= 0) |
e28b88a4 | 1408 | flower_print_port("dst_port", tb[nl_type]); |
b2141de1 | 1409 | nl_type = flower_port_attr_type(ip_proto, FLOWER_ENDPOINT_SRC); |
6bd5b80c | 1410 | if (nl_type >= 0) |
e28b88a4 | 1411 | flower_print_port("src_port", tb[nl_type]); |
30eb304e | 1412 | |
e28b88a4 | 1413 | flower_print_tcp_flags("tcp_flags", tb[TCA_FLOWER_KEY_TCP_FLAGS], |
0c30d14d JP |
1414 | tb[TCA_FLOWER_KEY_TCP_FLAGS_MASK]); |
1415 | ||
81f6e5a7 SH |
1416 | nl_type = flower_icmp_attr_type(eth_type, ip_proto, |
1417 | FLOWER_ICMP_FIELD_TYPE); | |
6374961a SH |
1418 | nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto, |
1419 | FLOWER_ICMP_FIELD_TYPE); | |
1420 | if (nl_type >= 0 && nl_mask_type >= 0) | |
e28b88a4 | 1421 | flower_print_masked_u8("icmp_type", tb[nl_type], |
6374961a SH |
1422 | tb[nl_mask_type], NULL); |
1423 | ||
81f6e5a7 SH |
1424 | nl_type = flower_icmp_attr_type(eth_type, ip_proto, |
1425 | FLOWER_ICMP_FIELD_CODE); | |
6374961a SH |
1426 | nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto, |
1427 | FLOWER_ICMP_FIELD_CODE); | |
1428 | if (nl_type >= 0 && nl_mask_type >= 0) | |
e28b88a4 | 1429 | flower_print_masked_u8("icmp_code", tb[nl_type], |
6374961a | 1430 | tb[nl_mask_type], NULL); |
eb3b5696 | 1431 | |
e28b88a4 | 1432 | flower_print_ip4_addr("arp_sip", tb[TCA_FLOWER_KEY_ARP_SIP], |
f888f4e2 | 1433 | tb[TCA_FLOWER_KEY_ARP_SIP_MASK]); |
e28b88a4 | 1434 | flower_print_ip4_addr("arp_tip", tb[TCA_FLOWER_KEY_ARP_TIP], |
f888f4e2 | 1435 | tb[TCA_FLOWER_KEY_ARP_TIP_MASK]); |
e28b88a4 | 1436 | flower_print_arp_op("arp_op", tb[TCA_FLOWER_KEY_ARP_OP], |
f888f4e2 | 1437 | tb[TCA_FLOWER_KEY_ARP_OP_MASK]); |
e28b88a4 | 1438 | flower_print_eth_addr("arp_sha", tb[TCA_FLOWER_KEY_ARP_SHA], |
f888f4e2 | 1439 | tb[TCA_FLOWER_KEY_ARP_SHA_MASK]); |
e28b88a4 | 1440 | flower_print_eth_addr("arp_tha", tb[TCA_FLOWER_KEY_ARP_THA], |
f888f4e2 SH |
1441 | tb[TCA_FLOWER_KEY_ARP_THA_MASK]); |
1442 | ||
e28b88a4 | 1443 | flower_print_ip_addr("enc_dst_ip", |
bb9b63b1 AV |
1444 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] ? |
1445 | htons(ETH_P_IP) : htons(ETH_P_IPV6), | |
1446 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST], | |
1447 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK], | |
1448 | tb[TCA_FLOWER_KEY_ENC_IPV6_DST], | |
1449 | tb[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]); | |
1450 | ||
e28b88a4 | 1451 | flower_print_ip_addr("enc_src_ip", |
bb9b63b1 AV |
1452 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] ? |
1453 | htons(ETH_P_IP) : htons(ETH_P_IPV6), | |
1454 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC], | |
1455 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK], | |
1456 | tb[TCA_FLOWER_KEY_ENC_IPV6_SRC], | |
1457 | tb[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]); | |
1458 | ||
e28b88a4 | 1459 | flower_print_key_id("enc_key_id", tb[TCA_FLOWER_KEY_ENC_KEY_ID]); |
bb9b63b1 | 1460 | |
e28b88a4 | 1461 | flower_print_port("enc_dst_port", tb[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]); |
41aa17ff | 1462 | |
e28b88a4 | 1463 | flower_print_matching_flags("ip_flags", FLOWER_IP_FLAGS, |
22a8f019 PB |
1464 | tb[TCA_FLOWER_KEY_FLAGS], |
1465 | tb[TCA_FLOWER_KEY_FLAGS_MASK]); | |
1466 | ||
e28b88a4 JP |
1467 | close_json_object(); |
1468 | ||
512caeb2 | 1469 | if (tb[TCA_FLOWER_FLAGS]) { |
cfcabf18 AV |
1470 | __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]); |
1471 | ||
1472 | if (flags & TCA_CLS_FLAGS_SKIP_HW) | |
e28b88a4 | 1473 | print_bool(PRINT_ANY, "skip_hw", "\n skip_hw", true); |
cfcabf18 | 1474 | if (flags & TCA_CLS_FLAGS_SKIP_SW) |
e28b88a4 | 1475 | print_bool(PRINT_ANY, "skip_sw", "\n skip_sw", true); |
e57285b8 OG |
1476 | |
1477 | if (flags & TCA_CLS_FLAGS_IN_HW) | |
e28b88a4 | 1478 | print_bool(PRINT_ANY, "in_hw", "\n in_hw", true); |
e57285b8 | 1479 | else if (flags & TCA_CLS_FLAGS_NOT_IN_HW) |
e28b88a4 | 1480 | print_bool(PRINT_ANY, "not_in_hw", "\n not_in_hw", true); |
cfcabf18 AV |
1481 | } |
1482 | ||
512caeb2 | 1483 | if (tb[TCA_FLOWER_ACT]) |
9e713525 | 1484 | tc_print_action(f, tb[TCA_FLOWER_ACT], 0); |
30eb304e JP |
1485 | |
1486 | return 0; | |
1487 | } | |
1488 | ||
1489 | struct filter_util flower_filter_util = { | |
1490 | .id = "flower", | |
1491 | .parse_fopt = flower_parse_opt, | |
1492 | .print_fopt = flower_print_opt, | |
1493 | }; |