iproute2/tc/m_ipt.c
<<
>>
Prefs
   1/*
   2 * m_ipt.c      iptables based targets
   3 *              utilities mostly ripped from iptables <duh, its the linux way>
   4 *
   5 *              This program is free software; you can distribute it and/or
   6 *              modify it under the terms of the GNU General Public License
   7 *              as published by the Free Software Foundation; either version
   8 *              2 of the License, or (at your option) any later version.
   9 *
  10 * Authors:  J Hadi Salim (hadi@cyberus.ca)
  11 */
  12
  13#include <sys/socket.h>
  14#include <netinet/in.h>
  15#include <arpa/inet.h>
  16#include <iptables.h>
  17#include <linux/netfilter.h>
  18#include <linux/netfilter_ipv4/ip_tables.h>
  19#include "utils.h"
  20#include "tc_util.h"
  21#include <linux/tc_act/tc_ipt.h>
  22#include <stdio.h>
  23#include <dlfcn.h>
  24#include <getopt.h>
  25#include <errno.h>
  26#include <string.h>
  27#include <netdb.h>
  28#include <stdlib.h>
  29#include <ctype.h>
  30#include <stdarg.h>
  31#include <unistd.h>
  32#include <fcntl.h>
  33#include <sys/wait.h>
  34
  35static const char *pname = "tc-ipt";
  36static const char *tname = "mangle";
  37static const char *pversion = "0.1";
  38
  39static const char *ipthooks[] = {
  40        "NF_IP_PRE_ROUTING",
  41        "NF_IP_LOCAL_IN",
  42        "NF_IP_FORWARD",
  43        "NF_IP_LOCAL_OUT",
  44        "NF_IP_POST_ROUTING",
  45};
  46
  47static struct option original_opts[] = {
  48        {"jump", 1, 0, 'j'},
  49        {0, 0, 0, 0}
  50};
  51
  52static struct xtables_target *t_list;
  53static struct option *opts = original_opts;
  54static unsigned int global_option_offset;
  55#define OPTION_OFFSET 256
  56
  57char *lib_dir;
  58
  59void
  60xtables_register_target(struct xtables_target *me)
  61{
  62        me->next = t_list;
  63        t_list = me;
  64
  65}
  66
  67static void exit_tryhelp(int status)
  68{
  69        fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
  70                pname, pname);
  71        exit(status);
  72}
  73
  74static void exit_error(enum xtables_exittype status, char *msg, ...)
  75{
  76        va_list args;
  77
  78        va_start(args, msg);
  79        fprintf(stderr, "%s v%s: ", pname, pversion);
  80        vfprintf(stderr, msg, args);
  81        va_end(args);
  82        fprintf(stderr, "\n");
  83        if (status == PARAMETER_PROBLEM)
  84                exit_tryhelp(status);
  85        if (status == VERSION_PROBLEM)
  86                fprintf(stderr,
  87                        "Perhaps iptables or your kernel needs to be upgraded.\n");
  88        exit(status);
  89}
  90
  91/* stolen from iptables 1.2.11
  92They should really have them as a library so i can link to them
  93Email them next time i remember
  94*/
  95
  96static void free_opts(struct option *local_opts)
  97{
  98        if (local_opts != original_opts) {
  99                free(local_opts);
 100                opts = original_opts;
 101                global_option_offset = 0;
 102        }
 103}
 104
 105static struct option *
 106merge_options(struct option *oldopts, const struct option *newopts,
 107              unsigned int *option_offset)
 108{
 109        struct option *merge;
 110        unsigned int num_old, num_new, i;
 111
 112        for (num_old = 0; oldopts[num_old].name; num_old++);
 113        for (num_new = 0; newopts[num_new].name; num_new++);
 114
 115        *option_offset = global_option_offset + OPTION_OFFSET;
 116
 117        merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
 118        memcpy(merge, oldopts, num_old * sizeof(struct option));
 119        for (i = 0; i < num_new; i++) {
 120                merge[num_old + i] = newopts[i];
 121                merge[num_old + i].val += *option_offset;
 122        }
 123        memset(merge + num_old + num_new, 0, sizeof(struct option));
 124
 125        return merge;
 126}
 127
 128static void *
 129fw_calloc(size_t count, size_t size)
 130{
 131        void *p;
 132
 133        if ((p = (void *) calloc(count, size)) == NULL) {
 134                perror("iptables: calloc failed");
 135                exit(1);
 136        }
 137        return p;
 138}
 139
 140static struct xtables_target *
 141find_t(char *name)
 142{
 143        struct xtables_target *m;
 144
 145        for (m = t_list; m; m = m->next) {
 146                if (strcmp(m->name, name) == 0)
 147                        return m;
 148        }
 149
 150        return NULL;
 151}
 152
 153static struct xtables_target *
 154get_target_name(const char *name)
 155{
 156        void *handle;
 157        char *error;
 158        char *new_name, *lname;
 159        struct xtables_target *m;
 160        char path[strlen(lib_dir) + sizeof("/libipt_.so") + strlen(name)];
 161
 162#ifdef NO_SHARED_LIBS
 163        return NULL;
 164#endif
 165
 166        new_name = calloc(1, strlen(name) + 1);
 167        lname = calloc(1, strlen(name) + 1);
 168        if (!new_name)
 169                exit_error(PARAMETER_PROBLEM, "get_target_name");
 170        if (!lname)
 171                exit_error(PARAMETER_PROBLEM, "get_target_name");
 172
 173        strcpy(new_name, name);
 174        strcpy(lname, name);
 175
 176        if (isupper(lname[0])) {
 177                int i;
 178
 179                for (i = 0; i < strlen(name); i++) {
 180                        lname[i] = tolower(lname[i]);
 181                }
 182        }
 183
 184        if (islower(new_name[0])) {
 185                int i;
 186
 187                for (i = 0; i < strlen(new_name); i++) {
 188                        new_name[i] = toupper(new_name[i]);
 189                }
 190        }
 191
 192        /* try libxt_xx first */
 193        sprintf(path, "%s/libxt_%s.so", lib_dir, new_name);
 194        handle = dlopen(path, RTLD_LAZY);
 195        if (!handle) {
 196                /* try libipt_xx next */
 197                sprintf(path, "%s/libipt_%s.so", lib_dir, new_name);
 198                handle = dlopen(path, RTLD_LAZY);
 199
 200                if (!handle) {
 201                        sprintf(path, "%s/libxt_%s.so", lib_dir, lname);
 202                        handle = dlopen(path, RTLD_LAZY);
 203                }
 204
 205                if (!handle) {
 206                        sprintf(path, "%s/libipt_%s.so", lib_dir, lname);
 207                        handle = dlopen(path, RTLD_LAZY);
 208                }
 209                /* ok, lets give up .. */
 210                if (!handle) {
 211                        fputs(dlerror(), stderr);
 212                        printf("\n");
 213                        free(new_name);
 214                        free(lname);
 215                        return NULL;
 216                }
 217        }
 218
 219        m = dlsym(handle, new_name);
 220        if ((error = dlerror()) != NULL) {
 221                m = (struct xtables_target *) dlsym(handle, lname);
 222                if ((error = dlerror()) != NULL) {
 223                        m = find_t(new_name);
 224                        if (m == NULL) {
 225                                m = find_t(lname);
 226                                if (m == NULL) {
 227                                        fputs(error, stderr);
 228                                        fprintf(stderr, "\n");
 229                                        dlclose(handle);
 230                                        free(new_name);
 231                                        free(lname);
 232                                        return NULL;
 233                                }
 234                        }
 235                }
 236        }
 237
 238        free(new_name);
 239        free(lname);
 240        return m;
 241}
 242
 243static void set_revision(char *name, u_int8_t revision)
 244{
 245        /* Old kernel sources don't have ".revision" field,
 246        *  but we stole a byte from name. */
 247        name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
 248        name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
 249}
 250
 251/*
 252 * we may need to check for version mismatch
 253*/
 254static int build_st(struct xtables_target *target, struct ipt_entry_target *t)
 255{
 256        if (target) {
 257                size_t size;
 258
 259                size =
 260                    XT_ALIGN(sizeof(struct ipt_entry_target)) + target->size;
 261
 262                if (t == NULL) {
 263                        target->t = fw_calloc(1, size);
 264                        target->t->u.target_size = size;
 265
 266                        if (target->init != NULL)
 267                                target->init(target->t);
 268                        set_revision(target->t->u.user.name, target->revision);
 269                } else {
 270                        target->t = t;
 271                }
 272                strcpy(target->t->u.user.name, target->name);
 273                return 0;
 274        }
 275
 276        return -1;
 277}
 278
 279static int parse_ipt(struct action_util *a, int *argc_p,
 280                     char ***argv_p, int tca_id, struct nlmsghdr *n)
 281{
 282        struct xtables_target *m = NULL;
 283        struct ipt_entry fw;
 284        struct rtattr *tail;
 285        int c;
 286        int rargc = *argc_p;
 287        char **argv = *argv_p;
 288        int argc = 0, iargc = 0;
 289        char k[FILTER_NAMESZ];
 290        int size = 0;
 291        int iok = 0, ok = 0;
 292        __u32 hook = 0, index = 0;
 293
 294        lib_dir = getenv("IPTABLES_LIB_DIR");
 295        if (!lib_dir)
 296                lib_dir = IPT_LIB_DIR;
 297
 298        {
 299                int i;
 300
 301                for (i = 0; i < rargc; i++) {
 302                        if (!argv[i] || strcmp(argv[i], "action") == 0)
 303                                break;
 304                }
 305                iargc = argc = i;
 306        }
 307
 308        if (argc <= 2) {
 309                fprintf(stderr, "bad arguments to ipt %d vs %d\n", argc, rargc);
 310                return -1;
 311        }
 312
 313        while (1) {
 314                c = getopt_long(argc, argv, "j:", opts, NULL);
 315                if (c == -1)
 316                        break;
 317                switch (c) {
 318                case 'j':
 319                        m = get_target_name(optarg);
 320                        if (m != NULL) {
 321
 322                                if (build_st(m, NULL) < 0) {
 323                                        printf(" %s error\n", m->name);
 324                                        return -1;
 325                                }
 326                                opts =
 327                                    merge_options(opts, m->extra_opts,
 328                                                  &m->option_offset);
 329                        } else {
 330                                fprintf(stderr, " failed to find target %s\n\n", optarg);
 331                                return -1;
 332                        }
 333                        ok++;
 334                        break;
 335
 336                default:
 337                        memset(&fw, 0, sizeof(fw));
 338                        if (m) {
 339                                m->parse(c - m->option_offset, argv, 0,
 340                                         &m->tflags, NULL, &m->t);
 341                        } else {
 342                                fprintf(stderr, " failed to find target %s\n\n", optarg);
 343                                return -1;
 344
 345                        }
 346                        ok++;
 347                        break;
 348
 349                }
 350        }
 351
 352        if (iargc > optind) {
 353                if (matches(argv[optind], "index") == 0) {
 354                        if (get_u32(&index, argv[optind + 1], 10)) {
 355                                fprintf(stderr, "Illegal \"index\"\n");
 356                                free_opts(opts);
 357                                return -1;
 358                        }
 359                        iok++;
 360
 361                        optind += 2;
 362                }
 363        }
 364
 365        if (!ok && !iok) {
 366                fprintf(stderr, " ipt Parser BAD!! (%s)\n", *argv);
 367                return -1;
 368        }
 369
 370        /* check that we passed the correct parameters to the target */
 371        if (m)
 372                m->final_check(m->tflags);
 373
 374        {
 375                struct tcmsg *t = NLMSG_DATA(n);
 376
 377                if (t->tcm_parent != TC_H_ROOT
 378                    && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
 379                        hook = NF_IP_PRE_ROUTING;
 380                } else {
 381                        hook = NF_IP_POST_ROUTING;
 382                }
 383        }
 384
 385        tail = addattr_nest(n, MAX_MSG, tca_id);
 386        fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
 387        fprintf(stdout, "\ttarget: ");
 388
 389        if (m)
 390                m->print(NULL, m->t, 0);
 391        fprintf(stdout, " index %d\n", index);
 392
 393        if (strlen(tname) > 16) {
 394                size = 16;
 395                k[15] = 0;
 396        } else {
 397                size = 1 + strlen(tname);
 398        }
 399        strncpy(k, tname, size);
 400
 401        addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
 402        addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
 403        addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
 404        if (m)
 405                addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
 406        addattr_nest_end(n, tail);
 407
 408        argc -= optind;
 409        argv += optind;
 410        *argc_p = rargc - iargc;
 411        *argv_p = argv;
 412
 413        optind = 0;
 414        free_opts(opts);
 415        /* Clear flags if target will be used again */
 416        m->tflags = 0;
 417        m->used = 0;
 418        /* Free allocated memory */
 419        if (m->t)
 420            free(m->t);
 421
 422
 423        return 0;
 424
 425}
 426
 427static int
 428print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
 429{
 430        struct rtattr *tb[TCA_IPT_MAX + 1];
 431        struct ipt_entry_target *t = NULL;
 432        struct xtables_target *m;
 433        __u32 hook;
 434
 435        if (arg == NULL)
 436                return 0;
 437
 438        lib_dir = getenv("IPTABLES_LIB_DIR");
 439        if (!lib_dir)
 440                lib_dir = IPT_LIB_DIR;
 441
 442        parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
 443
 444        if (tb[TCA_IPT_TABLE] == NULL) {
 445                fprintf(stderr,  "Missing ipt table name, assuming mangle\n");
 446        } else {
 447                fprintf(f, "tablename: %s ",
 448                        rta_getattr_str(tb[TCA_IPT_TABLE]));
 449        }
 450
 451        if (tb[TCA_IPT_HOOK] == NULL) {
 452                fprintf(stderr, "Missing ipt hook name\n ");
 453                return -1;
 454        }
 455
 456        hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
 457        fprintf(f, " hook: %s\n", ipthooks[hook]);
 458
 459        if (tb[TCA_IPT_TARG] == NULL) {
 460                fprintf(stderr, "Missing ipt target parameters\n");
 461                return -1;
 462        }
 463
 464
 465        t = RTA_DATA(tb[TCA_IPT_TARG]);
 466        m = get_target_name(t->u.user.name);
 467        if (m != NULL) {
 468                if (build_st(m, t) < 0) {
 469                        fprintf(stderr, " %s error\n", m->name);
 470                        return -1;
 471                }
 472
 473                opts =
 474                        merge_options(opts, m->extra_opts,
 475                                      &m->option_offset);
 476        } else {
 477                fprintf(stderr, " failed to find target %s\n\n",
 478                        t->u.user.name);
 479                return -1;
 480        }
 481
 482        fprintf(f, "\ttarget ");
 483        m->print(NULL, m->t, 0);
 484        if (tb[TCA_IPT_INDEX] == NULL) {
 485                fprintf(stderr, "Missing ipt target index\n");
 486        } else {
 487                __u32 index;
 488
 489                index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
 490                fprintf(f, "\n\tindex %u", index);
 491        }
 492
 493        if (tb[TCA_IPT_CNT]) {
 494                struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);
 495
 496                fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
 497        }
 498        if (show_stats) {
 499                if (tb[TCA_IPT_TM]) {
 500                        struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
 501
 502                        print_tm(f, tm);
 503                }
 504        }
 505        fprintf(f, "\n");
 506
 507        free_opts(opts);
 508
 509        return 0;
 510}
 511
 512struct action_util ipt_action_util = {
 513        .id = "ipt",
 514        .parse_aopt = parse_ipt,
 515        .print_aopt = print_ipt,
 516};
 517