iproute2/tc/f_rsvp.c
<<
>>
Prefs
   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 <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
  26static void explain(void)
  27{
  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");
  38}
  39
  40static 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
 166done:
 167        *argc_p = argc;
 168        *argv_p = argv;
 169        return 0;
 170}
 171
 172
 173static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc,
 174                          char **argv, struct nlmsghdr *n)
 175{
 176        int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6;
 177        struct tc_rsvp_pinfo pinfo = {};
 178        struct tcmsg *t = NLMSG_DATA(n);
 179        int pinfo_ok = 0;
 180        struct rtattr *tail;
 181
 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
 192        tail = addattr_nest(n, 4096, TCA_OPTIONS);
 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        addattr_nest_end(n, tail);
 298        return 0;
 299}
 300
 301static 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
 328static 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
 412struct filter_util rsvp_filter_util = {
 413        .id = "rsvp",
 414        .parse_fopt = rsvp_parse_opt,
 415        .print_fopt = rsvp_print_opt,
 416};
 417
 418struct filter_util rsvp6_filter_util = {
 419        .id = "rsvp6",
 420        .parse_fopt = rsvp_parse_opt,
 421        .print_fopt = rsvp_print_opt,
 422};
 423