15d50e1d8SJozsef Kadlecsik /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
241d22f7bSJozsef Kadlecsik  *
341d22f7bSJozsef Kadlecsik  * This program is free software; you can redistribute it and/or modify
441d22f7bSJozsef Kadlecsik  * it under the terms of the GNU General Public License version 2 as
541d22f7bSJozsef Kadlecsik  * published by the Free Software Foundation.
641d22f7bSJozsef Kadlecsik  */
741d22f7bSJozsef Kadlecsik 
841d22f7bSJozsef Kadlecsik /* Kernel module implementing an IP set type: the hash:ip,port,net type */
941d22f7bSJozsef Kadlecsik 
1041d22f7bSJozsef Kadlecsik #include <linux/jhash.h>
1141d22f7bSJozsef Kadlecsik #include <linux/module.h>
1241d22f7bSJozsef Kadlecsik #include <linux/ip.h>
1341d22f7bSJozsef Kadlecsik #include <linux/skbuff.h>
1441d22f7bSJozsef Kadlecsik #include <linux/errno.h>
1541d22f7bSJozsef Kadlecsik #include <linux/random.h>
1641d22f7bSJozsef Kadlecsik #include <net/ip.h>
1741d22f7bSJozsef Kadlecsik #include <net/ipv6.h>
1841d22f7bSJozsef Kadlecsik #include <net/netlink.h>
1941d22f7bSJozsef Kadlecsik #include <net/tcp.h>
2041d22f7bSJozsef Kadlecsik 
2141d22f7bSJozsef Kadlecsik #include <linux/netfilter.h>
2241d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/pfxlen.h>
2341d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set.h>
2441d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_getport.h>
2541d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h>
2641d22f7bSJozsef Kadlecsik 
2735b8dcf8SJozsef Kadlecsik #define IPSET_TYPE_REV_MIN	0
2810111a6eSJozsef Kadlecsik /*				1    SCTP and UDPLITE support added */
2910111a6eSJozsef Kadlecsik /*				2    Range as input support for IPv4 added */
3000d71b27SJozsef Kadlecsik /*				3    nomatch flag support added */
31fda75c6dSOliver Smith /*				4    Counters support added */
3207cf8f5aSJosh Hunt /*				5    Comments support added */
33af331419SAnton Danilov /*				6    Forceadd support added */
34af331419SAnton Danilov #define IPSET_TYPE_REV_MAX	7 /* skbinfo support added */
3510111a6eSJozsef Kadlecsik 
3641d22f7bSJozsef Kadlecsik MODULE_LICENSE("GPL");
3741d22f7bSJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
3835b8dcf8SJozsef Kadlecsik IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
3941d22f7bSJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:ip,port,net");
4041d22f7bSJozsef Kadlecsik 
4141d22f7bSJozsef Kadlecsik /* Type specific function prefix */
425d50e1d8SJozsef Kadlecsik #define HTYPE		hash_ipportnet
4341d22f7bSJozsef Kadlecsik 
442a7cef2aSJozsef Kadlecsik /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
452a7cef2aSJozsef Kadlecsik  * However this way we have to store internally cidr - 1,
462a7cef2aSJozsef Kadlecsik  * dancing back and forth.
472a7cef2aSJozsef Kadlecsik  */
482a7cef2aSJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS_PACKED
495d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_PROTO
505d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS
512a7cef2aSJozsef Kadlecsik 
5203c8b234SJozsef Kadlecsik /* IPv4 variant */
535d50e1d8SJozsef Kadlecsik 
545d50e1d8SJozsef Kadlecsik /* Member elements */
5541d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem {
5641d22f7bSJozsef Kadlecsik 	__be32 ip;
5741d22f7bSJozsef Kadlecsik 	__be32 ip2;
5841d22f7bSJozsef Kadlecsik 	__be16 port;
592a7cef2aSJozsef Kadlecsik 	u8 cidr:7;
602a7cef2aSJozsef Kadlecsik 	u8 nomatch:1;
6141d22f7bSJozsef Kadlecsik 	u8 proto;
6241d22f7bSJozsef Kadlecsik };
6341d22f7bSJozsef Kadlecsik 
645d50e1d8SJozsef Kadlecsik /* Common functions */
655d50e1d8SJozsef Kadlecsik 
6641d22f7bSJozsef Kadlecsik static inline bool
6741d22f7bSJozsef Kadlecsik hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
6889dc79b7SJozsef Kadlecsik 			   const struct hash_ipportnet4_elem *ip2,
6989dc79b7SJozsef Kadlecsik 			   u32 *multi)
7041d22f7bSJozsef Kadlecsik {
7141d22f7bSJozsef Kadlecsik 	return ip1->ip == ip2->ip &&
7241d22f7bSJozsef Kadlecsik 	       ip1->ip2 == ip2->ip2 &&
7341d22f7bSJozsef Kadlecsik 	       ip1->cidr == ip2->cidr &&
7441d22f7bSJozsef Kadlecsik 	       ip1->port == ip2->port &&
7541d22f7bSJozsef Kadlecsik 	       ip1->proto == ip2->proto;
7641d22f7bSJozsef Kadlecsik }
7741d22f7bSJozsef Kadlecsik 
783e0304a5SJozsef Kadlecsik static inline int
795d50e1d8SJozsef Kadlecsik hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem)
802a7cef2aSJozsef Kadlecsik {
813e0304a5SJozsef Kadlecsik 	return elem->nomatch ? -ENOTEMPTY : 1;
822a7cef2aSJozsef Kadlecsik }
832a7cef2aSJozsef Kadlecsik 
842a7cef2aSJozsef Kadlecsik static inline void
855d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags)
865d50e1d8SJozsef Kadlecsik {
875d50e1d8SJozsef Kadlecsik 	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
885d50e1d8SJozsef Kadlecsik }
895d50e1d8SJozsef Kadlecsik 
905d50e1d8SJozsef Kadlecsik static inline void
915d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags)
925d50e1d8SJozsef Kadlecsik {
935d50e1d8SJozsef Kadlecsik 	swap(*flags, elem->nomatch);
945d50e1d8SJozsef Kadlecsik }
955d50e1d8SJozsef Kadlecsik 
965d50e1d8SJozsef Kadlecsik static inline void
9741d22f7bSJozsef Kadlecsik hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
9841d22f7bSJozsef Kadlecsik {
9941d22f7bSJozsef Kadlecsik 	elem->ip2 &= ip_set_netmask(cidr);
1002a7cef2aSJozsef Kadlecsik 	elem->cidr = cidr - 1;
10141d22f7bSJozsef Kadlecsik }
10241d22f7bSJozsef Kadlecsik 
10341d22f7bSJozsef Kadlecsik static bool
10441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_list(struct sk_buff *skb,
10541d22f7bSJozsef Kadlecsik 			  const struct hash_ipportnet4_elem *data)
10641d22f7bSJozsef Kadlecsik {
1072a7cef2aSJozsef Kadlecsik 	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
1082a7cef2aSJozsef Kadlecsik 
1097cf7899dSDavid S. Miller 	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) ||
1107cf7899dSDavid S. Miller 	    nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) ||
1117cf7899dSDavid S. Miller 	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
1127cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
1137cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
1147cf7899dSDavid S. Miller 	    (flags &&
1157cf7899dSDavid S. Miller 	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
1167cf7899dSDavid S. Miller 		goto nla_put_failure;
117728a7e69SSergey Popovich 	return false;
11841d22f7bSJozsef Kadlecsik 
11941d22f7bSJozsef Kadlecsik nla_put_failure:
120728a7e69SSergey Popovich 	return true;
12141d22f7bSJozsef Kadlecsik }
12241d22f7bSJozsef Kadlecsik 
1233d14b171SJozsef Kadlecsik static inline void
1245d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next,
1253d14b171SJozsef Kadlecsik 			  const struct hash_ipportnet4_elem *d)
1263d14b171SJozsef Kadlecsik {
1275d50e1d8SJozsef Kadlecsik 	next->ip = d->ip;
1285d50e1d8SJozsef Kadlecsik 	next->port = d->port;
1295d50e1d8SJozsef Kadlecsik 	next->ip2 = d->ip2;
1303d14b171SJozsef Kadlecsik }
1313d14b171SJozsef Kadlecsik 
1325d50e1d8SJozsef Kadlecsik #define MTYPE		hash_ipportnet4
1335d50e1d8SJozsef Kadlecsik #define HOST_MASK	32
1345d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h"
1355d50e1d8SJozsef Kadlecsik 
13641d22f7bSJozsef Kadlecsik static int
13741d22f7bSJozsef Kadlecsik hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
138b66554cfSJozsef Kadlecsik 		     const struct xt_action_param *par,
1395d50e1d8SJozsef Kadlecsik 		     enum ipset_adt adt, struct ip_set_adt_opt *opt)
14041d22f7bSJozsef Kadlecsik {
1415d50e1d8SJozsef Kadlecsik 	const struct hash_ipportnet *h = set->data;
14241d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
1435d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet4_elem e = {
144a04d8b6bSJozsef Kadlecsik 		.cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
1459b03a5efSJozsef Kadlecsik 	};
146ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
14741d22f7bSJozsef Kadlecsik 
14841d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST)
1495d50e1d8SJozsef Kadlecsik 		e.cidr = HOST_MASK - 1;
15041d22f7bSJozsef Kadlecsik 
151ac8cc925SJozsef Kadlecsik 	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
1525d50e1d8SJozsef Kadlecsik 				 &e.port, &e.proto))
15341d22f7bSJozsef Kadlecsik 		return -EINVAL;
15441d22f7bSJozsef Kadlecsik 
1555d50e1d8SJozsef Kadlecsik 	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
1565d50e1d8SJozsef Kadlecsik 	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
1575d50e1d8SJozsef Kadlecsik 	e.ip2 &= ip_set_netmask(e.cidr + 1);
15841d22f7bSJozsef Kadlecsik 
1595d50e1d8SJozsef Kadlecsik 	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
16041d22f7bSJozsef Kadlecsik }
16141d22f7bSJozsef Kadlecsik 
16241d22f7bSJozsef Kadlecsik static int
16341d22f7bSJozsef Kadlecsik hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
1643d14b171SJozsef Kadlecsik 		     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
16541d22f7bSJozsef Kadlecsik {
1665d50e1d8SJozsef Kadlecsik 	const struct hash_ipportnet *h = set->data;
16741d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
1685d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
169ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
17020b2fab4SJozsef Kadlecsik 	u32 ip = 0, ip_to = 0, p = 0, port, port_to;
17120b2fab4SJozsef Kadlecsik 	u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
1725e0c1eb7SJozsef Kadlecsik 	bool with_ports = false;
1732a7cef2aSJozsef Kadlecsik 	u8 cidr;
17441d22f7bSJozsef Kadlecsik 	int ret;
17541d22f7bSJozsef Kadlecsik 
176a212e08eSSergey Popovich 	if (tb[IPSET_ATTR_LINENO])
177a212e08eSSergey Popovich 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
178a212e08eSSergey Popovich 
17941d22f7bSJozsef Kadlecsik 	if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
18041d22f7bSJozsef Kadlecsik 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
18141d22f7bSJozsef Kadlecsik 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
1827dd37bc8SSergey Popovich 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
18341d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_PROTOCOL;
18441d22f7bSJozsef Kadlecsik 
1858e55d2e5SSergey Popovich 	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
1868e55d2e5SSergey Popovich 	if (ret)
1878e55d2e5SSergey Popovich 		return ret;
1888e55d2e5SSergey Popovich 
1898e55d2e5SSergey Popovich 	ret = ip_set_get_extensions(set, tb, &ext);
19041d22f7bSJozsef Kadlecsik 	if (ret)
19141d22f7bSJozsef Kadlecsik 		return ret;
19241d22f7bSJozsef Kadlecsik 
193d0d9e0a5SJozsef Kadlecsik 	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from);
19441d22f7bSJozsef Kadlecsik 	if (ret)
19541d22f7bSJozsef Kadlecsik 		return ret;
19641d22f7bSJozsef Kadlecsik 
197d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CIDR2]) {
1982a7cef2aSJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
1992a7cef2aSJozsef Kadlecsik 		if (!cidr || cidr > HOST_MASK)
20041d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
2015d50e1d8SJozsef Kadlecsik 		e.cidr = cidr - 1;
202d0d9e0a5SJozsef Kadlecsik 	}
20341d22f7bSJozsef Kadlecsik 
2045d50e1d8SJozsef Kadlecsik 	e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
20541d22f7bSJozsef Kadlecsik 
20641d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_PROTO]) {
2075d50e1d8SJozsef Kadlecsik 		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
2085d50e1d8SJozsef Kadlecsik 		with_ports = ip_set_proto_with_ports(e.proto);
20941d22f7bSJozsef Kadlecsik 
2105d50e1d8SJozsef Kadlecsik 		if (e.proto == 0)
21141d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_PROTO;
21241d22f7bSJozsef Kadlecsik 	} else
21341d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_MISSING_PROTO;
21441d22f7bSJozsef Kadlecsik 
2155d50e1d8SJozsef Kadlecsik 	if (!(with_ports || e.proto == IPPROTO_ICMP))
2165d50e1d8SJozsef Kadlecsik 		e.port = 0;
21741d22f7bSJozsef Kadlecsik 
21843c56e59SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
2192a7cef2aSJozsef Kadlecsik 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
2202a7cef2aSJozsef Kadlecsik 		if (cadt_flags & IPSET_FLAG_NOMATCH)
22143c56e59SJozsef Kadlecsik 			flags |= (IPSET_FLAG_NOMATCH << 16);
2222a7cef2aSJozsef Kadlecsik 	}
2232a7cef2aSJozsef Kadlecsik 
224d0d9e0a5SJozsef Kadlecsik 	with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
22541d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST ||
226d0d9e0a5SJozsef Kadlecsik 	    !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
227d0d9e0a5SJozsef Kadlecsik 	      tb[IPSET_ATTR_IP2_TO])) {
2285d50e1d8SJozsef Kadlecsik 		e.ip = htonl(ip);
2295d50e1d8SJozsef Kadlecsik 		e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1));
2305d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
2310f1799baSJozsef Kadlecsik 		return ip_set_enomatch(ret, flags, adt, set) ? -ret :
23243c56e59SJozsef Kadlecsik 		       ip_set_eexist(ret, flags) ? 0 : ret;
23341d22f7bSJozsef Kadlecsik 	}
23441d22f7bSJozsef Kadlecsik 
2354fe198e6SJozsef Kadlecsik 	ip_to = ip;
23641d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_IP_TO]) {
23741d22f7bSJozsef Kadlecsik 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
23841d22f7bSJozsef Kadlecsik 		if (ret)
23941d22f7bSJozsef Kadlecsik 			return ret;
24041d22f7bSJozsef Kadlecsik 		if (ip > ip_to)
24141d22f7bSJozsef Kadlecsik 			swap(ip, ip_to);
24241d22f7bSJozsef Kadlecsik 	} else if (tb[IPSET_ATTR_CIDR]) {
243b3aabd14SJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
24441d22f7bSJozsef Kadlecsik 
245cabfd139SSergey Popovich 		if (!cidr || cidr > HOST_MASK)
24641d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
247e6146e86SJozsef Kadlecsik 		ip_set_mask_from_to(ip, ip_to, cidr);
248d0d9e0a5SJozsef Kadlecsik 	}
24941d22f7bSJozsef Kadlecsik 
2505d50e1d8SJozsef Kadlecsik 	port_to = port = ntohs(e.port);
251d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_PORT_TO]) {
25241d22f7bSJozsef Kadlecsik 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
25341d22f7bSJozsef Kadlecsik 		if (port > port_to)
25441d22f7bSJozsef Kadlecsik 			swap(port, port_to);
2555e0c1eb7SJozsef Kadlecsik 	}
2564fe198e6SJozsef Kadlecsik 
2574fe198e6SJozsef Kadlecsik 	ip2_to = ip2_from;
258d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_IP2_TO]) {
259d0d9e0a5SJozsef Kadlecsik 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
260d0d9e0a5SJozsef Kadlecsik 		if (ret)
261d0d9e0a5SJozsef Kadlecsik 			return ret;
262d0d9e0a5SJozsef Kadlecsik 		if (ip2_from > ip2_to)
263d0d9e0a5SJozsef Kadlecsik 			swap(ip2_from, ip2_to);
264d0d9e0a5SJozsef Kadlecsik 		if (ip2_from + UINT_MAX == ip2_to)
265d0d9e0a5SJozsef Kadlecsik 			return -IPSET_ERR_HASH_RANGE;
2665d50e1d8SJozsef Kadlecsik 	} else
2675d50e1d8SJozsef Kadlecsik 		ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
26841d22f7bSJozsef Kadlecsik 
2693d14b171SJozsef Kadlecsik 	if (retried)
2706e27c9b4SJozsef Kadlecsik 		ip = ntohl(h->next.ip);
2713d14b171SJozsef Kadlecsik 	for (; !before(ip_to, ip); ip++) {
2725d50e1d8SJozsef Kadlecsik 		e.ip = htonl(ip);
2736e27c9b4SJozsef Kadlecsik 		p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
2746e27c9b4SJozsef Kadlecsik 						       : port;
2753d14b171SJozsef Kadlecsik 		for (; p <= port_to; p++) {
2765d50e1d8SJozsef Kadlecsik 			e.port = htons(p);
277ca134ce8SJozsef Kadlecsik 			ip2 = retried &&
278ca134ce8SJozsef Kadlecsik 			      ip == ntohl(h->next.ip) &&
279ca134ce8SJozsef Kadlecsik 			      p == ntohs(h->next.port)
2806e27c9b4SJozsef Kadlecsik 				? ntohl(h->next.ip2) : ip2_from;
281d0d9e0a5SJozsef Kadlecsik 			while (!after(ip2, ip2_to)) {
2825d50e1d8SJozsef Kadlecsik 				e.ip2 = htonl(ip2);
283d0d9e0a5SJozsef Kadlecsik 				ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
2842a7cef2aSJozsef Kadlecsik 								&cidr);
2855d50e1d8SJozsef Kadlecsik 				e.cidr = cidr - 1;
2865d50e1d8SJozsef Kadlecsik 				ret = adtfn(set, &e, &ext, &ext, flags);
28741d22f7bSJozsef Kadlecsik 
28841d22f7bSJozsef Kadlecsik 				if (ret && !ip_set_eexist(ret, flags))
28941d22f7bSJozsef Kadlecsik 					return ret;
29041d22f7bSJozsef Kadlecsik 				else
29141d22f7bSJozsef Kadlecsik 					ret = 0;
292d0d9e0a5SJozsef Kadlecsik 				ip2 = ip2_last + 1;
293d0d9e0a5SJozsef Kadlecsik 			}
29441d22f7bSJozsef Kadlecsik 		}
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 
31241d22f7bSJozsef Kadlecsik static inline 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 
3243e0304a5SJozsef Kadlecsik static inline 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 
3302a7cef2aSJozsef Kadlecsik static inline 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 
3365d50e1d8SJozsef Kadlecsik static inline 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 
34241d22f7bSJozsef Kadlecsik static inline 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 
3695d50e1d8SJozsef Kadlecsik static inline void
3705d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_next(struct hash_ipportnet4_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 {
3895d50e1d8SJozsef Kadlecsik 	const struct hash_ipportnet *h = set->data;
39041d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
3915d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet6_elem e = {
392a04d8b6bSJozsef Kadlecsik 		.cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
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 {
4145d50e1d8SJozsef Kadlecsik 	const struct hash_ipportnet *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])) {
4342c227f27SSergey Popovich 		u8 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;
46941d22f7bSJozsef Kadlecsik 	} else
47041d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_MISSING_PROTO;
47141d22f7bSJozsef Kadlecsik 
4725d50e1d8SJozsef Kadlecsik 	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
4735d50e1d8SJozsef Kadlecsik 		e.port = 0;
47441d22f7bSJozsef Kadlecsik 
47543c56e59SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
4762a7cef2aSJozsef Kadlecsik 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
4772a7cef2aSJozsef Kadlecsik 		if (cadt_flags & IPSET_FLAG_NOMATCH)
47843c56e59SJozsef Kadlecsik 			flags |= (IPSET_FLAG_NOMATCH << 16);
4792a7cef2aSJozsef Kadlecsik 	}
4802a7cef2aSJozsef Kadlecsik 
4815e0c1eb7SJozsef Kadlecsik 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
4825d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
4830f1799baSJozsef Kadlecsik 		return ip_set_enomatch(ret, flags, adt, set) ? -ret :
48443c56e59SJozsef Kadlecsik 		       ip_set_eexist(ret, flags) ? 0 : ret;
48541d22f7bSJozsef Kadlecsik 	}
48641d22f7bSJozsef Kadlecsik 
4875d50e1d8SJozsef Kadlecsik 	port = ntohs(e.port);
48841d22f7bSJozsef Kadlecsik 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
48941d22f7bSJozsef Kadlecsik 	if (port > port_to)
49041d22f7bSJozsef Kadlecsik 		swap(port, port_to);
49141d22f7bSJozsef Kadlecsik 
4923d14b171SJozsef Kadlecsik 	if (retried)
4936e27c9b4SJozsef Kadlecsik 		port = ntohs(h->next.port);
49441d22f7bSJozsef Kadlecsik 	for (; port <= port_to; port++) {
4955d50e1d8SJozsef Kadlecsik 		e.port = htons(port);
4965d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
49741d22f7bSJozsef Kadlecsik 
49841d22f7bSJozsef Kadlecsik 		if (ret && !ip_set_eexist(ret, flags))
49941d22f7bSJozsef Kadlecsik 			return ret;
50041d22f7bSJozsef Kadlecsik 		else
50141d22f7bSJozsef Kadlecsik 			ret = 0;
50241d22f7bSJozsef Kadlecsik 	}
50341d22f7bSJozsef Kadlecsik 	return ret;
50441d22f7bSJozsef Kadlecsik }
50541d22f7bSJozsef Kadlecsik 
50641d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = {
50741d22f7bSJozsef Kadlecsik 	.name		= "hash:ip,port,net",
50841d22f7bSJozsef Kadlecsik 	.protocol	= IPSET_PROTOCOL,
5093e0304a5SJozsef Kadlecsik 	.features	= IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 |
5103e0304a5SJozsef Kadlecsik 			  IPSET_TYPE_NOMATCH,
51141d22f7bSJozsef Kadlecsik 	.dimension	= IPSET_DIM_THREE,
512c15f1c83SJan Engelhardt 	.family		= NFPROTO_UNSPEC,
51335b8dcf8SJozsef Kadlecsik 	.revision_min	= IPSET_TYPE_REV_MIN,
51435b8dcf8SJozsef Kadlecsik 	.revision_max	= IPSET_TYPE_REV_MAX,
51541d22f7bSJozsef Kadlecsik 	.create		= hash_ipportnet_create,
51641d22f7bSJozsef Kadlecsik 	.create_policy	= {
51741d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_HASHSIZE]	= { .type = NLA_U32 },
51841d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_MAXELEM]	= { .type = NLA_U32 },
51941d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PROBES]	= { .type = NLA_U8 },
52041d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
52141d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
5225d50e1d8SJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
52341d22f7bSJozsef Kadlecsik 	},
52441d22f7bSJozsef Kadlecsik 	.adt_policy	= {
52541d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
52641d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP_TO]	= { .type = NLA_NESTED },
52741d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP2]	= { .type = NLA_NESTED },
528d0d9e0a5SJozsef Kadlecsik 		[IPSET_ATTR_IP2_TO]	= { .type = NLA_NESTED },
52941d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PORT]	= { .type = NLA_U16 },
53041d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PORT_TO]	= { .type = NLA_U16 },
53141d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
53241d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_CIDR2]	= { .type = NLA_U8 },
53341d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
5342a7cef2aSJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
53541d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
53641d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
53700d71b27SJozsef Kadlecsik 		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
53800d71b27SJozsef Kadlecsik 		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
53903726186SSergey Popovich 		[IPSET_ATTR_COMMENT]	= { .type = NLA_NUL_STRING,
54003726186SSergey Popovich 					    .len  = IPSET_MAX_COMMENT_SIZE },
541af331419SAnton Danilov 		[IPSET_ATTR_SKBMARK]	= { .type = NLA_U64 },
542af331419SAnton Danilov 		[IPSET_ATTR_SKBPRIO]	= { .type = NLA_U32 },
543af331419SAnton Danilov 		[IPSET_ATTR_SKBQUEUE]	= { .type = NLA_U16 },
54441d22f7bSJozsef Kadlecsik 	},
54541d22f7bSJozsef Kadlecsik 	.me		= THIS_MODULE,
54641d22f7bSJozsef Kadlecsik };
54741d22f7bSJozsef Kadlecsik 
54841d22f7bSJozsef Kadlecsik static int __init
54941d22f7bSJozsef Kadlecsik hash_ipportnet_init(void)
55041d22f7bSJozsef Kadlecsik {
55141d22f7bSJozsef Kadlecsik 	return ip_set_type_register(&hash_ipportnet_type);
55241d22f7bSJozsef Kadlecsik }
55341d22f7bSJozsef Kadlecsik 
55441d22f7bSJozsef Kadlecsik static void __exit
55541d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void)
55641d22f7bSJozsef Kadlecsik {
55741d22f7bSJozsef Kadlecsik 	ip_set_type_unregister(&hash_ipportnet_type);
55841d22f7bSJozsef Kadlecsik }
55941d22f7bSJozsef Kadlecsik 
56041d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init);
56141d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini);
562