X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=tc%2Fm_ipt.c;fp=tc%2Fm_ipt.c;h=518e4a3cebb47ce379bbf1623e1c4fdf60f92725;hb=4f7d3057911ebc5df78113896cb02e1b4ffbfabe;hp=0000000000000000000000000000000000000000;hpb=8a59994861a17eb92c11553d88631757ee8e63c3;p=iproute2.git diff --git a/tc/m_ipt.c b/tc/m_ipt.c new file mode 100644 index 0000000..518e4a3 --- /dev/null +++ b/tc/m_ipt.c @@ -0,0 +1,599 @@ +/* + * m_ipt.c iptables based targets + * utilities mostly ripped from iptables + * + * This program is free software; you can distribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * + * TODO: bad bad hardcoding IPT_LIB_DIR and PROC_SYS_MODPROBE + * +*/ + +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "tc_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *pname = "tc-ipt"; +const char *tname = "mangle"; +const char *pversion = "0.1"; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/local/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +static const char *ipthooks[] = { + "NF_IP_PRE_ROUTING", + "NF_IP_LOCAL_IN", + "NF_IP_FORWARD", + "NF_IP_LOCAL_OUT", + "NF_IP_POST_ROUTING", +}; + +static struct option original_opts[] = { + {"jump", 1, 0, 'j'}, + {0, 0, 0, 0} +}; + +static struct iptables_target *t_list = NULL; +static unsigned int global_option_offset = 0; +#define OPTION_OFFSET 256 + + +void +register_target(struct iptables_target *me) +{ +/* fprintf(stderr, "\nDummy register_target %s \n", me->name); +*/ + me->next = t_list; + t_list = me; + +} + +void +exit_tryhelp(int status) +{ + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + pname, pname); + exit(status); +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", pname, pversion); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +/* stolen from iptables 1.2.11 +They should really have them as a library so i can link to them +Email them next time i remember +*/ + +char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +int string_to_number_ll(const char *s, unsigned long long min, + unsigned long long max, + unsigned long long *ret) +{ + unsigned long long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtoull(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && (!max || number <= max)) { + *ret = number; + return 0; + } + } + return -1; +} + +int string_to_number_l(const char *s, unsigned long min, unsigned long max, + unsigned long *ret) +{ + int result; + unsigned long long number; + + result = string_to_number_ll(s, min, max, &number); + *ret = (unsigned long)number; + + return result; +} + +int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + int result; + unsigned long number; + + result = string_to_number_l(s, min, max, &number); + *ret = (unsigned int)number; + + return result; +} + +static struct option * +copy_options(struct option *oldopts) +{ + struct option *merge; + unsigned int num_old; + for (num_old = 0; oldopts[num_old].name; num_old++) ; + merge = malloc(sizeof (struct option) * (num_old + 1)); + if (NULL == merge) + return NULL; + memcpy(merge, oldopts, num_old * sizeof (struct option)); + memset(merge + num_old, 0, sizeof (struct option)); + return merge; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + struct option *merge; + unsigned int num_old, num_new, i; + + for (num_old = 0; oldopts[num_old].name; num_old++) ; + for (num_new = 0; newopts[num_new].name; num_new++) ; + + *option_offset = global_option_offset + OPTION_OFFSET; + + merge = malloc(sizeof (struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof (struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof (struct option)); + + return merge; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = (void *) calloc(count, size)) == NULL) { + perror("iptables: calloc failed"); + exit(1); + } + return p; +} + +static struct iptables_target * +find_t(char *name) +{ + struct iptables_target *m; + for (m = t_list; m; m = m->next) { + if (strcmp(m->name, name) == 0) + return m; + } + + return NULL; +} + +static struct iptables_target * +get_target_name(char *name) +{ + void *handle; + char *error; + char *new_name, *lname; + struct iptables_target *m; + + char path[sizeof (IPT_LIB_DIR) + sizeof ("/libipt_.so") + strlen(name)]; + + new_name = malloc(strlen(name) + 1); + lname = malloc(strlen(name) + 1); + if (new_name) + memset(new_name, '\0', strlen(name) + 1); + else + exit_error(PARAMETER_PROBLEM, "get_target_name"); + + if (lname) + memset(lname, '\0', strlen(name) + 1); + else + exit_error(PARAMETER_PROBLEM, "get_target_name"); + + strcpy(new_name, name); + strcpy(lname, name); + + if (isupper(lname[0])) { + int i; + for (i = 0; i < strlen(name); i++) { + lname[i] = tolower(lname[i]); + } + } + + if (islower(new_name[0])) { + int i; + for (i = 0; i < strlen(new_name); i++) { + new_name[i] = toupper(new_name[i]); + } + } + + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", new_name); + handle = dlopen(path, RTLD_LAZY); + if (!handle) { + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", lname); + handle = dlopen(path, RTLD_LAZY); + if (!handle) { + fputs(dlerror(), stderr); + printf("\n"); + return NULL; + } + } + + m = dlsym(handle, new_name); + if ((error = dlerror()) != NULL) { + m = (struct iptables_target *) dlsym(handle, lname); + if ((error = dlerror()) != NULL) { + m = find_t(new_name); + if (NULL == m) { + m = find_t(lname); + if (NULL == m) { + fputs(error, stderr); + fprintf(stderr, "\n"); + dlclose(handle); + return NULL; + } + } + } + } + + return m; +} + + +struct in_addr *dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof (buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +int +build_st(struct iptables_target *target, struct ipt_entry_target *t) +{ + unsigned int nfcache = 0; + + if (target) { + size_t size; + + size = + IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size; + + if (NULL == t) { + target->t = fw_calloc(1, size); + target->init(target->t, &nfcache); + target->t->u.target_size = size; + } else { + target->t = t; + } + strcpy(target->t->u.user.name, target->name); + return 0; + } + + return -1; +} + +static int parse_ipt(struct action_util *a,int *argc_p, + char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + struct iptables_target *m = NULL; + struct ipt_entry fw; + struct rtattr *tail; + int c; + int rargc = *argc_p; + char **argv = *argv_p; + struct option *opts; + int argc = 0, iargc = 0; + char k[16]; + int res = -1; + int size = 0; + int iok = 0, ok = 0; + __u32 hook = 0, index = 0; + res = 0; + + { + int i; + for (i = 0; i < rargc; i++) { + if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { + break; + } + } + iargc = argc = i; + } + + if (argc <= 2) { + fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc); + return -1; + } + + opts = copy_options(original_opts); + + if (NULL == opts) + return -1; + + while (1) { + c = getopt_long(argc, argv, "j:", opts, NULL); + if (c == -1) + break; + switch (c) { + case 'j': + m = get_target_name(optarg); + if (NULL != m) { + + if (0 > build_st(m, NULL)) { + printf(" %s error \n", m->name); + return -1; + } + opts = + merge_options(opts, m->extra_opts, + &m->option_offset); + } else { + fprintf(stderr," failed to find target %s\n\n", optarg); + return -1; + } + ok++; + break; + + default: + memset(&fw, 0, sizeof (fw)); + if (m) { + unsigned int fake_flags = 0; + m->parse(c - m->option_offset, argv, 0, + &fake_flags, NULL, &m->t); + } else { + fprintf(stderr," failed to find target %s\n\n", optarg); + return -1; + + } + ok++; + + /*m->final_check(m->t); -- Is this necessary? + ** useful when theres depencies + ** eg ipt_TCPMSS.c has have the TCP match loaded + ** before this can be used; + ** also seems the ECN target needs it + */ + + break; + + } + } + + if (iargc > optind) { + if (matches(argv[optind], "index") == 0) { + if (get_u32(&index, argv[optind + 1], 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + iok++; + + optind += 2; + } + } + + if (!ok && !iok) { + fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); + return -1; + } + + { + struct tcmsg *t = NLMSG_DATA(n); + if (t->tcm_parent != TC_H_ROOT + && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { + hook = NF_IP_PRE_ROUTING; + } else { + hook = NF_IP_POST_ROUTING; + } + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); + fprintf(stdout, "\ttarget: "); + + if (m) + m->print(NULL, m->t, 0); + fprintf(stdout, " index %d\n", index); + + if (strlen(tname) > 16) { + size = 16; + k[15] = 0; + } else { + size = 1 + strlen(tname); + } + strncpy(k, tname, size); + + addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size); + addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4); + addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4); + if (m) + addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + argc -= optind; + argv += optind; + *argc_p = rargc - iargc; + *argv_p = argv; + + optind = 1; + + return 0; + +} + +static int +print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct rtattr *tb[TCA_IPT_MAX + 1]; + struct ipt_entry_target *t = NULL; + struct option *opts; + + if (arg == NULL) + return -1; + + opts = copy_options(original_opts); + + if (NULL == opts) + return -1; + + parse_rtattr_nested(tb, TCA_IPT_MAX, arg); + + if (tb[TCA_IPT_TABLE] == NULL) { + fprintf(f, "[NULL ipt table name ] assuming mangle "); + } else { + fprintf(f, "tablename: %s ", + (char *) RTA_DATA(tb[TCA_IPT_TABLE])); + } + + if (tb[TCA_IPT_HOOK] == NULL) { + fprintf(f, "[NULL ipt hook name ]\n "); + return -1; + } else { + __u32 hook; + hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]); + fprintf(f, " hook: %s \n", ipthooks[hook]); + } + + if (tb[TCA_IPT_TARG] == NULL) { + fprintf(f, "\t[NULL ipt target parameters ] \n"); + return -1; + } else { + struct iptables_target *m = NULL; + t = RTA_DATA(tb[TCA_IPT_TARG]); + m = get_target_name(t->u.user.name); + if (NULL != m) { + if (0 > build_st(m, t)) { + fprintf(stderr, " %s error \n", m->name); + return -1; + } + + opts = + merge_options(opts, m->extra_opts, + &m->option_offset); + } else { + fprintf(stderr, " failed to find target %s\n\n", + t->u.user.name); + return -1; + } + fprintf(f, "\ttarget "); + m->print(NULL, m->t, 0); + if (tb[TCA_IPT_INDEX] == NULL) { + fprintf(f, " [NULL ipt target index ]\n"); + } else { + __u32 index; + index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]); + fprintf(f, " \n\tindex %d", index); + } + + if (tb[TCA_IPT_CNT]) { + struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; + fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); + } + if (show_stats) { + if (tb[TCA_IPT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); + print_tm(f,tm); + } + } + fprintf(f, " \n"); + + } + + return 0; +} + +struct action_util ipt_action_util = { + .id = "ipt", + .parse_aopt = parse_ipt, + .print_aopt = print_ipt, +}; +