Commit 9131f3de authored by YOSHIFUJI Hideaki/吉藤英明's avatar YOSHIFUJI Hideaki/吉藤英明 Committed by David S. Miller

ipv6: Do not iterate over all interfaces when finding source address on specific interface.

If outgoing interface is specified and the candidate address is
restricted to the outgoing interface, it is enough to iterate
over that given interface only.
Signed-off-by: default avatarYOSHIFUJI Hideaki <hideaki.yoshifuji@miraclelinux.com>
Acked-by: default avatarErik Kline <ek@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6979b9cf
...@@ -1358,51 +1358,14 @@ static int ipv6_get_saddr_eval(struct net *net, ...@@ -1358,51 +1358,14 @@ static int ipv6_get_saddr_eval(struct net *net,
return ret; return ret;
} }
int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, static void __ipv6_dev_get_saddr(struct net *net,
const struct in6_addr *daddr, unsigned int prefs, struct ipv6_saddr_dst *dst,
struct in6_addr *saddr) unsigned int prefs,
const struct in6_addr *saddr,
struct inet6_dev *idev,
struct ipv6_saddr_score *scores)
{ {
struct ipv6_saddr_score scores[2], struct ipv6_saddr_score *score = &scores[0], *hiscore = &scores[1];
*score = &scores[0], *hiscore = &scores[1];
struct ipv6_saddr_dst dst;
struct net_device *dev;
int dst_type;
dst_type = __ipv6_addr_type(daddr);
dst.addr = daddr;
dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
dst.scope = __ipv6_addr_src_scope(dst_type);
dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex);
dst.prefs = prefs;
hiscore->rule = -1;
hiscore->ifa = NULL;
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); read_lock_bh(&idev->lock);
list_for_each_entry(score->ifa, &idev->addr_list, if_list) { list_for_each_entry(score->ifa, &idev->addr_list, if_list) {
...@@ -1427,7 +1390,7 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, ...@@ -1427,7 +1390,7 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
if (unlikely(score->addr_type == IPV6_ADDR_ANY || if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
score->addr_type & IPV6_ADDR_MULTICAST)) { score->addr_type & IPV6_ADDR_MULTICAST)) {
net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s", net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
dev->name); idev->dev->name);
continue; continue;
} }
...@@ -1437,8 +1400,8 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, ...@@ -1437,8 +1400,8 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) { for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
int minihiscore, miniscore; int minihiscore, miniscore;
minihiscore = ipv6_get_saddr_eval(net, hiscore, &dst, i); minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i);
miniscore = ipv6_get_saddr_eval(net, score, &dst, i); miniscore = ipv6_get_saddr_eval(net, score, dst, i);
if (minihiscore > miniscore) { if (minihiscore > miniscore) {
if (i == IPV6_SADDR_RULE_SCOPE && if (i == IPV6_SADDR_RULE_SCOPE &&
...@@ -1451,7 +1414,7 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, ...@@ -1451,7 +1414,7 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
* are sorted by their scope * are sorted by their scope
* values. * values.
*/ */
goto try_nextdev; goto out;
} }
break; break;
} else if (minihiscore < miniscore) { } else if (minihiscore < miniscore) {
...@@ -1469,8 +1432,62 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, ...@@ -1469,8 +1432,62 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
} }
} }
} }
try_nextdev: out:
read_unlock_bh(&idev->lock); 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], *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;
dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
dst.scope = __ipv6_addr_src_scope(dst_type);
dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex);
dst.prefs = prefs;
hiscore->rule = -1;
hiscore->ifa = NULL;
rcu_read_lock();
/* 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_dev) {
if ((dst_type & IPV6_ADDR_MULTICAST) ||
dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL) {
idev = __in6_dev_get(dst_dev);
use_oif_addr = true;
}
}
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;
__ipv6_dev_get_saddr(net, &dst, prefs, saddr, idev, scores);
}
} }
rcu_read_unlock(); rcu_read_unlock();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment