diff -ur v2.4.26/linux/Documentation/filesystems/proc.txt linux/Documentation/filesystems/proc.txt --- v2.4.26/linux/Documentation/filesystems/proc.txt 2004-04-04 13:08:30.000000000 +0300 +++ linux/Documentation/filesystems/proc.txt 2004-04-04 13:08:55.572858736 +0300 @@ -1550,6 +1550,18 @@ (external addresses can still be spoofed), without the need for additional firewall rules. +forward_shared +-------------- + +Integer value determines if a source validation should allow forwarding +of packets with local source address. 1 means yes, 0 means no. By default +the flag is disabled and such packets are not forwarded. + +If you enable this flag on internal network, the router will forward +packets from internal hosts with shared IP addresses no matter how +the rp_filter is set. This flag is activated only if it is enabled +both in specific device section and in "all" section. + secure_redirects ---------------- @@ -1567,6 +1579,16 @@ Determines whether to send ICMP redirects to other hosts. +hidden +------ + +Hide addresses attached to this device from other devices. +Such addresses will never be selected by source address autoselection +mechanism, host does not answer broadcast ARP requests for them, +does not announce them as source address of ARP requests, but they +are still reachable via IP. This flag is activated only if it is +enabled both in specific device section and in "all" section. + Routing settings ---------------- diff -ur v2.4.26/linux/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt --- v2.4.26/linux/Documentation/networking/ip-sysctl.txt 2004-04-04 13:08:30.000000000 +0300 +++ linux/Documentation/networking/ip-sysctl.txt 2004-04-04 13:08:55.573858584 +0300 @@ -385,6 +385,17 @@ forwarding - BOOLEAN Enable IP forwarding on this interface. +forward_shared - BOOLEAN + Integer value determines if a source validation should allow + forwarding of packets with local source address. 1 means yes, + 0 means no. By default the flag is disabled and such packets + are not forwarded. + + If you enable this flag on internal network, the router will forward + packets from internal hosts with shared IP addresses no matter how + the rp_filter is set. This flag is activated only if it is + enabled both in specific device section and in "all" section. + mc_forwarding - BOOLEAN Do multicast routing. The kernel needs to be compiled with CONFIG_MROUTE and a multicast routing daemon is required. @@ -530,6 +541,14 @@ The max value from conf/{all,interface}/arp_ignore is used when ARP request is received on the {interface} +hidden - BOOLEAN + Hide addresses attached to this device from other devices. + Such addresses will never be selected by source address autoselection + mechanism, host does not answer broadcast ARP requests for them, + does not announce them as source address of ARP requests, but they + are still reachable via IP. This flag is activated only if it is + enabled both in specific device section and in "all" section. + tag - INTEGER Allows you to write a number, which can be used as required. Default value is 0. diff -ur v2.4.26/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h --- v2.4.26/linux/include/linux/inetdevice.h 2004-04-04 13:08:30.000000000 +0300 +++ linux/include/linux/inetdevice.h 2004-04-04 13:08:55.573858584 +0300 @@ -17,11 +17,13 @@ int forwarding; int mc_forwarding; int tag; + int hidden; int arp_filter; int arp_announce; int arp_ignore; int medium_id; int force_igmp_version; + int forward_shared; void *sysctl; }; @@ -60,6 +62,7 @@ #define IN_DEV_LOG_MARTIANS(in_dev) (ipv4_devconf.log_martians || (in_dev)->cnf.log_martians) #define IN_DEV_PROXY_ARP(in_dev) (ipv4_devconf.proxy_arp || (in_dev)->cnf.proxy_arp) +#define IN_DEV_HIDDEN(in_dev) ((in_dev)->cnf.hidden && ipv4_devconf.hidden) #define IN_DEV_SHARED_MEDIA(in_dev) (ipv4_devconf.shared_media || (in_dev)->cnf.shared_media) #define IN_DEV_TX_REDIRECTS(in_dev) (ipv4_devconf.send_redirects || (in_dev)->cnf.send_redirects) #define IN_DEV_SEC_REDIRECTS(in_dev) (ipv4_devconf.secure_redirects || (in_dev)->cnf.secure_redirects) @@ -76,6 +79,8 @@ #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_FORWARD_SHARED(in_dev) ((in_dev)->cnf.forward_shared && ipv4_devconf.forward_shared) + struct in_ifaddr { struct in_ifaddr *ifa_next; 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 13:08:30.000000000 +0300 +++ linux/include/linux/in_route.h 2004-04-04 13:08:55.574858432 +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 13:08:30.000000000 +0300 +++ linux/include/linux/rtnetlink.h 2004-04-04 13:08:55.574858432 +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/include/linux/sysctl.h linux/include/linux/sysctl.h --- v2.4.26/linux/include/linux/sysctl.h 2004-04-04 13:08:30.000000000 +0300 +++ linux/include/linux/sysctl.h 2004-04-04 13:08:55.575858280 +0300 @@ -364,6 +364,8 @@ NET_IPV4_CONF_FORCE_IGMP_VERSION=17, NET_IPV4_CONF_ARP_ANNOUNCE=18, NET_IPV4_CONF_ARP_IGNORE=19, + NET_IPV4_CONF_HIDDEN=20, + NET_IPV4_CONF_FORWARD_SHARED=21, }; /* /proc/sys/net/ipv4/netfilter */ diff -ur v2.4.26/linux/include/net/ip_fib.h linux/include/net/ip_fib.h --- v2.4.26/linux/include/net/ip_fib.h 2004-04-04 13:08:30.000000000 +0300 +++ linux/include/net/ip_fib.h 2004-04-04 13:08:55.575858280 +0300 @@ -203,7 +203,7 @@ extern int inet_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); extern int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb); extern int fib_validate_source(u32 src, u32 dst, u8 tos, int oif, - struct net_device *dev, u32 *spec_dst, u32 *itag); + struct net_device *dev, u32 *spec_dst, u32 *itag, int our); extern void fib_select_multipath(const struct rt_key *key, struct fib_result *res); /* Exported by fib_semantics.c */ 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 13:08:30.000000000 +0300 +++ linux/net/ipv4/arp.c 2004-04-04 13:09:22.551757320 +0300 @@ -70,6 +70,9 @@ * arp_xmit so intermediate drivers like * bonding can change the skb before * sending (e.g. insert 8021q tag). + * Julian Anastasov: "hidden" flag: hide the + * interface and don't reply for it + * Julian Anastasov: Per-route ARP control */ #include @@ -327,21 +330,42 @@ u32 target = *(u32*)neigh->primary_key; int probes = atomic_read(&neigh->probes); struct in_device *in_dev = in_dev_get(dev); + struct rtable *rt; + struct in_device *in_dev2 = NULL; + struct net_device *dev2 = NULL; 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) && + (dev2 = ip_dev_find(skb->nh.iph->saddr)) != NULL && + (in_dev2 = in_dev_get(dev2)) != NULL && + !IN_DEV_HIDDEN(in_dev2)) + 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; @@ -352,6 +376,11 @@ break; } + if (dev2) { + if (in_dev2) + in_dev_put(in_dev2); + dev_put(dev2); + } if (in_dev) in_dev_put(in_dev); if (!saddr) @@ -411,22 +440,57 @@ 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; } +static int arp_hidden(u32 tip, struct net_device *dev) +{ + struct net_device *dev2 = NULL; + struct in_device *in_dev2 = NULL; + int ret = 0; + + if (!ipv4_devconf.hidden) + return 0; + + if ((dev2 = ip_dev_find(tip)) && dev2 != dev && + (in_dev2 = in_dev_get(dev2)) && IN_DEV_HIDDEN(in_dev2)) + ret = 1; + if (dev2) { + if (in_dev2) + in_dev_put(in_dev2); + dev_put(dev2); + } + return ret; +} + /* OBSOLETE FUNCTIONS */ /* @@ -848,6 +912,7 @@ if (sip == 0) { if (arp->ar_op == htons(ARPOP_REQUEST) && inet_addr_type(tip) == RTN_LOCAL && + !arp_hidden(tip, dev) && !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; @@ -865,9 +930,13 @@ 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 && skb->pkt_type != PACKET_HOST) + dont_send |= arp_hidden(tip,dev); if (!dont_send) arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); @@ -882,6 +951,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/devinet.c linux/net/ipv4/devinet.c --- v2.4.26/linux/net/ipv4/devinet.c 2004-04-04 13:08:30.000000000 +0300 +++ linux/net/ipv4/devinet.c 2004-04-04 13:08:55.577857976 +0300 @@ -756,7 +756,8 @@ read_lock(&in_dev->lock); for_primary_ifa(in_dev) { - if (ifa->ifa_scope != RT_SCOPE_LINK && + if (!IN_DEV_HIDDEN(in_dev) && + ifa->ifa_scope != RT_SCOPE_LINK && ifa->ifa_scope <= scope) { read_unlock(&in_dev->lock); read_unlock(&inetdev_lock); @@ -1134,7 +1135,7 @@ static struct devinet_sysctl_table { struct ctl_table_header *sysctl_header; - ctl_table devinet_vars[20]; + ctl_table devinet_vars[22]; ctl_table devinet_dev[2]; ctl_table devinet_conf_dir[2]; ctl_table devinet_proto_dir[2]; @@ -1180,6 +1181,9 @@ {NET_IPV4_CONF_TAG, "tag", &ipv4_devconf.tag, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_CONF_HIDDEN, "hidden", + &ipv4_devconf.hidden, sizeof(int), 0644, NULL, + &proc_dointvec}, {NET_IPV4_CONF_ARPFILTER, "arp_filter", &ipv4_devconf.arp_filter, sizeof(int), 0644, NULL, &proc_dointvec}, @@ -1192,6 +1196,9 @@ {NET_IPV4_CONF_FORCE_IGMP_VERSION, "force_igmp_version", &ipv4_devconf.force_igmp_version, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_CONF_FORWARD_SHARED, "forward_shared", + &ipv4_devconf.forward_shared, sizeof(int), 0644, NULL, + &proc_dointvec}, {0}}, {{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, devinet_sysctl.devinet_vars},{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 13:08:30.000000000 +0300 +++ linux/net/ipv4/fib_frontend.c 2004-04-04 13:08:55.577857976 +0300 @@ -204,13 +204,15 @@ */ int fib_validate_source(u32 src, u32 dst, u8 tos, int oif, - struct net_device *dev, u32 *spec_dst, u32 *itag) + struct net_device *dev, u32 *spec_dst, u32 *itag, + int our) { struct in_device *in_dev; struct rt_key key; struct fib_result res; int no_addr, rpf; int ret; + int fwdsh = 0; key.dst = src; key.src = dst; @@ -225,6 +227,7 @@ if (in_dev) { no_addr = in_dev->ifa_list == NULL; rpf = IN_DEV_RPFILTER(in_dev); + fwdsh = IN_DEV_FORWARD_SHARED(in_dev); } read_unlock(&inetdev_lock); @@ -233,7 +236,12 @@ if (fib_lookup(&key, &res)) goto last_resort; - if (res.type != RTN_UNICAST) + if (fwdsh) { + fwdsh = (res.type == RTN_LOCAL && !our); + if (fwdsh) + rpf = 0; + } + if (res.type != RTN_UNICAST && !fwdsh) goto e_inval_res; *spec_dst = FIB_RES_PREFSRC(res); fib_combine_itag(itag, &res); @@ -253,6 +261,8 @@ if (rpf) goto e_inval; key.oif = dev->ifindex; + if (fwdsh) + key.iif = loopback_dev.ifindex; ret = 0; if (fib_lookup(&key, &res) == 0) { @@ -445,6 +455,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 13:08:30.000000000 +0300 +++ linux/net/ipv4/route.c 2004-04-04 13:08:55.579857672 +0300 @@ -1324,7 +1324,7 @@ goto e_inval; spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); } else if (fib_validate_source(saddr, 0, tos, 0, - dev, &spec_dst, &itag) < 0) + dev, &spec_dst, &itag, our) < 0) goto e_inval; rth = dst_alloc(&ipv4_dst_ops); @@ -1484,11 +1484,14 @@ 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, loopback_dev.ifindex, - dev, &spec_dst, &itag); + dev, &spec_dst, &itag, 1); if (result < 0) goto martian_source; if (result) @@ -1515,7 +1518,7 @@ } err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(res), dev, - &spec_dst, &itag); + &spec_dst, &itag, 0); if (err < 0) goto martian_source; @@ -1599,7 +1602,7 @@ spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); else { err = fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst, - &itag); + &itag, 1); if (err < 0) goto martian_source; if (err) @@ -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;