From a52dca4ece3b555434e8b04ae0dd7c4f196823d9 Mon Sep 17 00:00:00 2001 From: Kazunori Miyazawa Date: Tue, 10 Feb 2004 18:37:48 -0800 Subject: [IPV6]: Unify ipv6 output routine. --- include/linux/ipv6_route.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ipv6_route.h b/include/linux/ipv6_route.h index 3a279f106325..46d8b7e8b1aa 100644 --- a/include/linux/ipv6_route.h +++ b/include/linux/ipv6_route.h @@ -24,7 +24,6 @@ #define RTF_CACHE 0x01000000 /* cache entry */ #define RTF_FLOW 0x02000000 /* flow significant route */ #define RTF_POLICY 0x04000000 /* policy route */ -#define RTF_NDISC 0x08000000 /* ndisc route */ #define RTF_LOCAL 0x80000000 -- cgit v1.2.3 From 8daca41052eee6c4b88e0313653f520745150d28 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 13 Feb 2004 07:06:58 -0800 Subject: [IPSEC]: Move hardware headers for decaped packets. Move the hardware header so that it appears next to the payload for AF_PACKET sockets. --- include/linux/skbuff.h | 3 +++ net/core/dev.c | 1 + net/ipv4/xfrm4_input.c | 18 +++++++++++++----- net/ipv6/xfrm6_input.c | 25 +++++++++++++++++-------- 4 files changed, 34 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e12d9bc152da..56590cedc22c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -163,6 +163,7 @@ struct skb_shared_info { * @cb: Control buffer. Free for use by every layer. Put private vars here * @len: Length of actual data * @data_len: Data length + * @mac_len: Length of link layer header * @csum: Checksum * @__unused: Dead field, may be reused * @cloned: Head may be cloned (check refcnt to be sure) @@ -204,6 +205,7 @@ struct sk_buff { struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; + struct ipv6hdr *ipv6h; unsigned char *raw; } h; @@ -232,6 +234,7 @@ struct sk_buff { unsigned int len, data_len, + mac_len, csum; unsigned char local_df, cloned, diff --git a/net/core/dev.c b/net/core/dev.c index c10acfc613e7..b2333ae5affe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1742,6 +1742,7 @@ int netif_receive_skb(struct sk_buff *skb) #endif skb->h.raw = skb->nh.raw = skb->data; + skb->mac_len = skb->nh.raw - skb->mac.raw; pt_prev = NULL; rcu_read_lock(); diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 9040408dbf81..b045c87197f1 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -9,6 +9,7 @@ * */ +#include #include #include #include @@ -18,9 +19,10 @@ int xfrm4_rcv(struct sk_buff *skb) return xfrm4_rcv_encap(skb, 0); } -static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb) +static inline void ipip_ecn_decapsulate(struct sk_buff *skb) { - struct iphdr *inner_iph = skb->nh.iph; + struct iphdr *outer_iph = skb->nh.iph; + struct iphdr *inner_iph = skb->h.ipiph; if (INET_ECN_is_ce(outer_iph->tos) && INET_ECN_is_not_ce(inner_iph->tos)) @@ -95,10 +97,16 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) if (x->props.mode) { if (iph->protocol != IPPROTO_IPIP) goto drop; - skb->nh.raw = skb->data; + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + goto drop; + if (skb_cloned(skb) && + pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + goto drop; if (!(x->props.flags & XFRM_STATE_NOECN)) - ipip_ecn_decapsulate(iph, skb); - iph = skb->nh.iph; + ipip_ecn_decapsulate(skb); + skb->mac.raw = memmove(skb->data - skb->mac_len, + skb->mac.raw, skb->mac_len); + skb->nh.raw = skb->data; memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); decaps = 1; break; diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 6df399edce74..c679a3ce4e16 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -9,17 +9,20 @@ * IPv6 support */ +#include #include #include #include #include -static inline void ipip6_ecn_decapsulate(struct ipv6hdr *iph, - struct sk_buff *skb) +static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) { - if (INET_ECN_is_ce(ip6_get_dsfield(iph)) && - INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h))) - IP6_ECN_set_ce(skb->nh.ipv6h); + struct ipv6hdr *outer_iph = skb->nh.ipv6h; + struct ipv6hdr *inner_iph = skb->h.ipv6h; + + if (INET_ECN_is_ce(ip6_get_dsfield(outer_iph)) && + INET_ECN_is_not_ce(ip6_get_dsfield(inner_iph))) + IP6_ECN_set_ce(inner_iph); } int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) @@ -77,10 +80,16 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) if (x->props.mode) { /* XXX */ if (nexthdr != IPPROTO_IPV6) goto drop; - skb->nh.raw = skb->data; + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto drop; + if (skb_cloned(skb) && + pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + goto drop; if (!(x->props.flags & XFRM_STATE_NOECN)) - ipip6_ecn_decapsulate(iph, skb); - iph = skb->nh.ipv6h; + ipip6_ecn_decapsulate(skb); + skb->mac.raw = memmove(skb->data - skb->mac_len, + skb->mac.raw, skb->mac_len); + skb->nh.raw = skb->data; decaps = 1; break; } -- cgit v1.2.3 From cd0e8af5454a59958c753071cd63576a8e83301e Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 13 Feb 2004 07:20:47 -0800 Subject: [IPV4]: Add configurable restriction of local IP announcements in ARP requests. --- Documentation/networking/ip-sysctl.txt | 31 +++++++++++++++++++++++++++++++ include/linux/inetdevice.h | 2 ++ include/linux/sysctl.h | 1 + net/ipv4/arp.c | 31 ++++++++++++++++++++++++++++--- net/ipv4/devinet.c | 10 +++++++++- 5 files changed, 71 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index c28305928945..03213c257b91 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -499,6 +499,37 @@ arp_filter - BOOLEAN 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 --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 1c5eb02667bb..d2100b938edb 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -18,6 +18,7 @@ struct ipv4_devconf int mc_forwarding; int tag; int arp_filter; + int arp_announce; int medium_id; int no_xfrm; int no_policy; @@ -71,6 +72,7 @@ struct in_device (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 --git a/include/linux/sysctl.h b/include/linux/sysctl.h index dc4167a0dbf2..cab2610aa003 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -362,6 +362,7 @@ enum 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 --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 0de93f953ef9..5f382df39910 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -325,15 +325,40 @@ static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb) 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 --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 4f3415afc983..38ebc705b8b6 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1132,7 +1132,7 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int *name, int nlen, 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]; @@ -1251,6 +1251,14 @@ static struct devinet_sysctl_table { .mode = 0644, .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", -- cgit v1.2.3 From fdd2f9fbb05c87643c463d557e3babb2f4d2a3d3 Mon Sep 17 00:00:00 2001 From: David Stevens Date: Mon, 16 Feb 2004 06:49:24 -0800 Subject: [IPV6]: Add sysctl to force MLD version. --- include/linux/ipv6.h | 2 ++ include/linux/sysctl.h | 3 ++- net/ipv6/addrconf.c | 12 +++++++++++- net/ipv6/mcast.c | 6 ++++-- 4 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 29911bf4218e..e98637f35817 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -136,6 +136,7 @@ struct ipv6_devconf { __s32 rtr_solicits; __s32 rtr_solicit_interval; __s32 rtr_solicit_delay; + __s32 force_mld_version; #ifdef CONFIG_IPV6_PRIVACY __s32 use_tempaddr; __s32 temp_valid_lft; @@ -165,6 +166,7 @@ enum { DEVCONF_REGEN_MAX_RETRY, DEVCONF_MAX_DESYNC_FACTOR, DEVCONF_MAX_ADDRESSES, + DEVCONF_FORCE_MLD_VERSION, DEVCONF_MAX }; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index cab2610aa003..8085f0e55513 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -424,7 +424,8 @@ enum { NET_IPV6_TEMP_PREFERED_LFT=13, NET_IPV6_REGEN_MAX_RETRY=14, NET_IPV6_MAX_DESYNC_FACTOR=15, - NET_IPV6_MAX_ADDRESSES=16 + NET_IPV6_MAX_ADDRESSES=16, + NET_IPV6_FORCE_MLD_VERSION=17 }; /* /proc/sys/net/ipv6/icmp */ diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c8e6d980236b..a4673092a2a4 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -149,6 +149,7 @@ struct ipv6_devconf ipv6_devconf = { .accept_ra = 1, .accept_redirects = 1, .autoconf = 1, + .force_mld_version = 0, .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, @@ -2739,6 +2740,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; array[DEVCONF_RTR_SOLICIT_INTERVAL] = cnf->rtr_solicit_interval; array[DEVCONF_RTR_SOLICIT_DELAY] = cnf->rtr_solicit_delay; + array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; #ifdef CONFIG_IPV6_PRIVACY array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; @@ -3042,7 +3044,7 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table, static struct addrconf_sysctl_table { struct ctl_table_header *sysctl_header; - ctl_table addrconf_vars[17]; + ctl_table addrconf_vars[18]; ctl_table addrconf_dev[2]; ctl_table addrconf_conf_dir[2]; ctl_table addrconf_proto_dir[2]; @@ -3133,6 +3135,14 @@ static struct addrconf_sysctl_table .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, + { + .ctl_name = NET_IPV6_FORCE_MLD_VERSION, + .procname = "force_mld_version", + .data = &ipv6_devconf.force_mld_version, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, #ifdef CONFIG_IPV6_PRIVACY { .ctl_name = NET_IPV6_USE_TEMPADDR, diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index f05b25c6398a..daad690f9927 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -152,8 +152,10 @@ int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, #define IGMP6_UNSOLICITED_IVAL (10*HZ) #define MLD_QRV_DEFAULT 2 -#define MLD_V1_SEEN(idev) ((idev)->mc_v1_seen && \ - time_before(jiffies, (idev)->mc_v1_seen)) +#define MLD_V1_SEEN(idev) (ipv6_devconf.force_mld_version == 1 || \ + (idev)->cnf.force_mld_version == 1 || \ + ((idev)->mc_v1_seen && \ + time_before(jiffies, (idev)->mc_v1_seen))) #define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value)) #define MLDV2_EXP(thresh, nbmant, nbexp, value) \ -- cgit v1.2.3 From 4d99775fee020b291acb77e91e9f97ffd23a7672 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 17 Feb 2004 03:06:26 -0800 Subject: [IPV4]: Add sophisticated ARP reply control via arp_ignore sysctl. --- Documentation/networking/ip-sysctl.txt | 18 +++++++ include/linux/inetdevice.h | 3 ++ include/linux/sysctl.h | 1 + net/ipv4/arp.c | 44 ++++++++++++++++- net/ipv4/devinet.c | 88 +++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 03213c257b91..6fd189917396 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -530,6 +530,24 @@ arp_announce - INTEGER 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 --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index d2100b938edb..c23cd45da7eb 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -19,6 +19,7 @@ struct ipv4_devconf int tag; int arp_filter; int arp_announce; + int arp_ignore; int medium_id; int no_xfrm; int no_policy; @@ -73,6 +74,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)) struct in_ifaddr { @@ -99,6 +101,7 @@ extern void devinet_init(void); 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 --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 8085f0e55513..d535235ca245 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -363,6 +363,7 @@ enum NET_IPV4_CONF_NOPOLICY=16, 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 --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 5f382df39910..2520e7889bbf 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -379,6 +379,42 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) 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 flowi fl = { .nl_u = { .ip4_u = { .daddr = sip, @@ -789,7 +825,8 @@ int arp_process(struct sk_buff *skb) /* 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; } @@ -804,7 +841,10 @@ int arp_process(struct sk_buff *skb) 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 --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 38ebc705b8b6..e470951d2137 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -809,6 +809,84 @@ out_unlock_inetdev: goto out; } +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 */ @@ -1132,7 +1210,7 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int *name, int nlen, 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]; @@ -1259,6 +1337,14 @@ static struct devinet_sysctl_table { .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = NET_IPV4_CONF_ARP_IGNORE, + .procname = "arp_ignore", + .data = &ipv4_devconf.arp_ignore, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = NET_IPV4_CONF_NOXFRM, .procname = "disable_xfrm", -- cgit v1.2.3