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