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: YOSHIFUJI Hideaki <hideaki.yoshifuji@miraclelinux.com>
Acked-by: Erik Kline <ek@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
YOSHIFUJI Hideaki/吉藤英明 2015-07-10 16:58:31 +09:00 committed by David S. Miller
parent 6979b9cf58
commit 9131f3de24
1 changed files with 109 additions and 92 deletions

View File

@ -1358,51 +1358,14 @@ out:
return ret;
}
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)
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 scores[2],
*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;
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) {
@ -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 ||
score->addr_type & IPV6_ADDR_MULTICAST)) {
net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
dev->name);
idev->dev->name);
continue;
}
@ -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++) {
int minihiscore, miniscore;
minihiscore = ipv6_get_saddr_eval(net, hiscore, &dst, i);
miniscore = ipv6_get_saddr_eval(net, score, &dst, i);
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 &&
@ -1451,7 +1414,7 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
* are sorted by their scope
* values.
*/
goto try_nextdev;
goto out;
}
break;
} else if (minihiscore < miniscore) {
@ -1469,9 +1432,63 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
}
}
}
try_nextdev:
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], *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();
if (!hiscore->ifa)