@@ -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 */
@@ -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;
@@ -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,
};
@@ -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,
};
@@ -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 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);
@@ -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 },
@@ -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;
@@ -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);
}
@@ -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
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> --- include/libipset/args.h | 1 + include/libipset/data.h | 4 +- include/libipset/linux_ip_set.h | 2 + .../uapi/linux/netfilter/ipset/ip_set.h | 2 + .../netfilter/ipset/ip_set_hash_netiface.c | 23 ++++- lib/args.c | 8 ++ lib/data.c | 8 ++ lib/ipset_hash_netiface.c | 95 +++++++++++++++++++ src/ipset.8 | 8 +- 9 files changed, 144 insertions(+), 7 deletions(-)