blob: 6139fe08651a43158d0fd724957f562fb0e5386d [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");
28 fprintf(stderr, " [ action ACTION-SPEC ] [ classid CLASSID ]\n");
29 fprintf(stderr, "\n");
30 fprintf(stderr, "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n");
Stephen Hemminger32a121c2016-03-21 11:48:36 -070031 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, " [ipv4 | ipv6 ] |\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");
Phil Sutter0a83e1e2015-10-23 19:21:17 +020039 fprintf(stderr, " src_port PORT-NUMBER }\n");
Jiri Pirko30eb3042015-05-15 13:34:04 +020040 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
48static 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
Jiri Pirko30eb3042015-05-15 13:34:04 +020063static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
64 __u8 *p_ip_proto, struct nlmsghdr *n)
65{
66 int ret;
67 __u8 ip_proto;
68
69 if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) {
70 fprintf(stderr, "Illegal \"eth_type\" for ip proto\n");
71 return -1;
72 }
73 if (matches(str, "tcp") == 0) {
74 ip_proto = IPPROTO_TCP;
75 } else if (matches(str, "udp") == 0) {
76 ip_proto = IPPROTO_UDP;
77 } else {
78 ret = get_u8(&ip_proto, str, 16);
79 if (ret)
80 return -1;
81 }
82 addattr8(n, MAX_MSG, type, ip_proto);
83 *p_ip_proto = ip_proto;
84 return 0;
85}
86
87static int flower_parse_ip_addr(char *str, __be16 eth_type,
88 int addr4_type, int mask4_type,
89 int addr6_type, int mask6_type,
90 struct nlmsghdr *n)
91{
92 int ret;
93 inet_prefix addr;
94 int family;
95 int bits;
96 int i;
97
98 if (eth_type == htons(ETH_P_IP)) {
99 family = AF_INET;
100 } else if (eth_type == htons(ETH_P_IPV6)) {
101 family = AF_INET6;
102 } else {
103 fprintf(stderr, "Illegal \"eth_type\" for ip address\n");
104 return -1;
105 }
106
107 ret = get_prefix(&addr, str, family);
108 if (ret)
109 return -1;
110
111 if (addr.family != family)
112 return -1;
113
114 addattr_l(n, MAX_MSG, addr.family == AF_INET ? addr4_type : addr6_type,
115 addr.data, addr.bytelen);
116
117 memset(addr.data, 0xff, addr.bytelen);
118 bits = addr.bitlen;
119 for (i = 0; i < addr.bytelen / 4; i++) {
120 if (!bits) {
121 addr.data[i] = 0;
122 } else if (bits / 32 >= 1) {
123 bits -= 32;
124 } else {
125 addr.data[i] <<= 32 - bits;
126 addr.data[i] = htonl(addr.data[i]);
127 bits = 0;
128 }
129 }
130
131 addattr_l(n, MAX_MSG, addr.family == AF_INET ? mask4_type : mask6_type,
132 addr.data, addr.bytelen);
133
134 return 0;
135}
136
137static int flower_parse_port(char *str, __u8 ip_port,
138 int tcp_type, int udp_type, struct nlmsghdr *n)
139{
140 int ret;
141 int type;
142 __be16 port;
143
144 if (ip_port == IPPROTO_TCP) {
145 type = tcp_type;
146 } else if (ip_port == IPPROTO_UDP) {
147 type = udp_type;
148 } else {
149 fprintf(stderr, "Illegal \"ip_proto\" for port\n");
150 return -1;
151 }
152
153 ret = get_u16(&port, str, 10);
154 if (ret)
155 return -1;
156
157 addattr16(n, MAX_MSG, type, htons(port));
158
159 return 0;
160}
161
162static int flower_parse_opt(struct filter_util *qu, char *handle,
163 int argc, char **argv, struct nlmsghdr *n)
164{
165 int ret;
166 struct tcmsg *t = NLMSG_DATA(n);
167 struct rtattr *tail;
Jamal Hadi Salim488b41d2016-01-10 14:56:31 -0500168 __be16 eth_type = TC_H_MIN(t->tcm_info);
Jiri Pirko30eb3042015-05-15 13:34:04 +0200169 __u8 ip_proto = 0xff;
170
Jiri Pirko30eb3042015-05-15 13:34:04 +0200171 if (handle) {
172 ret = get_u32(&t->tcm_handle, handle, 0);
173 if (ret) {
174 fprintf(stderr, "Illegal \"handle\"\n");
175 return -1;
176 }
177 }
178
179 tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len));
180 addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
181
Jamal Hadi Salim488b41d2016-01-10 14:56:31 -0500182 if (argc == 0) {
183 /*at minimal we will match all ethertype packets */
184 goto parse_done;
185 }
186
Jiri Pirko30eb3042015-05-15 13:34:04 +0200187 while (argc > 0) {
188 if (matches(*argv, "classid") == 0 ||
189 matches(*argv, "flowid") == 0) {
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700190 unsigned int handle;
Jiri Pirko30eb3042015-05-15 13:34:04 +0200191
192 NEXT_ARG();
193 ret = get_tc_classid(&handle, *argv);
194 if (ret) {
195 fprintf(stderr, "Illegal \"classid\"\n");
196 return -1;
197 }
198 addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4);
199 } else if (matches(*argv, "indev") == 0) {
200 char ifname[IFNAMSIZ];
201
202 NEXT_ARG();
203 memset(ifname, 0, sizeof(ifname));
204 strncpy(ifname, *argv, sizeof(ifname) - 1);
205 addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname);
206 } else if (matches(*argv, "dst_mac") == 0) {
207 NEXT_ARG();
208 ret = flower_parse_eth_addr(*argv,
209 TCA_FLOWER_KEY_ETH_DST,
210 TCA_FLOWER_KEY_ETH_DST_MASK,
211 n);
212 if (ret < 0) {
213 fprintf(stderr, "Illegal \"dst_mac\"\n");
214 return -1;
215 }
216 } else if (matches(*argv, "src_mac") == 0) {
217 NEXT_ARG();
218 ret = flower_parse_eth_addr(*argv,
219 TCA_FLOWER_KEY_ETH_SRC,
220 TCA_FLOWER_KEY_ETH_SRC_MASK,
221 n);
222 if (ret < 0) {
223 fprintf(stderr, "Illegal \"src_mac\"\n");
224 return -1;
225 }
Jiri Pirko30eb3042015-05-15 13:34:04 +0200226 } else if (matches(*argv, "ip_proto") == 0) {
227 NEXT_ARG();
228 ret = flower_parse_ip_proto(*argv, eth_type,
229 TCA_FLOWER_KEY_IP_PROTO,
230 &ip_proto, n);
231 if (ret < 0) {
232 fprintf(stderr, "Illegal \"ip_proto\"\n");
233 return -1;
234 }
235 } else if (matches(*argv, "dst_ip") == 0) {
236 NEXT_ARG();
237 ret = flower_parse_ip_addr(*argv, eth_type,
238 TCA_FLOWER_KEY_IPV4_DST,
239 TCA_FLOWER_KEY_IPV4_DST_MASK,
240 TCA_FLOWER_KEY_IPV6_DST,
241 TCA_FLOWER_KEY_IPV6_DST_MASK,
242 n);
243 if (ret < 0) {
244 fprintf(stderr, "Illegal \"dst_ip\"\n");
245 return -1;
246 }
247 } else if (matches(*argv, "src_ip") == 0) {
248 NEXT_ARG();
249 ret = flower_parse_ip_addr(*argv, eth_type,
250 TCA_FLOWER_KEY_IPV4_SRC,
251 TCA_FLOWER_KEY_IPV4_SRC_MASK,
252 TCA_FLOWER_KEY_IPV6_SRC,
253 TCA_FLOWER_KEY_IPV6_SRC_MASK,
254 n);
255 if (ret < 0) {
256 fprintf(stderr, "Illegal \"src_ip\"\n");
257 return -1;
258 }
259 } else if (matches(*argv, "dst_port") == 0) {
260 NEXT_ARG();
261 ret = flower_parse_port(*argv, ip_proto,
262 TCA_FLOWER_KEY_TCP_DST,
263 TCA_FLOWER_KEY_UDP_DST, n);
264 if (ret < 0) {
265 fprintf(stderr, "Illegal \"dst_port\"\n");
266 return -1;
267 }
268 } else if (matches(*argv, "src_port") == 0) {
269 NEXT_ARG();
270 ret = flower_parse_port(*argv, ip_proto,
271 TCA_FLOWER_KEY_TCP_SRC,
272 TCA_FLOWER_KEY_UDP_SRC, n);
273 if (ret < 0) {
274 fprintf(stderr, "Illegal \"src_port\"\n");
275 return -1;
276 }
277 } else if (matches(*argv, "action") == 0) {
278 NEXT_ARG();
279 ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
280 if (ret) {
281 fprintf(stderr, "Illegal \"action\"\n");
282 return -1;
283 }
284 continue;
285 } else if (strcmp(*argv, "help") == 0) {
286 explain();
287 return -1;
288 } else {
289 fprintf(stderr, "What is \"%s\"?\n", *argv);
290 explain();
291 return -1;
292 }
293 argc--; argv++;
294 }
295
Jamal Hadi Salim488b41d2016-01-10 14:56:31 -0500296parse_done:
297 ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type);
298 if (ret) {
299 fprintf(stderr, "Illegal \"eth_type\"(0x%x)\n",
300 ntohs(eth_type));
301 return -1;
302 }
303
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700304 tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail;
Jiri Pirko30eb3042015-05-15 13:34:04 +0200305
306 return 0;
307}
308
309static int __mask_bits(char *addr, size_t len)
310{
311 int bits = 0;
312 bool hole = false;
313 int i;
314 int j;
315
316 for (i = 0; i < len; i++, addr++) {
317 for (j = 7; j >= 0; j--) {
318 if (((*addr) >> j) & 0x1) {
319 if (hole)
320 return -1;
321 bits++;
322 } else if (bits) {
323 hole = true;
324 } else{
325 return -1;
326 }
327 }
328 }
329 return bits;
330}
331
332static void flower_print_eth_addr(FILE *f, char *name,
333 struct rtattr *addr_attr,
334 struct rtattr *mask_attr)
335{
336 SPRINT_BUF(b1);
337 int bits;
338
339 if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN)
340 return;
341 fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN,
342 0, b1, sizeof(b1)));
343 if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN)
344 return;
345 bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN);
346 if (bits < 0)
347 fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN,
348 0, b1, sizeof(b1)));
349 else if (bits < ETH_ALEN * 8)
350 fprintf(f, "/%d", bits);
351}
352
353static void flower_print_eth_type(FILE *f, __be16 *p_eth_type,
354 struct rtattr *eth_type_attr)
355{
356 __be16 eth_type;
357
358 if (!eth_type_attr)
359 return;
360
361 eth_type = rta_getattr_u16(eth_type_attr);
362 fprintf(f, "\n eth_type ");
363 if (eth_type == htons(ETH_P_IP))
364 fprintf(f, "ipv4");
365 else if (eth_type == htons(ETH_P_IPV6))
366 fprintf(f, "ipv6");
367 else
368 fprintf(f, "%04x", ntohs(eth_type));
369 *p_eth_type = eth_type;
370}
371
372static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto,
373 struct rtattr *ip_proto_attr)
374{
375 __u8 ip_proto;
376
377 if (!ip_proto_attr)
378 return;
379
380 ip_proto = rta_getattr_u8(ip_proto_attr);
381 fprintf(f, "\n ip_proto ");
382 if (ip_proto == IPPROTO_TCP)
383 fprintf(f, "tcp");
384 else if (ip_proto == IPPROTO_UDP)
385 fprintf(f, "udp");
386 else
387 fprintf(f, "%02x", ip_proto);
388 *p_ip_proto = ip_proto;
389}
390
391static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type,
392 struct rtattr *addr4_attr,
393 struct rtattr *mask4_attr,
394 struct rtattr *addr6_attr,
395 struct rtattr *mask6_attr)
396{
397 SPRINT_BUF(b1);
398 struct rtattr *addr_attr;
399 struct rtattr *mask_attr;
400 int family;
401 size_t len;
402 int bits;
403
404 if (eth_type == htons(ETH_P_IP)) {
405 family = AF_INET;
406 addr_attr = addr4_attr;
407 mask_attr = mask4_attr;
408 len = 4;
409 } else if (eth_type == htons(ETH_P_IPV6)) {
410 family = AF_INET6;
411 addr_attr = addr6_attr;
412 mask_attr = mask6_attr;
413 len = 16;
414 } else {
415 return;
416 }
417 if (!addr_attr || RTA_PAYLOAD(addr_attr) != len)
418 return;
419 fprintf(f, "\n %s %s", name, rt_addr_n2a(family,
420 RTA_PAYLOAD(addr_attr),
421 RTA_DATA(addr_attr),
422 b1, sizeof(b1)));
423 if (!mask_attr || RTA_PAYLOAD(mask_attr) != len)
424 return;
425 bits = __mask_bits(RTA_DATA(mask_attr), len);
426 if (bits < 0)
427 fprintf(f, "/%s", rt_addr_n2a(family,
428 RTA_PAYLOAD(mask_attr),
429 RTA_DATA(mask_attr),
430 b1, sizeof(b1)));
431 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
508 if (tb[TCA_FLOWER_ACT]) {
509 tc_print_action(f, tb[TCA_FLOWER_ACT]);
510 }
511
512 return 0;
513}
514
515struct filter_util flower_filter_util = {
516 .id = "flower",
517 .parse_fopt = flower_parse_opt,
518 .print_fopt = flower_print_opt,
519};