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