Re: Restrict local IP announcements in ARP requests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



	Hello,

On Tue, 17 Feb 2004, David S. Miller wrote:

> > http://www.ssi.bg/~ja/tmp/arp_ignore-2.6.2-arp-1.diff
>
> I've applied this, the patch is a NOP unless people actually change
> the sysctl value from the default.
>
> This allows me to just put it in now, and we can refine the details of
> the new behavior next.
>
> Please make me a 2.4.x version of this patch (again with the other ARP
> thing applied already).

	Thanks, it is attached.

Regards

--
Julian Anastasov <ja@ssi.bg>
diff -ur v2.4.25-rc2-arp/linux/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt
--- v2.4.25-rc2-arp/linux/Documentation/networking/ip-sysctl.txt	2004-02-18 09:55:12.000000000 +0200
+++ linux/Documentation/networking/ip-sysctl.txt	2004-02-18 09:58:20.611648280 +0200
@@ -512,6 +512,24 @@
 	receiving answer from the resolved target while decreasing
 	the level announces more valid sender's information.
 
+arp_ignore - INTEGER
+	Define different modes for sending replies in response to
+	received ARP requests that resolve local target IP addresses:
+	0 - (default): reply for any local target IP address, configured
+	on any interface
+	1 - reply only if the target IP address is local address
+	configured on the incoming interface
+	2 - reply only if the target IP address is local address
+	configured on the incoming interface and both with the
+	sender's IP address are part from same subnet on this interface
+	3 - do not reply for local addresses configured with scope host,
+	only resolutions for global and link addresses are replied
+	4-7 - reserved
+	8 - do not reply for all local addresses
+
+	The max value from conf/{all,interface}/arp_ignore is used
+	when ARP request is received on the {interface}
+
 tag - INTEGER
 	Allows you to write a number, which can be used as required.
 	Default value is 0.
diff -ur v2.4.25-rc2-arp/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h
--- v2.4.25-rc2-arp/linux/include/linux/inetdevice.h	2004-02-18 09:55:12.000000000 +0200
+++ linux/include/linux/inetdevice.h	2004-02-18 09:58:20.613647976 +0200
@@ -19,6 +19,7 @@
 	int	tag;
 	int     arp_filter;
 	int	arp_announce;
+	int	arp_ignore;
 	int	medium_id;
 	int	force_igmp_version;
 	void	*sysctl;
@@ -71,6 +72,7 @@
 
 #define IN_DEV_ARPFILTER(in_dev)	(ipv4_devconf.arp_filter || (in_dev)->cnf.arp_filter)
 #define IN_DEV_ARP_ANNOUNCE(in_dev)	(max(ipv4_devconf.arp_announce, (in_dev)->cnf.arp_announce))
+#define IN_DEV_ARP_IGNORE(in_dev)	(max(ipv4_devconf.arp_ignore, (in_dev)->cnf.arp_ignore))
 
 struct in_ifaddr
 {
@@ -97,6 +99,7 @@
 extern struct in_device *inetdev_init(struct net_device *dev);
 extern struct in_device	*inetdev_by_index(int);
 extern u32		inet_select_addr(const struct net_device *dev, u32 dst, int scope);
+extern u32		inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scope);
 extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask);
 extern void		inet_forward_change(void);
 
diff -ur v2.4.25-rc2-arp/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.4.25-rc2-arp/linux/include/linux/sysctl.h	2004-02-18 09:55:12.000000000 +0200
+++ linux/include/linux/sysctl.h	2004-02-18 09:58:20.614647824 +0200
@@ -361,6 +361,7 @@
 	NET_IPV4_CONF_MEDIUM_ID=14,
 	NET_IPV4_CONF_FORCE_IGMP_VERSION=17,
 	NET_IPV4_CONF_ARP_ANNOUNCE=18,
+	NET_IPV4_CONF_ARP_IGNORE=19,
 };
 
 /* /proc/sys/net/ipv4/netfilter */
diff -ur v2.4.25-rc2-arp/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.4.25-rc2-arp/linux/net/ipv4/arp.c	2004-02-18 09:55:12.000000000 +0200
+++ linux/net/ipv4/arp.c	2004-02-18 09:58:20.616647520 +0200
@@ -371,6 +371,42 @@
 		read_unlock_bh(&neigh->lock);
 }
 
+static int arp_ignore(struct in_device *in_dev, struct net_device *dev,
+		      u32 sip, u32 tip)
+{
+	int scope;
+
+	switch (IN_DEV_ARP_IGNORE(in_dev)) {
+	case 0:	/* Reply, the tip is already validated */
+		return 0;
+	case 1:	/* Reply only if tip is configured on the incoming interface */
+		sip = 0;
+		scope = RT_SCOPE_HOST;
+		break;
+	case 2:	/*
+		 * Reply only if tip is configured on the incoming interface
+		 * and is in same subnet as sip
+		 */
+		scope = RT_SCOPE_HOST;
+		break;
+	case 3:	/* Do not reply for scope host addresses */
+		sip = 0;
+		scope = RT_SCOPE_LINK;
+		dev = NULL;
+		break;
+	case 4:	/* Reserved */
+	case 5:
+	case 6:
+	case 7:
+		return 0;
+	case 8:	/* Do not reply */
+		return 1;
+	default:
+		return 0;
+	}
+	return !inet_confirm_addr(dev, sip, tip, scope);
+}
+
 static int arp_filter(__u32 sip, __u32 tip, struct net_device *dev)
 {
 	struct rtable *rt;
@@ -781,7 +817,8 @@
 	/* Special case: IPv4 duplicate address detection packet (RFC2131) */
 	if (sip == 0) {
 		if (arp->ar_op == htons(ARPOP_REQUEST) &&
-		    inet_addr_type(tip) == RTN_LOCAL)
+		    inet_addr_type(tip) == RTN_LOCAL &&
+		    !arp_ignore(in_dev,dev,sip,tip))
 			arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
 		goto out;
 	}
@@ -796,7 +833,10 @@
 			n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
 			if (n) {
 				int dont_send = 0;
-				if (IN_DEV_ARPFILTER(in_dev))
+
+				if (!dont_send)
+					dont_send |= arp_ignore(in_dev,dev,sip,tip);
+				if (!dont_send && IN_DEV_ARPFILTER(in_dev))
 					dont_send |= arp_filter(sip,tip,dev); 
 				if (!dont_send)
 					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
diff -ur v2.4.25-rc2-arp/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.4.25-rc2-arp/linux/net/ipv4/devinet.c	2004-02-18 09:55:12.000000000 +0200
+++ linux/net/ipv4/devinet.c	2004-02-18 09:59:34.675388880 +0200
@@ -772,6 +772,84 @@
 	return 0;
 }
 
+static u32 confirm_addr_indev(struct in_device *in_dev, u32 dst,
+			      u32 local, int scope)
+{
+	int same = 0;
+	u32 addr = 0;
+
+	for_ifa(in_dev) {
+		if (!addr &&
+		    (local == ifa->ifa_local || !local) &&
+		    ifa->ifa_scope <= scope) {
+			addr = ifa->ifa_local;
+			if (same)
+				break;
+		}
+		if (!same) {
+			same = (!local || inet_ifa_match(local, ifa)) &&
+				(!dst || inet_ifa_match(dst, ifa));
+			if (same && addr) {
+				if (local || !dst)
+					break;
+				/* Is the selected addr into dst subnet? */
+				if (inet_ifa_match(addr, ifa))
+					break;
+				/* No, then can we use new local src? */
+				if (ifa->ifa_scope <= scope) {
+					addr = ifa->ifa_local;
+					break;
+				}
+				/* search for large dst subnet for addr */
+				same = 0;
+			}
+		}
+	} endfor_ifa(in_dev);
+
+	return same? addr : 0;
+}
+
+/*
+ * Confirm that local IP address exists using wildcards:
+ * - dev: only on this interface, 0=any interface
+ * - dst: only in the same subnet as dst, 0=any dst
+ * - local: address, 0=autoselect the local address
+ * - scope: maximum allowed scope value for the local address
+ */
+u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scope)
+{
+	u32 addr = 0;
+	struct in_device *in_dev;
+
+	if (dev) {
+		read_lock(&inetdev_lock);
+		if ((in_dev = __in_dev_get(dev))) {
+			read_lock(&in_dev->lock);
+			addr = confirm_addr_indev(in_dev, dst, local, scope);
+			read_unlock(&in_dev->lock);
+		}
+		read_unlock(&inetdev_lock);
+
+		return addr;
+	}
+
+	read_lock(&dev_base_lock);
+	read_lock(&inetdev_lock);
+	for (dev = dev_base; dev; dev = dev->next) {
+		if ((in_dev = __in_dev_get(dev))) {
+			read_lock(&in_dev->lock);
+			addr = confirm_addr_indev(in_dev, dst, local, scope);
+			read_unlock(&in_dev->lock);
+			if (addr)
+				break;
+		}
+	}
+	read_unlock(&inetdev_lock);
+	read_unlock(&dev_base_lock);
+
+	return addr;
+}
+
 /*
  *	Device notifier
  */
@@ -1057,7 +1135,7 @@
 static struct devinet_sysctl_table
 {
 	struct ctl_table_header *sysctl_header;
-	ctl_table devinet_vars[19];
+	ctl_table devinet_vars[20];
 	ctl_table devinet_dev[2];
 	ctl_table devinet_conf_dir[2];
 	ctl_table devinet_proto_dir[2];
@@ -1109,6 +1187,9 @@
 	{NET_IPV4_CONF_ARP_ANNOUNCE, "arp_announce",
 	 &ipv4_devconf.arp_announce, sizeof(int), 0644, NULL,
 	 &proc_dointvec},
+	{NET_IPV4_CONF_ARP_IGNORE, "arp_ignore",
+	 &ipv4_devconf.arp_ignore, sizeof(int), 0644, NULL,
+	 &proc_dointvec},
 	{NET_IPV4_CONF_FORCE_IGMP_VERSION, "force_igmp_version",
 	 &ipv4_devconf.force_igmp_version, sizeof(int), 0644, NULL,
 	 &proc_dointvec},

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux