diff mbox series

[net,v2] net: ipv6: Fix a bug in ndisc_send_ns when netdev only has a global address

Message ID 1564454115-66184-1-git-send-email-suyj.fnst@cn.fujitsu.com
State Changes Requested
Delegated to: David Miller
Headers show
Series [net,v2] net: ipv6: Fix a bug in ndisc_send_ns when netdev only has a global address | expand

Commit Message

Su Yanjun July 30, 2019, 2:35 a.m. UTC
When the egress interface does not have a link local address, it can
not communicate with other hosts.

In RFC4861, 7.2.2 says
"If the source address of the packet prompting the solicitation is the
same as one of the addresses assigned to the outgoing interface, that
address SHOULD be placed in the IP Source Address of the outgoing
solicitation.  Otherwise, any one of the addresses assigned to the
interface should be used."

In this patch we try get a global address if we get ll address failed.

Signed-off-by: Su Yanjun <suyj.fnst@cn.fujitsu.com>
---
Changes since V1:
	- Change patch description and code optimization at 
          David Ahern's suggestion
---
 include/net/addrconf.h |  2 ++
 net/ipv6/addrconf.c    | 34 ++++++++++++++++++++++++++++++++++
 net/ipv6/ndisc.c       |  9 ++++++---
 3 files changed, 42 insertions(+), 3 deletions(-)

Comments

David Ahern July 30, 2019, 1:45 p.m. UTC | #1
On 7/29/19 8:35 PM, Su Yanjun wrote:
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 083cc1c..156c027 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -603,11 +603,14 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>  	int inc_opt = dev->addr_len;
>  	int optlen = 0;
>  	struct nd_msg *msg;
> +	u32 banned_flags = IFA_F_TENTATIVE | IFA_F_OPTIMISTIC;
>  
>  	if (!saddr) {

banned_flags should be declared here, under !saddr since that is the
scope of its use.


> -		if (ipv6_get_lladdr(dev, &addr_buf,
> -				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
> -			return;
> +		if (ipv6_get_lladdr(dev, &addr_buf, banned_flags)) {
> +			/* try global address */
> +			if (ipv6_get_addr(dev, &addr_buf, banned_flags))
> +				return;
> +		}
>  		saddr = &addr_buf;
>  	}
>  
>
diff mbox series

Patch

diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index becdad5..ce1561e 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -107,6 +107,8 @@  int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
 		      u32 banned_flags);
 int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
 		    u32 banned_flags);
+int ipv6_get_addr(struct net_device *dev, struct in6_addr *addr,
+		    u32 banned_flags);
 bool inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
 			  bool match_wildcard);
 bool inet_rcv_saddr_any(const struct sock *sk);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 521e320..9467457 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1870,6 +1870,40 @@  int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
 	return err;
 }
 
+int __ipv6_get_addr(struct inet6_dev *idev, struct in6_addr *addr,
+		    u32 banned_flags)
+{
+	struct inet6_ifaddr *ifp;
+	int err = -EADDRNOTAVAIL;
+
+	list_for_each_entry(ifp, &idev->addr_list, if_list) {
+		if (ifp->scope == 0 &&
+		    !(ifp->flags & banned_flags)) {
+			*addr = ifp->addr;
+			err = 0;
+			break;
+		}
+	}
+	return err;
+}
+
+int ipv6_get_addr(struct net_device *dev, struct in6_addr *addr,
+		  u32 banned_flags)
+{
+	struct inet6_dev *idev;
+	int err = -EADDRNOTAVAIL;
+
+	rcu_read_lock();
+	idev = __in6_dev_get(dev);
+	if (idev) {
+		read_lock_bh(&idev->lock);
+		err = __ipv6_get_addr(idev, addr, banned_flags);
+		read_unlock_bh(&idev->lock);
+	}
+	rcu_read_unlock();
+	return err;
+}
+
 static int ipv6_count_addresses(const struct inet6_dev *idev)
 {
 	const struct inet6_ifaddr *ifp;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 083cc1c..156c027 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -603,11 +603,14 @@  void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
 	int inc_opt = dev->addr_len;
 	int optlen = 0;
 	struct nd_msg *msg;
+	u32 banned_flags = IFA_F_TENTATIVE | IFA_F_OPTIMISTIC;
 
 	if (!saddr) {
-		if (ipv6_get_lladdr(dev, &addr_buf,
-				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
-			return;
+		if (ipv6_get_lladdr(dev, &addr_buf, banned_flags)) {
+			/* try global address */
+			if (ipv6_get_addr(dev, &addr_buf, banned_flags))
+				return;
+		}
 		saddr = &addr_buf;
 	}