]>
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> | |
16 | #include <syslog.h> | |
17 | #include <fcntl.h> | |
18 | #include <sys/socket.h> | |
19 | #include <netinet/in.h> | |
20 | #include <arpa/inet.h> | |
21 | #include <string.h> | |
22 | ||
23 | #include "rt_names.h" | |
24 | #include "utils.h" | |
25 | #include "tc_util.h" | |
26 | ||
27 | static void explain(void) | |
28 | { | |
29 | fprintf(stderr, "Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]\n"); | |
30 | fprintf(stderr, " [ sender SRC[/PORT | GPI ]\n"); | |
31 | fprintf(stderr, " [ classid CLASSID ] [ police POLICE_SPEC ]\n"); | |
32 | fprintf(stderr, " [ tunnelid ID ] [ tunnel ID skip NUMBER ]\n"); | |
33 | fprintf(stderr, "Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |\n"); | |
34 | fprintf(stderr, " u{8|16|32} NUMBER mask MASK at OFFSET}\n"); | |
35 | fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); | |
36 | fprintf(stderr, " FILTERID := X:Y\n"); | |
e9acc242 | 37 | fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); |
aba5acdf SH |
38 | } |
39 | ||
d1f28cf1 | 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; | |
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; | |
91 | NEXT_ARG(); | |
92 | if (get_u32(&gpi, *argv, 0)) | |
93 | return -1; | |
94 | pi->mask = htonl(0xFFFFFFFF); | |
95 | pi->key = htonl(gpi); | |
96 | pi->offset = 0; | |
97 | if (pinfo->protocol == 0) | |
98 | pinfo->protocol = IPPROTO_ESP; | |
99 | argc--; argv++; | |
100 | } else if (strcmp(*argv, "flowlabel") == 0) { | |
101 | __u32 flabel; | |
102 | NEXT_ARG(); | |
103 | if (get_u32(&flabel, *argv, 0)) | |
104 | return -1; | |
105 | if (family != AF_INET6) | |
106 | return -1; | |
107 | pi->mask = htonl(0x000FFFFF); | |
108 | pi->key = htonl(flabel) & pi->mask; | |
109 | pi->offset = -40; | |
110 | argc--; argv++; | |
111 | } else if (strcmp(*argv, "u32") == 0 || | |
112 | strcmp(*argv, "u16") == 0 || | |
113 | strcmp(*argv, "u8") == 0) { | |
114 | int sz = 1; | |
115 | __u32 tmp; | |
116 | __u32 mask = 0xff; | |
117 | if (strcmp(*argv, "u32") == 0) { | |
118 | sz = 4; | |
119 | mask = 0xffff; | |
120 | } else if (strcmp(*argv, "u16") == 0) { | |
121 | mask = 0xffffffff; | |
122 | sz = 2; | |
123 | } | |
124 | NEXT_ARG(); | |
125 | if (get_u32(&tmp, *argv, 0)) | |
126 | return -1; | |
127 | argc--; argv++; | |
128 | if (strcmp(*argv, "mask") == 0) { | |
129 | NEXT_ARG(); | |
130 | if (get_u32(&mask, *argv, 16)) | |
131 | return -1; | |
132 | argc--; argv++; | |
133 | } | |
134 | if (strcmp(*argv, "at") == 0) { | |
135 | NEXT_ARG(); | |
136 | if (get_integer(&pi->offset, *argv, 0)) | |
137 | return -1; | |
138 | argc--; argv++; | |
139 | } | |
140 | if (sz == 1) { | |
141 | if ((pi->offset & 3) == 0) { | |
142 | mask <<= 24; | |
143 | tmp <<= 24; | |
144 | } else if ((pi->offset & 3) == 1) { | |
145 | mask <<= 16; | |
146 | tmp <<= 16; | |
147 | } else if ((pi->offset & 3) == 3) { | |
148 | mask <<= 8; | |
149 | tmp <<= 8; | |
150 | } | |
151 | } else if (sz == 2) { | |
152 | if ((pi->offset & 3) == 0) { | |
153 | mask <<= 16; | |
154 | tmp <<= 16; | |
155 | } | |
156 | } | |
157 | pi->offset &= ~3; | |
158 | pi->mask = htonl(mask); | |
159 | pi->key = htonl(tmp) & pi->mask; | |
160 | } | |
161 | ||
162 | done: | |
163 | *argc_p = argc; | |
164 | *argv_p = argv; | |
165 | return 0; | |
166 | } | |
167 | ||
168 | ||
169 | static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) | |
170 | { | |
171 | int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; | |
172 | struct tc_rsvp_pinfo pinfo; | |
173 | struct tc_police tp; | |
174 | struct tcmsg *t = NLMSG_DATA(n); | |
175 | int pinfo_ok = 0; | |
176 | struct rtattr *tail; | |
177 | ||
178 | memset(&pinfo, 0, sizeof(pinfo)); | |
179 | memset(&tp, 0, sizeof(tp)); | |
180 | ||
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; | |
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; | |
209 | NEXT_ARG(); | |
210 | if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) { | |
211 | fprintf(stderr, "Illegal \"sender\"\n"); | |
212 | return -1; | |
213 | } | |
214 | addattr_l(n, 4096, TCA_RSVP_SRC, &addr.data, addr.bytelen); | |
215 | if (pinfo.spi.mask || pinfo.protocol) | |
216 | pinfo_ok++; | |
217 | continue; | |
218 | } else if (matches("ipproto", *argv) == 0) { | |
219 | int num; | |
220 | NEXT_ARG(); | |
221 | num = inet_proto_a2n(*argv); | |
222 | if (num < 0) { | |
223 | fprintf(stderr, "Illegal \"ipproto\"\n"); | |
224 | return -1; | |
225 | } | |
226 | pinfo.protocol = num; | |
227 | pinfo_ok++; | |
228 | } else if (matches(*argv, "classid") == 0 || | |
229 | strcmp(*argv, "flowid") == 0) { | |
230 | unsigned handle; | |
231 | NEXT_ARG(); | |
232 | if (get_tc_classid(&handle, *argv)) { | |
233 | fprintf(stderr, "Illegal \"classid\"\n"); | |
234 | return -1; | |
235 | } | |
236 | addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4); | |
237 | } else if (strcmp(*argv, "tunnelid") == 0) { | |
238 | unsigned tid; | |
239 | NEXT_ARG(); | |
240 | if (get_unsigned(&tid, *argv, 0)) { | |
241 | fprintf(stderr, "Illegal \"tunnelid\"\n"); | |
242 | return -1; | |
243 | } | |
244 | pinfo.tunnelid = tid; | |
245 | pinfo_ok++; | |
246 | } else if (strcmp(*argv, "tunnel") == 0) { | |
247 | unsigned tid; | |
248 | NEXT_ARG(); | |
249 | if (get_unsigned(&tid, *argv, 0)) { | |
250 | fprintf(stderr, "Illegal \"tunnel\"\n"); | |
251 | return -1; | |
252 | } | |
253 | addattr_l(n, 4096, TCA_RSVP_CLASSID, &tid, 4); | |
254 | NEXT_ARG(); | |
255 | if (strcmp(*argv, "skip") == 0) { | |
256 | NEXT_ARG(); | |
257 | } | |
258 | if (get_unsigned(&tid, *argv, 0)) { | |
259 | fprintf(stderr, "Illegal \"skip\"\n"); | |
260 | return -1; | |
261 | } | |
262 | pinfo.tunnelhdr = tid; | |
263 | pinfo_ok++; | |
264 | } else if (matches(*argv, "police") == 0) { | |
265 | NEXT_ARG(); | |
266 | if (parse_police(&argc, &argv, TCA_RSVP_POLICE, n)) { | |
267 | fprintf(stderr, "Illegal \"police\"\n"); | |
268 | return -1; | |
269 | } | |
270 | continue; | |
271 | } else if (strcmp(*argv, "help") == 0) { | |
272 | explain(); | |
273 | return -1; | |
274 | } else { | |
275 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
276 | explain(); | |
277 | return -1; | |
278 | } | |
279 | argc--; argv++; | |
280 | } | |
281 | ||
282 | if (pinfo_ok) | |
283 | addattr_l(n, 4096, TCA_RSVP_PINFO, &pinfo, sizeof(pinfo)); | |
228569c3 | 284 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
aba5acdf SH |
285 | return 0; |
286 | } | |
287 | ||
288 | static char * sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) | |
289 | { | |
290 | if (pi->offset == 0) { | |
291 | if (dir && pi->mask == htonl(0xFFFF)) { | |
292 | snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)); | |
293 | return buf; | |
294 | } | |
295 | if (!dir && pi->mask == htonl(0xFFFF0000)) { | |
296 | snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)>>16); | |
297 | return buf; | |
298 | } | |
299 | if (pi->mask == htonl(0xFFFFFFFF)) { | |
300 | snprintf(buf, SPRINT_BSIZE-1, " spi/esp 0x%08x", htonl(pi->key)); | |
301 | return buf; | |
302 | } | |
303 | } else if (pi->offset == 4 && pi->mask == htonl(0xFFFFFFFF)) { | |
304 | snprintf(buf, SPRINT_BSIZE-1, " spi/ah 0x%08x", htonl(pi->key)); | |
305 | return buf; | |
306 | } else if (pi->offset == -40 && pi->mask == htonl(0x000FFFFF)) { | |
307 | snprintf(buf, SPRINT_BSIZE-1, " flowlabel 0x%05x", htonl(pi->key)); | |
308 | return buf; | |
309 | } | |
310 | snprintf(buf, SPRINT_BSIZE-1, " u32 0x%08x mask %08x at %d", | |
311 | htonl(pi->key), htonl(pi->mask), pi->offset); | |
312 | return buf; | |
313 | } | |
314 | ||
315 | static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) | |
316 | { | |
317 | int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; | |
318 | struct rtattr *tb[TCA_RSVP_MAX+1]; | |
319 | struct tc_rsvp_pinfo *pinfo = NULL; | |
320 | ||
321 | if (opt == NULL) | |
322 | return 0; | |
323 | ||
3b3ecd31 | 324 | parse_rtattr_nested(tb, TCA_RSVP_MAX, opt); |
aba5acdf SH |
325 | |
326 | if (handle) | |
327 | fprintf(f, "fh 0x%08x ", handle); | |
328 | ||
329 | if (tb[TCA_RSVP_PINFO]) { | |
330 | if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO]) < sizeof(*pinfo)) | |
331 | return -1; | |
332 | ||
333 | pinfo = RTA_DATA(tb[TCA_RSVP_PINFO]); | |
334 | } | |
335 | ||
336 | if (tb[TCA_RSVP_CLASSID]) { | |
337 | SPRINT_BUF(b1); | |
338 | if (!pinfo || pinfo->tunnelhdr == 0) | |
ff24746c | 339 | fprintf(f, "flowid %s ", sprint_tc_classid(rta_getattr_u32(tb[TCA_RSVP_CLASSID]), b1)); |
aba5acdf | 340 | else |
ff24746c | 341 | fprintf(f, "tunnel %d skip %d ", rta_getattr_u32(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr); |
aba5acdf SH |
342 | } else if (pinfo && pinfo->tunnelhdr) |
343 | fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr); | |
344 | ||
345 | if (tb[TCA_RSVP_DST]) { | |
346 | char buf[128]; | |
347 | fprintf(f, "session "); | |
348 | if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0) | |
349 | fprintf(f, " [INVALID DADDR] "); | |
350 | else | |
351 | fprintf(f, "%s", buf); | |
352 | if (pinfo && pinfo->dpi.mask) { | |
353 | SPRINT_BUF(b2); | |
354 | fprintf(f, "%s ", sprint_spi(&pinfo->dpi, 1, b2)); | |
355 | } else | |
356 | fprintf(f, " "); | |
357 | } else { | |
358 | if (pinfo && pinfo->dpi.mask) { | |
359 | SPRINT_BUF(b2); | |
360 | fprintf(f, "session [NONE]%s ", sprint_spi(&pinfo->dpi, 1, b2)); | |
361 | } else | |
362 | fprintf(f, "session NONE "); | |
363 | } | |
364 | ||
365 | if (pinfo && pinfo->protocol) { | |
366 | SPRINT_BUF(b1); | |
367 | fprintf(f, "ipproto %s ", inet_proto_n2a(pinfo->protocol, b1, sizeof(b1))); | |
368 | } | |
369 | if (pinfo && pinfo->tunnelid) | |
370 | fprintf(f, "tunnelid %d ", pinfo->tunnelid); | |
371 | if (tb[TCA_RSVP_SRC]) { | |
372 | char buf[128]; | |
373 | fprintf(f, "sender "); | |
374 | if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) { | |
375 | fprintf(f, "[BAD]"); | |
376 | } else { | |
377 | fprintf(f, " %s", buf); | |
378 | } | |
379 | if (pinfo && pinfo->spi.mask) { | |
380 | SPRINT_BUF(b2); | |
381 | fprintf(f, "%s ", sprint_spi(&pinfo->spi, 0, b2)); | |
382 | } else | |
383 | fprintf(f, " "); | |
384 | } else if (pinfo && pinfo->spi.mask) { | |
385 | SPRINT_BUF(b2); | |
386 | fprintf(f, "sender [NONE]%s ", sprint_spi(&pinfo->spi, 0, b2)); | |
387 | } | |
388 | if (tb[TCA_RSVP_POLICE]) | |
389 | tc_print_police(f, tb[TCA_RSVP_POLICE]); | |
390 | return 0; | |
391 | } | |
392 | ||
6b7dff17 SH |
393 | struct filter_util rsvp_filter_util = { |
394 | .id = "rsvp", | |
395 | .parse_fopt = rsvp_parse_opt, | |
396 | .print_fopt = rsvp_print_opt, | |
aba5acdf SH |
397 | }; |
398 | ||
6b7dff17 SH |
399 | struct filter_util rsvp6_filter_util = { |
400 | .id = "rsvp6", | |
401 | .parse_fopt = rsvp_parse_opt, | |
402 | .print_fopt = rsvp_print_opt, | |
aba5acdf | 403 | }; |