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 {
14121956ab2SJozsef Kadlecsik 	const struct hash_ipportnet4 *h = set->data;
14241d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
1435d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet4_elem e = {
144f690cbaeSJozsef Kadlecsik 		.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
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 {
16621956ab2SJozsef Kadlecsik 	const struct hash_ipportnet4 *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;
212ca0f6a5cSJozsef Kadlecsik 	} else {
21341d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_MISSING_PROTO;
214ca0f6a5cSJozsef Kadlecsik 	}
21541d22f7bSJozsef Kadlecsik 
2165d50e1d8SJozsef Kadlecsik 	if (!(with_ports || e.proto == IPPROTO_ICMP))
2175d50e1d8SJozsef Kadlecsik 		e.port = 0;
21841d22f7bSJozsef Kadlecsik 
21943c56e59SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
2202a7cef2aSJozsef Kadlecsik 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
221ca0f6a5cSJozsef Kadlecsik 
2222a7cef2aSJozsef Kadlecsik 		if (cadt_flags & IPSET_FLAG_NOMATCH)
22343c56e59SJozsef Kadlecsik 			flags |= (IPSET_FLAG_NOMATCH << 16);
2242a7cef2aSJozsef Kadlecsik 	}
2252a7cef2aSJozsef Kadlecsik 
226d0d9e0a5SJozsef Kadlecsik 	with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
22741d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST ||
228d0d9e0a5SJozsef Kadlecsik 	    !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
229d0d9e0a5SJozsef Kadlecsik 	      tb[IPSET_ATTR_IP2_TO])) {
2305d50e1d8SJozsef Kadlecsik 		e.ip = htonl(ip);
2315d50e1d8SJozsef Kadlecsik 		e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1));
2325d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
2330f1799baSJozsef Kadlecsik 		return ip_set_enomatch(ret, flags, adt, set) ? -ret :
23443c56e59SJozsef Kadlecsik 		       ip_set_eexist(ret, flags) ? 0 : ret;
23541d22f7bSJozsef Kadlecsik 	}
23641d22f7bSJozsef Kadlecsik 
2374fe198e6SJozsef Kadlecsik 	ip_to = ip;
23841d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_IP_TO]) {
23941d22f7bSJozsef Kadlecsik 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
24041d22f7bSJozsef Kadlecsik 		if (ret)
24141d22f7bSJozsef Kadlecsik 			return ret;
24241d22f7bSJozsef Kadlecsik 		if (ip > ip_to)
24341d22f7bSJozsef Kadlecsik 			swap(ip, ip_to);
24441d22f7bSJozsef Kadlecsik 	} else if (tb[IPSET_ATTR_CIDR]) {
245b3aabd14SJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
24641d22f7bSJozsef Kadlecsik 
247cabfd139SSergey Popovich 		if (!cidr || cidr > HOST_MASK)
24841d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
249e6146e86SJozsef Kadlecsik 		ip_set_mask_from_to(ip, ip_to, cidr);
250d0d9e0a5SJozsef Kadlecsik 	}
25141d22f7bSJozsef Kadlecsik 
2525d50e1d8SJozsef Kadlecsik 	port_to = port = ntohs(e.port);
253d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_PORT_TO]) {
25441d22f7bSJozsef Kadlecsik 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
25541d22f7bSJozsef Kadlecsik 		if (port > port_to)
25641d22f7bSJozsef Kadlecsik 			swap(port, port_to);
2575e0c1eb7SJozsef Kadlecsik 	}
2584fe198e6SJozsef Kadlecsik 
2594fe198e6SJozsef Kadlecsik 	ip2_to = ip2_from;
260d0d9e0a5SJozsef Kadlecsik 	if (tb[IPSET_ATTR_IP2_TO]) {
261d0d9e0a5SJozsef Kadlecsik 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
262d0d9e0a5SJozsef Kadlecsik 		if (ret)
263d0d9e0a5SJozsef Kadlecsik 			return ret;
264d0d9e0a5SJozsef Kadlecsik 		if (ip2_from > ip2_to)
265d0d9e0a5SJozsef Kadlecsik 			swap(ip2_from, ip2_to);
266d0d9e0a5SJozsef Kadlecsik 		if (ip2_from + UINT_MAX == ip2_to)
267d0d9e0a5SJozsef Kadlecsik 			return -IPSET_ERR_HASH_RANGE;
268ca0f6a5cSJozsef Kadlecsik 	} else {
2695d50e1d8SJozsef Kadlecsik 		ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
270ca0f6a5cSJozsef Kadlecsik 	}
27141d22f7bSJozsef Kadlecsik 
2723d14b171SJozsef Kadlecsik 	if (retried)
2736e27c9b4SJozsef Kadlecsik 		ip = ntohl(h->next.ip);
2743d14b171SJozsef Kadlecsik 	for (; !before(ip_to, ip); ip++) {
2755d50e1d8SJozsef Kadlecsik 		e.ip = htonl(ip);
2766e27c9b4SJozsef Kadlecsik 		p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
2776e27c9b4SJozsef Kadlecsik 						       : port;
2783d14b171SJozsef Kadlecsik 		for (; p <= port_to; p++) {
2795d50e1d8SJozsef Kadlecsik 			e.port = htons(p);
280ca134ce8SJozsef Kadlecsik 			ip2 = retried &&
281ca134ce8SJozsef Kadlecsik 			      ip == ntohl(h->next.ip) &&
282ca134ce8SJozsef Kadlecsik 			      p == ntohs(h->next.port)
2836e27c9b4SJozsef Kadlecsik 				? ntohl(h->next.ip2) : ip2_from;
284d0d9e0a5SJozsef Kadlecsik 			while (!after(ip2, ip2_to)) {
2855d50e1d8SJozsef Kadlecsik 				e.ip2 = htonl(ip2);
286d0d9e0a5SJozsef Kadlecsik 				ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
2872a7cef2aSJozsef Kadlecsik 								&cidr);
2885d50e1d8SJozsef Kadlecsik 				e.cidr = cidr - 1;
2895d50e1d8SJozsef Kadlecsik 				ret = adtfn(set, &e, &ext, &ext, flags);
29041d22f7bSJozsef Kadlecsik 
29141d22f7bSJozsef Kadlecsik 				if (ret && !ip_set_eexist(ret, flags))
29241d22f7bSJozsef Kadlecsik 					return ret;
293ca0f6a5cSJozsef Kadlecsik 
29441d22f7bSJozsef Kadlecsik 				ret = 0;
295d0d9e0a5SJozsef Kadlecsik 				ip2 = ip2_last + 1;
296d0d9e0a5SJozsef Kadlecsik 			}
29741d22f7bSJozsef Kadlecsik 		}
2983d14b171SJozsef Kadlecsik 	}
29941d22f7bSJozsef Kadlecsik 	return ret;
30041d22f7bSJozsef Kadlecsik }
30141d22f7bSJozsef Kadlecsik 
30203c8b234SJozsef Kadlecsik /* IPv6 variant */
30341d22f7bSJozsef Kadlecsik 
30441d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem {
30541d22f7bSJozsef Kadlecsik 	union nf_inet_addr ip;
30641d22f7bSJozsef Kadlecsik 	union nf_inet_addr ip2;
30741d22f7bSJozsef Kadlecsik 	__be16 port;
3082a7cef2aSJozsef Kadlecsik 	u8 cidr:7;
3092a7cef2aSJozsef Kadlecsik 	u8 nomatch:1;
31041d22f7bSJozsef Kadlecsik 	u8 proto;
31141d22f7bSJozsef Kadlecsik };
31241d22f7bSJozsef Kadlecsik 
3135d50e1d8SJozsef Kadlecsik /* Common functions */
3145d50e1d8SJozsef Kadlecsik 
31541d22f7bSJozsef Kadlecsik static inline bool
31641d22f7bSJozsef Kadlecsik hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
31789dc79b7SJozsef Kadlecsik 			   const struct hash_ipportnet6_elem *ip2,
31889dc79b7SJozsef Kadlecsik 			   u32 *multi)
31941d22f7bSJozsef Kadlecsik {
32029e3b160SYOSHIFUJI Hideaki / 吉藤英明 	return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) &&
32129e3b160SYOSHIFUJI Hideaki / 吉藤英明 	       ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) &&
32241d22f7bSJozsef Kadlecsik 	       ip1->cidr == ip2->cidr &&
32341d22f7bSJozsef Kadlecsik 	       ip1->port == ip2->port &&
32441d22f7bSJozsef Kadlecsik 	       ip1->proto == ip2->proto;
32541d22f7bSJozsef Kadlecsik }
32641d22f7bSJozsef Kadlecsik 
3273e0304a5SJozsef Kadlecsik static inline int
3285d50e1d8SJozsef Kadlecsik hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem)
3292a7cef2aSJozsef Kadlecsik {
3303e0304a5SJozsef Kadlecsik 	return elem->nomatch ? -ENOTEMPTY : 1;
3312a7cef2aSJozsef Kadlecsik }
3322a7cef2aSJozsef Kadlecsik 
3332a7cef2aSJozsef Kadlecsik static inline void
3345d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags)
33541d22f7bSJozsef Kadlecsik {
3365d50e1d8SJozsef Kadlecsik 	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
3375d50e1d8SJozsef Kadlecsik }
3385d50e1d8SJozsef Kadlecsik 
3395d50e1d8SJozsef Kadlecsik static inline void
3405d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags)
3415d50e1d8SJozsef Kadlecsik {
3425d50e1d8SJozsef Kadlecsik 	swap(*flags, elem->nomatch);
34341d22f7bSJozsef Kadlecsik }
34441d22f7bSJozsef Kadlecsik 
34541d22f7bSJozsef Kadlecsik static inline void
34641d22f7bSJozsef Kadlecsik hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
34741d22f7bSJozsef Kadlecsik {
34841d22f7bSJozsef Kadlecsik 	ip6_netmask(&elem->ip2, cidr);
3492a7cef2aSJozsef Kadlecsik 	elem->cidr = cidr - 1;
35041d22f7bSJozsef Kadlecsik }
35141d22f7bSJozsef Kadlecsik 
35241d22f7bSJozsef Kadlecsik static bool
35341d22f7bSJozsef Kadlecsik hash_ipportnet6_data_list(struct sk_buff *skb,
35441d22f7bSJozsef Kadlecsik 			  const struct hash_ipportnet6_elem *data)
35541d22f7bSJozsef Kadlecsik {
3562a7cef2aSJozsef Kadlecsik 	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
3572a7cef2aSJozsef Kadlecsik 
3587cf7899dSDavid S. Miller 	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) ||
3597cf7899dSDavid S. Miller 	    nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) ||
3607cf7899dSDavid S. Miller 	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
3617cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
3627cf7899dSDavid S. Miller 	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
3637cf7899dSDavid S. Miller 	    (flags &&
3647cf7899dSDavid S. Miller 	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
3657cf7899dSDavid S. Miller 		goto nla_put_failure;
366728a7e69SSergey Popovich 	return false;
36741d22f7bSJozsef Kadlecsik 
36841d22f7bSJozsef Kadlecsik nla_put_failure:
369728a7e69SSergey Popovich 	return true;
37041d22f7bSJozsef Kadlecsik }
37141d22f7bSJozsef Kadlecsik 
3725d50e1d8SJozsef Kadlecsik static inline void
37321956ab2SJozsef Kadlecsik hash_ipportnet6_data_next(struct hash_ipportnet6_elem *next,
3745d50e1d8SJozsef Kadlecsik 			  const struct hash_ipportnet6_elem *d)
37541d22f7bSJozsef Kadlecsik {
3765d50e1d8SJozsef Kadlecsik 	next->port = d->port;
37741d22f7bSJozsef Kadlecsik }
37841d22f7bSJozsef Kadlecsik 
3795d50e1d8SJozsef Kadlecsik #undef MTYPE
38041d22f7bSJozsef Kadlecsik #undef HOST_MASK
38141d22f7bSJozsef Kadlecsik 
3825d50e1d8SJozsef Kadlecsik #define MTYPE		hash_ipportnet6
38341d22f7bSJozsef Kadlecsik #define HOST_MASK	128
3845d50e1d8SJozsef Kadlecsik #define IP_SET_EMIT_CREATE
3855d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h"
3863d14b171SJozsef Kadlecsik 
38741d22f7bSJozsef Kadlecsik static int
38841d22f7bSJozsef Kadlecsik hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
389b66554cfSJozsef Kadlecsik 		     const struct xt_action_param *par,
3905d50e1d8SJozsef Kadlecsik 		     enum ipset_adt adt, struct ip_set_adt_opt *opt)
39141d22f7bSJozsef Kadlecsik {
39221956ab2SJozsef Kadlecsik 	const struct hash_ipportnet6 *h = set->data;
39341d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
3945d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet6_elem e = {
395f690cbaeSJozsef Kadlecsik 		.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
3969b03a5efSJozsef Kadlecsik 	};
397ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
39841d22f7bSJozsef Kadlecsik 
39941d22f7bSJozsef Kadlecsik 	if (adt == IPSET_TEST)
4005d50e1d8SJozsef Kadlecsik 		e.cidr = HOST_MASK - 1;
40141d22f7bSJozsef Kadlecsik 
402ac8cc925SJozsef Kadlecsik 	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
4035d50e1d8SJozsef Kadlecsik 				 &e.port, &e.proto))
40441d22f7bSJozsef Kadlecsik 		return -EINVAL;
40541d22f7bSJozsef Kadlecsik 
4065d50e1d8SJozsef Kadlecsik 	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
4075d50e1d8SJozsef Kadlecsik 	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
4085d50e1d8SJozsef Kadlecsik 	ip6_netmask(&e.ip2, e.cidr + 1);
40941d22f7bSJozsef Kadlecsik 
4105d50e1d8SJozsef Kadlecsik 	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
41141d22f7bSJozsef Kadlecsik }
41241d22f7bSJozsef Kadlecsik 
41341d22f7bSJozsef Kadlecsik static int
41441d22f7bSJozsef Kadlecsik hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
4153d14b171SJozsef Kadlecsik 		     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
41641d22f7bSJozsef Kadlecsik {
41721956ab2SJozsef Kadlecsik 	const struct hash_ipportnet6 *h = set->data;
41841d22f7bSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
4195d50e1d8SJozsef Kadlecsik 	struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
420ca134ce8SJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
42141d22f7bSJozsef Kadlecsik 	u32 port, port_to;
4225e0c1eb7SJozsef Kadlecsik 	bool with_ports = false;
4232a7cef2aSJozsef Kadlecsik 	u8 cidr;
42441d22f7bSJozsef Kadlecsik 	int ret;
42541d22f7bSJozsef Kadlecsik 
426a212e08eSSergey Popovich 	if (tb[IPSET_ATTR_LINENO])
427a212e08eSSergey Popovich 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
428a212e08eSSergey Popovich 
42941d22f7bSJozsef Kadlecsik 	if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
43041d22f7bSJozsef Kadlecsik 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
43141d22f7bSJozsef Kadlecsik 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
4322c227f27SSergey Popovich 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
43341d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_PROTOCOL;
434d0d9e0a5SJozsef Kadlecsik 	if (unlikely(tb[IPSET_ATTR_IP_TO]))
435d0d9e0a5SJozsef Kadlecsik 		return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
4362c227f27SSergey Popovich 	if (unlikely(tb[IPSET_ATTR_CIDR])) {
4378851e799SJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
4382c227f27SSergey Popovich 
4392c227f27SSergey Popovich 		if (cidr != HOST_MASK)
4402c227f27SSergey Popovich 			return -IPSET_ERR_INVALID_CIDR;
4412c227f27SSergey Popovich 	}
44241d22f7bSJozsef Kadlecsik 
4438e55d2e5SSergey Popovich 	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip);
4448e55d2e5SSergey Popovich 	if (ret)
4458e55d2e5SSergey Popovich 		return ret;
4468e55d2e5SSergey Popovich 
4478e55d2e5SSergey Popovich 	ret = ip_set_get_extensions(set, tb, &ext);
44841d22f7bSJozsef Kadlecsik 	if (ret)
44941d22f7bSJozsef Kadlecsik 		return ret;
45041d22f7bSJozsef Kadlecsik 
4515d50e1d8SJozsef Kadlecsik 	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2);
45241d22f7bSJozsef Kadlecsik 	if (ret)
45341d22f7bSJozsef Kadlecsik 		return ret;
45441d22f7bSJozsef Kadlecsik 
4552a7cef2aSJozsef Kadlecsik 	if (tb[IPSET_ATTR_CIDR2]) {
4562a7cef2aSJozsef Kadlecsik 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
4572a7cef2aSJozsef Kadlecsik 		if (!cidr || cidr > HOST_MASK)
45841d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_CIDR;
4595d50e1d8SJozsef Kadlecsik 		e.cidr = cidr - 1;
4602a7cef2aSJozsef Kadlecsik 	}
46141d22f7bSJozsef Kadlecsik 
4625d50e1d8SJozsef Kadlecsik 	ip6_netmask(&e.ip2, e.cidr + 1);
46341d22f7bSJozsef Kadlecsik 
4645d50e1d8SJozsef Kadlecsik 	e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
46541d22f7bSJozsef Kadlecsik 
46641d22f7bSJozsef Kadlecsik 	if (tb[IPSET_ATTR_PROTO]) {
4675d50e1d8SJozsef Kadlecsik 		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
4685d50e1d8SJozsef Kadlecsik 		with_ports = ip_set_proto_with_ports(e.proto);
46941d22f7bSJozsef Kadlecsik 
4705d50e1d8SJozsef Kadlecsik 		if (e.proto == 0)
47141d22f7bSJozsef Kadlecsik 			return -IPSET_ERR_INVALID_PROTO;
472ca0f6a5cSJozsef Kadlecsik 	} else {
47341d22f7bSJozsef Kadlecsik 		return -IPSET_ERR_MISSING_PROTO;
474ca0f6a5cSJozsef Kadlecsik 	}
47541d22f7bSJozsef Kadlecsik 
4765d50e1d8SJozsef Kadlecsik 	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
4775d50e1d8SJozsef Kadlecsik 		e.port = 0;
47841d22f7bSJozsef Kadlecsik 
47943c56e59SJozsef Kadlecsik 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
4802a7cef2aSJozsef Kadlecsik 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
481ca0f6a5cSJozsef Kadlecsik 
4822a7cef2aSJozsef Kadlecsik 		if (cadt_flags & IPSET_FLAG_NOMATCH)
48343c56e59SJozsef Kadlecsik 			flags |= (IPSET_FLAG_NOMATCH << 16);
4842a7cef2aSJozsef Kadlecsik 	}
4852a7cef2aSJozsef Kadlecsik 
4865e0c1eb7SJozsef Kadlecsik 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
4875d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
4880f1799baSJozsef Kadlecsik 		return ip_set_enomatch(ret, flags, adt, set) ? -ret :
48943c56e59SJozsef Kadlecsik 		       ip_set_eexist(ret, flags) ? 0 : ret;
49041d22f7bSJozsef Kadlecsik 	}
49141d22f7bSJozsef Kadlecsik 
4925d50e1d8SJozsef Kadlecsik 	port = ntohs(e.port);
49341d22f7bSJozsef Kadlecsik 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
49441d22f7bSJozsef Kadlecsik 	if (port > port_to)
49541d22f7bSJozsef Kadlecsik 		swap(port, port_to);
49641d22f7bSJozsef Kadlecsik 
4973d14b171SJozsef Kadlecsik 	if (retried)
4986e27c9b4SJozsef Kadlecsik 		port = ntohs(h->next.port);
49941d22f7bSJozsef Kadlecsik 	for (; port <= port_to; port++) {
5005d50e1d8SJozsef Kadlecsik 		e.port = htons(port);
5015d50e1d8SJozsef Kadlecsik 		ret = adtfn(set, &e, &ext, &ext, flags);
50241d22f7bSJozsef Kadlecsik 
50341d22f7bSJozsef Kadlecsik 		if (ret && !ip_set_eexist(ret, flags))
50441d22f7bSJozsef Kadlecsik 			return ret;
505ca0f6a5cSJozsef Kadlecsik 
50641d22f7bSJozsef Kadlecsik 		ret = 0;
50741d22f7bSJozsef Kadlecsik 	}
50841d22f7bSJozsef Kadlecsik 	return ret;
50941d22f7bSJozsef Kadlecsik }
51041d22f7bSJozsef Kadlecsik 
51141d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = {
51241d22f7bSJozsef Kadlecsik 	.name		= "hash:ip,port,net",
51341d22f7bSJozsef Kadlecsik 	.protocol	= IPSET_PROTOCOL,
5143e0304a5SJozsef Kadlecsik 	.features	= IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 |
5153e0304a5SJozsef Kadlecsik 			  IPSET_TYPE_NOMATCH,
51641d22f7bSJozsef Kadlecsik 	.dimension	= IPSET_DIM_THREE,
517c15f1c83SJan Engelhardt 	.family		= NFPROTO_UNSPEC,
51835b8dcf8SJozsef Kadlecsik 	.revision_min	= IPSET_TYPE_REV_MIN,
51935b8dcf8SJozsef Kadlecsik 	.revision_max	= IPSET_TYPE_REV_MAX,
52041d22f7bSJozsef Kadlecsik 	.create		= hash_ipportnet_create,
52141d22f7bSJozsef Kadlecsik 	.create_policy	= {
52241d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_HASHSIZE]	= { .type = NLA_U32 },
52341d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_MAXELEM]	= { .type = NLA_U32 },
52441d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PROBES]	= { .type = NLA_U8 },
52541d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
52641d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
5275d50e1d8SJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
52841d22f7bSJozsef Kadlecsik 	},
52941d22f7bSJozsef Kadlecsik 	.adt_policy	= {
53041d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
53141d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP_TO]	= { .type = NLA_NESTED },
53241d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_IP2]	= { .type = NLA_NESTED },
533d0d9e0a5SJozsef Kadlecsik 		[IPSET_ATTR_IP2_TO]	= { .type = NLA_NESTED },
53441d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PORT]	= { .type = NLA_U16 },
53541d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PORT_TO]	= { .type = NLA_U16 },
53641d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
53741d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_CIDR2]	= { .type = NLA_U8 },
53841d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
5392a7cef2aSJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
54041d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
54141d22f7bSJozsef Kadlecsik 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
54200d71b27SJozsef Kadlecsik 		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
54300d71b27SJozsef Kadlecsik 		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
54403726186SSergey Popovich 		[IPSET_ATTR_COMMENT]	= { .type = NLA_NUL_STRING,
54503726186SSergey Popovich 					    .len  = IPSET_MAX_COMMENT_SIZE },
546af331419SAnton Danilov 		[IPSET_ATTR_SKBMARK]	= { .type = NLA_U64 },
547af331419SAnton Danilov 		[IPSET_ATTR_SKBPRIO]	= { .type = NLA_U32 },
548af331419SAnton Danilov 		[IPSET_ATTR_SKBQUEUE]	= { .type = NLA_U16 },
54941d22f7bSJozsef Kadlecsik 	},
55041d22f7bSJozsef Kadlecsik 	.me		= THIS_MODULE,
55141d22f7bSJozsef Kadlecsik };
55241d22f7bSJozsef Kadlecsik 
55341d22f7bSJozsef Kadlecsik static int __init
55441d22f7bSJozsef Kadlecsik hash_ipportnet_init(void)
55541d22f7bSJozsef Kadlecsik {
55641d22f7bSJozsef Kadlecsik 	return ip_set_type_register(&hash_ipportnet_type);
55741d22f7bSJozsef Kadlecsik }
55841d22f7bSJozsef Kadlecsik 
55941d22f7bSJozsef Kadlecsik static void __exit
56041d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void)
56141d22f7bSJozsef Kadlecsik {
56218f84d41SJozsef Kadlecsik 	rcu_barrier();
56341d22f7bSJozsef Kadlecsik 	ip_set_type_unregister(&hash_ipportnet_type);
56441d22f7bSJozsef Kadlecsik }
56541d22f7bSJozsef Kadlecsik 
56641d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init);
56741d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini);
568