netfilter: ipset: fix hash:net,port,net hang with /0 subnet
[ Upstream commit a31d47be64b9b74f8cfedffe03e0a8a1f9e51f23 ] The hash:net,port,net set type supports /0 subnets. However, the patch commit5f7b51bf09titled "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 patch5f7b51bf09brings it out earlier. Handle /0 subnets properly in hash:net,port,net set types. Fixes:5f7b51bf09("netfilter: ipset: Limit the maximal range of consecutive elements to add/delete") Reported-by: Марк Коренберг <socketpair@gmail.com> Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
774d259749
commit
6f19a38483
@@ -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);
|
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
|
static int
|
||||||
hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
|
hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
|
||||||
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
|
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];
|
ipset_adtfn adtfn = set->variant->adt[adt];
|
||||||
struct hash_netportnet4_elem e = { };
|
struct hash_netportnet4_elem e = { };
|
||||||
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
|
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
|
||||||
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
|
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
|
||||||
u32 ip2_from = 0, ip2_to = 0, ip2, ipn;
|
u32 ip2_from = 0, ip2_to = 0, ip2, i = 0;
|
||||||
u64 n = 0, m = 0;
|
|
||||||
bool with_ports = false;
|
bool with_ports = false;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -285,19 +294,6 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
|
|||||||
} else {
|
} else {
|
||||||
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
|
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) {
|
if (retried) {
|
||||||
ip = ntohl(h->next.ip[0]);
|
ip = ntohl(h->next.ip[0]);
|
||||||
@@ -310,13 +306,19 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
e.ip[0] = htonl(ip);
|
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++) {
|
for (; p <= port_to; p++) {
|
||||||
e.port = htons(p);
|
e.port = htons(p);
|
||||||
do {
|
do {
|
||||||
|
i++;
|
||||||
e.ip[1] = htonl(ip2);
|
e.ip[1] = htonl(ip2);
|
||||||
ip2 = ip_set_range_to_cidr(ip2, ip2_to,
|
if (i > IPSET_MAX_RANGE) {
|
||||||
&e.cidr[1]);
|
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);
|
ret = adtfn(set, &e, &ext, &ext, flags);
|
||||||
if (ret && !ip_set_eexist(ret, flags))
|
if (ret && !ip_set_eexist(ret, flags))
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
Reference in New Issue
Block a user