diff options
Diffstat (limited to 'net/ipv6/route.c')
| -rw-r--r-- | net/ipv6/route.c | 46 |
1 files changed, 26 insertions, 20 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 40b225f87d5e..ce15dc4ccbfa 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
| @@ -1274,18 +1274,29 @@ static DEFINE_SPINLOCK(rt6_exception_lock); | |||
| 1274 | static void rt6_remove_exception(struct rt6_exception_bucket *bucket, | 1274 | static void rt6_remove_exception(struct rt6_exception_bucket *bucket, |
| 1275 | struct rt6_exception *rt6_ex) | 1275 | struct rt6_exception *rt6_ex) |
| 1276 | { | 1276 | { |
| 1277 | struct fib6_info *from; | ||
| 1277 | struct net *net; | 1278 | struct net *net; |
| 1278 | 1279 | ||
| 1279 | if (!bucket || !rt6_ex) | 1280 | if (!bucket || !rt6_ex) |
| 1280 | return; | 1281 | return; |
| 1281 | 1282 | ||
| 1282 | net = dev_net(rt6_ex->rt6i->dst.dev); | 1283 | net = dev_net(rt6_ex->rt6i->dst.dev); |
| 1284 | net->ipv6.rt6_stats->fib_rt_cache--; | ||
| 1285 | |||
| 1286 | /* purge completely the exception to allow releasing the held resources: | ||
| 1287 | * some [sk] cache may keep the dst around for unlimited time | ||
| 1288 | */ | ||
| 1289 | from = rcu_dereference_protected(rt6_ex->rt6i->from, | ||
| 1290 | lockdep_is_held(&rt6_exception_lock)); | ||
| 1291 | rcu_assign_pointer(rt6_ex->rt6i->from, NULL); | ||
| 1292 | fib6_info_release(from); | ||
| 1293 | dst_dev_put(&rt6_ex->rt6i->dst); | ||
| 1294 | |||
| 1283 | hlist_del_rcu(&rt6_ex->hlist); | 1295 | hlist_del_rcu(&rt6_ex->hlist); |
| 1284 | dst_release(&rt6_ex->rt6i->dst); | 1296 | dst_release(&rt6_ex->rt6i->dst); |
| 1285 | kfree_rcu(rt6_ex, rcu); | 1297 | kfree_rcu(rt6_ex, rcu); |
| 1286 | WARN_ON_ONCE(!bucket->depth); | 1298 | WARN_ON_ONCE(!bucket->depth); |
| 1287 | bucket->depth--; | 1299 | bucket->depth--; |
| 1288 | net->ipv6.rt6_stats->fib_rt_cache--; | ||
| 1289 | } | 1300 | } |
| 1290 | 1301 | ||
| 1291 | /* Remove oldest rt6_ex in bucket and free the memory | 1302 | /* Remove oldest rt6_ex in bucket and free the memory |
| @@ -1599,15 +1610,15 @@ static int rt6_remove_exception_rt(struct rt6_info *rt) | |||
| 1599 | static void rt6_update_exception_stamp_rt(struct rt6_info *rt) | 1610 | static void rt6_update_exception_stamp_rt(struct rt6_info *rt) |
| 1600 | { | 1611 | { |
| 1601 | struct rt6_exception_bucket *bucket; | 1612 | struct rt6_exception_bucket *bucket; |
| 1602 | struct fib6_info *from = rt->from; | ||
| 1603 | struct in6_addr *src_key = NULL; | 1613 | struct in6_addr *src_key = NULL; |
| 1604 | struct rt6_exception *rt6_ex; | 1614 | struct rt6_exception *rt6_ex; |
| 1605 | 1615 | struct fib6_info *from; | |
| 1606 | if (!from || | ||
| 1607 | !(rt->rt6i_flags & RTF_CACHE)) | ||
| 1608 | return; | ||
| 1609 | 1616 | ||
| 1610 | rcu_read_lock(); | 1617 | rcu_read_lock(); |
| 1618 | from = rcu_dereference(rt->from); | ||
| 1619 | if (!from || !(rt->rt6i_flags & RTF_CACHE)) | ||
| 1620 | goto unlock; | ||
| 1621 | |||
| 1611 | bucket = rcu_dereference(from->rt6i_exception_bucket); | 1622 | bucket = rcu_dereference(from->rt6i_exception_bucket); |
| 1612 | 1623 | ||
| 1613 | #ifdef CONFIG_IPV6_SUBTREES | 1624 | #ifdef CONFIG_IPV6_SUBTREES |
| @@ -1626,6 +1637,7 @@ static void rt6_update_exception_stamp_rt(struct rt6_info *rt) | |||
| 1626 | if (rt6_ex) | 1637 | if (rt6_ex) |
| 1627 | rt6_ex->stamp = jiffies; | 1638 | rt6_ex->stamp = jiffies; |
| 1628 | 1639 | ||
| 1640 | unlock: | ||
| 1629 | rcu_read_unlock(); | 1641 | rcu_read_unlock(); |
| 1630 | } | 1642 | } |
| 1631 | 1643 | ||
| @@ -2742,20 +2754,24 @@ static int ip6_route_check_nh_onlink(struct net *net, | |||
| 2742 | u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN; | 2754 | u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN; |
| 2743 | const struct in6_addr *gw_addr = &cfg->fc_gateway; | 2755 | const struct in6_addr *gw_addr = &cfg->fc_gateway; |
| 2744 | u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT; | 2756 | u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT; |
| 2757 | struct fib6_info *from; | ||
| 2745 | struct rt6_info *grt; | 2758 | struct rt6_info *grt; |
| 2746 | int err; | 2759 | int err; |
| 2747 | 2760 | ||
| 2748 | err = 0; | 2761 | err = 0; |
| 2749 | grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0); | 2762 | grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0); |
| 2750 | if (grt) { | 2763 | if (grt) { |
| 2764 | rcu_read_lock(); | ||
| 2765 | from = rcu_dereference(grt->from); | ||
| 2751 | if (!grt->dst.error && | 2766 | if (!grt->dst.error && |
| 2752 | /* ignore match if it is the default route */ | 2767 | /* ignore match if it is the default route */ |
| 2753 | grt->from && !ipv6_addr_any(&grt->from->fib6_dst.addr) && | 2768 | from && !ipv6_addr_any(&from->fib6_dst.addr) && |
| 2754 | (grt->rt6i_flags & flags || dev != grt->dst.dev)) { | 2769 | (grt->rt6i_flags & flags || dev != grt->dst.dev)) { |
| 2755 | NL_SET_ERR_MSG(extack, | 2770 | NL_SET_ERR_MSG(extack, |
| 2756 | "Nexthop has invalid gateway or device mismatch"); | 2771 | "Nexthop has invalid gateway or device mismatch"); |
| 2757 | err = -EINVAL; | 2772 | err = -EINVAL; |
| 2758 | } | 2773 | } |
| 2774 | rcu_read_unlock(); | ||
| 2759 | 2775 | ||
| 2760 | ip6_rt_put(grt); | 2776 | ip6_rt_put(grt); |
| 2761 | } | 2777 | } |
| @@ -4251,17 +4267,6 @@ struct rt6_nh { | |||
| 4251 | struct list_head next; | 4267 | struct list_head next; |
| 4252 | }; | 4268 | }; |
| 4253 | 4269 | ||
| 4254 | static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) | ||
| 4255 | { | ||
| 4256 | struct rt6_nh *nh; | ||
| 4257 | |||
| 4258 | list_for_each_entry(nh, rt6_nh_list, next) { | ||
| 4259 | pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6c nexthop %pI6c ifi %d\n", | ||
| 4260 | &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, | ||
| 4261 | nh->r_cfg.fc_ifindex); | ||
| 4262 | } | ||
| 4263 | } | ||
| 4264 | |||
| 4265 | static int ip6_route_info_append(struct net *net, | 4270 | static int ip6_route_info_append(struct net *net, |
| 4266 | struct list_head *rt6_nh_list, | 4271 | struct list_head *rt6_nh_list, |
| 4267 | struct fib6_info *rt, | 4272 | struct fib6_info *rt, |
| @@ -4407,7 +4412,8 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, | |||
| 4407 | nh->fib6_info = NULL; | 4412 | nh->fib6_info = NULL; |
| 4408 | if (err) { | 4413 | if (err) { |
| 4409 | if (replace && nhn) | 4414 | if (replace && nhn) |
| 4410 | ip6_print_replace_route_err(&rt6_nh_list); | 4415 | NL_SET_ERR_MSG_MOD(extack, |
| 4416 | "multipath route replace failed (check consistency of installed routes)"); | ||
| 4411 | err_nh = nh; | 4417 | err_nh = nh; |
| 4412 | goto add_errout; | 4418 | goto add_errout; |
| 4413 | } | 4419 | } |
| @@ -4659,7 +4665,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb, | |||
| 4659 | table = rt->fib6_table->tb6_id; | 4665 | table = rt->fib6_table->tb6_id; |
| 4660 | else | 4666 | else |
| 4661 | table = RT6_TABLE_UNSPEC; | 4667 | table = RT6_TABLE_UNSPEC; |
| 4662 | rtm->rtm_table = table; | 4668 | rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT; |
| 4663 | if (nla_put_u32(skb, RTA_TABLE, table)) | 4669 | if (nla_put_u32(skb, RTA_TABLE, table)) |
| 4664 | goto nla_put_failure; | 4670 | goto nla_put_failure; |
| 4665 | 4671 | ||
