aboutsummaryrefslogtreecommitdiff
path: root/ip6tables-save.c
diff options
context:
space:
mode:
Diffstat (limited to 'ip6tables-save.c')
-rw-r--r--ip6tables-save.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/ip6tables-save.c b/ip6tables-save.c
new file mode 100644
index 00000000..486c5224
--- /dev/null
+++ b/ip6tables-save.c
@@ -0,0 +1,359 @@
+/* Code to save the ip6tables state, in human readable-form. */
+/* Author: Andras Kis-Szabo <kisza@sch.bme.hu>
+ * Original code: iptables-save
+ * Authors: Paul 'Rusty' Russel <rusty@linuxcare.com.au> and
+ * Harald Welte <laforge@gnumonks.org>
+ * This code is distributed under the terms of GNU GPL v2
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <time.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "libiptc/libip6tc.h"
+#include "ip6tables.h"
+
+static int binary = 0, counters = 0;
+
+static struct option options[] = {
+ { "binary", 0, 0, 'b' },
+ { "counters", 0, 0, 'c' },
+ { "dump", 0, 0, 'd' },
+ { "table", 1, 0, 't' },
+ { 0 }
+};
+
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+ int invert)
+{
+ unsigned int i;
+
+ if (mask[0] == 0)
+ return;
+
+ printf("-%c %s", letter, invert ? "! " : "");
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (mask[i] != 0) {
+ if (iface[i] != '\0')
+ printf("%c", iface[i]);
+ } else {
+ /* we can access iface[i-1] here, because
+ * a few lines above we make sure that mask[0] != 0 */
+ if (iface[i-1] != '\0')
+ printf("+");
+ break;
+ }
+ }
+
+ printf(" ");
+}
+
+/* These are hardcoded backups in ip6tables.c, so they are safe */
+struct pprot {
+ char *name;
+ u_int8_t num;
+};
+
+static const struct pprot chain_protos[] = {
+ { "tcp", IPPROTO_TCP },
+ { "udp", IPPROTO_UDP },
+ { "icmpv6", IPPROTO_ICMPV6 },
+ { "esp", IPPROTO_ESP },
+ { "ah", IPPROTO_AH },
+};
+
+/* The ip6tables looks up the /etc/protocols. */
+static void print_proto(u_int16_t proto, int invert)
+{
+ if (proto) {
+ unsigned int i;
+ const char *invertstr = invert ? "! " : "";
+
+ struct protoent *pent = getprotobynumber(proto);
+ if (pent) {
+ printf("-p %s%s ",
+ invertstr, pent->p_name);
+ return;
+ }
+
+ for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+ if (chain_protos[i].num == proto) {
+ printf("-p %s%s ",
+ invertstr, chain_protos[i].name);
+ return;
+ }
+
+ printf("-p %s%u ", invertstr, proto);
+ }
+}
+
+static int print_match(const struct ip6t_entry_match *e,
+ const struct ip6t_ip6 *ip)
+{
+ struct ip6tables_match *match
+ = find_match(e->u.user.name, TRY_LOAD, NULL);
+
+ if (match) {
+ printf("-m %s ", e->u.user.name);
+
+ /* some matches don't provide a save function */
+ if (match->save)
+ match->save(ip, e);
+ } else {
+ if (e->u.match_size) {
+ fprintf(stderr,
+ "Can't find library for match `%s'\n",
+ e->u.user.name);
+ exit(1);
+ }
+ }
+ return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, const struct in6_addr *ip, const struct in6_addr *mask, int invert)
+{
+ char buf[51];
+ int l = ipv6_prefix_length(mask);
+
+ if (l == 0 && !invert)
+ return;
+
+ printf("%s %s%s",
+ prefix,
+ invert ? "! " : "",
+ inet_ntop(AF_INET6, ip, buf, sizeof buf));
+
+ if (l == -1)
+ printf("/%s ", inet_ntop(AF_INET6, mask, buf, sizeof buf));
+ else
+ printf("/%d ", l);
+}
+
+/* We want this to be readable, so only print out neccessary fields.
+ * Because that's the kind of world I want to live in. */
+static void print_rule(const struct ip6t_entry *e,
+ ip6tc_handle_t *h, const char *chain, int counters)
+{
+ struct ip6t_entry_target *t;
+ const char *target_name;
+
+ /* print counters */
+ if (counters)
+ printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+ /* print chain name */
+ printf("-A %s ", chain);
+
+ /* Print IP part. */
+ print_ip("-s", &(e->ipv6.src), &(e->ipv6.smsk),
+ e->ipv6.invflags & IP6T_INV_SRCIP);
+
+ print_ip("-d", &(e->ipv6.dst), &(e->ipv6.dmsk),
+ e->ipv6.invflags & IP6T_INV_DSTIP);
+
+ print_iface('i', e->ipv6.iniface, e->ipv6.iniface_mask,
+ e->ipv6.invflags & IP6T_INV_VIA_IN);
+
+ print_iface('o', e->ipv6.outiface, e->ipv6.outiface_mask,
+ e->ipv6.invflags & IP6T_INV_VIA_OUT);
+
+ print_proto(e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
+
+#if 0
+ /* not definied in ipv6
+ * FIXME: linux/netfilter_ipv6/ip6_tables: IP6T_INV_FRAG why definied? */
+ if (e->ipv6.flags & IPT_F_FRAG)
+ printf("%s-f ",
+ e->ipv6.invflags & IP6T_INV_FRAG ? "! " : "");
+#endif
+
+ if (e->ipv6.flags & IP6T_F_TOS)
+ printf("%s-? %d ",
+ e->ipv6.invflags & IP6T_INV_TOS ? "! " : "",
+ e->ipv6.tos);
+
+ /* Print matchinfo part */
+ if (e->target_offset) {
+ IP6T_MATCH_ITERATE(e, print_match, &e->ipv6);
+ }
+
+ /* Print target name */
+ target_name = ip6tc_get_target(e, h);
+ if (target_name && (*target_name != '\0'))
+ printf("-j %s ", target_name);
+
+ /* Print targinfo part */
+ t = ip6t_get_target((struct ip6t_entry *)e);
+ if (t->u.user.name[0]) {
+ struct ip6tables_target *target
+ = find_target(t->u.user.name, TRY_LOAD);
+
+ if (!target) {
+ fprintf(stderr, "Can't find library for target `%s'\n",
+ t->u.user.name);
+ exit(1);
+ }
+
+ if (target->save)
+ target->save(&e->ipv6, t);
+ else {
+ /* If the target size is greater than ip6t_entry_target
+ * there is something to be saved, we just don't know
+ * how to print it */
+ if (t->u.target_size !=
+ sizeof(struct ip6t_entry_target)) {
+ fprintf(stderr, "Target `%s' is missing "
+ "save function\n",
+ t->u.user.name);
+ exit(1);
+ }
+ }
+ }
+ printf("\n");
+}
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+ int ret = 1;
+ FILE *procfile = NULL;
+ char tablename[IP6T_TABLE_MAXNAMELEN+1];
+
+ procfile = fopen("/proc/net/ip6_tables_names", "r");
+ if (!procfile)
+ return 0;
+
+ while (fgets(tablename, sizeof(tablename), procfile)) {
+ if (tablename[strlen(tablename) - 1] != '\n')
+ exit_error(OTHER_PROBLEM,
+ "Badly formed tablename `%s'\n",
+ tablename);
+ tablename[strlen(tablename) - 1] = '\0';
+ ret &= func(tablename);
+ }
+
+ return ret;
+}
+
+
+static int do_output(const char *tablename)
+{
+ ip6tc_handle_t h;
+ const char *chain = NULL;
+
+ if (!tablename)
+ return for_each_table(&do_output);
+
+ h = ip6tc_init(tablename);
+ if (!h)
+ exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
+ ip6tc_strerror(errno));
+
+ if (!binary) {
+ time_t now = time(NULL);
+
+ printf("# Generated by ip6tables-save v%s on %s",
+ IPTABLES_VERSION, ctime(&now));
+ printf("*%s\n", tablename);
+
+ /* Dump out chain names first,
+ * thereby preventing dependency conflicts */
+ for (chain = ip6tc_first_chain(&h);
+ chain;
+ chain = ip6tc_next_chain(&h)) {
+
+ printf(":%s ", chain);
+ if (ip6tc_builtin(chain, h)) {
+ struct ip6t_counters count;
+ printf("%s ",
+ ip6tc_get_policy(chain, &count, &h));
+ printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+ } else {
+ printf("- [0:0]\n");
+ }
+ }
+
+
+ for (chain = ip6tc_first_chain(&h);
+ chain;
+ chain = ip6tc_next_chain(&h)) {
+ const struct ip6t_entry *e;
+
+ /* Dump out rules */
+ e = ip6tc_first_rule(chain, &h);
+ while(e) {
+ print_rule(e, &h, chain, counters);
+ e = ip6tc_next_rule(e, &h);
+ }
+ }
+
+ now = time(NULL);
+ printf("COMMIT\n");
+ printf("# Completed on %s", ctime(&now));
+ } else {
+ /* Binary, huh? OK. */
+ exit_error(OTHER_PROBLEM, "Binary NYI\n");
+ }
+
+ ip6tc_free(&h);
+
+ return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int main(int argc, char *argv[])
+{
+ const char *tablename = NULL;
+ int c;
+
+ program_name = "ip6tables-save";
+ program_version = IPTABLES_VERSION;
+
+ lib_dir = getenv("IP6TABLES_LIB_DIR");
+ if (!lib_dir)
+ lib_dir = IP6T_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+ init_extensions();
+#endif
+
+ while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ binary = 1;
+ break;
+
+ case 'c':
+ counters = 1;
+ break;
+
+ case 't':
+ /* Select specific table. */
+ tablename = optarg;
+ break;
+ case 'd':
+ do_output(tablename);
+ exit(0);
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline");
+ exit(1);
+ }
+
+ return !do_output(tablename);
+}