]>
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> | |
20 | ||
21 | #include "utils.h" | |
22 | #include "tc_util.h" | |
23 | #include "rt_names.h" | |
24 | ||
25 | static void explain(void) | |
26 | { | |
27 | fprintf(stderr, "Usage: ... flower [ MATCH-LIST ]\n"); | |
28 | fprintf(stderr, " [ action ACTION-SPEC ] [ classid CLASSID ]\n"); | |
29 | fprintf(stderr, "\n"); | |
30 | fprintf(stderr, "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n"); | |
31 | fprintf(stderr, " MATCH := [ indev DEV-NAME | \n"); | |
32 | fprintf(stderr, " dst_mac MAC-ADDR | \n"); | |
33 | fprintf(stderr, " src_mac MAC-ADDR | \n"); | |
34 | fprintf(stderr, " eth_type [ipv4 | ipv6 | ETH-TYPE ] | \n"); | |
35 | fprintf(stderr, " ip_proto [tcp | udp | IP-PROTO ] | \n"); | |
36 | fprintf(stderr, " dst_ip [ IPV4-ADDR | IPV6-ADDR ] | \n"); | |
37 | fprintf(stderr, " src_ip [ IPV4-ADDR | IPV6-ADDR ] | \n"); | |
38 | fprintf(stderr, " dst_port PORT-NUMBER | \n"); | |
39 | fprintf(stderr, " src_port PORT-NUMBER | \n"); | |
40 | fprintf(stderr, " FILTERID := X:Y:Z\n"); | |
41 | fprintf(stderr, " ACTION-SPEC := ... look at individual actions\n"); | |
42 | fprintf(stderr, "\n"); | |
43 | fprintf(stderr, "NOTE: CLASSID, ETH-TYPE, IP-PROTO are parsed as hexadecimal input.\n"); | |
44 | fprintf(stderr, "NOTE: There can be only used one mask per one prio. If user needs\n"); | |
45 | fprintf(stderr, " to specify different mask, he has to use different prio.\n"); | |
46 | } | |
47 | ||
48 | static int flower_parse_eth_addr(char *str, int addr_type, int mask_type, | |
49 | struct nlmsghdr *n) | |
50 | { | |
51 | int ret; | |
52 | char addr[ETH_ALEN]; | |
53 | ||
54 | ret = ll_addr_a2n(addr, sizeof(addr), str); | |
55 | if (ret < 0) | |
56 | return -1; | |
57 | addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr)); | |
58 | memset(addr, 0xff, ETH_ALEN); | |
59 | addattr_l(n, MAX_MSG, mask_type, addr, sizeof(addr)); | |
60 | return 0; | |
61 | } | |
62 | ||
63 | static int flower_parse_eth_type(char *str, int type, __be16 *p_eth_type, | |
64 | struct nlmsghdr *n) | |
65 | { | |
66 | int ret; | |
67 | __be16 eth_type; | |
68 | ||
69 | if (matches(str, "ipv4") == 0) { | |
70 | eth_type = htons(ETH_P_IP); | |
71 | } else if (matches(str, "ipv6") == 0) { | |
72 | eth_type = htons(ETH_P_IPV6); | |
73 | } else { | |
74 | __u16 tmp; | |
75 | ||
76 | ret = get_u16(&tmp, str, 16); | |
77 | if (ret) | |
78 | return -1; | |
79 | eth_type = htons(tmp); | |
80 | } | |
81 | addattr16(n, MAX_MSG, type, eth_type); | |
82 | *p_eth_type = eth_type; | |
83 | return 0; | |
84 | } | |
85 | ||
86 | static int flower_parse_ip_proto(char *str, __be16 eth_type, int type, | |
87 | __u8 *p_ip_proto, struct nlmsghdr *n) | |
88 | { | |
89 | int ret; | |
90 | __u8 ip_proto; | |
91 | ||
92 | if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) { | |
93 | fprintf(stderr, "Illegal \"eth_type\" for ip proto\n"); | |
94 | return -1; | |
95 | } | |
96 | if (matches(str, "tcp") == 0) { | |
97 | ip_proto = IPPROTO_TCP; | |
98 | } else if (matches(str, "udp") == 0) { | |
99 | ip_proto = IPPROTO_UDP; | |
100 | } else { | |
101 | ret = get_u8(&ip_proto, str, 16); | |
102 | if (ret) | |
103 | return -1; | |
104 | } | |
105 | addattr8(n, MAX_MSG, type, ip_proto); | |
106 | *p_ip_proto = ip_proto; | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static int flower_parse_ip_addr(char *str, __be16 eth_type, | |
111 | int addr4_type, int mask4_type, | |
112 | int addr6_type, int mask6_type, | |
113 | struct nlmsghdr *n) | |
114 | { | |
115 | int ret; | |
116 | inet_prefix addr; | |
117 | int family; | |
118 | int bits; | |
119 | int i; | |
120 | ||
121 | if (eth_type == htons(ETH_P_IP)) { | |
122 | family = AF_INET; | |
123 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
124 | family = AF_INET6; | |
125 | } else { | |
126 | fprintf(stderr, "Illegal \"eth_type\" for ip address\n"); | |
127 | return -1; | |
128 | } | |
129 | ||
130 | ret = get_prefix(&addr, str, family); | |
131 | if (ret) | |
132 | return -1; | |
133 | ||
134 | if (addr.family != family) | |
135 | return -1; | |
136 | ||
137 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? addr4_type : addr6_type, | |
138 | addr.data, addr.bytelen); | |
139 | ||
140 | memset(addr.data, 0xff, addr.bytelen); | |
141 | bits = addr.bitlen; | |
142 | for (i = 0; i < addr.bytelen / 4; i++) { | |
143 | if (!bits) { | |
144 | addr.data[i] = 0; | |
145 | } else if (bits / 32 >= 1) { | |
146 | bits -= 32; | |
147 | } else { | |
148 | addr.data[i] <<= 32 - bits; | |
149 | addr.data[i] = htonl(addr.data[i]); | |
150 | bits = 0; | |
151 | } | |
152 | } | |
153 | ||
154 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? mask4_type : mask6_type, | |
155 | addr.data, addr.bytelen); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | static int flower_parse_port(char *str, __u8 ip_port, | |
161 | int tcp_type, int udp_type, struct nlmsghdr *n) | |
162 | { | |
163 | int ret; | |
164 | int type; | |
165 | __be16 port; | |
166 | ||
167 | if (ip_port == IPPROTO_TCP) { | |
168 | type = tcp_type; | |
169 | } else if (ip_port == IPPROTO_UDP) { | |
170 | type = udp_type; | |
171 | } else { | |
172 | fprintf(stderr, "Illegal \"ip_proto\" for port\n"); | |
173 | return -1; | |
174 | } | |
175 | ||
176 | ret = get_u16(&port, str, 10); | |
177 | if (ret) | |
178 | return -1; | |
179 | ||
180 | addattr16(n, MAX_MSG, type, htons(port)); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int flower_parse_opt(struct filter_util *qu, char *handle, | |
186 | int argc, char **argv, struct nlmsghdr *n) | |
187 | { | |
188 | int ret; | |
189 | struct tcmsg *t = NLMSG_DATA(n); | |
190 | struct rtattr *tail; | |
191 | __be16 eth_type = 0; | |
192 | __u8 ip_proto = 0xff; | |
193 | ||
194 | if (argc == 0) | |
195 | return 0; | |
196 | ||
197 | if (handle) { | |
198 | ret = get_u32(&t->tcm_handle, handle, 0); | |
199 | if (ret) { | |
200 | fprintf(stderr, "Illegal \"handle\"\n"); | |
201 | return -1; | |
202 | } | |
203 | } | |
204 | ||
205 | tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); | |
206 | addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); | |
207 | ||
208 | while (argc > 0) { | |
209 | if (matches(*argv, "classid") == 0 || | |
210 | matches(*argv, "flowid") == 0) { | |
211 | unsigned handle; | |
212 | ||
213 | NEXT_ARG(); | |
214 | ret = get_tc_classid(&handle, *argv); | |
215 | if (ret) { | |
216 | fprintf(stderr, "Illegal \"classid\"\n"); | |
217 | return -1; | |
218 | } | |
219 | addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); | |
220 | } else if (matches(*argv, "indev") == 0) { | |
221 | char ifname[IFNAMSIZ]; | |
222 | ||
223 | NEXT_ARG(); | |
224 | memset(ifname, 0, sizeof(ifname)); | |
225 | strncpy(ifname, *argv, sizeof(ifname) - 1); | |
226 | addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname); | |
227 | } else if (matches(*argv, "dst_mac") == 0) { | |
228 | NEXT_ARG(); | |
229 | ret = flower_parse_eth_addr(*argv, | |
230 | TCA_FLOWER_KEY_ETH_DST, | |
231 | TCA_FLOWER_KEY_ETH_DST_MASK, | |
232 | n); | |
233 | if (ret < 0) { | |
234 | fprintf(stderr, "Illegal \"dst_mac\"\n"); | |
235 | return -1; | |
236 | } | |
237 | } else if (matches(*argv, "src_mac") == 0) { | |
238 | NEXT_ARG(); | |
239 | ret = flower_parse_eth_addr(*argv, | |
240 | TCA_FLOWER_KEY_ETH_SRC, | |
241 | TCA_FLOWER_KEY_ETH_SRC_MASK, | |
242 | n); | |
243 | if (ret < 0) { | |
244 | fprintf(stderr, "Illegal \"src_mac\"\n"); | |
245 | return -1; | |
246 | } | |
247 | } else if (matches(*argv, "eth_type") == 0) { | |
248 | NEXT_ARG(); | |
249 | ret = flower_parse_eth_type(*argv, | |
250 | TCA_FLOWER_KEY_ETH_TYPE, | |
251 | ð_type, n); | |
252 | if (ret < 0) { | |
253 | fprintf(stderr, "Illegal \"eth_type\"\n"); | |
254 | return -1; | |
255 | } | |
256 | } else if (matches(*argv, "ip_proto") == 0) { | |
257 | NEXT_ARG(); | |
258 | ret = flower_parse_ip_proto(*argv, eth_type, | |
259 | TCA_FLOWER_KEY_IP_PROTO, | |
260 | &ip_proto, n); | |
261 | if (ret < 0) { | |
262 | fprintf(stderr, "Illegal \"ip_proto\"\n"); | |
263 | return -1; | |
264 | } | |
265 | } else if (matches(*argv, "dst_ip") == 0) { | |
266 | NEXT_ARG(); | |
267 | ret = flower_parse_ip_addr(*argv, eth_type, | |
268 | TCA_FLOWER_KEY_IPV4_DST, | |
269 | TCA_FLOWER_KEY_IPV4_DST_MASK, | |
270 | TCA_FLOWER_KEY_IPV6_DST, | |
271 | TCA_FLOWER_KEY_IPV6_DST_MASK, | |
272 | n); | |
273 | if (ret < 0) { | |
274 | fprintf(stderr, "Illegal \"dst_ip\"\n"); | |
275 | return -1; | |
276 | } | |
277 | } else if (matches(*argv, "src_ip") == 0) { | |
278 | NEXT_ARG(); | |
279 | ret = flower_parse_ip_addr(*argv, eth_type, | |
280 | TCA_FLOWER_KEY_IPV4_SRC, | |
281 | TCA_FLOWER_KEY_IPV4_SRC_MASK, | |
282 | TCA_FLOWER_KEY_IPV6_SRC, | |
283 | TCA_FLOWER_KEY_IPV6_SRC_MASK, | |
284 | n); | |
285 | if (ret < 0) { | |
286 | fprintf(stderr, "Illegal \"src_ip\"\n"); | |
287 | return -1; | |
288 | } | |
289 | } else if (matches(*argv, "dst_port") == 0) { | |
290 | NEXT_ARG(); | |
291 | ret = flower_parse_port(*argv, ip_proto, | |
292 | TCA_FLOWER_KEY_TCP_DST, | |
293 | TCA_FLOWER_KEY_UDP_DST, n); | |
294 | if (ret < 0) { | |
295 | fprintf(stderr, "Illegal \"dst_port\"\n"); | |
296 | return -1; | |
297 | } | |
298 | } else if (matches(*argv, "src_port") == 0) { | |
299 | NEXT_ARG(); | |
300 | ret = flower_parse_port(*argv, ip_proto, | |
301 | TCA_FLOWER_KEY_TCP_SRC, | |
302 | TCA_FLOWER_KEY_UDP_SRC, n); | |
303 | if (ret < 0) { | |
304 | fprintf(stderr, "Illegal \"src_port\"\n"); | |
305 | return -1; | |
306 | } | |
307 | } else if (matches(*argv, "action") == 0) { | |
308 | NEXT_ARG(); | |
309 | ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n); | |
310 | if (ret) { | |
311 | fprintf(stderr, "Illegal \"action\"\n"); | |
312 | return -1; | |
313 | } | |
314 | continue; | |
315 | } else if (strcmp(*argv, "help") == 0) { | |
316 | explain(); | |
317 | return -1; | |
318 | } else { | |
319 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
320 | explain(); | |
321 | return -1; | |
322 | } | |
323 | argc--; argv++; | |
324 | } | |
325 | ||
326 | tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail; | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | static int __mask_bits(char *addr, size_t len) | |
332 | { | |
333 | int bits = 0; | |
334 | bool hole = false; | |
335 | int i; | |
336 | int j; | |
337 | ||
338 | for (i = 0; i < len; i++, addr++) { | |
339 | for (j = 7; j >= 0; j--) { | |
340 | if (((*addr) >> j) & 0x1) { | |
341 | if (hole) | |
342 | return -1; | |
343 | bits++; | |
344 | } else if (bits) { | |
345 | hole = true; | |
346 | } else{ | |
347 | return -1; | |
348 | } | |
349 | } | |
350 | } | |
351 | return bits; | |
352 | } | |
353 | ||
354 | static void flower_print_eth_addr(FILE *f, char *name, | |
355 | struct rtattr *addr_attr, | |
356 | struct rtattr *mask_attr) | |
357 | { | |
358 | SPRINT_BUF(b1); | |
359 | int bits; | |
360 | ||
361 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN) | |
362 | return; | |
363 | fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN, | |
364 | 0, b1, sizeof(b1))); | |
365 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN) | |
366 | return; | |
367 | bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN); | |
368 | if (bits < 0) | |
369 | fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN, | |
370 | 0, b1, sizeof(b1))); | |
371 | else if (bits < ETH_ALEN * 8) | |
372 | fprintf(f, "/%d", bits); | |
373 | } | |
374 | ||
375 | static void flower_print_eth_type(FILE *f, __be16 *p_eth_type, | |
376 | struct rtattr *eth_type_attr) | |
377 | { | |
378 | __be16 eth_type; | |
379 | ||
380 | if (!eth_type_attr) | |
381 | return; | |
382 | ||
383 | eth_type = rta_getattr_u16(eth_type_attr); | |
384 | fprintf(f, "\n eth_type "); | |
385 | if (eth_type == htons(ETH_P_IP)) | |
386 | fprintf(f, "ipv4"); | |
387 | else if (eth_type == htons(ETH_P_IPV6)) | |
388 | fprintf(f, "ipv6"); | |
389 | else | |
390 | fprintf(f, "%04x", ntohs(eth_type)); | |
391 | *p_eth_type = eth_type; | |
392 | } | |
393 | ||
394 | static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto, | |
395 | struct rtattr *ip_proto_attr) | |
396 | { | |
397 | __u8 ip_proto; | |
398 | ||
399 | if (!ip_proto_attr) | |
400 | return; | |
401 | ||
402 | ip_proto = rta_getattr_u8(ip_proto_attr); | |
403 | fprintf(f, "\n ip_proto "); | |
404 | if (ip_proto == IPPROTO_TCP) | |
405 | fprintf(f, "tcp"); | |
406 | else if (ip_proto == IPPROTO_UDP) | |
407 | fprintf(f, "udp"); | |
408 | else | |
409 | fprintf(f, "%02x", ip_proto); | |
410 | *p_ip_proto = ip_proto; | |
411 | } | |
412 | ||
413 | static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type, | |
414 | struct rtattr *addr4_attr, | |
415 | struct rtattr *mask4_attr, | |
416 | struct rtattr *addr6_attr, | |
417 | struct rtattr *mask6_attr) | |
418 | { | |
419 | SPRINT_BUF(b1); | |
420 | struct rtattr *addr_attr; | |
421 | struct rtattr *mask_attr; | |
422 | int family; | |
423 | size_t len; | |
424 | int bits; | |
425 | ||
426 | if (eth_type == htons(ETH_P_IP)) { | |
427 | family = AF_INET; | |
428 | addr_attr = addr4_attr; | |
429 | mask_attr = mask4_attr; | |
430 | len = 4; | |
431 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
432 | family = AF_INET6; | |
433 | addr_attr = addr6_attr; | |
434 | mask_attr = mask6_attr; | |
435 | len = 16; | |
436 | } else { | |
437 | return; | |
438 | } | |
439 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != len) | |
440 | return; | |
441 | fprintf(f, "\n %s %s", name, rt_addr_n2a(family, | |
442 | RTA_PAYLOAD(addr_attr), | |
443 | RTA_DATA(addr_attr), | |
444 | b1, sizeof(b1))); | |
445 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != len) | |
446 | return; | |
447 | bits = __mask_bits(RTA_DATA(mask_attr), len); | |
448 | if (bits < 0) | |
449 | fprintf(f, "/%s", rt_addr_n2a(family, | |
450 | RTA_PAYLOAD(mask_attr), | |
451 | RTA_DATA(mask_attr), | |
452 | b1, sizeof(b1))); | |
453 | else if (bits < len * 8) | |
454 | fprintf(f, "/%d", bits); | |
455 | } | |
456 | ||
457 | static void flower_print_port(FILE *f, char *name, __u8 ip_proto, | |
458 | struct rtattr *tcp_attr, | |
459 | struct rtattr *udp_attr) | |
460 | { | |
461 | struct rtattr *attr; | |
462 | ||
463 | if (ip_proto == IPPROTO_TCP) | |
464 | attr = tcp_attr; | |
465 | else if (ip_proto == IPPROTO_UDP) | |
466 | attr = udp_attr; | |
467 | else | |
468 | return; | |
469 | if (!attr) | |
470 | return; | |
471 | fprintf(f, "\n %s %d", name, ntohs(rta_getattr_u16(attr))); | |
472 | } | |
473 | ||
474 | static int flower_print_opt(struct filter_util *qu, FILE *f, | |
475 | struct rtattr *opt, __u32 handle) | |
476 | { | |
477 | struct rtattr *tb[TCA_FLOWER_MAX + 1]; | |
478 | __be16 eth_type = 0; | |
479 | __u8 ip_proto = 0xff; | |
480 | ||
481 | if (!opt) | |
482 | return 0; | |
483 | ||
484 | parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt); | |
485 | ||
486 | if (handle) | |
487 | fprintf(f, "handle 0x%x ", handle); | |
488 | ||
489 | if (tb[TCA_FLOWER_CLASSID]) { | |
490 | SPRINT_BUF(b1); | |
491 | fprintf(f, "classid %s ", | |
492 | sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]), b1)); | |
493 | } | |
494 | ||
495 | if (tb[TCA_FLOWER_INDEV]) { | |
496 | struct rtattr *attr = tb[TCA_FLOWER_INDEV]; | |
497 | ||
498 | fprintf(f, "\n indev %s", rta_getattr_str(attr)); | |
499 | } | |
500 | ||
501 | flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], | |
502 | tb[TCA_FLOWER_KEY_ETH_DST_MASK]); | |
503 | flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], | |
504 | tb[TCA_FLOWER_KEY_ETH_SRC_MASK]); | |
505 | ||
506 | flower_print_eth_type(f, ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]); | |
507 | flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]); | |
508 | ||
509 | flower_print_ip_addr(f, "dst_ip", eth_type, | |
510 | tb[TCA_FLOWER_KEY_IPV4_DST], | |
511 | tb[TCA_FLOWER_KEY_IPV4_DST_MASK], | |
512 | tb[TCA_FLOWER_KEY_IPV6_DST], | |
513 | tb[TCA_FLOWER_KEY_IPV6_DST_MASK]); | |
514 | ||
515 | flower_print_ip_addr(f, "src_ip", eth_type, | |
516 | tb[TCA_FLOWER_KEY_IPV4_SRC], | |
517 | tb[TCA_FLOWER_KEY_IPV4_SRC_MASK], | |
518 | tb[TCA_FLOWER_KEY_IPV6_SRC], | |
519 | tb[TCA_FLOWER_KEY_IPV6_SRC_MASK]); | |
520 | ||
521 | flower_print_port(f, "dst_port", ip_proto, | |
522 | tb[TCA_FLOWER_KEY_TCP_DST], | |
523 | tb[TCA_FLOWER_KEY_UDP_DST]); | |
524 | ||
525 | flower_print_port(f, "src_port", ip_proto, | |
526 | tb[TCA_FLOWER_KEY_TCP_SRC], | |
527 | tb[TCA_FLOWER_KEY_UDP_SRC]); | |
528 | ||
529 | if (tb[TCA_FLOWER_ACT]) { | |
530 | tc_print_action(f, tb[TCA_FLOWER_ACT]); | |
531 | } | |
532 | ||
533 | return 0; | |
534 | } | |
535 | ||
536 | struct filter_util flower_filter_util = { | |
537 | .id = "flower", | |
538 | .parse_fopt = flower_parse_opt, | |
539 | .print_fopt = flower_print_opt, | |
540 | }; |