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