1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21c5ba67dSPablo Neira Ayuso /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@netfilter.org> */
341d22f7bSJozsef Kadlecsik 
441d22f7bSJozsef Kadlecsik /* Kernel module implementing an IP set type: the hash:ip,port,net type */
541d22f7bSJozsef Kadlecsik 
641d22f7bSJozsef Kadlecsik #include <linux/jhash.h>
741d22f7bSJozsef Kadlecsik #include <linux/module.h>
841d22f7bSJozsef Kadlecsik #include <linux/ip.h>
941d22f7bSJozsef Kadlecsik #include <linux/skbuff.h>
1041d22f7bSJozsef Kadlecsik #include <linux/errno.h>
1141d22f7bSJozsef Kadlecsik #include <linux/random.h>
1241d22f7bSJozsef Kadlecsik #include <net/ip.h>
1341d22f7bSJozsef Kadlecsik #include <net/ipv6.h>
1441d22f7bSJozsef Kadlecsik #include <net/netlink.h>
1541d22f7bSJozsef Kadlecsik #include <net/tcp.h>
1641d22f7bSJozsef Kadlecsik 
1741d22f7bSJozsef Kadlecsik #include <linux/netfilter.h>
1841d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/pfxlen.h>
1941d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set.h>
2041d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_getport.h>
2141d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h>
2241d22f7bSJozsef Kadlecsik 
2335b8dcf8SJozsef Kadlecsik #define IPSET_TYPE_REV_MIN	0
2410111a6eSJozsef Kadlecsik /*				1    SCTP and UDPLITE support added */
2510111a6eSJozsef Kadlecsik /*				2    Range as input support for IPv4 added */
2600d71b27SJozsef Kadlecsik /*				3    nomatch flag support added */
27fda75c6dSOliver Smith /*				4    Counters support added */
2807cf8f5aSJosh Hunt /*				5    Comments support added */
29af331419SAnton Danilov /*				6    Forceadd support added */
30*ccf0a4b7SJozsef Kadlecsik /*				7    skbinfo support added */
31*ccf0a4b7SJozsef Kadlecsik #define IPSET_TYPE_REV_MAX	8 /* bucketsize support added */
3210111a6eSJozsef Kadlecsik 
3341d22f7bSJozsef Kadlecsik MODULE_LICENSE("GPL");
34fe03d474SJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
3535b8dcf8SJozsef Kadlecsik IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
3641d22f7bSJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:ip,port,net");
3741d22f7bSJozsef Kadlecsik 
3841d22f7bSJozsef Kadlecsik /* Type specific function prefix */
395d50e1d8SJozsef Kadlecsik #define HTYPE		hash_ipportnet
4041d22f7bSJozsef Kadlecsik 
412a7cef2aSJozsef Kadlecsik /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
422a7cef2aSJozsef Kadlecsik  * However this way we have to store internally cidr - 1,
432a7cef2aSJozsef Kadlecsik  * dancing back and forth.
442a7cef2aSJozsef Kadlecsik  */
452a7cef2aSJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS_PACKED
465d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_PROTO
475d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS
482a7cef2aSJozsef Kadlecsik 
4903c8b234SJozsef Kadlecsik /* IPv4 variant */
505d50e1d8SJozsef Kadlecsik 
515d50e1d8SJozsef Kadlecsik /* Member elements */
5241d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem {
5341d22f7bSJozsef Kadlecsik 	__be32 ip;
5441d22f7bSJozsef Kadlecsik 	__be32 ip2;
5541d22f7bSJozsef Kadlecsik 	__be16 port;
562a7cef2aSJozsef Kadlecsik 	u8 cidr:7;
572a7cef2aSJozsef Kadlecsik 	u8 nomatch:1;
5841d22f7bSJozsef Kadlecsik 	u8 proto;
5941d22f7bSJozsef Kadlecsik };
6041d22f7bSJozsef Kadlecsik 
615d50e1d8SJozsef Kadlecsik /* Common functions */
625d50e1d8SJozsef Kadlecsik 
638dea982aSJeremy Sowden static bool
6441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
6589dc79b7SJozsef Kadlecsik 			   const struct hash_ipportnet4_elem *ip2,
6689dc79b7SJozsef Kadlecsik 			   u32 *multi)
6741d22f7bSJozsef Kadlecsik {
6841d22f7bSJozsef Kadlecsik 	return ip1->ip == ip2->ip &&
6941d22f7bSJozsef Kadlecsik 	       ip1->ip2 == ip2->ip2 &&
7041d22f7bSJozsef Kadlecsik 	       ip1->cidr == ip2->cidr &&
7141d22f7bSJozsef Kadlecsik 	       ip1->port == ip2->port &&
7241d22f7bSJozsef Kadlecsik 	       ip1->proto == ip2->proto;
7341d22f7bSJozsef Kadlecsik }
7441d22f7bSJozsef Kadlecsik 
758dea982aSJeremy Sowden static int
765d50e1d8SJozsef Kadlecsik hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem)
772a7cef2aSJozsef Kadlecsik {
783e0304a5SJozsef Kadlecsik 	return elem->nomatch ? -ENOTEMPTY : 1;
792a7cef2aSJozsef Kadlecsik }
802a7cef2aSJozsef Kadlecsik 
818dea982aSJeremy Sowden static void
825d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags)
835d50e1d8SJozsef Kadlecsik {
845d50e1d8SJozsef Kadlecsik 	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
855d50e1d8SJozsef Kadlecsik }
865d50e1d8SJozsef Kadlecsik 
878dea982aSJeremy Sowden static void
885d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags)
895d50e1d8SJozsef Kadlecsik {
905d50e1d8SJozsef Kadlecsik 	swap(*flags, elem->nomatch);
915d50e1d8SJozsef Kadlecsik }
925d50e1d8SJozsef Kadlecsik 
938dea982aSJeremy Sowden static void
9441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
9541d22f7bSJozsef Kadlecsik {
9641d22f7bSJozsef Kadlecsik 	elem->ip2 &= ip_set_netmask(cidr);
972a7cef2aSJozsef Kadlecsik 	elem->cidr = cidr - 1;
9841d22f7bSJozsef Kadlecsik }
9941d22f7bSJozsef Kadlecsik 
10041d22f7bSJozsef Kadlecsik static bool
10141d22f7bSJozsef Kadlecsik hash_ipportnet4_data_list(struct sk_buff *skb,
10241d22f7bSJozsef Kadlecsik 			  const struct hash_ipportnet4_elem *data)
10341d22f7bSJozsef Kadlecsik {
1042a7cef2aSJozsef Kadlecsik 	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
1052a7cef2aSJozsef Kadlecsik 
1067cf7899dSDavid S. Miller 	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) ||
1077cf7899dSDavid S. Miller 	    nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) ||
1087cf7899dSDavid S. Miller 	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
1097cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
1107cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
1117cf7899dSDavid S. Miller 	    (flags &&
1127cf7899dSDavid S. Miller 	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
1137cf7899dSDavid S. Miller 		goto nla_put_failure;
114728a7e69SSergey Popovich 	return false;
11541d22f7bSJozsef Kadlecsik 
11641d22f7bSJozsef Kadlecsik nla_put_failure:
117728a7e69SSergey Popovich 	return true;
11841d22f7bSJozsef Kadlecsik }
11941d22f7bSJozsef Kadlecsik 
1208dea982aSJeremy Sowden static void
1215d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next,
1223d14b171SJozsef Kadlecsik 			  const struct hash_ipportnet4_elem *d)
1233d14b171SJozsef Kadlecsik {
1245d50e1d8SJozsef Kadlecsik 	next->ip = d->ip;
1255d50e1d8SJozsef Kadlecsik 	next->port = d->port;
1265d50e1d8SJozsef Kadlecsik 	next->ip2 = d->ip2;
1273d14b171SJozsef Kadlecsik }
1283d14b171SJozsef Kadlecsik 
1295d50e1d8SJozsef Kadlecsik #define MTYPE		hash_ipportnet4
1305d50e1d8SJozsef Kadlecsik #define HOST_MASK	32
1315d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h"
1325d50e1d8SJozsef Kadlecsik 
13341d22f7bSJozsef Kadlecsik static int
13441d22f7bSJozsef Kadlecsik hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
135b66554cfSJozsef Kadlecsik 		     const struct xt_action_param *par,
1365d50e1d8SJozsef Kadlecsik 		     enum ipset_adt adt, struct ip_set_adt_opt *opt)
13741d22f7bSJozsef Kadlecsik {
13821956ab2SJozsef Kadlecsik 	const struct hash_ipportnet4 *h = set->data;
13941d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
1405d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet4_elem e = {
141f690cbaeSJozsef Kadlecsik 		.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
1429b03a5efSJozsef Kadlecsik 	};
143ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
14441d22f7bSJozsef Kadlecsik 
14541d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST)
1465d50e1d8SJozsef Kadlecsik 		e.cidr = HOST_MASK - 1;
14741d22f7bSJozsef Kadlecsik 
148ac8cc925SJozsef Kadlecsik 	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
1495d50e1d8SJozsef Kadlecsik 				 &e.port, &e.proto))
15041d22f7bSJozsef Kadlecsik 		return -EINVAL;
15141d22f7bSJozsef Kadlecsik 
1525d50e1d8SJozsef Kadlecsik 	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
1535d50e1d8SJozsef Kadlecsik 	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
1545d50e1d8SJozsef Kadlecsik 	e.ip2 &= ip_set_netmask(e.cidr + 1);
15541d22f7bSJozsef Kadlecsik 
1565d50e1d8SJozsef Kadlecsik 	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
15741d22f7bSJozsef Kadlecsik }
15841d22f7bSJozsef Kadlecsik 
15941d22f7bSJozsef Kadlecsik static int
16041d22f7bSJozsef Kadlecsik hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
1613d14b171SJozsef Kadlecsik 		     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
16241d22f7bSJozsef Kadlecsik {
16321956ab2SJozsef Kadlecsik 	const struct hash_ipportnet4 *h = set->data;
16441d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
1655d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
166ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
16720b2fab4SJozsef Kadlecsik 	u32 ip = 0, ip_to = 0, p = 0, port, port_to;
1680b8d9073SJozsef Kadlecsik 	u32 ip2_from = 0, ip2_to = 0, ip2;
1695e0c1eb7SJozsef Kadlecsik 	bool with_ports = false;
1702a7cef2aSJozsef Kadlecsik 	u8 cidr;
17141d22f7bSJozsef Kadlecsik 	int ret;
17241d22f7bSJozsef Kadlecsik 
173a212e08eSSergey Popovich 	if (tb[IPSET_ATTR_LINENO])
174a212e08eSSergey Popovich 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
175a212e08eSSergey Popovich 
17641d22f7bSJozsef Kadlecsik 	if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
17741d22f7bSJozsef Kadlecsik 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
17841d22f7bSJozsef Kadlecsik 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
1797dd37bc8SSergey Popovich 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
18041d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_PROTOCOL;
18141d22f7bSJozsef Kadlecsik 
1828e55d2e5SSergey Popovich 	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
1838e55d2e5SSergey Popovich 	if (ret)
1848e55d2e5SSergey Popovich 		return ret;
1858e55d2e5SSergey Popovich 
1868e55d2e5SSergey Popovich 	ret = ip_set_get_extensions(set, tb, &ext);
18741d22f7bSJozsef Kadlecsik 	if (ret)
18841d22f7bSJozsef Kadlecsik 		return ret;
18941d22f7bSJozsef Kadlecsik 
190d0d9e0a5SJozsef Kadlecsik 	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from);
19141d22f7bSJozsef Kadlecsik 	if (ret)
19241d22f7bSJozsef Kadlecsik 		return ret;
19341d22f7bSJozsef Kadlecsik 
194d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CIDR2]) {
1952a7cef2aSJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
1962a7cef2aSJozsef Kadlecsik 		if (!cidr || cidr > HOST_MASK)
19741d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
1985d50e1d8SJozsef Kadlecsik 		e.cidr = cidr - 1;
199d0d9e0a5SJozsef Kadlecsik 	}
20041d22f7bSJozsef Kadlecsik 
2015d50e1d8SJozsef Kadlecsik 	e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
20241d22f7bSJozsef Kadlecsik 
20341d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_PROTO]) {
2045d50e1d8SJozsef Kadlecsik 		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
2055d50e1d8SJozsef Kadlecsik 		with_ports = ip_set_proto_with_ports(e.proto);
20641d22f7bSJozsef Kadlecsik 
2075d50e1d8SJozsef Kadlecsik 		if (e.proto == 0)
20841d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_PROTO;
209ca0f6a5cSJozsef Kadlecsik 	} else {
21041d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_MISSING_PROTO;
211ca0f6a5cSJozsef Kadlecsik 	}
21241d22f7bSJozsef Kadlecsik 
2135d50e1d8SJozsef Kadlecsik 	if (!(with_ports || e.proto == IPPROTO_ICMP))
2145d50e1d8SJozsef Kadlecsik 		e.port = 0;
21541d22f7bSJozsef Kadlecsik 
21643c56e59SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
2172a7cef2aSJozsef Kadlecsik 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
218ca0f6a5cSJozsef Kadlecsik 
2192a7cef2aSJozsef Kadlecsik 		if (cadt_flags & IPSET_FLAG_NOMATCH)
22043c56e59SJozsef Kadlecsik 			flags |= (IPSET_FLAG_NOMATCH << 16);
2212a7cef2aSJozsef Kadlecsik 	}
2222a7cef2aSJozsef Kadlecsik 
223d0d9e0a5SJozsef Kadlecsik 	with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
22441d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST ||
225d0d9e0a5SJozsef Kadlecsik 	    !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
226d0d9e0a5SJozsef Kadlecsik 	      tb[IPSET_ATTR_IP2_TO])) {
2275d50e1d8SJozsef Kadlecsik 		e.ip = htonl(ip);
2285d50e1d8SJozsef Kadlecsik 		e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1));
2295d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
2300f1799baSJozsef Kadlecsik 		return ip_set_enomatch(ret, flags, adt, set) ? -ret :
23143c56e59SJozsef Kadlecsik 		       ip_set_eexist(ret, flags) ? 0 : ret;
23241d22f7bSJozsef Kadlecsik 	}
23341d22f7bSJozsef Kadlecsik 
2344fe198e6SJozsef Kadlecsik 	ip_to = ip;
23541d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_IP_TO]) {
23641d22f7bSJozsef Kadlecsik 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
23741d22f7bSJozsef Kadlecsik 		if (ret)
23841d22f7bSJozsef Kadlecsik 			return ret;
23941d22f7bSJozsef Kadlecsik 		if (ip > ip_to)
24041d22f7bSJozsef Kadlecsik 			swap(ip, ip_to);
24141d22f7bSJozsef Kadlecsik 	} else if (tb[IPSET_ATTR_CIDR]) {
242b3aabd14SJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
24341d22f7bSJozsef Kadlecsik 
244cabfd139SSergey Popovich 		if (!cidr || cidr > HOST_MASK)
24541d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
246e6146e86SJozsef Kadlecsik 		ip_set_mask_from_to(ip, ip_to, cidr);
247d0d9e0a5SJozsef Kadlecsik 	}
24841d22f7bSJozsef Kadlecsik 
2495d50e1d8SJozsef Kadlecsik 	port_to = port = ntohs(e.port);
250d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_PORT_TO]) {
25141d22f7bSJozsef Kadlecsik 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
25241d22f7bSJozsef Kadlecsik 		if (port > port_to)
25341d22f7bSJozsef Kadlecsik 			swap(port, port_to);
2545e0c1eb7SJozsef Kadlecsik 	}
2554fe198e6SJozsef Kadlecsik 
2564fe198e6SJozsef Kadlecsik 	ip2_to = ip2_from;
257d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_IP2_TO]) {
258d0d9e0a5SJozsef Kadlecsik 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
259d0d9e0a5SJozsef Kadlecsik 		if (ret)
260d0d9e0a5SJozsef Kadlecsik 			return ret;
261d0d9e0a5SJozsef Kadlecsik 		if (ip2_from > ip2_to)
262d0d9e0a5SJozsef Kadlecsik 			swap(ip2_from, ip2_to);
263d0d9e0a5SJozsef Kadlecsik 		if (ip2_from + UINT_MAX == ip2_to)
264d0d9e0a5SJozsef Kadlecsik 			return -IPSET_ERR_HASH_RANGE;
265ca0f6a5cSJozsef Kadlecsik 	} else {
2665d50e1d8SJozsef Kadlecsik 		ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
267ca0f6a5cSJozsef Kadlecsik 	}
26841d22f7bSJozsef Kadlecsik 
2690b8d9073SJozsef Kadlecsik 	if (retried) {
2706e27c9b4SJozsef Kadlecsik 		ip = ntohl(h->next.ip);
2710b8d9073SJozsef Kadlecsik 		p = ntohs(h->next.port);
2720b8d9073SJozsef Kadlecsik 		ip2 = ntohl(h->next.ip2);
2730b8d9073SJozsef Kadlecsik 	} else {
2740b8d9073SJozsef Kadlecsik 		p = port;
2750b8d9073SJozsef Kadlecsik 		ip2 = ip2_from;
2760b8d9073SJozsef Kadlecsik 	}
27748596a8dSJozsef Kadlecsik 	for (; ip <= ip_to; ip++) {
2785d50e1d8SJozsef Kadlecsik 		e.ip = htonl(ip);
2793d14b171SJozsef Kadlecsik 		for (; p <= port_to; p++) {
2805d50e1d8SJozsef Kadlecsik 			e.port = htons(p);
2810b8d9073SJozsef Kadlecsik 			do {
2825d50e1d8SJozsef Kadlecsik 				e.ip2 = htonl(ip2);
2830b8d9073SJozsef Kadlecsik 				ip2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr);
2845d50e1d8SJozsef Kadlecsik 				e.cidr = cidr - 1;
2855d50e1d8SJozsef Kadlecsik 				ret = adtfn(set, &e, &ext, &ext, flags);
28641d22f7bSJozsef Kadlecsik 
28741d22f7bSJozsef Kadlecsik 				if (ret && !ip_set_eexist(ret, flags))
28841d22f7bSJozsef Kadlecsik 					return ret;
289ca0f6a5cSJozsef Kadlecsik 
29041d22f7bSJozsef Kadlecsik 				ret = 0;
2910b8d9073SJozsef Kadlecsik 			} while (ip2++ < ip2_to);
2920b8d9073SJozsef Kadlecsik 			ip2 = ip2_from;
293d0d9e0a5SJozsef Kadlecsik 		}
2940b8d9073SJozsef Kadlecsik 		p = port;
2953d14b171SJozsef Kadlecsik 	}
29641d22f7bSJozsef Kadlecsik 	return ret;
29741d22f7bSJozsef Kadlecsik }
29841d22f7bSJozsef Kadlecsik 
29903c8b234SJozsef Kadlecsik /* IPv6 variant */
30041d22f7bSJozsef Kadlecsik 
30141d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem {
30241d22f7bSJozsef Kadlecsik 	union nf_inet_addr ip;
30341d22f7bSJozsef Kadlecsik 	union nf_inet_addr ip2;
30441d22f7bSJozsef Kadlecsik 	__be16 port;
3052a7cef2aSJozsef Kadlecsik 	u8 cidr:7;
3062a7cef2aSJozsef Kadlecsik 	u8 nomatch:1;
30741d22f7bSJozsef Kadlecsik 	u8 proto;
30841d22f7bSJozsef Kadlecsik };
30941d22f7bSJozsef Kadlecsik 
3105d50e1d8SJozsef Kadlecsik /* Common functions */
3115d50e1d8SJozsef Kadlecsik 
3128dea982aSJeremy Sowden static bool
31341d22f7bSJozsef Kadlecsik hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
31489dc79b7SJozsef Kadlecsik 			   const struct hash_ipportnet6_elem *ip2,
31589dc79b7SJozsef Kadlecsik 			   u32 *multi)
31641d22f7bSJozsef Kadlecsik {
31729e3b160SYOSHIFUJI Hideaki / 吉藤英明 	return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) &&
31829e3b160SYOSHIFUJI Hideaki / 吉藤英明 	       ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) &&
31941d22f7bSJozsef Kadlecsik 	       ip1->cidr == ip2->cidr &&
32041d22f7bSJozsef Kadlecsik 	       ip1->port == ip2->port &&
32141d22f7bSJozsef Kadlecsik 	       ip1->proto == ip2->proto;
32241d22f7bSJozsef Kadlecsik }
32341d22f7bSJozsef Kadlecsik 
3248dea982aSJeremy Sowden static int
3255d50e1d8SJozsef Kadlecsik hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem)
3262a7cef2aSJozsef Kadlecsik {
3273e0304a5SJozsef Kadlecsik 	return elem->nomatch ? -ENOTEMPTY : 1;
3282a7cef2aSJozsef Kadlecsik }
3292a7cef2aSJozsef Kadlecsik 
3308dea982aSJeremy Sowden static void
3315d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags)
33241d22f7bSJozsef Kadlecsik {
3335d50e1d8SJozsef Kadlecsik 	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
3345d50e1d8SJozsef Kadlecsik }
3355d50e1d8SJozsef Kadlecsik 
3368dea982aSJeremy Sowden static void
3375d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags)
3385d50e1d8SJozsef Kadlecsik {
3395d50e1d8SJozsef Kadlecsik 	swap(*flags, elem->nomatch);
34041d22f7bSJozsef Kadlecsik }
34141d22f7bSJozsef Kadlecsik 
3428dea982aSJeremy Sowden static void
34341d22f7bSJozsef Kadlecsik hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
34441d22f7bSJozsef Kadlecsik {
34541d22f7bSJozsef Kadlecsik 	ip6_netmask(&elem->ip2, cidr);
3462a7cef2aSJozsef Kadlecsik 	elem->cidr = cidr - 1;
34741d22f7bSJozsef Kadlecsik }
34841d22f7bSJozsef Kadlecsik 
34941d22f7bSJozsef Kadlecsik static bool
35041d22f7bSJozsef Kadlecsik hash_ipportnet6_data_list(struct sk_buff *skb,
35141d22f7bSJozsef Kadlecsik 			  const struct hash_ipportnet6_elem *data)
35241d22f7bSJozsef Kadlecsik {
3532a7cef2aSJozsef Kadlecsik 	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
3542a7cef2aSJozsef Kadlecsik 
3557cf7899dSDavid S. Miller 	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) ||
3567cf7899dSDavid S. Miller 	    nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) ||
3577cf7899dSDavid S. Miller 	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
3587cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
3597cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
3607cf7899dSDavid S. Miller 	    (flags &&
3617cf7899dSDavid S. Miller 	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
3627cf7899dSDavid S. Miller 		goto nla_put_failure;
363728a7e69SSergey Popovich 	return false;
36441d22f7bSJozsef Kadlecsik 
36541d22f7bSJozsef Kadlecsik nla_put_failure:
366728a7e69SSergey Popovich 	return true;
36741d22f7bSJozsef Kadlecsik }
36841d22f7bSJozsef Kadlecsik 
3698dea982aSJeremy Sowden static void
37021956ab2SJozsef Kadlecsik hash_ipportnet6_data_next(struct hash_ipportnet6_elem *next,
3715d50e1d8SJozsef Kadlecsik 			  const struct hash_ipportnet6_elem *d)
37241d22f7bSJozsef Kadlecsik {
3735d50e1d8SJozsef Kadlecsik 	next->port = d->port;
37441d22f7bSJozsef Kadlecsik }
37541d22f7bSJozsef Kadlecsik 
3765d50e1d8SJozsef Kadlecsik #undef MTYPE
37741d22f7bSJozsef Kadlecsik #undef HOST_MASK
37841d22f7bSJozsef Kadlecsik 
3795d50e1d8SJozsef Kadlecsik #define MTYPE		hash_ipportnet6
38041d22f7bSJozsef Kadlecsik #define HOST_MASK	128
3815d50e1d8SJozsef Kadlecsik #define IP_SET_EMIT_CREATE
3825d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h"
3833d14b171SJozsef Kadlecsik 
38441d22f7bSJozsef Kadlecsik static int
38541d22f7bSJozsef Kadlecsik hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
386b66554cfSJozsef Kadlecsik 		     const struct xt_action_param *par,
3875d50e1d8SJozsef Kadlecsik 		     enum ipset_adt adt, struct ip_set_adt_opt *opt)
38841d22f7bSJozsef Kadlecsik {
38921956ab2SJozsef Kadlecsik 	const struct hash_ipportnet6 *h = set->data;
39041d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
3915d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet6_elem e = {
392f690cbaeSJozsef Kadlecsik 		.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
3939b03a5efSJozsef Kadlecsik 	};
394ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
39541d22f7bSJozsef Kadlecsik 
39641d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST)
3975d50e1d8SJozsef Kadlecsik 		e.cidr = HOST_MASK - 1;
39841d22f7bSJozsef Kadlecsik 
399ac8cc925SJozsef Kadlecsik 	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
4005d50e1d8SJozsef Kadlecsik 				 &e.port, &e.proto))
40141d22f7bSJozsef Kadlecsik 		return -EINVAL;
40241d22f7bSJozsef Kadlecsik 
4035d50e1d8SJozsef Kadlecsik 	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
4045d50e1d8SJozsef Kadlecsik 	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
4055d50e1d8SJozsef Kadlecsik 	ip6_netmask(&e.ip2, e.cidr + 1);
40641d22f7bSJozsef Kadlecsik 
4075d50e1d8SJozsef Kadlecsik 	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
40841d22f7bSJozsef Kadlecsik }
40941d22f7bSJozsef Kadlecsik 
41041d22f7bSJozsef Kadlecsik static int
41141d22f7bSJozsef Kadlecsik hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
4123d14b171SJozsef Kadlecsik 		     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
41341d22f7bSJozsef Kadlecsik {
41421956ab2SJozsef Kadlecsik 	const struct hash_ipportnet6 *h = set->data;
41541d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
4165d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
417ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
41841d22f7bSJozsef Kadlecsik 	u32 port, port_to;
4195e0c1eb7SJozsef Kadlecsik 	bool with_ports = false;
4202a7cef2aSJozsef Kadlecsik 	u8 cidr;
42141d22f7bSJozsef Kadlecsik 	int ret;
42241d22f7bSJozsef Kadlecsik 
423a212e08eSSergey Popovich 	if (tb[IPSET_ATTR_LINENO])
424a212e08eSSergey Popovich 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
425a212e08eSSergey Popovich 
42641d22f7bSJozsef Kadlecsik 	if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
42741d22f7bSJozsef Kadlecsik 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
42841d22f7bSJozsef Kadlecsik 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
4292c227f27SSergey Popovich 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
43041d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_PROTOCOL;
431d0d9e0a5SJozsef Kadlecsik 	if (unlikely(tb[IPSET_ATTR_IP_TO]))
432d0d9e0a5SJozsef Kadlecsik 		return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
4332c227f27SSergey Popovich 	if (unlikely(tb[IPSET_ATTR_CIDR])) {
4348851e799SJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
4352c227f27SSergey Popovich 
4362c227f27SSergey Popovich 		if (cidr != HOST_MASK)
4372c227f27SSergey Popovich 			return -IPSET_ERR_INVALID_CIDR;
4382c227f27SSergey Popovich 	}
43941d22f7bSJozsef Kadlecsik 
4408e55d2e5SSergey Popovich 	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip);
4418e55d2e5SSergey Popovich 	if (ret)
4428e55d2e5SSergey Popovich 		return ret;
4438e55d2e5SSergey Popovich 
4448e55d2e5SSergey Popovich 	ret = ip_set_get_extensions(set, tb, &ext);
44541d22f7bSJozsef Kadlecsik 	if (ret)
44641d22f7bSJozsef Kadlecsik 		return ret;
44741d22f7bSJozsef Kadlecsik 
4485d50e1d8SJozsef Kadlecsik 	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2);
44941d22f7bSJozsef Kadlecsik 	if (ret)
45041d22f7bSJozsef Kadlecsik 		return ret;
45141d22f7bSJozsef Kadlecsik 
4522a7cef2aSJozsef Kadlecsik 	if (tb[IPSET_ATTR_CIDR2]) {
4532a7cef2aSJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
4542a7cef2aSJozsef Kadlecsik 		if (!cidr || cidr > HOST_MASK)
45541d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
4565d50e1d8SJozsef Kadlecsik 		e.cidr = cidr - 1;
4572a7cef2aSJozsef Kadlecsik 	}
45841d22f7bSJozsef Kadlecsik 
4595d50e1d8SJozsef Kadlecsik 	ip6_netmask(&e.ip2, e.cidr + 1);
46041d22f7bSJozsef Kadlecsik 
4615d50e1d8SJozsef Kadlecsik 	e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
46241d22f7bSJozsef Kadlecsik 
46341d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_PROTO]) {
4645d50e1d8SJozsef Kadlecsik 		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
4655d50e1d8SJozsef Kadlecsik 		with_ports = ip_set_proto_with_ports(e.proto);
46641d22f7bSJozsef Kadlecsik 
4675d50e1d8SJozsef Kadlecsik 		if (e.proto == 0)
46841d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_PROTO;
469ca0f6a5cSJozsef Kadlecsik 	} else {
47041d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_MISSING_PROTO;
471ca0f6a5cSJozsef Kadlecsik 	}
47241d22f7bSJozsef Kadlecsik 
4735d50e1d8SJozsef Kadlecsik 	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
4745d50e1d8SJozsef Kadlecsik 		e.port = 0;
47541d22f7bSJozsef Kadlecsik 
47643c56e59SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
4772a7cef2aSJozsef Kadlecsik 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
478ca0f6a5cSJozsef Kadlecsik 
4792a7cef2aSJozsef Kadlecsik 		if (cadt_flags & IPSET_FLAG_NOMATCH)
48043c56e59SJozsef Kadlecsik 			flags |= (IPSET_FLAG_NOMATCH << 16);
4812a7cef2aSJozsef Kadlecsik 	}
4822a7cef2aSJozsef Kadlecsik 
4835e0c1eb7SJozsef Kadlecsik 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
4845d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
4850f1799baSJozsef Kadlecsik 		return ip_set_enomatch(ret, flags, adt, set) ? -ret :
48643c56e59SJozsef Kadlecsik 		       ip_set_eexist(ret, flags) ? 0 : ret;
48741d22f7bSJozsef Kadlecsik 	}
48841d22f7bSJozsef Kadlecsik 
4895d50e1d8SJozsef Kadlecsik 	port = ntohs(e.port);
49041d22f7bSJozsef Kadlecsik 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
49141d22f7bSJozsef Kadlecsik 	if (port > port_to)
49241d22f7bSJozsef Kadlecsik 		swap(port, port_to);
49341d22f7bSJozsef Kadlecsik 
4943d14b171SJozsef Kadlecsik 	if (retried)
4956e27c9b4SJozsef Kadlecsik 		port = ntohs(h->next.port);
49641d22f7bSJozsef Kadlecsik 	for (; port <= port_to; port++) {
4975d50e1d8SJozsef Kadlecsik 		e.port = htons(port);
4985d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
49941d22f7bSJozsef Kadlecsik 
50041d22f7bSJozsef Kadlecsik 		if (ret && !ip_set_eexist(ret, flags))
50141d22f7bSJozsef Kadlecsik 			return ret;
502ca0f6a5cSJozsef Kadlecsik 
50341d22f7bSJozsef Kadlecsik 		ret = 0;
50441d22f7bSJozsef Kadlecsik 	}
50541d22f7bSJozsef Kadlecsik 	return ret;
50641d22f7bSJozsef Kadlecsik }
50741d22f7bSJozsef Kadlecsik 
50841d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = {
50941d22f7bSJozsef Kadlecsik 	.name		= "hash:ip,port,net",
51041d22f7bSJozsef Kadlecsik 	.protocol	= IPSET_PROTOCOL,
5113e0304a5SJozsef Kadlecsik 	.features	= IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 |
5123e0304a5SJozsef Kadlecsik 			  IPSET_TYPE_NOMATCH,
51341d22f7bSJozsef Kadlecsik 	.dimension	= IPSET_DIM_THREE,
514c15f1c83SJan Engelhardt 	.family		= NFPROTO_UNSPEC,
51535b8dcf8SJozsef Kadlecsik 	.revision_min	= IPSET_TYPE_REV_MIN,
51635b8dcf8SJozsef Kadlecsik 	.revision_max	= IPSET_TYPE_REV_MAX,
517*ccf0a4b7SJozsef Kadlecsik 	.create_flags[IPSET_TYPE_REV_MAX] = IPSET_CREATE_FLAG_BUCKETSIZE,
51841d22f7bSJozsef Kadlecsik 	.create		= hash_ipportnet_create,
51941d22f7bSJozsef Kadlecsik 	.create_policy	= {
52041d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_HASHSIZE]	= { .type = NLA_U32 },
52141d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_MAXELEM]	= { .type = NLA_U32 },
522*ccf0a4b7SJozsef Kadlecsik 		[IPSET_ATTR_BUCKETSIZE]	= { .type = NLA_U8 },
52341d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
52441d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
5255d50e1d8SJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
52641d22f7bSJozsef Kadlecsik 	},
52741d22f7bSJozsef Kadlecsik 	.adt_policy	= {
52841d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
52941d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP_TO]	= { .type = NLA_NESTED },
53041d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP2]	= { .type = NLA_NESTED },
531d0d9e0a5SJozsef Kadlecsik 		[IPSET_ATTR_IP2_TO]	= { .type = NLA_NESTED },
53241d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PORT]	= { .type = NLA_U16 },
53341d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PORT_TO]	= { .type = NLA_U16 },
53441d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
53541d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_CIDR2]	= { .type = NLA_U8 },
53641d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
5372a7cef2aSJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
53841d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
53941d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
54000d71b27SJozsef Kadlecsik 		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
54100d71b27SJozsef Kadlecsik 		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
54203726186SSergey Popovich 		[IPSET_ATTR_COMMENT]	= { .type = NLA_NUL_STRING,
54303726186SSergey Popovich 					    .len  = IPSET_MAX_COMMENT_SIZE },
544af331419SAnton Danilov 		[IPSET_ATTR_SKBMARK]	= { .type = NLA_U64 },
545af331419SAnton Danilov 		[IPSET_ATTR_SKBPRIO]	= { .type = NLA_U32 },
546af331419SAnton Danilov 		[IPSET_ATTR_SKBQUEUE]	= { .type = NLA_U16 },
54741d22f7bSJozsef Kadlecsik 	},
54841d22f7bSJozsef Kadlecsik 	.me		= THIS_MODULE,
54941d22f7bSJozsef Kadlecsik };
55041d22f7bSJozsef Kadlecsik 
55141d22f7bSJozsef Kadlecsik static int __init
55241d22f7bSJozsef Kadlecsik hash_ipportnet_init(void)
55341d22f7bSJozsef Kadlecsik {
55441d22f7bSJozsef Kadlecsik 	return ip_set_type_register(&hash_ipportnet_type);
55541d22f7bSJozsef Kadlecsik }
55641d22f7bSJozsef Kadlecsik 
55741d22f7bSJozsef Kadlecsik static void __exit
55841d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void)
55941d22f7bSJozsef Kadlecsik {
56018f84d41SJozsef Kadlecsik 	rcu_barrier();
56141d22f7bSJozsef Kadlecsik 	ip_set_type_unregister(&hash_ipportnet_type);
56241d22f7bSJozsef Kadlecsik }
56341d22f7bSJozsef Kadlecsik 
56441d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init);
56541d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini);
566