blob: e57cd7ccb45fcec38abe54bec3d388e4edf3613c [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Code to save the iptables state, in human readable-form. */
Rusty Russell6df83682001-07-10 13:41:49 +00002/* Authors: Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
Harald Welteae1ff9f2000-12-01 14:26:20 +00003 * Harald Welte <laforge@gnumonks.org>
4 */
Marc Bouchere6869a82000-03-20 06:03:29 +00005#include <getopt.h>
6#include <sys/errno.h>
7#include <stdio.h>
Harald Welteae1ff9f2000-12-01 14:26:20 +00008#include <fcntl.h>
9#include <stdlib.h>
10#include <string.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000011#include <dlfcn.h>
12#include <time.h>
Rusty Russellb1f69be2000-08-27 07:42:54 +000013#include "libiptc/libiptc.h"
14#include "iptables.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000015
Rusty Russella8f033e2000-07-30 01:43:01 +000016static int binary = 0, counters = 0;
17
Marc Bouchere6869a82000-03-20 06:03:29 +000018static struct option options[] = {
Rusty Russella8f033e2000-07-30 01:43:01 +000019 { "binary", 0, 0, 'b' },
20 { "counters", 0, 0, 'c' },
21 { "dump", 0, 0, 'd' },
22 { "table", 1, 0, 't' },
Marc Bouchere6869a82000-03-20 06:03:29 +000023 { 0 }
24};
25
26#define IP_PARTS_NATIVE(n) \
27(unsigned int)((n)>>24)&0xFF, \
28(unsigned int)((n)>>16)&0xFF, \
29(unsigned int)((n)>>8)&0xFF, \
30(unsigned int)((n)&0xFF)
31
32#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
33
34/* This assumes that mask is contiguous, and byte-bounded. */
35static void
36print_iface(char letter, const char *iface, const unsigned char *mask,
37 int invert)
38{
39 unsigned int i;
40
41 if (mask[0] == 0)
42 return;
Rusty Russell7e53bf92000-03-20 07:03:28 +000043
Marc Bouchere6869a82000-03-20 06:03:29 +000044 printf("-%c %s", letter, invert ? "! " : "");
45
46 for (i = 0; i < IFNAMSIZ; i++) {
47 if (mask[i] != 0) {
48 if (iface[i] != '\0')
49 printf("%c", iface[i]);
50 } else {
Harald Welte9535e682001-11-08 22:28:23 +000051 /* we can access iface[i-1] here, because
52 * a few lines above we make sure that mask[0] != 0 */
53 if (iface[i-1] != '\0')
Marc Bouchere6869a82000-03-20 06:03:29 +000054 printf("+");
55 break;
56 }
57 }
Harald Welteae1ff9f2000-12-01 14:26:20 +000058
59 printf(" ");
Marc Bouchere6869a82000-03-20 06:03:29 +000060}
61
62/* These are hardcoded backups in iptables.c, so they are safe */
63struct pprot {
64 char *name;
65 u_int8_t num;
66};
67
András Kis-Szabó764316a2001-02-26 17:31:20 +000068/* FIXME: why don't we use /etc/protocols ? */
Marc Bouchere6869a82000-03-20 06:03:29 +000069static const struct pprot chain_protos[] = {
70 { "tcp", IPPROTO_TCP },
71 { "udp", IPPROTO_UDP },
72 { "icmp", IPPROTO_ICMP },
András Kis-Szabó764316a2001-02-26 17:31:20 +000073 { "esp", IPPROTO_ESP },
74 { "ah", IPPROTO_AH },
Marc Bouchere6869a82000-03-20 06:03:29 +000075};
76
77static void print_proto(u_int16_t proto, int invert)
78{
79 if (proto) {
80 unsigned int i;
81 const char *invertstr = invert ? "! " : "";
82
83 for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
84 if (chain_protos[i].num == proto) {
85 printf("-p %s%s ",
86 invertstr, chain_protos[i].name);
87 return;
88 }
89
90 printf("-p %s%u ", invertstr, proto);
91 }
92}
93
Harald Welteae1ff9f2000-12-01 14:26:20 +000094#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +000095static int non_zero(const void *ptr, size_t size)
96{
97 unsigned int i;
98
99 for (i = 0; i < size; i++)
100 if (((char *)ptr)[i])
101 return 0;
102
103 return 1;
104}
Harald Welteae1ff9f2000-12-01 14:26:20 +0000105#endif
106
Harald Welte32db5242001-01-23 22:46:22 +0000107static int print_match(const struct ipt_entry_match *e,
108 const struct ipt_ip *ip)
Harald Welteae1ff9f2000-12-01 14:26:20 +0000109{
110 struct iptables_match *match
111 = find_match(e->u.user.name, TRY_LOAD);
112
113 if (match) {
114 printf("-m %s ", e->u.user.name);
Harald Weltee8137792001-01-24 01:32:51 +0000115
116 /* some matches don't provide a save function */
117 if (match->save)
118 match->save(ip, e);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000119 } else {
120 if (e->u.match_size) {
121 fprintf(stderr,
122 "Can't find library for match `%s'\n",
123 e->u.user.name);
124 exit(1);
125 }
126 }
127 return 0;
128}
129
130/* print a given ip including mask if neccessary */
131static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
132{
133 if (!mask && !ip)
134 return;
135
136 printf("%s %s%u.%u.%u.%u",
137 prefix,
138 invert ? "! " : "",
139 IP_PARTS(ip));
140
141 if (mask != 0xffffffff)
142 printf("/%u.%u.%u.%u ", IP_PARTS(mask));
143 else
144 printf(" ");
145}
Marc Bouchere6869a82000-03-20 06:03:29 +0000146
147/* We want this to be readable, so only print out neccessary fields.
148 * Because that's the kind of world I want to live in. */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000149static void print_rule(const struct ipt_entry *e,
Harald Welte9f7fa492001-03-15 15:12:02 +0000150 iptc_handle_t *h, const char *chain, int counters)
Marc Bouchere6869a82000-03-20 06:03:29 +0000151{
Harald Welteae1ff9f2000-12-01 14:26:20 +0000152 struct ipt_entry_target *t;
Harald Welteace8a012001-07-05 06:29:10 +0000153 const char *target_name;
Harald Welteae1ff9f2000-12-01 14:26:20 +0000154
155 /* print counters */
Marc Bouchere6869a82000-03-20 06:03:29 +0000156 if (counters)
Harald Welted8e65632001-01-05 15:20:07 +0000157 printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
Marc Bouchere6869a82000-03-20 06:03:29 +0000158
Harald Welte9f7fa492001-03-15 15:12:02 +0000159 /* print chain name */
160 printf("-A %s ", chain);
161
Marc Bouchere6869a82000-03-20 06:03:29 +0000162 /* Print IP part. */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000163 print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
164 e->ip.invflags & IPT_INV_SRCIP);
165
166 print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
Harald Weltefa7625a2001-01-26 03:28:18 +0000167 e->ip.invflags & IPT_INV_DSTIP);
Marc Bouchere6869a82000-03-20 06:03:29 +0000168
169 print_iface('i', e->ip.iniface, e->ip.iniface_mask,
170 e->ip.invflags & IPT_INV_VIA_IN);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000171
Marc Bouchere6869a82000-03-20 06:03:29 +0000172 print_iface('o', e->ip.outiface, e->ip.outiface_mask,
173 e->ip.invflags & IPT_INV_VIA_OUT);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000174
Marc Bouchere6869a82000-03-20 06:03:29 +0000175 print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
176
177 if (e->ip.flags & IPT_F_FRAG)
178 printf("%s-f ",
179 e->ip.invflags & IPT_INV_FRAG ? "! " : "");
180
Marc Bouchere6869a82000-03-20 06:03:29 +0000181 /* Print matchinfo part */
Harald Welteae1ff9f2000-12-01 14:26:20 +0000182 if (e->target_offset) {
Harald Welte32db5242001-01-23 22:46:22 +0000183 IPT_MATCH_ITERATE(e, print_match, &e->ip);
Marc Bouchere6869a82000-03-20 06:03:29 +0000184 }
185
Harald Welteae1ff9f2000-12-01 14:26:20 +0000186 /* Print target name */
Harald Welte6f83cf02001-05-24 23:22:28 +0000187 target_name = iptc_get_target(e, h);
188 if (target_name && (*target_name != '\0'))
189 printf("-j %s ", target_name);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000190
Marc Bouchere6869a82000-03-20 06:03:29 +0000191 /* Print targinfo part */
Rusty Russell082ba022001-01-07 06:54:51 +0000192 t = ipt_get_target((struct ipt_entry *)e);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000193 if (t->u.user.name[0]) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000194 struct iptables_target *target
Harald Welteae1ff9f2000-12-01 14:26:20 +0000195 = find_target(t->u.user.name, TRY_LOAD);
Marc Bouchere6869a82000-03-20 06:03:29 +0000196
Harald Welte31098ad2001-10-16 09:40:13 +0000197 if (!target) {
198 fprintf(stderr, "Can't find library for target `%s'\n",
199 t->u.user.name);
200 exit(1);
201 }
202
203 if (target->save)
Harald Welte32db5242001-01-23 22:46:22 +0000204 target->save(&e->ip, t);
Marc Bouchere6869a82000-03-20 06:03:29 +0000205 else {
Harald Welte31098ad2001-10-16 09:40:13 +0000206 /* If the target size is greater than ipt_entry_target
207 * there is something to be saved, we just don't know
208 * how to print it */
209 if (t->u.target_size !=
210 sizeof(struct ipt_entry_target)) {
211 fprintf(stderr, "Target `%s' is missing "
212 "save function\n",
Harald Welteae1ff9f2000-12-01 14:26:20 +0000213 t->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000214 exit(1);
215 }
216 }
217 }
218 printf("\n");
219}
220
221/* Debugging prototype. */
Rusty Russella8f033e2000-07-30 01:43:01 +0000222static int for_each_table(int (*func)(const char *tablename))
223{
224 int ret = 1;
Harald Welteae1ff9f2000-12-01 14:26:20 +0000225 FILE *procfile = NULL;
Rusty Russella8f033e2000-07-30 01:43:01 +0000226 char tablename[IPT_TABLE_MAXNAMELEN+1];
227
Harald Welteae1ff9f2000-12-01 14:26:20 +0000228 procfile = fopen("/proc/net/ip_tables_names", "r");
Rusty Russella8f033e2000-07-30 01:43:01 +0000229 if (!procfile)
230 return 0;
231
232 while (fgets(tablename, sizeof(tablename), procfile)) {
233 if (tablename[strlen(tablename) - 1] != '\n')
234 exit_error(OTHER_PROBLEM,
235 "Badly formed tablename `%s'\n",
236 tablename);
237 tablename[strlen(tablename) - 1] = '\0';
238 ret &= func(tablename);
239 }
240
241 return ret;
242}
243
244
Rusty Russella8f033e2000-07-30 01:43:01 +0000245static int do_output(const char *tablename)
246{
247 iptc_handle_t h;
248 const char *chain = NULL;
249
250 if (!tablename)
251 return for_each_table(&do_output);
252
253 h = iptc_init(tablename);
254 if (!h)
255 exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
256 iptc_strerror(errno));
257
258 if (!binary) {
259 time_t now = time(NULL);
260
261 printf("# Generated by iptables-save v%s on %s",
262 NETFILTER_VERSION, ctime(&now));
Harald Welteae1ff9f2000-12-01 14:26:20 +0000263 printf("*%s\n", tablename);
Rusty Russella8f033e2000-07-30 01:43:01 +0000264
Harald Welte9f7fa492001-03-15 15:12:02 +0000265 /* Dump out chain names first,
266 * thereby preventing dependency conflicts */
Rusty Russella8f033e2000-07-30 01:43:01 +0000267 for (chain = iptc_first_chain(&h);
268 chain;
269 chain = iptc_next_chain(&h)) {
Harald Welte9f7fa492001-03-15 15:12:02 +0000270
Rusty Russella8f033e2000-07-30 01:43:01 +0000271 printf(":%s ", chain);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000272 if (iptc_builtin(chain, h)) {
Rusty Russella8f033e2000-07-30 01:43:01 +0000273 struct ipt_counters count;
274 printf("%s ",
275 iptc_get_policy(chain, &count, &h));
Harald Welted8e65632001-01-05 15:20:07 +0000276 printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
Rusty Russella8f033e2000-07-30 01:43:01 +0000277 } else {
Harald Welted8e65632001-01-05 15:20:07 +0000278 printf("- [0:0]\n");
Rusty Russella8f033e2000-07-30 01:43:01 +0000279 }
Harald Welte9f7fa492001-03-15 15:12:02 +0000280 }
281
282
283 for (chain = iptc_first_chain(&h);
284 chain;
285 chain = iptc_next_chain(&h)) {
286 const struct ipt_entry *e;
Rusty Russella8f033e2000-07-30 01:43:01 +0000287
Harald Welteae1ff9f2000-12-01 14:26:20 +0000288 /* Dump out rules */
289 e = iptc_first_rule(chain, &h);
290 while(e) {
Harald Welte9f7fa492001-03-15 15:12:02 +0000291 print_rule(e, &h, chain, counters);
Harald Welteae1ff9f2000-12-01 14:26:20 +0000292 e = iptc_next_rule(e, &h);
Rusty Russella8f033e2000-07-30 01:43:01 +0000293 }
294 }
295
296 now = time(NULL);
297 printf("COMMIT\n");
298 printf("# Completed on %s", ctime(&now));
299 } else {
300 /* Binary, huh? OK. */
301 exit_error(OTHER_PROBLEM, "Binary NYI\n");
302 }
303
304 return 1;
305}
306
Marc Bouchere6869a82000-03-20 06:03:29 +0000307/* Format:
308 * :Chain name POLICY packets bytes
309 * rule
310 */
311int main(int argc, char *argv[])
312{
Rusty Russella8f033e2000-07-30 01:43:01 +0000313 const char *tablename = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000314 int c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000315
316 program_name = "iptables-save";
317 program_version = NETFILTER_VERSION;
318
Harald Welte3efb6ea2001-08-06 18:50:21 +0000319#ifdef NO_SHARED_LIBS
320 init_extensions();
321#endif
322
Marc Boucher163ad782001-12-06 15:05:48 +0000323 while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000324 switch (c) {
325 case 'b':
326 binary = 1;
327 break;
328
329 case 'c':
330 counters = 1;
331 break;
332
Rusty Russella8f033e2000-07-30 01:43:01 +0000333 case 't':
334 /* Select specific table. */
335 tablename = optarg;
336 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000337 case 'd':
Harald Welteae1ff9f2000-12-01 14:26:20 +0000338 do_output(tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000339 exit(0);
340 }
341 }
342
343 if (optind < argc) {
344 fprintf(stderr, "Unknown arguments found on commandline");
345 exit(1);
346 }
347
Rusty Russella8f033e2000-07-30 01:43:01 +0000348 return !do_output(tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000349}