aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/route.c
diff options
context:
space:
mode:
authorMichal Kubeček <mkubecek@suse.cz>2014-03-27 08:04:08 -0400
committerDavid S. Miller <davem@davemloft.net>2014-03-27 15:09:07 -0400
commite5fd387ad5b30ca3971fbccb0735c843cdebf967 (patch)
treed0fb62b5c313a0d6944692dced80918ebf6b0b90 /net/ipv6/route.c
parent4ec54f95736f2d90e4d9e4fcc75cf1228f0f3ede (diff)
ipv6: do not overwrite inetpeer metrics prematurely
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. Another problem is that old metrics in inetpeer can reappear unexpectedly for a new route, e.g. 12sp0:~ # ip route add fec0::1 dev eth0 rto_min 1000 12sp0:~ # ip route del fec0::1 12sp0:~ # ip route add fec0::1 dev eth0 12sp0:~ # ip route change fec0::1 dev eth0 hoplimit 10 12sp0:~ # ip -6 route show fe80::/64 dev eth0 proto kernel metric 256 fec0::1 dev eth0 metric 1024 hoplimit 10 rto_min lock 1s Resolve the first problem by moving the setting of metrics down into fib6_add_rt2node() to the point we are sure we are inserting the new route into the tree. Second problem is addressed by introducing new flag DST_METRICS_FORCE_OVERWRITE which is set for a new host route in ip6_route_add() and makes ipv6_cow_metrics() always overwrite the metrics in inetpeer (even if they are not "new"); it is reset after that. v5: use a flag in _metrics member rather than one in flags v4: fix a typo making a condition always true (thanks to Hannes Frederic Sowa) v3: rewritten based on David Miller's idea to move setting the metrics (and allocation in non-host case) down to the point we already know the route is to be inserted. Also rebased to net-next as it is quite late in the cycle. Signed-off-by: Michal Kubecek <mkubecek@suse.cz> Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r--net/ipv6/route.c44
1 files changed, 11 insertions, 33 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fba54a407bb2..b93ae6a6a31c 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -149,7 +149,8 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
149 unsigned long prev, new; 149 unsigned long prev, new;
150 150
151 p = peer->metrics; 151 p = peer->metrics;
152 if (inet_metrics_new(peer)) 152 if (inet_metrics_new(peer) ||
153 (old & DST_METRICS_FORCE_OVERWRITE))
153 memcpy(p, old_p, sizeof(u32) * RTAX_MAX); 154 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
154 155
155 new = (unsigned long) p; 156 new = (unsigned long) p;
@@ -857,14 +858,15 @@ EXPORT_SYMBOL(rt6_lookup);
857 be destroyed. 858 be destroyed.
858 */ 859 */
859 860
860static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 861static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
862 struct nlattr *mx, int mx_len)
861{ 863{
862 int err; 864 int err;
863 struct fib6_table *table; 865 struct fib6_table *table;
864 866
865 table = rt->rt6i_table; 867 table = rt->rt6i_table;
866 write_lock_bh(&table->tb6_lock); 868 write_lock_bh(&table->tb6_lock);
867 err = fib6_add(&table->tb6_root, rt, info); 869 err = fib6_add(&table->tb6_root, rt, info, mx, mx_len);
868 write_unlock_bh(&table->tb6_lock); 870 write_unlock_bh(&table->tb6_lock);
869 871
870 return err; 872 return err;
@@ -875,7 +877,7 @@ int ip6_ins_rt(struct rt6_info *rt)
875 struct nl_info info = { 877 struct nl_info info = {
876 .nl_net = dev_net(rt->dst.dev), 878 .nl_net = dev_net(rt->dst.dev),
877 }; 879 };
878 return __ip6_ins_rt(rt, &info); 880 return __ip6_ins_rt(rt, &info, NULL, 0);
879} 881}
880 882
881static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 883static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
@@ -1543,17 +1545,11 @@ int ip6_route_add(struct fib6_config *cfg)
1543 1545
1544 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 1546 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1545 rt->rt6i_dst.plen = cfg->fc_dst_len; 1547 rt->rt6i_dst.plen = cfg->fc_dst_len;
1546 if (rt->rt6i_dst.plen == 128) 1548 if (rt->rt6i_dst.plen == 128) {
1547 rt->dst.flags |= DST_HOST; 1549 rt->dst.flags |= DST_HOST;
1548 1550 dst_metrics_set_force_overwrite(&rt->dst);
1549 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1550 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1551 if (!metrics) {
1552 err = -ENOMEM;
1553 goto out;
1554 }
1555 dst_init_metrics(&rt->dst, metrics, 0);
1556 } 1551 }
1552
1557#ifdef CONFIG_IPV6_SUBTREES 1553#ifdef CONFIG_IPV6_SUBTREES
1558 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 1554 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1559 rt->rt6i_src.plen = cfg->fc_src_len; 1555 rt->rt6i_src.plen = cfg->fc_src_len;
@@ -1672,31 +1668,13 @@ int ip6_route_add(struct fib6_config *cfg)
1672 rt->rt6i_flags = cfg->fc_flags; 1668 rt->rt6i_flags = cfg->fc_flags;
1673 1669
1674install_route: 1670install_route:
1675 if (cfg->fc_mx) {
1676 struct nlattr *nla;
1677 int remaining;
1678
1679 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1680 int type = nla_type(nla);
1681
1682 if (type) {
1683 if (type > RTAX_MAX) {
1684 err = -EINVAL;
1685 goto out;
1686 }
1687
1688 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
1689 }
1690 }
1691 }
1692
1693 rt->dst.dev = dev; 1671 rt->dst.dev = dev;
1694 rt->rt6i_idev = idev; 1672 rt->rt6i_idev = idev;
1695 rt->rt6i_table = table; 1673 rt->rt6i_table = table;
1696 1674
1697 cfg->fc_nlinfo.nl_net = dev_net(dev); 1675 cfg->fc_nlinfo.nl_net = dev_net(dev);
1698 1676
1699 return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 1677 return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len);
1700 1678
1701out: 1679out:
1702 if (dev) 1680 if (dev)