mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -772,8 +772,6 @@ static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool icmp_global_allow(void);
|
bool icmp_global_allow(void);
|
||||||
void icmp_global_consume(void);
|
|
||||||
|
|
||||||
extern int sysctl_icmp_msgs_per_sec;
|
extern int sysctl_icmp_msgs_per_sec;
|
||||||
extern int sysctl_icmp_msgs_burst;
|
extern int sysctl_icmp_msgs_burst;
|
||||||
|
|
||||||
|
|||||||
105
net/ipv4/icmp.c
105
net/ipv4/icmp.c
@@ -222,59 +222,57 @@ int sysctl_icmp_msgs_per_sec __read_mostly = 1000;
|
|||||||
int sysctl_icmp_msgs_burst __read_mostly = 50;
|
int sysctl_icmp_msgs_burst __read_mostly = 50;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
atomic_t credit;
|
spinlock_t lock;
|
||||||
|
u32 credit;
|
||||||
u32 stamp;
|
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 ?
|
* 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.
|
* 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.
|
* 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)
|
bool icmp_global_allow(void)
|
||||||
{
|
{
|
||||||
u32 delta, now, oldstamp;
|
u32 credit, delta, incr = 0, now = (u32)jiffies;
|
||||||
int incr, new, old;
|
bool rc = false;
|
||||||
|
|
||||||
/* Note: many cpus could find this condition true.
|
/* Check if token bucket is empty and cannot be refilled
|
||||||
* Then later icmp_global_consume() could consume more credits,
|
* without taking the spinlock. The READ_ONCE() are paired
|
||||||
* this is an acceptable race.
|
* with the following WRITE_ONCE() in this same function.
|
||||||
*/
|
*/
|
||||||
if (atomic_read(&icmp_global.credit) > 0)
|
if (!READ_ONCE(icmp_global.credit)) {
|
||||||
return true;
|
delta = min_t(u32, now - READ_ONCE(icmp_global.stamp), HZ);
|
||||||
|
if (delta < HZ / 50)
|
||||||
now = jiffies;
|
return false;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
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);
|
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)
|
static bool icmpv4_mask_allow(struct net *net, int type, int code)
|
||||||
{
|
{
|
||||||
if (type > NR_ICMP_TYPES)
|
if (type > NR_ICMP_TYPES)
|
||||||
@@ -291,16 +289,14 @@ static bool icmpv4_mask_allow(struct net *net, int type, int code)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool icmpv4_global_allow(struct net *net, int type, int code,
|
static bool icmpv4_global_allow(struct net *net, int type, int code)
|
||||||
bool *apply_ratelimit)
|
|
||||||
{
|
{
|
||||||
if (icmpv4_mask_allow(net, type, code))
|
if (icmpv4_mask_allow(net, type, code))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (icmp_global_allow()) {
|
if (icmp_global_allow())
|
||||||
*apply_ratelimit = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);
|
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);
|
||||||
return false;
|
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,
|
static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
|
||||||
struct flowi4 *fl4, int type, int code,
|
struct flowi4 *fl4, int type, int code)
|
||||||
bool apply_ratelimit)
|
|
||||||
{
|
{
|
||||||
struct dst_entry *dst = &rt->dst;
|
struct dst_entry *dst = &rt->dst;
|
||||||
struct inet_peer *peer;
|
struct inet_peer *peer;
|
||||||
bool rc = true;
|
bool rc = true;
|
||||||
int vif;
|
int vif;
|
||||||
|
|
||||||
if (!apply_ratelimit)
|
if (icmpv4_mask_allow(net, type, code))
|
||||||
return true;
|
goto out;
|
||||||
|
|
||||||
/* No rate limit on loopback */
|
/* No rate limit on loopback */
|
||||||
if (dst->dev && (dst->dev->flags&IFF_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:
|
out:
|
||||||
if (!rc)
|
if (!rc)
|
||||||
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);
|
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);
|
||||||
else
|
|
||||||
icmp_global_consume();
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +400,6 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
|
|||||||
struct ipcm_cookie ipc;
|
struct ipcm_cookie ipc;
|
||||||
struct rtable *rt = skb_rtable(skb);
|
struct rtable *rt = skb_rtable(skb);
|
||||||
struct net *net = dev_net(rt->dst.dev);
|
struct net *net = dev_net(rt->dst.dev);
|
||||||
bool apply_ratelimit = false;
|
|
||||||
struct flowi4 fl4;
|
struct flowi4 fl4;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct inet_sock *inet;
|
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))
|
if (ip_options_echo(net, &icmp_param->replyopts.opt.opt, skb))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Needed by both icmpv4_global_allow and icmp_xmit_lock */
|
/* Needed by both icmp_global_allow and icmp_xmit_lock */
|
||||||
local_bh_disable();
|
local_bh_disable();
|
||||||
|
|
||||||
/* is global icmp_msgs_per_sec exhausted ? */
|
/* global icmp_msgs_per_sec */
|
||||||
if (!icmpv4_global_allow(net, type, code, &apply_ratelimit))
|
if (!icmpv4_global_allow(net, type, code))
|
||||||
goto out_bh_enable;
|
goto out_bh_enable;
|
||||||
|
|
||||||
sk = icmp_xmit_lock(net);
|
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);
|
rt = ip_route_output_key(net, &fl4);
|
||||||
if (IS_ERR(rt))
|
if (IS_ERR(rt))
|
||||||
goto out_unlock;
|
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);
|
icmp_push_reply(sk, icmp_param, &fl4, &ipc, &rt);
|
||||||
ip_rt_put(rt);
|
ip_rt_put(rt);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
@@ -600,7 +592,6 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
|
|||||||
int room;
|
int room;
|
||||||
struct icmp_bxm icmp_param;
|
struct icmp_bxm icmp_param;
|
||||||
struct rtable *rt = skb_rtable(skb_in);
|
struct rtable *rt = skb_rtable(skb_in);
|
||||||
bool apply_ratelimit = false;
|
|
||||||
struct ipcm_cookie ipc;
|
struct ipcm_cookie ipc;
|
||||||
struct flowi4 fl4;
|
struct flowi4 fl4;
|
||||||
__be32 saddr;
|
__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();
|
local_bh_disable();
|
||||||
|
|
||||||
/* Check global sysctl_icmp_msgs_per_sec ratelimit, unless
|
/* 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)
|
* loopback, then peer ratelimit still work (in icmpv4_xrlim_allow)
|
||||||
*/
|
*/
|
||||||
if (!(skb_in->dev && (skb_in->dev->flags&IFF_LOOPBACK)) &&
|
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;
|
goto out_bh_enable;
|
||||||
|
|
||||||
sk = icmp_xmit_lock(net);
|
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;
|
goto out_unlock;
|
||||||
|
|
||||||
/* peer icmp_ratelimit */
|
/* 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;
|
goto ende;
|
||||||
|
|
||||||
/* RFC says return as much as we can without exceeding 576 bytes. */
|
/* RFC says return as much as we can without exceeding 576 bytes. */
|
||||||
|
|||||||
@@ -175,16 +175,14 @@ static bool icmpv6_mask_allow(struct net *net, int type)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool icmpv6_global_allow(struct net *net, int type,
|
static bool icmpv6_global_allow(struct net *net, int type)
|
||||||
bool *apply_ratelimit)
|
|
||||||
{
|
{
|
||||||
if (icmpv6_mask_allow(net, type))
|
if (icmpv6_mask_allow(net, type))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (icmp_global_allow()) {
|
if (icmp_global_allow())
|
||||||
*apply_ratelimit = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);
|
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -193,13 +191,13 @@ static bool icmpv6_global_allow(struct net *net, int type,
|
|||||||
* Check the ICMP output rate limit
|
* Check the ICMP output rate limit
|
||||||
*/
|
*/
|
||||||
static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
|
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 net *net = sock_net(sk);
|
||||||
struct dst_entry *dst;
|
struct dst_entry *dst;
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
|
||||||
if (!apply_ratelimit)
|
if (icmpv6_mask_allow(net, type))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -230,8 +228,6 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
|
|||||||
if (!res)
|
if (!res)
|
||||||
__ICMP6_INC_STATS(net, ip6_dst_idev(dst),
|
__ICMP6_INC_STATS(net, ip6_dst_idev(dst),
|
||||||
ICMP6_MIB_RATELIMITHOST);
|
ICMP6_MIB_RATELIMITHOST);
|
||||||
else
|
|
||||||
icmp_global_consume();
|
|
||||||
dst_release(dst);
|
dst_release(dst);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -458,7 +454,6 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
|||||||
struct net *net;
|
struct net *net;
|
||||||
struct ipv6_pinfo *np;
|
struct ipv6_pinfo *np;
|
||||||
const struct in6_addr *saddr = NULL;
|
const struct in6_addr *saddr = NULL;
|
||||||
bool apply_ratelimit = false;
|
|
||||||
struct dst_entry *dst;
|
struct dst_entry *dst;
|
||||||
struct icmp6hdr tmp_hdr;
|
struct icmp6hdr tmp_hdr;
|
||||||
struct flowi6 fl6;
|
struct flowi6 fl6;
|
||||||
@@ -540,12 +535,11 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Needed by both icmpv6_global_allow and icmpv6_xmit_lock */
|
/* Needed by both icmp_global_allow and icmpv6_xmit_lock */
|
||||||
local_bh_disable();
|
local_bh_disable();
|
||||||
|
|
||||||
/* Check global sysctl_icmp_msgs_per_sec ratelimit */
|
/* Check global sysctl_icmp_msgs_per_sec ratelimit */
|
||||||
if (!(skb->dev->flags & IFF_LOOPBACK) &&
|
if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type))
|
||||||
!icmpv6_global_allow(net, type, &apply_ratelimit))
|
|
||||||
goto out_bh_enable;
|
goto out_bh_enable;
|
||||||
|
|
||||||
mip6_addr_swap(skb, parm);
|
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);
|
np = inet6_sk(sk);
|
||||||
|
|
||||||
if (!icmpv6_xrlim_allow(sk, type, &fl6, apply_ratelimit))
|
if (!icmpv6_xrlim_allow(sk, type, &fl6))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
tmp_hdr.icmp6_type = type;
|
tmp_hdr.icmp6_type = type;
|
||||||
@@ -725,7 +719,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
|
|||||||
struct ipv6_pinfo *np;
|
struct ipv6_pinfo *np;
|
||||||
const struct in6_addr *saddr = NULL;
|
const struct in6_addr *saddr = NULL;
|
||||||
struct icmp6hdr *icmph = icmp6_hdr(skb);
|
struct icmp6hdr *icmph = icmp6_hdr(skb);
|
||||||
bool apply_ratelimit = false;
|
|
||||||
struct icmp6hdr tmp_hdr;
|
struct icmp6hdr tmp_hdr;
|
||||||
struct flowi6 fl6;
|
struct flowi6 fl6;
|
||||||
struct icmpv6_msg msg;
|
struct icmpv6_msg msg;
|
||||||
@@ -789,9 +782,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Check the ratelimit */
|
/* Check the ratelimit */
|
||||||
if ((!(skb->dev->flags & IFF_LOOPBACK) &&
|
if ((!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, ICMPV6_ECHO_REPLY)) ||
|
||||||
!icmpv6_global_allow(net, ICMPV6_ECHO_REPLY, &apply_ratelimit)) ||
|
!icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6))
|
||||||
!icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6, apply_ratelimit))
|
|
||||||
goto out_dst_release;
|
goto out_dst_release;
|
||||||
|
|
||||||
idev = __in6_dev_get(skb->dev);
|
idev = __in6_dev_get(skb->dev);
|
||||||
|
|||||||
Reference in New Issue
Block a user