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