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 			free(lname);
    285 			return NULL;
    286 		}
    287 	}
    288 
    289 	m = dlsym(handle, new_name);
    290 	if ((error = dlerror()) != NULL) {
    291 		m = (struct iptables_target *) dlsym(handle, lname);
    292 		if ((error = dlerror()) != NULL) {
    293 			m = find_t(new_name);
    294 			if (NULL == m) {
    295 				m = find_t(lname);
    296 				if (NULL == m) {
    297 					fputs(error, stderr);
    298 					fprintf(stderr, "\n");
    299 					dlclose(handle);
    300 					free(new_name);
    301 					free(lname);
    302 					return NULL;
    303 				}
    304 			}
    305 		}
    306 	}
    307 
    308 	free(new_name);
    309 	free(lname);
    310 	return m;
    311 }
    312 
    313 
    314 struct in_addr *dotted_to_addr(const char *dotted)
    315 {
    316 	static struct in_addr addr;
    317 	unsigned char *addrp;
    318 	char *p, *q;
    319 	unsigned int onebyte;
    320 	int i;
    321 	char buf[20];
    322 
    323 	/* copy dotted string, because we need to modify it */
    324 	strncpy(buf, dotted, sizeof (buf) - 1);
    325 	addrp = (unsigned char *) &(addr.s_addr);
    326 
    327 	p = buf;
    328 	for (i = 0; i < 3; i++) {
    329 		if ((q = strchr(p, '.')) == NULL)
    330 			return (struct in_addr *) NULL;
    331 
    332 		*q = '\0';
    333 		if (string_to_number(p, 0, 255, &onebyte) == -1)
    334 			return (struct in_addr *) NULL;
    335 
    336 		addrp[i] = (unsigned char) onebyte;
    337 		p = q + 1;
    338 	}
    339 
    340 	/* we've checked 3 bytes, now we check the last one */
    341 	if (string_to_number(p, 0, 255, &onebyte) == -1)
    342 		return (struct in_addr *) NULL;
    343 
    344 	addrp[3] = (unsigned char) onebyte;
    345 
    346 	return &addr;
    347 }
    348 
    349 static void set_revision(char *name, u_int8_t revision)
    350 {
    351 	/* Old kernel sources don't have ".revision" field,
    352 	*  but we stole a byte from name. */
    353 	name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
    354 	name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
    355 }
    356 
    357 /*
    358  * we may need to check for version mismatch
    359 */
    360 int
    361 build_st(struct iptables_target *target, struct ipt_entry_target *t)
    362 {
    363 	unsigned int nfcache = 0;
    364 
    365 	if (target) {
    366 		size_t size;
    367 
    368 		size =
    369 		    IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size;
    370 
    371 		if (NULL == t) {
    372 			target->t = fw_calloc(1, size);
    373 			target->t->u.target_size = size;
    374 
    375 			if (target->init != NULL)
    376 				target->init(target->t, &nfcache);
    377 			set_revision(target->t->u.user.name, target->revision);
    378 		} else {
    379 			target->t = t;
    380 		}
    381 		strcpy(target->t->u.user.name, target->name);
    382 		return 0;
    383 	}
    384 
    385 	return -1;
    386 }
    387 
    388 static int parse_ipt(struct action_util *a,int *argc_p,
    389 		     char ***argv_p, int tca_id, struct nlmsghdr *n)
    390 {
    391 	struct iptables_target *m = NULL;
    392 	struct ipt_entry fw;
    393 	struct rtattr *tail;
    394 	int c;
    395 	int rargc = *argc_p;
    396 	char **argv = *argv_p;
    397 	int argc = 0, iargc = 0;
    398 	char k[16];
    399 	int size = 0;
    400 	int iok = 0, ok = 0;
    401 	__u32 hook = 0, index = 0;
    402 
    403 	lib_dir = getenv("IPTABLES_LIB_DIR");
    404 	if (!lib_dir)
    405 		lib_dir = IPT_LIB_DIR;
    406 
    407 	{
    408 		int i;
    409 		for (i = 0; i < rargc; i++) {
    410 			if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
    411 				break;
    412 			}
    413 		}
    414 		iargc = argc = i;
    415 	}
    416 
    417 	if (argc <= 2) {
    418 		fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
    419 		return -1;
    420 	}
    421 
    422 	while (1) {
    423 		c = getopt_long(argc, argv, "j:", opts, NULL);
    424 		if (c == -1)
    425 			break;
    426 		switch (c) {
    427 		case 'j':
    428 			m = get_target_name(optarg);
    429 			if (NULL != m) {
    430 
    431 				if (0 > build_st(m, NULL)) {
    432 					printf(" %s error \n", m->name);
    433 					return -1;
    434 				}
    435 				opts =
    436 				    merge_options(opts, m->extra_opts,
    437 						  &m->option_offset);
    438 			} else {
    439 				fprintf(stderr," failed to find target %s\n\n", optarg);
    440 				return -1;
    441 			}
    442 			ok++;
    443 			break;
    444 
    445 		default:
    446 			memset(&fw, 0, sizeof (fw));
    447 			if (m) {
    448 				m->parse(c - m->option_offset, argv, 0,
    449 					 &m->tflags, NULL, &m->t);
    450 			} else {
    451 				fprintf(stderr," failed to find target %s\n\n", optarg);
    452 				return -1;
    453 
    454 			}
    455 			ok++;
    456 			break;
    457 
    458 		}
    459 	}
    460 
    461 	if (iargc > optind) {
    462 		if (matches(argv[optind], "index") == 0) {
    463 			if (get_u32(&index, argv[optind + 1], 10)) {
    464 				fprintf(stderr, "Illegal \"index\"\n");
    465 				free_opts(opts);
    466 				return -1;
    467 			}
    468 			iok++;
    469 
    470 			optind += 2;
    471 		}
    472 	}
    473 
    474 	if (!ok && !iok) {
    475 		fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
    476 		return -1;
    477 	}
    478 
    479 	/* check that we passed the correct parameters to the target */
    480 	if (m)
    481 		m->final_check(m->tflags);
    482 
    483 	{
    484 		struct tcmsg *t = NLMSG_DATA(n);
    485 		if (t->tcm_parent != TC_H_ROOT
    486 		    && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
    487 			hook = NF_IP_PRE_ROUTING;
    488 		} else {
    489 			hook = NF_IP_POST_ROUTING;
    490 		}
    491 	}
    492 
    493 	tail = NLMSG_TAIL(n);
    494 	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
    495 	fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
    496 	fprintf(stdout, "\ttarget: ");
    497 
    498 	if (m)
    499 		m->print(NULL, m->t, 0);
    500 	fprintf(stdout, " index %d\n", index);
    501 
    502 	if (strlen(tname) > 16) {
    503 		size = 16;
    504 		k[15] = 0;
    505 	} else {
    506 		size = 1 + strlen(tname);
    507 	}
    508 	strncpy(k, tname, size);
    509 
    510 	addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
    511 	addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
    512 	addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
    513 	if (m)
    514 		addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
    515 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    516 
    517 	argc -= optind;
    518 	argv += optind;
    519 	*argc_p = rargc - iargc;
    520 	*argv_p = argv;
    521 
    522 	optind = 0;
    523 	free_opts(opts);
    524 	/* Clear flags if target will be used again */
    525         m->tflags=0;
    526         m->used=0;
    527 	/* Free allocated memory */
    528         if (m->t)
    529             free(m->t);
    530 
    531 
    532 	return 0;
    533 
    534 }
    535 
    536 static int
    537 print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
    538 {
    539 	struct rtattr *tb[TCA_IPT_MAX + 1];
    540 	struct ipt_entry_target *t = NULL;
    541 
    542 	if (arg == NULL)
    543 		return -1;
    544 
    545 	lib_dir = getenv("IPTABLES_LIB_DIR");
    546 	if (!lib_dir)
    547 		lib_dir = IPT_LIB_DIR;
    548 
    549 	parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
    550 
    551 	if (tb[TCA_IPT_TABLE] == NULL) {
    552 		fprintf(f, "[NULL ipt table name ] assuming mangle ");
    553 	} else {
    554 		fprintf(f, "tablename: %s ",
    555 			rta_getattr_str(tb[TCA_IPT_TABLE]));
    556 	}
    557 
    558 	if (tb[TCA_IPT_HOOK] == NULL) {
    559 		fprintf(f, "[NULL ipt hook name ]\n ");
    560 		return -1;
    561 	} else {
    562 		__u32 hook;
    563 		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
    564 		fprintf(f, " hook: %s \n", ipthooks[hook]);
    565 	}
    566 
    567 	if (tb[TCA_IPT_TARG] == NULL) {
    568 		fprintf(f, "\t[NULL ipt target parameters ] \n");
    569 		return -1;
    570 	} else {
    571 		struct iptables_target *m = NULL;
    572 		t = RTA_DATA(tb[TCA_IPT_TARG]);
    573 		m = get_target_name(t->u.user.name);
    574 		if (NULL != m) {
    575 			if (0 > build_st(m, t)) {
    576 				fprintf(stderr, " %s error \n", m->name);
    577 				return -1;
    578 			}
    579 
    580 			opts =
    581 			    merge_options(opts, m->extra_opts,
    582 					  &m->option_offset);
    583 		} else {
    584 			fprintf(stderr, " failed to find target %s\n\n",
    585 				t->u.user.name);
    586 			return -1;
    587 		}
    588 		fprintf(f, "\ttarget ");
    589 		m->print(NULL, m->t, 0);
    590 		if (tb[TCA_IPT_INDEX] == NULL) {
    591 			fprintf(f, " [NULL ipt target index ]\n");
    592 		} else {
    593 			__u32 index;
    594 			index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
    595 			fprintf(f, " \n\tindex %d", index);
    596 		}
    597 
    598 		if (tb[TCA_IPT_CNT]) {
    599 			struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);;
    600 			fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
    601 		}
    602 		if (show_stats) {
    603 			if (tb[TCA_IPT_TM]) {
    604 				struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
    605 				print_tm(f,tm);
    606 			}
    607 		}
    608 		fprintf(f, " \n");
    609 
    610 	}
    611 	free_opts(opts);
    612 
    613 	return 0;
    614 }
    615 
    616 struct action_util ipt_action_util = {
    617         .id = "ipt",
    618         .parse_aopt = parse_ipt,
    619         .print_aopt = print_ipt,
    620 };
    621 
    622