|
@@ -231,12 +231,62 @@ static inline void icmp_xmit_unlock(struct sock *sk)
|
|
|
spin_unlock_bh(&sk->sk_lock.slock);
|
|
|
}
|
|
|
|
|
|
+int sysctl_icmp_msgs_per_sec __read_mostly = 1000;
|
|
|
+int sysctl_icmp_msgs_burst __read_mostly = 50;
|
|
|
+
|
|
|
+static struct {
|
|
|
+ spinlock_t lock;
|
|
|
+ u32 credit;
|
|
|
+ u32 stamp;
|
|
|
+} 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.
|
|
|
+ * Note: called with BH disabled
|
|
|
+ */
|
|
|
+bool icmp_global_allow(void)
|
|
|
+{
|
|
|
+ u32 credit, delta, incr = 0, now = (u32)jiffies;
|
|
|
+ bool rc = false;
|
|
|
+
|
|
|
+ /* Check if token bucket is empty and cannot be refilled
|
|
|
+ * without taking the spinlock.
|
|
|
+ */
|
|
|
+ if (!icmp_global.credit) {
|
|
|
+ delta = min_t(u32, now - icmp_global.stamp, HZ);
|
|
|
+ if (delta < HZ / 50)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock(&icmp_global.lock);
|
|
|
+ delta = min_t(u32, now - icmp_global.stamp, HZ);
|
|
|
+ if (delta >= HZ / 50) {
|
|
|
+ incr = sysctl_icmp_msgs_per_sec * delta / HZ ;
|
|
|
+ if (incr)
|
|
|
+ icmp_global.stamp = now;
|
|
|
+ }
|
|
|
+ credit = min_t(u32, icmp_global.credit + incr, sysctl_icmp_msgs_burst);
|
|
|
+ if (credit) {
|
|
|
+ credit--;
|
|
|
+ rc = true;
|
|
|
+ }
|
|
|
+ icmp_global.credit = credit;
|
|
|
+ spin_unlock(&icmp_global.lock);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(icmp_global_allow);
|
|
|
+
|
|
|
/*
|
|
|
* Send an ICMP frame.
|
|
|
*/
|
|
|
|
|
|
-static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
|
|
|
- struct flowi4 *fl4, int type, int code)
|
|
|
+static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
|
|
|
+ struct flowi4 *fl4, int type, int code)
|
|
|
{
|
|
|
struct dst_entry *dst = &rt->dst;
|
|
|
bool rc = true;
|
|
@@ -253,8 +303,14 @@ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
|
|
|
goto out;
|
|
|
|
|
|
/* Limit if icmp type is enabled in ratemask. */
|
|
|
- if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) {
|
|
|
- struct inet_peer *peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, 1);
|
|
|
+ if (!((1 << type) & net->ipv4.sysctl_icmp_ratemask))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ rc = false;
|
|
|
+ if (icmp_global_allow()) {
|
|
|
+ struct inet_peer *peer;
|
|
|
+
|
|
|
+ peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, 1);
|
|
|
rc = inet_peer_xrlim_allow(peer,
|
|
|
net->ipv4.sysctl_icmp_ratelimit);
|
|
|
if (peer)
|