m_ipt.c revision 1ffd7fd23eeaff57eb3b83b9dbbbda89ddf030e3
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * m_ipt.c	iptables based targets
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 		utilities mostly ripped from iptables <duh, its the linux way>
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *		This program is free software; you can distribute it and/or
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *		modify it under the terms of the GNU General Public License
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *		as published by the Free Software Foundation; either version
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *		2 of the License, or (at your option) any later version.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Authors:  J Hadi Salim (hadi@cyberus.ca)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * TODO: bad bad hardcoding IPT_LIB_DIR and PROC_SYS_MODPROBE
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <syslog.h>
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/socket.h>
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <netinet/in.h>
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <arpa/inet.h>
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <iptables.h>
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <linux/netfilter_ipv4/ip_tables.h>
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "utils.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "tc_util.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <linux/tc_act/tc_ipt.h>
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <dlfcn.h>
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <getopt.h>
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <errno.h>
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h>
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <netdb.h>
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <ctype.h>
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdarg.h>
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <limits.h>
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <unistd.h>
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <fcntl.h>
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/wait.h>
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char *pname = "tc-ipt";
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char *tname = "mangle";
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char *pversion = "0.1";
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef TRUE
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define TRUE 1
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef FALSE
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define FALSE 0
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef IPT_LIB_DIR
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define IPT_LIB_DIR "/usr/local/lib/iptables"
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef PROC_SYS_MODPROBE
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char *ipthooks[] = {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	"NF_IP_PRE_ROUTING",
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	"NF_IP_LOCAL_IN",
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	"NF_IP_FORWARD",
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	"NF_IP_LOCAL_OUT",
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	"NF_IP_POST_ROUTING",
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct option original_opts[] = {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	{"jump", 1, 0, 'j'},
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	{0, 0, 0, 0}
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct iptables_target *t_list = NULL;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static unsigned int global_option_offset = 0;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define OPTION_OFFSET 256
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if 0
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* 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 = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len));
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 =
573	    (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)) - (void *) tail;
574
575	argc -= optind;
576	argv += optind;
577	*argc_p = rargc - iargc;
578	*argv_p = argv;
579
580	optind = 1;
581
582	return 0;
583
584}
585
586static int
587print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
588{
589	struct rtattr *tb[TCA_IPT_MAX + 1];
590	struct ipt_entry_target *t = NULL;
591	struct option *opts;
592
593	if (arg == NULL)
594		return -1;
595
596	opts = copy_options(original_opts);
597
598	if (NULL == opts)
599		return -1;
600	memset(tb, 0, sizeof (tb));
601	parse_rtattr(tb, TCA_IPT_MAX, RTA_DATA(arg), RTA_PAYLOAD(arg));
602
603	if (tb[TCA_IPT_TABLE] == NULL) {
604		fprintf(f, "[NULL ipt table name ] assuming mangle ");
605	} else {
606		fprintf(f, "tablename: %s ",
607			(char *) RTA_DATA(tb[TCA_IPT_TABLE]));
608	}
609
610	if (tb[TCA_IPT_HOOK] == NULL) {
611		fprintf(f, "[NULL ipt hook name ]\n ");
612		return -1;
613	} else {
614		__u32 hook;
615		hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
616		fprintf(f, " hook: %s \n", ipthooks[hook]);
617	}
618
619	if (tb[TCA_IPT_TARG] == NULL) {
620		fprintf(f, "\t[NULL ipt target parameters ] \n");
621		return -1;
622	} else {
623		struct iptables_target *m = NULL;
624		t = RTA_DATA(tb[TCA_IPT_TARG]);
625		m = get_target_name(t->u.user.name);
626		if (NULL != m) {
627			if (0 > build_st(m, t)) {
628				fprintf(stderr, " %s error \n", m->name);
629				return -1;
630			}
631
632			opts =
633			    merge_options(opts, m->extra_opts,
634					  &m->option_offset);
635		} else {
636			fprintf(stderr, " failed to find target %s\n\n",
637				t->u.user.name);
638			return -1;
639		}
640		fprintf(f, "\ttarget ");
641		m->print(NULL, m->t, 0);
642		if (tb[TCA_IPT_INDEX] == NULL) {
643			fprintf(f, " [NULL ipt target index ]\n");
644		} else {
645			__u32 index;
646			index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
647			fprintf(f, " \n\tindex %d", index);
648		}
649
650		if (tb[TCA_IPT_CNT]) {
651			struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);;
652			fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
653		}
654		if (show_stats) {
655			if (tb[TCA_IPT_TM]) {
656				struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
657				print_tm(f,tm);
658			}
659		}
660		fprintf(f, " \n");
661
662	}
663
664	return 0;
665}
666
667struct action_util ipt_action_util = {
668        .id = "ipt",
669        .parse_aopt = parse_ipt,
670        .print_aopt = print_ipt,
671};
672
673