Home | History | Annotate | Download | only in tc
      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 (at) cyberus.ca)
     11  */
     12 
     13 #include <syslog.h>
     14 #include <sys/socket.h>
     15 #include <netinet/in.h>
     16 #include <arpa/inet.h>
     17 #include <linux/if.h>
     18 #include <iptables.h>
     19 #include <linux/netfilter.h>
     20 #include <linux/netfilter_ipv4/ip_tables.h>
     21 #include "utils.h"
     22 #include "tc_util.h"
     23 #include <linux/tc_act/tc_ipt.h>
     24 #include <stdio.h>
     25 #include <dlfcn.h>
     26 #include <getopt.h>
     27 #include <errno.h>
     28 #include <string.h>
     29 #include <netdb.h>
     30 #include <stdlib.h>
     31 #include <ctype.h>
     32 #include <stdarg.h>
     33 #include <unistd.h>
     34 #include <fcntl.h>
     35 #include <sys/wait.h>
     36 
     37 static const char *pname = "tc-ipt";
     38 static const char *tname = "mangle";
     39 static const char *pversion = "0.1";
     40 
     41 static const char *ipthooks[] = {
     42 	"NF_IP_PRE_ROUTING",
     43 	"NF_IP_LOCAL_IN",
     44 	"NF_IP_FORWARD",
     45 	"NF_IP_LOCAL_OUT",
     46 	"NF_IP_POST_ROUTING",
     47 };
     48 
     49 static struct option original_opts[] = {
     50 	{"jump", 1, 0, 'j'},
     51 	{0, 0, 0, 0}
     52 };
     53 
     54 static struct iptables_target *t_list = NULL;
     55 static struct option *opts = original_opts;
     56 static unsigned int global_option_offset = 0;
     57 #define OPTION_OFFSET 256
     58 
     59 char *lib_dir;
     60 
     61 void
     62 register_target(struct iptables_target *me)
     63 {
     64 /*      fprintf(stderr, "\nDummy register_target %s \n", me->name);
     65 */
     66 	me->next = t_list;
     67 	t_list = me;
     68 
     69 }
     70 
     71 void
     72 xtables_register_target(struct iptables_target *me)
     73 {
     74 	me->next = t_list;
     75 	t_list = me;
     76 }
     77 
     78 void
     79 exit_tryhelp(int status)
     80 {
     81 	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
     82 		pname, pname);
     83 	exit(status);
     84 }
     85 
     86 void
     87 exit_error(enum exittype status, char *msg, ...)
     88 {
     89 	va_list args;
     90 
     91 	va_start(args, msg);
     92 	fprintf(stderr, "%s v%s: ", pname, pversion);
     93 	vfprintf(stderr, msg, args);
     94 	va_end(args);
     95 	fprintf(stderr, "\n");
     96 	if (status == PARAMETER_PROBLEM)
     97 		exit_tryhelp(status);
     98 	if (status == VERSION_PROBLEM)
     99 		fprintf(stderr,
    100 			"Perhaps iptables or your kernel needs to be upgraded.\n");
    101 	exit(status);
    102 }
    103 
    104 /* stolen from iptables 1.2.11
    105 They should really have them as a library so i can link to them
    106 Email them next time i remember
    107 */
    108 
    109 char *
    110 addr_to_dotted(const struct in_addr *addrp)
    111 {
    112 	static char buf[20];
    113 	const unsigned char *bytep;
    114 
    115 	bytep = (const unsigned char *) &(addrp->s_addr);
    116 	sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
    117 	return buf;
    118 }
    119 
    120 int string_to_number_ll(const char *s, unsigned long long min,
    121 			unsigned long long max,
    122 		 unsigned long long *ret)
    123 {
    124 	unsigned long long number;
    125 	char *end;
    126 
    127 	/* Handle hex, octal, etc. */
    128 	errno = 0;
    129 	number = strtoull(s, &end, 0);
    130 	if (*end == '\0' && end != s) {
    131 		/* we parsed a number, let's see if we want this */
    132 		if (errno != ERANGE && min <= number && (!max || number <= max)) {
    133 			*ret = number;
    134 			return 0;
    135 		}
    136 	}
    137 	return -1;
    138 }
    139 
    140 int string_to_number_l(const char *s, unsigned long min, unsigned long max,
    141 		       unsigned long *ret)
    142 {
    143 	int result;
    144 	unsigned long long number;
    145 
    146 	result = string_to_number_ll(s, min, max, &number);
    147 	*ret = (unsigned long)number;
    148 
    149 	return result;
    150 }
    151 
    152 int string_to_number(const char *s, unsigned int min, unsigned int max,
    153 		unsigned int *ret)
    154 {
    155 	int result;
    156 	unsigned long number;
    157 
    158 	result = string_to_number_l(s, min, max, &number);
    159 	*ret = (unsigned int)number;
    160 
    161 	return result;
    162 }
    163 
    164 static void free_opts(struct option *local_opts)
    165 {
    166 	if (local_opts != original_opts) {
    167 		free(local_opts);
    168 		opts = original_opts;
    169 		global_option_offset = 0;
    170 	}
    171 }
    172 
    173 static struct option *
    174 merge_options(struct option *oldopts, const struct option *newopts,
    175 	      unsigned int *option_offset)
    176 {
    177 	struct option *merge;
    178 	unsigned int num_old, num_new, i;
    179 
    180 	for (num_old = 0; oldopts[num_old].name; num_old++) ;
    181 	for (num_new = 0; newopts[num_new].name; num_new++) ;
    182 
    183 	*option_offset = global_option_offset + OPTION_OFFSET;
    184 
    185 	merge = malloc(sizeof (struct option) * (num_new + num_old + 1));
    186 	memcpy(merge, oldopts, num_old * sizeof (struct option));
    187 	for (i = 0; i < num_new; i++) {
    188 		merge[num_old + i] = newopts[i];
    189 		merge[num_old + i].val += *option_offset;
    190 	}
    191 	memset(merge + num_old + num_new, 0, sizeof (struct option));
    192 
    193 	return merge;
    194 }
    195 
    196 static void *
    197 fw_calloc(size_t count, size_t size)
    198 {
    199 	void *p;
    200 
    201 	if ((p = (void *) calloc(count, size)) == NULL) {
    202 		perror("iptables: calloc failed");
    203 		exit(1);
    204 	}
    205 	return p;
    206 }
    207 
    208 static struct iptables_target *
    209 find_t(char *name)
    210 {
    211 	struct iptables_target *m;
    212 	for (m = t_list; m; m = m->next) {
    213 		if (strcmp(m->name, name) == 0)
    214 			return m;
    215 	}
    216 
    217 	return NULL;
    218 }
    219 
    220 static struct iptables_target *
    221 get_target_name(const char *name)
    222 {
    223 	void *handle;
    224 	char *error;
    225 	char *new_name, *lname;
    226 	struct iptables_target *m;
    227 	char path[strlen(lib_dir) + sizeof ("/libipt_.so") + strlen(name)];
    228 
    229 #ifdef NO_SHARED_LIBS
    230 	return NULL;
    231 #endif
    232 
    233 	new_name = malloc(strlen(name) + 1);
    234 	lname = malloc(strlen(name) + 1);
    235 	if (new_name)
    236 		memset(new_name, '\0', strlen(name) + 1);
    237 	else
    238 		exit_error(PARAMETER_PROBLEM, "get_target_name");
    239 
    240 	if (lname)
    241 		memset(lname, '\0', strlen(name) + 1);
    242 	else
    243 		exit_error(PARAMETER_PROBLEM, "get_target_name");
    244 
    245 	strcpy(new_name, name);
    246 	strcpy(lname, name);
    247 
    248 	if (isupper(lname[0])) {
    249 		int i;
    250 		for (i = 0; i < strlen(name); i++) {
    251 			lname[i] = tolower(lname[i]);
    252 		}
    253 	}
    254 
    255 	if (islower(new_name[0])) {
    256 		int i;
    257 		for (i = 0; i < strlen(new_name); i++) {
    258 			new_name[i] = toupper(new_name[i]);
    259 		}
    260 	}
    261 
    262 	/* try libxt_xx first */
    263 	sprintf(path, "%s/libxt_%s.so", lib_dir, new_name);
    264 	handle = dlopen(path, RTLD_LAZY);
    265 	if (!handle) {
    266 		/* try libipt_xx next */
    267 		sprintf(path, "%s/libipt_%s.so", lib_dir, new_name);
    268 		handle = dlopen(path, RTLD_LAZY);
    269 
    270 		if (!handle) {
    271 			sprintf(path, "%s/libxt_%s.so", lib_dir , lname);
    272 			handle = dlopen(path, RTLD_LAZY);
    273 		}
    274 
    275 		if (!handle) {
    276 			sprintf(path, "%s/libipt_%s.so", lib_dir , lname);
    277 			handle = dlopen(path, RTLD_LAZY);
    278 		}
    279 		/* ok, lets give up .. */
    280 		if (!handle) {
    281 			fputs(dlerror(), stderr);
    282 			printf("\n");
    283 			free(new_name);
    284 			return NULL;
    285 		}
    286 	}
    287 
    288 	m = dlsym(handle, new_name);
    289 	if ((error = dlerror()) != NULL) {
    290 		m = (struct iptables_target *) dlsym(handle, lname);
    291 		if ((error = dlerror()) != NULL) {
    292 			m = find_t(new_name);
    293 			if (NULL == m) {
    294 				m = find_t(lname);
    295 				if (NULL == m) {
    296 					fputs(error, stderr);
    297 					fprintf(stderr, "\n");
    298 					dlclose(handle);
    299 					free(new_name);
    300 					return NULL;
    301 				}
    302 			}
    303 		}
    304 	}
    305 
    306 	free(new_name);
    307 	return m;
    308 }
    309 
    310 
    311 struct in_addr *dotted_to_addr(const char *dotted)
    312 {
    313 	static struct in_addr addr;
    314 	unsigned char *addrp;
    315 	char *p, *q;
    316 	unsigned int onebyte;
    317 	int i;
    318 	char buf[20];
    319 
    320 	/* copy dotted string, because we need to modify it */
    321 	strncpy(buf, dotted, sizeof (buf) - 1);
    322 	addrp = (unsigned char *) &(addr.s_addr);
    323 
    324 	p = buf;
    325 	for (i = 0; i < 3; i++) {
    326 		if ((q = strchr(p, '.')) == NULL)
    327 			return (struct in_addr *) NULL;
    328 
    329 		*q = '\0';
    330 		if (string_to_number(p, 0, 255, &onebyte) == -1)
    331 			return (struct in_addr *) NULL;
    332 
    333 		addrp[i] = (unsigned char) onebyte;
    334 		p = q + 1;
    335 	}
    336 
    337 	/* we've checked 3 bytes, now we check the last one */
    338 	if (string_to_number(p, 0, 255, &onebyte) == -1)
    339 		return (struct in_addr *) NULL;
    340 
    341 	addrp[3] = (unsigned char) onebyte;
    342 
    343 	return &addr;
    344 }
    345 
    346 static void set_revision(char *name, u_int8_t revision)
    347 {
    348 	/* Old kernel sources don't have ".revision" field,
    349 	*  but we stole a byte from name. */
    350 	name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
    351 	name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
    352 }
    353 
    354 /*
    355  * we may need to check for version mismatch
    356 */
    357 int
    358 build_st(struct iptables_target *target, struct ipt_entry_target *t)
    359 {
    360 	unsigned int nfcache = 0;
    361 
    362 	if (target) {
    363 		size_t size;
    364 
    365 		size =
    366 		    IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size;
    367 
    368 		if (NULL == t) {
    369 			target->t = fw_calloc(1, size);
    370 			target->t->u.target_size = size;
    371 
    372 			if (target->init != NULL)
    373 				target->init(target->t, &nfcache);
    374 			set_revision(target->t->u.user.name, target->revision);
    375 		} else {
    376 			target->t = t;
    377 		}
    378 		strcpy(target->t->u.user.name, target->name);
    379 		return 0;
    380 	}
    381 
    382 	return -1;
    383 }
    384 
    385 static int parse_ipt(struct action_util *a,int *argc_p,
    386 		     char ***argv_p, int tca_id, struct nlmsghdr *n)
    387 {
    388 	struct iptables_target *m = NULL;
    389 	struct ipt_entry fw;
    390 	struct rtattr *tail;
    391 	int c;
    392 	int rargc = *argc_p;
    393 	char **argv = *argv_p;
    394 	int argc = 0, iargc = 0;
    395 	char k[16];
    396 	int res = -1;
    397 	int size = 0;
    398 	int iok = 0, ok = 0;
    399 	__u32 hook = 0, index = 0;
    400 	res = 0;
    401 
    402 	lib_dir = getenv("IPTABLES_LIB_DIR");
    403 	if (!lib_dir)
    404 		lib_dir = IPT_LIB_DIR;
    405 
    406 	{
    407 		int i;
    408 		for (i = 0; i < rargc; i++) {
    409 			if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
    410 				break;
    411 			}
    412 		}
    413 		iargc = argc = i;
    414 	}
    415 
    416 	if (argc <= 2) {
    417 		fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
    418 		return -1;
    419 	}
    420 
    421 	while (1) {
    422 		c = getopt_long(argc, argv, "j:", opts, NULL);
    423 		if (c == -1)
    424 			break;
    425 		switch (c) {
    426 		case 'j':
    427 			m = get_target_name(optarg);
    428 			if (NULL != m) {
    429 
    430 				if (0 > build_st(m, NULL)) {
    431 					printf(" %s error \n", m->name);
    432 					return -1;
    433 				}
    434 				opts =
    435 				    merge_options(opts, m->extra_opts,
    436 						  &m->option_offset);
    437 			} else {
    438 				fprintf(stderr," failed to find target %s\n\n", optarg);
    439 				return -1;
    440 			}
    441 			ok++;
    442 			break;
    443 
    444 		default:
    445 			memset(&fw, 0, sizeof (fw));
    446 			if (m) {
    447 				m->parse(c - m->option_offset, argv, 0,
    448 					 &m->tflags, NULL, &m->t);
    449 			} else {
    450 				fprintf(stderr," failed to find target %s\n\n", optarg);
    451 				return -1;
    452 
    453 			}
    454 			ok++;
    455 			break;
    456 
    457 		}
    458 	}
    459 
    460 	if (iargc > optind) {
    461 		if (matches(argv[optind], "index") == 0) {
    462 			if (get_u32(&index, argv[optind + 1], 10)) {
    463 				fprintf(stderr, "Illegal \"index\"\n");
    464 				free_opts(opts);
    465 				return -1;
    466 			}
    467 			iok++;
    468 
    469 			optind += 2;
    470 		}
    471 	}
    472 
    473 	if (!ok && !iok) {
    474 		fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
    475 		return -1;
    476 	}
    477 
    478 	/* check that we passed the correct parameters to the target */
    479 	if (m)
    480 		m->final_check(m->tflags);
    481 
    482 	{
    483 		struct tcmsg *t = NLMSG_DATA(n);
    484 		if (t->tcm_parent != TC_H_ROOT
    485 		    && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
    486 			hook = NF_IP_PRE_ROUTING;
    487 		} else {
    488 			hook = NF_IP_POST_ROUTING;
    489 		}
    490 	}
    491 
    492 	tail = NLMSG_TAIL(n);
    493 	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
    494 	fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
    495 	fprintf(stdout, "\ttarget: ");
    496 
    497 	if (m)
    498 		m->print(NULL, m->t, 0);
    499 	fprintf(stdout, " index %d\n", index);
    500 
    501 	if (strlen(tname) > 16) {
    502 		size = 16;
    503 		k[15] = 0;
    504 	} else {
    505 		size = 1 + strlen(tname);
    506 	}
    507 	strncpy(k, tname, size);
    508 
    509 	addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
    510 	addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
    511 	addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
    512 	if (m)
    513 		addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
    514 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    515 
    516 	argc -= optind;
    517 	argv += optind;
    518 	*argc_p = rargc - iargc;
    519 	*argv_p = argv;
    520 
    521 	optind = 0;
    522 	free_opts(opts);
    523 	/* Clear flags if target will be used again */
    524         m->tflags=0;
    525         m->used=0;
    526 	/* Free allocated memory */
    527         if (m->t)
    528             free(m->t);
    529 
    530 
    531 	return 0;
    532 
    533 }
    534 
    535 static int
    536 print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
    537 {
    538 	struct rtattr *tb[TCA_IPT_MAX + 1];
    539 	struct ipt_entry_target *t = NULL;
    540 
    541 	if (arg == NULL)
    542 		return -1;
    543 
    544 	lib_dir = getenv("IPTABLES_LIB_DIR");
    545 	if (!lib_dir)
    546 		lib_dir = IPT_LIB_DIR;
    547 
    548 	parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
    549 
    550 	if (tb[TCA_IPT_TABLE] == NULL) {
    551 		fprintf(f, "[NULL ipt table name ] assuming mangle ");
    552 	} else {
    553 		fprintf(f, "tablename: %s ",
    554 			(char *) RTA_DATA(tb[TCA_IPT_TABLE]));
    555 	}
    556 
    557 	if (tb[TCA_IPT_HOOK] == NULL) {
    558 		fprintf(f, "[NULL ipt hook name ]\n ");
    559 		return -1;
    560 	} else {
    561 		__u32 hook;
    562 		hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
    563 		fprintf(f, " hook: %s \n", ipthooks[hook]);
    564 	}
    565 
    566 	if (tb[TCA_IPT_TARG] == NULL) {
    567 		fprintf(f, "\t[NULL ipt target parameters ] \n");
    568 		return -1;
    569 	} else {
    570 		struct iptables_target *m = NULL;
    571 		t = RTA_DATA(tb[TCA_IPT_TARG]);
    572 		m = get_target_name(t->u.user.name);
    573 		if (NULL != m) {
    574 			if (0 > build_st(m, t)) {
    575 				fprintf(stderr, " %s error \n", m->name);
    576 				return -1;
    577 			}
    578 
    579 			opts =
    580 			    merge_options(opts, m->extra_opts,
    581 					  &m->option_offset);
    582 		} else {
    583 			fprintf(stderr, " failed to find target %s\n\n",
    584 				t->u.user.name);
    585 			return -1;
    586 		}
    587 		fprintf(f, "\ttarget ");
    588 		m->print(NULL, m->t, 0);
    589 		if (tb[TCA_IPT_INDEX] == NULL) {
    590 			fprintf(f, " [NULL ipt target index ]\n");
    591 		} else {
    592 			__u32 index;
    593 			index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
    594 			fprintf(f, " \n\tindex %d", index);
    595 		}
    596 
    597 		if (tb[TCA_IPT_CNT]) {
    598 			struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);;
    599 			fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
    600 		}
    601 		if (show_stats) {
    602 			if (tb[TCA_IPT_TM]) {
    603 				struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
    604 				print_tm(f,tm);
    605 			}
    606 		}
    607 		fprintf(f, " \n");
    608 
    609 	}
    610 	free_opts(opts);
    611 
    612 	return 0;
    613 }
    614 
    615 struct action_util ipt_action_util = {
    616         .id = "ipt",
    617         .parse_aopt = parse_ipt,
    618         .print_aopt = print_ipt,
    619 };
    620 
    621