]>
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 | { | |
8589eb4e MC |
28 | fprintf(stderr, |
29 | "Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]\n" | |
30 | " [ sender SRC[/PORT | GPI ] ]\n" | |
31 | " [ classid CLASSID ] [ action ACTION_SPEC ]\n" | |
32 | " [ tunnelid ID ] [ tunnel ID skip NUMBER ]\n" | |
33 | "Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |\n" | |
34 | " u{8|16|32} NUMBER mask MASK at OFFSET}\n" | |
35 | " ACTION_SPEC := ... look at individual actions\n" | |
36 | " FILTERID := X:Y\n" | |
37 | "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); | |
aba5acdf SH |
38 | } |
39 | ||
32a121cb | 40 | static int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix *addr, |
aba5acdf SH |
41 | struct tc_rsvp_pinfo *pinfo, int dir, int family) |
42 | { | |
43 | int argc = *argc_p; | |
44 | char **argv = *argv_p; | |
45 | char *p = strchr(*argv, '/'); | |
46 | struct tc_rsvp_gpi *pi = dir ? &pinfo->dpi : &pinfo->spi; | |
47 | ||
48 | if (p) { | |
49 | __u16 tmp; | |
50 | ||
51 | if (get_u16(&tmp, p+1, 0)) | |
52 | return -1; | |
53 | ||
54 | if (dir == 0) { | |
55 | /* Source port: u16 at offset 0 */ | |
56 | pi->key = htonl(((__u32)tmp)<<16); | |
57 | pi->mask = htonl(0xFFFF0000); | |
58 | } else { | |
59 | /* Destination port: u16 at offset 2 */ | |
60 | pi->key = htonl(((__u32)tmp)); | |
61 | pi->mask = htonl(0x0000FFFF); | |
62 | } | |
63 | pi->offset = 0; | |
64 | *p = 0; | |
65 | } | |
66 | if (get_addr_1(addr, *argv, family)) | |
67 | return -1; | |
68 | if (p) | |
69 | *p = '/'; | |
70 | ||
71 | argc--; argv++; | |
72 | ||
73 | if (pi->mask || argc <= 0) | |
74 | goto done; | |
75 | ||
76 | if (strcmp(*argv, "spi/ah") == 0 || | |
77 | strcmp(*argv, "gpi/ah") == 0) { | |
78 | __u32 gpi; | |
32a121cb | 79 | |
aba5acdf SH |
80 | NEXT_ARG(); |
81 | if (get_u32(&gpi, *argv, 0)) | |
82 | return -1; | |
83 | pi->mask = htonl(0xFFFFFFFF); | |
84 | pi->key = htonl(gpi); | |
85 | pi->offset = 4; | |
86 | if (pinfo->protocol == 0) | |
87 | pinfo->protocol = IPPROTO_AH; | |
88 | argc--; argv++; | |
89 | } else if (strcmp(*argv, "spi/esp") == 0 || | |
90 | strcmp(*argv, "gpi/esp") == 0) { | |
91 | __u32 gpi; | |
32a121cb | 92 | |
aba5acdf SH |
93 | NEXT_ARG(); |
94 | if (get_u32(&gpi, *argv, 0)) | |
95 | return -1; | |
96 | pi->mask = htonl(0xFFFFFFFF); | |
97 | pi->key = htonl(gpi); | |
98 | pi->offset = 0; | |
99 | if (pinfo->protocol == 0) | |
100 | pinfo->protocol = IPPROTO_ESP; | |
101 | argc--; argv++; | |
102 | } else if (strcmp(*argv, "flowlabel") == 0) { | |
103 | __u32 flabel; | |
32a121cb | 104 | |
aba5acdf SH |
105 | NEXT_ARG(); |
106 | if (get_u32(&flabel, *argv, 0)) | |
107 | return -1; | |
108 | if (family != AF_INET6) | |
109 | return -1; | |
110 | pi->mask = htonl(0x000FFFFF); | |
111 | pi->key = htonl(flabel) & pi->mask; | |
112 | pi->offset = -40; | |
113 | argc--; argv++; | |
114 | } else if (strcmp(*argv, "u32") == 0 || | |
115 | strcmp(*argv, "u16") == 0 || | |
116 | strcmp(*argv, "u8") == 0) { | |
117 | int sz = 1; | |
118 | __u32 tmp; | |
119 | __u32 mask = 0xff; | |
32a121cb | 120 | |
aba5acdf SH |
121 | if (strcmp(*argv, "u32") == 0) { |
122 | sz = 4; | |
123 | mask = 0xffff; | |
124 | } else if (strcmp(*argv, "u16") == 0) { | |
125 | mask = 0xffffffff; | |
126 | sz = 2; | |
127 | } | |
128 | NEXT_ARG(); | |
129 | if (get_u32(&tmp, *argv, 0)) | |
130 | return -1; | |
131 | argc--; argv++; | |
132 | if (strcmp(*argv, "mask") == 0) { | |
133 | NEXT_ARG(); | |
134 | if (get_u32(&mask, *argv, 16)) | |
135 | return -1; | |
136 | argc--; argv++; | |
137 | } | |
138 | if (strcmp(*argv, "at") == 0) { | |
139 | NEXT_ARG(); | |
140 | if (get_integer(&pi->offset, *argv, 0)) | |
141 | return -1; | |
142 | argc--; argv++; | |
143 | } | |
144 | if (sz == 1) { | |
145 | if ((pi->offset & 3) == 0) { | |
146 | mask <<= 24; | |
147 | tmp <<= 24; | |
148 | } else if ((pi->offset & 3) == 1) { | |
149 | mask <<= 16; | |
150 | tmp <<= 16; | |
151 | } else if ((pi->offset & 3) == 3) { | |
152 | mask <<= 8; | |
153 | tmp <<= 8; | |
154 | } | |
155 | } else if (sz == 2) { | |
156 | if ((pi->offset & 3) == 0) { | |
157 | mask <<= 16; | |
158 | tmp <<= 16; | |
159 | } | |
160 | } | |
161 | pi->offset &= ~3; | |
162 | pi->mask = htonl(mask); | |
163 | pi->key = htonl(tmp) & pi->mask; | |
164 | } | |
165 | ||
166 | done: | |
167 | *argc_p = argc; | |
168 | *argv_p = argv; | |
169 | return 0; | |
170 | } | |
171 | ||
172 | ||
859af0a5 SH |
173 | static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, |
174 | char **argv, struct nlmsghdr *n) | |
aba5acdf SH |
175 | { |
176 | int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; | |
d17b136f | 177 | struct tc_rsvp_pinfo pinfo = {}; |
aba5acdf SH |
178 | struct tcmsg *t = NLMSG_DATA(n); |
179 | int pinfo_ok = 0; | |
180 | struct rtattr *tail; | |
181 | ||
aba5acdf SH |
182 | if (handle) { |
183 | if (get_u32(&t->tcm_handle, handle, 0)) { | |
184 | fprintf(stderr, "Illegal \"handle\"\n"); | |
185 | return -1; | |
186 | } | |
187 | } | |
188 | ||
189 | if (argc == 0) | |
190 | return 0; | |
191 | ||
c14f9d92 | 192 | tail = addattr_nest(n, 4096, TCA_OPTIONS); |
aba5acdf SH |
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)); | |
c14f9d92 | 297 | addattr_nest_end(n, 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 | }; |