]>
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 | ||
c14f9d92 | 191 | tail = addattr_nest(n, 4096, TCA_OPTIONS); |
aba5acdf SH |
192 | |
193 | while (argc > 0) { | |
194 | if (matches(*argv, "session") == 0) { | |
195 | inet_prefix addr; | |
32a121cb | 196 | |
aba5acdf SH |
197 | NEXT_ARG(); |
198 | if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 1, family)) { | |
199 | fprintf(stderr, "Illegal \"session\"\n"); | |
200 | return -1; | |
201 | } | |
202 | addattr_l(n, 4096, TCA_RSVP_DST, &addr.data, addr.bytelen); | |
203 | if (pinfo.dpi.mask || pinfo.protocol) | |
204 | pinfo_ok++; | |
205 | continue; | |
206 | } else if (matches(*argv, "sender") == 0 || | |
207 | matches(*argv, "flowspec") == 0) { | |
208 | inet_prefix addr; | |
32a121cb | 209 | |
aba5acdf SH |
210 | NEXT_ARG(); |
211 | if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) { | |
212 | fprintf(stderr, "Illegal \"sender\"\n"); | |
213 | return -1; | |
214 | } | |
215 | addattr_l(n, 4096, TCA_RSVP_SRC, &addr.data, addr.bytelen); | |
216 | if (pinfo.spi.mask || pinfo.protocol) | |
217 | pinfo_ok++; | |
218 | continue; | |
219 | } else if (matches("ipproto", *argv) == 0) { | |
220 | int num; | |
32a121cb | 221 | |
aba5acdf SH |
222 | NEXT_ARG(); |
223 | num = inet_proto_a2n(*argv); | |
224 | if (num < 0) { | |
225 | fprintf(stderr, "Illegal \"ipproto\"\n"); | |
226 | return -1; | |
227 | } | |
228 | pinfo.protocol = num; | |
229 | pinfo_ok++; | |
230 | } else if (matches(*argv, "classid") == 0 || | |
231 | strcmp(*argv, "flowid") == 0) { | |
32a121cb SH |
232 | unsigned int handle; |
233 | ||
aba5acdf SH |
234 | NEXT_ARG(); |
235 | if (get_tc_classid(&handle, *argv)) { | |
236 | fprintf(stderr, "Illegal \"classid\"\n"); | |
237 | return -1; | |
238 | } | |
239 | addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4); | |
240 | } else if (strcmp(*argv, "tunnelid") == 0) { | |
32a121cb SH |
241 | unsigned int tid; |
242 | ||
aba5acdf SH |
243 | NEXT_ARG(); |
244 | if (get_unsigned(&tid, *argv, 0)) { | |
245 | fprintf(stderr, "Illegal \"tunnelid\"\n"); | |
246 | return -1; | |
247 | } | |
248 | pinfo.tunnelid = tid; | |
249 | pinfo_ok++; | |
250 | } else if (strcmp(*argv, "tunnel") == 0) { | |
32a121cb SH |
251 | unsigned int tid; |
252 | ||
aba5acdf SH |
253 | NEXT_ARG(); |
254 | if (get_unsigned(&tid, *argv, 0)) { | |
255 | fprintf(stderr, "Illegal \"tunnel\"\n"); | |
256 | return -1; | |
257 | } | |
258 | addattr_l(n, 4096, TCA_RSVP_CLASSID, &tid, 4); | |
259 | NEXT_ARG(); | |
260 | if (strcmp(*argv, "skip") == 0) { | |
261 | NEXT_ARG(); | |
262 | } | |
263 | if (get_unsigned(&tid, *argv, 0)) { | |
264 | fprintf(stderr, "Illegal \"skip\"\n"); | |
265 | return -1; | |
266 | } | |
267 | pinfo.tunnelhdr = tid; | |
268 | pinfo_ok++; | |
10f5a375 JHS |
269 | } else if (matches(*argv, "action") == 0) { |
270 | NEXT_ARG(); | |
271 | if (parse_action(&argc, &argv, TCA_RSVP_ACT, n)) { | |
272 | fprintf(stderr, "Illegal \"action\"\n"); | |
273 | return -1; | |
274 | } | |
275 | continue; | |
aba5acdf SH |
276 | } else if (matches(*argv, "police") == 0) { |
277 | NEXT_ARG(); | |
278 | if (parse_police(&argc, &argv, TCA_RSVP_POLICE, n)) { | |
279 | fprintf(stderr, "Illegal \"police\"\n"); | |
280 | return -1; | |
281 | } | |
282 | continue; | |
283 | } else if (strcmp(*argv, "help") == 0) { | |
284 | explain(); | |
285 | return -1; | |
286 | } else { | |
287 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
288 | explain(); | |
289 | return -1; | |
290 | } | |
291 | argc--; argv++; | |
292 | } | |
293 | ||
294 | if (pinfo_ok) | |
295 | addattr_l(n, 4096, TCA_RSVP_PINFO, &pinfo, sizeof(pinfo)); | |
c14f9d92 | 296 | addattr_nest_end(n, tail); |
aba5acdf SH |
297 | return 0; |
298 | } | |
299 | ||
32a121cb | 300 | static char *sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) |
aba5acdf SH |
301 | { |
302 | if (pi->offset == 0) { | |
303 | if (dir && pi->mask == htonl(0xFFFF)) { | |
304 | snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)); | |
305 | return buf; | |
306 | } | |
307 | if (!dir && pi->mask == htonl(0xFFFF0000)) { | |
308 | snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)>>16); | |
309 | return buf; | |
310 | } | |
311 | if (pi->mask == htonl(0xFFFFFFFF)) { | |
312 | snprintf(buf, SPRINT_BSIZE-1, " spi/esp 0x%08x", htonl(pi->key)); | |
313 | return buf; | |
314 | } | |
315 | } else if (pi->offset == 4 && pi->mask == htonl(0xFFFFFFFF)) { | |
316 | snprintf(buf, SPRINT_BSIZE-1, " spi/ah 0x%08x", htonl(pi->key)); | |
317 | return buf; | |
318 | } else if (pi->offset == -40 && pi->mask == htonl(0x000FFFFF)) { | |
319 | snprintf(buf, SPRINT_BSIZE-1, " flowlabel 0x%05x", htonl(pi->key)); | |
320 | return buf; | |
321 | } | |
322 | snprintf(buf, SPRINT_BSIZE-1, " u32 0x%08x mask %08x at %d", | |
323 | htonl(pi->key), htonl(pi->mask), pi->offset); | |
324 | return buf; | |
325 | } | |
326 | ||
327 | static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) | |
328 | { | |
329 | int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; | |
330 | struct rtattr *tb[TCA_RSVP_MAX+1]; | |
331 | struct tc_rsvp_pinfo *pinfo = NULL; | |
332 | ||
333 | if (opt == NULL) | |
334 | return 0; | |
335 | ||
3b3ecd31 | 336 | parse_rtattr_nested(tb, TCA_RSVP_MAX, opt); |
aba5acdf SH |
337 | |
338 | if (handle) | |
339 | fprintf(f, "fh 0x%08x ", handle); | |
340 | ||
341 | if (tb[TCA_RSVP_PINFO]) { | |
342 | if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO]) < sizeof(*pinfo)) | |
343 | return -1; | |
344 | ||
345 | pinfo = RTA_DATA(tb[TCA_RSVP_PINFO]); | |
346 | } | |
347 | ||
348 | if (tb[TCA_RSVP_CLASSID]) { | |
349 | SPRINT_BUF(b1); | |
350 | if (!pinfo || pinfo->tunnelhdr == 0) | |
ff24746c | 351 | fprintf(f, "flowid %s ", sprint_tc_classid(rta_getattr_u32(tb[TCA_RSVP_CLASSID]), b1)); |
aba5acdf | 352 | else |
ff24746c | 353 | fprintf(f, "tunnel %d skip %d ", rta_getattr_u32(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr); |
aba5acdf SH |
354 | } else if (pinfo && pinfo->tunnelhdr) |
355 | fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr); | |
356 | ||
357 | if (tb[TCA_RSVP_DST]) { | |
358 | char buf[128]; | |
32a121cb | 359 | |
aba5acdf SH |
360 | fprintf(f, "session "); |
361 | if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0) | |
362 | fprintf(f, " [INVALID DADDR] "); | |
363 | else | |
364 | fprintf(f, "%s", buf); | |
365 | if (pinfo && pinfo->dpi.mask) { | |
366 | SPRINT_BUF(b2); | |
367 | fprintf(f, "%s ", sprint_spi(&pinfo->dpi, 1, b2)); | |
368 | } else | |
369 | fprintf(f, " "); | |
370 | } else { | |
371 | if (pinfo && pinfo->dpi.mask) { | |
372 | SPRINT_BUF(b2); | |
373 | fprintf(f, "session [NONE]%s ", sprint_spi(&pinfo->dpi, 1, b2)); | |
374 | } else | |
375 | fprintf(f, "session NONE "); | |
376 | } | |
377 | ||
378 | if (pinfo && pinfo->protocol) { | |
379 | SPRINT_BUF(b1); | |
380 | fprintf(f, "ipproto %s ", inet_proto_n2a(pinfo->protocol, b1, sizeof(b1))); | |
381 | } | |
382 | if (pinfo && pinfo->tunnelid) | |
383 | fprintf(f, "tunnelid %d ", pinfo->tunnelid); | |
384 | if (tb[TCA_RSVP_SRC]) { | |
385 | char buf[128]; | |
32a121cb | 386 | |
aba5acdf SH |
387 | fprintf(f, "sender "); |
388 | if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) { | |
389 | fprintf(f, "[BAD]"); | |
390 | } else { | |
391 | fprintf(f, " %s", buf); | |
392 | } | |
393 | if (pinfo && pinfo->spi.mask) { | |
394 | SPRINT_BUF(b2); | |
395 | fprintf(f, "%s ", sprint_spi(&pinfo->spi, 0, b2)); | |
396 | } else | |
397 | fprintf(f, " "); | |
398 | } else if (pinfo && pinfo->spi.mask) { | |
399 | SPRINT_BUF(b2); | |
400 | fprintf(f, "sender [NONE]%s ", sprint_spi(&pinfo->spi, 0, b2)); | |
401 | } | |
10f5a375 JHS |
402 | |
403 | if (tb[TCA_RSVP_ACT]) { | |
9e713525 | 404 | tc_print_action(f, tb[TCA_RSVP_ACT], 0); |
10f5a375 | 405 | } |
aba5acdf SH |
406 | if (tb[TCA_RSVP_POLICE]) |
407 | tc_print_police(f, tb[TCA_RSVP_POLICE]); | |
408 | return 0; | |
409 | } | |
410 | ||
6b7dff17 SH |
411 | struct filter_util rsvp_filter_util = { |
412 | .id = "rsvp", | |
413 | .parse_fopt = rsvp_parse_opt, | |
414 | .print_fopt = rsvp_print_opt, | |
aba5acdf SH |
415 | }; |
416 | ||
6b7dff17 SH |
417 | struct filter_util rsvp6_filter_util = { |
418 | .id = "rsvp6", | |
419 | .parse_fopt = rsvp_parse_opt, | |
420 | .print_fopt = rsvp_print_opt, | |
aba5acdf | 421 | }; |