diff mbox

[net-next] ipv6: Do not iterate over all interfaces when finding source address on specific interface.

Message ID 559F6B2E.8010408@miraclelinux.com
State Superseded, archived
Delegated to: David Miller
Headers show

Commit Message

Hideaki Yoshifuji July 10, 2015, 6:50 a.m. UTC
If outgoing interface is specified and the candidate addresses
are restricted to the outgoing interface, it is enough to iterate
over that given interface only.

Signed-off-by: YOSHIFUJI Hideaki <hideaki.yoshifuji@miraclelinux.com>
---
 net/ipv6/addrconf.c | 201 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 111 insertions(+), 90 deletions(-)

Comments

Erik Kline July 10, 2015, 7:19 a.m. UTC | #1
On 10 July 2015 at 15:50, YOSHIFUJI Hideaki/吉藤英明
<hideaki.yoshifuji@miraclelinux.com> wrote:
> If outgoing interface is specified and the candidate addresses
> are restricted to the outgoing interface, it is enough to iterate
> over that given interface only.
>
> Signed-off-by: YOSHIFUJI Hideaki <hideaki.yoshifuji@miraclelinux.com>
> ---
>  net/ipv6/addrconf.c | 201 +++++++++++++++++++++++++++++-----------------------
>  1 file changed, 111 insertions(+), 90 deletions(-)
>
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 21c2c81..b4c82d8 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -1358,15 +1358,94 @@ out:
>         return ret;
>  }
>
> +static void __ipv6_dev_get_saddr(struct net *net,
> +                                struct ipv6_saddr_dst *dst,
> +                                unsigned int prefs,
> +                                const struct in6_addr *saddr,
> +                                struct inet6_dev *idev,
> +                                struct ipv6_saddr_score *scores)
> +{
> +       struct ipv6_saddr_score *score = &scores[0], *hiscore = &scores[1];
> +
> +       read_lock_bh(&idev->lock);
> +       list_for_each_entry(score->ifa, &idev->addr_list, if_list) {
> +               int i;
> +
> +               /*
> +                * - Tentative Address (RFC2462 section 5.4)
> +                *  - A tentative address is not considered
> +                *    "assigned to an interface" in the traditional
> +                *    sense, unless it is also flagged as optimistic.
> +                * - Candidate Source Address (section 4)
> +                *  - In any case, anycast addresses, multicast
> +                *    addresses, and the unspecified address MUST
> +                *    NOT be included in a candidate set.
> +                */
> +               if ((score->ifa->flags & IFA_F_TENTATIVE) &&
> +                   (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
> +                       continue;
> +
> +               score->addr_type = __ipv6_addr_type(&score->ifa->addr);
> +
> +               if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
> +                            score->addr_type & IPV6_ADDR_MULTICAST)) {
> +                       net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
> +                                           idev->dev->name);
> +                       continue;
> +               }
> +
> +               score->rule = -1;
> +               bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
> +
> +               for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
> +                       int minihiscore, miniscore;
> +
> +                       minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i);
> +                       miniscore = ipv6_get_saddr_eval(net, score, dst, i);
> +
> +                       if (minihiscore > miniscore) {
> +                               if (i == IPV6_SADDR_RULE_SCOPE &&
> +                                   score->scopedist > 0) {
> +                                       /*
> +                                        * special case:
> +                                        * each remaining entry
> +                                        * has too small (not enough)
> +                                        * scope, because ifa entries
> +                                        * are sorted by their scope
> +                                        * values.
> +                                        */
> +                                       goto out;
> +                               }
> +                               break;
> +                       } else if (minihiscore < miniscore) {
> +                               if (hiscore->ifa)
> +                                       in6_ifa_put(hiscore->ifa);
> +
> +                               in6_ifa_hold(score->ifa);
> +
> +                               swap(hiscore, score);
> +
> +                               /* restore our iterator */
> +                               score->ifa = hiscore->ifa;
> +
> +                               break;
> +                       }
> +               }
> +       }
> +out:
> +       read_unlock_bh(&idev->lock);
> +}
> +
>  int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
>                        const struct in6_addr *daddr, unsigned int prefs,
>                        struct in6_addr *saddr)
>  {
> -       struct ipv6_saddr_score scores[2],
> -                               *score = &scores[0], *hiscore = &scores[1];
> +       struct ipv6_saddr_score scores[2], *hiscore = &scores[1];
>         struct ipv6_saddr_dst dst;
> +       struct inet6_dev *idev;
>         struct net_device *dev;
>         int dst_type;
> +       bool use_oif_addr = false;
>
>         dst_type = __ipv6_addr_type(daddr);
>         dst.addr = daddr;
> @@ -1380,97 +1459,39 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
>
>         rcu_read_lock();
>
> -       for_each_netdev_rcu(net, dev) {
> -               struct inet6_dev *idev;
> -
> -               /* Candidate Source Address (section 4)
> -                *  - multicast and link-local destination address,
> -                *    the set of candidate source address MUST only
> -                *    include addresses assigned to interfaces
> -                *    belonging to the same link as the outgoing
> -                *    interface.
> -                * (- For site-local destination addresses, the
> -                *    set of candidate source addresses MUST only
> -                *    include addresses assigned to interfaces
> -                *    belonging to the same site as the outgoing
> -                *    interface.)
> -                */
> -               if (((dst_type & IPV6_ADDR_MULTICAST) ||
> -                    dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
> -                   dst.ifindex && dev->ifindex != dst.ifindex)
> -                       continue;
> -
> -               idev = __in6_dev_get(dev);
> -               if (!idev)
> -                       continue;
> -
> -               read_lock_bh(&idev->lock);
> -               list_for_each_entry(score->ifa, &idev->addr_list, if_list) {
> -                       int i;
> -
> -                       /*
> -                        * - Tentative Address (RFC2462 section 5.4)
> -                        *  - A tentative address is not considered
> -                        *    "assigned to an interface" in the traditional
> -                        *    sense, unless it is also flagged as optimistic.
> -                        * - Candidate Source Address (section 4)
> -                        *  - In any case, anycast addresses, multicast
> -                        *    addresses, and the unspecified address MUST
> -                        *    NOT be included in a candidate set.
> -                        */
> -                       if ((score->ifa->flags & IFA_F_TENTATIVE) &&
> -                           (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
> -                               continue;
> -
> -                       score->addr_type = __ipv6_addr_type(&score->ifa->addr);
> +       /* Candidate Source Address (section 4)
> +        *  - multicast and link-local destination address,
> +        *    the set of candidate source address MUST only
> +        *    include addresses assigned to interfaces
> +        *    belonging to the same link as the outgoing
> +        *    interface.
> +        * (- For site-local destination addresses, the
> +        *    set of candidate source addresses MUST only
> +        *    include addresses assigned to interfaces
> +        *    belonging to the same site as the outgoing
> +        *    interface.)
> +        *  - "It is RECOMMENDED that the candidate source addresses
> +        *    be the set of unicast addresses assigned to the
> +        *    interface that will be used to send to the destination
> +        *    (the 'outgoing' interface)." (RFC 6724)
> +        */
> +       if (dst_dev) {
> +               if ((dst_type & IPV6_ADDR_MULTICAST) ||
> +                   dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) {
> +                       idev = __in6_dev_get(dst_dev);
> +                       use_oif_addr = true;
> +               }
> +       }
>
> -                       if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
> -                                    score->addr_type & IPV6_ADDR_MULTICAST)) {
> -                               net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
> -                                                   dev->name);
> +       if (use_oif_addr) {
> +               __ipv6_dev_get_saddr(net, &dst, prefs, saddr, idev, scores);
> +       } else {
> +               for_each_netdev_rcu(net, dev) {
> +                       idev = __in6_dev_get(dev);
> +                       if (!idev)
>                                 continue;
> -                       }
> -
> -                       score->rule = -1;
> -                       bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
> -
> -                       for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
> -                               int minihiscore, miniscore;
> -
> -                               minihiscore = ipv6_get_saddr_eval(net, hiscore, &dst, i);
> -                               miniscore = ipv6_get_saddr_eval(net, score, &dst, i);
> -
> -                               if (minihiscore > miniscore) {
> -                                       if (i == IPV6_SADDR_RULE_SCOPE &&
> -                                           score->scopedist > 0) {
> -                                               /*
> -                                                * special case:
> -                                                * each remaining entry
> -                                                * has too small (not enough)
> -                                                * scope, because ifa entries
> -                                                * are sorted by their scope
> -                                                * values.
> -                                                */
> -                                               goto try_nextdev;
> -                                       }
> -                                       break;
> -                               } else if (minihiscore < miniscore) {
> -                                       if (hiscore->ifa)
> -                                               in6_ifa_put(hiscore->ifa);
> -
> -                                       in6_ifa_hold(score->ifa);
> -
> -                                       swap(hiscore, score);
> -
> -                                       /* restore our iterator */
> -                                       score->ifa = hiscore->ifa;
> -
> -                                       break;
> -                               }
> -                       }
> +                       __ipv6_dev_get_saddr(net, &dst, prefs, saddr, idev, scores);
>                 }
> -try_nextdev:
> -               read_unlock_bh(&idev->lock);
>         }
>         rcu_read_unlock();
>
> --
> 1.9.1
>

LGTM, and thanks again.

Acked-by: Erik Kline <ek@google.com>
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hideaki Yoshifuji July 10, 2015, 7:31 a.m. UTC | #2
Hi,

Erik Kline wrote:

>> +       /* Candidate Source Address (section 4)
>> +        *  - multicast and link-local destination address,
>> +        *    the set of candidate source address MUST only
>> +        *    include addresses assigned to interfaces
>> +        *    belonging to the same link as the outgoing
>> +        *    interface.
>> +        * (- For site-local destination addresses, the
>> +        *    set of candidate source addresses MUST only
>> +        *    include addresses assigned to interfaces
>> +        *    belonging to the same site as the outgoing
>> +        *    interface.)
>> +        *  - "It is RECOMMENDED that the candidate source addresses
>> +        *    be the set of unicast addresses assigned to the
>> +        *    interface that will be used to send to the destination
>> +        *    (the 'outgoing' interface)." (RFC 6724)
>> +        */

Sorry, I forgot to remove 3rd one, which should be removed for now; I'll
resubmit.
diff mbox

Patch

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 21c2c81..b4c82d8 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1358,15 +1358,94 @@  out:
 	return ret;
 }
 
+static void __ipv6_dev_get_saddr(struct net *net,
+				 struct ipv6_saddr_dst *dst,
+				 unsigned int prefs,
+				 const struct in6_addr *saddr,
+				 struct inet6_dev *idev,
+				 struct ipv6_saddr_score *scores)
+{
+	struct ipv6_saddr_score *score = &scores[0], *hiscore = &scores[1];
+
+	read_lock_bh(&idev->lock);
+	list_for_each_entry(score->ifa, &idev->addr_list, if_list) {
+		int i;
+
+		/*
+		 * - Tentative Address (RFC2462 section 5.4)
+		 *  - A tentative address is not considered
+		 *    "assigned to an interface" in the traditional
+		 *    sense, unless it is also flagged as optimistic.
+		 * - Candidate Source Address (section 4)
+		 *  - In any case, anycast addresses, multicast
+		 *    addresses, and the unspecified address MUST
+		 *    NOT be included in a candidate set.
+		 */
+		if ((score->ifa->flags & IFA_F_TENTATIVE) &&
+		    (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
+			continue;
+
+		score->addr_type = __ipv6_addr_type(&score->ifa->addr);
+
+		if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
+			     score->addr_type & IPV6_ADDR_MULTICAST)) {
+			net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
+					    idev->dev->name);
+			continue;
+		}
+
+		score->rule = -1;
+		bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
+
+		for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
+			int minihiscore, miniscore;
+
+			minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i);
+			miniscore = ipv6_get_saddr_eval(net, score, dst, i);
+
+			if (minihiscore > miniscore) {
+				if (i == IPV6_SADDR_RULE_SCOPE &&
+				    score->scopedist > 0) {
+					/*
+					 * special case:
+					 * each remaining entry
+					 * has too small (not enough)
+					 * scope, because ifa entries
+					 * are sorted by their scope
+					 * values.
+					 */
+					goto out;
+				}
+				break;
+			} else if (minihiscore < miniscore) {
+				if (hiscore->ifa)
+					in6_ifa_put(hiscore->ifa);
+
+				in6_ifa_hold(score->ifa);
+
+				swap(hiscore, score);
+
+				/* restore our iterator */
+				score->ifa = hiscore->ifa;
+
+				break;
+			}
+		}
+	}
+out:
+	read_unlock_bh(&idev->lock);
+}
+
 int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
 		       const struct in6_addr *daddr, unsigned int prefs,
 		       struct in6_addr *saddr)
 {
-	struct ipv6_saddr_score scores[2],
-				*score = &scores[0], *hiscore = &scores[1];
+	struct ipv6_saddr_score scores[2], *hiscore = &scores[1];
 	struct ipv6_saddr_dst dst;
+	struct inet6_dev *idev;
 	struct net_device *dev;
 	int dst_type;
+	bool use_oif_addr = false;
 
 	dst_type = __ipv6_addr_type(daddr);
 	dst.addr = daddr;
@@ -1380,97 +1459,39 @@  int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
 
 	rcu_read_lock();
 
-	for_each_netdev_rcu(net, dev) {
-		struct inet6_dev *idev;
-
-		/* Candidate Source Address (section 4)
-		 *  - multicast and link-local destination address,
-		 *    the set of candidate source address MUST only
-		 *    include addresses assigned to interfaces
-		 *    belonging to the same link as the outgoing
-		 *    interface.
-		 * (- For site-local destination addresses, the
-		 *    set of candidate source addresses MUST only
-		 *    include addresses assigned to interfaces
-		 *    belonging to the same site as the outgoing
-		 *    interface.)
-		 */
-		if (((dst_type & IPV6_ADDR_MULTICAST) ||
-		     dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
-		    dst.ifindex && dev->ifindex != dst.ifindex)
-			continue;
-
-		idev = __in6_dev_get(dev);
-		if (!idev)
-			continue;
-
-		read_lock_bh(&idev->lock);
-		list_for_each_entry(score->ifa, &idev->addr_list, if_list) {
-			int i;
-
-			/*
-			 * - Tentative Address (RFC2462 section 5.4)
-			 *  - A tentative address is not considered
-			 *    "assigned to an interface" in the traditional
-			 *    sense, unless it is also flagged as optimistic.
-			 * - Candidate Source Address (section 4)
-			 *  - In any case, anycast addresses, multicast
-			 *    addresses, and the unspecified address MUST
-			 *    NOT be included in a candidate set.
-			 */
-			if ((score->ifa->flags & IFA_F_TENTATIVE) &&
-			    (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
-				continue;
-
-			score->addr_type = __ipv6_addr_type(&score->ifa->addr);
+	/* Candidate Source Address (section 4)
+	 *  - multicast and link-local destination address,
+	 *    the set of candidate source address MUST only
+	 *    include addresses assigned to interfaces
+	 *    belonging to the same link as the outgoing
+	 *    interface.
+	 * (- For site-local destination addresses, the
+	 *    set of candidate source addresses MUST only
+	 *    include addresses assigned to interfaces
+	 *    belonging to the same site as the outgoing
+	 *    interface.)
+	 *  - "It is RECOMMENDED that the candidate source addresses
+	 *    be the set of unicast addresses assigned to the
+	 *    interface that will be used to send to the destination
+	 *    (the 'outgoing' interface)." (RFC 6724)
+	 */
+	if (dst_dev) {
+		if ((dst_type & IPV6_ADDR_MULTICAST) ||
+		    dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) {
+			idev = __in6_dev_get(dst_dev);
+			use_oif_addr = true;
+		}
+	}
 
-			if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
-				     score->addr_type & IPV6_ADDR_MULTICAST)) {
-				net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
-						    dev->name);
+	if (use_oif_addr) {
+		__ipv6_dev_get_saddr(net, &dst, prefs, saddr, idev, scores);
+	} else {
+		for_each_netdev_rcu(net, dev) {
+			idev = __in6_dev_get(dev);
+			if (!idev)
 				continue;
-			}
-
-			score->rule = -1;
-			bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
-
-			for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
-				int minihiscore, miniscore;
-
-				minihiscore = ipv6_get_saddr_eval(net, hiscore, &dst, i);
-				miniscore = ipv6_get_saddr_eval(net, score, &dst, i);
-
-				if (minihiscore > miniscore) {
-					if (i == IPV6_SADDR_RULE_SCOPE &&
-					    score->scopedist > 0) {
-						/*
-						 * special case:
-						 * each remaining entry
-						 * has too small (not enough)
-						 * scope, because ifa entries
-						 * are sorted by their scope
-						 * values.
-						 */
-						goto try_nextdev;
-					}
-					break;
-				} else if (minihiscore < miniscore) {
-					if (hiscore->ifa)
-						in6_ifa_put(hiscore->ifa);
-
-					in6_ifa_hold(score->ifa);
-
-					swap(hiscore, score);
-
-					/* restore our iterator */
-					score->ifa = hiscore->ifa;
-
-					break;
-				}
-			}
+			__ipv6_dev_get_saddr(net, &dst, prefs, saddr, idev, scores);
 		}
-try_nextdev:
-		read_unlock_bh(&idev->lock);
 	}
 	rcu_read_unlock();