]> rtime.felk.cvut.cz Git - zynq/linux.git/blobdiff - net/ipv4/icmp.c
Apply preempt_rt patch-4.9-rt1.patch.xz
[zynq/linux.git] / net / ipv4 / icmp.c
index 48734ee6293f3383e064ba18734abc485b1c3639..e6864ff11352d500b22d471e195cbdd892903c35 100644 (file)
@@ -69,6 +69,7 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/fcntl.h>
+#include <linux/sysrq.h>
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/inet.h>
@@ -77,6 +78,7 @@
 #include <linux/string.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/slab.h>
+#include <linux/locallock.h>
 #include <net/snmp.h>
 #include <net/ip.h>
 #include <net/route.h>
@@ -204,6 +206,8 @@ static const struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
  *
  *     On SMP we have one ICMP socket per-cpu.
  */
+static DEFINE_LOCAL_IRQ_LOCK(icmp_sk_lock);
+
 static struct sock *icmp_sk(struct net *net)
 {
        return *this_cpu_ptr(net->ipv4.icmp_sk);
@@ -215,12 +219,14 @@ static inline struct sock *icmp_xmit_lock(struct net *net)
 
        local_bh_disable();
 
+       local_lock(icmp_sk_lock);
        sk = icmp_sk(net);
 
        if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
                /* This can happen if the output path signals a
                 * dst_link_failure() for an outgoing ICMP packet.
                 */
+               local_unlock(icmp_sk_lock);
                local_bh_enable();
                return NULL;
        }
@@ -230,6 +236,7 @@ static inline struct sock *icmp_xmit_lock(struct net *net)
 static inline void icmp_xmit_unlock(struct sock *sk)
 {
        spin_unlock_bh(&sk->sk_lock.slock);
+       local_unlock(icmp_sk_lock);
 }
 
 int sysctl_icmp_msgs_per_sec __read_mostly = 1000;
@@ -358,6 +365,7 @@ static void icmp_push_reply(struct icmp_bxm *icmp_param,
        struct sock *sk;
        struct sk_buff *skb;
 
+       local_lock(icmp_sk_lock);
        sk = icmp_sk(dev_net((*rt)->dst.dev));
        if (ip_append_data(sk, fl4, icmp_glue_bits, icmp_param,
                           icmp_param->data_len+icmp_param->head_len,
@@ -380,6 +388,7 @@ static void icmp_push_reply(struct icmp_bxm *icmp_param,
                skb->ip_summed = CHECKSUM_NONE;
                ip_push_pending_frames(sk, fl4);
        }
+       local_unlock(icmp_sk_lock);
 }
 
 /*
@@ -890,6 +899,30 @@ static bool icmp_redirect(struct sk_buff *skb)
        return true;
 }
 
+/*
+ * 32bit and 64bit have different timestamp length, so we check for
+ * the cookie at offset 20 and verify it is repeated at offset 50
+ */
+#define CO_POS0                20
+#define CO_POS1                50
+#define CO_SIZE                sizeof(int)
+#define ICMP_SYSRQ_SIZE        57
+
+/*
+ * We got a ICMP_SYSRQ_SIZE sized ping request. Check for the cookie
+ * pattern and if it matches send the next byte as a trigger to sysrq.
+ */
+static void icmp_check_sysrq(struct net *net, struct sk_buff *skb)
+{
+       int cookie = htonl(net->ipv4.sysctl_icmp_echo_sysrq);
+       char *p = skb->data;
+
+       if (!memcmp(&cookie, p + CO_POS0, CO_SIZE) &&
+           !memcmp(&cookie, p + CO_POS1, CO_SIZE) &&
+           p[CO_POS0 + CO_SIZE] == p[CO_POS1 + CO_SIZE])
+               handle_sysrq(p[CO_POS0 + CO_SIZE]);
+}
+
 /*
  *     Handle ICMP_ECHO ("ping") requests.
  *
@@ -917,6 +950,11 @@ static bool icmp_echo(struct sk_buff *skb)
                icmp_param.data_len        = skb->len;
                icmp_param.head_len        = sizeof(struct icmphdr);
                icmp_reply(&icmp_param, skb);
+
+               if (skb->len == ICMP_SYSRQ_SIZE &&
+                   net->ipv4.sysctl_icmp_echo_sysrq) {
+                       icmp_check_sysrq(net, skb);
+               }
        }
        /* should there be an ICMP stat for ignored echos? */
        return true;