From b60b8e0e3a1dfbe664d693512d978e2cd1880704 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 10 Sep 2012 21:22:23 +0200 Subject: Add /0 network support to hash:net,iface type Now it is possible to setup a single hash:net,iface type of set and a single ip6?tables match which covers all egress/ingress filtering. --- kernel/net/netfilter/ipset/ip_set_hash_netiface.c | 44 ++++++++--------- lib/ipset_hash_netiface.c | 58 +++++++++++++++++++++++ src/ipset.8 | 3 +- tests/hash:net,iface.t | 42 ++++++++++++++-- 4 files changed, 117 insertions(+), 30 deletions(-) diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c index 632693f..e7c671d 100644 --- a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c @@ -140,7 +140,7 @@ struct hash_netiface4_elem_hashed { u8 physdev; u8 cidr; u8 nomatch; - u8 padding; + u8 elem; }; #define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed) @@ -151,7 +151,7 @@ struct hash_netiface4_elem { u8 physdev; u8 cidr; u8 nomatch; - u8 padding; + u8 elem; const char *iface; }; @@ -161,7 +161,7 @@ struct hash_netiface4_telem { u8 physdev; u8 cidr; u8 nomatch; - u8 padding; + u8 elem; const char *iface; unsigned long timeout; }; @@ -181,18 +181,14 @@ hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1, static inline bool hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem) { - return elem->cidr == 0; + return elem->elem == 0; } static inline void hash_netiface4_data_copy(struct hash_netiface4_elem *dst, const struct hash_netiface4_elem *src) { - dst->ip = src->ip; - dst->cidr = src->cidr; - dst->physdev = src->physdev; - dst->iface = src->iface; - dst->nomatch = src->nomatch; + memcpy(dst, src, sizeof(*dst)); } static inline void @@ -217,7 +213,7 @@ hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr) static inline void hash_netiface4_data_zero_out(struct hash_netiface4_elem *elem) { - elem->cidr = 0; + elem->elem = 0; } static bool @@ -288,7 +284,8 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem data = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, + .elem = 1, }; int ret; @@ -339,7 +336,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], { struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netiface4_elem data = { .cidr = HOST_MASK }; + struct hash_netiface4_elem data = { .cidr = HOST_MASK, .elem = 1 }; u32 ip = 0, ip_to, last; u32 timeout = h->timeout; char iface[IFNAMSIZ]; @@ -360,7 +357,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_CIDR]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr || data.cidr > HOST_MASK) + if (data.cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; } @@ -389,7 +386,6 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH)) flags |= (cadt_flags << 16); } - if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { data.ip = htonl(ip & ip_set_hostmask(data.cidr)); ret = adtfn(set, &data, timeout, flags); @@ -442,7 +438,7 @@ struct hash_netiface6_elem_hashed { u8 physdev; u8 cidr; u8 nomatch; - u8 padding; + u8 elem; }; #define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed) @@ -452,7 +448,7 @@ struct hash_netiface6_elem { u8 physdev; u8 cidr; u8 nomatch; - u8 padding; + u8 elem; const char *iface; }; @@ -461,7 +457,7 @@ struct hash_netiface6_telem { u8 physdev; u8 cidr; u8 nomatch; - u8 padding; + u8 elem; const char *iface; unsigned long timeout; }; @@ -481,7 +477,7 @@ hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1, static inline bool hash_netiface6_data_isnull(const struct hash_netiface6_elem *elem) { - return elem->cidr == 0; + return elem->elem == 0; } static inline void @@ -506,7 +502,7 @@ hash_netiface6_data_match(const struct hash_netiface6_elem *elem) static inline void hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem) { - elem->cidr = 0; + elem->elem = 0; } static inline void @@ -590,7 +586,8 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem data = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, + .elem = 1, }; int ret; @@ -637,7 +634,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], { struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netiface6_elem data = { .cidr = HOST_MASK }; + struct hash_netiface6_elem data = { .cidr = HOST_MASK, .elem = 1 }; u32 timeout = h->timeout; char iface[IFNAMSIZ]; int ret; @@ -659,7 +656,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_CIDR]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr || data.cidr > HOST_MASK) + if (data.cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; ip6_netmask(&data.ip, data.cidr); @@ -777,7 +774,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = { .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, .revision_min = 0, - .revision_max = 1, /* nomatch flag support added */ + /* = 1, nomatch flag support added */ + .revision_max = 2, /* /0 support added */ .create = hash_netiface_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/lib/ipset_hash_netiface.c b/lib/ipset_hash_netiface.c index bc6aa3d..298ce2a 100644 --- a/lib/ipset_hash_netiface.c +++ b/lib/ipset_hash_netiface.c @@ -200,9 +200,67 @@ static struct ipset_type ipset_hash_netiface1 = { .usage = hash_netiface1_usage, }; +static struct ipset_type ipset_hash_netiface2 = { + .name = "hash:net,iface", + .alias = { "netifacehash", NULL }, + .revision = 2, + .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 + }, + }, + .args = { + [IPSET_CREATE] = hash_netiface_create_args, + [IPSET_ADD] = hash_netiface1_add_args, + }, + .mandatory = { + [IPSET_CREATE] = 0, + [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IFACE), + [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IFACE), + [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP) + | IPSET_FLAG(IPSET_OPT_IFACE), + }, + .full = { + [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE) + | IPSET_FLAG(IPSET_OPT_MAXELEM) + | IPSET_FLAG(IPSET_OPT_TIMEOUT), + [IPSET_ADD] = 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) + | IPSET_FLAG(IPSET_OPT_TIMEOUT) + | IPSET_FLAG(IPSET_OPT_NOMATCH), + [IPSET_DEL] = 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), + [IPSET_TEST] = 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), + }, + + .usage = hash_netiface1_usage, +}; + void _init(void); void _init(void) { ipset_type_add(&ipset_hash_netiface0); ipset_type_add(&ipset_hash_netiface1); + ipset_type_add(&ipset_hash_netiface2); } diff --git a/src/ipset.8 b/src/ipset.8 index bbad680..3a5a5db 100644 --- a/src/ipset.8 +++ b/src/ipset.8 @@ -751,8 +751,7 @@ ipset add foo 192.168.2,25,10.1.0.0/16 ipset test foo 192.168.1,80.10.0.0/24 .SS hash:net,iface The \fBhash:net,iface\fR set type uses a hash to store different sized IP network -address and interface name pairs. Network address with zero prefix size is not -accepted. +address and interface name pairs. .PP \fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ] .PP diff --git a/tests/hash:net,iface.t b/tests/hash:net,iface.t index 0ae4d4e..779d77f 100644 --- a/tests/hash:net,iface.t +++ b/tests/hash:net,iface.t @@ -1,13 +1,17 @@ # Create a set 0 ipset create test hash:net,iface hashsize 128 # Add zero valued element -1 ipset add test 0.0.0.0/0,eth0 +0 ipset add test 0.0.0.0/0,eth0 # Test zero valued element -1 ipset test test 0.0.0.0/0,eth0 +0 ipset test test 0.0.0.0/0,eth0 # Delete zero valued element -1 ipset del test 0.0.0.0/0,eth0 -# Try to add /0 -1 ipset add test 1.1.1.1/0,eth0 +0 ipset del test 0.0.0.0/0,eth0 +# Add 1.1.1.1/0 +0 ipset add test 1.1.1.1/0,eth0 +# Test 1.1.1.1/0 +0 ipset test test 1.1.1.1/0,eth0 +# Delete 1.1.1.1/0 +0 ipset del test 1.1.1.1/0,eth0 # Try to add /32 0 ipset add test 1.1.1.1/32,eth0 # Add almost zero valued element @@ -52,6 +56,34 @@ 0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0 # Check listing 0 diff -u -I 'Size in memory.*' .foo hash:net,iface.t.list2 +# Flush test set +0 ipset flush test +# Add 0/0,eth0 +0 ipset add test 0/0,eth0 +# Add 10.0.0.0/16,eth1 +0 ipset add test 10.0.0.0/16,eth1 +# Add 10.0.0.0/24,eth0 +0 ipset add test 10.0.0.0/24,eth0 +# Add 10.0.0.0/16,eth2 +0 ipset add test 10.0.0.0/16,eth2 +# Check 10.0.1.1 with eth1 +0 ipset test test 10.0.1.1,eth1 +# Check 10.0.1.1 with eth2 +0 ipset test test 10.0.1.1,eth2 +# Check 10.0.1.1 with eth0 +1 ipset test test 10.0.1.1,eth0 +# Check 10.0.0.1 with eth1 +1 ipset test test 10.0.0.1,eth1 +# Check 10.0.0.1 with eth2 +1 ipset test test 10.0.0.1,eth2 +# Check 10.0.0.1 with eth0 +0 ipset test test 10.0.0.1,eth0 +# Check 1.0.1.1 with eth1 +1 ipset test test 1.0.1.1,eth1 +# Check 1.0.1.1 with eth2 +1 ipset test test 1.0.1.1,eth2 +# Check 1.0.1.1 with eth0 +0 ipset test test 1.0.1.1,eth0 # Delete test set 0 ipset destroy test # Create test set -- cgit v1.2.3