]>
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> | |
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 | ||
6910d656 SH |
26 | enum flower_endpoint { |
27 | FLOWER_ENDPOINT_SRC, | |
28 | FLOWER_ENDPOINT_DST | |
29 | }; | |
30 | ||
eb3b5696 SH |
31 | enum flower_icmp_field { |
32 | FLOWER_ICMP_FIELD_TYPE, | |
33 | FLOWER_ICMP_FIELD_CODE | |
34 | }; | |
35 | ||
30eb304e JP |
36 | static void explain(void) |
37 | { | |
512caeb2 SH |
38 | fprintf(stderr, |
39 | "Usage: ... flower [ MATCH-LIST ]\n" | |
40 | " [ skip_sw | skip_hw ]\n" | |
41 | " [ action ACTION-SPEC ] [ classid CLASSID ]\n" | |
42 | "\n" | |
43 | "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n" | |
44 | " MATCH := { indev DEV-NAME |\n" | |
45 | " vlan_id VID |\n" | |
46 | " vlan_prio PRIORITY |\n" | |
47 | " vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n" | |
8578bb73 SH |
48 | " dst_mac MAC-ADDR |\n" |
49 | " src_mac MAC-ADDR |\n" | |
eb3b5696 | 50 | " ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n" |
10da5528 SH |
51 | " dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" |
52 | " src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" | |
512caeb2 | 53 | " dst_port PORT-NUMBER |\n" |
bb9b63b1 | 54 | " src_port PORT-NUMBER |\n" |
eb3b5696 | 55 | " type ICMP-TYPE |\n" |
bf73c650 | 56 | " code ICMP-CODE |\n" |
10da5528 SH |
57 | " enc_dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" |
58 | " enc_src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" | |
bf73c650 HHZ |
59 | " enc_key_id [ KEY-ID ] |\n" |
60 | " enc_dst_port [ UDP-PORT ] }\n" | |
512caeb2 SH |
61 | " FILTERID := X:Y:Z\n" |
62 | " ACTION-SPEC := ... look at individual actions\n" | |
63 | "\n" | |
328374dc | 64 | "NOTE: CLASSID, IP-PROTO are parsed as hexadecimal input.\n" |
512caeb2 SH |
65 | "NOTE: There can be only used one mask per one prio. If user needs\n" |
66 | " to specify different mask, he has to use different prio.\n"); | |
30eb304e JP |
67 | } |
68 | ||
69 | static int flower_parse_eth_addr(char *str, int addr_type, int mask_type, | |
70 | struct nlmsghdr *n) | |
71 | { | |
8578bb73 SH |
72 | int ret; |
73 | char addr[ETH_ALEN]; | |
30eb304e JP |
74 | |
75 | ret = ll_addr_a2n(addr, sizeof(addr), str); | |
76 | if (ret < 0) | |
8578bb73 | 77 | return -1; |
30eb304e | 78 | addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr)); |
8578bb73 | 79 | memset(addr, 0xff, ETH_ALEN); |
30eb304e | 80 | addattr_l(n, MAX_MSG, mask_type, addr, sizeof(addr)); |
8578bb73 | 81 | return 0; |
30eb304e JP |
82 | } |
83 | ||
745d9172 | 84 | static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type, |
512caeb2 SH |
85 | __be16 *p_vlan_eth_type, |
86 | struct nlmsghdr *n) | |
745d9172 HHZ |
87 | { |
88 | __be16 vlan_eth_type; | |
89 | ||
90 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
91 | fprintf(stderr, |
92 | "Can't set \"vlan_ethtype\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
93 | return -1; |
94 | } | |
95 | ||
96 | if (ll_proto_a2n(&vlan_eth_type, str)) | |
97 | invarg("invalid vlan_ethtype", str); | |
98 | addattr16(n, MAX_MSG, type, vlan_eth_type); | |
99 | *p_vlan_eth_type = vlan_eth_type; | |
100 | return 0; | |
101 | } | |
102 | ||
30eb304e JP |
103 | static int flower_parse_ip_proto(char *str, __be16 eth_type, int type, |
104 | __u8 *p_ip_proto, struct nlmsghdr *n) | |
105 | { | |
106 | int ret; | |
107 | __u8 ip_proto; | |
108 | ||
eb3b5696 SH |
109 | if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) |
110 | goto err; | |
111 | ||
30eb304e JP |
112 | if (matches(str, "tcp") == 0) { |
113 | ip_proto = IPPROTO_TCP; | |
114 | } else if (matches(str, "udp") == 0) { | |
115 | ip_proto = IPPROTO_UDP; | |
a1fb0d48 SH |
116 | } else if (matches(str, "sctp") == 0) { |
117 | ip_proto = IPPROTO_SCTP; | |
eb3b5696 SH |
118 | } else if (matches(str, "icmp") == 0) { |
119 | if (eth_type != htons(ETH_P_IP)) | |
120 | goto err; | |
121 | ip_proto = IPPROTO_ICMP; | |
122 | } else if (matches(str, "icmpv6") == 0) { | |
123 | if (eth_type != htons(ETH_P_IPV6)) | |
124 | goto err; | |
125 | ip_proto = IPPROTO_ICMPV6; | |
30eb304e JP |
126 | } else { |
127 | ret = get_u8(&ip_proto, str, 16); | |
128 | if (ret) | |
129 | return -1; | |
130 | } | |
131 | addattr8(n, MAX_MSG, type, ip_proto); | |
132 | *p_ip_proto = ip_proto; | |
133 | return 0; | |
eb3b5696 SH |
134 | |
135 | err: | |
136 | fprintf(stderr, "Illegal \"eth_type\" for ip proto\n"); | |
137 | return -1; | |
30eb304e JP |
138 | } |
139 | ||
140 | static int flower_parse_ip_addr(char *str, __be16 eth_type, | |
141 | int addr4_type, int mask4_type, | |
142 | int addr6_type, int mask6_type, | |
143 | struct nlmsghdr *n) | |
144 | { | |
145 | int ret; | |
146 | inet_prefix addr; | |
147 | int family; | |
148 | int bits; | |
149 | int i; | |
150 | ||
151 | if (eth_type == htons(ETH_P_IP)) { | |
152 | family = AF_INET; | |
153 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
154 | family = AF_INET6; | |
bb9b63b1 AV |
155 | } else if (!eth_type) { |
156 | family = AF_UNSPEC; | |
30eb304e | 157 | } else { |
30eb304e JP |
158 | return -1; |
159 | } | |
160 | ||
161 | ret = get_prefix(&addr, str, family); | |
162 | if (ret) | |
163 | return -1; | |
164 | ||
bb9b63b1 AV |
165 | if (family && (addr.family != family)) { |
166 | fprintf(stderr, "Illegal \"eth_type\" for ip address\n"); | |
30eb304e | 167 | return -1; |
bb9b63b1 | 168 | } |
30eb304e JP |
169 | |
170 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? addr4_type : addr6_type, | |
171 | addr.data, addr.bytelen); | |
172 | ||
173 | memset(addr.data, 0xff, addr.bytelen); | |
174 | bits = addr.bitlen; | |
175 | for (i = 0; i < addr.bytelen / 4; i++) { | |
176 | if (!bits) { | |
177 | addr.data[i] = 0; | |
178 | } else if (bits / 32 >= 1) { | |
179 | bits -= 32; | |
180 | } else { | |
181 | addr.data[i] <<= 32 - bits; | |
182 | addr.data[i] = htonl(addr.data[i]); | |
183 | bits = 0; | |
184 | } | |
185 | } | |
186 | ||
187 | addattr_l(n, MAX_MSG, addr.family == AF_INET ? mask4_type : mask6_type, | |
188 | addr.data, addr.bytelen); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
eb3b5696 SH |
193 | static int flower_icmp_attr_type(__be16 eth_type, __u8 ip_proto, |
194 | enum flower_icmp_field field) | |
195 | { | |
196 | if (eth_type == htons(ETH_P_IP) && ip_proto == IPPROTO_ICMP) | |
197 | return field == FLOWER_ICMP_FIELD_CODE ? | |
198 | TCA_FLOWER_KEY_ICMPV4_CODE : | |
199 | TCA_FLOWER_KEY_ICMPV4_TYPE; | |
200 | else if (eth_type == htons(ETH_P_IPV6) && ip_proto == IPPROTO_ICMPV6) | |
201 | return field == FLOWER_ICMP_FIELD_CODE ? | |
202 | TCA_FLOWER_KEY_ICMPV6_CODE : | |
203 | TCA_FLOWER_KEY_ICMPV6_TYPE; | |
204 | ||
205 | return -1; | |
206 | } | |
207 | ||
208 | static int flower_parse_icmp(char *str, __u16 eth_type, __u8 ip_proto, | |
209 | enum flower_icmp_field field, struct nlmsghdr *n) | |
210 | { | |
211 | int ret; | |
212 | int type; | |
213 | uint8_t value; | |
214 | ||
215 | type = flower_icmp_attr_type(eth_type, ip_proto, field); | |
216 | if (type < 0) | |
217 | return -1; | |
218 | ||
219 | ret = get_u8(&value, str, 10); | |
220 | if (ret) | |
221 | return -1; | |
222 | ||
223 | addattr8(n, MAX_MSG, type, value); | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
6910d656 | 228 | static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint) |
30eb304e | 229 | { |
6bd5b80c | 230 | if (ip_proto == IPPROTO_TCP) |
6910d656 SH |
231 | return endpoint == FLOWER_ENDPOINT_SRC ? |
232 | TCA_FLOWER_KEY_TCP_SRC : | |
a1fb0d48 | 233 | TCA_FLOWER_KEY_TCP_DST; |
6bd5b80c | 234 | else if (ip_proto == IPPROTO_UDP) |
6910d656 SH |
235 | return endpoint == FLOWER_ENDPOINT_SRC ? |
236 | TCA_FLOWER_KEY_UDP_SRC : | |
a1fb0d48 | 237 | TCA_FLOWER_KEY_UDP_DST; |
6bd5b80c | 238 | else if (ip_proto == IPPROTO_SCTP) |
6910d656 SH |
239 | return endpoint == FLOWER_ENDPOINT_SRC ? |
240 | TCA_FLOWER_KEY_SCTP_SRC : | |
a1fb0d48 | 241 | TCA_FLOWER_KEY_SCTP_DST; |
6bd5b80c | 242 | else |
30eb304e | 243 | return -1; |
a1fb0d48 SH |
244 | } |
245 | ||
6910d656 SH |
246 | static int flower_parse_port(char *str, __u8 ip_proto, |
247 | enum flower_endpoint endpoint, | |
a1fb0d48 SH |
248 | struct nlmsghdr *n) |
249 | { | |
250 | int ret; | |
251 | int type; | |
252 | __be16 port; | |
253 | ||
6910d656 | 254 | type = flower_port_attr_type(ip_proto, endpoint); |
a1fb0d48 SH |
255 | if (type < 0) |
256 | return -1; | |
30eb304e | 257 | |
9f7401fa | 258 | ret = get_be16(&port, str, 10); |
30eb304e JP |
259 | if (ret) |
260 | return -1; | |
261 | ||
9f7401fa | 262 | addattr16(n, MAX_MSG, type, port); |
30eb304e JP |
263 | |
264 | return 0; | |
265 | } | |
266 | ||
bb9b63b1 AV |
267 | static int flower_parse_key_id(const char *str, int type, struct nlmsghdr *n) |
268 | { | |
269 | int ret; | |
270 | __be32 key_id; | |
271 | ||
272 | ret = get_be32(&key_id, str, 10); | |
273 | if (!ret) | |
274 | addattr32(n, MAX_MSG, type, key_id); | |
275 | ||
276 | return ret; | |
277 | } | |
278 | ||
41aa17ff HHZ |
279 | static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n) |
280 | { | |
281 | int ret; | |
282 | __be16 port; | |
283 | ||
284 | ret = get_be16(&port, str, 10); | |
285 | if (ret) | |
286 | return -1; | |
287 | ||
288 | addattr16(n, MAX_MSG, type, port); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
30eb304e JP |
293 | static int flower_parse_opt(struct filter_util *qu, char *handle, |
294 | int argc, char **argv, struct nlmsghdr *n) | |
295 | { | |
296 | int ret; | |
297 | struct tcmsg *t = NLMSG_DATA(n); | |
298 | struct rtattr *tail; | |
488b41d0 | 299 | __be16 eth_type = TC_H_MIN(t->tcm_info); |
745d9172 | 300 | __be16 vlan_ethtype = 0; |
30eb304e | 301 | __u8 ip_proto = 0xff; |
cfcabf18 | 302 | __u32 flags = 0; |
30eb304e | 303 | |
30eb304e JP |
304 | if (handle) { |
305 | ret = get_u32(&t->tcm_handle, handle, 0); | |
306 | if (ret) { | |
307 | fprintf(stderr, "Illegal \"handle\"\n"); | |
308 | return -1; | |
309 | } | |
310 | } | |
311 | ||
312 | tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); | |
313 | addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); | |
314 | ||
488b41d0 JHS |
315 | if (argc == 0) { |
316 | /*at minimal we will match all ethertype packets */ | |
317 | goto parse_done; | |
318 | } | |
319 | ||
30eb304e JP |
320 | while (argc > 0) { |
321 | if (matches(*argv, "classid") == 0 || | |
322 | matches(*argv, "flowid") == 0) { | |
32a121cb | 323 | unsigned int handle; |
30eb304e JP |
324 | |
325 | NEXT_ARG(); | |
326 | ret = get_tc_classid(&handle, *argv); | |
327 | if (ret) { | |
328 | fprintf(stderr, "Illegal \"classid\"\n"); | |
329 | return -1; | |
330 | } | |
331 | addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); | |
cfcabf18 AV |
332 | } else if (matches(*argv, "skip_hw") == 0) { |
333 | flags |= TCA_CLS_FLAGS_SKIP_HW; | |
334 | } else if (matches(*argv, "skip_sw") == 0) { | |
335 | flags |= TCA_CLS_FLAGS_SKIP_SW; | |
30eb304e | 336 | } else if (matches(*argv, "indev") == 0) { |
d17b136f | 337 | char ifname[IFNAMSIZ] = {}; |
30eb304e JP |
338 | |
339 | NEXT_ARG(); | |
30eb304e JP |
340 | strncpy(ifname, *argv, sizeof(ifname) - 1); |
341 | addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname); | |
745d9172 HHZ |
342 | } else if (matches(*argv, "vlan_id") == 0) { |
343 | __u16 vid; | |
344 | ||
345 | NEXT_ARG(); | |
346 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
347 | fprintf(stderr, |
348 | "Can't set \"vlan_id\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
349 | return -1; |
350 | } | |
351 | ret = get_u16(&vid, *argv, 10); | |
352 | if (ret < 0 || vid & ~0xfff) { | |
353 | fprintf(stderr, "Illegal \"vlan_id\"\n"); | |
354 | return -1; | |
355 | } | |
356 | addattr16(n, MAX_MSG, TCA_FLOWER_KEY_VLAN_ID, vid); | |
357 | } else if (matches(*argv, "vlan_prio") == 0) { | |
358 | __u8 vlan_prio; | |
359 | ||
360 | NEXT_ARG(); | |
361 | if (eth_type != htons(ETH_P_8021Q)) { | |
512caeb2 SH |
362 | fprintf(stderr, |
363 | "Can't set \"vlan_prio\" if ethertype isn't 802.1Q\n"); | |
745d9172 HHZ |
364 | return -1; |
365 | } | |
366 | ret = get_u8(&vlan_prio, *argv, 10); | |
367 | if (ret < 0 || vlan_prio & ~0x7) { | |
368 | fprintf(stderr, "Illegal \"vlan_prio\"\n"); | |
369 | return -1; | |
370 | } | |
512caeb2 SH |
371 | addattr8(n, MAX_MSG, |
372 | TCA_FLOWER_KEY_VLAN_PRIO, vlan_prio); | |
745d9172 HHZ |
373 | } else if (matches(*argv, "vlan_ethtype") == 0) { |
374 | NEXT_ARG(); | |
375 | ret = flower_parse_vlan_eth_type(*argv, eth_type, | |
512caeb2 SH |
376 | TCA_FLOWER_KEY_VLAN_ETH_TYPE, |
377 | &vlan_ethtype, n); | |
745d9172 HHZ |
378 | if (ret < 0) |
379 | return -1; | |
30eb304e JP |
380 | } else if (matches(*argv, "dst_mac") == 0) { |
381 | NEXT_ARG(); | |
382 | ret = flower_parse_eth_addr(*argv, | |
383 | TCA_FLOWER_KEY_ETH_DST, | |
384 | TCA_FLOWER_KEY_ETH_DST_MASK, | |
385 | n); | |
386 | if (ret < 0) { | |
387 | fprintf(stderr, "Illegal \"dst_mac\"\n"); | |
388 | return -1; | |
389 | } | |
390 | } else if (matches(*argv, "src_mac") == 0) { | |
391 | NEXT_ARG(); | |
392 | ret = flower_parse_eth_addr(*argv, | |
393 | TCA_FLOWER_KEY_ETH_SRC, | |
394 | TCA_FLOWER_KEY_ETH_SRC_MASK, | |
395 | n); | |
396 | if (ret < 0) { | |
397 | fprintf(stderr, "Illegal \"src_mac\"\n"); | |
398 | return -1; | |
399 | } | |
30eb304e JP |
400 | } else if (matches(*argv, "ip_proto") == 0) { |
401 | NEXT_ARG(); | |
745d9172 HHZ |
402 | ret = flower_parse_ip_proto(*argv, vlan_ethtype ? |
403 | vlan_ethtype : eth_type, | |
30eb304e JP |
404 | TCA_FLOWER_KEY_IP_PROTO, |
405 | &ip_proto, n); | |
406 | if (ret < 0) { | |
407 | fprintf(stderr, "Illegal \"ip_proto\"\n"); | |
408 | return -1; | |
409 | } | |
410 | } else if (matches(*argv, "dst_ip") == 0) { | |
411 | NEXT_ARG(); | |
745d9172 HHZ |
412 | ret = flower_parse_ip_addr(*argv, vlan_ethtype ? |
413 | vlan_ethtype : eth_type, | |
30eb304e JP |
414 | TCA_FLOWER_KEY_IPV4_DST, |
415 | TCA_FLOWER_KEY_IPV4_DST_MASK, | |
416 | TCA_FLOWER_KEY_IPV6_DST, | |
417 | TCA_FLOWER_KEY_IPV6_DST_MASK, | |
418 | n); | |
419 | if (ret < 0) { | |
420 | fprintf(stderr, "Illegal \"dst_ip\"\n"); | |
421 | return -1; | |
422 | } | |
423 | } else if (matches(*argv, "src_ip") == 0) { | |
424 | NEXT_ARG(); | |
745d9172 HHZ |
425 | ret = flower_parse_ip_addr(*argv, vlan_ethtype ? |
426 | vlan_ethtype : eth_type, | |
30eb304e JP |
427 | TCA_FLOWER_KEY_IPV4_SRC, |
428 | TCA_FLOWER_KEY_IPV4_SRC_MASK, | |
429 | TCA_FLOWER_KEY_IPV6_SRC, | |
430 | TCA_FLOWER_KEY_IPV6_SRC_MASK, | |
431 | n); | |
432 | if (ret < 0) { | |
433 | fprintf(stderr, "Illegal \"src_ip\"\n"); | |
434 | return -1; | |
435 | } | |
436 | } else if (matches(*argv, "dst_port") == 0) { | |
437 | NEXT_ARG(); | |
6910d656 SH |
438 | ret = flower_parse_port(*argv, ip_proto, |
439 | FLOWER_ENDPOINT_DST, n); | |
30eb304e JP |
440 | if (ret < 0) { |
441 | fprintf(stderr, "Illegal \"dst_port\"\n"); | |
442 | return -1; | |
443 | } | |
444 | } else if (matches(*argv, "src_port") == 0) { | |
445 | NEXT_ARG(); | |
6910d656 SH |
446 | ret = flower_parse_port(*argv, ip_proto, |
447 | FLOWER_ENDPOINT_SRC, n); | |
30eb304e JP |
448 | if (ret < 0) { |
449 | fprintf(stderr, "Illegal \"src_port\"\n"); | |
450 | return -1; | |
451 | } | |
eb3b5696 SH |
452 | } else if (matches(*argv, "type") == 0) { |
453 | NEXT_ARG(); | |
454 | ret = flower_parse_icmp(*argv, eth_type, ip_proto, | |
455 | FLOWER_ICMP_FIELD_TYPE, n); | |
456 | if (ret < 0) { | |
457 | fprintf(stderr, "Illegal \"icmp type\"\n"); | |
458 | return -1; | |
459 | } | |
460 | } else if (matches(*argv, "code") == 0) { | |
461 | NEXT_ARG(); | |
462 | ret = flower_parse_icmp(*argv, eth_type, ip_proto, | |
463 | FLOWER_ICMP_FIELD_CODE, n); | |
464 | if (ret < 0) { | |
465 | fprintf(stderr, "Illegal \"icmp code\"\n"); | |
466 | return -1; | |
467 | } | |
bb9b63b1 AV |
468 | } else if (matches(*argv, "enc_dst_ip") == 0) { |
469 | NEXT_ARG(); | |
470 | ret = flower_parse_ip_addr(*argv, 0, | |
471 | TCA_FLOWER_KEY_ENC_IPV4_DST, | |
472 | TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, | |
473 | TCA_FLOWER_KEY_ENC_IPV6_DST, | |
474 | TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, | |
475 | n); | |
476 | if (ret < 0) { | |
477 | fprintf(stderr, "Illegal \"enc_dst_ip\"\n"); | |
478 | return -1; | |
479 | } | |
480 | } else if (matches(*argv, "enc_src_ip") == 0) { | |
481 | NEXT_ARG(); | |
482 | ret = flower_parse_ip_addr(*argv, 0, | |
483 | TCA_FLOWER_KEY_ENC_IPV4_SRC, | |
484 | TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, | |
485 | TCA_FLOWER_KEY_ENC_IPV6_SRC, | |
486 | TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, | |
487 | n); | |
488 | if (ret < 0) { | |
489 | fprintf(stderr, "Illegal \"enc_src_ip\"\n"); | |
490 | return -1; | |
491 | } | |
492 | } else if (matches(*argv, "enc_key_id") == 0) { | |
493 | NEXT_ARG(); | |
494 | ret = flower_parse_key_id(*argv, | |
495 | TCA_FLOWER_KEY_ENC_KEY_ID, n); | |
496 | if (ret < 0) { | |
497 | fprintf(stderr, "Illegal \"enc_key_id\"\n"); | |
498 | return -1; | |
499 | } | |
41aa17ff HHZ |
500 | } else if (matches(*argv, "enc_dst_port") == 0) { |
501 | NEXT_ARG(); | |
502 | ret = flower_parse_enc_port(*argv, | |
503 | TCA_FLOWER_KEY_ENC_UDP_DST_PORT, n); | |
504 | if (ret < 0) { | |
505 | fprintf(stderr, "Illegal \"enc_dst_port\"\n"); | |
506 | return -1; | |
507 | } | |
30eb304e JP |
508 | } else if (matches(*argv, "action") == 0) { |
509 | NEXT_ARG(); | |
510 | ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n); | |
511 | if (ret) { | |
512 | fprintf(stderr, "Illegal \"action\"\n"); | |
513 | return -1; | |
514 | } | |
515 | continue; | |
516 | } else if (strcmp(*argv, "help") == 0) { | |
517 | explain(); | |
518 | return -1; | |
519 | } else { | |
520 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
521 | explain(); | |
522 | return -1; | |
523 | } | |
524 | argc--; argv++; | |
525 | } | |
526 | ||
488b41d0 | 527 | parse_done: |
c85609b2 RD |
528 | ret = addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags); |
529 | if (ret) | |
530 | return ret; | |
cfcabf18 | 531 | |
488b41d0 | 532 | ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type); |
00697ca1 RD |
533 | if (ret) |
534 | return ret; | |
488b41d0 | 535 | |
32a121cb | 536 | tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail; |
30eb304e JP |
537 | |
538 | return 0; | |
539 | } | |
540 | ||
541 | static int __mask_bits(char *addr, size_t len) | |
542 | { | |
543 | int bits = 0; | |
544 | bool hole = false; | |
545 | int i; | |
546 | int j; | |
547 | ||
548 | for (i = 0; i < len; i++, addr++) { | |
549 | for (j = 7; j >= 0; j--) { | |
550 | if (((*addr) >> j) & 0x1) { | |
551 | if (hole) | |
552 | return -1; | |
553 | bits++; | |
554 | } else if (bits) { | |
555 | hole = true; | |
556 | } else{ | |
557 | return -1; | |
558 | } | |
559 | } | |
560 | } | |
561 | return bits; | |
562 | } | |
563 | ||
564 | static void flower_print_eth_addr(FILE *f, char *name, | |
565 | struct rtattr *addr_attr, | |
566 | struct rtattr *mask_attr) | |
567 | { | |
568 | SPRINT_BUF(b1); | |
569 | int bits; | |
570 | ||
571 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN) | |
572 | return; | |
573 | fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN, | |
574 | 0, b1, sizeof(b1))); | |
575 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN) | |
576 | return; | |
577 | bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN); | |
578 | if (bits < 0) | |
579 | fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN, | |
580 | 0, b1, sizeof(b1))); | |
581 | else if (bits < ETH_ALEN * 8) | |
582 | fprintf(f, "/%d", bits); | |
583 | } | |
584 | ||
585 | static void flower_print_eth_type(FILE *f, __be16 *p_eth_type, | |
586 | struct rtattr *eth_type_attr) | |
587 | { | |
588 | __be16 eth_type; | |
589 | ||
590 | if (!eth_type_attr) | |
591 | return; | |
592 | ||
593 | eth_type = rta_getattr_u16(eth_type_attr); | |
594 | fprintf(f, "\n eth_type "); | |
595 | if (eth_type == htons(ETH_P_IP)) | |
596 | fprintf(f, "ipv4"); | |
597 | else if (eth_type == htons(ETH_P_IPV6)) | |
598 | fprintf(f, "ipv6"); | |
599 | else | |
600 | fprintf(f, "%04x", ntohs(eth_type)); | |
601 | *p_eth_type = eth_type; | |
602 | } | |
603 | ||
604 | static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto, | |
605 | struct rtattr *ip_proto_attr) | |
606 | { | |
607 | __u8 ip_proto; | |
608 | ||
609 | if (!ip_proto_attr) | |
610 | return; | |
611 | ||
612 | ip_proto = rta_getattr_u8(ip_proto_attr); | |
613 | fprintf(f, "\n ip_proto "); | |
614 | if (ip_proto == IPPROTO_TCP) | |
615 | fprintf(f, "tcp"); | |
616 | else if (ip_proto == IPPROTO_UDP) | |
617 | fprintf(f, "udp"); | |
a1fb0d48 SH |
618 | else if (ip_proto == IPPROTO_SCTP) |
619 | fprintf(f, "sctp"); | |
eb3b5696 SH |
620 | else if (ip_proto == IPPROTO_ICMP) |
621 | fprintf(f, "icmp"); | |
622 | else if (ip_proto == IPPROTO_ICMPV6) | |
623 | fprintf(f, "icmpv6"); | |
30eb304e JP |
624 | else |
625 | fprintf(f, "%02x", ip_proto); | |
626 | *p_ip_proto = ip_proto; | |
627 | } | |
628 | ||
629 | static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type, | |
630 | struct rtattr *addr4_attr, | |
631 | struct rtattr *mask4_attr, | |
632 | struct rtattr *addr6_attr, | |
633 | struct rtattr *mask6_attr) | |
634 | { | |
30eb304e JP |
635 | struct rtattr *addr_attr; |
636 | struct rtattr *mask_attr; | |
637 | int family; | |
638 | size_t len; | |
639 | int bits; | |
640 | ||
641 | if (eth_type == htons(ETH_P_IP)) { | |
642 | family = AF_INET; | |
643 | addr_attr = addr4_attr; | |
644 | mask_attr = mask4_attr; | |
645 | len = 4; | |
646 | } else if (eth_type == htons(ETH_P_IPV6)) { | |
647 | family = AF_INET6; | |
648 | addr_attr = addr6_attr; | |
649 | mask_attr = mask6_attr; | |
650 | len = 16; | |
651 | } else { | |
652 | return; | |
653 | } | |
654 | if (!addr_attr || RTA_PAYLOAD(addr_attr) != len) | |
655 | return; | |
7faf1588 | 656 | fprintf(f, "\n %s %s", name, rt_addr_n2a_rta(family, addr_attr)); |
30eb304e JP |
657 | if (!mask_attr || RTA_PAYLOAD(mask_attr) != len) |
658 | return; | |
659 | bits = __mask_bits(RTA_DATA(mask_attr), len); | |
660 | if (bits < 0) | |
7faf1588 | 661 | fprintf(f, "/%s", rt_addr_n2a_rta(family, mask_attr)); |
30eb304e JP |
662 | else if (bits < len * 8) |
663 | fprintf(f, "/%d", bits); | |
664 | } | |
665 | ||
a1fb0d48 | 666 | static void flower_print_port(FILE *f, char *name, struct rtattr *attr) |
30eb304e | 667 | { |
6bd5b80c SH |
668 | if (attr) |
669 | fprintf(f, "\n %s %d", name, rta_getattr_be16(attr)); | |
30eb304e JP |
670 | } |
671 | ||
bb9b63b1 AV |
672 | static void flower_print_key_id(FILE *f, const char *name, |
673 | struct rtattr *attr) | |
674 | { | |
675 | if (attr) | |
676 | fprintf(f, "\n %s %d", name, rta_getattr_be32(attr)); | |
677 | } | |
678 | ||
eb3b5696 SH |
679 | static void flower_print_icmp(FILE *f, char *name, struct rtattr *attr) |
680 | { | |
681 | if (attr) | |
682 | fprintf(f, "\n %s %d", name, rta_getattr_u8(attr)); | |
683 | } | |
684 | ||
30eb304e JP |
685 | static int flower_print_opt(struct filter_util *qu, FILE *f, |
686 | struct rtattr *opt, __u32 handle) | |
687 | { | |
688 | struct rtattr *tb[TCA_FLOWER_MAX + 1]; | |
689 | __be16 eth_type = 0; | |
690 | __u8 ip_proto = 0xff; | |
6bd5b80c | 691 | int nl_type; |
30eb304e JP |
692 | |
693 | if (!opt) | |
694 | return 0; | |
695 | ||
696 | parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt); | |
697 | ||
698 | if (handle) | |
699 | fprintf(f, "handle 0x%x ", handle); | |
700 | ||
701 | if (tb[TCA_FLOWER_CLASSID]) { | |
702 | SPRINT_BUF(b1); | |
703 | fprintf(f, "classid %s ", | |
488b41d0 JHS |
704 | sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]), |
705 | b1)); | |
30eb304e JP |
706 | } |
707 | ||
708 | if (tb[TCA_FLOWER_INDEV]) { | |
709 | struct rtattr *attr = tb[TCA_FLOWER_INDEV]; | |
710 | ||
711 | fprintf(f, "\n indev %s", rta_getattr_str(attr)); | |
712 | } | |
713 | ||
745d9172 HHZ |
714 | if (tb[TCA_FLOWER_KEY_VLAN_ID]) { |
715 | struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID]; | |
716 | ||
717 | fprintf(f, "\n vlan_id %d", rta_getattr_u16(attr)); | |
718 | } | |
719 | ||
720 | if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { | |
721 | struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_PRIO]; | |
722 | ||
723 | fprintf(f, "\n vlan_prio %d", rta_getattr_u8(attr)); | |
724 | } | |
725 | ||
30eb304e JP |
726 | flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST], |
727 | tb[TCA_FLOWER_KEY_ETH_DST_MASK]); | |
728 | flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC], | |
729 | tb[TCA_FLOWER_KEY_ETH_SRC_MASK]); | |
730 | ||
731 | flower_print_eth_type(f, ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]); | |
732 | flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]); | |
733 | ||
734 | flower_print_ip_addr(f, "dst_ip", eth_type, | |
735 | tb[TCA_FLOWER_KEY_IPV4_DST], | |
736 | tb[TCA_FLOWER_KEY_IPV4_DST_MASK], | |
737 | tb[TCA_FLOWER_KEY_IPV6_DST], | |
738 | tb[TCA_FLOWER_KEY_IPV6_DST_MASK]); | |
739 | ||
740 | flower_print_ip_addr(f, "src_ip", eth_type, | |
741 | tb[TCA_FLOWER_KEY_IPV4_SRC], | |
742 | tb[TCA_FLOWER_KEY_IPV4_SRC_MASK], | |
743 | tb[TCA_FLOWER_KEY_IPV6_SRC], | |
744 | tb[TCA_FLOWER_KEY_IPV6_SRC_MASK]); | |
745 | ||
b2141de1 | 746 | nl_type = flower_port_attr_type(ip_proto, FLOWER_ENDPOINT_DST); |
6bd5b80c SH |
747 | if (nl_type >= 0) |
748 | flower_print_port(f, "dst_port", tb[nl_type]); | |
b2141de1 | 749 | nl_type = flower_port_attr_type(ip_proto, FLOWER_ENDPOINT_SRC); |
6bd5b80c SH |
750 | if (nl_type >= 0) |
751 | flower_print_port(f, "src_port", tb[nl_type]); | |
30eb304e | 752 | |
eb3b5696 SH |
753 | nl_type = flower_icmp_attr_type(eth_type, ip_proto, false); |
754 | if (nl_type >= 0) | |
755 | flower_print_icmp(f, "icmp_type", tb[nl_type]); | |
756 | nl_type = flower_icmp_attr_type(eth_type, ip_proto, true); | |
757 | if (nl_type >= 0) | |
758 | flower_print_icmp(f, "icmp_code", tb[nl_type]); | |
759 | ||
bb9b63b1 AV |
760 | flower_print_ip_addr(f, "enc_dst_ip", |
761 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] ? | |
762 | htons(ETH_P_IP) : htons(ETH_P_IPV6), | |
763 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST], | |
764 | tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK], | |
765 | tb[TCA_FLOWER_KEY_ENC_IPV6_DST], | |
766 | tb[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]); | |
767 | ||
768 | flower_print_ip_addr(f, "enc_src_ip", | |
769 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] ? | |
770 | htons(ETH_P_IP) : htons(ETH_P_IPV6), | |
771 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC], | |
772 | tb[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK], | |
773 | tb[TCA_FLOWER_KEY_ENC_IPV6_SRC], | |
774 | tb[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]); | |
775 | ||
776 | flower_print_key_id(f, "enc_key_id", | |
777 | tb[TCA_FLOWER_KEY_ENC_KEY_ID]); | |
778 | ||
41aa17ff HHZ |
779 | flower_print_port(f, "enc_dst_port", |
780 | tb[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]); | |
781 | ||
512caeb2 | 782 | if (tb[TCA_FLOWER_FLAGS]) { |
cfcabf18 AV |
783 | __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]); |
784 | ||
785 | if (flags & TCA_CLS_FLAGS_SKIP_HW) | |
786 | fprintf(f, "\n skip_hw"); | |
787 | if (flags & TCA_CLS_FLAGS_SKIP_SW) | |
788 | fprintf(f, "\n skip_sw"); | |
789 | } | |
790 | ||
512caeb2 | 791 | if (tb[TCA_FLOWER_ACT]) |
30eb304e | 792 | tc_print_action(f, tb[TCA_FLOWER_ACT]); |
30eb304e JP |
793 | |
794 | return 0; | |
795 | } | |
796 | ||
797 | struct filter_util flower_filter_util = { | |
798 | .id = "flower", | |
799 | .parse_fopt = flower_parse_opt, | |
800 | .print_fopt = flower_print_opt, | |
801 | }; |