diff -ur v2.6.2-bk/linux/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt --- v2.6.2-bk/linux/Documentation/networking/ip-sysctl.txt 2004-02-13 01:21:02.000000000 +0200 +++ linux/Documentation/networking/ip-sysctl.txt 2004-02-13 01:24:03.721159936 +0200 @@ -499,6 +499,37 @@ conf/{all,interface}/arp_filter is set to TRUE, it will be disabled otherwise +arp_announce - INTEGER + Define different restriction levels for announcing the local + source IP address from IP packets in ARP requests sent on + interface: + 0 - (default) Use any local address, configured on any interface + 1 - Try to avoid local addresses that are not in the target's + subnet for this interface. This mode is useful when target + hosts reachable via this interface require the source IP + address in ARP requests to be part of their logical network + configured on the receiving interface. When we generate the + request we will check all our subnets that include the + target IP and will preserve the source address if it is from + such subnet. If there is no such subnet we select source + address according to the rules for level 2. + 2 - Always use the best local address for this target. + In this mode we ignore the source address in the IP packet + and try to select local address that we prefer for talks with + the target host. Such local address is selected by looking + for primary IP addresses on all our subnets on the outgoing + interface that include the target IP address. If no suitable + local address is found we select the first local address + we have on the outgoing interface or on all other interfaces, + with the hope we will receive reply for our request and + even sometimes no matter the source IP address we announce. + + The max value from conf/{all,interface}/arp_announce is used. + + Increasing the restriction level gives more chance for + receiving answer from the resolved target while decreasing + the level announces more valid sender's information. + tag - INTEGER Allows you to write a number, which can be used as required. Default value is 0. diff -ur v2.6.2-bk/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h --- v2.6.2-bk/linux/include/linux/inetdevice.h 2004-02-13 01:21:15.000000000 +0200 +++ linux/include/linux/inetdevice.h 2004-02-13 01:24:03.722159784 +0200 @@ -18,6 +18,7 @@ int mc_forwarding; int tag; int arp_filter; + int arp_announce; int medium_id; int no_xfrm; int no_policy; @@ -71,6 +72,7 @@ (ipv4_devconf.accept_redirects || (in_dev)->cnf.accept_redirects))) #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)) struct in_ifaddr { diff -ur v2.6.2-bk/linux/include/linux/sysctl.h linux/include/linux/sysctl.h --- v2.6.2-bk/linux/include/linux/sysctl.h 2004-02-13 01:21:29.000000000 +0200 +++ linux/include/linux/sysctl.h 2004-02-13 01:24:53.509590944 +0200 @@ -362,6 +362,7 @@ NET_IPV4_CONF_NOXFRM=15, NET_IPV4_CONF_NOPOLICY=16, NET_IPV4_CONF_FORCE_IGMP_VERSION=17, + NET_IPV4_CONF_ARP_ANNOUNCE=18, }; /* /proc/sys/net/ipv4/netfilter */ diff -ur v2.6.2-bk/linux/net/ipv4/arp.c linux/net/ipv4/arp.c --- v2.6.2-bk/linux/net/ipv4/arp.c 2004-02-13 01:21:40.000000000 +0200 +++ linux/net/ipv4/arp.c 2004-02-13 01:24:03.725159328 +0200 @@ -325,15 +325,40 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) { - u32 saddr; + u32 saddr = 0; u8 *dst_ha = NULL; struct net_device *dev = neigh->dev; u32 target = *(u32*)neigh->primary_key; int probes = atomic_read(&neigh->probes); + struct in_device *in_dev = in_dev_get(dev); + + if (!in_dev) + return; - if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) + switch (IN_DEV_ARP_ANNOUNCE(in_dev)) { + default: + case 0: /* By default announce any local IP */ + if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) + saddr = skb->nh.iph->saddr; + break; + case 1: /* Restrict announcements of saddr in same subnet */ + if (!skb) + break; saddr = skb->nh.iph->saddr; - else + if (inet_addr_type(saddr) == RTN_LOCAL) { + /* saddr should be known to target */ + if (inet_addr_onlink(in_dev, target, saddr)) + break; + } + saddr = 0; + break; + case 2: /* Avoid secondary IPs, get a primary/preferred one */ + break; + } + + if (in_dev) + in_dev_put(in_dev); + if (!saddr) saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); if ((probes -= neigh->parms->ucast_probes) < 0) { diff -ur v2.6.2-bk/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c --- v2.6.2-bk/linux/net/ipv4/devinet.c 2004-02-13 01:21:52.000000000 +0200 +++ linux/net/ipv4/devinet.c 2004-02-13 01:26:10.534881320 +0200 @@ -1132,7 +1132,7 @@ static struct devinet_sysctl_table { struct ctl_table_header *sysctl_header; - ctl_table devinet_vars[18]; + ctl_table devinet_vars[19]; ctl_table devinet_dev[2]; ctl_table devinet_conf_dir[2]; ctl_table devinet_proto_dir[2]; @@ -1252,6 +1252,14 @@ .proc_handler = &proc_dointvec, }, { + .ctl_name = NET_IPV4_CONF_ARP_ANNOUNCE, + .procname = "arp_announce", + .data = &ipv4_devconf.arp_announce, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = NET_IPV4_CONF_NOXFRM, .procname = "disable_xfrm", .data = &ipv4_devconf.no_xfrm,