netfilter: ipset: Add wildcard support to net,iface
authorKristian Evensen <kristian.evensen@gmail.com>
Thu, 26 Sep 2019 10:06:45 +0000 (12:06 +0200)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Mon, 4 Nov 2019 19:44:17 +0000 (20:44 +0100)
The net,iface equal functions currently compares the full interface
names. In several cases, wildcard (or prefix) matching is useful. For
example, when converting a large iptables rule-set to make use of ipset,
I was able to significantly reduce the number of set elements by making
use of wildcard matching.

Wildcard matching is enabled by adding "wildcard" when adding an element
to a set. Internally, this causes the IPSET_FLAG_IFACE_WILDCARD-flag to
be set.  When this flag is set, only the initial part of the interface
name is used for comparison.

Wildcard matching is done per element and not per set, as there are many
cases where mixing wildcard and non-wildcard elements are useful. This
means that is up to the user to handle (avoid) overlapping interface
names.

Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
include/uapi/linux/netfilter/ipset/ip_set.h
net/netfilter/ipset/ip_set_hash_netiface.c

index eea166c52c365a43edd654fab41bf9f9201842df..11a72a938eb1fa5400d52f217aef249584dcac66 100644 (file)
@@ -205,6 +205,8 @@ enum ipset_cadt_flags {
        IPSET_FLAG_WITH_FORCEADD = (1 << IPSET_FLAG_BIT_WITH_FORCEADD),
        IPSET_FLAG_BIT_WITH_SKBINFO = 6,
        IPSET_FLAG_WITH_SKBINFO = (1 << IPSET_FLAG_BIT_WITH_SKBINFO),
+       IPSET_FLAG_BIT_IFACE_WILDCARD = 7,
+       IPSET_FLAG_IFACE_WILDCARD = (1 << IPSET_FLAG_BIT_IFACE_WILDCARD),
        IPSET_FLAG_CADT_MAX     = 15,
 };
 
index 1a04e092973833f09a70b13aa7a2eff339ef06e6..be5e95a0d8762fb3381650aba7831bdc69eae934 100644 (file)
@@ -25,7 +25,8 @@
 /*                             3    Counters support added */
 /*                             4    Comments support added */
 /*                             5    Forceadd support added */
-#define IPSET_TYPE_REV_MAX     6 /* skbinfo support added */
+/*                             6    skbinfo support added */
+#define IPSET_TYPE_REV_MAX     7 /* interface wildcard support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
@@ -57,6 +58,7 @@ struct hash_netiface4_elem {
        u8 cidr;
        u8 nomatch;
        u8 elem;
+       u8 wildcard;
        char iface[IFNAMSIZ];
 };
 
@@ -71,7 +73,9 @@ hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
               ip1->cidr == ip2->cidr &&
               (++*multi) &&
               ip1->physdev == ip2->physdev &&
-              strcmp(ip1->iface, ip2->iface) == 0;
+              (ip1->wildcard ?
+               strncmp(ip1->iface, ip2->iface, strlen(ip1->iface)) == 0 :
+               strcmp(ip1->iface, ip2->iface) == 0);
 }
 
 static int
@@ -103,7 +107,8 @@ static bool
 hash_netiface4_data_list(struct sk_buff *skb,
                         const struct hash_netiface4_elem *data)
 {
-       u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
+       u32 flags = (data->physdev ? IPSET_FLAG_PHYSDEV : 0) |
+                   (data->wildcard ? IPSET_FLAG_IFACE_WILDCARD : 0);
 
        if (data->nomatch)
                flags |= IPSET_FLAG_NOMATCH;
@@ -229,6 +234,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
                        e.physdev = 1;
                if (cadt_flags & IPSET_FLAG_NOMATCH)
                        flags |= (IPSET_FLAG_NOMATCH << 16);
+               if (cadt_flags & IPSET_FLAG_IFACE_WILDCARD)
+                       e.wildcard = 1;
        }
        if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
                e.ip = htonl(ip & ip_set_hostmask(e.cidr));
@@ -280,6 +287,7 @@ struct hash_netiface6_elem {
        u8 cidr;
        u8 nomatch;
        u8 elem;
+       u8 wildcard;
        char iface[IFNAMSIZ];
 };
 
@@ -294,7 +302,9 @@ hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
               ip1->cidr == ip2->cidr &&
               (++*multi) &&
               ip1->physdev == ip2->physdev &&
-              strcmp(ip1->iface, ip2->iface) == 0;
+              (ip1->wildcard ?
+               strncmp(ip1->iface, ip2->iface, strlen(ip1->iface)) == 0 :
+               strcmp(ip1->iface, ip2->iface) == 0);
 }
 
 static int
@@ -326,7 +336,8 @@ static bool
 hash_netiface6_data_list(struct sk_buff *skb,
                         const struct hash_netiface6_elem *data)
 {
-       u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
+       u32 flags = (data->physdev ? IPSET_FLAG_PHYSDEV : 0) |
+                   (data->wildcard ? IPSET_FLAG_IFACE_WILDCARD : 0);
 
        if (data->nomatch)
                flags |= IPSET_FLAG_NOMATCH;
@@ -440,6 +451,8 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
                        e.physdev = 1;
                if (cadt_flags & IPSET_FLAG_NOMATCH)
                        flags |= (IPSET_FLAG_NOMATCH << 16);
+               if (cadt_flags & IPSET_FLAG_IFACE_WILDCARD)
+                       e.wildcard = 1;
        }
 
        ret = adtfn(set, &e, &ext, &ext, flags);