Subject: net: sysrq via icmp
From: Carsten Emde <C.Emde@osadl.org>
Date: Tue, 19 Jul 2011 13:51:17 +0100

There are (probably rare) situations when a system crashed and the system
console becomes unresponsive but the network icmp layer still is alive.
Wouldn't it be wonderful, if we then could submit a sysreq command via ping?

This patch provides this facility. Please consult the updated documentation
Documentation/sysrq.txt for details.

Signed-off-by: Carsten Emde <C.Emde@osadl.org>

---
 Documentation/admin-guide/sysrq.rst |   12 ++++++++++++
 include/net/netns/ipv4.h            |    1 +
 net/ipv4/icmp.c                     |   28 ++++++++++++++++++++++++++++
 net/ipv4/sysctl_net_ipv4.c          |    7 +++++++
 4 files changed, 48 insertions(+)

Index: linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst
===================================================================
@ linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst:84 @ On all
 
 The :kbd:`<command key>` is case sensitive.
 
+On all
+        Enable network SysRq by writing a cookie to icmp_echo_sysrq, e.g.::
+
+                echo 0x01020304 >/proc/sys/net/ipv4/icmp_echo_sysrq
+
+        Send an ICMP echo request with this pattern plus the particular
+        SysRq command key. Example::
+
+                ping -c1 -s57 -p0102030468
+
+        will trigger the SysRq-H (help) command.
+
 What are the 'command' keys?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Index: linux-6.3.1-rt13/include/net/netns/ipv4.h
===================================================================
--- linux-6.3.1-rt13.orig/include/net/netns/ipv4.h
+++ linux-6.3.1-rt13/include/net/netns/ipv4.h
@ linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst:82 @ struct netns_ipv4 {
 	u8 sysctl_icmp_echo_ignore_all;
 	u8 sysctl_icmp_echo_enable_probe;
 	u8 sysctl_icmp_echo_ignore_broadcasts;
+	int sysctl_icmp_echo_sysrq;
 	u8 sysctl_icmp_ignore_bogus_error_responses;
 	u8 sysctl_icmp_errors_use_inbound_ifaddr;
 	int sysctl_icmp_ratelimit;
Index: linux-6.3.1-rt13/net/ipv4/icmp.c
===================================================================
--- linux-6.3.1-rt13.orig/net/ipv4/icmp.c
+++ linux-6.3.1-rt13/net/ipv4/icmp.c
@ linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst:67 @
 #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>
@ linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst:985 @ static enum skb_drop_reason icmp_redirec
 	return SKB_NOT_DROPPED_YET;
 }
 
+ /* 32bit and 64bit have different timestamp length, so we check for
+ * the cookie at offset 20 and verify if 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") and ICMP_EXT_ECHO ("PROBE") requests.
  *
@ linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst:1043 @ static enum skb_drop_reason icmp_echo(st
 	else if (!icmp_build_probe(skb, &icmp_param.data.icmph))
 		return SKB_NOT_DROPPED_YET;
 
+	if (skb->len == ICMP_SYSRQ_SIZE &&
+	    net->ipv4.sysctl_icmp_echo_sysrq)
+		icmp_check_sysrq(net, skb);
+
 	icmp_reply(&icmp_param, skb);
 	return SKB_NOT_DROPPED_YET;
 }
Index: linux-6.3.1-rt13/net/ipv4/sysctl_net_ipv4.c
===================================================================
--- linux-6.3.1-rt13.orig/net/ipv4/sysctl_net_ipv4.c
+++ linux-6.3.1-rt13/net/ipv4/sysctl_net_ipv4.c
@ linux-6.3.1-rt13/Documentation/admin-guide/sysrq.rst:620 @ static struct ctl_table ipv4_net_table[]
 		.extra2		= SYSCTL_ONE
 	},
 	{
+		.procname	= "icmp_echo_sysrq",
+		.data		= &init_net.ipv4.sysctl_icmp_echo_sysrq,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
+	{
 		.procname	= "icmp_ignore_bogus_error_responses",
 		.data		= &init_net.ipv4.sysctl_icmp_ignore_bogus_error_responses,
 		.maxlen		= sizeof(u8),