diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index c0f7aec..88f78b6 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -31,6 +31,7 @@ struct ipv4_devconf int no_policy; int force_igmp_version; int promote_secondaries; + int accept_sts; void *sysctl; }; @@ -84,6 +85,7 @@ struct in_device #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)) +#define IN_DEV_ACCEPT_STS(in_dev) (max(ipv4_devconf.accept_sts, (in_dev)->cnf.accept_sts)) struct in_ifaddr { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 47f1c53..6c00bf4 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -496,6 +496,7 @@ enum NET_IPV4_CONF_ARP_IGNORE=19, NET_IPV4_CONF_PROMOTE_SECONDARIES=20, NET_IPV4_CONF_ARP_ACCEPT=21, + NET_IPV4_CONF_ACCEPT_STS=22, __NET_IPV4_CONF_MAX }; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 7110779..9866f1b 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -419,6 +419,26 @@ static int arp_ignore(struct in_device *in_dev, struct net_device *dev, return !inet_confirm_addr(dev, sip, tip, scope); } +static int is_ip_on_dev(struct net_device* dev, __u32 ip) { + int rv = 0; + struct in_device* in_dev = in_dev_get(dev); + if (in_dev) { + struct in_ifaddr *ifa; + + rcu_read_lock(); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_address == ip) { + /* match */ + rv = 1; + break; + } + } + rcu_read_unlock(); + in_dev_put(in_dev); + } + return rv; +} + static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev) { struct flowi fl = { .nl_u = { .ip4_u = { .daddr = sip, @@ -430,8 +450,38 @@ static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev) if (ip_route_output_key(&rt, &fl) < 0) return 1; if (rt->u.dst.dev != dev) { - NET_INC_STATS_BH(LINUX_MIB_ARPFILTER); - flag = 1; + struct in_device *in_dev = in_dev_get(dev); + if (in_dev && IN_DEV_ACCEPT_STS(in_dev) && + (rt->u.dst.dev == &loopback_dev)) { + /* Accept these IFF target-ip == dev's IP */ + /* TODO: Need to force the ARP response back out the interface + * instead of letting it route locally. + */ + + if (is_ip_on_dev(dev, tip)) { + /* OK, we'll let this special case slide, so that we can + * arp from one local interface to another. This seems + * to work, but could use some review. --Ben + */ + /*printk("arp_filter, sip: %x tip: %x dev: %s, STS override (ip on dev)\n", + sip, tip, dev->name);*/ + } + else { + /*printk("arp_filter, sip: %x tip: %x dev: %s, IP is NOT on dev\n", + sip, tip, dev->name);*/ + NET_INC_STATS_BH(LINUX_MIB_ARPFILTER); + flag = 1; + } + } + else { + /*printk("arp_filter, not lpbk sip: %x tip: %x dev: %s flgs: %hx dst.dev: %p lbk: %p\n", + sip, tip, dev->name, dev->priv_flags, rt->u.dst.dev, &loopback_dev);*/ + NET_INC_STATS_BH(LINUX_MIB_ARPFILTER); + flag = 1; + } + if (in_dev) { + in_dev_put(in_dev); + } } ip_rt_put(rt); return flag; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 7f95e6e..33ac2ed 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1513,6 +1513,15 @@ static struct devinet_sysctl_table { .proc_handler = &ipv4_doint_and_flush, .strategy = &ipv4_doint_and_flush_strategy, }, + { + .ctl_name = NET_IPV4_CONF_ACCEPT_STS, + .procname = "accept_sts", + .data = &ipv4_devconf.accept_sts, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + }, .devinet_dev = { { diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 837f295..9b57bf5 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -206,8 +206,16 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif, if (fib_lookup(&fl, &res)) goto last_resort; - if (res.type != RTN_UNICAST) - goto e_inval_res; + if (res.type != RTN_UNICAST) { + if ((res.type == RTN_LOCAL) && + (IN_DEV_ACCEPT_STS(in_dev))) { + /* All is OK */ + } + else { + goto e_inval_res; + } + } + *spec_dst = FIB_RES_PREFSRC(res); fib_combine_itag(itag, &res); #ifdef CONFIG_IP_ROUTE_MULTIPATH