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 setting the
IPSET_FLAG_IFACE_WILDCARD-flag when adding an element.  When this flag
is set, only the initial part of the interface name of the set element
is used for comparison.

I am submitting this change as an RFC, as I am not sure if my approach
with using a flag (or wildcard matching at all) is OK. Please note that
this patch is against kernel 4.14, as that is what my current devices
are running. A final submission will be against net-next.

I will send my changes to the ipset-user space utility/library in a
follow-up email.

Signed-off-by: Kristian Evensen <kristian.even...@gmail.com>
---
 include/uapi/linux/netfilter/ipset/ip_set.h |  2 ++
 net/netfilter/ipset/ip_set_hash_netiface.c  | 23 ++++++++++++++++-----
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h 
b/include/uapi/linux/netfilter/ipset/ip_set.h
index 60236f694..71d6de524 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -201,6 +201,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,
 };
 
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c 
b/net/netfilter/ipset/ip_set_hash_netiface.c
index f5164c1ef..8ac0757c2 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -29,7 +29,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 <kad...@blackhole.kfki.hu>");
@@ -61,6 +62,7 @@ struct hash_netiface4_elem {
        u8 cidr;
        u8 nomatch;
        u8 elem;
+       u8 wildcard;
        char iface[IFNAMSIZ];
 };
 
@@ -75,7 +77,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 inline int
@@ -107,7 +111,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;
@@ -233,6 +238,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));
@@ -284,6 +291,7 @@ struct hash_netiface6_elem {
        u8 cidr;
        u8 nomatch;
        u8 elem;
+       u8 wildcard;
        char iface[IFNAMSIZ];
 };
 
@@ -298,7 +306,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 inline int
@@ -330,7 +340,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;
@@ -444,6 +455,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);
-- 
2.20.1

Reply via email to