Skip to content

Commit

Permalink
icmp: move icmp_global.credit and icmp_global.stamp to per netns storage
Browse files Browse the repository at this point in the history
Host wide ICMP ratelimiter should be per netns, to provide better isolation.

Following patch in this series makes the sysctl per netns.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://patch.msgid.link/20240829144641.3880376-3-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Eric Dumazet authored and Jakub Kicinski committed Aug 30, 2024
1 parent 8c2bd38 commit b056b4c
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 20 deletions.
4 changes: 2 additions & 2 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -794,8 +794,8 @@ static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
ip_cmsg_recv_offset(msg, skb->sk, skb, 0, 0);
}

bool icmp_global_allow(void);
void icmp_global_consume(void);
bool icmp_global_allow(struct net *net);
void icmp_global_consume(struct net *net);

extern int sysctl_icmp_msgs_per_sec;
extern int sysctl_icmp_msgs_burst;
Expand Down
3 changes: 2 additions & 1 deletion include/net/netns/ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ struct netns_ipv4 {
u8 sysctl_icmp_errors_use_inbound_ifaddr;
int sysctl_icmp_ratelimit;
int sysctl_icmp_ratemask;

atomic_t icmp_global_credit;
u32 icmp_global_stamp;
u32 ip_rt_min_pmtu;
int ip_rt_mtu_expires;
int ip_rt_min_advmss;
Expand Down
26 changes: 11 additions & 15 deletions net/ipv4/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,19 +223,15 @@ static inline void icmp_xmit_unlock(struct sock *sk)
int sysctl_icmp_msgs_per_sec __read_mostly = 1000;
int sysctl_icmp_msgs_burst __read_mostly = 50;

static struct {
atomic_t credit;
u32 stamp;
} icmp_global;

/**
* icmp_global_allow - Are we allowed to send one more ICMP message ?
* @net: network namespace
*
* 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().
*/
bool icmp_global_allow(void)
bool icmp_global_allow(struct net *net)
{
u32 delta, now, oldstamp;
int incr, new, old;
Expand All @@ -244,11 +240,11 @@ bool icmp_global_allow(void)
* Then later icmp_global_consume() could consume more credits,
* this is an acceptable race.
*/
if (atomic_read(&icmp_global.credit) > 0)
if (atomic_read(&net->ipv4.icmp_global_credit) > 0)
return true;

now = jiffies;
oldstamp = READ_ONCE(icmp_global.stamp);
oldstamp = READ_ONCE(net->ipv4.icmp_global_stamp);
delta = min_t(u32, now - oldstamp, HZ);
if (delta < HZ / 50)
return false;
Expand All @@ -257,23 +253,23 @@ bool icmp_global_allow(void)
if (!incr)
return false;

if (cmpxchg(&icmp_global.stamp, oldstamp, now) == oldstamp) {
old = atomic_read(&icmp_global.credit);
if (cmpxchg(&net->ipv4.icmp_global_stamp, oldstamp, now) == oldstamp) {
old = atomic_read(&net->ipv4.icmp_global_credit);
do {
new = min(old + incr, READ_ONCE(sysctl_icmp_msgs_burst));
} while (!atomic_try_cmpxchg(&icmp_global.credit, &old, new));
} while (!atomic_try_cmpxchg(&net->ipv4.icmp_global_credit, &old, new));
}
return true;
}
EXPORT_SYMBOL(icmp_global_allow);

void icmp_global_consume(void)
void icmp_global_consume(struct net *net)
{
int credits = get_random_u32_below(3);

/* Note: this might make icmp_global.credit negative. */
if (credits)
atomic_sub(credits, &icmp_global.credit);
atomic_sub(credits, &net->ipv4.icmp_global_credit);
}
EXPORT_SYMBOL(icmp_global_consume);

Expand All @@ -299,7 +295,7 @@ 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()) {
if (icmp_global_allow(net)) {
*apply_ratelimit = true;
return true;
}
Expand Down Expand Up @@ -337,7 +333,7 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
if (!rc)
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);
else
icmp_global_consume();
icmp_global_consume(net);
return rc;
}

Expand Down
4 changes: 2 additions & 2 deletions net/ipv6/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ static bool icmpv6_global_allow(struct net *net, int type,
if (icmpv6_mask_allow(net, type))
return true;

if (icmp_global_allow()) {
if (icmp_global_allow(net)) {
*apply_ratelimit = true;
return true;
}
Expand Down Expand Up @@ -231,7 +231,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
__ICMP6_INC_STATS(net, ip6_dst_idev(dst),
ICMP6_MIB_RATELIMITHOST);
else
icmp_global_consume();
icmp_global_consume(net);
dst_release(dst);
return res;
}
Expand Down

0 comments on commit b056b4c

Please sign in to comment.