From 66432c0998d53c381aa378b8d38010672f7da20a Mon Sep 17 00:00:00 2001
From: Matteo Croce <matteo@openwrt.org>
Date: Thu, 2 Jul 2015 10:30:29 +0200
Subject: [PATCH] add stealth mode

Add option to disable any reply not related to a listening socket,
like RST/ACK for TCP and ICMP Port-Unreachable for UDP.
Also disables ICMP replies to echo request and timestamp.
The stealth mode can be enabled selectively for a single interface.

Signed-off-by: Matteo Croce <matteo@openwrt.org>
---
 Documentation/networking/ip-sysctl.txt | 12 ++++++++++++
 include/linux/inetdevice.h             |  1 +
 include/linux/ipv6.h                   |  1 +
 include/uapi/linux/ip.h                |  1 +
 net/ipv4/devinet.c                     |  1 +
 net/ipv4/icmp.c                        |  6 ++++++
 net/ipv4/tcp_ipv4.c                    |  3 ++-
 net/ipv4/udp.c                         |  4 +++-
 net/ipv6/addrconf.c                    |  7 +++++++
 net/ipv6/icmp.c                        |  3 ++-
 net/ipv6/tcp_ipv6.c                    |  2 +-
 net/ipv6/udp.c                         |  3 ++-
 12 files changed, 39 insertions(+), 5 deletions(-)

Index: linux-4.1/Documentation/networking/ip-sysctl.txt
===================================================================
--- linux-4.1.orig/Documentation/networking/ip-sysctl.txt
+++ linux-4.1/Documentation/networking/ip-sysctl.txt
@@ -1170,6 +1170,12 @@ tag - INTEGER
 	Allows you to write a number, which can be used as required.
 	Default value is 0.
 
+stealth - BOOLEAN
+	Disable any reply not related to a listening socket,
+	like RST/ACK for TCP and ICMP Port-Unreachable for UDP.
+	Also disables ICMP replies to echo requests and timestamp.
+	Default value is 0.
+
 Alexey Kuznetsov.
 kuznet@ms2.inr.ac.ru
 
@@ -1565,6 +1571,12 @@ stable_secret - IPv6 address
 
 	By default the stable secret is unset.
 
+stealth - BOOLEAN
+	Disable any reply not related to a listening socket,
+	like RST/ACK for TCP and ICMP Port-Unreachable for UDP.
+	Also disables ICMPv6 replies to echo requests.
+	Default value is 0.
+
 icmp/*:
 ratelimit - INTEGER
 	Limit the maximal rates for sending ICMPv6 packets.
Index: linux-4.1/include/linux/inetdevice.h
===================================================================
--- linux-4.1.orig/include/linux/inetdevice.h
+++ linux-4.1/include/linux/inetdevice.h
@@ -125,6 +125,7 @@ static inline void ipv4_devconf_setall(s
 #define IN_DEV_ARP_ANNOUNCE(in_dev)	IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE)
 #define IN_DEV_ARP_IGNORE(in_dev)	IN_DEV_MAXCONF((in_dev), ARP_IGNORE)
 #define IN_DEV_ARP_NOTIFY(in_dev)	IN_DEV_MAXCONF((in_dev), ARP_NOTIFY)
+#define IN_DEV_STEALTH(in_dev)		IN_DEV_MAXCONF((in_dev), STEALTH)
 
 struct in_ifaddr {
 	struct hlist_node	hash;
Index: linux-4.1/include/linux/ipv6.h
===================================================================
--- linux-4.1.orig/include/linux/ipv6.h
+++ linux-4.1/include/linux/ipv6.h
@@ -53,6 +53,7 @@ struct ipv6_devconf {
 	__s32           ndisc_notify;
 	__s32		suppress_frag_ndisc;
 	__s32		accept_ra_mtu;
+	__s32		stealth;
 	struct ipv6_stable_secret {
 		bool initialized;
 		struct in6_addr secret;
Index: linux-4.1/include/uapi/linux/ip.h
===================================================================
--- linux-4.1.orig/include/uapi/linux/ip.h
+++ linux-4.1/include/uapi/linux/ip.h
@@ -164,6 +164,7 @@ enum
 	IPV4_DEVCONF_ROUTE_LOCALNET,
 	IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL,
 	IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL,
+	IPV4_DEVCONF_STEALTH,
 	__IPV4_DEVCONF_MAX
 };
 
Index: linux-4.1/net/ipv4/devinet.c
===================================================================
--- linux-4.1.orig/net/ipv4/devinet.c
+++ linux-4.1/net/ipv4/devinet.c
@@ -2176,6 +2176,7 @@ static struct devinet_sysctl_table {
 					      "promote_secondaries"),
 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
 					      "route_localnet"),
+		DEVINET_SYSCTL_RW_ENTRY(STEALTH, "stealth"),
 	},
 };
 
Index: linux-4.1/net/ipv4/icmp.c
===================================================================
--- linux-4.1.orig/net/ipv4/icmp.c
+++ linux-4.1/net/ipv4/icmp.c
@@ -882,6 +882,9 @@ static bool icmp_echo(struct sk_buff *sk
 {
 	struct net *net;
 
+	if (IN_DEV_STEALTH(skb->dev->ip_ptr))
+		return true;
+
 	net = dev_net(skb_dst(skb)->dev);
 	if (!net->ipv4.sysctl_icmp_echo_ignore_all) {
 		struct icmp_bxm icmp_param;
@@ -915,6 +918,9 @@ static bool icmp_timestamp(struct sk_buf
 	if (skb->len < 4)
 		goto out_err;
 
+	if (IN_DEV_STEALTH(skb->dev->ip_ptr))
+		return true;
+
 	/*
 	 *	Fill in the current time as ms since midnight UT:
 	 */
Index: linux-4.1/net/ipv4/tcp_ipv4.c
===================================================================
--- linux-4.1.orig/net/ipv4/tcp_ipv4.c
+++ linux-4.1/net/ipv4/tcp_ipv4.c
@@ -77,6 +77,7 @@
 #include <net/busy_poll.h>
 
 #include <linux/inet.h>
+#include <linux/inetdevice.h>
 #include <linux/ipv6.h>
 #include <linux/stddef.h>
 #include <linux/proc_fs.h>
@@ -1651,7 +1652,7 @@ csum_error:
 		TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
 bad_packet:
 		TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
-	} else {
+	} else if (!IN_DEV_STEALTH(skb->dev->ip_ptr)) {
 		tcp_v4_send_reset(NULL, skb);
 	}
 
Index: linux-4.1/net/ipv4/udp.c
===================================================================
--- linux-4.1.orig/net/ipv4/udp.c
+++ linux-4.1/net/ipv4/udp.c
@@ -96,6 +96,7 @@
 #include <linux/timer.h>
 #include <linux/mm.h>
 #include <linux/inet.h>
+#include <linux/inetdevice.h>
 #include <linux/netdevice.h>
 #include <linux/slab.h>
 #include <net/tcp_states.h>
@@ -1823,7 +1824,8 @@ int __udp4_lib_rcv(struct sk_buff *skb,
 		goto csum_error;
 
 	UDP_INC_STATS_BH(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+	if (!IN_DEV_STEALTH(skb->dev->ip_ptr))
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 	/*
 	 * Hmm.  We got an UDP packet to a port to which we
Index: linux-4.1/net/ipv6/addrconf.c
===================================================================
--- linux-4.1.orig/net/ipv6/addrconf.c
+++ linux-4.1/net/ipv6/addrconf.c
@@ -5583,6 +5583,13 @@ static struct addrconf_sysctl_table
 			.proc_handler	= addrconf_sysctl_stable_secret,
 		},
 		{
+			.procname	= "stealth",
+			.data		= &ipv6_devconf.stealth,
+			.maxlen		= sizeof(int),
+			.mode		= 0644,
+			.proc_handler	= proc_dointvec,
+		},
+		{
 			/* sentinel */
 		}
 	},
Index: linux-4.1/net/ipv6/icmp.c
===================================================================
--- linux-4.1.orig/net/ipv6/icmp.c
+++ linux-4.1/net/ipv6/icmp.c
@@ -723,7 +723,8 @@ static int icmpv6_rcv(struct sk_buff *sk
 
 	switch (type) {
 	case ICMPV6_ECHO_REQUEST:
-		icmpv6_echo_reply(skb);
+		if (!idev->cnf.stealth)
+			icmpv6_echo_reply(skb);
 		break;
 
 	case ICMPV6_ECHO_REPLY:
Index: linux-4.1/net/ipv6/tcp_ipv6.c
===================================================================
--- linux-4.1.orig/net/ipv6/tcp_ipv6.c
+++ linux-4.1/net/ipv6/tcp_ipv6.c
@@ -1447,7 +1447,7 @@ csum_error:
 		TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
 bad_packet:
 		TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
-	} else {
+	} else if (!__in6_dev_get(skb->dev)->cnf.stealth) {
 		tcp_v6_send_reset(NULL, skb);
 	}
 
Index: linux-4.1/net/ipv6/udp.c
===================================================================
--- linux-4.1.orig/net/ipv6/udp.c
+++ linux-4.1/net/ipv6/udp.c
@@ -934,7 +934,8 @@ int __udp6_lib_rcv(struct sk_buff *skb,
 		goto csum_error;
 
 	UDP6_INC_STATS_BH(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
-	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
+	if (!__in6_dev_get(skb->dev)->cnf.stealth)
+		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
 
 	kfree_skb(skb);
 	return 0;
