blob: 7b46ceb14f4665798367dca446cc8b1bb4c32766 [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) {
206 char ifname[IFNAMSIZ];
207
208 NEXT_ARG();
209 memset(ifname, 0, sizeof(ifname));
210 strncpy(ifname, *argv, sizeof(ifname) - 1);
211 addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname);
212 } else if (matches(*argv, "dst_mac") == 0) {
213 NEXT_ARG();
214 ret = flower_parse_eth_addr(*argv,
215 TCA_FLOWER_KEY_ETH_DST,
216 TCA_FLOWER_KEY_ETH_DST_MASK,
217 n);
218 if (ret < 0) {
219 fprintf(stderr, "Illegal \"dst_mac\"\n");
220 return -1;
221 }
222 } else if (matches(*argv, "src_mac") == 0) {
223 NEXT_ARG();
224 ret = flower_parse_eth_addr(*argv,
225 TCA_FLOWER_KEY_ETH_SRC,
226 TCA_FLOWER_KEY_ETH_SRC_MASK,
227 n);
228 if (ret < 0) {
229 fprintf(stderr, "Illegal \"src_mac\"\n");
230 return -1;
231 }
Jiri Pirko30eb3042015-05-15 13:34:04 +0200232 } else if (matches(*argv, "ip_proto") == 0) {
233 NEXT_ARG();
234 ret = flower_parse_ip_proto(*argv, eth_type,
235 TCA_FLOWER_KEY_IP_PROTO,
236 &ip_proto, n);
237 if (ret < 0) {
238 fprintf(stderr, "Illegal \"ip_proto\"\n");
239 return -1;
240 }
241 } else if (matches(*argv, "dst_ip") == 0) {
242 NEXT_ARG();
243 ret = flower_parse_ip_addr(*argv, eth_type,
244 TCA_FLOWER_KEY_IPV4_DST,
245 TCA_FLOWER_KEY_IPV4_DST_MASK,
246 TCA_FLOWER_KEY_IPV6_DST,
247 TCA_FLOWER_KEY_IPV6_DST_MASK,
248 n);
249 if (ret < 0) {
250 fprintf(stderr, "Illegal \"dst_ip\"\n");
251 return -1;
252 }
253 } else if (matches(*argv, "src_ip") == 0) {
254 NEXT_ARG();
255 ret = flower_parse_ip_addr(*argv, eth_type,
256 TCA_FLOWER_KEY_IPV4_SRC,
257 TCA_FLOWER_KEY_IPV4_SRC_MASK,
258 TCA_FLOWER_KEY_IPV6_SRC,
259 TCA_FLOWER_KEY_IPV6_SRC_MASK,
260 n);
261 if (ret < 0) {
262 fprintf(stderr, "Illegal \"src_ip\"\n");
263 return -1;
264 }
265 } else if (matches(*argv, "dst_port") == 0) {
266 NEXT_ARG();
267 ret = flower_parse_port(*argv, ip_proto,
268 TCA_FLOWER_KEY_TCP_DST,
269 TCA_FLOWER_KEY_UDP_DST, n);
270 if (ret < 0) {
271 fprintf(stderr, "Illegal \"dst_port\"\n");
272 return -1;
273 }
274 } else if (matches(*argv, "src_port") == 0) {
275 NEXT_ARG();
276 ret = flower_parse_port(*argv, ip_proto,
277 TCA_FLOWER_KEY_TCP_SRC,
278 TCA_FLOWER_KEY_UDP_SRC, n);
279 if (ret < 0) {
280 fprintf(stderr, "Illegal \"src_port\"\n");
281 return -1;
282 }
283 } else if (matches(*argv, "action") == 0) {
284 NEXT_ARG();
285 ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
286 if (ret) {
287 fprintf(stderr, "Illegal \"action\"\n");
288 return -1;
289 }
290 continue;
291 } else if (strcmp(*argv, "help") == 0) {
292 explain();
293 return -1;
294 } else {
295 fprintf(stderr, "What is \"%s\"?\n", *argv);
296 explain();
297 return -1;
298 }
299 argc--; argv++;
300 }
301
Jamal Hadi Salim488b41d2016-01-10 14:56:31 -0500302parse_done:
Amir Vadaicfcabf12016-07-04 10:34:11 +0300303 addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags);
304
Jamal Hadi Salim488b41d2016-01-10 14:56:31 -0500305 ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type);
306 if (ret) {
307 fprintf(stderr, "Illegal \"eth_type\"(0x%x)\n",
308 ntohs(eth_type));
309 return -1;
310 }
311
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700312 tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail;
Jiri Pirko30eb3042015-05-15 13:34:04 +0200313
314 return 0;
315}
316
317static int __mask_bits(char *addr, size_t len)
318{
319 int bits = 0;
320 bool hole = false;
321 int i;
322 int j;
323
324 for (i = 0; i < len; i++, addr++) {
325 for (j = 7; j >= 0; j--) {
326 if (((*addr) >> j) & 0x1) {
327 if (hole)
328 return -1;
329 bits++;
330 } else if (bits) {
331 hole = true;
332 } else{
333 return -1;
334 }
335 }
336 }
337 return bits;
338}
339
340static void flower_print_eth_addr(FILE *f, char *name,
341 struct rtattr *addr_attr,
342 struct rtattr *mask_attr)
343{
344 SPRINT_BUF(b1);
345 int bits;
346
347 if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN)
348 return;
349 fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN,
350 0, b1, sizeof(b1)));
351 if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN)
352 return;
353 bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN);
354 if (bits < 0)
355 fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN,
356 0, b1, sizeof(b1)));
357 else if (bits < ETH_ALEN * 8)
358 fprintf(f, "/%d", bits);
359}
360
361static void flower_print_eth_type(FILE *f, __be16 *p_eth_type,
362 struct rtattr *eth_type_attr)
363{
364 __be16 eth_type;
365
366 if (!eth_type_attr)
367 return;
368
369 eth_type = rta_getattr_u16(eth_type_attr);
370 fprintf(f, "\n eth_type ");
371 if (eth_type == htons(ETH_P_IP))
372 fprintf(f, "ipv4");
373 else if (eth_type == htons(ETH_P_IPV6))
374 fprintf(f, "ipv6");
375 else
376 fprintf(f, "%04x", ntohs(eth_type));
377 *p_eth_type = eth_type;
378}
379
380static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto,
381 struct rtattr *ip_proto_attr)
382{
383 __u8 ip_proto;
384
385 if (!ip_proto_attr)
386 return;
387
388 ip_proto = rta_getattr_u8(ip_proto_attr);
389 fprintf(f, "\n ip_proto ");
390 if (ip_proto == IPPROTO_TCP)
391 fprintf(f, "tcp");
392 else if (ip_proto == IPPROTO_UDP)
393 fprintf(f, "udp");
394 else
395 fprintf(f, "%02x", ip_proto);
396 *p_ip_proto = ip_proto;
397}
398
399static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type,
400 struct rtattr *addr4_attr,
401 struct rtattr *mask4_attr,
402 struct rtattr *addr6_attr,
403 struct rtattr *mask6_attr)
404{
Jiri Pirko30eb3042015-05-15 13:34:04 +0200405 struct rtattr *addr_attr;
406 struct rtattr *mask_attr;
407 int family;
408 size_t len;
409 int bits;
410
411 if (eth_type == htons(ETH_P_IP)) {
412 family = AF_INET;
413 addr_attr = addr4_attr;
414 mask_attr = mask4_attr;
415 len = 4;
416 } else if (eth_type == htons(ETH_P_IPV6)) {
417 family = AF_INET6;
418 addr_attr = addr6_attr;
419 mask_attr = mask6_attr;
420 len = 16;
421 } else {
422 return;
423 }
424 if (!addr_attr || RTA_PAYLOAD(addr_attr) != len)
425 return;
Phil Sutter7faf1582016-03-22 19:35:18 +0100426 fprintf(f, "\n %s %s", name, rt_addr_n2a_rta(family, addr_attr));
Jiri Pirko30eb3042015-05-15 13:34:04 +0200427 if (!mask_attr || RTA_PAYLOAD(mask_attr) != len)
428 return;
429 bits = __mask_bits(RTA_DATA(mask_attr), len);
430 if (bits < 0)
Phil Sutter7faf1582016-03-22 19:35:18 +0100431 fprintf(f, "/%s", rt_addr_n2a_rta(family, mask_attr));
Jiri Pirko30eb3042015-05-15 13:34:04 +0200432 else if (bits < len * 8)
433 fprintf(f, "/%d", bits);
434}
435
436static void flower_print_port(FILE *f, char *name, __u8 ip_proto,
437 struct rtattr *tcp_attr,
438 struct rtattr *udp_attr)
439{
440 struct rtattr *attr;
441
442 if (ip_proto == IPPROTO_TCP)
443 attr = tcp_attr;
444 else if (ip_proto == IPPROTO_UDP)
445 attr = udp_attr;
446 else
447 return;
448 if (!attr)
449 return;
450 fprintf(f, "\n %s %d", name, ntohs(rta_getattr_u16(attr)));
451}
452
453static int flower_print_opt(struct filter_util *qu, FILE *f,
454 struct rtattr *opt, __u32 handle)
455{
456 struct rtattr *tb[TCA_FLOWER_MAX + 1];
457 __be16 eth_type = 0;
458 __u8 ip_proto = 0xff;
459
460 if (!opt)
461 return 0;
462
463 parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt);
464
465 if (handle)
466 fprintf(f, "handle 0x%x ", handle);
467
468 if (tb[TCA_FLOWER_CLASSID]) {
469 SPRINT_BUF(b1);
470 fprintf(f, "classid %s ",
Jamal Hadi Salim488b41d2016-01-10 14:56:31 -0500471 sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]),
472 b1));
Jiri Pirko30eb3042015-05-15 13:34:04 +0200473 }
474
475 if (tb[TCA_FLOWER_INDEV]) {
476 struct rtattr *attr = tb[TCA_FLOWER_INDEV];
477
478 fprintf(f, "\n indev %s", rta_getattr_str(attr));
479 }
480
481 flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST],
482 tb[TCA_FLOWER_KEY_ETH_DST_MASK]);
483 flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC],
484 tb[TCA_FLOWER_KEY_ETH_SRC_MASK]);
485
486 flower_print_eth_type(f, &eth_type, tb[TCA_FLOWER_KEY_ETH_TYPE]);
487 flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]);
488
489 flower_print_ip_addr(f, "dst_ip", eth_type,
490 tb[TCA_FLOWER_KEY_IPV4_DST],
491 tb[TCA_FLOWER_KEY_IPV4_DST_MASK],
492 tb[TCA_FLOWER_KEY_IPV6_DST],
493 tb[TCA_FLOWER_KEY_IPV6_DST_MASK]);
494
495 flower_print_ip_addr(f, "src_ip", eth_type,
496 tb[TCA_FLOWER_KEY_IPV4_SRC],
497 tb[TCA_FLOWER_KEY_IPV4_SRC_MASK],
498 tb[TCA_FLOWER_KEY_IPV6_SRC],
499 tb[TCA_FLOWER_KEY_IPV6_SRC_MASK]);
500
501 flower_print_port(f, "dst_port", ip_proto,
502 tb[TCA_FLOWER_KEY_TCP_DST],
503 tb[TCA_FLOWER_KEY_UDP_DST]);
504
505 flower_print_port(f, "src_port", ip_proto,
506 tb[TCA_FLOWER_KEY_TCP_SRC],
507 tb[TCA_FLOWER_KEY_UDP_SRC]);
508
Amir Vadaicfcabf12016-07-04 10:34:11 +0300509 if (tb[TCA_FLOWER_FLAGS]) {
510 __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]);
511
512 if (flags & TCA_CLS_FLAGS_SKIP_HW)
513 fprintf(f, "\n skip_hw");
514 if (flags & TCA_CLS_FLAGS_SKIP_SW)
515 fprintf(f, "\n skip_sw");
516 }
517
Jiri Pirko30eb3042015-05-15 13:34:04 +0200518 if (tb[TCA_FLOWER_ACT]) {
519 tc_print_action(f, tb[TCA_FLOWER_ACT]);
520 }
521
522 return 0;
523}
524
525struct filter_util flower_filter_util = {
526 .id = "flower",
527 .parse_fopt = flower_parse_opt,
528 .print_fopt = flower_print_opt,
529};