• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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
xtables_register_target(struct xtables_target * me)61 xtables_register_target(struct xtables_target *me)
62 {
63 	me->next = t_list;
64 	t_list = me;
65 
66 }
67 
exit_tryhelp(int status)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 
exit_error(enum xtables_exittype status,char * msg,...)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 
free_opts(struct option * local_opts)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 *
merge_options(struct option * oldopts,const struct option * newopts,unsigned int * option_offset)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 *
fw_calloc(size_t count,size_t size)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 *
find_t(char * name)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 *
get_target_name(const char * name)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 
set_revision(char * name,u_int8_t revision)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 */
build_st(struct xtables_target * target,struct ipt_entry_target * t)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 
parse_ipt(struct action_util * a,int * argc_p,char *** argv_p,int tca_id,struct nlmsghdr * n)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
print_ipt(struct action_util * au,FILE * f,struct rtattr * arg)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