Home | History | Annotate | Download | only in tc
      1 /*
      2  * m_xt.c	xtables 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 <net/if.h>
     18 #include <limits.h>
     19 #include <linux/netfilter.h>
     20 #include <linux/netfilter_ipv4/ip_tables.h>
     21 #include <xtables.h>
     22 #include "utils.h"
     23 #include "tc_util.h"
     24 #include <linux/tc_act/tc_ipt.h>
     25 #include <stdio.h>
     26 #include <dlfcn.h>
     27 #include <getopt.h>
     28 #include <errno.h>
     29 #include <string.h>
     30 #include <netdb.h>
     31 #include <stdlib.h>
     32 #include <ctype.h>
     33 #include <stdarg.h>
     34 #include <unistd.h>
     35 #include <fcntl.h>
     36 #include <sys/wait.h>
     37 #ifndef XT_LIB_DIR
     38 #       define XT_LIB_DIR "/lib/xtables"
     39 #endif
     40 
     41 #ifndef __ALIGN_KERNEL
     42 #define __ALIGN_KERNEL(x, a)	\
     43 	__ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
     44 #define __ALIGN_KERNEL_MASK(x, mask) \
     45 	(((x) + (mask)) & ~(mask))
     46 #endif
     47 
     48 #ifndef ALIGN
     49 #define ALIGN(x, a)	__ALIGN_KERNEL((x), (a))
     50 #endif
     51 
     52 static const char *tname = "mangle";
     53 
     54 char *lib_dir;
     55 
     56 static const char * const ipthooks[] = {
     57 	"NF_IP_PRE_ROUTING",
     58 	"NF_IP_LOCAL_IN",
     59 	"NF_IP_FORWARD",
     60 	"NF_IP_LOCAL_OUT",
     61 	"NF_IP_POST_ROUTING",
     62 };
     63 
     64 static struct option original_opts[] = {
     65 	{
     66 		.name = "jump",
     67 		.has_arg = 1,
     68 		.val = 'j'
     69 	},
     70 	{0, 0, 0, 0}
     71 };
     72 
     73 static struct xtables_globals tcipt_globals = {
     74 	.option_offset = 0,
     75 	.program_name = "tc-ipt",
     76 	.program_version = "0.2",
     77 	.orig_opts = original_opts,
     78 	.opts = original_opts,
     79 	.exit_err = NULL,
     80 #if XTABLES_VERSION_CODE >= 11
     81 	.compat_rev = xtables_compatible_revision,
     82 #endif
     83 };
     84 
     85 /*
     86  * we may need to check for version mismatch
     87 */
     88 static int
     89 build_st(struct xtables_target *target, struct xt_entry_target *t)
     90 {
     91 
     92 	size_t size =
     93 		    XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
     94 
     95 	if (t == NULL) {
     96 		target->t = xtables_calloc(1, size);
     97 		target->t->u.target_size = size;
     98 		strncpy(target->t->u.user.name, target->name,
     99 			sizeof(target->t->u.user.name) - 1);
    100 		target->t->u.user.revision = target->revision;
    101 
    102 		if (target->init != NULL)
    103 			target->init(target->t);
    104 	} else {
    105 		target->t = t;
    106 	}
    107 	return 0;
    108 
    109 }
    110 
    111 static void set_lib_dir(void)
    112 {
    113 
    114 	lib_dir = getenv("XTABLES_LIBDIR");
    115 	if (!lib_dir) {
    116 		lib_dir = getenv("IPTABLES_LIB_DIR");
    117 		if (lib_dir)
    118 			fprintf(stderr, "using deprecated IPTABLES_LIB_DIR\n");
    119 	}
    120 	if (lib_dir == NULL)
    121 		lib_dir = XT_LIB_DIR;
    122 
    123 }
    124 
    125 static int get_xtables_target_opts(struct xtables_globals *globals,
    126 				   struct xtables_target *m)
    127 {
    128 	struct option *opts;
    129 
    130 #if XTABLES_VERSION_CODE >= 6
    131 	opts = xtables_options_xfrm(globals->orig_opts,
    132 				    globals->opts,
    133 				    m->x6_options,
    134 				    &m->option_offset);
    135 #else
    136 	opts = xtables_merge_options(globals->opts,
    137 				     m->extra_opts,
    138 				     &m->option_offset);
    139 #endif
    140 	if (!opts)
    141 		return -1;
    142 	globals->opts = opts;
    143 	return 0;
    144 }
    145 
    146 static int parse_ipt(struct action_util *a, int *argc_p,
    147 		     char ***argv_p, int tca_id, struct nlmsghdr *n)
    148 {
    149 	struct xtables_target *m = NULL;
    150 #if XTABLES_VERSION_CODE >= 6
    151 	struct ipt_entry fw = {};
    152 #endif
    153 	struct rtattr *tail;
    154 
    155 	int c;
    156 	char **argv = *argv_p;
    157 	int argc;
    158 	char k[16];
    159 	int size = 0;
    160 	int iok = 0, ok = 0;
    161 	__u32 hook = 0, index = 0;
    162 
    163 	/* copy tcipt_globals because .opts will be modified by iptables */
    164 	struct xtables_globals tmp_tcipt_globals = tcipt_globals;
    165 
    166 	xtables_init_all(&tmp_tcipt_globals, NFPROTO_IPV4);
    167 	set_lib_dir();
    168 
    169 	/* parse only up until the next action */
    170 	for (argc = 0; argc < *argc_p; argc++) {
    171 		if (!argv[argc] || !strcmp(argv[argc], "action"))
    172 			break;
    173 	}
    174 
    175 	if (argc <= 2) {
    176 		fprintf(stderr,
    177 			"too few arguments for xt, need at least '-j <target>'\n");
    178 		return -1;
    179 	}
    180 
    181 	while (1) {
    182 		c = getopt_long(argc, argv, "j:", tmp_tcipt_globals.opts, NULL);
    183 		if (c == -1)
    184 			break;
    185 		switch (c) {
    186 		case 'j':
    187 			m = xtables_find_target(optarg, XTF_TRY_LOAD);
    188 			if (!m) {
    189 				fprintf(stderr,
    190 					" failed to find target %s\n\n",
    191 					optarg);
    192 				return -1;
    193 			}
    194 
    195 			if (build_st(m, NULL) < 0) {
    196 				printf(" %s error\n", m->name);
    197 				return -1;
    198 			}
    199 
    200 			if (get_xtables_target_opts(&tmp_tcipt_globals,
    201 						    m) < 0) {
    202 				fprintf(stderr,
    203 					" failed to find additional options for target %s\n\n",
    204 					optarg);
    205 				return -1;
    206 			}
    207 			ok++;
    208 			break;
    209 
    210 		default:
    211 #if XTABLES_VERSION_CODE >= 6
    212 			if (m != NULL && m->x6_parse != NULL) {
    213 				xtables_option_tpcall(c, argv, 0, m, &fw);
    214 #else
    215 			if (m != NULL && m->parse != NULL) {
    216 				m->parse(c - m->option_offset, argv, 0,
    217 					 &m->tflags, NULL, &m->t);
    218 #endif
    219 			} else {
    220 				fprintf(stderr,
    221 					"failed to find target %s\n\n", optarg);
    222 				return -1;
    223 
    224 			}
    225 			ok++;
    226 			break;
    227 		}
    228 	}
    229 
    230 	if (argc > optind) {
    231 		if (matches(argv[optind], "index") == 0) {
    232 			if (get_u32(&index, argv[optind + 1], 10)) {
    233 				fprintf(stderr, "Illegal \"index\"\n");
    234 				xtables_free_opts(1);
    235 				return -1;
    236 			}
    237 			iok++;
    238 
    239 			optind += 2;
    240 		}
    241 	}
    242 
    243 	if (!ok && !iok) {
    244 		fprintf(stderr, " ipt Parser BAD!! (%s)\n", *argv);
    245 		return -1;
    246 	}
    247 
    248 	/* check that we passed the correct parameters to the target */
    249 #if XTABLES_VERSION_CODE >= 6
    250 	if (m)
    251 		xtables_option_tfcall(m);
    252 #else
    253 	if (m && m->final_check)
    254 		m->final_check(m->tflags);
    255 #endif
    256 
    257 	{
    258 		struct tcmsg *t = NLMSG_DATA(n);
    259 
    260 		if (t->tcm_parent != TC_H_ROOT
    261 		    && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
    262 			hook = NF_IP_PRE_ROUTING;
    263 		} else {
    264 			hook = NF_IP_POST_ROUTING;
    265 		}
    266 	}
    267 
    268 	tail = NLMSG_TAIL(n);
    269 	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
    270 	fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
    271 	fprintf(stdout, "\ttarget: ");
    272 
    273 	if (m) {
    274 		if (m->print)
    275 			m->print(NULL, m->t, 0);
    276 		else
    277 			printf("%s ", m->name);
    278 	}
    279 	fprintf(stdout, " index %d\n", index);
    280 
    281 	if (strlen(tname) >= 16) {
    282 		size = 15;
    283 		k[15] = 0;
    284 	} else {
    285 		size = 1 + strlen(tname);
    286 	}
    287 	strncpy(k, tname, size);
    288 
    289 	addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
    290 	addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
    291 	addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
    292 	if (m)
    293 		addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
    294 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    295 
    296 	argv += optind;
    297 	*argc_p -= argc;
    298 	*argv_p = argv;
    299 
    300 	optind = 0;
    301 	xtables_free_opts(1);
    302 
    303 	if (m) {
    304 		/* Clear flags if target will be used again */
    305 		m->tflags = 0;
    306 		m->used = 0;
    307 		/* Free allocated memory */
    308 		if (m->t)
    309 			free(m->t);
    310 	}
    311 
    312 	return 0;
    313 
    314 }
    315 
    316 static int
    317 print_ipt(struct action_util *au, FILE *f, struct rtattr *arg)
    318 {
    319 	struct xtables_target *m;
    320 	struct rtattr *tb[TCA_IPT_MAX + 1];
    321 	struct xt_entry_target *t = NULL;
    322 
    323 	if (arg == NULL)
    324 		return -1;
    325 
    326 	/* copy tcipt_globals because .opts will be modified by iptables */
    327 	struct xtables_globals tmp_tcipt_globals = tcipt_globals;
    328 
    329 	xtables_init_all(&tmp_tcipt_globals, NFPROTO_IPV4);
    330 	set_lib_dir();
    331 
    332 	parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
    333 
    334 	if (tb[TCA_IPT_TABLE] == NULL) {
    335 		fprintf(f, "[NULL ipt table name ] assuming mangle ");
    336 	} else {
    337 		fprintf(f, "tablename: %s ",
    338 			rta_getattr_str(tb[TCA_IPT_TABLE]));
    339 	}
    340 
    341 	if (tb[TCA_IPT_HOOK] == NULL) {
    342 		fprintf(f, "[NULL ipt hook name ]\n ");
    343 		return -1;
    344 	} else {
    345 		__u32 hook;
    346 
    347 		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
    348 		fprintf(f, " hook: %s\n", ipthooks[hook]);
    349 	}
    350 
    351 	if (tb[TCA_IPT_TARG] == NULL) {
    352 		fprintf(f, "\t[NULL ipt target parameters ]\n");
    353 		return -1;
    354 	}
    355 
    356 	t = RTA_DATA(tb[TCA_IPT_TARG]);
    357 	m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
    358 	if (!m) {
    359 		fprintf(stderr, " failed to find target %s\n\n",
    360 			t->u.user.name);
    361 		return -1;
    362 	}
    363 	if (build_st(m, t) < 0) {
    364 		fprintf(stderr, " %s error\n", m->name);
    365 		return -1;
    366 	}
    367 
    368 	if (get_xtables_target_opts(&tmp_tcipt_globals, m) < 0) {
    369 		fprintf(stderr,
    370 			" failed to find additional options for target %s\n\n",
    371 			t->u.user.name);
    372 		return -1;
    373 	}
    374 	fprintf(f, "\ttarget ");
    375 	m->print(NULL, m->t, 0);
    376 	if (tb[TCA_IPT_INDEX] == NULL) {
    377 		fprintf(f, " [NULL ipt target index ]\n");
    378 	} else {
    379 		__u32 index;
    380 
    381 		index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
    382 		fprintf(f, "\n\tindex %u", index);
    383 	}
    384 
    385 	if (tb[TCA_IPT_CNT]) {
    386 		struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);
    387 
    388 		fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
    389 	}
    390 	if (show_stats) {
    391 		if (tb[TCA_IPT_TM]) {
    392 			struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
    393 
    394 			print_tm(f, tm);
    395 		}
    396 	}
    397 	fprintf(f, "\n");
    398 
    399 	xtables_free_opts(1);
    400 
    401 	return 0;
    402 }
    403 
    404 struct action_util xt_action_util = {
    405 	.id = "xt",
    406 	.parse_aopt = parse_ipt,
    407 	.print_aopt = print_ipt,
    408 };
    409