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