Only run includes and set sysctls if either v4 or v6 firewall was actually started
[project/firewall3.git] / options.c
index 5d325fc..344d33d 100644 (file)
--- a/options.c
+++ b/options.c
 
 #include "options.h"
 
+
+static bool
+parse_enum(void *ptr, const char *val, const char **values, int min, int max)
+{
+       int i, l = strlen(val);
+
+       if (l > 0)
+       {
+               for (i = 0; i <= (max - min); i++)
+               {
+                       if (!strncasecmp(val, values[i], l))
+                       {
+                               *((int *)ptr) = min + i;
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+
+const char *fw3_flag_names[FW3_DEFAULT_DROP_INVALID + 1] = {
+       "filter",
+       "nat",
+       "mangle",
+       "raw",
+
+       "IPv4",
+       "IPv6",
+
+       "ACCEPT",
+       "REJECT",
+       "DROP",
+       "NOTRACK",
+       "DNAT",
+       "SNAT",
+
+       "ACCEPT",
+       "REJECT",
+       "DROP",
+};
+
+static const char *limit_units[] = {
+       "second",
+       "minute",
+       "hour",
+       "day",
+};
+
+static const char *ipset_methods[] = {
+       "bitmap",
+       "hash",
+       "list",
+};
+
+static const char *ipset_types[] = {
+       "ip",
+       "port",
+       "mac",
+       "net",
+       "set",
+};
+
+static const char *weekdays[] = {
+       "monday",
+       "tuesday",
+       "wednesday",
+       "thursday",
+       "friday",
+       "saturday",
+       "sunday",
+};
+
+static const char *include_types[] = {
+       "script",
+       "restore",
+};
+
+
 bool
 fw3_parse_bool(void *ptr, const char *val)
 {
@@ -52,38 +132,8 @@ fw3_parse_string(void *ptr, const char *val)
 bool
 fw3_parse_target(void *ptr, const char *val)
 {
-       if (!strcmp(val, "ACCEPT"))
-       {
-               *((enum fw3_target *)ptr) = FW3_TARGET_ACCEPT;
-               return true;
-       }
-       else if (!strcmp(val, "REJECT"))
-       {
-               *((enum fw3_target *)ptr) = FW3_TARGET_REJECT;
-               return true;
-       }
-       else if (!strcmp(val, "DROP"))
-       {
-               *((enum fw3_target *)ptr) = FW3_TARGET_DROP;
-               return true;
-       }
-       else if (!strcmp(val, "NOTRACK"))
-       {
-               *((enum fw3_target *)ptr) = FW3_TARGET_NOTRACK;
-               return true;
-       }
-       else if (!strcmp(val, "DNAT"))
-       {
-               *((enum fw3_target *)ptr) = FW3_TARGET_DNAT;
-               return true;
-       }
-       else if (!strcmp(val, "SNAT"))
-       {
-               *((enum fw3_target *)ptr) = FW3_TARGET_SNAT;
-               return true;
-       }
-
-       return false;
+       return parse_enum(ptr, val, &fw3_flag_names[FW3_TARGET_ACCEPT],
+                         FW3_TARGET_ACCEPT, FW3_TARGET_SNAT);
 }
 
 bool
@@ -111,15 +161,7 @@ fw3_parse_limit(void *ptr, const char *val)
        if (!strlen(e))
                return false;
 
-       if (!strncmp(e, "second", strlen(e)))
-               u = FW3_LIMIT_UNIT_SECOND;
-       else if (!strncmp(e, "minute", strlen(e)))
-               u = FW3_LIMIT_UNIT_MINUTE;
-       else if (!strncmp(e, "hour", strlen(e)))
-               u = FW3_LIMIT_UNIT_HOUR;
-       else if (!strncmp(e, "day", strlen(e)))
-               u = FW3_LIMIT_UNIT_DAY;
-       else
+       if (!parse_enum(&u, e, limit_units, 0, FW3_LIMIT_UNIT_DAY))
                return false;
 
        limit->rate = n;
@@ -195,6 +237,28 @@ fw3_parse_address(void *ptr, const char *val)
                        }
                }
        }
+       else if ((p = strchr(s, '-')) != NULL)
+       {
+               *p++ = 0;
+
+               if (inet_pton(AF_INET6, p, &v6))
+               {
+                       addr->family = FW3_FAMILY_V6;
+                       addr->address2.v6 = v6;
+                       addr->range = true;
+               }
+               else if (inet_pton(AF_INET, p, &v4))
+               {
+                       addr->family = FW3_FAMILY_V4;
+                       addr->address2.v4 = v4;
+                       addr->range = true;
+               }
+               else
+               {
+                       free(s);
+                       return false;
+               }
+       }
 
        if (inet_pton(AF_INET6, s, &v6))
        {
@@ -410,23 +474,8 @@ fw3_parse_protocol(void *ptr, const char *val)
 bool
 fw3_parse_ipset_method(void *ptr, const char *val)
 {
-       if (!strncmp(val, "bitmap", strlen(val)))
-       {
-               *((enum fw3_ipset_method *)ptr) = FW3_IPSET_METHOD_BITMAP;
-               return true;
-       }
-       else if (!strncmp(val, "hash", strlen(val)))
-       {
-               *((enum fw3_ipset_method *)ptr) = FW3_IPSET_METHOD_HASH;
-               return true;
-       }
-       else if (!strncmp(val, "list", strlen(val)))
-       {
-               *((enum fw3_ipset_method *)ptr) = FW3_IPSET_METHOD_LIST;
-               return true;
-       }
-
-       return false;
+       return parse_enum(ptr, val, ipset_methods,
+                         FW3_IPSET_METHOD_BITMAP, FW3_IPSET_METHOD_LIST);
 }
 
 bool
@@ -450,47 +499,181 @@ fw3_parse_ipset_datatype(void *ptr, const char *val)
                type->dest = false;
        }
 
-       if (!strncmp(val, "ip", strlen(val)))
+       return parse_enum(&type->type, val, ipset_types,
+                         FW3_IPSET_TYPE_IP, FW3_IPSET_TYPE_SET);
+}
+
+bool
+fw3_parse_date(void *ptr, const char *val)
+{
+       unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
+       struct tm tm = { 0 };
+       char *p;
+
+       year = strtoul(val, &p, 10);
+       if ((*p != '-' && *p) || year < 1970 || year > 2038)
+               goto fail;
+       else if (!*p)
+               goto ret;
+
+       mon = strtoul(++p, &p, 10);
+       if ((*p != '-' && *p) || mon > 12)
+               goto fail;
+       else if (!*p)
+               goto ret;
+
+       day = strtoul(++p, &p, 10);
+       if ((*p != 'T' && *p) || day > 31)
+               goto fail;
+       else if (!*p)
+               goto ret;
+
+       hour = strtoul(++p, &p, 10);
+       if ((*p != ':' && *p) || hour > 23)
+               goto fail;
+       else if (!*p)
+               goto ret;
+
+       min = strtoul(++p, &p, 10);
+       if ((*p != ':' && *p) || min > 59)
+               goto fail;
+       else if (!*p)
+               goto ret;
+
+       sec = strtoul(++p, &p, 10);
+       if (*p || sec > 59)
+               goto fail;
+
+ret:
+       tm.tm_year = year - 1900;
+       tm.tm_mon  = mon - 1;
+       tm.tm_mday = day;
+       tm.tm_hour = hour;
+       tm.tm_min  = min;
+       tm.tm_sec  = sec;
+
+       if (mktime(&tm) >= 0)
        {
-               type->type = FW3_IPSET_TYPE_IP;
+               *((struct tm *)ptr) = tm;
                return true;
        }
-       else if (!strncmp(val, "port", strlen(val)))
+
+fail:
+       return false;
+}
+
+bool
+fw3_parse_time(void *ptr, const char *val)
+{
+       unsigned int hour = 0, min = 0, sec = 0;
+       char *p;
+
+       hour = strtoul(val, &p, 10);
+       if (*p != ':' || hour > 23)
+               goto fail;
+
+       min = strtoul(++p, &p, 10);
+       if ((*p != ':' && *p) || min > 59)
+               goto fail;
+       else if (!*p)
+               goto ret;
+
+       sec = strtoul(++p, &p, 10);
+       if (*p || sec > 59)
+               goto fail;
+
+ret:
+       *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
+       return true;
+
+fail:
+       return false;
+}
+
+bool
+fw3_parse_weekdays(void *ptr, const char *val)
+{
+       unsigned int w = 0;
+       char *p, *s;
+
+       if (*val == '!')
        {
-               type->type = FW3_IPSET_TYPE_PORT;
-               return true;
+               setbit(*(uint8_t *)ptr, 0);
+               while (isspace(*++val));
        }
-       else if (!strncmp(val, "mac", strlen(val)))
+
+       if (!(s = strdup(val)))
+               return false;
+
+       for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
        {
-               type->type = FW3_IPSET_TYPE_MAC;
-               return true;
+               if (!parse_enum(&w, p, weekdays, 1, 7))
+               {
+                       w = strtoul(p, &p, 10);
+
+                       if (*p || w < 1 || w > 7)
+                       {
+                               free(s);
+                               return false;
+                       }
+               }
+
+               setbit(*(uint8_t *)ptr, w);
        }
-       else if (!strncmp(val, "net", strlen(val)))
+
+       free(s);
+       return true;
+}
+
+bool
+fw3_parse_monthdays(void *ptr, const char *val)
+{
+       unsigned int d;
+       char *p, *s;
+
+       if (*val == '!')
        {
-               type->type = FW3_IPSET_TYPE_NET;
-               return true;
+               setbit(*(uint32_t *)ptr, 0);
+               while (isspace(*++val));
        }
-       else if (!strncmp(val, "set", strlen(val)))
+
+       if (!(s = strdup(val)))
+               return false;
+
+       for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
        {
-               type->type = FW3_IPSET_TYPE_SET;
-               return true;
+               d = strtoul(p, &p, 10);
+
+               if (*p || d < 1 || d > 31)
+               {
+                       free(s);
+                       return false;
+               }
+
+               setbit(*(uint32_t *)ptr, d);
        }
 
-       return false;
+       free(s);
+       return true;
+}
+
+bool
+fw3_parse_include_type(void *ptr, const char *val)
+{
+       return parse_enum(ptr, val, include_types,
+                         FW3_INC_TYPE_SCRIPT, FW3_INC_TYPE_RESTORE);
 }
 
 
 void
-fw3_parse_options(void *s,
-                  struct fw3_option *opts, int n,
+fw3_parse_options(void *s, const struct fw3_option *opts,
                   struct uci_section *section)
 {
-       int i;
-       char *p;
+       char *p, *v;
        bool known;
        struct uci_element *e, *l;
        struct uci_option *o;
-       struct fw3_option *opt;
+       const struct fw3_option *opt;
        struct list_head *item;
        struct list_head *dest;
 
@@ -499,11 +682,9 @@ fw3_parse_options(void *s,
                o = uci_to_option(e);
                known = false;
 
-               for (i = 0; i < n; i++)
+               for (opt = opts; opt->name; opt++)
                {
-                       opt = &opts[i];
-
-                       if (!opt->parse || !opt->name)
+                       if (!opt->parse)
                                continue;
 
                        if (strcmp(opt->name, e->name))
@@ -543,9 +724,15 @@ fw3_parse_options(void *s,
                        }
                        else
                        {
-                               if (!o->v.string)
+                               v = o->v.string;
+
+                               if (!v)
                                        continue;
 
+                               /* protocol "tcpudp" compatibility hack */
+                               if (opt->parse == fw3_parse_protocol && !strcmp(v, "tcpudp"))
+                                       v = strdup("tcp udp");
+
                                if (!opt->elem_size)
                                {
                                        if (!opt->parse((char *)s + opt->offset, o->v.string))
@@ -553,9 +740,7 @@ fw3_parse_options(void *s,
                                }
                                else
                                {
-                                       for (p = strtok(o->v.string, " \t");
-                                            p != NULL;
-                                            p = strtok(NULL, " \t"))
+                                       for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
                                        {
                                                item = malloc(opt->elem_size);
 
@@ -575,6 +760,9 @@ fw3_parse_options(void *s,
                                                list_add_tail(item, dest);
                                        }
                                }
+
+                               if (v != o->v.string)
+                                       free(v);
                        }
 
                        known = true;
@@ -602,20 +790,53 @@ fw3_format_src_dest(struct fw3_address *src, struct fw3_address *dest)
 {
        char s[INET6_ADDRSTRLEN];
 
+       if ((src && src->range) || (dest && dest->range))
+               fw3_pr(" -m iprange");
+
        if (src && src->set)
        {
-               inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-                         &src->address.v4, s, sizeof(s));
+               if (src->range)
+               {
+                       inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                                         &src->address.v4, s, sizeof(s));
+
+                       fw3_pr(" %s--src-range %s", src->invert ? "! " : "", s);
+
+                       inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                                         &src->address2.v4, s, sizeof(s));
 
-               fw3_pr(" %s-s %s/%u", src->invert ? "! " : "", s, src->mask);
+                       fw3_pr("-%s", s);
+               }
+               else
+               {
+                       inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                                         &src->address.v4, s, sizeof(s));
+
+                       fw3_pr(" %s-s %s/%u", src->invert ? "! " : "", s, src->mask);
+               }
        }
 
        if (dest && dest->set)
        {
-               inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-                         &dest->address.v4, s, sizeof(s));
+               if (dest->range)
+               {
+                       inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                                         &dest->address.v4, s, sizeof(s));
+
+                       fw3_pr(" %s--dst-range %s", dest->invert ? "! " : "", s);
+
+                       inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                                         &dest->address2.v4, s, sizeof(s));
+
+                       fw3_pr("-%s", s);
+               }
+               else
+               {
+                       inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                                         &dest->address.v4, s, sizeof(s));
 
-               fw3_pr(" %s-d %s/%u", dest->invert ? "! " : "", s, dest->mask);
+                       fw3_pr(" %s-d %s/%u", dest->invert ? "! " : "", s, dest->mask);
+               }
        }
 }
 
@@ -700,17 +921,11 @@ fw3_format_limit(struct fw3_limit *limit)
        if (!limit)
                return;
 
-       const char *units[] = {
-               [FW3_LIMIT_UNIT_SECOND] = "second",
-               [FW3_LIMIT_UNIT_MINUTE] = "minute",
-               [FW3_LIMIT_UNIT_HOUR]   = "hour",
-               [FW3_LIMIT_UNIT_DAY]    = "day",
-       };
-
        if (limit->rate > 0)
        {
                fw3_pr(" -m limit %s--limit %u/%s",
-                      limit->invert ? "! " : "", limit->rate, units[limit->unit]);
+                      limit->invert ? "! " : "",
+                      limit->rate, limit_units[limit->unit]);
 
                if (limit->burst > 0)
                        fw3_pr(" --limit-burst %u", limit->burst);
@@ -742,6 +957,84 @@ fw3_format_ipset(struct fw3_ipset *ipset, bool invert)
 }
 
 void
+fw3_format_time(struct fw3_time *time)
+{
+       int i;
+       struct tm empty = { 0 };
+       char buf[sizeof("9999-99-99T23:59:59\0")];
+       bool d1 = memcmp(&time->datestart, &empty, sizeof(empty));
+       bool d2 = memcmp(&time->datestop, &empty, sizeof(empty));
+       bool first;
+
+       if (!d1 && !d2 && !time->timestart && !time->timestop &&
+           !(time->monthdays & 0xFFFFFFFE) && !(time->weekdays & 0xFE))
+       {
+               return;
+       }
+
+       fw3_pr(" -m time");
+
+       if (time->utc)
+               fw3_pr(" --utc");
+
+       if (d1)
+       {
+               strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestart);
+               fw3_pr(" --datestart %s", buf);
+       }
+
+       if (d2)
+       {
+               strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestop);
+               fw3_pr(" --datestop %s", buf);
+       }
+
+       if (time->timestart)
+       {
+               fw3_pr(" --timestart %02d:%02d:%02d",
+                      time->timestart / 3600,
+                      time->timestart % 3600 / 60,
+                      time->timestart % 60);
+       }
+
+       if (time->timestop)
+       {
+               fw3_pr(" --timestop %02d:%02d:%02d",
+                      time->timestop / 3600,
+                      time->timestop % 3600 / 60,
+                      time->timestop % 60);
+       }
+
+       if (time->monthdays & 0xFFFFFFFE)
+       {
+               fw3_pr(" %s--monthdays", hasbit(time->monthdays, 0) ? "! " : "");
+
+               for (i = 1, first = true; i < 32; i++)
+               {
+                       if (hasbit(time->monthdays, i))
+                       {
+                               fw3_pr("%c%u", first ? ' ' : ',', i);
+                               first = false;
+                       }
+               }
+       }
+
+       if (time->weekdays & 0xFE)
+       {
+               fw3_pr(" %s--weekdays", hasbit(time->weekdays, 0) ? "! " : "");
+
+               for (i = 1, first = true; i < 8; i++)
+               {
+                       if (hasbit(time->weekdays, i))
+                       {
+                               fw3_pr("%c%u", first ? ' ' : ',', i);
+                               first = false;
+                       }
+               }
+       }
+}
+
+void
 __fw3_format_comment(const char *comment, ...)
 {
        va_list ap;