From 020936c8c3375e1efe44a3087c891a4b2cbfe044 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 22 Jun 2010 10:49:41 +0200 Subject: ipset 5: last new feature added - the hash types can now store protocol together port, not only port - lots of fixes everywhere: parser, error reporting, manpage The last bits on the todo list before announcing ipset 5: - recheck all the error messages - add possibly more tests - polish manpage --- kernel/include/linux/netfilter/ip_set.h | 80 ++++++++------- kernel/include/linux/netfilter/ip_set_chash.h | 12 ++- kernel/include/linux/netfilter/ip_set_getport.h | 99 ++++++++++-------- kernel/include/linux/netfilter/ip_set_hash.h | 2 + kernel/ip_set.c | 1 - kernel/ip_set_bitmap_ip.c | 23 +++-- kernel/ip_set_bitmap_ipmac.c | 11 +- kernel/ip_set_bitmap_port.c | 31 +++--- kernel/ip_set_hash_ip.c | 25 +++-- kernel/ip_set_hash_ipport.c | 131 +++++++++++++++++++----- kernel/ip_set_hash_ipportip.c | 128 ++++++++++++++++++----- kernel/ip_set_hash_ipportnet.c | 128 +++++++++++++++++------ kernel/ip_set_hash_net.c | 43 +++++--- kernel/ip_set_list_set.c | 16 ++- 14 files changed, 504 insertions(+), 226 deletions(-) (limited to 'kernel') diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h index e700503..f306859 100644 --- a/kernel/include/linux/netfilter/ip_set.h +++ b/kernel/include/linux/netfilter/ip_set.h @@ -20,47 +20,47 @@ /* Message types and commands */ enum ipset_cmd { IPSET_CMD_NONE, - IPSET_CMD_CREATE, /* Create a new (empty) set */ - IPSET_CMD_DESTROY, /* Remove a (empty) set */ - IPSET_CMD_FLUSH, /* Remove all elements from a set */ - IPSET_CMD_RENAME, /* Rename a set */ - IPSET_CMD_SWAP, /* Swap two sets */ - IPSET_CMD_LIST, /* List sets */ - IPSET_CMD_SAVE, /* Save sets */ - IPSET_CMD_ADD, /* Add an element to a set */ - IPSET_CMD_DEL, /* Delete an element from a set */ - IPSET_CMD_TEST, /* Test an element in a set */ - IPSET_CMD_HEADER, /* Get set header data only */ - IPSET_CMD_TYPE, /* Get set type */ - IPSET_CMD_PROTOCOL, /* Return protocol version */ + IPSET_CMD_CREATE, /* 1: Create a new (empty) set */ + IPSET_CMD_DESTROY, /* 2: Remove a (empty) set */ + IPSET_CMD_FLUSH, /* 3: Remove all elements from a set */ + IPSET_CMD_RENAME, /* 4: Rename a set */ + IPSET_CMD_SWAP, /* 5: Swap two sets */ + IPSET_CMD_LIST, /* 6: List sets */ + IPSET_CMD_SAVE, /* 7: Save sets */ + IPSET_CMD_ADD, /* 8: Add an element to a set */ + IPSET_CMD_DEL, /* 9: Delete an element from a set */ + IPSET_CMD_TEST, /* 10: Test an element in a set */ + IPSET_CMD_HEADER, /* 11: Get set header data only */ + IPSET_CMD_TYPE, /* 12: Get set type */ + IPSET_CMD_PROTOCOL, /* 13: Return protocol version */ IPSET_MSG_MAX, /* Netlink message commands */ /* Commands in userspace: */ - IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */ - IPSET_CMD_HELP, /* Get help */ - IPSET_CMD_VERSION, /* Get program version */ - IPSET_CMD_QUIT, /* Quit from interactive mode */ + IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */ + IPSET_CMD_HELP, /* 15: Get help */ + IPSET_CMD_VERSION, /* 16: Get program version */ + IPSET_CMD_QUIT, /* 17: Quit from interactive mode */ IPSET_CMD_MAX, - IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* Commit buffered commands */ + IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */ }; /* Attributes at command level */ enum { IPSET_ATTR_UNSPEC, - IPSET_ATTR_PROTOCOL, /* Protocol version */ - IPSET_ATTR_SETNAME, /* Name of the set */ - IPSET_ATTR_TYPENAME, /* Typename */ + IPSET_ATTR_PROTOCOL, /* 1: Protocol version */ + IPSET_ATTR_SETNAME, /* 2: Name of the set */ + IPSET_ATTR_TYPENAME, /* 3: Typename */ IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */ - IPSET_ATTR_REVISION, /* Settype revision */ - IPSET_ATTR_FAMILY, /* Settype family */ - IPSET_ATTR_FLAGS, /* Flags at command level */ - IPSET_ATTR_DATA, /* Nested attributes */ - IPSET_ATTR_ADT, /* Multiple data containers */ - IPSET_ATTR_LINENO, /* Restore lineno */ - IPSET_ATTR_PROTOCOL_MIN,/* Minimal supported version number */ - IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */ + IPSET_ATTR_REVISION, /* 4: Settype revision */ + IPSET_ATTR_FAMILY, /* 5: Settype family */ + IPSET_ATTR_FLAGS, /* 6: Flags at command level */ + IPSET_ATTR_DATA, /* 7: Nested attributes */ + IPSET_ATTR_ADT, /* 8: Multiple data containers */ + IPSET_ATTR_LINENO, /* 9: Restore lineno */ + IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */ + IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */ __IPSET_ATTR_CMD_MAX, }; #define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1) @@ -69,13 +69,14 @@ enum { enum { IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1, IPSET_ATTR_IP_FROM = IPSET_ATTR_IP, - IPSET_ATTR_IP_TO, - IPSET_ATTR_CIDR, - IPSET_ATTR_PORT, + IPSET_ATTR_IP_TO, /* 2 */ + IPSET_ATTR_CIDR, /* 3 */ + IPSET_ATTR_PORT, /* 4 */ IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, - IPSET_ATTR_PORT_TO, - IPSET_ATTR_TIMEOUT, - IPSET_ATTR_CADT_FLAGS, + IPSET_ATTR_PORT_TO, /* 5 */ + IPSET_ATTR_TIMEOUT, /* 6 */ + IPSET_ATTR_PROTO, /* 7 */ + IPSET_ATTR_CADT_FLAGS, /* 8 */ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* Reserve empty slots */ IPSET_ATTR_CADT_MAX = 16, @@ -147,6 +148,9 @@ enum ipset_adt { IPSET_CADT_MAX, }; +#define IPSET_IPPROTO_ANY 255 +#define IPSET_IPPROTO_TCPUDP 254 + #ifdef __KERNEL__ #include #include @@ -343,6 +347,12 @@ ip_set_free(void *members, u8 flags) vfree(members); } +static inline bool +ip_set_eexist(int ret, u32 flags) +{ + return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST); +} + /* Useful converters */ static inline u32 ip_set_get_h32(const struct nlattr *attr) diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h index 0d77a5d..e0e16bd 100644 --- a/kernel/include/linux/netfilter/ip_set_chash.h +++ b/kernel/include/linux/netfilter/ip_set_chash.h @@ -37,6 +37,9 @@ struct chash { #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif +#ifdef IP_SET_HASH_WITH_PROTO + u8 proto; /* default protocol for SET target */ +#endif #ifdef IP_SET_HASH_WITH_NETS struct chash_nets nets[0]; /* book keeping of networks */ #endif @@ -205,8 +208,9 @@ jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ /* Flavour without timeout */ -#define chash_data(n, i) \ -(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_elem)) +#define chash_data(n, i) \ +(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) \ + + (i)*sizeof(struct type_pf_elem)) static int type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits, @@ -506,6 +510,10 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb) #ifdef IP_SET_HASH_WITH_NETMASK if (h->netmask != HOST_MASK) NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); +#endif +#ifdef IP_SET_HASH_WITH_PROTO + if (h->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, h->proto); #endif NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(atomic_read(&set->ref) - 1)); diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h index ffa89f1..680a89a 100644 --- a/kernel/include/linux/netfilter/ip_set_getport.h +++ b/kernel/include/linux/netfilter/ip_set_getport.h @@ -8,72 +8,91 @@ #define IPSET_INVALID_PORT 65536 /* We must handle non-linear skbs */ -static bool -get_port(u8 pf, const struct sk_buff *skb, bool src, u16 *port) -{ - unsigned short protocol; - unsigned int protoff; - int fragoff; - - switch (pf) { - case AF_INET: { - const struct iphdr *iph = ip_hdr(skb); - - protocol = iph->protocol; - fragoff = ntohs(iph->frag_off) & IP_OFFSET; - protoff = ip_hdrlen(skb); - break; - } - case AF_INET6: { - int protohdr; - unsigned short frag_off; - - protohdr = ipv6_find_hdr(skb, &protoff, -1, &frag_off); - if (protohdr < 0) - return false; - - protocol = protohdr; - fragoff = frag_off; - break; - } - default: - return false; - } - - /* See comments at tcp_match in ip_tables.c */ - if (fragoff) - return false; +static inline bool +get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, + bool src, u16 *port, u8 *proto) +{ switch (protocol) { case IPPROTO_TCP: { struct tcphdr _tcph; const struct tcphdr *th; - th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph); if (th == NULL) /* No choice either */ return false; *port = src ? th->source : th->dest; break; - } + } case IPPROTO_UDP: { struct udphdr _udph; const struct udphdr *uh; - uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); + uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); if (uh == NULL) /* No choice either */ return false; *port = src ? uh->source : uh->dest; break; - } + } default: - return false; + if (*proto == IPSET_IPPROTO_TCPUDP) + return false; + break; } + if (*proto != IPSET_IPPROTO_TCPUDP) + *proto = protocol; + return true; } + +static inline bool +get_ip4_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto) +{ + const struct iphdr *iph = ip_hdr(skb); + unsigned int protooff = ip_hdrlen(skb); + int protocol = iph->protocol; + + if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol)) + return false; + + /* See comments at tcp_match in ip_tables.c */ + if (ntohs(iph->frag_off) & IP_OFFSET) + return false; + + return get_port(skb, protocol, protooff, src, port, proto); +} + +static inline bool +get_ip6_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto) +{ + unsigned int *protooff = 0; + int protocol; + unsigned short fragoff; + + protocol = ipv6_find_hdr(skb, protooff, -1, &fragoff); + if (protocol < 0 || fragoff) + return false; + + if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol)) + return false; + + return get_port(skb, protocol, *protooff, src, port, proto); +} + +static inline bool +get_ip_port(const struct sk_buff *skb, u8 pf, bool src, u16 *port) +{ + u8 proto = IPSET_IPPROTO_TCPUDP; + + if (pf == AF_INET) + return get_ip4_port(skb, src, port, &proto); + else + return get_ip6_port(skb, src, port, &proto); +} #endif /* __KERNEL__ */ #endif /*_IP_SET_GETPORT_H*/ diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h index c1a6964..4003af0 100644 --- a/kernel/include/linux/netfilter/ip_set_hash.h +++ b/kernel/include/linux/netfilter/ip_set_hash.h @@ -5,6 +5,8 @@ enum { IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC, IPSET_ERR_HASH_ELEM, + IPSET_ERR_INVALID_PROTO, + IPSET_ERR_MISSING_PROTO, }; #ifdef __KERNEL__ diff --git a/kernel/ip_set.c b/kernel/ip_set.c index 5bf331e..9d7093c 100644 --- a/kernel/ip_set.c +++ b/kernel/ip_set.c @@ -474,7 +474,6 @@ ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = { .len = IPSET_MAXNAMELEN - 1}, [IPSET_ATTR_REVISION] = { .type = NLA_U8 }, [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, - [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, }; diff --git a/kernel/ip_set_bitmap_ip.c b/kernel/ip_set_bitmap_ip.c index 66d3979..27b9665 100644 --- a/kernel/ip_set_bitmap_ip.c +++ b/kernel/ip_set_bitmap_ip.c @@ -107,6 +107,7 @@ bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -115,7 +116,6 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, { struct bitmap_ip *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u32 ip, ip_to, id; int ret = 0; @@ -123,6 +123,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ip_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); else @@ -164,11 +167,10 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adt == IPSET_ADD ? bitmap_ip_add(map, id) : bitmap_ip_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } @@ -356,7 +358,6 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, { struct bitmap_ip_timeout *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u32 ip, ip_to, id, timeout = map->timeout; int ret = 0; @@ -364,6 +365,9 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ip_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = ip_set_get_h32(tb[IPSET_ATTR_IP]); else @@ -405,11 +409,10 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, ? bitmap_ip_timeout_add(map, id, timeout) : bitmap_ip_timeout_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } diff --git a/kernel/ip_set_bitmap_ipmac.c b/kernel/ip_set_bitmap_ipmac.c index d036862..3c94975 100644 --- a/kernel/ip_set_bitmap_ipmac.c +++ b/kernel/ip_set_bitmap_ipmac.c @@ -368,6 +368,7 @@ bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -377,7 +378,6 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, struct bitmap_ipmac *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - bool eexist = flags & IPSET_FLAG_EXIST; struct ipmac data; u32 timeout = map->timeout; int ret = 0; @@ -386,6 +386,9 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_ipmac_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]); else @@ -409,11 +412,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static void diff --git a/kernel/ip_set_bitmap_port.c b/kernel/ip_set_bitmap_port.c index f3e498a..6517252 100644 --- a/kernel/ip_set_bitmap_port.c +++ b/kernel/ip_set_bitmap_port.c @@ -72,9 +72,9 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_port *map = set->data; - u16 port; + u16 port = 0; - if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port)) + if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port)) return -EINVAL; port = ntohs(port); @@ -101,6 +101,7 @@ bitmap_port_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -109,7 +110,6 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, { struct bitmap_port *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u32 port; /* wraparound */ u16 id, port_to; int ret = 0; @@ -118,6 +118,9 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_port_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_PORT]) port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); else @@ -150,11 +153,10 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adt == IPSET_ADD ? bitmap_port_add(map, id) : bitmap_port_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } @@ -313,9 +315,9 @@ bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb, enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { struct bitmap_port_timeout *map = set->data; - u16 port; + u16 port = 0; - if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port)) + if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port)) return -EINVAL; port = ntohs(port); @@ -343,7 +345,6 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, { const struct bitmap_port_timeout *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; u16 id, port_to; u32 port, timeout = map->timeout; /* wraparound */ int ret = 0; @@ -352,6 +353,9 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, bitmap_port_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_PORT]) port = ip_set_get_h16(tb[IPSET_ATTR_PORT]); else @@ -385,11 +389,10 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len, ? bitmap_port_timeout_add(map, id, timeout) : bitmap_port_timeout_del(map, id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } diff --git a/kernel/ip_set_hash_ip.c b/kernel/ip_set_hash_ip.c index e5ce6a4..d73a5da 100644 --- a/kernel/ip_set_hash_ip.c +++ b/kernel/ip_set_hash_ip.c @@ -140,6 +140,7 @@ hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP_TO] = { .type = NLA_U32 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -148,7 +149,6 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; u32 ip, nip, ip_to, hosts, timeout = h->timeout; int ret = 0; @@ -157,6 +157,9 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ip4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -196,11 +199,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len, nip = htonl(ip); ret = adtfn(set, &nip, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (ret && !ip_set_eexist(ret, flags)) return ret; - } + else + ret = 0; } return ret; } @@ -213,10 +215,10 @@ hash_ip_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->netmask == y->netmask && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size - && x->chain_limit == y->chain_limit - && x->netmask == y->netmask; + && x->chain_limit == y->chain_limit; } /* The type variant functions: IPv6 */ @@ -327,6 +329,7 @@ hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_BINARY, .len = sizeof(struct in6_addr) }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -338,11 +341,15 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, ipset_adtfn adtfn = set->variant->adt[adt]; union nf_inet_addr *ip; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ip6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) ip = nla_data(tb[IPSET_ATTR_IP]); else @@ -358,7 +365,9 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, ip, GFP_KERNEL, timeout); + ret = adtfn(set, ip, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ diff --git a/kernel/ip_set_hash_ipport.c b/kernel/ip_set_hash_ipport.c index 8210f67..cb319d2 100644 --- a/kernel/ip_set_hash_ipport.c +++ b/kernel/ip_set_hash_ipport.c @@ -48,14 +48,16 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b); struct hash_ipport4_elem { u32 ip; u16 port; - u16 match; + u8 proto; + u8 padding; }; /* Member elements with timeout support */ struct hash_ipport4_telem { u32 ip; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -63,13 +65,15 @@ static inline bool hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, const struct hash_ipport4_elem *ip2) { - return ip1->ip == ip2->ip && ip1->port == ip2->port; + return ip1->ip == ip2->ip + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -78,7 +82,7 @@ hash_ipport4_data_copy(struct hash_ipport4_elem *dst, { dst->ip = src->ip; dst->port = src->port; - dst->match = 1; + dst->proto = src->proto; } static inline void @@ -87,12 +91,13 @@ hash_ipport4_data_swap(struct hash_ipport4_elem *dst, { swap(dst->ip, src->ip); swap(dst->port, src->port); + swap(dst->proto, src->proto); } static inline void hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -101,6 +106,8 @@ hash_ipport4_data_list(struct sk_buff *skb, { NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -116,6 +123,8 @@ hash_ipport4_data_tlist(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -125,6 +134,8 @@ nla_put_failure: return 1; } +#define IP_SET_HASH_WITH_PROTO + #define PF 4 #define HOST_MASK 32 #include @@ -135,12 +146,14 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport4_elem data = {}; + struct hash_ipport4_elem data = { .proto = h->proto }; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip4_port(skb, flags & IPSET_DIM_ONE_SRC, + &data.port, &data.proto)) return -EINVAL; + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } @@ -148,7 +161,9 @@ static const struct nla_policy hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -157,9 +172,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport4_elem data = {}; + struct hash_ipport4_elem data = { .proto = h->proto }; u32 timeout = h->timeout; int ret; @@ -167,6 +181,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipport4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -177,6 +194,24 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; @@ -185,11 +220,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -200,6 +231,7 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->proto == y->proto && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size && x->chain_limit == y->chain_limit; @@ -210,13 +242,15 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b) struct hash_ipport6_elem { union nf_inet_addr ip; u16 port; - u16 match; + u8 proto; + u8 padding; }; struct hash_ipport6_telem { union nf_inet_addr ip; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -225,13 +259,14 @@ hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, const struct hash_ipport6_elem *ip2) { return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -239,7 +274,6 @@ hash_ipport6_data_copy(struct hash_ipport6_elem *dst, const struct hash_ipport6_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -256,7 +290,7 @@ hash_ipport6_data_swap(struct hash_ipport6_elem *dst, static inline void hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -265,6 +299,8 @@ hash_ipport6_data_list(struct sk_buff *skb, { NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -280,6 +316,8 @@ hash_ipport6_data_tlist(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -301,12 +339,14 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport6_elem data = {}; + struct hash_ipport6_elem data = { .proto = h->proto }; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip6_port(skb, flags & IPSET_DIM_ONE_SRC, + &data.port, &data.proto)) return -EINVAL; + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); + return adtfn(set, &data, GFP_ATOMIC, h->timeout); } @@ -315,7 +355,9 @@ hash_ipport6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_BINARY, .len = sizeof(struct in6_addr) }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -325,13 +367,17 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipport6_elem data = {}; + struct hash_ipport6_elem data = { .proto = h->proto }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ipport6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -343,13 +389,33 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ @@ -360,6 +426,7 @@ hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -367,8 +434,9 @@ static int hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -386,6 +454,12 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + if (tb[IPSET_ATTR_PROTO]) { + proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + if (!proto) + return -IPSET_ERR_INVALID_PROTO; + } + h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -395,6 +469,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); + h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), diff --git a/kernel/ip_set_hash_ipportip.c b/kernel/ip_set_hash_ipportip.c index fbf2780..2c3cf9b 100644 --- a/kernel/ip_set_hash_ipportip.c +++ b/kernel/ip_set_hash_ipportip.c @@ -49,7 +49,8 @@ struct hash_ipportip4_elem { u32 ip; u32 ip2; u16 port; - u16 match; + u8 proto; + u8 padding; }; /* Member elements with timeout support */ @@ -57,7 +58,8 @@ struct hash_ipportip4_telem { u32 ip; u32 ip2; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -67,13 +69,14 @@ hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, { return ip1->ip == ip2->ip && ip1->ip2 == ip2->ip2 - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -81,7 +84,6 @@ hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst, const struct hash_ipportip4_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -98,7 +100,7 @@ hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst, static inline void hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -108,6 +110,8 @@ hash_ipportip4_data_list(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -124,6 +128,8 @@ hash_ipportip4_data_tlist(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -133,6 +139,8 @@ nla_put_failure: return 1; } +#define IP_SET_HASH_WITH_PROTO + #define PF 4 #define HOST_MASK 32 #include @@ -143,11 +151,13 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip4_elem data = {}; + struct hash_ipportip4_elem data = { .proto = h->proto }; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); return adtfn(set, &data, GFP_ATOMIC, h->timeout); @@ -158,7 +168,9 @@ hash_ipportip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_U32 }, [IPSET_ATTR_IP2] = { .type = NLA_U32 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -167,9 +179,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip4_elem data = {}; + struct hash_ipportip4_elem data = { .proto = h->proto }; u32 timeout = h->timeout; int ret; @@ -177,6 +188,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportip4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -192,6 +206,24 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; @@ -200,11 +232,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -215,6 +243,7 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->proto == y->proto && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size && x->chain_limit == y->chain_limit; @@ -226,14 +255,16 @@ struct hash_ipportip6_elem { union nf_inet_addr ip; union nf_inet_addr ip2; u16 port; - u16 match; + u8 proto; + u8 padding; }; struct hash_ipportip6_telem { union nf_inet_addr ip; union nf_inet_addr ip2; u16 port; - u16 match; + u8 proto; + u8 padding; unsigned long timeout; }; @@ -243,13 +274,14 @@ hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, { return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -257,7 +289,6 @@ hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst, const struct hash_ipportip6_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -274,7 +305,7 @@ hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst, static inline void hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -284,6 +315,8 @@ hash_ipportip6_data_list(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip); NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -300,6 +333,8 @@ hash_ipportip6_data_tlist(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip); NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -321,11 +356,13 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, { struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip6_elem data = {}; + struct hash_ipportip6_elem data = { .proto = h->proto }; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); return adtfn(set, &data, GFP_ATOMIC, h->timeout); @@ -338,7 +375,9 @@ hash_ipportip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP2] = { .type = NLA_BINARY, .len = sizeof(struct in6_addr) }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -348,13 +387,17 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportip6_elem data = {}; + struct hash_ipportip6_elem data = { .proto = h->proto }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ipportip6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -372,13 +415,33 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ @@ -389,6 +452,7 @@ hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -397,8 +461,9 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -416,6 +481,12 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + if (tb[IPSET_ATTR_PROTO]) { + proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + if (!proto) + return -IPSET_ERR_INVALID_PROTO; + } + h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) return -ENOMEM; @@ -425,6 +496,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head, h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); + h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), diff --git a/kernel/ip_set_hash_ipportnet.c b/kernel/ip_set_hash_ipportnet.c index dfe9348..4bc44f5 100644 --- a/kernel/ip_set_hash_ipportnet.c +++ b/kernel/ip_set_hash_ipportnet.c @@ -50,7 +50,7 @@ struct hash_ipportnet4_elem { u32 ip2; u16 port; u8 cidr; - u8 match; + u8 proto; }; /* Member elements with timeout support */ @@ -59,7 +59,7 @@ struct hash_ipportnet4_telem { u32 ip2; u16 port; u8 cidr; - u8 match; + u8 proto; unsigned long timeout; }; @@ -70,13 +70,14 @@ hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, return ip1->ip == ip2->ip && ip1->ip2 == ip2->ip2 && ip1->cidr == ip2->cidr - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -84,7 +85,6 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst, const struct hash_ipportnet4_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -108,7 +108,7 @@ hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) static inline void hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline bool @@ -119,6 +119,8 @@ hash_ipportnet4_data_list(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -136,6 +138,8 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb, NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -145,6 +149,7 @@ nla_put_failure: return 1; } +#define IP_SET_HASH_WITH_PROTO #define IP_SET_HASH_WITH_NETS #define PF 4 @@ -158,16 +163,19 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK }; - + { .cidr = h->nets[0].cidr || HOST_MASK, + .proto = h->proto }; + if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2); data.ip2 &= NETMASK(data.cidr); @@ -180,7 +188,9 @@ hash_ipportnet4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { [IPSET_ATTR_IP2] = { .type = NLA_U32 }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -189,9 +199,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; + struct hash_ipportnet4_elem data = { .cidr = HOST_MASK, + .proto = h->proto }; u32 timeout = h->timeout; int ret; @@ -199,6 +209,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_ipportnet4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -222,6 +235,24 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; @@ -230,11 +261,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -245,6 +272,7 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b) return x->maxelem == y->maxelem && x->timeout == y->timeout + && x->proto == y->proto && x->htable_bits == y->htable_bits /* resizing ? */ && x->array_size == y->array_size && x->chain_limit == y->chain_limit; @@ -257,7 +285,7 @@ struct hash_ipportnet6_elem { union nf_inet_addr ip2; u16 port; u8 cidr; - u8 match; + u8 proto; }; struct hash_ipportnet6_telem { @@ -265,7 +293,7 @@ struct hash_ipportnet6_telem { union nf_inet_addr ip2; u16 port; u8 cidr; - u8 match; + u8 proto; unsigned long timeout; }; @@ -276,13 +304,14 @@ hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && ip1->cidr == ip2->cidr - && ip1->port == ip2->port; + && ip1->port == ip2->port + && ip1->proto == ip2->proto; } static inline bool hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem) { - return elem->match == 0; + return elem->proto == 0; } static inline void @@ -290,7 +319,6 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst, const struct hash_ipportnet6_elem *src) { memcpy(dst, src, sizeof(*dst)); - dst->match = 1; } static inline void @@ -307,7 +335,7 @@ hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst, static inline void hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem) { - elem->match = 0; + elem->proto = 0; } static inline void @@ -334,6 +362,8 @@ hash_ipportnet6_data_list(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); return 0; nla_put_failure: @@ -351,6 +381,8 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb, NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + if (data->proto != IPSET_IPPROTO_TCPUDP) + NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -373,16 +405,19 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct chash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem data = - { .cidr = h->nets[0].cidr || HOST_MASK }; + { .cidr = h->nets[0].cidr || HOST_MASK, + .proto = h->proto }; if (data.cidr == 0) return -EINVAL; if (adt == IPSET_TEST) data.cidr = HOST_MASK; - ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port)) + if (!get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC, + &data.port, &data.proto)) return -EINVAL; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); ip6_netmask(&data.ip2, data.cidr); @@ -397,7 +432,9 @@ hash_ipportnet6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { .len = sizeof(struct in6_addr) }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -407,13 +444,18 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; + struct hash_ipportnet6_elem data = { .cidr = HOST_MASK, + .proto = h->proto }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_ipportnet6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -439,13 +481,33 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len, else return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_PROTO]) { + data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + + if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP) + return -IPSET_ERR_INVALID_PROTO; + } else if (data.proto == IPSET_IPPROTO_ANY) + return -IPSET_ERR_MISSING_PROTO; + + switch (data.proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPSET_IPPROTO_TCPUDP: + break; + default: + data.port = 0; + break; + } + if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout(h->timeout)) return -IPSET_ERR_TIMEOUT; timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ @@ -456,6 +518,7 @@ hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, }; @@ -464,8 +527,9 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, int len, u32 flags) { struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; - u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; struct chash *h; + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */ if (!(set->family == AF_INET || set->family == AF_INET6)) return -IPSET_ERR_INVALID_FAMILY; @@ -483,6 +547,11 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + if (tb[IPSET_ATTR_PROTO]) { + proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + if (!proto) + return -IPSET_ERR_INVALID_PROTO; + } h = kzalloc(sizeof(*h) + sizeof(struct chash_nets) * (set->family == AF_INET ? 31 : 127), GFP_KERNEL); @@ -494,6 +563,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head, h->array_size = CHASH_DEFAULT_ARRAY_SIZE; h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT; get_random_bytes(&h->initval, sizeof(h->initval)); + h->proto = proto; h->timeout = IPSET_NO_TIMEOUT; h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist), diff --git a/kernel/ip_set_hash_net.c b/kernel/ip_set_hash_net.c index a8611c2..9be9e2c 100644 --- a/kernel/ip_set_hash_net.c +++ b/kernel/ip_set_hash_net.c @@ -46,13 +46,17 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b); /* Member elements without timeout */ struct hash_net4_elem { u32 ip; - u8 cidr; /* Not hashed, zero for null value */ + u16 padding0; + u8 padding1; + u8 cidr; }; /* Member elements with timeout support */ struct hash_net4_telem { u32 ip; - u8 cidr; /* Not hashed, zero for null value */ + u16 padding0; + u8 padding1; + u8 cidr; unsigned long timeout; }; @@ -60,10 +64,7 @@ static inline bool hash_net4_data_equal(const struct hash_net4_elem *ip1, const struct hash_net4_elem *ip2) { - /* We don't have to check the cidr equality - * because overlapping nets cannot be added to the set - */ - return ip1->ip == ip2->ip; + return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr; } static inline bool @@ -168,7 +169,6 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, { struct chash *h = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem data = { .cidr = HOST_MASK }; u32 timeout = h->timeout; @@ -178,6 +178,9 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, hash_net4_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]); else @@ -199,11 +202,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len, ret = adtfn(set, &data, GFP_KERNEL, timeout); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static bool @@ -223,12 +222,16 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b) struct hash_net6_elem { union nf_inet_addr ip; - u8 cidr; /* Not hashed */ + u16 padding0; + u8 padding1; + u8 cidr; }; struct hash_net6_telem { union nf_inet_addr ip; - u8 cidr; /* Not hashed */ + u16 padding0; + u8 padding1; + u8 cidr; unsigned long timeout; }; @@ -236,7 +239,8 @@ static inline bool hash_net6_data_equal(const struct hash_net6_elem *ip1, const struct hash_net6_elem *ip2) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 + && ip1->cidr == ip2->cidr; } static inline bool @@ -344,6 +348,7 @@ hash_net6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = { .len = sizeof(struct in6_addr) }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }; static int @@ -355,11 +360,15 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem data = { .cidr = HOST_MASK }; u32 timeout = h->timeout; + int ret; if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, hash_net6_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_IP]) memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]), sizeof(struct in6_addr)); @@ -380,7 +389,9 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len, timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } - return adtfn(set, &data, GFP_KERNEL, timeout); + ret = adtfn(set, &data, GFP_KERNEL, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; } /* Create hash:ip type of sets */ diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c index ce6c4d1..94c5702 100644 --- a/kernel/ip_set_list_set.c +++ b/kernel/ip_set_list_set.c @@ -119,6 +119,7 @@ list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, .len = IPSET_MAXNAMELEN }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, }; @@ -210,8 +211,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len, { struct list_set *map = set->data; struct nlattr *tb[IPSET_ATTR_ADT_MAX]; - bool eexist = flags & IPSET_FLAG_EXIST, - with_timeout = with_timeout(map->timeout); + bool with_timeout = with_timeout(map->timeout); int before = 0; u32 timeout = map->timeout; ip_set_id_t id, refid = IPSET_INVALID_ID; @@ -224,6 +224,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len, list_set_adt_policy)) return -IPSET_ERR_PROTOCOL; + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + if (tb[IPSET_ATTR_NAME]) { id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); if (id == IPSET_INVALID_ID) @@ -334,12 +337,7 @@ finish: if (adt != IPSET_ADD || ret) ip_set_put_byindex(id); - if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - return ret; - } - return ret; + return ip_set_eexist(ret, flags) ? 0 : ret; } static void @@ -565,7 +563,7 @@ list_set_create(struct ip_set *set, struct nlattr *head, int len, static struct ip_set_type list_set_type = { .name = "list:set", .protocol = IPSET_PROTOCOL, - .features = IPSET_TYPE_NAME, + .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, .family = AF_UNSPEC, .revision = 0, -- cgit v1.2.3