treewide: replace unsafe string functions
[project/firewall3.git] / ipsets.c
index 30c6463ff05a73e73fb9a99b2812f738577f7f86..280845b9b7105c7aff6ec7b320cc647f01a7e775 100644 (file)
--- a/ipsets.c
+++ b/ipsets.c
@@ -21,6 +21,9 @@
 
 const struct fw3_option fw3_ipset_opts[] = {
        FW3_OPT("enabled",       bool,           ipset,     enabled),
+       FW3_OPT("reload_set",    bool,           ipset,     reload_set),
+       FW3_OPT("counters",      bool,           ipset,     counters),
+       FW3_OPT("comment",       bool,           ipset,     comment),
 
        FW3_OPT("name",          string,         ipset,     name),
        FW3_OPT("family",        family,         ipset,     family),
@@ -38,6 +41,9 @@ const struct fw3_option fw3_ipset_opts[] = {
 
        FW3_OPT("external",      string,         ipset,     external),
 
+       FW3_LIST("entry",        setentry,       ipset,     entries),
+       FW3_OPT("loadfile",      string,         ipset,     loadfile),
+
        { }
 };
 
@@ -201,6 +207,10 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset)
 static bool
 check_ipset(struct fw3_state *state, struct fw3_ipset *ipset, struct uci_element *e)
 {
+       if (!ipset->enabled) {
+               return false;
+       }
+
        if (ipset->external)
        {
                if (!*ipset->external)
@@ -247,9 +257,13 @@ fw3_alloc_ipset(struct fw3_state *state)
                return NULL;
 
        INIT_LIST_HEAD(&ipset->datatypes);
+       INIT_LIST_HEAD(&ipset->entries);
 
-       ipset->enabled = true;
-       ipset->family  = FW3_FAMILY_V4;
+       ipset->comment    = false;
+       ipset->counters   = false;
+       ipset->enabled    = true;
+       ipset->family     = FW3_FAMILY_V4;
+       ipset->reload_set = false;
 
        list_add_tail(&ipset->list, &state->ipsets);
 
@@ -318,11 +332,35 @@ fw3_load_ipsets(struct fw3_state *state, struct uci_package *p,
 }
 
 
+static void
+load_file(struct fw3_ipset *ipset)
+{
+       FILE *f;
+       char line[128];
+
+       if (!ipset->loadfile)
+               return;
+
+       info("   * Loading file %s", ipset->loadfile);
+
+       f = fopen(ipset->loadfile, "r");
+
+       if (!f) {
+               info("     ! Skipping due to open error: %s", strerror(errno));
+               return;
+       }
+
+       while (fgets(line, sizeof(line), f))
+               fw3_pr("add %s %s", ipset->name, line);
+
+       fclose(f);
+}
+
 static void
 create_ipset(struct fw3_ipset *ipset, struct fw3_state *state)
 {
        bool first = true;
-
+       struct fw3_setentry *entry;
        struct fw3_ipset_datatype *type;
 
        info(" * Creating ipset %s", ipset->name);
@@ -361,13 +399,25 @@ create_ipset(struct fw3_ipset *ipset, struct fw3_state *state)
        if (ipset->hashsize > 0)
                fw3_pr(" hashsize %u", ipset->hashsize);
 
+       if (ipset->counters)
+               fw3_pr(" counters");
+
+       if (ipset->comment)
+               fw3_pr(" comment");
+
        fw3_pr("\n");
+
+       list_for_each_entry(entry, &ipset->entries, list)
+               fw3_pr("add %s %s\n", ipset->name, entry->value);
+
+       load_file(ipset);
 }
 
 void
-fw3_create_ipsets(struct fw3_state *state)
+fw3_create_ipsets(struct fw3_state *state, enum fw3_family family,
+                 bool reload_set)
 {
-       int tries;
+       unsigned int delay, tries;
        bool exec = false;
        struct fw3_ipset *ipset;
 
@@ -377,9 +427,16 @@ fw3_create_ipsets(struct fw3_state *state)
        /* spawn ipsets */
        list_for_each_entry(ipset, &state->ipsets, list)
        {
+               if (ipset->family != family)
+                       continue;
+
                if (ipset->external)
                        continue;
 
+               if (fw3_check_ipset(ipset) &&
+                   (reload_set && !ipset->reload_set))
+                       continue;
+
                if (!exec)
                {
                        exec = fw3_command_pipe(false, "ipset", "-exist", "-");
@@ -397,27 +454,36 @@ fw3_create_ipsets(struct fw3_state *state)
                fw3_command_close();
        }
 
-       /* wait for ipsets to appear */
+       /* wait a little expontially for ipsets to appear */
        list_for_each_entry(ipset, &state->ipsets, list)
        {
                if (ipset->external)
                        continue;
 
+               delay = 5;
                for (tries = 0; !fw3_check_ipset(ipset) && tries < 10; tries++)
-                       usleep(50000);
+                       usleep(delay<<1);
        }
 }
 
 void
-fw3_destroy_ipsets(struct fw3_state *state)
+fw3_destroy_ipsets(struct fw3_state *state, enum fw3_family family,
+                  bool reload_set)
 {
-       int tries;
+       unsigned int delay, tries;
        bool exec = false;
        struct fw3_ipset *ipset;
 
+       if (state->disable_ipsets)
+               return;
+
        /* destroy ipsets */
        list_for_each_entry(ipset, &state->ipsets, list)
        {
+               if (ipset->family != family ||
+                   (reload_set && !ipset->reload_set))
+                       continue;
+
                if (!exec)
                {
                        exec = fw3_command_pipe(false, "ipset", "-exist", "-");
@@ -444,8 +510,9 @@ fw3_destroy_ipsets(struct fw3_state *state)
                if (ipset->external)
                        continue;
 
+               delay = 5;
                for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++)
-                       usleep(50000);
+                       usleep(delay<<1);
        }
 }
 
@@ -504,3 +571,43 @@ out:
 
        return rv;
 }
+
+void
+fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state *run_state,
+                           struct fw3_state *cfg_state)
+{
+       struct fw3_ipset *ipset_run, *ipset_cfg;
+       bool in_cfg;
+
+       list_for_each_entry(ipset_run, &run_state->ipsets, list) {
+               if (ipset_run->family != family)
+                       continue;
+
+               in_cfg = false;
+
+               list_for_each_entry(ipset_cfg, &cfg_state->ipsets, list) {
+                       if (ipset_cfg->family != family)
+                               continue;
+
+                       if (strlen(ipset_run->name) ==
+                           strlen(ipset_cfg->name) &&
+                           !strcmp(ipset_run->name, ipset_cfg->name)) {
+                               in_cfg = true;
+                               break;
+                       }
+               }
+
+               /* If a set is found in run_state, but not in cfg_state then the
+                * set has been deleted/renamed. Set reload_set to true to force
+                * the old set to be destroyed in the "stop" fase of the reload.
+                * If the set is found, then copy the reload_set value from the
+                * configuration state. This ensures that the elements are
+                * always updated according to the configuration, and not the
+                * runtime state (which the user might have forgotten).
+                */
+               if (!in_cfg)
+                       ipset_run->reload_set = true;
+               else
+                       ipset_run->reload_set = ipset_cfg->reload_set;
+       }
+}