blob: 90163b501aa1c0e67e5015200879591dcd7757cf [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Code to save the iptables state, in human readable-form. */
Harald Welte10a907f2002-08-07 09:07:41 +00002/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
3 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
4 *
5 * This code is distributed under the terms of GNU GPL v2
6 *
Harald Welteae1ff9f2000-12-01 14:26:20 +00007 */
Marc Bouchere6869a82000-03-20 06:03:29 +00008#include <getopt.h>
9#include <sys/errno.h>
10#include <stdio.h>
Harald Welteae1ff9f2000-12-01 14:26:20 +000011#include <fcntl.h>
12#include <stdlib.h>
13#include <string.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000014#include <dlfcn.h>
15#include <time.h>
Rusty Russellb1f69be2000-08-27 07:42:54 +000016#include "libiptc/libiptc.h"
17#include "iptables.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000018
Rusty Russella8f033e2000-07-30 01:43:01 +000019static int binary = 0, counters = 0;
20
Marc Bouchere6869a82000-03-20 06:03:29 +000021static struct option options[] = {
Rusty Russella8f033e2000-07-30 01:43:01 +000022 { "binary", 0, 0, 'b' },
23 { "counters", 0, 0, 'c' },
24 { "dump", 0, 0, 'd' },
25 { "table", 1, 0, 't' },
Marc Bouchere6869a82000-03-20 06:03:29 +000026 { 0 }
27};
28
29#define IP_PARTS_NATIVE(n) \
30(unsigned int)((n)>>24)&0xFF, \
31(unsigned int)((n)>>16)&0xFF, \
32(unsigned int)((n)>>8)&0xFF, \
33(unsigned int)((n)&0xFF)
34
35#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
36
37/* This assumes that mask is contiguous, and byte-bounded. */
38static void
39print_iface(char letter, const char *iface, const unsigned char *mask,
40 int invert)
41{
42 unsigned int i;
43
44 if (mask[0] == 0)
45 return;
Rusty Russell7e53bf92000-03-20 07:03:28 +000046
Marc Bouchere6869a82000-03-20 06:03:29 +000047 printf("-%c %s", letter, invert ? "! " : "");
48
49 for (i = 0; i < IFNAMSIZ; i++) {
50 if (mask[i] != 0) {
51 if (iface[i] != '\0')
52 printf("%c", iface[i]);
53 } else {
Harald Welte9535e682001-11-08 22:28:23 +000054 /* we can access iface[i-1] here, because
55 * a few lines above we make sure that mask[0] != 0 */
56 if (iface[i-1] != '\0')
Marc Bouchere6869a82000-03-20 06:03:29 +000057 printf("+");
58 break;
59 }
60 }
Harald Welteae1ff9f2000-12-01 14:26:20 +000061
62 printf(" ");
Marc Bouchere6869a82000-03-20 06:03:29 +000063}
64
65/* These are hardcoded backups in iptables.c, so they are safe */
66struct pprot {
67 char *name;
68 u_int8_t num;
69};
70
András Kis-Szabó764316a2001-02-26 17:31:20 +000071/* FIXME: why don't we use /etc/protocols ? */
Marc Bouchere6869a82000-03-20 06:03:29 +000072static const struct pprot chain_protos[] = {
73 { "tcp", IPPROTO_TCP },
74 { "udp", IPPROTO_UDP },
75 { "icmp", IPPROTO_ICMP },
András Kis-Szabó764316a2001-02-26 17:31:20 +000076 { "esp", IPPROTO_ESP },
77 { "ah", IPPROTO_AH },
Marc Bouchere6869a82000-03-20 06:03:29 +000078};
79
80static void print_proto(u_int16_t proto, int invert)
81{
82 if (proto) {
83 unsigned int i;
84 const char *invertstr = invert ? "! " : "";
85
86 for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
87 if (chain_protos[i].num == proto) {
88 printf("-p %s%s ",
89 invertstr, chain_protos[i].name);
90 return;
91 }
92
93 printf("-p %s%u ", invertstr, proto);
94 }
95}
96
Harald Welteae1ff9f2000-12-01 14:26:20 +000097#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +000098static int non_zero(const void *ptr, size_t size)
99{
100 unsigned int i;
101
102 for (i = 0; i < size; i++)
103 if (((char *)ptr)[i])
104 return 0;
105
106 return 1;
107}
Harald Welteae1ff9f2000-12-01 14:26:20 +0000108#endif
109
Harald Welte32db5242001-01-23 22:46:22 +0000110static int print_match(const struct ipt_entry_match *e,
111 const struct ipt_ip *ip)
Harald Welteae1ff9f2000-12-01 14:26:20 +0000112{
113 struct iptables_match *match
114 = find_match(e->u.user.name, TRY_LOAD);
115
116 if (match) {
117 printf("-m %s ", e->u.user.name);
Harald Weltee8137792001-01-24 01:32:51 +0000118
119 /* some matches don't provide a save function */
120 if (match->save)
121 match->save(ip, e);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000122 } else {
123 if (e->u.match_size) {
124 fprintf(stderr,
125 "Can't find library for match `%s'\n",
126 e->u.user.name);
127 exit(1);
128 }
129 }
130 return 0;
131}
132
133/* print a given ip including mask if neccessary */
134static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
135{
136 if (!mask && !ip)
137 return;
138
139 printf("%s %s%u.%u.%u.%u",
140 prefix,
141 invert ? "! " : "",
142 IP_PARTS(ip));
143
144 if (mask != 0xffffffff)
145 printf("/%u.%u.%u.%u ", IP_PARTS(mask));
146 else
147 printf(" ");
148}
Marc Bouchere6869a82000-03-20 06:03:29 +0000149
150/* We want this to be readable, so only print out neccessary fields.
151 * Because that's the kind of world I want to live in. */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000152static void print_rule(const struct ipt_entry *e,
Harald Welte9f7fa492001-03-15 15:12:02 +0000153 iptc_handle_t *h, const char *chain, int counters)
Marc Bouchere6869a82000-03-20 06:03:29 +0000154{
Harald Welteae1ff9f2000-12-01 14:26:20 +0000155 struct ipt_entry_target *t;
Harald Welteace8a012001-07-05 06:29:10 +0000156 const char *target_name;
Harald Welteae1ff9f2000-12-01 14:26:20 +0000157
158 /* print counters */
Marc Bouchere6869a82000-03-20 06:03:29 +0000159 if (counters)
Harald Welted8e65632001-01-05 15:20:07 +0000160 printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
Marc Bouchere6869a82000-03-20 06:03:29 +0000161
Harald Welte9f7fa492001-03-15 15:12:02 +0000162 /* print chain name */
163 printf("-A %s ", chain);
164
Marc Bouchere6869a82000-03-20 06:03:29 +0000165 /* Print IP part. */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000166 print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
167 e->ip.invflags & IPT_INV_SRCIP);
168
169 print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
Harald Weltefa7625a2001-01-26 03:28:18 +0000170 e->ip.invflags & IPT_INV_DSTIP);
Marc Bouchere6869a82000-03-20 06:03:29 +0000171
172 print_iface('i', e->ip.iniface, e->ip.iniface_mask,
173 e->ip.invflags & IPT_INV_VIA_IN);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000174
Marc Bouchere6869a82000-03-20 06:03:29 +0000175 print_iface('o', e->ip.outiface, e->ip.outiface_mask,
176 e->ip.invflags & IPT_INV_VIA_OUT);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000177
Marc Bouchere6869a82000-03-20 06:03:29 +0000178 print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
179
180 if (e->ip.flags & IPT_F_FRAG)
181 printf("%s-f ",
182 e->ip.invflags & IPT_INV_FRAG ? "! " : "");
183
Marc Bouchere6869a82000-03-20 06:03:29 +0000184 /* Print matchinfo part */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000185 if (e->target_offset) {
Harald Welte32db5242001-01-23 22:46:22 +0000186 IPT_MATCH_ITERATE(e, print_match, &e->ip);
Marc Bouchere6869a82000-03-20 06:03:29 +0000187 }
188
Harald Welteae1ff9f2000-12-01 14:26:20 +0000189 /* Print target name */
Harald Welte6f83cf02001-05-24 23:22:28 +0000190 target_name = iptc_get_target(e, h);
191 if (target_name && (*target_name != '\0'))
192 printf("-j %s ", target_name);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000193
Marc Bouchere6869a82000-03-20 06:03:29 +0000194 /* Print targinfo part */
Rusty Russell082ba022001-01-07 06:54:51 +0000195 t = ipt_get_target((struct ipt_entry *)e);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000196 if (t->u.user.name[0]) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000197 struct iptables_target *target
Harald Welteae1ff9f2000-12-01 14:26:20 +0000198 = find_target(t->u.user.name, TRY_LOAD);
Marc Bouchere6869a82000-03-20 06:03:29 +0000199
Harald Welte31098ad2001-10-16 09:40:13 +0000200 if (!target) {
201 fprintf(stderr, "Can't find library for target `%s'\n",
202 t->u.user.name);
203 exit(1);
204 }
205
206 if (target->save)
Harald Welte32db5242001-01-23 22:46:22 +0000207 target->save(&e->ip, t);
Marc Bouchere6869a82000-03-20 06:03:29 +0000208 else {
Harald Welte31098ad2001-10-16 09:40:13 +0000209 /* If the target size is greater than ipt_entry_target
210 * there is something to be saved, we just don't know
211 * how to print it */
212 if (t->u.target_size !=
213 sizeof(struct ipt_entry_target)) {
214 fprintf(stderr, "Target `%s' is missing "
215 "save function\n",
Harald Welteae1ff9f2000-12-01 14:26:20 +0000216 t->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000217 exit(1);
218 }
219 }
220 }
221 printf("\n");
222}
223
224/* Debugging prototype. */
Rusty Russella8f033e2000-07-30 01:43:01 +0000225static int for_each_table(int (*func)(const char *tablename))
226{
227 int ret = 1;
Harald Welteae1ff9f2000-12-01 14:26:20 +0000228 FILE *procfile = NULL;
Rusty Russella8f033e2000-07-30 01:43:01 +0000229 char tablename[IPT_TABLE_MAXNAMELEN+1];
230
Harald Welteae1ff9f2000-12-01 14:26:20 +0000231 procfile = fopen("/proc/net/ip_tables_names", "r");
Rusty Russella8f033e2000-07-30 01:43:01 +0000232 if (!procfile)
233 return 0;
234
235 while (fgets(tablename, sizeof(tablename), procfile)) {
236 if (tablename[strlen(tablename) - 1] != '\n')
237 exit_error(OTHER_PROBLEM,
238 "Badly formed tablename `%s'\n",
239 tablename);
240 tablename[strlen(tablename) - 1] = '\0';
241 ret &= func(tablename);
242 }
243
244 return ret;
245}
246
247
Rusty Russella8f033e2000-07-30 01:43:01 +0000248static int do_output(const char *tablename)
249{
250 iptc_handle_t h;
251 const char *chain = NULL;
252
253 if (!tablename)
254 return for_each_table(&do_output);
255
256 h = iptc_init(tablename);
257 if (!h)
258 exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
259 iptc_strerror(errno));
260
261 if (!binary) {
262 time_t now = time(NULL);
263
264 printf("# Generated by iptables-save v%s on %s",
Harald Welte80fe35d2002-05-29 13:08:15 +0000265 IPTABLES_VERSION, ctime(&now));
Harald Welteae1ff9f2000-12-01 14:26:20 +0000266 printf("*%s\n", tablename);
Rusty Russella8f033e2000-07-30 01:43:01 +0000267
Harald Welte9f7fa492001-03-15 15:12:02 +0000268 /* Dump out chain names first,
269 * thereby preventing dependency conflicts */
Rusty Russella8f033e2000-07-30 01:43:01 +0000270 for (chain = iptc_first_chain(&h);
271 chain;
272 chain = iptc_next_chain(&h)) {
Harald Welte9f7fa492001-03-15 15:12:02 +0000273
Rusty Russella8f033e2000-07-30 01:43:01 +0000274 printf(":%s ", chain);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000275 if (iptc_builtin(chain, h)) {
Rusty Russella8f033e2000-07-30 01:43:01 +0000276 struct ipt_counters count;
277 printf("%s ",
278 iptc_get_policy(chain, &count, &h));
Harald Welted8e65632001-01-05 15:20:07 +0000279 printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
Rusty Russella8f033e2000-07-30 01:43:01 +0000280 } else {
Harald Welted8e65632001-01-05 15:20:07 +0000281 printf("- [0:0]\n");
Rusty Russella8f033e2000-07-30 01:43:01 +0000282 }
Harald Welte9f7fa492001-03-15 15:12:02 +0000283 }
284
285
286 for (chain = iptc_first_chain(&h);
287 chain;
288 chain = iptc_next_chain(&h)) {
289 const struct ipt_entry *e;
Rusty Russella8f033e2000-07-30 01:43:01 +0000290
Harald Welteae1ff9f2000-12-01 14:26:20 +0000291 /* Dump out rules */
292 e = iptc_first_rule(chain, &h);
293 while(e) {
Harald Welte9f7fa492001-03-15 15:12:02 +0000294 print_rule(e, &h, chain, counters);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000295 e = iptc_next_rule(e, &h);
Rusty Russella8f033e2000-07-30 01:43:01 +0000296 }
297 }
298
299 now = time(NULL);
300 printf("COMMIT\n");
301 printf("# Completed on %s", ctime(&now));
302 } else {
303 /* Binary, huh? OK. */
304 exit_error(OTHER_PROBLEM, "Binary NYI\n");
305 }
306
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000307 iptc_free(&h);
308
Rusty Russella8f033e2000-07-30 01:43:01 +0000309 return 1;
310}
311
Marc Bouchere6869a82000-03-20 06:03:29 +0000312/* Format:
313 * :Chain name POLICY packets bytes
314 * rule
315 */
316int main(int argc, char *argv[])
317{
Rusty Russella8f033e2000-07-30 01:43:01 +0000318 const char *tablename = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000319 int c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000320
321 program_name = "iptables-save";
Harald Welte80fe35d2002-05-29 13:08:15 +0000322 program_version = IPTABLES_VERSION;
Marc Bouchere6869a82000-03-20 06:03:29 +0000323
Harald Welte3efb6ea2001-08-06 18:50:21 +0000324#ifdef NO_SHARED_LIBS
325 init_extensions();
326#endif
327
Marc Boucher163ad782001-12-06 15:05:48 +0000328 while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000329 switch (c) {
330 case 'b':
331 binary = 1;
332 break;
333
334 case 'c':
335 counters = 1;
336 break;
337
Rusty Russella8f033e2000-07-30 01:43:01 +0000338 case 't':
339 /* Select specific table. */
340 tablename = optarg;
341 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000342 case 'd':
Harald Welteae1ff9f2000-12-01 14:26:20 +0000343 do_output(tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000344 exit(0);
345 }
346 }
347
348 if (optind < argc) {
349 fprintf(stderr, "Unknown arguments found on commandline");
350 exit(1);
351 }
352
Rusty Russella8f033e2000-07-30 01:43:01 +0000353 return !do_output(tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000354}