]>
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> | |
15 | #include <syslog.h> | |
16 | #include <string.h> | |
17 | #include <net/if.h> | |
18 | #include <linux/if_ether.h> | |
19 | #include <linux/ip.h> | |
745d9172 | 20 | #include <linux/tc_act/tc_vlan.h> |
30eb304e JP |
21 | |
22 | #include "utils.h" | |
23 | #include "tc_util.h" | |
24 | #include "rt_names.h" | |
25 | ||
6910d656 SH |
26 | enum flower_endpoint { |
27 | FLOWER_ENDPOINT_SRC, | |
28 | FLOWER_ENDPOINT_DST | |
29 | }; | |
30 | ||
eb3b5696 SH |
31 | enum flower_icmp_field { |
32 | FLOWER_ICMP_FIELD_TYPE, | |
33 | FLOWER_ICMP_FIELD_CODE | |
34 | }; | |
35 | ||
30eb304e JP |
36 | static void explain(void) |
37 | { | |
512caeb2 SH |
38 | fprintf(stderr, |
39 | "Usage: ... flower [ MATCH-LIST ]\n" | |
40 | " [ skip_sw | skip_hw ]\n" | |
41 | " [ action ACTION-SPEC ] [ classid CLASSID ]\n" | |
42 | "\n" | |
43 | "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n" | |
44 | " MATCH := { indev DEV-NAME |\n" | |
45 | " vlan_id VID |\n" | |
46 | " vlan_prio PRIORITY |\n" | |
47 | " vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n" | |
03901850 SH |
48 | " dst_mac MASKED-LLADDR |\n" |
49 | " src_mac MASKED-LLADDR |\n" | |
eb3b5696 | 50 | " ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n" |
a8a1dccd SH |
51 | " dst_ip PREFIX |\n" |
52 | " src_ip PREFIX |\n" | |
512caeb2 | 53 | " dst_port PORT-NUMBER |\n" |
bb9b63b1 | 54 | " src_port PORT-NUMBER |\n" |
eb3b5696 SH |
55 | " type ICMP-TYPE |\n" |
56 | " code ICMP-CODE }\n" | |
a8a1dccd SH |
57 | " enc_dst_ip PREFIX |\n" |
58 | " enc_src_ip PREFIX |\n" | |
bb9b63b1 | 59 | " enc_key_id [ KEY-ID ] }\n" |
512caeb2 | 60 | " FILTERID := X:Y:Z\n" |
03901850 | 61 | " MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n" |
512caeb2 SH |
62 | " ACTION-SPEC := ... look at individual actions\n" |
63 | "\n" | |
328374dc | 64 | "NOTE: CLASSID, IP-PROTO are parsed as hexadecimal input.\n" |
512caeb2 SH |
65 | "NOTE: There can be only used one mask per one prio. If user needs\n" |
66 | " to specify different mask, he has to use different prio.\n"); | |
30eb304e JP |
67 | } |
68 | ||
69 | static int flower_parse_eth_addr(char *str, int addr_type, int mask_type, | |
70 | struct nlmsghdr *n) | |
71 | { | |
03901850 SH |
72 | int ret, err = -1; |
73 | char addr[ETH_ALEN], *slash; | |
74 | ||
75 | slash = strchr(str, '/'); | |
76 | if (slash) | |
77 | *slash = '\0'; | |
30eb304e JP |
78 | |
79 | ret = ll_addr_a2n(addr, sizeof(addr), str); | |
80 | if (ret < 0) | |
03901850 | 81 | goto err; |
30eb304e | 82 | addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr)); |
03901850 SH |
83 | |
84 | if (slash) { | |
85 | unsigned bits; | |
86 | ||
87 | if (!get_unsigned(&bits, slash + 1, 10)) { | |
88 | uint64_t mask; | |
89 | ||
90 | /* Extra 16 bit shift to push mac address into | |
91 | * high bits of uint64_t | |
92 | */ | |
93 | mask = htonll(0xffffffffffffULL << (16 + 48 - bits)); | |
94 | memcpy(addr, &mask, ETH_ALEN); | |
95 | } else { | |
96 | ret = ll_addr_a2n(addr, sizeof(addr), slash + 1); | |
97 | if (ret < 0) | |
98 | goto err; | |
99 | } | |
100 | } else { | |
101 | memset(addr, 0xff, ETH_ALEN); | |
102 | } | |
30eb304e | 103 | addattr_l(n, MAX_MSG, mask_type, addr, sizeof(addr)); |
03901850 SH |
104 | |
105 | err = 0; | |
106 | err: | |
107 | if (slash) | |
108 | *slash = '/'; | |
109 | return err; | |
30eb304e JP |
110 | } |
111 | ||
745d9172 | 112 | static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type, |
512caeb2 SH |
113 | __be16 *p_vlan_eth_type, |
114 | struct nlmsghdr *n) | |
745d9172 HHZ |
115 | { |
116 | __be16 vlan_eth_type; | |
117 | ||
118 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
119 | fprintf(stderr, |
120 | "Can't set \"vlan_ethtype\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
121 | return -1; |
122 | } | |
123 | ||
124 | if (ll_proto_a2n(&vlan_eth_type, str)) | |
125 | invarg("invalid vlan_ethtype", str); | |
126 | addattr16(n, MAX_MSG, type, vlan_eth_type); | |
127 | *p_vlan_eth_type = vlan_eth_type; | |
128 | return 0; | |
129 | } | |
130 | ||
30eb304e JP |
131 | static int flower_parse_ip_proto(char *str, __be16 eth_type, int type, |
132 | __u8 *p_ip_proto, struct nlmsghdr *n) | |
133 | { | |
134 | int ret; | |
135 | __u8 ip_proto; | |
136 | ||
eb3b5696 SH |
137 | if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) |
138 | goto err; | |
139 | ||
30eb304e JP |
140 | if (matches(str, "tcp") == 0) { |
141 | ip_proto = IPPROTO_TCP; | |
142 | } else if (matches(str, "udp") == 0) { | |
143 | ip_proto = IPPROTO_UDP; | |
a1fb0d48 SH |
144 | } else if (matches(str, "sctp") == 0) { |
145 | ip_proto = IPPROTO_SCTP; | |
eb3b5696 SH |
146 | } else if (matches(str, "icmp") == 0) { |
147 | if (eth_type != htons(ETH_P_IP)) | |
148 | goto err; | |
149 | ip_proto = IPPROTO_ICMP; | |
150 | } else if (matches(str, "icmpv6") == 0) { | |
151 | if (eth_type != htons(ETH_P_IPV6)) | |
152 | goto err; | |
153 | ip_proto = IPPROTO_ICMPV6; | |
30eb304e JP |
154 | } else { |
155 | ret = get_u8(&ip_proto, str, 16); | |
156 | if (ret) | |
157 | return -1; | |
158 | } | |
159 | addattr8(n, MAX_MSG, type, ip_proto); | |
160 | *p_ip_proto = ip_proto; | |
161 | return 0; | |
eb3b5696 SH |
162 | |
163 | err: | |
164 | fprintf(stderr, "Illegal \"eth_type\" for ip proto\n"); | |
165 | return -1; | |
30eb304e JP |
166 | } |
167 | ||
168 | static int flower_parse_ip_addr(char *str, __be16 eth_type, | |
169 | int addr4_type, int mask4_type, | |
170 | int addr6_type, int mask6_type, | |
171 | struct nlmsghdr *n) | |
172 | { | |
173 | int ret; | |
174 | inet_prefix addr; | |
175 | int family; | |
176 | int bits; | |
177 | int i; | |
178 | ||
179 | if (eth_type == htons(ETH_P_IP)) { | |
180 | family = AF_INET; | |
181 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
182 | family = AF_INET6; | |
bb9b63b1 AV |
183 | } else if (!eth_type) { |
184 | family = AF_UNSPEC; | |
30eb304e | 185 | } else { |
30eb304e JP |
186 | return -1; |
187 | } | |
188 | ||
189 | ret = get_prefix(&addr, str, family); | |
190 | if (ret) | |
191 | return -1; | |
192 | ||
bb9b63b1 AV |
193 | if (family && (addr.family != family)) { |
194 | fprintf(stderr, "Illegal \"eth_type\" for ip address\n"); | |
30eb304e | 195 | return -1; |
bb9b63b1 | 196 | } |
30eb304e JP |
197 | |
198 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? addr4_type : addr6_type, | |
199 | addr.data, addr.bytelen); | |
200 | ||
201 | memset(addr.data, 0xff, addr.bytelen); | |
202 | bits = addr.bitlen; | |
203 | for (i = 0; i < addr.bytelen / 4; i++) { | |
204 | if (!bits) { | |
205 | addr.data[i] = 0; | |
206 | } else if (bits / 32 >= 1) { | |
207 | bits -= 32; | |
208 | } else { | |
209 | addr.data[i] <<= 32 - bits; | |
210 | addr.data[i] = htonl(addr.data[i]); | |
211 | bits = 0; | |
212 | } | |
213 | } | |
214 | ||
215 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? mask4_type : mask6_type, | |
216 | addr.data, addr.bytelen); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
eb3b5696 SH |
221 | static int flower_icmp_attr_type(__be16 eth_type, __u8 ip_proto, |
222 | enum flower_icmp_field field) | |
223 | { | |
224 | if (eth_type == htons(ETH_P_IP) && ip_proto == IPPROTO_ICMP) | |
225 | return field == FLOWER_ICMP_FIELD_CODE ? | |
226 | TCA_FLOWER_KEY_ICMPV4_CODE : | |
227 | TCA_FLOWER_KEY_ICMPV4_TYPE; | |
228 | else if (eth_type == htons(ETH_P_IPV6) && ip_proto == IPPROTO_ICMPV6) | |
229 | return field == FLOWER_ICMP_FIELD_CODE ? | |
230 | TCA_FLOWER_KEY_ICMPV6_CODE : | |
231 | TCA_FLOWER_KEY_ICMPV6_TYPE; | |
232 | ||
233 | return -1; | |
234 | } | |
235 | ||
236 | static int flower_parse_icmp(char *str, __u16 eth_type, __u8 ip_proto, | |
237 | enum flower_icmp_field field, struct nlmsghdr *n) | |
238 | { | |
239 | int ret; | |
240 | int type; | |
241 | uint8_t value; | |
242 | ||
243 | type = flower_icmp_attr_type(eth_type, ip_proto, field); | |
244 | if (type < 0) | |
245 | return -1; | |
246 | ||
247 | ret = get_u8(&value, str, 10); | |
248 | if (ret) | |
249 | return -1; | |
250 | ||
251 | addattr8(n, MAX_MSG, type, value); | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
6910d656 | 256 | static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint) |
30eb304e | 257 | { |
6bd5b80c | 258 | if (ip_proto == IPPROTO_TCP) |
6910d656 SH |
259 | return endpoint == FLOWER_ENDPOINT_SRC ? |
260 | TCA_FLOWER_KEY_TCP_SRC : | |
a1fb0d48 | 261 | TCA_FLOWER_KEY_TCP_DST; |
6bd5b80c | 262 | else if (ip_proto == IPPROTO_UDP) |
6910d656 SH |
263 | return endpoint == FLOWER_ENDPOINT_SRC ? |
264 | TCA_FLOWER_KEY_UDP_SRC : | |
a1fb0d48 | 265 | TCA_FLOWER_KEY_UDP_DST; |
6bd5b80c | 266 | else if (ip_proto == IPPROTO_SCTP) |
6910d656 SH |
267 | return endpoint == FLOWER_ENDPOINT_SRC ? |
268 | TCA_FLOWER_KEY_SCTP_SRC : | |
a1fb0d48 | 269 | TCA_FLOWER_KEY_SCTP_DST; |
6bd5b80c | 270 | else |
30eb304e | 271 | return -1; |
a1fb0d48 SH |
272 | } |
273 | ||
6910d656 SH |
274 | static int flower_parse_port(char *str, __u8 ip_proto, |
275 | enum flower_endpoint endpoint, | |
a1fb0d48 SH |
276 | struct nlmsghdr *n) |
277 | { | |
278 | int ret; | |
279 | int type; | |
280 | __be16 port; | |
281 | ||
6910d656 | 282 | type = flower_port_attr_type(ip_proto, endpoint); |
a1fb0d48 SH |
283 | if (type < 0) |
284 | return -1; | |
30eb304e | 285 | |
9f7401fa | 286 | ret = get_be16(&port, str, 10); |
30eb304e JP |
287 | if (ret) |
288 | return -1; | |
289 | ||
9f7401fa | 290 | addattr16(n, MAX_MSG, type, port); |
30eb304e JP |
291 | |
292 | return 0; | |
293 | } | |
294 | ||
bb9b63b1 AV |
295 | static int flower_parse_key_id(const char *str, int type, struct nlmsghdr *n) |
296 | { | |
297 | int ret; | |
298 | __be32 key_id; | |
299 | ||
300 | ret = get_be32(&key_id, str, 10); | |
301 | if (!ret) | |
302 | addattr32(n, MAX_MSG, type, key_id); | |
303 | ||
304 | return ret; | |
305 | } | |
306 | ||
41aa17ff HHZ |
307 | static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n) |
308 | { | |
309 | int ret; | |
310 | __be16 port; | |
311 | ||
312 | ret = get_be16(&port, str, 10); | |
313 | if (ret) | |
314 | return -1; | |
315 | ||
316 | addattr16(n, MAX_MSG, type, port); | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
30eb304e JP |
321 | static int flower_parse_opt(struct filter_util *qu, char *handle, |
322 | int argc, char **argv, struct nlmsghdr *n) | |
323 | { | |
324 | int ret; | |
325 | struct tcmsg *t = NLMSG_DATA(n); | |
326 | struct rtattr *tail; | |
488b41d0 | 327 | __be16 eth_type = TC_H_MIN(t->tcm_info); |
745d9172 | 328 | __be16 vlan_ethtype = 0; |
30eb304e | 329 | __u8 ip_proto = 0xff; |
cfcabf18 | 330 | __u32 flags = 0; |
30eb304e | 331 | |
30eb304e JP |
332 | if (handle) { |
333 | ret = get_u32(&t->tcm_handle, handle, 0); | |
334 | if (ret) { | |
335 | fprintf(stderr, "Illegal \"handle\"\n"); | |
336 | return -1; | |
337 | } | |
338 | } | |
339 | ||
340 | tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); | |
341 | addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); | |
342 | ||
488b41d0 JHS |
343 | if (argc == 0) { |
344 | /*at minimal we will match all ethertype packets */ | |
345 | goto parse_done; | |
346 | } | |
347 | ||
30eb304e JP |
348 | while (argc > 0) { |
349 | if (matches(*argv, "classid") == 0 || | |
350 | matches(*argv, "flowid") == 0) { | |
32a121cb | 351 | unsigned int handle; |
30eb304e JP |
352 | |
353 | NEXT_ARG(); | |
354 | ret = get_tc_classid(&handle, *argv); | |
355 | if (ret) { | |
356 | fprintf(stderr, "Illegal \"classid\"\n"); | |
357 | return -1; | |
358 | } | |
359 | addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); | |
cfcabf18 AV |
360 | } else if (matches(*argv, "skip_hw") == 0) { |
361 | flags |= TCA_CLS_FLAGS_SKIP_HW; | |
362 | } else if (matches(*argv, "skip_sw") == 0) { | |
363 | flags |= TCA_CLS_FLAGS_SKIP_SW; | |
30eb304e | 364 | } else if (matches(*argv, "indev") == 0) { |
d17b136f | 365 | char ifname[IFNAMSIZ] = {}; |
30eb304e JP |
366 | |
367 | NEXT_ARG(); | |
30eb304e JP |
368 | strncpy(ifname, *argv, sizeof(ifname) - 1); |
369 | addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname); | |
745d9172 HHZ |
370 | } else if (matches(*argv, "vlan_id") == 0) { |
371 | __u16 vid; | |
372 | ||
373 | NEXT_ARG(); | |
374 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
375 | fprintf(stderr, |
376 | "Can't set \"vlan_id\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
377 | return -1; |
378 | } | |
379 | ret = get_u16(&vid, *argv, 10); | |
380 | if (ret < 0 || vid & ~0xfff) { | |
381 | fprintf(stderr, "Illegal \"vlan_id\"\n"); | |
382 | return -1; | |
383 | } | |
384 | addattr16(n, MAX_MSG, TCA_FLOWER_KEY_VLAN_ID, vid); | |
385 | } else if (matches(*argv, "vlan_prio") == 0) { | |
386 | __u8 vlan_prio; | |
387 | ||
388 | NEXT_ARG(); | |
389 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
390 | fprintf(stderr, |
391 | "Can't set \"vlan_prio\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
392 | return -1; |
393 | } | |
394 | ret = get_u8(&vlan_prio, *argv, 10); | |
395 | if (ret < 0 || vlan_prio & ~0x7) { | |
396 | fprintf(stderr, "Illegal \"vlan_prio\"\n"); | |
397 | return -1; | |
398 | } | |
512caeb2 SH |
399 | addattr8(n, MAX_MSG, |
400 | TCA_FLOWER_KEY_VLAN_PRIO, vlan_prio); | |
745d9172 HHZ |
401 | } else if (matches(*argv, "vlan_ethtype") == 0) { |
402 | NEXT_ARG(); | |
403 | ret = flower_parse_vlan_eth_type(*argv, eth_type, | |
512caeb2 SH |
404 | TCA_FLOWER_KEY_VLAN_ETH_TYPE, |
405 | &vlan_ethtype, n); | |
745d9172 HHZ |
406 | if (ret < 0) |
407 | return -1; | |
30eb304e JP |
408 | } else if (matches(*argv, "dst_mac") == 0) { |
409 | NEXT_ARG(); | |
410 | ret = flower_parse_eth_addr(*argv, | |
411 | TCA_FLOWER_KEY_ETH_DST, | |
412 | TCA_FLOWER_KEY_ETH_DST_MASK, | |
413 | n); | |
414 | if (ret < 0) { | |
415 | fprintf(stderr, "Illegal \"dst_mac\"\n"); | |
416 | return -1; | |
417 | } | |
418 | } else if (matches(*argv, "src_mac") == 0) { | |
419 | NEXT_ARG(); | |
420 | ret = flower_parse_eth_addr(*argv, | |
421 | TCA_FLOWER_KEY_ETH_SRC, | |
422 | TCA_FLOWER_KEY_ETH_SRC_MASK, | |
423 | n); | |
424 | if (ret < 0) { | |
425 | fprintf(stderr, "Illegal \"src_mac\"\n"); | |
426 | return -1; | |
427 | } | |
30eb304e JP |
428 | } else if (matches(*argv, "ip_proto") == 0) { |
429 | NEXT_ARG(); | |
745d9172 HHZ |
430 | ret = flower_parse_ip_proto(*argv, vlan_ethtype ? |
431 | vlan_ethtype : eth_type, | |
30eb304e JP |
432 | TCA_FLOWER_KEY_IP_PROTO, |
433 | &ip_proto, n); | |
434 | if (ret < 0) { | |
435 | fprintf(stderr, "Illegal \"ip_proto\"\n"); | |
436 | return -1; | |
437 | } | |
438 | } else if (matches(*argv, "dst_ip") == 0) { | |
439 | NEXT_ARG(); | |
745d9172 HHZ |
440 | ret = flower_parse_ip_addr(*argv, vlan_ethtype ? |
441 | vlan_ethtype : eth_type, | |
30eb304e JP |
442 | TCA_FLOWER_KEY_IPV4_DST, |
443 | TCA_FLOWER_KEY_IPV4_DST_MASK, | |
444 | TCA_FLOWER_KEY_IPV6_DST, | |
445 | TCA_FLOWER_KEY_IPV6_DST_MASK, | |
446 | n); | |
447 | if (ret < 0) { | |
448 | fprintf(stderr, "Illegal \"dst_ip\"\n"); | |
449 | return -1; | |
450 | } | |
451 | } else if (matches(*argv, "src_ip") == 0) { | |
452 | NEXT_ARG(); | |
745d9172 HHZ |
453 | ret = flower_parse_ip_addr(*argv, vlan_ethtype ? |
454 | vlan_ethtype : eth_type, | |
30eb304e JP |
455 | TCA_FLOWER_KEY_IPV4_SRC, |
456 | TCA_FLOWER_KEY_IPV4_SRC_MASK, | |
457 | TCA_FLOWER_KEY_IPV6_SRC, | |
458 | TCA_FLOWER_KEY_IPV6_SRC_MASK, | |
459 | n); | |
460 | if (ret < 0) { | |
461 | fprintf(stderr, "Illegal \"src_ip\"\n"); | |
462 | return -1; | |
463 | } | |
464 | } else if (matches(*argv, "dst_port") == 0) { | |
465 | NEXT_ARG(); | |
6910d656 SH |
466 | ret = flower_parse_port(*argv, ip_proto, |
467 | FLOWER_ENDPOINT_DST, n); | |
30eb304e JP |
468 | if (ret < 0) { |
469 | fprintf(stderr, "Illegal \"dst_port\"\n"); | |
470 | return -1; | |
471 | } | |
472 | } else if (matches(*argv, "src_port") == 0) { | |
473 | NEXT_ARG(); | |
6910d656 SH |
474 | ret = flower_parse_port(*argv, ip_proto, |
475 | FLOWER_ENDPOINT_SRC, n); | |
30eb304e JP |
476 | if (ret < 0) { |
477 | fprintf(stderr, "Illegal \"src_port\"\n"); | |
478 | return -1; | |
479 | } | |
eb3b5696 SH |
480 | } else if (matches(*argv, "type") == 0) { |
481 | NEXT_ARG(); | |
482 | ret = flower_parse_icmp(*argv, eth_type, ip_proto, | |
483 | FLOWER_ICMP_FIELD_TYPE, n); | |
484 | if (ret < 0) { | |
485 | fprintf(stderr, "Illegal \"icmp type\"\n"); | |
486 | return -1; | |
487 | } | |
488 | } else if (matches(*argv, "code") == 0) { | |
489 | NEXT_ARG(); | |
490 | ret = flower_parse_icmp(*argv, eth_type, ip_proto, | |
491 | FLOWER_ICMP_FIELD_CODE, n); | |
492 | if (ret < 0) { | |
493 | fprintf(stderr, "Illegal \"icmp code\"\n"); | |
494 | return -1; | |
495 | } | |
bb9b63b1 AV |
496 | } else if (matches(*argv, "enc_dst_ip") == 0) { |
497 | NEXT_ARG(); | |
498 | ret = flower_parse_ip_addr(*argv, 0, | |
499 | TCA_FLOWER_KEY_ENC_IPV4_DST, | |
500 | TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, | |
501 | TCA_FLOWER_KEY_ENC_IPV6_DST, | |
502 | TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, | |
503 | n); | |
504 | if (ret < 0) { | |
505 | fprintf(stderr, "Illegal \"enc_dst_ip\"\n"); | |
506 | return -1; | |
507 | } | |
508 | } else if (matches(*argv, "enc_src_ip") == 0) { | |
509 | NEXT_ARG(); | |
510 | ret = flower_parse_ip_addr(*argv, 0, | |
511 | TCA_FLOWER_KEY_ENC_IPV4_SRC, | |
512 | TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, | |
513 | TCA_FLOWER_KEY_ENC_IPV6_SRC, | |
514 | TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, | |
515 | n); | |
516 | if (ret < 0) { | |
517 | fprintf(stderr, "Illegal \"enc_src_ip\"\n"); | |
518 | return -1; | |
519 | } | |
520 | } else if (matches(*argv, "enc_key_id") == 0) { | |
521 | NEXT_ARG(); | |
522 | ret = flower_parse_key_id(*argv, | |
523 | TCA_FLOWER_KEY_ENC_KEY_ID, n); | |
524 | if (ret < 0) { | |
525 | fprintf(stderr, "Illegal \"enc_key_id\"\n"); | |
526 | return -1; | |
527 | } | |
41aa17ff HHZ |
528 | } else if (matches(*argv, "enc_dst_port") == 0) { |
529 | NEXT_ARG(); | |
530 | ret = flower_parse_enc_port(*argv, | |
531 | TCA_FLOWER_KEY_ENC_UDP_DST_PORT, n); | |
532 | if (ret < 0) { | |
533 | fprintf(stderr, "Illegal \"enc_dst_port\"\n"); | |
534 | return -1; | |
535 | } | |
30eb304e JP |
536 | } else if (matches(*argv, "action") == 0) { |
537 | NEXT_ARG(); | |
538 | ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n); | |
539 | if (ret) { | |
540 | fprintf(stderr, "Illegal \"action\"\n"); | |
541 | return -1; | |
542 | } | |
543 | continue; | |
544 | } else if (strcmp(*argv, "help") == 0) { | |
545 | explain(); | |
546 | return -1; | |
547 | } else { | |
548 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
549 | explain(); | |
550 | return -1; | |
551 | } | |
552 | argc--; argv++; | |
553 | } | |
554 | ||
488b41d0 | 555 | parse_done: |
cfcabf18 AV |
556 | addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags); |
557 | ||
488b41d0 JHS |
558 | ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type); |
559 | if (ret) { | |
560 | fprintf(stderr, "Illegal \"eth_type\"(0x%x)\n", | |
561 | ntohs(eth_type)); | |
562 | return -1; | |
563 | } | |
564 | ||
32a121cb | 565 | tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; |
30eb304e JP |
566 | |
567 | return 0; | |
568 | } | |
569 | ||
570 | static int __mask_bits(char *addr, size_t len) | |
571 | { | |
572 | int bits = 0; | |
573 | bool hole = false; | |
574 | int i; | |
575 | int j; | |
576 | ||
577 | for (i = 0; i < len; i++, addr++) { | |
578 | for (j = 7; j >= 0; j--) { | |
579 | if (((*addr) >> j) & 0x1) { | |
580 | if (hole) | |
581 | return -1; | |
582 | bits++; | |
583 | } else if (bits) { | |
584 | hole = true; | |
585 | } else{ | |
586 | return -1; | |
587 | } | |
588 | } | |
589 | } | |
590 | return bits; | |
591 | } | |
592 | ||
593 | static void flower_print_eth_addr(FILE *f, char *name, | |
594 | struct rtattr *addr_attr, | |
595 | struct rtattr *mask_attr) | |
596 | { | |
597 | SPRINT_BUF(b1); | |
598 | int bits; | |
599 | ||
600 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN) | |
601 | return; | |
602 | fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN, | |
603 | 0, b1, sizeof(b1))); | |
604 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN) | |
605 | return; | |
606 | bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN); | |
607 | if (bits < 0) | |
608 | fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN, | |
609 | 0, b1, sizeof(b1))); | |
610 | else if (bits < ETH_ALEN * 8) | |
611 | fprintf(f, "/%d", bits); | |
612 | } | |
613 | ||
614 | static void flower_print_eth_type(FILE *f, __be16 *p_eth_type, | |
615 | struct rtattr *eth_type_attr) | |
616 | { | |
617 | __be16 eth_type; | |
618 | ||
619 | if (!eth_type_attr) | |
620 | return; | |
621 | ||
622 | eth_type = rta_getattr_u16(eth_type_attr); | |
623 | fprintf(f, "\n eth_type "); | |
624 | if (eth_type == htons(ETH_P_IP)) | |
625 | fprintf(f, "ipv4"); | |
626 | else if (eth_type == htons(ETH_P_IPV6)) | |
627 | fprintf(f, "ipv6"); | |
628 | else | |
629 | fprintf(f, "%04x", ntohs(eth_type)); | |
630 | *p_eth_type = eth_type; | |
631 | } | |
632 | ||
633 | static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto, | |
634 | struct rtattr *ip_proto_attr) | |
635 | { | |
636 | __u8 ip_proto; | |
637 | ||
638 | if (!ip_proto_attr) | |
639 | return; | |
640 | ||
641 | ip_proto = rta_getattr_u8(ip_proto_attr); | |
642 | fprintf(f, "\n ip_proto "); | |
643 | if (ip_proto == IPPROTO_TCP) | |
644 | fprintf(f, "tcp"); | |
645 | else if (ip_proto == IPPROTO_UDP) | |
646 | fprintf(f, "udp"); | |
a1fb0d48 SH |
647 | else if (ip_proto == IPPROTO_SCTP) |
648 | fprintf(f, "sctp"); | |
eb3b5696 SH |
649 | else if (ip_proto == IPPROTO_ICMP) |
650 | fprintf(f, "icmp"); | |
651 | else if (ip_proto == IPPROTO_ICMPV6) | |
652 | fprintf(f, "icmpv6"); | |
30eb304e JP |
653 | else |
654 | fprintf(f, "%02x", ip_proto); | |
655 | *p_ip_proto = ip_proto; | |
656 | } | |
657 | ||
658 | static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type, | |
659 | struct rtattr *addr4_attr, | |
660 | struct rtattr *mask4_attr, | |
661 | struct rtattr *addr6_attr, | |
662 | struct rtattr *mask6_attr) | |
663 | { | |
30eb304e JP |
664 | struct rtattr *addr_attr; |
665 | struct rtattr *mask_attr; | |
666 | int family; | |
667 | size_t len; | |
668 | int bits; | |
669 | ||
670 | if (eth_type == htons(ETH_P_IP)) { | |
671 | family = AF_INET; | |
672 | addr_attr = addr4_attr; | |
673 | mask_attr = mask4_attr; | |
674 | len = 4; | |
675 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
676 | family = AF_INET6; | |
677 | addr_attr = addr6_attr; | |
678 | mask_attr = mask6_attr; | |
679 | len = 16; | |
680 | } else { | |
681 | return; | |
682 | } | |
683 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != len) | |
684 | return; | |
7faf1588 | 685 | fprintf(f, "\n %s %s", name, rt_addr_n2a_rta(family, addr_attr)); |
30eb304e JP |
686 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != len) |
687 | return; | |
688 | bits = __mask_bits(RTA_DATA(mask_attr), len); | |
689 | if (bits < 0) | |
7faf1588 | 690 | fprintf(f, "/%s", rt_addr_n2a_rta(family, mask_attr)); |
30eb304e JP |
691 | else if (bits < len * 8) |
692 | fprintf(f, "/%d", bits); | |
693 | } | |
694 | ||
a1fb0d48 | 695 | static void flower_print_port(FILE *f, char *name, struct rtattr *attr) |
30eb304e | 696 | { |
6bd5b80c SH |
697 | if (attr) |
698 | fprintf(f, "\n %s %d", name, rta_getattr_be16(attr)); | |
30eb304e JP |
699 | } |
700 | ||
bb9b63b1 AV |
701 | static void flower_print_key_id(FILE *f, const char *name, |
702 | struct rtattr *attr) | |
703 | { | |
704 | if (attr) | |
705 | fprintf(f, "\n %s %d", name, rta_getattr_be32(attr)); | |
706 | } | |
707 | ||
eb3b5696 SH |
708 | static void flower_print_icmp(FILE *f, char *name, struct rtattr *attr) |
709 | { | |
710 | if (attr) | |
711 | fprintf(f, "\n %s %d", name, rta_getattr_u8(attr)); | |
712 | } | |
713 | ||
30eb304e JP |
714 | static int flower_print_opt(struct filter_util *qu, FILE *f, |
715 | struct rtattr *opt, __u32 handle) | |
716 | { | |
717 | struct rtattr *tb[TCA_FLOWER_MAX + 1]; | |
718 | __be16 eth_type = 0; | |
719 | __u8 ip_proto = 0xff; | |
6bd5b80c | 720 | int nl_type; |
30eb304e JP |
721 | |
722 | if (!opt) | |
723 | return 0; | |
724 | ||
725 | parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt); | |
726 | ||
727 | if (handle) | |
728 | fprintf(f, "handle 0x%x ", handle); | |
729 | ||
730 | if (tb[TCA_FLOWER_CLASSID]) { | |
731 | SPRINT_BUF(b1); | |
732 | fprintf(f, "classid %s ", | |
488b41d0 JHS |
733 | sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]), |
734 | b1)); | |
30eb304e JP |
735 | } |
736 | ||
737 | if (tb[TCA_FLOWER_INDEV]) { | |
738 | struct rtattr *attr = tb[TCA_FLOWER_INDEV]; | |
739 | ||
740 | fprintf(f, "\n indev %s", rta_getattr_str(attr)); | |
741 | } | |
742 | ||
745d9172 HHZ |
743 | if (tb[TCA_FLOWER_KEY_VLAN_ID]) { |
744 | struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID]; | |
745 | ||
746 | fprintf(f, "\n vlan_id %d", rta_getattr_u16(attr)); | |
747 | } | |
748 | ||
749 | if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { | |
750 | struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_PRIO]; | |
751 | ||
752 | fprintf(f, "\n vlan_prio %d", rta_getattr_u8(attr)); | |
753 | } | |
754 | ||
30eb304e JP |
755 | flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], |
756 | tb[TCA_FLOWER_KEY_ETH_DST_MASK]); | |
757 | flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], | |
758 | tb[TCA_FLOWER_KEY_ETH_SRC_MASK]); | |
759 | ||
760 | flower_print_eth_type(f, ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]); | |
761 | flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]); | |
762 | ||
763 | flower_print_ip_addr(f, "dst_ip", eth_type, | |
764 | tb[TCA_FLOWER_KEY_IPV4_DST], | |
765 | tb[TCA_FLOWER_KEY_IPV4_DST_MASK], | |
766 | tb[TCA_FLOWER_KEY_IPV6_DST], | |
767 | tb[TCA_FLOWER_KEY_IPV6_DST_MASK]); | |
768 | ||
769 | flower_print_ip_addr(f, "src_ip", eth_type, | |
770 | tb[TCA_FLOWER_KEY_IPV4_SRC], | |
771 | tb[TCA_FLOWER_KEY_IPV4_SRC_MASK], | |
772 | tb[TCA_FLOWER_KEY_IPV6_SRC], | |
773 | tb[TCA_FLOWER_KEY_IPV6_SRC_MASK]); | |
774 | ||
6bd5b80c SH |
775 | nl_type = flower_port_attr_type(ip_proto, false); |
776 | if (nl_type >= 0) | |
777 | flower_print_port(f, "dst_port", tb[nl_type]); | |
778 | nl_type = flower_port_attr_type(ip_proto, true); | |
779 | if (nl_type >= 0) | |
780 | flower_print_port(f, "src_port", tb[nl_type]); | |
30eb304e | 781 | |
eb3b5696 SH |
782 | nl_type = flower_icmp_attr_type(eth_type, ip_proto, false); |
783 | if (nl_type >= 0) | |
784 | flower_print_icmp(f, "icmp_type", tb[nl_type]); | |
785 | nl_type = flower_icmp_attr_type(eth_type, ip_proto, true); | |
786 | if (nl_type >= 0) | |
787 | flower_print_icmp(f, "icmp_code", tb[nl_type]); | |
788 | ||
bb9b63b1 AV |
789 | flower_print_ip_addr(f, "enc_dst_ip", |
790 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] ? | |
791 | htons(ETH_P_IP) : htons(ETH_P_IPV6), | |
792 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST], | |
793 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK], | |
794 | tb[TCA_FLOWER_KEY_ENC_IPV6_DST], | |
795 | tb[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]); | |
796 | ||
797 | flower_print_ip_addr(f, "enc_src_ip", | |
798 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] ? | |
799 | htons(ETH_P_IP) : htons(ETH_P_IPV6), | |
800 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC], | |
801 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK], | |
802 | tb[TCA_FLOWER_KEY_ENC_IPV6_SRC], | |
803 | tb[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]); | |
804 | ||
805 | flower_print_key_id(f, "enc_key_id", | |
806 | tb[TCA_FLOWER_KEY_ENC_KEY_ID]); | |
807 | ||
41aa17ff HHZ |
808 | flower_print_port(f, "enc_dst_port", |
809 | tb[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]); | |
810 | ||
512caeb2 | 811 | if (tb[TCA_FLOWER_FLAGS]) { |
cfcabf18 AV |
812 | __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]); |
813 | ||
814 | if (flags & TCA_CLS_FLAGS_SKIP_HW) | |
815 | fprintf(f, "\n skip_hw"); | |
816 | if (flags & TCA_CLS_FLAGS_SKIP_SW) | |
817 | fprintf(f, "\n skip_sw"); | |
818 | } | |
819 | ||
512caeb2 | 820 | if (tb[TCA_FLOWER_ACT]) |
30eb304e | 821 | tc_print_action(f, tb[TCA_FLOWER_ACT]); |
30eb304e JP |
822 | |
823 | return 0; | |
824 | } | |
825 | ||
826 | struct filter_util flower_filter_util = { | |
827 | .id = "flower", | |
828 | .parse_fopt = flower_parse_opt, | |
829 | .print_fopt = flower_print_opt, | |
830 | }; |