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