summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-22 10:49:41 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-22 10:49:41 +0200
commit020936c8c3375e1efe44a3087c891a4b2cbfe044 (patch)
treea94751e6f1f11bcf118129c343d1942bbf53e808 /kernel
parent97a12ba3f184a76c406eb5622ec21a4d4d6fc8bf (diff)
ipset 5: last new feature addedv5.0-pre3
- 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
Diffstat (limited to 'kernel')
-rw-r--r--kernel/include/linux/netfilter/ip_set.h80
-rw-r--r--kernel/include/linux/netfilter/ip_set_chash.h12
-rw-r--r--kernel/include/linux/netfilter/ip_set_getport.h99
-rw-r--r--kernel/include/linux/netfilter/ip_set_hash.h2
-rw-r--r--kernel/ip_set.c1
-rw-r--r--kernel/ip_set_bitmap_ip.c23
-rw-r--r--kernel/ip_set_bitmap_ipmac.c11
-rw-r--r--kernel/ip_set_bitmap_port.c31
-rw-r--r--kernel/ip_set_hash_ip.c25
-rw-r--r--kernel/ip_set_hash_ipport.c131
-rw-r--r--kernel/ip_set_hash_ipportip.c128
-rw-r--r--kernel/ip_set_hash_ipportnet.c128
-rw-r--r--kernel/ip_set_hash_net.c43
-rw-r--r--kernel/ip_set_list_set.c16
14 files changed, 504 insertions, 226 deletions
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 <linux/ip.h>
#include <linux/ipv6.h>
@@ -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,
@@ -507,6 +511,10 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb)
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));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
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 <linux/netfilter/ip_set_chash.h>
@@ -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 <linux/netfilter/ip_set_chash.h>
@@ -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,