Add support for fwmark matches and targets
authorJo-Philipp Wich <jow@openwrt.org>
Fri, 5 Apr 2013 14:02:31 +0000 (16:02 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Tue, 9 Apr 2013 13:37:42 +0000 (15:37 +0200)
defaults.c
options.c
options.h
redirects.c
rules.c

index 10b1163..b5a94e6 100644 (file)
@@ -38,6 +38,8 @@ static const struct fw3_rule_spec default_chains[] = {
        C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_rule"),
 
        C(ANY, MANGLE, UNSPEC,        "mssfix"),
+       C(ANY, MANGLE, UNSPEC,        "fwmark"),
+
        C(ANY, RAW,    UNSPEC,        "notrack"),
 
        { }
@@ -52,6 +54,8 @@ static const struct fw3_rule_spec toplevel_rules[] = {
        C(V4,  NAT,    UNSPEC,        "POSTROUTING -j delegate_postrouting"),
 
        C(ANY, MANGLE, UNSPEC,        "FORWARD -j mssfix"),
+       C(ANY, MANGLE, UNSPEC,        "PREROUTING -j fwmark"),
+
        C(ANY, RAW,    UNSPEC,        "PREROUTING -j notrack"),
 
        { }
index 031de83..1f561d3 100644 (file)
--- a/options.c
+++ b/options.c
@@ -75,6 +75,7 @@ const char *fw3_flag_names[__FW3_FLAG_MAX] = {
        "REJECT",
        "DROP",
        "NOTRACK",
+       "MARK",
        "DNAT",
        "SNAT",
 
@@ -754,6 +755,44 @@ fw3_parse_reflection_source(void *ptr, const char *val, bool is_list)
                          FW3_REFLECTION_INTERNAL, FW3_REFLECTION_EXTERNAL);
 }
 
+bool
+fw3_parse_mark(void *ptr, const char *val, bool is_list)
+{
+       uint32_t n;
+       char *s, *e;
+       struct fw3_mark *m = ptr;
+
+       if (*val == '!')
+       {
+               m->invert = true;
+               while (isspace(*++val));
+       }
+
+       if ((s = strchr(val, '/')) != NULL)
+               *s++ = 0;
+
+       n = strtoul(val, &e, 0);
+
+       if (e == val || *e)
+               return false;
+
+       m->mark = n;
+       m->mask = 0xFFFFFFFF;
+
+       if (s)
+       {
+               n = strtoul(s, &e, 0);
+
+               if (e == s || *e)
+                       return false;
+
+               m->mask = n;
+       }
+
+       m->set = true;
+       return true;
+}
+
 
 void
 fw3_parse_options(void *s, const struct fw3_option *opts,
@@ -1099,6 +1138,18 @@ fw3_format_time(struct fw3_time *time)
 }
 
 void
+fw3_format_mark(struct fw3_mark *mark)
+{
+       if (!mark->set)
+               return;
+
+       fw3_pr(" -m mark %s--mark 0x%x", mark->invert ? "! " : "", mark->mark);
+
+       if (mark->mask < 0xFFFFFFFF)
+               fw3_pr("/0x%x", mark->mask);
+}
+
+void
 __fw3_format_comment(const char *comment, ...)
 {
        va_list ap;
index dd86d05..3fb7a9c 100644 (file)
--- a/options.h
+++ b/options.h
@@ -68,17 +68,18 @@ enum fw3_flag
        FW3_FLAG_REJECT        = 7,
        FW3_FLAG_DROP          = 8,
        FW3_FLAG_NOTRACK       = 9,
-       FW3_FLAG_DNAT          = 10,
-       FW3_FLAG_SNAT          = 11,
-       FW3_FLAG_SRC_ACCEPT    = 12,
-       FW3_FLAG_SRC_REJECT    = 13,
-       FW3_FLAG_SRC_DROP      = 14,
-       FW3_FLAG_CUSTOM_CHAINS = 15,
-       FW3_FLAG_SYN_FLOOD     = 16,
-       FW3_FLAG_MTU_FIX       = 17,
-       FW3_FLAG_DROP_INVALID  = 18,
-       FW3_FLAG_HOTPLUG       = 19,
-       FW3_FLAG_DELETED       = 20,
+       FW3_FLAG_MARK          = 10,
+       FW3_FLAG_DNAT          = 11,
+       FW3_FLAG_SNAT          = 12,
+       FW3_FLAG_SRC_ACCEPT    = 13,
+       FW3_FLAG_SRC_REJECT    = 14,
+       FW3_FLAG_SRC_DROP      = 15,
+       FW3_FLAG_CUSTOM_CHAINS = 16,
+       FW3_FLAG_SYN_FLOOD     = 17,
+       FW3_FLAG_MTU_FIX       = 18,
+       FW3_FLAG_DROP_INVALID  = 19,
+       FW3_FLAG_HOTPLUG       = 20,
+       FW3_FLAG_DELETED       = 21,
 
        __FW3_FLAG_MAX
 };
@@ -224,6 +225,14 @@ struct fw3_time
        uint8_t weekdays;   /* bit 0 is invert + 1 .. 7 */
 };
 
+struct fw3_mark
+{
+       bool set;
+       bool invert;
+       uint32_t mark;
+       uint32_t mask;
+};
+
 struct fw3_defaults
 {
        enum fw3_flag policy_input;
@@ -319,8 +328,11 @@ struct fw3_rule
 
        struct fw3_limit limit;
        struct fw3_time time;
+       struct fw3_mark mark;
 
        enum fw3_flag target;
+       struct fw3_mark set_mark;
+       struct fw3_mark set_xmark;
 
        const char *extra;
 };
@@ -356,6 +368,7 @@ struct fw3_redirect
        struct fw3_port port_redir;
 
        struct fw3_time time;
+       struct fw3_mark mark;
 
        enum fw3_flag target;
 
@@ -480,6 +493,7 @@ bool fw3_parse_date(void *ptr, const char *val, bool is_list);
 bool fw3_parse_time(void *ptr, const char *val, bool is_list);
 bool fw3_parse_weekdays(void *ptr, const char *val, bool is_list);
 bool fw3_parse_monthdays(void *ptr, const char *val, bool is_list);
+bool fw3_parse_mark(void *ptr, const char *val, bool is_list);
 
 void fw3_parse_options(void *s, const struct fw3_option *opts,
                        struct uci_section *section);
@@ -493,6 +507,7 @@ void fw3_format_icmptype(struct fw3_icmptype *icmp, enum fw3_family family);
 void fw3_format_limit(struct fw3_limit *limit);
 void fw3_format_ipset(struct fw3_ipset *ipset, bool invert);
 void fw3_format_time(struct fw3_time *time);
+void fw3_format_mark(struct fw3_mark *mark);
 
 void __fw3_format_comment(const char *comment, ...);
 #define fw3_format_comment(...) __fw3_format_comment(__VA_ARGS__, NULL)
index 7817e9b..0a13d85 100644 (file)
@@ -52,6 +52,8 @@ const struct fw3_option fw3_redirect_opts[] = {
        FW3_OPT("weekdays",            weekdays,  redirect,     time.weekdays),
        FW3_OPT("monthdays",           monthdays, redirect,     time.monthdays),
 
+       FW3_OPT("mark",                mark,      redirect,     mark),
+
        FW3_OPT("reflection",          bool,      redirect,     reflection),
        FW3_OPT("reflection_src",      reflection_source,
                                                  redirect,     reflection_src),
@@ -395,6 +397,7 @@ print_redirect(struct fw3_state *state, enum fw3_family family,
 
                        fw3_format_mac(mac);
                        fw3_format_time(&redir->time);
+                       fw3_format_mark(&redir->mark);
                        fw3_format_extra(redir->extra);
                        fw3_format_comment(redir->name);
                        print_target_nat(redir);
@@ -408,6 +411,7 @@ print_redirect(struct fw3_state *state, enum fw3_family family,
                        fw3_format_sport_dport(&redir->port_src, &redir->port_redir);
                        fw3_format_mac(mac);
                        fw3_format_time(&redir->time);
+                       fw3_format_mark(&redir->mark);
                        fw3_format_extra(redir->extra);
                        fw3_format_comment(redir->name);
                        print_target_filter(redir);
diff --git a/rules.c b/rules.c
index 1037d2f..287ad90 100644 (file)
--- a/rules.c
+++ b/rules.c
@@ -53,6 +53,10 @@ const struct fw3_option fw3_rule_opts[] = {
        FW3_OPT("weekdays",            weekdays,  rule,     time.weekdays),
        FW3_OPT("monthdays",           monthdays, rule,     time.monthdays),
 
+       FW3_OPT("mark",                mark,      rule,     mark),
+       FW3_OPT("set_mark",            mark,      rule,     set_mark),
+       FW3_OPT("set_xmark",           mark,      rule,     set_xmark),
+
        FW3_OPT("target",              target,    rule,     target),
 
        { }
@@ -144,6 +148,29 @@ fw3_load_rules(struct fw3_state *state, struct uci_package *p)
                        continue;
                }
 
+               if (!rule->set_mark.set && !rule->set_xmark.set &&
+                   rule->target == FW3_FLAG_MARK)
+               {
+                       warn_elem(e, "is set to target MARK but specifies neither "
+                                    "'set_mark' nor 'set_xmark' option");
+                       fw3_free_rule(rule);
+                       continue;
+               }
+
+               if (rule->_dest && rule->target == FW3_FLAG_MARK)
+               {
+                       warn_elem(e, "must not specify 'dest' for MARK target");
+                       fw3_free_rule(rule);
+                       continue;
+               }
+
+               if (rule->set_mark.invert || rule->set_xmark.invert)
+               {
+                       warn_elem(e, "must not have inverted 'set_mark' or 'set_xmark'");
+                       fw3_free_rule(rule);
+                       continue;
+               }
+
                if (!rule->_src && !rule->_dest && !rule->src.any && !rule->dest.any)
                {
                        warn_elem(e, "has neither a source nor a destination zone assigned "
@@ -161,7 +188,7 @@ fw3_load_rules(struct fw3_state *state, struct uci_package *p)
                        warn_elem(e, "has no target specified, defaulting to REJECT");
                        rule->target = FW3_FLAG_REJECT;
                }
-               else if (rule->target > FW3_FLAG_NOTRACK)
+               else if (rule->target > FW3_FLAG_MARK)
                {
                        warn_elem(e, "has invalid target specified, defaulting to REJECT");
                        rule->target = FW3_FLAG_REJECT;
@@ -191,6 +218,10 @@ print_chain(struct fw3_rule *rule)
        {
                sprintf(chain, "zone_%s_notrack", rule->src.name);
        }
+       else if (rule->target == FW3_FLAG_MARK)
+       {
+               sprintf(chain, "fwmark");
+       }
        else
        {
                if (rule->src.set)
@@ -224,6 +255,15 @@ static void print_target(struct fw3_rule *rule)
 
        switch(rule->target)
        {
+       case FW3_FLAG_MARK:
+               if (rule->set_mark.set)
+                       fw3_pr(" -j MARK --set-mark 0x%x/0x%x\n",
+                              rule->set_mark.mark, rule->set_mark.mask);
+               else
+                       fw3_pr(" -j MARK --set-xmark 0x%x/0x%x\n",
+                              rule->set_xmark.mark, rule->set_xmark.mask);
+               return;
+
        case FW3_FLAG_ACCEPT:
        case FW3_FLAG_DROP:
        case FW3_FLAG_NOTRACK:
@@ -272,6 +312,7 @@ print_rule(struct fw3_state *state, enum fw3_family family,
        fw3_format_mac(mac);
        fw3_format_limit(&rule->limit);
        fw3_format_time(&rule->time);
+       fw3_format_mark(&rule->mark);
        fw3_format_extra(rule->extra);
        fw3_format_comment(rule->name);
        print_target(rule);
@@ -299,8 +340,9 @@ expand_rule(struct fw3_state *state, enum fw3_family family,
        if (!fw3_is_family(rule, family))
                return;
 
-       if ((table == FW3_TABLE_RAW && rule->target != FW3_FLAG_NOTRACK) ||
-           (table != FW3_TABLE_FILTER))
+       if ((rule->target == FW3_FLAG_NOTRACK && table != FW3_TABLE_RAW) ||
+           (rule->target == FW3_FLAG_MARK && table != FW3_TABLE_MANGLE) ||
+               (rule->target < FW3_FLAG_NOTRACK && table != FW3_TABLE_FILTER))
                return;
 
        if (rule->name)