diff -ur v2.4.26/linux/include/linux/in_route.h linux/include/linux/in_route.h --- v2.4.26/linux/include/linux/in_route.h 2004-04-04 12:56:06.000000000 +0300 +++ linux/include/linux/in_route.h 2004-04-04 12:57:53.064575304 +0300 @@ -13,6 +13,7 @@ #define RTCF_DIRECTDST 0x00020000 #define RTCF_REDIRECTED 0x00040000 #define RTCF_TPROXY 0x00080000 +#define RTCF_NOARP 0x00100000 #define RTCF_FAST 0x00200000 #define RTCF_MASQ 0x00400000 diff -ur v2.4.26/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h --- v2.4.26/linux/include/linux/rtnetlink.h 2004-04-04 12:56:06.000000000 +0300 +++ linux/include/linux/rtnetlink.h 2004-04-04 12:57:53.065575152 +0300 @@ -169,6 +169,7 @@ #define RTM_F_CLONED 0x200 /* This route is cloned */ #define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ #define RTM_F_PREFIX 0x800 /* Prefix addresses */ +#define RTM_F_NOARP 0x1000 /* Disable ARP for this route */ /* Reserved table identifiers */ @@ -317,6 +318,7 @@ /* ifa_flags */ #define IFA_F_SECONDARY 0x01 +#define IFA_F_HIDDEN 0x02 #define IFA_F_DEPRECATED 0x20 #define IFA_F_TENTATIVE 0x40 diff -ur v2.4.26/linux/net/ipv4/arp.c linux/net/ipv4/arp.c --- v2.4.26/linux/net/ipv4/arp.c 2004-04-04 12:56:06.000000000 +0300 +++ linux/net/ipv4/arp.c 2004-04-04 13:00:33.793140848 +0300 @@ -70,6 +70,7 @@ * arp_xmit so intermediate drivers like * bonding can change the skb before * sending (e.g. insert 8021q tag). + * Julian Anastasov: Per-route ARP control */ #include @@ -327,21 +328,37 @@ u32 target = *(u32*)neigh->primary_key; int probes = atomic_read(&neigh->probes); struct in_device *in_dev = in_dev_get(dev); + struct rtable *rt; if (!in_dev) return; + /* Determine source IP for the probing packet. */ + if (skb != NULL) { + rt = (struct rtable*)skb->dst; + if (rt && !rt->rt_iif && !(rt->rt_flags & RTCF_NOARP)) + saddr = rt->rt_src; + } + if (!saddr) { + if (!ip_route_output(&rt, target, 0, 0, dev->ifindex)) { + saddr = rt->rt_src; + ip_rt_put(rt); + } + } + switch (IN_DEV_ARP_ANNOUNCE(in_dev)) { default: case 0: /* By default announce any local IP */ + if (saddr) + break; 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; - if (inet_addr_type(saddr) == RTN_LOCAL) { + if (saddr || (saddr = skb->nh.iph->saddr, + inet_addr_type(saddr) == RTN_LOCAL)) { /* saddr should be known to target */ if (inet_addr_onlink(in_dev, target, saddr)) break; @@ -411,20 +428,35 @@ return !inet_confirm_addr(dev, sip, tip, scope); } -static int arp_filter(__u32 sip, __u32 tip, struct net_device *dev) +static int arp_filter(struct sk_buff *skb, __u32 sip, __u32 tip, + struct in_device *in_dev) { struct rtable *rt; int flag = 0; - /*unsigned long now; */ + if (!IN_DEV_ARPFILTER(in_dev)) + return 0; + + /* Always answer direct queries. */ + if (skb->pkt_type == PACKET_HOST) + return 0; + + /* Then check routes: + * primarily, this check is used to not to answer to some requests if + * several interfaces are connected to the same segment. + * This check also may be used for manual control of who sees IP + * addresses at which link-level addresses by installing prohibiting + * routes. -- 2001/05/20 SAW + */ if (ip_route_output(&rt, sip, tip, 0, 0) < 0) return 1; - if (rt->u.dst.dev != dev) { + if (rt->u.dst.dev != in_dev->dev) { NET_INC_STATS_BH(ArpFilter); flag = 1; } ip_rt_put(rt); - return flag; + + return flag; } /* OBSOLETE FUNCTIONS */ @@ -865,9 +897,11 @@ int dont_send = 0; if (!dont_send) + dont_send |= (rt->rt_flags & RTCF_NOARP); + 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); + dont_send |= arp_filter(skb,sip,tip,in_dev); if (!dont_send) arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); @@ -882,6 +916,9 @@ if (n) neigh_release(n); + if (rt->rt_flags & RTCF_NOARP) + goto out; + if (skb->stamp.tv_sec == 0 || skb->pkt_type == PACKET_HOST || in_dev->arp_parms->proxy_delay == 0) { diff -ur v2.4.26/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c --- v2.4.26/linux/net/ipv4/fib_frontend.c 2004-04-04 12:56:06.000000000 +0300 +++ linux/net/ipv4/fib_frontend.c 2004-04-04 12:57:53.066575000 +0300 @@ -445,6 +445,9 @@ req.rtm.rtm_protocol = RTPROT_KERNEL; req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST); req.rtm.rtm_type = type; + if (ifa->ifa_flags & IFA_F_HIDDEN && type == RTN_LOCAL && + cmd == RTM_NEWROUTE) + req.rtm.rtm_flags |= RTM_F_NOARP; rta.rta_dst = &dst; rta.rta_prefsrc = &ifa->ifa_local; diff -ur v2.4.26/linux/net/ipv4/route.c linux/net/ipv4/route.c --- v2.4.26/linux/net/ipv4/route.c 2004-04-04 12:56:06.000000000 +0300 +++ linux/net/ipv4/route.c 2004-04-04 12:57:53.068574696 +0300 @@ -1484,6 +1484,9 @@ if (res.type == RTN_BROADCAST) goto brd_input; + if (res.fi && res.fi->fib_flags & RTM_F_NOARP) + flags |= RTCF_NOARP; + if (res.type == RTN_LOCAL) { int result; result = fib_validate_source(saddr, daddr, tos, @@ -1921,6 +1924,9 @@ if (res.type == RTN_NAT) goto e_inval; + if (res.fi && res.fi->fib_flags & RTM_F_NOARP) + flags |= RTCF_NOARP; + if (res.type == RTN_LOCAL) { if (!key.src) key.src = key.dst; @@ -2124,6 +2130,8 @@ r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED; if (rt->rt_flags & RTCF_NOTIFY) r->rtm_flags |= RTM_F_NOTIFY; + if (rt->rt_flags & RTCF_NOARP) + r->rtm_flags |= RTM_F_NOARP; RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst); if (rt->key.src) { r->rtm_src_len = 32; @@ -2248,6 +2256,8 @@ skb->dst = &rt->u.dst; if (rtm->rtm_flags & RTM_F_NOTIFY) rt->rt_flags |= RTCF_NOTIFY; + if (rtm->rtm_flags & RTM_F_NOARP) + rt->rt_flags |= RTCF_NOARP; NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;