]> git.ipfire.org Git - thirdparty/ipset.git/commitdiff
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@netfilter.org>
Thu, 31 Oct 2019 14:11:09 +0000 (15:11 +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/libipset/args.h
include/libipset/data.h
include/libipset/linux_ip_set.h
kernel/include/uapi/linux/netfilter/ipset/ip_set.h
kernel/net/netfilter/ipset/ip_set_hash_netiface.c
lib/args.c
lib/data.c
lib/ipset_hash_netiface.c
src/ipset.8

index ce1425110d8a3e6af8871b400302cac32a69a40d..616cca5e6581d1881b9584f966f2cb13f3e3928a 100644 (file)
@@ -44,6 +44,7 @@ enum ipset_keywords {
        IPSET_ARG_FORCEADD,                     /* forceadd */
        IPSET_ARG_MARKMASK,                     /* markmask */
        IPSET_ARG_NOMATCH,                      /* nomatch */
+       IPSET_ARG_IFACE_WILDCARD,               /* interface wildcard match */
        /* Extensions */
        IPSET_ARG_TIMEOUT,                      /* timeout */
        IPSET_ARG_COUNTERS,                     /* counters */
index 97498479b5c08dce4b87bb73b7e94eeef1d3f9b6..851773ab80cfde5cf84ebe7e0b6299249a6c3dd6 100644 (file)
@@ -66,6 +66,7 @@ enum ipset_opt {
        IPSET_OPT_SKBMARK,
        IPSET_OPT_SKBPRIO,
        IPSET_OPT_SKBQUEUE,
+       IPSET_OPT_IFACE_WILDCARD,
        /* Internal options */
        IPSET_OPT_FLAGS = 48,   /* IPSET_FLAG_EXIST| */
        IPSET_OPT_CADT_FLAGS,   /* IPSET_FLAG_BEFORE| */
@@ -128,7 +129,8 @@ enum ipset_opt {
        | IPSET_FLAG(IPSET_OPT_ADT_COMMENT)\
        | IPSET_FLAG(IPSET_OPT_SKBMARK) \
        | IPSET_FLAG(IPSET_OPT_SKBPRIO) \
-       | IPSET_FLAG(IPSET_OPT_SKBQUEUE))
+       | IPSET_FLAG(IPSET_OPT_SKBQUEUE) \
+       | IPSET_FLAG(IPSET_OPT_IFACE_WILDCARD))
 
 struct ipset_data;
 
index 3cd151f022e4c25518879ce8ba658f6e6ac935cc..d2337f99b10afba3df8e440c5c04dac66c02bd1f 100644 (file)
@@ -204,6 +204,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 a89c5967fade2f0798b66c9deeb77fef2301c64b..d8ab718055557b15198122288f4340e1c4dcdaee 100644 (file)
@@ -204,6 +204,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 88da2f55e128e7c294d9f37670eba0cd48e3e33a..e4e70c6b1f562d9c7eccf8b88ea2dab0d362eecb 100644 (file)
@@ -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 <kadlec@netfilter.org>");
@@ -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 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 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);
index 204c54412a13ee5a68a50487aef84a722dfbf770..c25bb8012083852c9e4798ee82f5c05889289b22 100644 (file)
@@ -195,6 +195,14 @@ static const struct ipset_arg ipset_args[] = {
                .print = ipset_print_flag,
                .help = "[nomatch]",
        },
+       [IPSET_ARG_IFACE_WILDCARD] = {
+               .name = { "wildcard", NULL },
+               .has_arg = IPSET_NO_ARG,
+               .opt = IPSET_OPT_IFACE_WILDCARD,
+               .parse = ipset_parse_flag,
+               .print = ipset_print_flag,
+               .help = "[wildcard]",
+       },
        /* Extensions */
        [IPSET_ARG_TIMEOUT] = {
                .name = { "timeout", NULL },
index 47c9ddb10a381e0514f7896da5c8d444649d969e..f28d1d333d0d2cfbf4ebe476595f4633879fbbaf 100644 (file)
@@ -410,6 +410,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
        case IPSET_OPT_NOMATCH:
                cadt_flag_type_attr(data, opt, IPSET_FLAG_NOMATCH);
                break;
+       case IPSET_OPT_IFACE_WILDCARD:
+               cadt_flag_type_attr(data, opt, IPSET_FLAG_IFACE_WILDCARD);
+               break;
        case IPSET_OPT_FLAGS:
                data->flags = *(const uint32_t *)value;
                break;
@@ -433,6 +436,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
                if (data->cadt_flags & IPSET_FLAG_WITH_SKBINFO)
                        ipset_data_flags_set(data,
                                             IPSET_FLAG(IPSET_OPT_SKBINFO));
+               if (data->cadt_flags & IPSET_FLAG_IFACE_WILDCARD)
+                       ipset_data_flags_set(data,
+                                            IPSET_FLAG(IPSET_OPT_IFACE_WILDCARD));
                break;
        default:
                return -1;
@@ -564,6 +570,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
        case IPSET_OPT_CREATE_COMMENT:
        case IPSET_OPT_FORCEADD:
        case IPSET_OPT_SKBINFO:
+       case IPSET_OPT_IFACE_WILDCARD:
                return &data->cadt_flags;
        default:
                return NULL;
@@ -630,6 +637,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
        case IPSET_OPT_NOMATCH:
        case IPSET_OPT_COUNTERS:
        case IPSET_OPT_FORCEADD:
+       case IPSET_OPT_IFACE_WILDCARD:
                return sizeof(uint32_t);
        case IPSET_OPT_ADT_COMMENT:
                return IPSET_MAX_COMMENT_SIZE + 1;
index a7098164d285f952a82aaa2df1ccf13d0aefda6b..67557825b92520d0ce8e323d4834737c8b14f22d 100644 (file)
@@ -619,6 +619,100 @@ static struct ipset_type ipset_hash_netiface6 = {
                 "      Adding/deleting multiple elements with IPv4 is supported.",
        .description = "skbinfo support",
 };
+/* interface wildcard support */
+static struct ipset_type ipset_hash_netiface7 = {
+       .name = "hash:net,iface",
+       .alias = { "netifacehash", NULL },
+       .revision = 7,
+       .family = NFPROTO_IPSET_IPV46,
+       .dimension = IPSET_DIM_TWO,
+       .elem = {
+               [IPSET_DIM_ONE - 1] = {
+                       .parse = ipset_parse_ip4_net6,
+                       .print = ipset_print_ip,
+                       .opt = IPSET_OPT_IP
+               },
+               [IPSET_DIM_TWO - 1] = {
+                       .parse = ipset_parse_iface,
+                       .print = ipset_print_iface,
+                       .opt = IPSET_OPT_IFACE
+               },
+       },
+       .cmd = {
+               [IPSET_CREATE] = {
+                       .args = {
+                               IPSET_ARG_FAMILY,
+                               /* Aliases */
+                               IPSET_ARG_INET,
+                               IPSET_ARG_INET6,
+                               IPSET_ARG_HASHSIZE,
+                               IPSET_ARG_MAXELEM,
+                               IPSET_ARG_TIMEOUT,
+                               IPSET_ARG_COUNTERS,
+                               IPSET_ARG_COMMENT,
+                               IPSET_ARG_FORCEADD,
+                               IPSET_ARG_SKBINFO,
+                               IPSET_ARG_NONE,
+                       },
+                       .need = 0,
+                       .full = 0,
+                       .help = "",
+               },
+               [IPSET_ADD] = {
+                       .args = {
+                               IPSET_ARG_TIMEOUT,
+                               IPSET_ARG_NOMATCH,
+                               IPSET_ARG_IFACE_WILDCARD,
+                               IPSET_ARG_PACKETS,
+                               IPSET_ARG_BYTES,
+                               IPSET_ARG_ADT_COMMENT,
+                               IPSET_ARG_SKBMARK,
+                               IPSET_ARG_SKBPRIO,
+                               IPSET_ARG_SKBQUEUE,
+                               IPSET_ARG_NONE,
+                       },
+                       .need = IPSET_FLAG(IPSET_OPT_IP)
+                               | IPSET_FLAG(IPSET_OPT_IFACE),
+                       .full = IPSET_FLAG(IPSET_OPT_IP)
+                               | IPSET_FLAG(IPSET_OPT_CIDR)
+                               | IPSET_FLAG(IPSET_OPT_IP_TO)
+                               | IPSET_FLAG(IPSET_OPT_IFACE)
+                               | IPSET_FLAG(IPSET_OPT_PHYSDEV),
+                       .help = "IP[/CIDR]|FROM-TO,[physdev:]IFACE",
+               },
+               [IPSET_DEL] = {
+                       .args = {
+                               IPSET_ARG_NONE,
+                       },
+                       .need = IPSET_FLAG(IPSET_OPT_IP)
+                               | IPSET_FLAG(IPSET_OPT_IFACE),
+                       .full = IPSET_FLAG(IPSET_OPT_IP)
+                               | IPSET_FLAG(IPSET_OPT_CIDR)
+                               | IPSET_FLAG(IPSET_OPT_IP_TO)
+                               | IPSET_FLAG(IPSET_OPT_IFACE)
+                               | IPSET_FLAG(IPSET_OPT_PHYSDEV),
+                       .help = "IP[/CIDR]|FROM-TO,[physdev:]IFACE",
+               },
+               [IPSET_TEST] = {
+                       .args = {
+                               IPSET_ARG_NOMATCH,
+                               IPSET_ARG_NONE,
+                       },
+                       .need = IPSET_FLAG(IPSET_OPT_IP)
+                               | IPSET_FLAG(IPSET_OPT_IFACE),
+                       .full = IPSET_FLAG(IPSET_OPT_IP)
+                               | IPSET_FLAG(IPSET_OPT_CIDR)
+                               | IPSET_FLAG(IPSET_OPT_IFACE)
+                               | IPSET_FLAG(IPSET_OPT_PHYSDEV),
+                       .help = "IP[/CIDR],[physdev:]IFACE",
+               },
+       },
+       .usage = "where depending on the INET family\n"
+                "      IP is a valid IPv4 or IPv6 address (or hostname),\n"
+                "      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+                "      Adding/deleting multiple elements with IPv4 is supported.",
+       .description = "skbinfo and wildcard support",
+};
 
 void _init(void);
 void _init(void)
@@ -630,4 +724,5 @@ void _init(void)
        ipset_type_add(&ipset_hash_netiface4);
        ipset_type_add(&ipset_hash_netiface5);
        ipset_type_add(&ipset_hash_netiface6);
+       ipset_type_add(&ipset_hash_netiface7);
 }
index 9c128895bbc610e144925163d43b292b3772d126..f1a1368e7680672a756d0fbdad704f24bdc3bf7b 100644 (file)
@@ -385,6 +385,12 @@ succeed and evict a random entry from the set.
 .IP
 ipset create foo hash:ip forceadd
 .PP
+.SS wildcard
+This flag is valid when adding elements to a \fBhash:net,iface\fR set. If the
+flag is set, then prefix matching is used when comparing with this element. For
+example, an element containing the interface name "eth" will match any name with
+that prefix.
+.PP
 .SH "SET TYPES"
 .SS bitmap:ip
 The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host
@@ -957,7 +963,7 @@ address and interface name pairs.
 .PP
 \fIADD\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
 .PP
-\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]  [ \fBnomatch\fR ] [ \fBpackets\fR \fIvalue\fR ] [ \fBbytes\fR \fIvalue\fR ] [ \fBcomment\fR \fIstring\fR ] [ \fBskbmark\fR \fIvalue\fR ] [ \fBskbprio\fR \fIvalue\fR ] [ \fBskbqueue\fR \fIvalue\fR ]
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]  [ \fBnomatch\fR ] [ \fBpackets\fR \fIvalue\fR ] [ \fBbytes\fR \fIvalue\fR ] [ \fBcomment\fR \fIstring\fR ] [ \fBskbmark\fR \fIvalue\fR ] [ \fBskbprio\fR \fIvalue\fR ] [ \fBskbqueue\fR \fIvalue\fR ] [ \fBwildcard\fR ]
 .PP
 \fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
 .PP