]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
1 | /* |
2 | * q_rsvp.c RSVP filter. | |
3 | * | |
4 | * This program is free software; you can redistribute 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | |
10 | * | |
11 | */ | |
12 | ||
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
aba5acdf SH |
16 | #include <fcntl.h> |
17 | #include <sys/socket.h> | |
18 | #include <netinet/in.h> | |
19 | #include <arpa/inet.h> | |
20 | #include <string.h> | |
21 | ||
22 | #include "rt_names.h" | |
23 | #include "utils.h" | |
24 | #include "tc_util.h" | |
25 | ||
26 | static void explain(void) | |
27 | { | |
28 | fprintf(stderr, "Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]\n"); | |
0a83e1ea | 29 | fprintf(stderr, " [ sender SRC[/PORT | GPI ] ]\n"); |
863ecb04 | 30 | fprintf(stderr, " [ classid CLASSID ] [ action ACTION_SPEC ]\n"); |
aba5acdf SH |
31 | fprintf(stderr, " [ tunnelid ID ] [ tunnel ID skip NUMBER ]\n"); |
32 | fprintf(stderr, "Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |\n"); | |
33 | fprintf(stderr, " u{8|16|32} NUMBER mask MASK at OFFSET}\n"); | |
863ecb04 | 34 | fprintf(stderr, " ACTION_SPEC := ... look at individual actions\n"); |
aba5acdf | 35 | fprintf(stderr, " FILTERID := X:Y\n"); |
e9acc242 | 36 | fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); |
aba5acdf SH |
37 | } |
38 | ||
32a121cb | 39 | static int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix *addr, |
aba5acdf SH |
40 | struct tc_rsvp_pinfo *pinfo, int dir, int family) |
41 | { | |
42 | int argc = *argc_p; | |
43 | char **argv = *argv_p; | |
44 | char *p = strchr(*argv, '/'); | |
45 | struct tc_rsvp_gpi *pi = dir ? &pinfo->dpi : &pinfo->spi; | |
46 | ||
47 | if (p) { | |
48 | __u16 tmp; | |
49 | ||
50 | if (get_u16(&tmp, p+1, 0)) | |
51 | return -1; | |
52 | ||
53 | if (dir == 0) { | |
54 | /* Source port: u16 at offset 0 */ | |
55 | pi->key = htonl(((__u32)tmp)<<16); | |
56 | pi->mask = htonl(0xFFFF0000); | |
57 | } else { | |
58 | /* Destination port: u16 at offset 2 */ | |
59 | pi->key = htonl(((__u32)tmp)); | |
60 | pi->mask = htonl(0x0000FFFF); | |
61 | } | |
62 | pi->offset = 0; | |
63 | *p = 0; | |
64 | } | |
65 | if (get_addr_1(addr, *argv, family)) | |
66 | return -1; | |
67 | if (p) | |
68 | *p = '/'; | |
69 | ||
70 | argc--; argv++; | |
71 | ||
72 | if (pi->mask || argc <= 0) | |
73 | goto done; | |
74 | ||
75 | if (strcmp(*argv, "spi/ah") == 0 || | |
76 | strcmp(*argv, "gpi/ah") == 0) { | |
77 | __u32 gpi; | |
32a121cb | 78 | |
aba5acdf SH |
79 | NEXT_ARG(); |
80 | if (get_u32(&gpi, *argv, 0)) | |
81 | return -1; | |
82 | pi->mask = htonl(0xFFFFFFFF); | |
83 | pi->key = htonl(gpi); | |
84 | pi->offset = 4; | |
85 | if (pinfo->protocol == 0) | |
86 | pinfo->protocol = IPPROTO_AH; | |
87 | argc--; argv++; | |
88 | } else if (strcmp(*argv, "spi/esp") == 0 || | |
89 | strcmp(*argv, "gpi/esp") == 0) { | |
90 | __u32 gpi; | |
32a121cb | 91 | |
aba5acdf SH |
92 | NEXT_ARG(); |
93 | if (get_u32(&gpi, *argv, 0)) | |
94 | return -1; | |
95 | pi->mask = htonl(0xFFFFFFFF); | |
96 | pi->key = htonl(gpi); | |
97 | pi->offset = 0; | |
98 | if (pinfo->protocol == 0) | |
99 | pinfo->protocol = IPPROTO_ESP; | |
100 | argc--; argv++; | |
101 | } else if (strcmp(*argv, "flowlabel") == 0) { | |
102 | __u32 flabel; | |
32a121cb | 103 | |
aba5acdf SH |
104 | NEXT_ARG(); |
105 | if (get_u32(&flabel, *argv, 0)) | |
106 | return -1; | |
107 | if (family != AF_INET6) | |
108 | return -1; | |
109 | pi->mask = htonl(0x000FFFFF); | |
110 | pi->key = htonl(flabel) & pi->mask; | |
111 | pi->offset = -40; | |
112 | argc--; argv++; | |
113 | } else if (strcmp(*argv, "u32") == 0 || | |
114 | strcmp(*argv, "u16") == 0 || | |
115 | strcmp(*argv, "u8") == 0) { | |
116 | int sz = 1; | |
117 | __u32 tmp; | |
118 | __u32 mask = 0xff; | |
32a121cb | 119 | |
aba5acdf SH |
120 | if (strcmp(*argv, "u32") == 0) { |
121 | sz = 4; | |
122 | mask = 0xffff; | |
123 | } else if (strcmp(*argv, "u16") == 0) { | |
124 | mask = 0xffffffff; | |
125 | sz = 2; | |
126 | } | |
127 | NEXT_ARG(); | |
128 | if (get_u32(&tmp, *argv, 0)) | |
129 | return -1; | |
130 | argc--; argv++; | |
131 | if (strcmp(*argv, "mask") == 0) { | |
132 | NEXT_ARG(); | |
133 | if (get_u32(&mask, *argv, 16)) | |
134 | return -1; | |
135 | argc--; argv++; | |
136 | } | |
137 | if (strcmp(*argv, "at") == 0) { | |
138 | NEXT_ARG(); | |
139 | if (get_integer(&pi->offset, *argv, 0)) | |
140 | return -1; | |
141 | argc--; argv++; | |
142 | } | |
143 | if (sz == 1) { | |
144 | if ((pi->offset & 3) == 0) { | |
145 | mask <<= 24; | |
146 | tmp <<= 24; | |
147 | } else if ((pi->offset & 3) == 1) { | |
148 | mask <<= 16; | |
149 | tmp <<= 16; | |
150 | } else if ((pi->offset & 3) == 3) { | |
151 | mask <<= 8; | |
152 | tmp <<= 8; | |
153 | } | |
154 | } else if (sz == 2) { | |
155 | if ((pi->offset & 3) == 0) { | |
156 | mask <<= 16; | |
157 | tmp <<= 16; | |
158 | } | |
159 | } | |
160 | pi->offset &= ~3; | |
161 | pi->mask = htonl(mask); | |
162 | pi->key = htonl(tmp) & pi->mask; | |
163 | } | |
164 | ||
165 | done: | |
166 | *argc_p = argc; | |
167 | *argv_p = argv; | |
168 | return 0; | |
169 | } | |
170 | ||
171 | ||
859af0a5 SH |
172 | static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, |
173 | char **argv, struct nlmsghdr *n) | |
aba5acdf SH |
174 | { |
175 | int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; | |
d17b136f | 176 | struct tc_rsvp_pinfo pinfo = {}; |
aba5acdf SH |
177 | struct tcmsg *t = NLMSG_DATA(n); |
178 | int pinfo_ok = 0; | |
179 | struct rtattr *tail; | |
180 | ||
aba5acdf SH |
181 | if (handle) { |
182 | if (get_u32(&t->tcm_handle, handle, 0)) { | |
183 | fprintf(stderr, "Illegal \"handle\"\n"); | |
184 | return -1; | |
185 | } | |
186 | } | |
187 | ||
188 | if (argc == 0) | |
189 | return 0; | |
190 | ||
228569c3 | 191 | tail = NLMSG_TAIL(n); |
aba5acdf SH |
192 | addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); |
193 | ||
194 | while (argc > 0) { | |
195 | if (matches(*argv, "session") == 0) { | |
196 | inet_prefix addr; | |
32a121cb | 197 | |
aba5acdf SH |
198 | NEXT_ARG(); |
199 | if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 1, family)) { | |
200 | fprintf(stderr, "Illegal \"session\"\n"); | |
201 | return -1; | |
202 | } | |
203 | addattr_l(n, 4096, TCA_RSVP_DST, &addr.data, addr.bytelen); | |
204 | if (pinfo.dpi.mask || pinfo.protocol) | |
205 | pinfo_ok++; | |
206 | continue; | |
207 | } else if (matches(*argv, "sender") == 0 || | |
208 | matches(*argv, "flowspec") == 0) { | |
209 | inet_prefix addr; | |
32a121cb | 210 | |
aba5acdf SH |
211 | NEXT_ARG(); |
212 | if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) { | |
213 | fprintf(stderr, "Illegal \"sender\"\n"); | |
214 | return -1; | |
215 | } | |
216 | addattr_l(n, 4096, TCA_RSVP_SRC, &addr.data, addr.bytelen); | |
217 | if (pinfo.spi.mask || pinfo.protocol) | |
218 | pinfo_ok++; | |
219 | continue; | |
220 | } else if (matches("ipproto", *argv) == 0) { | |
221 | int num; | |
32a121cb | 222 | |
aba5acdf SH |
223 | NEXT_ARG(); |
224 | num = inet_proto_a2n(*argv); | |
225 | if (num < 0) { | |
226 | fprintf(stderr, "Illegal \"ipproto\"\n"); | |
227 | return -1; | |
228 | } | |
229 | pinfo.protocol = num; | |
230 | pinfo_ok++; | |
231 | } else if (matches(*argv, "classid") == 0 || | |
232 | strcmp(*argv, "flowid") == 0) { | |
32a121cb SH |
233 | unsigned int handle; |
234 | ||
aba5acdf SH |
235 | NEXT_ARG(); |
236 | if (get_tc_classid(&handle, *argv)) { | |
237 | fprintf(stderr, "Illegal \"classid\"\n"); | |
238 | return -1; | |
239 | } | |
240 | addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4); | |
241 | } else if (strcmp(*argv, "tunnelid") == 0) { | |
32a121cb SH |
242 | unsigned int tid; |
243 | ||
aba5acdf SH |
244 | NEXT_ARG(); |
245 | if (get_unsigned(&tid, *argv, 0)) { | |
246 | fprintf(stderr, "Illegal \"tunnelid\"\n"); | |
247 | return -1; | |
248 | } | |
249 | pinfo.tunnelid = tid; | |
250 | pinfo_ok++; | |
251 | } else if (strcmp(*argv, "tunnel") == 0) { | |
32a121cb SH |
252 | unsigned int tid; |
253 | ||
aba5acdf SH |
254 | NEXT_ARG(); |
255 | if (get_unsigned(&tid, *argv, 0)) { | |
256 | fprintf(stderr, "Illegal \"tunnel\"\n"); | |
257 | return -1; | |
258 | } | |
259 | addattr_l(n, 4096, TCA_RSVP_CLASSID, &tid, 4); | |
260 | NEXT_ARG(); | |
261 | if (strcmp(*argv, "skip") == 0) { | |
262 | NEXT_ARG(); | |
263 | } | |
264 | if (get_unsigned(&tid, *argv, 0)) { | |
265 | fprintf(stderr, "Illegal \"skip\"\n"); | |
266 | return -1; | |
267 | } | |
268 | pinfo.tunnelhdr = tid; | |
269 | pinfo_ok++; | |
10f5a375 JHS |
270 | } else if (matches(*argv, "action") == 0) { |
271 | NEXT_ARG(); | |
272 | if (parse_action(&argc, &argv, TCA_RSVP_ACT, n)) { | |
273 | fprintf(stderr, "Illegal \"action\"\n"); | |
274 | return -1; | |
275 | } | |
276 | continue; | |
aba5acdf SH |
277 | } else if (matches(*argv, "police") == 0) { |
278 | NEXT_ARG(); | |
279 | if (parse_police(&argc, &argv, TCA_RSVP_POLICE, n)) { | |
280 | fprintf(stderr, "Illegal \"police\"\n"); | |
281 | return -1; | |
282 | } | |
283 | continue; | |
284 | } else if (strcmp(*argv, "help") == 0) { | |
285 | explain(); | |
286 | return -1; | |
287 | } else { | |
288 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
289 | explain(); | |
290 | return -1; | |
291 | } | |
292 | argc--; argv++; | |
293 | } | |
294 | ||
295 | if (pinfo_ok) | |
296 | addattr_l(n, 4096, TCA_RSVP_PINFO, &pinfo, sizeof(pinfo)); | |
228569c3 | 297 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
aba5acdf SH |
298 | return 0; |
299 | } | |
300 | ||
32a121cb | 301 | static char *sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) |
aba5acdf SH |
302 | { |
303 | if (pi->offset == 0) { | |
304 | if (dir && pi->mask == htonl(0xFFFF)) { | |
305 | snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)); | |
306 | return buf; | |
307 | } | |
308 | if (!dir && pi->mask == htonl(0xFFFF0000)) { | |
309 | snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)>>16); | |
310 | return buf; | |
311 | } | |
312 | if (pi->mask == htonl(0xFFFFFFFF)) { | |
313 | snprintf(buf, SPRINT_BSIZE-1, " spi/esp 0x%08x", htonl(pi->key)); | |
314 | return buf; | |
315 | } | |
316 | } else if (pi->offset == 4 && pi->mask == htonl(0xFFFFFFFF)) { | |
317 | snprintf(buf, SPRINT_BSIZE-1, " spi/ah 0x%08x", htonl(pi->key)); | |
318 | return buf; | |
319 | } else if (pi->offset == -40 && pi->mask == htonl(0x000FFFFF)) { | |
320 | snprintf(buf, SPRINT_BSIZE-1, " flowlabel 0x%05x", htonl(pi->key)); | |
321 | return buf; | |
322 | } | |
323 | snprintf(buf, SPRINT_BSIZE-1, " u32 0x%08x mask %08x at %d", | |
324 | htonl(pi->key), htonl(pi->mask), pi->offset); | |
325 | return buf; | |
326 | } | |
327 | ||
328 | static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) | |
329 | { | |
330 | int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; | |
331 | struct rtattr *tb[TCA_RSVP_MAX+1]; | |
332 | struct tc_rsvp_pinfo *pinfo = NULL; | |
333 | ||
334 | if (opt == NULL) | |
335 | return 0; | |
336 | ||
3b3ecd31 | 337 | parse_rtattr_nested(tb, TCA_RSVP_MAX, opt); |
aba5acdf SH |
338 | |
339 | if (handle) | |
340 | fprintf(f, "fh 0x%08x ", handle); | |
341 | ||
342 | if (tb[TCA_RSVP_PINFO]) { | |
343 | if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO]) < sizeof(*pinfo)) | |
344 | return -1; | |
345 | ||
346 | pinfo = RTA_DATA(tb[TCA_RSVP_PINFO]); | |
347 | } | |
348 | ||
349 | if (tb[TCA_RSVP_CLASSID]) { | |
350 | SPRINT_BUF(b1); | |
351 | if (!pinfo || pinfo->tunnelhdr == 0) | |
ff24746c | 352 | fprintf(f, "flowid %s ", sprint_tc_classid(rta_getattr_u32(tb[TCA_RSVP_CLASSID]), b1)); |
aba5acdf | 353 | else |
ff24746c | 354 | fprintf(f, "tunnel %d skip %d ", rta_getattr_u32(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr); |
aba5acdf SH |
355 | } else if (pinfo && pinfo->tunnelhdr) |
356 | fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr); | |
357 | ||
358 | if (tb[TCA_RSVP_DST]) { | |
359 | char buf[128]; | |
32a121cb | 360 | |
aba5acdf SH |
361 | fprintf(f, "session "); |
362 | if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0) | |
363 | fprintf(f, " [INVALID DADDR] "); | |
364 | else | |
365 | fprintf(f, "%s", buf); | |
366 | if (pinfo && pinfo->dpi.mask) { | |
367 | SPRINT_BUF(b2); | |
368 | fprintf(f, "%s ", sprint_spi(&pinfo->dpi, 1, b2)); | |
369 | } else | |
370 | fprintf(f, " "); | |
371 | } else { | |
372 | if (pinfo && pinfo->dpi.mask) { | |
373 | SPRINT_BUF(b2); | |
374 | fprintf(f, "session [NONE]%s ", sprint_spi(&pinfo->dpi, 1, b2)); | |
375 | } else | |
376 | fprintf(f, "session NONE "); | |
377 | } | |
378 | ||
379 | if (pinfo && pinfo->protocol) { | |
380 | SPRINT_BUF(b1); | |
381 | fprintf(f, "ipproto %s ", inet_proto_n2a(pinfo->protocol, b1, sizeof(b1))); | |
382 | } | |
383 | if (pinfo && pinfo->tunnelid) | |
384 | fprintf(f, "tunnelid %d ", pinfo->tunnelid); | |
385 | if (tb[TCA_RSVP_SRC]) { | |
386 | char buf[128]; | |
32a121cb | 387 | |
aba5acdf SH |
388 | fprintf(f, "sender "); |
389 | if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) { | |
390 | fprintf(f, "[BAD]"); | |
391 | } else { | |
392 | fprintf(f, " %s", buf); | |
393 | } | |
394 | if (pinfo && pinfo->spi.mask) { | |
395 | SPRINT_BUF(b2); | |
396 | fprintf(f, "%s ", sprint_spi(&pinfo->spi, 0, b2)); | |
397 | } else | |
398 | fprintf(f, " "); | |
399 | } else if (pinfo && pinfo->spi.mask) { | |
400 | SPRINT_BUF(b2); | |
401 | fprintf(f, "sender [NONE]%s ", sprint_spi(&pinfo->spi, 0, b2)); | |
402 | } | |
10f5a375 JHS |
403 | |
404 | if (tb[TCA_RSVP_ACT]) { | |
9e713525 | 405 | tc_print_action(f, tb[TCA_RSVP_ACT], 0); |
10f5a375 | 406 | } |
aba5acdf SH |
407 | if (tb[TCA_RSVP_POLICE]) |
408 | tc_print_police(f, tb[TCA_RSVP_POLICE]); | |
409 | return 0; | |
410 | } | |
411 | ||
6b7dff17 SH |
412 | struct filter_util rsvp_filter_util = { |
413 | .id = "rsvp", | |
414 | .parse_fopt = rsvp_parse_opt, | |
415 | .print_fopt = rsvp_print_opt, | |
aba5acdf SH |
416 | }; |
417 | ||
6b7dff17 SH |
418 | struct filter_util rsvp6_filter_util = { |
419 | .id = "rsvp6", | |
420 | .parse_fopt = rsvp_parse_opt, | |
421 | .print_fopt = rsvp_print_opt, | |
aba5acdf | 422 | }; |