Subject: [RFC] Netfilter: Provide xt_SYSRQ target and tools
From: Carsten Emde <C.Emde@osadl.org>
Date: Sat, 18 Mar 2017 22:10:20 +0100

The SYSRQ target allows to remotely trigger sysrq commands on the local
machine via UDP network packets. This is useful when the machine hangs
and keyboard input no longer works, but UDP network communication still
is available. The code was originally written by Jan Engelhardt and John
Haxby <john.haxby@oracle.com> and made available along with a variety
of additional Xtable extensions. This current version was adapted to
the Linux mainline requirements in the hope that it may be suitable for
this purpose. As a side effect, the current ping sysrq implementation
of the PREEMPT_RT realtime patches that serves the same purpose could
be dropped and, thus, the size of the realtime patches would shrink.

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

---
 Documentation/networking/netfilter-sysrq.txt |   58 +++
 net/netfilter/Kconfig                        |   98 +++---
 net/netfilter/Makefile                       |    1 
 net/netfilter/xt_SYSRQ.c                     |  416 +++++++++++++++++++++++++++
 tools/net/udp-sysrq                          |   80 +++++
 5 files changed, 609 insertions(+), 44 deletions(-)

Index: linux-4.4.39-rt50/Documentation/networking/netfilter-sysrq.txt
===================================================================
--- /dev/null
+++ linux-4.4.39-rt50/Documentation/networking/netfilter-sysrq.txt
@@ -0,0 +1,58 @@
+The SYSRQ target allows to remotely trigger sysrq commands on the local
+machine via UDP network packets. This is useful when the machine hangs
+and keyboard input no longer works, but UDP network communication still
+is available. It should, however, be noted that low-level serial console
+output must be connected and functional, if diagnostic SysRq commands
+are going to be used. Such diagnostic SysRq command, for example, is
+SysRq-T that allows to study the tasks and probably identify the one
+that runs havoc.
+
+The xt_SYSRQ implementation uses a salted hash and a sequence number to
+prevent network sniffers from either guessing the password or replaying
+earlier requests. This makes it possible to enable the SYSRQ target even
+in a production system. Please remember, however, that it may be needed
+to use as many filter options as possible including time limits to
+prevent unauthorized parties from executing SysRq commands.
+
+To facilitate using the SYSRQ target, the script tools/net/udp-sysrq is
+provided. It is used to enable and disable reception of UDP SysRq
+packets. It works best when a network link and an ssh login from the
+local machine to the remote computer is established. Its syntax is
+
+  udp-sysrq <enable|disable> <thisip> <fromip> [<frommac>]
+
+If, for example, the command
+
+  ./tools/net/udp-sysrq enable 192.168.0.1 192.168.0.2
+
+is submitted, the dynamically generated script /tmp/udp-sysrq-doit is
+transferred to the remote system. Its syntax is
+
+  udp-sysrq-doit <sysrq-key[:<msdelay:]>
+
+To execute SysRq-H, for example, the following command is used on the
+remote machine:
+
+  /tmp/udp-sysrq-doit h
+
+It is also possible to submit more than a single command by simply
+appending more characters to the command string. However, some commands
+may need some time to execute; to delay execution start of a subsequent
+command, a millisecond count may be specified as an integer number
+surrounded by colons.
+
+Example 1
+Sync file systems using the SYSRQ target:
+local: ./tools/net/udp-sysrq enable 192.168.0.1 192.168.0.2
+remote: /tmp/udp-sysrq-doit s
+local: dmesg | tail -3
+[12084.999793] xt_SYSRQ: SysRq s
+[12084.999804] sysrq: SysRq : Emergency Sync
+[12085.024860] Emergency Sync complete
+
+Example 2
+Sync file systems, unmount them and reboot the system:
+local: ./tools/net/udp-sysrq enable 192.168.0.1 192.168.0.2
+remote: /tmp/udp-sysrq-doit s:5000:u:1000:b
+(Allow 5 seconds to complete the sync and 1 second to complete the
+umount command, before the system is rebooted.)
Index: linux-4.4.39-rt50/net/netfilter/Kconfig
===================================================================
--- linux-4.4.39-rt50.orig/net/netfilter/Kconfig
+++ linux-4.4.39-rt50/net/netfilter/Kconfig
@@ -863,49 +863,6 @@ config NETFILTER_XT_TARGET_REDIRECT
 
        To compile it as a module, choose M here. If unsure, say N.
 
-config NETFILTER_XT_TARGET_TEE
-       tristate '"TEE" - packet cloning to alternate destination'
-       depends on NETFILTER_ADVANCED
-       depends on IPV6 || IPV6=n
-       depends on !NF_CONNTRACK || NF_CONNTRACK
-       select NF_DUP_IPV4
-       select NF_DUP_IPV6 if IP6_NF_IPTABLES != n
-       ---help---
-       This option adds a "TEE" target with which a packet can be cloned and
-       this clone be rerouted to another nexthop.
-
-config NETFILTER_XT_TARGET_TPROXY
-       tristate '"TPROXY" target transparent proxying support'
-       depends on NETFILTER_XTABLES
-       depends on NETFILTER_ADVANCED
-       depends on IPV6 || IPV6=n
-       depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n
-       depends on IP_NF_MANGLE
-       select NF_DEFRAG_IPV4
-       select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n
-       help
-         This option adds a `TPROXY' target, which is somewhat similar to
-         REDIRECT.  It can only be used in the mangle table and is useful
-         to redirect traffic to a transparent proxy.  It does _not_ depend
-         on Netfilter connection tracking and NAT, unlike REDIRECT.
-         For it to work you will have to configure certain iptables rules
-         and use policy routing. For more information on how to set it up
-         see Documentation/networking/tproxy.txt.
-
-         To compile it as a module, choose M here.  If unsure, say N.
-
-config NETFILTER_XT_TARGET_TRACE
-       tristate  '"TRACE" target support'
-       depends on IP_NF_RAW || IP6_NF_RAW
-       depends on NETFILTER_ADVANCED
-       help
-         The TRACE target allows you to mark packets so that the kernel
-         will log every rule which match the packets as those traverse
-         the tables, chains, rules.
-
-         If you want to compile it as a module, say M here and read
-         <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
-
 config NETFILTER_XT_TARGET_SECMARK
        tristate '"SECMARK" target support'
        depends on NETWORK_SECMARK
@@ -916,6 +873,16 @@ config NETFILTER_XT_TARGET_SECMARK
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config NETFILTER_XT_TARGET_SYSRQ
+       tristate '"SYSRQ" target support'
+       depends on MAGIC_SYSRQ
+       default m if NETFILTER_ADVANCED=n
+       help
+         The SYSRQ target allows to execute local SysRq commands from a
+         remote location using a cryptographically encoded UDP packet.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config NETFILTER_XT_TARGET_TCPMSS
        tristate '"TCPMSS" target support'
        depends on IPV6 || IPV6=n
@@ -951,6 +918,49 @@ config NETFILTER_XT_TARGET_TCPOPTSTRIP
          This option adds a "TCPOPTSTRIP" target, which allows you to strip
          TCP options from TCP packets.
 
+config NETFILTER_XT_TARGET_TEE
+       tristate '"TEE" - packet cloning to alternate destination'
+       depends on NETFILTER_ADVANCED
+       depends on IPV6 || IPV6=n
+       depends on !NF_CONNTRACK || NF_CONNTRACK
+       select NF_DUP_IPV4
+       select NF_DUP_IPV6 if IPV6
+       ---help---
+       This option adds a "TEE" target with which a packet can be cloned and
+       this clone be rerouted to another nexthop.
+
+config NETFILTER_XT_TARGET_TPROXY
+       tristate '"TPROXY" target transparent proxying support'
+       depends on NETFILTER_XTABLES
+       depends on NETFILTER_ADVANCED
+       depends on IPV6 || IPV6=n
+       depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n
+       depends on IP_NF_MANGLE
+       select NF_DEFRAG_IPV4
+       select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n
+       help
+         This option adds a `TPROXY' target, which is somewhat similar to
+         REDIRECT.  It can only be used in the mangle table and is useful
+         to redirect traffic to a transparent proxy.  It does _not_ depend
+         on Netfilter connection tracking and NAT, unlike REDIRECT.
+         For it to work you will have to configure certain iptables rules
+         and use policy routing. For more information on how to set it up
+         see Documentation/networking/tproxy.txt.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
+config NETFILTER_XT_TARGET_TRACE
+       tristate  '"TRACE" target support'
+       depends on IP_NF_RAW || IP6_NF_RAW
+       depends on NETFILTER_ADVANCED
+       help
+         The TRACE target allows you to mark packets so that the kernel
+         will log every rule which match the packets as those traverse
+         the tables, chains, rules.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
+
 # alphabetically ordered list of matches
 
 comment "Xtables matches"
Index: linux-4.4.39-rt50/net/netfilter/Makefile
===================================================================
--- linux-4.4.39-rt50.orig/net/netfilter/Makefile
+++ linux-4.4.39-rt50/net/netfilter/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTS
 obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o
 obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_SYSRQ) += xt_SYSRQ.o
 
 # matches
 obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o
Index: linux-4.4.39-rt50/net/netfilter/xt_SYSRQ.c
===================================================================
--- /dev/null
+++ linux-4.4.39-rt50/net/netfilter/xt_SYSRQ.c
@@ -0,0 +1,416 @@
+/*
+ *     "SYSRQ" target extension for Xtables
+ *     Copyright Jan Engelhardt, 2016
+ *
+ *     Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk>
+ *
+ *     Security additions John Haxby <john.haxby [at] oracle com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     version 2 or 3 as published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/sysrq.h>
+#include <linux/udp.h>
+#include <linux/version.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/x_tables.h>
+#include <crypto/hash.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <linux/delay.h>
+
+#if IS_ENABLED(CONFIG_CRYPTO) || IS_ENABLED(CONFIG_CRYPTO_MODULE)
+#      define WITH_CRYPTO 1
+#endif
+#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) || \
+       IS_ENABLED(CONFIG_IP6_NF_IPTABLES_MODULE)
+#      define WITH_IPV6 1
+#endif
+
+#ifdef WITH_IPV6
+# define NIP6(addr) \
+       ntohs((addr).s6_addr16[0]), \
+       ntohs((addr).s6_addr16[1]), \
+       ntohs((addr).s6_addr16[2]), \
+       ntohs((addr).s6_addr16[3]), \
+       ntohs((addr).s6_addr16[4]), \
+       ntohs((addr).s6_addr16[5]), \
+       ntohs((addr).s6_addr16[6]), \
+       ntohs((addr).s6_addr16[7])
+# define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+#endif
+
+# define NIPQUAD(addr) \
+       ((const unsigned char *)&addr)[0], \
+       ((const unsigned char *)&addr)[1], \
+       ((const unsigned char *)&addr)[2], \
+       ((const unsigned char *)&addr)[3]
+# define NIPQUAD_FMT "%u.%u.%u.%u"
+
+static bool sysrq_once;
+static char sysrq_password[64];
+static char sysrq_hash[16] = "sha1";
+static long sysrq_seqno;
+static int sysrq_debug;
+module_param_string(password, sysrq_password, sizeof(sysrq_password), 0600);
+module_param_string(hash, sysrq_hash, sizeof(sysrq_hash), 0400);
+module_param_named(seqno, sysrq_seqno, long, 0600);
+module_param_named(debug, sysrq_debug, int, 0600);
+MODULE_PARM_DESC(password, "password for remote sysrq");
+MODULE_PARM_DESC(hash, "hash algorithm, default sha1");
+MODULE_PARM_DESC(seqno, "sequence number for remote sysrq");
+MODULE_PARM_DESC(debug, "debugging: 0=off, 1=on");
+
+#ifdef WITH_CRYPTO
+static struct crypto_shash *sysrq_tfm;
+static int sysrq_digest_size;
+static unsigned char *sysrq_digest_password;
+static unsigned char *sysrq_digest;
+static char *sysrq_hexdigest;
+
+/* The data is of the form "<requests>,<seqno>,<salt>,<hash>" where <requests>
+ * is a series of sysrq requests; <seqno> is a sequence number that must be
+ * greater than the last sequence number; <salt> is some random bytes; and
+ * <hash> is the hash of everything up to and including the preceding ","
+ * together with "<dstaddr>,<password>".
+ *
+ * For example
+ *
+ *   salt=$RANDOM
+ *   req="s,$(date +%s),$salt"
+ *   echo "$req,$(echo -n $req,10.10.25.1,secret | sha1sum | cut -c1-40)"
+ *
+ * You will want a better salt and password than that though :-)
+ */
+static unsigned int sysrq_tg(const void *pdata, uint16_t len)
+{
+       const char *data = pdata;
+       int i, n;
+       int ret;
+       long new_seqno = 0;
+       struct shash_desc *desc;
+
+       if (sysrq_debug)
+               pr_info("starting execution of SYSRQ target\n");
+
+       if (*sysrq_password == '\0') {
+               if (!sysrq_once)
+                       pr_info("no password set\n");
+               sysrq_once = true;
+               return NF_DROP;
+       }
+       if (len == 0)
+               return NF_DROP;
+
+       for (i = 0; sysrq_password[i] != '\0' &&
+            sysrq_password[i] != '\n'; ++i)
+       sysrq_password[i] = '\0';
+
+       i = 0;
+       for (n = 0; n < len - 1; ++n) {
+               if (i == 1 && '0' <= data[n] && data[n] <= '9')
+                       new_seqno = 10L * new_seqno + data[n] - '0';
+               if (data[n] == ',' && ++i == 3)
+                       break;
+       }
+       ++n;
+       if (i != 3) {
+               if (sysrq_debug)
+                       pr_err("badly formatted request\n");
+               return NF_DROP;
+       }
+       if (sysrq_seqno >= new_seqno) {
+               if (sysrq_debug)
+                       pr_err("old sequence number invalid\n");
+               return NF_DROP;
+       }
+
+       desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(sysrq_tfm),
+                      GFP_KERNEL);
+       if (!desc)
+               return NF_DROP;
+
+       desc->tfm = sysrq_tfm;
+       ret = crypto_shash_init(desc);
+       if (ret != 0)
+               goto hash_fail;
+
+       if (crypto_shash_update(desc, data, n) != 0)
+               goto hash_fail;
+       if (crypto_shash_update(desc, sysrq_digest_password,
+                               strlen(sysrq_digest_password)) != 0)
+               goto hash_fail;
+       if (crypto_shash_final(desc, sysrq_digest) != 0)
+               goto hash_fail;
+
+       kfree(desc);
+
+       for (i = 0; i < sysrq_digest_size; ++i) {
+               sysrq_hexdigest[2 * i] =
+                       "0123456789abcdef"[(sysrq_digest[i] >> 4) & 0xf];
+               sysrq_hexdigest[2 * i + 1] =
+                       "0123456789abcdef"[sysrq_digest[i] & 0xf];
+       }
+       sysrq_hexdigest[sysrq_digest_size * 2] = '\0';
+       if (len - n < sysrq_digest_size * 2) {
+               if (sysrq_debug)
+                       pr_err("short digest: got %d, expected %d\n",
+                              len - n, sysrq_digest_size * 2);
+               return NF_DROP;
+       }
+
+       if (strncmp(data + n, sysrq_hexdigest, sysrq_digest_size * 2) != 0) {
+               if (sysrq_debug)
+                       pr_err("bad digest: expected %s\n", sysrq_hexdigest);
+               return NF_DROP;
+       }
+
+       /* Now we trust the requester */
+       sysrq_seqno = new_seqno;
+       for (i = 0; i < len && data[i] != ','; ++i) {
+               char *i1, *i2 = NULL;
+
+               pr_info("SysRq %c\n", data[i]);
+               handle_sysrq(data[i]);
+
+               i1 = strchr(&data[i + 1], ':');
+               if (i1)
+                       i2 = strchr(i1 + 1, ':');
+               if (i1 && i2 && *(i2 + 1) != ',') {
+                       unsigned int msdelay;
+
+                       *i2 = '\0';
+                       if (!kstrtouint(i1 + 1, 10, &msdelay) && msdelay) {
+                               if (sysrq_debug)
+                                       pr_info("delaying %d ms\n", msdelay);
+                               mdelay(msdelay);
+                       }
+                       i = i2 - data;
+               }
+       }
+       return NF_ACCEPT;
+
+hash_fail:
+       kfree(desc);
+       pr_err("digest failure\n");
+       return NF_DROP;
+}
+#else
+static unsigned int sysrq_tg(const void *pdata, uint16_t len)
+{
+       const char *data = pdata;
+       char c;
+
+       if (*sysrq_password == '\0') {
+               if (!sysrq_once)
+                       pr_info("no password set\n");
+               sysrq_once = true;
+               return NF_DROP;
+       }
+
+       if (len == 0)
+               return NF_DROP;
+
+       c = *data;
+       if (strncmp(&data[1], sysrq_password, len - 1) != 0) {
+               pr_err("Failed attempt - password mismatch\n");
+               return NF_DROP;
+       }
+
+       handle_sysrq(c);
+       return NF_ACCEPT;
+}
+#endif
+
+static unsigned int
+sysrq_tg4(struct sk_buff *skb, const struct xt_action_param *par)
+{
+       const struct iphdr *iph;
+       const struct udphdr *udph;
+       u16 len;
+
+       if (skb_linearize(skb) < 0)
+               return NF_DROP;
+
+       iph = ip_hdr(skb);
+       if (iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_UDPLITE)
+               return NF_DROP;
+
+       udph = (const void *)iph + ip_hdrlen(skb);
+       len  = ntohs(udph->len) - sizeof(struct udphdr);
+
+       if (sysrq_debug)
+               pr_info(NIPQUAD_FMT ":%u -> :%u len=%u\n",
+                       NIPQUAD(iph->saddr), htons(udph->source),
+                       htons(udph->dest), len);
+#ifdef WITH_CRYPTO
+       sprintf(sysrq_digest_password, NIPQUAD_FMT ",%s",
+               NIPQUAD(iph->daddr), sysrq_password);
+#endif
+       return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
+}
+
+#ifdef WITH_IPV6
+static unsigned int
+sysrq_tg6(struct sk_buff *skb, const struct xt_action_param *par)
+{
+       const struct ipv6hdr *iph;
+       const struct udphdr *udph;
+       unsigned short frag_off;
+       unsigned int th_off = 0;
+       u16 len;
+
+       if (skb_linearize(skb) < 0)
+               return NF_DROP;
+
+       iph = ipv6_hdr(skb);
+       /* Should probably be using %IP6T_FH_F_AUTH */
+       if ((ipv6_find_hdr(skb, &th_off, IPPROTO_UDP, &frag_off, NULL) < 0 &&
+            ipv6_find_hdr(skb, &th_off, IPPROTO_UDPLITE, &frag_off, NULL)
+             < 0) || frag_off > 0)
+               return NF_DROP;
+
+       udph = (const void *)iph + th_off;
+       len  = ntohs(udph->len) - sizeof(struct udphdr);
+
+       if (sysrq_debug)
+               pr_info(NIP6_FMT ":%hu -> :%hu len=%u\n",
+                       NIP6(iph->saddr), ntohs(udph->source),
+                       ntohs(udph->dest), len);
+#ifdef WITH_CRYPTO
+       sprintf(sysrq_digest_password, NIP6_FMT ",%s",
+               NIP6(iph->daddr), sysrq_password);
+#endif
+       return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
+}
+#endif
+
+static int sysrq_tg_check(const struct xt_tgchk_param *par)
+{
+       if (par->target->family == NFPROTO_IPV4) {
+               const struct ipt_entry *entry = par->entryinfo;
+
+               if ((entry->ip.proto != IPPROTO_UDP &&
+                    entry->ip.proto != IPPROTO_UDPLITE) ||
+                   entry->ip.invflags & XT_INV_PROTO)
+                       goto out;
+       } else if (par->target->family == NFPROTO_IPV6) {
+               const struct ip6t_entry *entry = par->entryinfo;
+
+               if ((entry->ipv6.proto != IPPROTO_UDP &&
+                    entry->ipv6.proto != IPPROTO_UDPLITE) ||
+                   entry->ipv6.invflags & XT_INV_PROTO)
+                       goto out;
+       }
+
+       return 0;
+
+ out:
+       pr_err("only available for UDP and UDP-Lite");
+       return -EINVAL;
+}
+
+static struct xt_target sysrq_tg_reg[] __read_mostly = {
+       {
+               .name       = "SYSRQ",
+               .revision   = 1,
+               .family     = NFPROTO_IPV4,
+               .target     = sysrq_tg4,
+               .checkentry = sysrq_tg_check,
+               .me         = THIS_MODULE,
+       },
+#ifdef WITH_IPV6
+       {
+               .name       = "SYSRQ",
+               .revision   = 1,
+               .family     = NFPROTO_IPV6,
+               .target     = sysrq_tg6,
+               .checkentry = sysrq_tg_check,
+               .me         = THIS_MODULE,
+       },
+#endif
+};
+
+static void sysrq_crypto_exit(void)
+{
+#ifdef WITH_CRYPTO
+       crypto_free_shash(sysrq_tfm);
+       kfree(sysrq_digest);
+       kfree(sysrq_hexdigest);
+       kfree(sysrq_digest_password);
+#endif
+}
+
+static int __init sysrq_crypto_init(void)
+{
+#if defined(WITH_CRYPTO)
+       struct timeval now;
+       int ret;
+
+       sysrq_tfm = crypto_alloc_shash(sysrq_hash, 0, 0);
+       if (IS_ERR(sysrq_tfm)) {
+               pr_err("error: could not find or load %s hash\n",
+                      sysrq_hash);
+               ret = PTR_ERR(sysrq_tfm);
+               sysrq_tfm = NULL;
+               goto fail;
+       }
+       sysrq_digest_size = crypto_shash_digestsize(sysrq_tfm);
+       sysrq_digest = kmalloc(sysrq_digest_size, GFP_KERNEL);
+       ret = -ENOMEM;
+       if (!sysrq_digest)
+               goto fail;
+       sysrq_hexdigest = kmalloc(2 * sysrq_digest_size + 1, GFP_KERNEL);
+       if (!sysrq_hexdigest)
+               goto fail;
+       sysrq_digest_password =
+           kmalloc(sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255") +
+                   sizeof(sysrq_password), GFP_KERNEL);
+       if (!sysrq_digest_password)
+               goto fail;
+       do_gettimeofday(&now);
+       sysrq_seqno = now.tv_sec;
+       return 0;
+
+ fail:
+       sysrq_crypto_exit();
+       return ret;
+#else
+       pr_warn("kernel was compiled without crypto, so xt_SYSRQ won't use it\n");
+#endif
+       return -EINVAL;
+}
+
+static int __init sysrq_tg_init(void)
+{
+       if (sysrq_crypto_init() < 0)
+               pr_warn("xt_SYSRQ starting without crypto\n");
+       if (sysrq_debug)
+               pr_info("about to register SYSRQ target\n");
+       return xt_register_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg));
+}
+
+static void __exit sysrq_tg_exit(void)
+{
+       sysrq_crypto_exit();
+       if (sysrq_debug)
+               pr_info("about to unregister SYSRQ target");
+       xt_unregister_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg));
+}
+
+module_init(sysrq_tg_init);
+module_exit(sysrq_tg_exit);
+MODULE_DESCRIPTION("Xtables: SYSRQ target to trigger SysRq actions from remote via UDP packet");
+MODULE_AUTHOR("Jan Engelhardt");
+MODULE_AUTHOR("John Haxby <john.haxby@oracle.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_SYSRQ");
+MODULE_ALIAS("ip6t_SYSRQ");
Index: linux-4.4.39-rt50/tools/net/udp-sysrq
===================================================================
--- /dev/null
+++ linux-4.4.39-rt50/tools/net/udp-sysrq
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+this=`basename $0`
+mode="$1"
+thisip="$2"
+fromip="$3"
+frommac="$4"
+
+if test -z "mode" -o -z "$thisip" -o -z "$fromip"
+then
+  echo 1>&2 "Syntax: $this <enable|disable> <thisip> <fromip> [<frommac>]"
+  exit 1
+fi
+
+pleasespecifymac="please specify MAC address of remote host"
+if test -z "$frommac" && which arp >/dev/null 2>&1
+then
+  if ping -c1 -t2 $fromip >/dev/null
+  then
+    frommac=`arp -a $fromip | cut -d" " -f4`
+    if test -z "$frommac" || ! echo "$frommac" | grep -q '..:..:..:..:..:..'
+    then
+      echo 1>&2 Host $fromip is online but not local, $pleasespecifymac
+      exit 1
+    fi
+  else
+    echo 1>&2 Host $fromip is not online, $pleasespecifymac
+    exit 1
+  fi
+fi
+if test -z "$frommac"
+then
+  echo 1>&2 Required arp tool not available, $pleasespecifymac
+  exit 1
+fi
+
+if test $mode = enable
+then
+  iptables -I INPUT -s $fromip -m mac --mac-source $frommac -d $thisip -p udp --dport 9 -j SYSRQ
+
+  password=`tr -dc 'A-Za-z0-9' </dev/urandom | head -c8`
+  modulepassword=/sys/module/xt_SYSRQ/parameters/password
+  echo -n $password >$modulepassword
+
+  cat >/tmp/$this.$$ << EOF
+#!/bin/bash
+
+this="\$(basename \$0)"
+
+sysrq_key=\$1
+if test -z "\$sysrq_key"
+then
+  echo 1>&2 "Syntax: \$this <sysrq-key[:<msdelay>:]>"
+  echo 1>&2 "Function: Execute <sysrq> command on remote host"
+  echo 1>&2 "          Optionally wait <msdelay> milliseconds after execution"
+  echo 1>&2 "Example:  Sync file systems, unmount them and reboot"
+  echo 1>&2 "          \$this s:5000:u:1000:b"
+  exit 1
+fi
+
+seqno="\$(date +%s)"
+salt="\$(dd bs=12 count=1 if=/dev/urandom 2>/dev/null | openssl enc -base64)"
+req="\$sysrq_key,\$seqno,\$salt"
+req="\$req,\$(echo -n "\$req,$thisip,$password" | sha1sum | cut -c1-40)"
+echo "\$req" | socat stdin udp-sendto:$thisip:9
+EOF
+  chmod +x /tmp/$this.$$
+  scp -qp /tmp/$this.$$ $fromip:/tmp/udp-sysrq-doit
+  rm -f /tmp/$this.$$
+
+  echo "[Remote] To trigger SysRq from $fromip use: /tmp/udp-sysrq-doit <sysrq-key[:<msdelay>:]>"
+  echo "[Local] To re-enable use: echo -n $password >$modulepassword"
+fi
+
+if test $mode = disable
+then
+  sysrqline=`iptables -L -v -n --line-numbers | grep -e SYSRQ -e $thisip -e $fromip | tail -1 | cut -d" " -f1`
+  iptables -D INPUT $sysrqline
+  rmmod xt_SYSRQ
+fi