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