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