Commit b056b4cd authored by Eric Dumazet's avatar Eric Dumazet Committed by Jakub Kicinski
Browse files

icmp: move icmp_global.credit and icmp_global.stamp to per netns storage



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: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarDavid Ahern <dsahern@kernel.org>
Link: https://patch.msgid.link/20240829144641.3880376-3-edumazet@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 8c2bd38b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;
+2 −1
Original line number Diff line number Diff line
@@ -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;
+11 −15
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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);

@@ -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;
	}
@@ -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;
}

+2 −2
Original line number Diff line number Diff line
@@ -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;
	}
@@ -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;
}