[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

(usagi-users 04079) [PATCH] Pv6: fix outgoing device selection



Hi all

There is still a problem for user about outgoing interface.

I want to fix the rt6_select().
It should compare the source address specified by user
with the assigned addrss to device like rt6_device_match() too.

I tried to create the patch.
This fix propagates "saddr" to rt6_check_dev() from rt6_select().
And the rt6_check_dev() compares them.

Could you please check this patch ?

Does this fix violate any RFCs ?

I already tested this patch on 2.6.26.


Signed-off-by: Naohiro Ooiwa <nooiwa@xxxxxxxxxxxxxxxx>
---
 net/ipv6/route.c |   49 ++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f4385a6..eca5424 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -321,9 +321,23 @@ static inline void rt6_probe(struct rt6_info *rt)
 /*
  * Default Router Selection (RFC 2461 6.3.6)
  */
-static inline int rt6_check_dev(struct rt6_info *rt, int oif)
+static inline int rt6_check_dev(struct net *net,
+				struct rt6_info *rt,
+				struct in6_addr *saddr,
+				int oif,
+				int strict)
 {
 	struct net_device *dev = rt->rt6i_dev;
+
+	/*
+	 * When user didn't specify outgoing interface (ex.SO_BINDTODEVICE)
+	 * but specify the source address,
+	 * we should complete the source address and
+	 * the assigned address to device.
+	 * This is convinient for user.
+	 */
+	if (!oif && saddr && ipv6_chk_addr(net, saddr, dev, strict))
+		return 3;
 	if (!oif || dev->ifindex == oif)
 		return 2;
 	if ((dev->flags & IFF_LOOPBACK) &&
@@ -355,12 +369,15 @@ static inline int rt6_check_neigh(struct rt6_info *rt)
 	return m;
 }

-static int rt6_score_route(struct rt6_info *rt, int oif,
+static int rt6_score_route(struct net *net,
+			   struct rt6_info *rt,
+			   struct in6_addr *saddr,
+			   int oif,
 			   int strict)
 {
 	int m, n;

-	m = rt6_check_dev(rt, oif);
+	m = rt6_check_dev(net, rt, saddr, oif, strict);
 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
 		return -1;
 #ifdef CONFIG_IPV6_ROUTER_PREF
@@ -372,7 +389,9 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
 	return m;
 }

-static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
+static struct rt6_info *find_match(struct net *net,
+				   struct rt6_info *rt,
+				   struct in6_addr *saddr, int oif, int strict,
 				   int *mpri, struct rt6_info *match)
 {
 	int m;
@@ -380,7 +399,7 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
 	if (rt6_check_expired(rt))
 		goto out;

-	m = rt6_score_route(rt, oif, strict);
+	m = rt6_score_route(net, rt, saddr, oif, strict);
 	if (m < 0)
 		goto out;

@@ -397,7 +416,9 @@ out:
 	return match;
 }

-static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
+static struct rt6_info *find_rr_leaf(struct net *net,
+				     struct fib6_node *fn,
+				     struct in6_addr *saddr,
 				     struct rt6_info *rr_head,
 				     u32 metric, int oif, int strict)
 {
@@ -407,18 +428,21 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
 	match = NULL;
 	for (rt = rr_head; rt && rt->rt6i_metric == metric;
 	     rt = rt->u.dst.rt6_next)
-		match = find_match(rt, oif, strict, &mpri, match);
+		match = find_match(net, rt, saddr, oif, strict, &mpri, match);
 	for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
 	     rt = rt->u.dst.rt6_next)
-		match = find_match(rt, oif, strict, &mpri, match);
+		match = find_match(net, rt, saddr, oif, strict, &mpri, match);

 	return match;
 }

-static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
+static struct rt6_info *rt6_select(struct net *net,
+				   struct fib6_node *fn,
+				   struct in6_addr *saddr,
+				   int oif,
+				   int strict)
 {
 	struct rt6_info *match, *rt0;
-	struct net *net;

 	RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
 		  __func__, fn->leaf, oif);
@@ -427,7 +451,7 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
 	if (!rt0)
 		fn->rr_ptr = rt0 = fn->leaf;

-	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
+	match = find_rr_leaf(net, fn, saddr, rt0, rt0->rt6i_metric, oif, strict);

 	if (!match &&
 	    (strict & RT6_LOOKUP_F_REACHABLE)) {
@@ -444,7 +468,6 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
 	RT6_TRACE("%s() => %p\n",
 		  __func__, match);

-	net = dev_net(rt0->rt6i_dev);
 	return (match ? match : net->ipv6.ip6_null_entry);
 }

@@ -687,7 +710,7 @@ restart_2:
 	fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);

 restart:
-	rt = rt6_select(fn, oif, strict | reachable);
+	rt = rt6_select(net, fn, fl ? &fl->fl6_src : NULL, oif, strict | reachable);

 	BACKTRACK(net, &fl->fl6_src);
 	if (rt == net->ipv6.ip6_null_entry ||
-- 
1.5.4.1