Commit a31d47be authored by Jozsef Kadlecsik's avatar Jozsef Kadlecsik Committed by Pablo Neira Ayuso
Browse files

netfilter: ipset: fix hash:net,port,net hang with /0 subnet



The hash:net,port,net set type supports /0 subnets. However, the patch
commit 5f7b51bf titled "netfilter: ipset: Limit the maximal range
of consecutive elements to add/delete" did not take into account it and
resulted in an endless loop. The bug is actually older but the patch
5f7b51bf brings it out earlier.

Handle /0 subnets properly in hash:net,port,net set types.

Fixes: 5f7b51bf ("netfilter: ipset: Limit the maximal range of consecutive elements to add/delete")
Reported-by: default avatarМарк Коренберг <socketpair@gmail.com>
Signed-off-by: default avatarJozsef Kadlecsik <kadlec@netfilter.org>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 123b9961
Loading
Loading
Loading
Loading
+21 −19
Original line number Diff line number Diff line
@@ -173,17 +173,26 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}

static u32
hash_netportnet4_range_to_cidr(u32 from, u32 to, u8 *cidr)
{
	if (from == 0 && to == UINT_MAX) {
		*cidr = 0;
		return to;
	}
	return ip_set_range_to_cidr(from, to, cidr);
}

static int
hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
		      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
	const struct hash_netportnet4 *h = set->data;
	struct hash_netportnet4 *h = set->data;
	ipset_adtfn adtfn = set->variant->adt[adt];
	struct hash_netportnet4_elem e = { };
	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
	u32 ip = 0, ip_to = 0, p = 0, port, port_to;
	u32 ip2_from = 0, ip2_to = 0, ip2, ipn;
	u64 n = 0, m = 0;
	u32 ip2_from = 0, ip2_to = 0, ip2, i = 0;
	bool with_ports = false;
	int ret;

@@ -285,19 +294,6 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
	} else {
		ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
	}
	ipn = ip;
	do {
		ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]);
		n++;
	} while (ipn++ < ip_to);
	ipn = ip2_from;
	do {
		ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]);
		m++;
	} while (ipn++ < ip2_to);

	if (n*m*(port_to - port + 1) > IPSET_MAX_RANGE)
		return -ERANGE;

	if (retried) {
		ip = ntohl(h->next.ip[0]);
@@ -310,13 +306,19 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],

	do {
		e.ip[0] = htonl(ip);
		ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
		ip = hash_netportnet4_range_to_cidr(ip, ip_to, &e.cidr[0]);
		for (; p <= port_to; p++) {
			e.port = htons(p);
			do {
				i++;
				e.ip[1] = htonl(ip2);
				ip2 = ip_set_range_to_cidr(ip2, ip2_to,
							   &e.cidr[1]);
				if (i > IPSET_MAX_RANGE) {
					hash_netportnet4_data_next(&h->next,
								   &e);
					return -ERANGE;
				}
				ip2 = hash_netportnet4_range_to_cidr(ip2,
							ip2_to, &e.cidr[1]);
				ret = adtfn(set, &e, &ext, &ext, flags);
				if (ret && !ip_set_eexist(ret, flags))
					return ret;