1/* Code to save the ip6tables state, in human readable-form. */ 2/* Author: Andras Kis-Szabo <kisza@sch.bme.hu> 3 * Original code: iptables-save 4 * Authors: Paul 'Rusty' Russel <rusty@linuxcare.com.au> and 5 * Harald Welte <laforge@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 21static int binary = 0, counters = 0; 22 23static 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. */ 33static void 34print_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 */ 61struct pprot { 62 char *name; 63 u_int8_t num; 64}; 65 66static 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. */ 75static 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 99static 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 */ 123static 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. */ 144static 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. */ 226static 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 249static 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 */ 317int 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