Home | History | Annotate | Download | only in iptables
      1 /* Code to save the iptables state, in human readable-form. */
      2 /* (C) 1999 by Paul 'Rusty' Russell <rusty (at) rustcorp.com.au> and
      3  * (C) 2000-2002 by Harald Welte <laforge (at) gnumonks.org>
      4  *
      5  * This code is distributed under the terms of GNU GPL v2
      6  *
      7  */
      8 #include <getopt.h>
      9 #include <sys/errno.h>
     10 #include <stdio.h>
     11 #include <fcntl.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <dlfcn.h>
     15 #include <time.h>
     16 #include <netdb.h>
     17 #include "libiptc/libiptc.h"
     18 #include "iptables.h"
     19 
     20 static int binary = 0, counters = 0;
     21 
     22 static struct option options[] = {
     23 	{ "binary", 0, 0, 'b' },
     24 	{ "counters", 0, 0, 'c' },
     25 	{ "dump", 0, 0, 'd' },
     26 	{ "table", 1, 0, 't' },
     27 	{ 0 }
     28 };
     29 
     30 #define IP_PARTS_NATIVE(n)			\
     31 (unsigned int)((n)>>24)&0xFF,			\
     32 (unsigned int)((n)>>16)&0xFF,			\
     33 (unsigned int)((n)>>8)&0xFF,			\
     34 (unsigned int)((n)&0xFF)
     35 
     36 #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
     37 
     38 /* This assumes that mask is contiguous, and byte-bounded. */
     39 static void
     40 print_iface(char letter, const char *iface, const unsigned char *mask,
     41 	    int invert)
     42 {
     43 	unsigned int i;
     44 
     45 	if (mask[0] == 0)
     46 		return;
     47 
     48 	printf("-%c %s", letter, invert ? "! " : "");
     49 
     50 	for (i = 0; i < IFNAMSIZ; i++) {
     51 		if (mask[i] != 0) {
     52 			if (iface[i] != '\0')
     53 				printf("%c", iface[i]);
     54 		} else {
     55 			/* we can access iface[i-1] here, because
     56 			 * a few lines above we make sure that mask[0] != 0 */
     57 			if (iface[i-1] != '\0')
     58 				printf("+");
     59 			break;
     60 		}
     61 	}
     62 
     63 	printf(" ");
     64 }
     65 
     66 /* These are hardcoded backups in iptables.c, so they are safe */
     67 struct pprot {
     68 	char *name;
     69 	u_int8_t num;
     70 };
     71 
     72 /* FIXME: why don't we use /etc/protocols ? */
     73 static const struct pprot chain_protos[] = {
     74 	{ "tcp", IPPROTO_TCP },
     75 	{ "udp", IPPROTO_UDP },
     76 	{ "icmp", IPPROTO_ICMP },
     77 	{ "esp", IPPROTO_ESP },
     78 	{ "ah", IPPROTO_AH },
     79 	{ "sctp", IPPROTO_SCTP },
     80 };
     81 
     82 static void print_proto(u_int16_t proto, int invert)
     83 {
     84 	if (proto) {
     85 		unsigned int i;
     86 		const char *invertstr = invert ? "! " : "";
     87 
     88 		struct protoent *pent = getprotobynumber(proto);
     89 		if (pent) {
     90 			printf("-p %s%s ", invertstr, pent->p_name);
     91 			return;
     92 		}
     93 
     94 		for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
     95 			if (chain_protos[i].num == proto) {
     96 				printf("-p %s%s ",
     97 				       invertstr, chain_protos[i].name);
     98 				return;
     99 			}
    100 
    101 		printf("-p %s%u ", invertstr, proto);
    102 	}
    103 }
    104 
    105 #if 0
    106 static int non_zero(const void *ptr, size_t size)
    107 {
    108 	unsigned int i;
    109 
    110 	for (i = 0; i < size; i++)
    111 		if (((char *)ptr)[i])
    112 			return 0;
    113 
    114 	return 1;
    115 }
    116 #endif
    117 
    118 static int print_match(const struct ipt_entry_match *e,
    119 			const struct ipt_ip *ip)
    120 {
    121 	struct iptables_match *match
    122 		= find_match(e->u.user.name, TRY_LOAD, NULL);
    123 
    124 	if (match) {
    125 		printf("-m %s ", e->u.user.name);
    126 
    127 		/* some matches don't provide a save function */
    128 		if (match->save)
    129 			match->save(ip, e);
    130 	} else {
    131 		if (e->u.match_size) {
    132 			fprintf(stderr,
    133 				"Can't find library for match `%s'\n",
    134 				e->u.user.name);
    135 			exit(1);
    136 		}
    137 	}
    138 	return 0;
    139 }
    140 
    141 /* print a given ip including mask if neccessary */
    142 static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
    143 {
    144 	if (!mask && !ip && !invert)
    145 		return;
    146 
    147 	printf("%s %s%u.%u.%u.%u",
    148 		prefix,
    149 		invert ? "! " : "",
    150 		IP_PARTS(ip));
    151 
    152 	if (mask != 0xffffffff)
    153 		printf("/%u.%u.%u.%u ", IP_PARTS(mask));
    154 	else
    155 		printf(" ");
    156 }
    157 
    158 /* We want this to be readable, so only print out neccessary fields.
    159  * Because that's the kind of world I want to live in.  */
    160 static void print_rule(const struct ipt_entry *e,
    161 		iptc_handle_t *h, const char *chain, int counters)
    162 {
    163 	struct ipt_entry_target *t;
    164 	const char *target_name;
    165 
    166 	/* print counters */
    167 	if (counters)
    168 		printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
    169 
    170 	/* print chain name */
    171 	printf("-A %s ", chain);
    172 
    173 	/* Print IP part. */
    174 	print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
    175 			e->ip.invflags & IPT_INV_SRCIP);
    176 
    177 	print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
    178 			e->ip.invflags & IPT_INV_DSTIP);
    179 
    180 	print_iface('i', e->ip.iniface, e->ip.iniface_mask,
    181 		    e->ip.invflags & IPT_INV_VIA_IN);
    182 
    183 	print_iface('o', e->ip.outiface, e->ip.outiface_mask,
    184 		    e->ip.invflags & IPT_INV_VIA_OUT);
    185 
    186 	print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
    187 
    188 	if (e->ip.flags & IPT_F_FRAG)
    189 		printf("%s-f ",
    190 		       e->ip.invflags & IPT_INV_FRAG ? "! " : "");
    191 
    192 	/* Print matchinfo part */
    193 	if (e->target_offset) {
    194 		IPT_MATCH_ITERATE(e, print_match, &e->ip);
    195 	}
    196 
    197 	/* Print target name */
    198 	target_name = iptc_get_target(e, h);
    199 	if (target_name && (*target_name != '\0'))
    200 #ifdef IPT_F_GOTO
    201 		printf("-%c %s ", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
    202 #else
    203 		printf("-j %s ", target_name);
    204 #endif
    205 
    206 	/* Print targinfo part */
    207 	t = ipt_get_target((struct ipt_entry *)e);
    208 	if (t->u.user.name[0]) {
    209 		struct iptables_target *target
    210 			= find_target(t->u.user.name, TRY_LOAD);
    211 
    212 		if (!target) {
    213 			fprintf(stderr, "Can't find library for target `%s'\n",
    214 				t->u.user.name);
    215 			exit(1);
    216 		}
    217 
    218 		if (target->save)
    219 			target->save(&e->ip, t);
    220 		else {
    221 			/* If the target size is greater than ipt_entry_target
    222 			 * there is something to be saved, we just don't know
    223 			 * how to print it */
    224 			if (t->u.target_size !=
    225 			    sizeof(struct ipt_entry_target)) {
    226 				fprintf(stderr, "Target `%s' is missing "
    227 						"save function\n",
    228 					t->u.user.name);
    229 				exit(1);
    230 			}
    231 		}
    232 	}
    233 	printf("\n");
    234 }
    235 
    236 /* Debugging prototype. */
    237 static int for_each_table(int (*func)(const char *tablename))
    238 {
    239         int ret = 1;
    240 	FILE *procfile = NULL;
    241 	char tablename[IPT_TABLE_MAXNAMELEN+1];
    242 
    243 	procfile = fopen("/proc/net/ip_tables_names", "r");
    244 	if (!procfile)
    245 		return 0;
    246 
    247 	while (fgets(tablename, sizeof(tablename), procfile)) {
    248 		if (tablename[strlen(tablename) - 1] != '\n')
    249 			exit_error(OTHER_PROBLEM,
    250 				   "Badly formed tablename `%s'\n",
    251 				   tablename);
    252 		tablename[strlen(tablename) - 1] = '\0';
    253 		ret &= func(tablename);
    254 	}
    255 
    256 	return ret;
    257 }
    258 
    259 
    260 static int do_output(const char *tablename)
    261 {
    262 	iptc_handle_t h;
    263 	const char *chain = NULL;
    264 
    265 	if (!tablename)
    266 		return for_each_table(&do_output);
    267 
    268 	h = iptc_init(tablename);
    269 	if (!h)
    270  		exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
    271 			   iptc_strerror(errno));
    272 
    273 	if (!binary) {
    274 		time_t now = time(NULL);
    275 
    276 		printf("# Generated by iptables-save v%s on %s",
    277 		       IPTABLES_VERSION, ctime(&now));
    278 		printf("*%s\n", tablename);
    279 
    280 		/* Dump out chain names first,
    281 		 * thereby preventing dependency conflicts */
    282 		for (chain = iptc_first_chain(&h);
    283 		     chain;
    284 		     chain = iptc_next_chain(&h)) {
    285 
    286 			printf(":%s ", chain);
    287 			if (iptc_builtin(chain, h)) {
    288 				struct ipt_counters count;
    289 				printf("%s ",
    290 				       iptc_get_policy(chain, &count, &h));
    291 				printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
    292 			} else {
    293 				printf("- [0:0]\n");
    294 			}
    295 		}
    296 
    297 
    298 		for (chain = iptc_first_chain(&h);
    299 		     chain;
    300 		     chain = iptc_next_chain(&h)) {
    301 			const struct ipt_entry *e;
    302 
    303 			/* Dump out rules */
    304 			e = iptc_first_rule(chain, &h);
    305 			while(e) {
    306 				print_rule(e, &h, chain, counters);
    307 				e = iptc_next_rule(e, &h);
    308 			}
    309 		}
    310 
    311 		now = time(NULL);
    312 		printf("COMMIT\n");
    313 		printf("# Completed on %s", ctime(&now));
    314 	} else {
    315 		/* Binary, huh?  OK. */
    316 		exit_error(OTHER_PROBLEM, "Binary NYI\n");
    317 	}
    318 
    319 	iptc_free(&h);
    320 
    321 	return 1;
    322 }
    323 
    324 /* Format:
    325  * :Chain name POLICY packets bytes
    326  * rule
    327  */
    328 #ifdef IPTABLES_MULTI
    329 int
    330 iptables_save_main(int argc, char *argv[])
    331 #else
    332 int
    333 main(int argc, char *argv[])
    334 #endif
    335 {
    336 	const char *tablename = NULL;
    337 	int c;
    338 
    339 	program_name = "iptables-save";
    340 	program_version = IPTABLES_VERSION;
    341 
    342 	lib_dir = getenv("IPTABLES_LIB_DIR");
    343 	if (!lib_dir)
    344 		lib_dir = IPT_LIB_DIR;
    345 
    346 #ifdef NO_SHARED_LIBS
    347 	init_extensions();
    348 #endif
    349 
    350 	while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
    351 		switch (c) {
    352 		case 'b':
    353 			binary = 1;
    354 			break;
    355 
    356 		case 'c':
    357 			counters = 1;
    358 			break;
    359 
    360 		case 't':
    361 			/* Select specific table. */
    362 			tablename = optarg;
    363 			break;
    364 		case 'd':
    365 			do_output(tablename);
    366 			exit(0);
    367 		}
    368 	}
    369 
    370 	if (optind < argc) {
    371 		fprintf(stderr, "Unknown arguments found on commandline");
    372 		exit(1);
    373 	}
    374 
    375 	return !do_output(tablename);
    376 }
    377