Revert "icmp: change the order of rate limits"

This reverts commit 997ba88896 which is
commit 8c2bd38b95f75f3d2a08c93e35303e26d480d24e upstream.

It breaks the Android kernel abi and can be brought back in the future
in an abi-safe way if it is really needed.

Bug: 161946584
Change-Id: Ia2a9191d440cf2ed5526ff624bb2d637d2325b17
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2024-11-11 14:20:45 +00:00
parent d181caa76b
commit da03308716
3 changed files with 58 additions and 77 deletions

View File

@@ -772,8 +772,6 @@ static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
}
bool icmp_global_allow(void);
void icmp_global_consume(void);
extern int sysctl_icmp_msgs_per_sec;
extern int sysctl_icmp_msgs_burst;

View File

@@ -222,59 +222,57 @@ int sysctl_icmp_msgs_per_sec __read_mostly = 1000;
int sysctl_icmp_msgs_burst __read_mostly = 50;
static struct {
atomic_t credit;
spinlock_t lock;
u32 credit;
u32 stamp;
} icmp_global;
} icmp_global = {
.lock = __SPIN_LOCK_UNLOCKED(icmp_global.lock),
};
/**
* icmp_global_allow - Are we allowed to send one more ICMP message ?
*
* Uses a token bucket to limit our ICMP messages to ~sysctl_icmp_msgs_per_sec.
* Returns false if we reached the limit and can not send another packet.
* Works in tandem with icmp_global_consume().
* Note: called with BH disabled
*/
bool icmp_global_allow(void)
{
u32 delta, now, oldstamp;
int incr, new, old;
u32 credit, delta, incr = 0, now = (u32)jiffies;
bool rc = false;
/* Note: many cpus could find this condition true.
* Then later icmp_global_consume() could consume more credits,
* this is an acceptable race.
/* Check if token bucket is empty and cannot be refilled
* without taking the spinlock. The READ_ONCE() are paired
* with the following WRITE_ONCE() in this same function.
*/
if (atomic_read(&icmp_global.credit) > 0)
return true;
now = jiffies;
oldstamp = READ_ONCE(icmp_global.stamp);
delta = min_t(u32, now - oldstamp, HZ);
if (delta < HZ / 50)
return false;
incr = READ_ONCE(sysctl_icmp_msgs_per_sec) * delta / HZ;
if (!incr)
return false;
if (cmpxchg(&icmp_global.stamp, oldstamp, now) == oldstamp) {
old = atomic_read(&icmp_global.credit);
do {
new = min(old + incr, READ_ONCE(sysctl_icmp_msgs_burst));
} while (!atomic_try_cmpxchg(&icmp_global.credit, &old, new));
if (!READ_ONCE(icmp_global.credit)) {
delta = min_t(u32, now - READ_ONCE(icmp_global.stamp), HZ);
if (delta < HZ / 50)
return false;
}
return true;
spin_lock(&icmp_global.lock);
delta = min_t(u32, now - icmp_global.stamp, HZ);
if (delta >= HZ / 50) {
incr = READ_ONCE(sysctl_icmp_msgs_per_sec) * delta / HZ;
if (incr)
WRITE_ONCE(icmp_global.stamp, now);
}
credit = min_t(u32, icmp_global.credit + incr,
READ_ONCE(sysctl_icmp_msgs_burst));
if (credit) {
/* We want to use a credit of one in average, but need to randomize
* it for security reasons.
*/
credit = max_t(int, credit - prandom_u32_max(3), 0);
rc = true;
}
WRITE_ONCE(icmp_global.credit, credit);
spin_unlock(&icmp_global.lock);
return rc;
}
EXPORT_SYMBOL(icmp_global_allow);
void icmp_global_consume(void)
{
int credits = get_random_u32_below(3);
/* Note: this might make icmp_global.credit negative. */
if (credits)
atomic_sub(credits, &icmp_global.credit);
}
EXPORT_SYMBOL(icmp_global_consume);
static bool icmpv4_mask_allow(struct net *net, int type, int code)
{
if (type > NR_ICMP_TYPES)
@@ -291,16 +289,14 @@ static bool icmpv4_mask_allow(struct net *net, int type, int code)
return false;
}
static bool icmpv4_global_allow(struct net *net, int type, int code,
bool *apply_ratelimit)
static bool icmpv4_global_allow(struct net *net, int type, int code)
{
if (icmpv4_mask_allow(net, type, code))
return true;
if (icmp_global_allow()) {
*apply_ratelimit = true;
if (icmp_global_allow())
return true;
}
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);
return false;
}
@@ -310,16 +306,15 @@ static bool icmpv4_global_allow(struct net *net, int type, int code,
*/
static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
struct flowi4 *fl4, int type, int code,
bool apply_ratelimit)
struct flowi4 *fl4, int type, int code)
{
struct dst_entry *dst = &rt->dst;
struct inet_peer *peer;
bool rc = true;
int vif;
if (!apply_ratelimit)
return true;
if (icmpv4_mask_allow(net, type, code))
goto out;
/* No rate limit on loopback */
if (dst->dev && (dst->dev->flags&IFF_LOOPBACK))
@@ -334,8 +329,6 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
out:
if (!rc)
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);
else
icmp_global_consume();
return rc;
}
@@ -407,7 +400,6 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
struct ipcm_cookie ipc;
struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(rt->dst.dev);
bool apply_ratelimit = false;
struct flowi4 fl4;
struct sock *sk;
struct inet_sock *inet;
@@ -419,11 +411,11 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
if (ip_options_echo(net, &icmp_param->replyopts.opt.opt, skb))
return;
/* Needed by both icmpv4_global_allow and icmp_xmit_lock */
/* Needed by both icmp_global_allow and icmp_xmit_lock */
local_bh_disable();
/* is global icmp_msgs_per_sec exhausted ? */
if (!icmpv4_global_allow(net, type, code, &apply_ratelimit))
/* global icmp_msgs_per_sec */
if (!icmpv4_global_allow(net, type, code))
goto out_bh_enable;
sk = icmp_xmit_lock(net);
@@ -456,7 +448,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
goto out_unlock;
if (icmpv4_xrlim_allow(net, rt, &fl4, type, code, apply_ratelimit))
if (icmpv4_xrlim_allow(net, rt, &fl4, type, code))
icmp_push_reply(sk, icmp_param, &fl4, &ipc, &rt);
ip_rt_put(rt);
out_unlock:
@@ -600,7 +592,6 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
int room;
struct icmp_bxm icmp_param;
struct rtable *rt = skb_rtable(skb_in);
bool apply_ratelimit = false;
struct ipcm_cookie ipc;
struct flowi4 fl4;
__be32 saddr;
@@ -682,7 +673,7 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
}
}
/* Needed by both icmpv4_global_allow and icmp_xmit_lock */
/* Needed by both icmp_global_allow and icmp_xmit_lock */
local_bh_disable();
/* Check global sysctl_icmp_msgs_per_sec ratelimit, unless
@@ -690,7 +681,7 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
* loopback, then peer ratelimit still work (in icmpv4_xrlim_allow)
*/
if (!(skb_in->dev && (skb_in->dev->flags&IFF_LOOPBACK)) &&
!icmpv4_global_allow(net, type, code, &apply_ratelimit))
!icmpv4_global_allow(net, type, code))
goto out_bh_enable;
sk = icmp_xmit_lock(net);
@@ -749,7 +740,7 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
goto out_unlock;
/* peer icmp_ratelimit */
if (!icmpv4_xrlim_allow(net, rt, &fl4, type, code, apply_ratelimit))
if (!icmpv4_xrlim_allow(net, rt, &fl4, type, code))
goto ende;
/* RFC says return as much as we can without exceeding 576 bytes. */

View File

@@ -175,16 +175,14 @@ static bool icmpv6_mask_allow(struct net *net, int type)
return false;
}
static bool icmpv6_global_allow(struct net *net, int type,
bool *apply_ratelimit)
static bool icmpv6_global_allow(struct net *net, int type)
{
if (icmpv6_mask_allow(net, type))
return true;
if (icmp_global_allow()) {
*apply_ratelimit = true;
if (icmp_global_allow())
return true;
}
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);
return false;
}
@@ -193,13 +191,13 @@ static bool icmpv6_global_allow(struct net *net, int type,
* Check the ICMP output rate limit
*/
static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
struct flowi6 *fl6, bool apply_ratelimit)
struct flowi6 *fl6)
{
struct net *net = sock_net(sk);
struct dst_entry *dst;
bool res = false;
if (!apply_ratelimit)
if (icmpv6_mask_allow(net, type))
return true;
/*
@@ -230,8 +228,6 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
if (!res)
__ICMP6_INC_STATS(net, ip6_dst_idev(dst),
ICMP6_MIB_RATELIMITHOST);
else
icmp_global_consume();
dst_release(dst);
return res;
}
@@ -458,7 +454,6 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
struct net *net;
struct ipv6_pinfo *np;
const struct in6_addr *saddr = NULL;
bool apply_ratelimit = false;
struct dst_entry *dst;
struct icmp6hdr tmp_hdr;
struct flowi6 fl6;
@@ -540,12 +535,11 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
return;
}
/* Needed by both icmpv6_global_allow and icmpv6_xmit_lock */
/* Needed by both icmp_global_allow and icmpv6_xmit_lock */
local_bh_disable();
/* Check global sysctl_icmp_msgs_per_sec ratelimit */
if (!(skb->dev->flags & IFF_LOOPBACK) &&
!icmpv6_global_allow(net, type, &apply_ratelimit))
if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type))
goto out_bh_enable;
mip6_addr_swap(skb, parm);
@@ -583,7 +577,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
np = inet6_sk(sk);
if (!icmpv6_xrlim_allow(sk, type, &fl6, apply_ratelimit))
if (!icmpv6_xrlim_allow(sk, type, &fl6))
goto out;
tmp_hdr.icmp6_type = type;
@@ -725,7 +719,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
struct ipv6_pinfo *np;
const struct in6_addr *saddr = NULL;
struct icmp6hdr *icmph = icmp6_hdr(skb);
bool apply_ratelimit = false;
struct icmp6hdr tmp_hdr;
struct flowi6 fl6;
struct icmpv6_msg msg;
@@ -789,9 +782,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
goto out;
/* Check the ratelimit */
if ((!(skb->dev->flags & IFF_LOOPBACK) &&
!icmpv6_global_allow(net, ICMPV6_ECHO_REPLY, &apply_ratelimit)) ||
!icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6, apply_ratelimit))
if ((!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, ICMPV6_ECHO_REPLY)) ||
!icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6))
goto out_dst_release;
idev = __in6_dev_get(skb->dev);