netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net] ipv6: do not overwrite inetpeer metrics prematurely
@ 2014-03-06  9:50 Michal Kubecek
  2014-03-06 19:24 ` David Miller
  0 siblings, 1 reply; 31+ messages in thread
From: Michal Kubecek @ 2014-03-06  9:50 UTC (permalink / raw)
  To: David S. Miller
  Cc: netdev, Alexey Kuznetsov, James Morris, Hideaki YOSHIFUJI,
	Patrick McHardy

If an IPv6 host route with metrics exists, an attempt to add a
new route for the same target with different metrics fails but
rewrites the metrics anyway:

12sp0:~ # ip route add fec0::1 dev eth0 rto_min 1000
12sp0:~ # ip -6 route show
fe80::/64 dev eth0  proto kernel  metric 256
fec0::1 dev eth0  metric 1024  rto_min lock 1s
12sp0:~ # ip route add fec0::1 dev eth0 rto_min 1500
RTNETLINK answers: File exists
12sp0:~ # ip -6 route show
fe80::/64 dev eth0  proto kernel  metric 256
fec0::1 dev eth0  metric 1024  rto_min lock 1.5s

This is caused by all IPv6 host routes using the metrics in
their inetpeer (or the shared default). This also holds for the
new route created in ip6_route_add() which shares the metrics
with the already existing route and thus ip6_route_add()
rewrites the metrics even if the new route ends up not being
used at all.

Allocate separate metrics in ip6_route_add() and copy them into
inetpeer (and update dst->_metrics) just before the new route is
actually inserted in fib6_add_rt2node().

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 net/ipv6/ip6_fib.c | 28 ++++++++++++++++++++++++++++
 net/ipv6/route.c   | 10 +++++-----
 2 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 075602f..3067715 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -638,6 +638,32 @@ static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt)
 	       RTF_GATEWAY;
 }
 
+/*	Called just before inserting a new route into the tree.
+ *	If it is a host route and has its own metrics allocated, copy
+ *	them to its inetpeer (create and/or bind if needed) and free
+ *	the temporary block.
+ */
+
+static void rt6_metrics_to_peer(struct rt6_info *rt)
+{
+	struct inet_peer *peer = rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL;
+	struct dst_entry *dst = &rt->dst;
+	unsigned long old = dst->_metrics;
+
+	if (!(rt->dst.flags & DST_HOST) || dst_metrics_read_only(dst))
+		return;
+	if (peer && dst_metrics_ptr(dst) == peer->metrics)
+		return;
+
+	dst->ops->cow_metrics(dst, old);
+	if (dst->_metrics != old) {
+		u32 *old_p = __DST_METRICS_PTR(old);
+
+		memcpy(dst_metrics_ptr(dst), old_p, RTAX_MAX * sizeof(u32));
+		kfree(old_p);
+	}
+}
+
 /*
  *	Insert routing information in a node.
  */
@@ -752,6 +778,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
 
 add:
 		rt->dst.rt6_next = iter;
+		rt6_metrics_to_peer(rt);
 		*ins = rt;
 		rt->rt6i_node = fn;
 		atomic_inc(&rt->rt6i_ref);
@@ -770,6 +797,7 @@ add:
 			pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
 			return -ENOENT;
 		}
+		rt6_metrics_to_peer(rt);
 		*ins = rt;
 		rt->rt6i_node = fn;
 		rt->dst.rt6_next = iter->dst.rt6_next;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 11dac21..4ba981e 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -324,8 +324,10 @@ static void ip6_dst_destroy(struct dst_entry *dst)
 	struct rt6_info *rt = (struct rt6_info *)dst;
 	struct inet6_dev *idev = rt->rt6i_idev;
 	struct dst_entry *from = dst->from;
+	struct inet_peer *peer = rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL;
 
-	if (!(rt->dst.flags & DST_HOST))
+	if (!(dst->flags & DST_HOST) || !peer ||
+	    DST_METRICS_PTR(dst) != peer->metrics)
 		dst_destroy_metrics_generic(dst);
 
 	if (idev) {
@@ -336,10 +338,8 @@ static void ip6_dst_destroy(struct dst_entry *dst)
 	dst->from = NULL;
 	dst_release(from);
 
-	if (rt6_has_peer(rt)) {
-		struct inet_peer *peer = rt6_peer_ptr(rt);
+	if (peer)
 		inet_putpeer(peer);
-	}
 }
 
 static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
@@ -1546,7 +1546,7 @@ int ip6_route_add(struct fib6_config *cfg)
 	if (rt->rt6i_dst.plen == 128)
 	       rt->dst.flags |= DST_HOST;
 
-	if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
+	if (cfg->fc_mx) {
 		u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
 		if (!metrics) {
 			err = -ENOMEM;
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2014-03-27 19:09 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-06  9:50 [PATCH net] ipv6: do not overwrite inetpeer metrics prematurely Michal Kubecek
2014-03-06 19:24 ` David Miller
2014-03-06 20:06   ` Michal Kubecek
2014-03-07 12:36     ` [PATCH net v2] " Michal Kubecek
2014-03-07 20:52     ` [PATCH net] " David Miller
2014-03-07 21:38       ` Michal Kubecek
2014-03-08  8:34         ` Hannes Frederic Sowa
2014-03-08  8:06       ` Hannes Frederic Sowa
2014-03-10  0:26         ` David Miller
2014-03-10  0:52           ` Hannes Frederic Sowa
2014-03-10  5:03             ` David Miller
2014-03-10  8:15               ` Michal Kubecek
2014-03-10 12:00                 ` Hannes Frederic Sowa
2014-03-10 13:15                   ` Michal Kubecek
2014-03-11  2:38                   ` David Miller
2014-03-11  9:53                     ` Michal Kubecek
2014-03-11 15:08                       ` Michal Kubecek
2014-03-11 15:20                         ` Hannes Frederic Sowa
2014-03-11 15:39                         ` Michal Kubecek
2014-03-25 19:11                           ` Hannes Frederic Sowa
2014-03-26 15:09                             ` Michal Kubecek
2014-03-26 15:42                             ` [PATCH net-next v3] " Michal Kubecek
2014-03-26 15:50                               ` Hannes Frederic Sowa
2014-03-26 15:56                                 ` Michal Kubecek
2014-03-26 16:05                                 ` [PATCH net-next v4] " Michal Kubecek
2014-03-27  5:06                                   ` Hannes Frederic Sowa
2014-03-27  7:43                                     ` Michal Kubecek
2014-03-27 12:04                                     ` [PATCH net-next v5] " Michal Kubecek
2014-03-27 16:30                                       ` Hannes Frederic Sowa
2014-03-27 19:09                                         ` David Miller
2014-03-12 20:54                         ` [PATCH net] " David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).