Restore iptables-save include functionality
[project/firewall3.git] / utils.c
diff --git a/utils.c b/utils.c
index f43a859..ea40974 100644 (file)
--- a/utils.c
+++ b/utils.c
 #include "utils.h"
 #include "options.h"
 
+#include "zones.h"
+#include "ipsets.h"
+
+
 static int lock_fd = -1;
 static pid_t pipe_pid = -1;
 static FILE *pipe_fd = NULL;
 
+bool fw3_pr_debug = false;
+
+
 static void
 warn_elem_section_name(struct uci_section *s, bool find_name)
 {
@@ -125,6 +132,32 @@ info(const char* format, ...)
        fprintf(stderr, "\n");
 }
 
+void *
+fw3_alloc(size_t size)
+{
+       void *mem;
+
+       mem = calloc(1, size);
+
+       if (!mem)
+               error("Out of memory while allocating %d bytes", size);
+
+       return mem;
+}
+
+char *
+fw3_strdup(const char *s)
+{
+       char *ns;
+
+       ns = strdup(s);
+
+       if (!ns)
+               error("Out of memory while duplicating string '%s'", s);
+
+       return ns;
+}
+
 const char *
 fw3_find_command(const char *cmd)
 {
@@ -237,6 +270,7 @@ __fw3_command_pipe(bool silent, const char *command, ...)
                signal(SIGPIPE, SIG_IGN);
                pipe_pid = pid;
                close(pfds[0]);
+               fcntl(pfds[1], F_SETFD, fcntl(pfds[1], F_GETFD) | FD_CLOEXEC);
        }
 
        pipe_fd = fdopen(pfds[1], "w");
@@ -246,10 +280,18 @@ __fw3_command_pipe(bool silent, const char *command, ...)
 void
 fw3_pr(const char *fmt, ...)
 {
-    va_list args;
-    va_start(args, fmt);
-    vfprintf(pipe_fd, fmt, args);
-    va_end(args);
+       va_list args;
+
+       if (fw3_pr_debug && pipe_fd != stdout)
+       {
+               va_start(args, fmt);
+               vfprintf(stderr, fmt, args);
+               va_end(args);
+       }
+
+       va_start(args, fmt);
+       vfprintf(pipe_fd, fmt, args);
+       va_end(args);
 }
 
 void
@@ -332,66 +374,214 @@ fw3_unlock(void)
 }
 
 
-struct list_head *
-fw3_read_statefile(void)
+static void
+write_defaults_uci(struct uci_context *ctx, struct fw3_defaults *d,
+                   struct uci_package *dest)
 {
-       FILE *sf;
+       char buf[sizeof("0xffffffff\0")];
+       struct uci_ptr ptr = { .p = dest };
+
+       uci_add_section(ctx, dest, "defaults", &ptr.s);
+
+       ptr.o      = NULL;
+       ptr.option = "input";
+       ptr.value  = fw3_flag_names[d->policy_input];
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "output";
+       ptr.value  = fw3_flag_names[d->policy_output];
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "forward";
+       ptr.value  = fw3_flag_names[d->policy_forward];
+       uci_set(ctx, &ptr);
+
+       sprintf(buf, "0x%x", d->flags[0]);
+       ptr.o      = NULL;
+       ptr.option = "__flags_v4";
+       ptr.value  = buf;
+       uci_set(ctx, &ptr);
+
+       sprintf(buf, "0x%x", d->flags[1]);
+       ptr.o      = NULL;
+       ptr.option = "__flags_v6";
+       ptr.value  = buf;
+       uci_set(ctx, &ptr);
+}
 
-       int n;
-       char line[128];
-       const char *p;
+static void
+write_zone_uci(struct uci_context *ctx, struct fw3_zone *z,
+               struct uci_package *dest)
+{
+       struct fw3_device *dev;
+       struct fw3_address *sub;
+       enum fw3_family fam = FW3_FAMILY_ANY;
 
-       struct list_head *state;
-       struct fw3_statefile_entry *entry;
+       char *p, buf[34];
 
-       sf = fopen(FW3_STATEFILE, "r");
+       struct uci_ptr ptr = { .p = dest };
 
-       if (!sf)
-               return NULL;
+       if (!z->enabled)
+               return;
 
-       state = malloc(sizeof(*state));
+       if (fw3_no_table(z->flags[0]) && !fw3_no_table(z->flags[1]))
+               fam = FW3_FAMILY_V6;
+       else if (!fw3_no_table(z->flags[0]) && fw3_no_table(z->flags[1]))
+               fam = FW3_FAMILY_V4;
+       else if (fw3_no_table(z->flags[0]) && fw3_no_table(z->flags[1]))
+               return;
 
-       if (!state)
-               return NULL;
+       uci_add_section(ctx, dest, "zone", &ptr.s);
+
+       ptr.o      = NULL;
+       ptr.option = "name";
+       ptr.value  = z->name;
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "input";
+       ptr.value  = fw3_flag_names[z->policy_input];
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "output";
+       ptr.value  = fw3_flag_names[z->policy_output];
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "forward";
+       ptr.value  = fw3_flag_names[z->policy_forward];
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "masq";
+       ptr.value  = z->masq ? "1" : "0";
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "conntrack";
+       ptr.value  = z->conntrack ? "1" : "0";
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "mtu_fix";
+       ptr.value  = z->mtu_fix ? "1" : "0";
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "custom_chains";
+       ptr.value  = z->custom_chains ? "1" : "0";
+       uci_set(ctx, &ptr);
+
+       if (fam != FW3_FAMILY_ANY)
+       {
+               ptr.o      = NULL;
+               ptr.option = "family";
+               ptr.value  = fw3_flag_names[fam];
+               uci_set(ctx, &ptr);
+       }
 
-       INIT_LIST_HEAD(state);
+       ptr.o      = NULL;
+       ptr.option = "device";
 
-       while (fgets(line, sizeof(line), sf))
+       fw3_foreach(dev, &z->devices)
        {
-               entry = malloc(sizeof(*entry));
-
-               if (!entry)
+               if (!dev)
                        continue;
 
-               memset(entry, 0, sizeof(*entry));
+               p = buf;
 
-               p = strtok(line, " \t\n");
+               if (dev->invert)
+                       p += sprintf(p, "!");
 
-               if (!p)
-                       continue;
+               if (*dev->network)
+                       p += sprintf(p, "%s@%s", dev->name, dev->network);
+               else
+                       p += sprintf(p, "%s", dev->name);
 
-               entry->type = strtoul(p, NULL, 10);
+               ptr.value = buf;
+               uci_add_list(ctx, &ptr);
+       }
 
-               p = strtok(NULL, " \t\n");
+       ptr.o      = NULL;
+       ptr.option = "subnet";
 
-               if (!p)
+       fw3_foreach(sub, &z->subnets)
+       {
+               if (!sub)
                        continue;
 
-               entry->name = strdup(p);
+               ptr.value = fw3_address_to_string(sub, true);
+               uci_add_list(ctx, &ptr);
+       }
+
+       sprintf(buf, "0x%x", z->flags[0]);
+       ptr.o      = NULL;
+       ptr.option = "__flags_v4";
+       ptr.value  = buf;
+       uci_set(ctx, &ptr);
+
+       sprintf(buf, "0x%x", z->flags[1]);
+       ptr.o      = NULL;
+       ptr.option = "__flags_v6";
+       ptr.value  = buf;
+       uci_set(ctx, &ptr);
+}
 
-               for (n = 0, p = strtok(NULL, " \t\n");
-                    n < ARRAY_SIZE(entry->flags) && p != NULL;
-                    n++, p = strtok(NULL, " \t\n"))
-               {
-                       entry->flags[n] = strtoul(p, NULL, 10);
-               }
+static void
+write_ipset_uci(struct uci_context *ctx, struct fw3_ipset *s,
+                struct uci_package *dest)
+{
+       struct fw3_ipset_datatype *type;
+
+       char buf[sizeof("65535-65535\0")];
+
+       struct uci_ptr ptr = { .p = dest };
+
+       if (!s->enabled || s->external)
+               return;
+
+       uci_add_section(ctx, dest, "ipset", &ptr.s);
 
-               list_add_tail(&entry->list, state);
+       ptr.o      = NULL;
+       ptr.option = "name";
+       ptr.value  = s->name;
+       uci_set(ctx, &ptr);
+
+       ptr.o      = NULL;
+       ptr.option = "storage";
+       ptr.value  = fw3_ipset_method_names[s->method];
+       uci_set(ctx, &ptr);
+
+       list_for_each_entry(type, &s->datatypes, list)
+       {
+               sprintf(buf, "%s_%s", type->dest ? "dst" : "src",
+                                     fw3_ipset_type_names[type->type]);
+
+               ptr.o      = NULL;
+               ptr.option = "match";
+               ptr.value  = buf;
+               uci_add_list(ctx, &ptr);
        }
 
-       fclose(sf);
+       if (s->iprange.set)
+       {
+               ptr.o      = NULL;
+               ptr.option = "iprange";
+               ptr.value  = fw3_address_to_string(&s->iprange, false);
+               uci_set(ctx, &ptr);
+       }
 
-       return state;
+       if (s->portrange.set)
+       {
+               sprintf(buf, "%u-%u", s->portrange.port_min, s->portrange.port_max);
+               ptr.o      = NULL;
+               ptr.option = "portrange";
+               ptr.value  = buf;
+               uci_set(ctx, &ptr);
+       }
 }
 
 void
@@ -399,62 +589,109 @@ fw3_write_statefile(void *state)
 {
        FILE *sf;
        struct fw3_state *s = state;
-       struct fw3_defaults *d = &s->defaults;
        struct fw3_zone *z;
        struct fw3_ipset *i;
 
-       int mask = (1 << FW3_DEFAULT_IPV4_LOADED) | (1 << FW3_DEFAULT_IPV6_LOADED);
+       struct uci_package *p;
 
-       if (!(d->flags & mask))
+       if (fw3_no_family(s->defaults.flags[0]) &&
+           fw3_no_family(s->defaults.flags[1]))
        {
-               if (unlink(FW3_STATEFILE))
-                       warn("Unable to remove state %s: %s",
-                            FW3_STATEFILE, strerror(errno));
-
-               return;
+               unlink(FW3_STATEFILE);
        }
+       else
+       {
+               sf = fopen(FW3_STATEFILE, "w+");
 
-       sf = fopen(FW3_STATEFILE, "w");
+               if (!sf)
+               {
+                       warn("Cannot create state %s: %s", FW3_STATEFILE, strerror(errno));
+                       return;
+               }
 
-       if (!sf)
-       {
-               warn("Cannot create state %s: %s", FW3_STATEFILE, strerror(errno));
-               return;
-       }
+               if ((p = uci_lookup_package(s->uci, "fw3_state")) != NULL)
+                       uci_unload(s->uci, p);
 
-       fprintf(sf, "%u - %u\n", FW3_TYPE_DEFAULTS, d->flags);
+               uci_import(s->uci, sf, "fw3_state", NULL, true);
 
-       list_for_each_entry(z, &s->zones, list)
-       {
-               fprintf(sf, "%u %s %u %u\n", FW3_TYPE_ZONE,
-                       z->name, z->src_flags, z->dst_flags);
+               if ((p = uci_lookup_package(s->uci, "fw3_state")) != NULL)
+               {
+                       write_defaults_uci(s->uci, &s->defaults, p);
+
+                       list_for_each_entry(z, &s->zones, list)
+                               write_zone_uci(s->uci, z, p);
+
+                       list_for_each_entry(i, &s->ipsets, list)
+                               write_ipset_uci(s->uci, i, p);
+
+                       uci_export(s->uci, sf, p, true);
+                       uci_unload(s->uci, p);
+               }
+
+               fsync(fileno(sf));
+               fclose(sf);
        }
+}
+
+
+void
+fw3_free_object(void *obj, const void *opts)
+{
+       const struct fw3_option *ol;
+       struct list_head *list, *cur, *tmp;
 
-       list_for_each_entry(i, &s->ipsets, list)
+       for (ol = opts; ol->name; ol++)
        {
-               if (i->external && *i->external)
+               if (!ol->elem_size)
                        continue;
 
-               fprintf(sf, "%u %s\n", FW3_TYPE_IPSET, i->name);
+               list = (struct list_head *)((char *)obj + ol->offset);
+               list_for_each_safe(cur, tmp, list)
+               {
+                       list_del(cur);
+                       free(cur);
+               }
        }
 
-       fclose(sf);
+       free(obj);
 }
 
-void
-fw3_free_statefile(struct list_head *statefile)
+
+bool
+fw3_hotplug(bool add, void *zone, void *device)
 {
-       struct fw3_statefile_entry *e, *tmp;
+       struct fw3_zone *z = zone;
+       struct fw3_device *d = device;
 
-       if (!statefile)
-               return;
+       if (!*d->network)
+               return false;
 
-       list_for_each_entry_safe(e, tmp, statefile, list)
+       switch (fork())
        {
-               list_del(&e->list);
-               free(e->name);
-               free(e);
+       case -1:
+               warn("Unable to fork(): %s\n", strerror(errno));
+               return false;
+
+       case 0:
+               break;
+
+       default:
+               return true;
        }
 
-       free(statefile);
+       close(0);
+       close(1);
+       close(2);
+       chdir("/");
+
+       clearenv();
+       setenv("ACTION",    add ? "add" : "remove", 1);
+       setenv("ZONE",      z->name,                1);
+       setenv("INTERFACE", d->network,             1);
+       setenv("DEVICE",    d->name,                1);
+
+       execl(FW3_HOTPLUG, FW3_HOTPLUG, "firewall", NULL);
+
+       /* unreached */
+       return false;
 }