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 (at) 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 ] [ action ACTION_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, " ACTION_SPEC := ... look at individual actions\n"); 36 fprintf(stderr, " FILTERID := X:Y\n"); 37 fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n"); 38 } 39 40 static int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix *addr, 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 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; 92 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; 104 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; 120 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 173 static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) 174 { 175 int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; 176 struct tc_rsvp_pinfo pinfo = {}; 177 struct tcmsg *t = NLMSG_DATA(n); 178 int pinfo_ok = 0; 179 struct rtattr *tail; 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 191 tail = NLMSG_TAIL(n); 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 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; 210 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; 222 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) { 233 unsigned int handle; 234 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) { 242 unsigned int tid; 243 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) { 252 unsigned int tid; 253 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++; 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; 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)); 297 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 298 return 0; 299 } 300 301 static char *sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) 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 337 parse_rtattr_nested(tb, TCA_RSVP_MAX, opt); 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) 352 fprintf(f, "flowid %s ", sprint_tc_classid(rta_getattr_u32(tb[TCA_RSVP_CLASSID]), b1)); 353 else 354 fprintf(f, "tunnel %d skip %d ", rta_getattr_u32(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr); 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]; 360 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]; 387 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 } 403 404 if (tb[TCA_RSVP_ACT]) { 405 tc_print_action(f, tb[TCA_RSVP_ACT], 0); 406 } 407 if (tb[TCA_RSVP_POLICE]) 408 tc_print_police(f, tb[TCA_RSVP_POLICE]); 409 return 0; 410 } 411 412 struct filter_util rsvp_filter_util = { 413 .id = "rsvp", 414 .parse_fopt = rsvp_parse_opt, 415 .print_fopt = rsvp_print_opt, 416 }; 417 418 struct filter_util rsvp6_filter_util = { 419 .id = "rsvp6", 420 .parse_fopt = rsvp_parse_opt, 421 .print_fopt = rsvp_print_opt, 422 }; 423