Re: Restrict local IP announcements in ARP requests

From: Julian Anastasov
Date: Wed Feb 18 2004 - 03:23:00 EST



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@xxxxxx>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},