1/* Code to save the iptables state, in human readable-form. */ 2/* (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 * 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 20static int binary = 0, counters = 0; 21 22static 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. */ 39static void 40print_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 */ 67struct pprot { 68 char *name; 69 u_int8_t num; 70}; 71 72static const struct pprot chain_protos[] = { 73 { "tcp", IPPROTO_TCP }, 74 { "udp", IPPROTO_UDP }, 75 { "icmp", IPPROTO_ICMP }, 76 { "esp", IPPROTO_ESP }, 77 { "ah", IPPROTO_AH }, 78 { "sctp", IPPROTO_SCTP }, 79}; 80 81static void print_proto(u_int16_t proto, int invert) 82{ 83 if (proto) { 84 unsigned int i; 85 const char *invertstr = invert ? "! " : ""; 86 87 struct protoent *pent = getprotobynumber(proto); 88 if (pent) { 89 printf("-p %s%s ", invertstr, pent->p_name); 90 return; 91 } 92 93 for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) 94 if (chain_protos[i].num == proto) { 95 printf("-p %s%s ", 96 invertstr, chain_protos[i].name); 97 return; 98 } 99 100 printf("-p %s%u ", invertstr, proto); 101 } 102} 103 104 105static int print_match(const struct ipt_entry_match *e, 106 const struct ipt_ip *ip) 107{ 108 struct iptables_match *match 109 = find_match(e->u.user.name, TRY_LOAD, NULL); 110 111 if (match) { 112 printf("-m %s ", e->u.user.name); 113 114 /* some matches don't provide a save function */ 115 if (match->save) 116 match->save(ip, e); 117 } else { 118 if (e->u.match_size) { 119 fprintf(stderr, 120 "Can't find library for match `%s'\n", 121 e->u.user.name); 122 exit(1); 123 } 124 } 125 return 0; 126} 127 128/* print a given ip including mask if neccessary */ 129static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert) 130{ 131 if (!mask && !ip && !invert) 132 return; 133 134 printf("%s %s%u.%u.%u.%u", 135 prefix, 136 invert ? "! " : "", 137 IP_PARTS(ip)); 138 139 if (mask != 0xffffffff) 140 printf("/%u.%u.%u.%u ", IP_PARTS(mask)); 141 else 142 printf(" "); 143} 144 145/* We want this to be readable, so only print out neccessary fields. 146 * Because that's the kind of world I want to live in. */ 147static void print_rule(const struct ipt_entry *e, 148 iptc_handle_t *h, const char *chain, int counters) 149{ 150 struct ipt_entry_target *t; 151 const char *target_name; 152 153 /* print counters */ 154 if (counters) 155 printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt); 156 157 /* print chain name */ 158 printf("-A %s ", chain); 159 160 /* Print IP part. */ 161 print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr, 162 e->ip.invflags & IPT_INV_SRCIP); 163 164 print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr, 165 e->ip.invflags & IPT_INV_DSTIP); 166 167 print_iface('i', e->ip.iniface, e->ip.iniface_mask, 168 e->ip.invflags & IPT_INV_VIA_IN); 169 170 print_iface('o', e->ip.outiface, e->ip.outiface_mask, 171 e->ip.invflags & IPT_INV_VIA_OUT); 172 173 print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO); 174 175 if (e->ip.flags & IPT_F_FRAG) 176 printf("%s-f ", 177 e->ip.invflags & IPT_INV_FRAG ? "! " : ""); 178 179 /* Print matchinfo part */ 180 if (e->target_offset) { 181 IPT_MATCH_ITERATE(e, print_match, &e->ip); 182 } 183 184 /* Print target name */ 185 target_name = iptc_get_target(e, h); 186 if (target_name && (*target_name != '\0')) 187#ifdef IPT_F_GOTO 188 printf("-%c %s ", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name); 189#else 190 printf("-j %s ", target_name); 191#endif 192 193 /* Print targinfo part */ 194 t = ipt_get_target((struct ipt_entry *)e); 195 if (t->u.user.name[0]) { 196 struct iptables_target *target 197 = find_target(t->u.user.name, TRY_LOAD); 198 199 if (!target) { 200 fprintf(stderr, "Can't find library for target `%s'\n", 201 t->u.user.name); 202 exit(1); 203 } 204 205 if (target->save) 206 target->save(&e->ip, t); 207 else { 208 /* If the target size is greater than ipt_entry_target 209 * there is something to be saved, we just don't know 210 * how to print it */ 211 if (t->u.target_size != 212 sizeof(struct ipt_entry_target)) { 213 fprintf(stderr, "Target `%s' is missing " 214 "save function\n", 215 t->u.user.name); 216 exit(1); 217 } 218 } 219 } 220 printf("\n"); 221} 222 223/* Debugging prototype. */ 224static int for_each_table(int (*func)(const char *tablename)) 225{ 226 int ret = 1; 227 FILE *procfile = NULL; 228 char tablename[IPT_TABLE_MAXNAMELEN+1]; 229 230 procfile = fopen("/proc/net/ip_tables_names", "r"); 231 if (!procfile) 232 return 0; 233 234 while (fgets(tablename, sizeof(tablename), procfile)) { 235 if (tablename[strlen(tablename) - 1] != '\n') 236 exit_error(OTHER_PROBLEM, 237 "Badly formed tablename `%s'\n", 238 tablename); 239 tablename[strlen(tablename) - 1] = '\0'; 240 ret &= func(tablename); 241 } 242 243 return ret; 244} 245 246 247static int do_output(const char *tablename) 248{ 249 iptc_handle_t h; 250 const char *chain = NULL; 251 252 if (!tablename) 253 return for_each_table(&do_output); 254 255 h = iptc_init(tablename); 256 if (!h) 257 exit_error(OTHER_PROBLEM, "Can't initialize: %s\n", 258 iptc_strerror(errno)); 259 260 if (!binary) { 261 time_t now = time(NULL); 262 263 printf("# Generated by iptables-save v%s on %s", 264 IPTABLES_VERSION, ctime(&now)); 265 printf("*%s\n", tablename); 266 267 /* Dump out chain names first, 268 * thereby preventing dependency conflicts */ 269 for (chain = iptc_first_chain(&h); 270 chain; 271 chain = iptc_next_chain(&h)) { 272 273 printf(":%s ", chain); 274 if (iptc_builtin(chain, h)) { 275 struct ipt_counters count; 276 printf("%s ", 277 iptc_get_policy(chain, &count, &h)); 278 printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt); 279 } else { 280 printf("- [0:0]\n"); 281 } 282 } 283 284 285 for (chain = iptc_first_chain(&h); 286 chain; 287 chain = iptc_next_chain(&h)) { 288 const struct ipt_entry *e; 289 290 /* Dump out rules */ 291 e = iptc_first_rule(chain, &h); 292 while(e) { 293 print_rule(e, &h, chain, counters); 294 e = iptc_next_rule(e, &h); 295 } 296 } 297 298 now = time(NULL); 299 printf("COMMIT\n"); 300 printf("# Completed on %s", ctime(&now)); 301 } else { 302 /* Binary, huh? OK. */ 303 exit_error(OTHER_PROBLEM, "Binary NYI\n"); 304 } 305 306 iptc_free(&h); 307 308 return 1; 309} 310 311/* Format: 312 * :Chain name POLICY packets bytes 313 * rule 314 */ 315#ifdef IPTABLES_MULTI 316int 317iptables_save_main(int argc, char *argv[]) 318#else 319int 320main(int argc, char *argv[]) 321#endif 322{ 323 const char *tablename = NULL; 324 int c; 325 326 program_name = "iptables-save"; 327 program_version = IPTABLES_VERSION; 328 329 lib_dir = getenv("IPTABLES_LIB_DIR"); 330 if (!lib_dir) 331 lib_dir = IPT_LIB_DIR; 332 333#ifdef NO_SHARED_LIBS 334 init_extensions(); 335#endif 336 337 while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) { 338 switch (c) { 339 case 'b': 340 binary = 1; 341 break; 342 343 case 'c': 344 counters = 1; 345 break; 346 347 case 't': 348 /* Select specific table. */ 349 tablename = optarg; 350 break; 351 case 'd': 352 do_output(tablename); 353 exit(0); 354 } 355 } 356 357 if (optind < argc) { 358 fprintf(stderr, "Unknown arguments found on commandline\n"); 359 exit(1); 360 } 361 362 return !do_output(tablename); 363} 364