diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 74 |
1 files changed, 52 insertions, 22 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index bdbc38e8bf29..947ed1ded026 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -102,11 +102,13 @@ static int rt6_score_route(struct rt6_info *rt, int oif, int strict); | |||
102 | #ifdef CONFIG_IPV6_ROUTE_INFO | 102 | #ifdef CONFIG_IPV6_ROUTE_INFO |
103 | static struct rt6_info *rt6_add_route_info(struct net *net, | 103 | static struct rt6_info *rt6_add_route_info(struct net *net, |
104 | const struct in6_addr *prefix, int prefixlen, | 104 | const struct in6_addr *prefix, int prefixlen, |
105 | const struct in6_addr *gwaddr, int ifindex, | 105 | const struct in6_addr *gwaddr, |
106 | struct net_device *dev, | ||
106 | unsigned int pref); | 107 | unsigned int pref); |
107 | static struct rt6_info *rt6_get_route_info(struct net *net, | 108 | static struct rt6_info *rt6_get_route_info(struct net *net, |
108 | const struct in6_addr *prefix, int prefixlen, | 109 | const struct in6_addr *prefix, int prefixlen, |
109 | const struct in6_addr *gwaddr, int ifindex); | 110 | const struct in6_addr *gwaddr, |
111 | struct net_device *dev); | ||
110 | #endif | 112 | #endif |
111 | 113 | ||
112 | struct uncached_list { | 114 | struct uncached_list { |
@@ -656,7 +658,8 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, | |||
656 | struct net_device *dev = rt->dst.dev; | 658 | struct net_device *dev = rt->dst.dev; |
657 | 659 | ||
658 | if (dev && !netif_carrier_ok(dev) && | 660 | if (dev && !netif_carrier_ok(dev) && |
659 | idev->cnf.ignore_routes_with_linkdown) | 661 | idev->cnf.ignore_routes_with_linkdown && |
662 | !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE)) | ||
660 | goto out; | 663 | goto out; |
661 | 664 | ||
662 | if (rt6_check_expired(rt)) | 665 | if (rt6_check_expired(rt)) |
@@ -803,7 +806,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, | |||
803 | rt = rt6_get_dflt_router(gwaddr, dev); | 806 | rt = rt6_get_dflt_router(gwaddr, dev); |
804 | else | 807 | else |
805 | rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, | 808 | rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, |
806 | gwaddr, dev->ifindex); | 809 | gwaddr, dev); |
807 | 810 | ||
808 | if (rt && !lifetime) { | 811 | if (rt && !lifetime) { |
809 | ip6_del_rt(rt); | 812 | ip6_del_rt(rt); |
@@ -811,8 +814,8 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, | |||
811 | } | 814 | } |
812 | 815 | ||
813 | if (!rt && lifetime) | 816 | if (!rt && lifetime) |
814 | rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, | 817 | rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, |
815 | pref); | 818 | dev, pref); |
816 | else if (rt) | 819 | else if (rt) |
817 | rt->rt6i_flags = RTF_ROUTEINFO | | 820 | rt->rt6i_flags = RTF_ROUTEINFO | |
818 | (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); | 821 | (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); |
@@ -1050,6 +1053,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, | |||
1050 | int strict = 0; | 1053 | int strict = 0; |
1051 | 1054 | ||
1052 | strict |= flags & RT6_LOOKUP_F_IFACE; | 1055 | strict |= flags & RT6_LOOKUP_F_IFACE; |
1056 | strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; | ||
1053 | if (net->ipv6.devconf_all->forwarding == 0) | 1057 | if (net->ipv6.devconf_all->forwarding == 0) |
1054 | strict |= RT6_LOOKUP_F_REACHABLE; | 1058 | strict |= RT6_LOOKUP_F_REACHABLE; |
1055 | 1059 | ||
@@ -1789,7 +1793,7 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net, | |||
1789 | }; | 1793 | }; |
1790 | struct fib6_table *table; | 1794 | struct fib6_table *table; |
1791 | struct rt6_info *rt; | 1795 | struct rt6_info *rt; |
1792 | int flags = RT6_LOOKUP_F_IFACE; | 1796 | int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE; |
1793 | 1797 | ||
1794 | table = fib6_get_table(net, cfg->fc_table); | 1798 | table = fib6_get_table(net, cfg->fc_table); |
1795 | if (!table) | 1799 | if (!table) |
@@ -2325,13 +2329,16 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) | |||
2325 | #ifdef CONFIG_IPV6_ROUTE_INFO | 2329 | #ifdef CONFIG_IPV6_ROUTE_INFO |
2326 | static struct rt6_info *rt6_get_route_info(struct net *net, | 2330 | static struct rt6_info *rt6_get_route_info(struct net *net, |
2327 | const struct in6_addr *prefix, int prefixlen, | 2331 | const struct in6_addr *prefix, int prefixlen, |
2328 | const struct in6_addr *gwaddr, int ifindex) | 2332 | const struct in6_addr *gwaddr, |
2333 | struct net_device *dev) | ||
2329 | { | 2334 | { |
2335 | u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; | ||
2336 | int ifindex = dev->ifindex; | ||
2330 | struct fib6_node *fn; | 2337 | struct fib6_node *fn; |
2331 | struct rt6_info *rt = NULL; | 2338 | struct rt6_info *rt = NULL; |
2332 | struct fib6_table *table; | 2339 | struct fib6_table *table; |
2333 | 2340 | ||
2334 | table = fib6_get_table(net, RT6_TABLE_INFO); | 2341 | table = fib6_get_table(net, tb_id); |
2335 | if (!table) | 2342 | if (!table) |
2336 | return NULL; | 2343 | return NULL; |
2337 | 2344 | ||
@@ -2357,12 +2364,13 @@ out: | |||
2357 | 2364 | ||
2358 | static struct rt6_info *rt6_add_route_info(struct net *net, | 2365 | static struct rt6_info *rt6_add_route_info(struct net *net, |
2359 | const struct in6_addr *prefix, int prefixlen, | 2366 | const struct in6_addr *prefix, int prefixlen, |
2360 | const struct in6_addr *gwaddr, int ifindex, | 2367 | const struct in6_addr *gwaddr, |
2368 | struct net_device *dev, | ||
2361 | unsigned int pref) | 2369 | unsigned int pref) |
2362 | { | 2370 | { |
2363 | struct fib6_config cfg = { | 2371 | struct fib6_config cfg = { |
2364 | .fc_metric = IP6_RT_PRIO_USER, | 2372 | .fc_metric = IP6_RT_PRIO_USER, |
2365 | .fc_ifindex = ifindex, | 2373 | .fc_ifindex = dev->ifindex, |
2366 | .fc_dst_len = prefixlen, | 2374 | .fc_dst_len = prefixlen, |
2367 | .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | | 2375 | .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | |
2368 | RTF_UP | RTF_PREF(pref), | 2376 | RTF_UP | RTF_PREF(pref), |
@@ -2371,7 +2379,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, | |||
2371 | .fc_nlinfo.nl_net = net, | 2379 | .fc_nlinfo.nl_net = net, |
2372 | }; | 2380 | }; |
2373 | 2381 | ||
2374 | cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO; | 2382 | cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO, |
2375 | cfg.fc_dst = *prefix; | 2383 | cfg.fc_dst = *prefix; |
2376 | cfg.fc_gateway = *gwaddr; | 2384 | cfg.fc_gateway = *gwaddr; |
2377 | 2385 | ||
@@ -2381,16 +2389,17 @@ static struct rt6_info *rt6_add_route_info(struct net *net, | |||
2381 | 2389 | ||
2382 | ip6_route_add(&cfg); | 2390 | ip6_route_add(&cfg); |
2383 | 2391 | ||
2384 | return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); | 2392 | return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev); |
2385 | } | 2393 | } |
2386 | #endif | 2394 | #endif |
2387 | 2395 | ||
2388 | struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) | 2396 | struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) |
2389 | { | 2397 | { |
2398 | u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; | ||
2390 | struct rt6_info *rt; | 2399 | struct rt6_info *rt; |
2391 | struct fib6_table *table; | 2400 | struct fib6_table *table; |
2392 | 2401 | ||
2393 | table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); | 2402 | table = fib6_get_table(dev_net(dev), tb_id); |
2394 | if (!table) | 2403 | if (!table) |
2395 | return NULL; | 2404 | return NULL; |
2396 | 2405 | ||
@@ -2424,20 +2433,20 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, | |||
2424 | 2433 | ||
2425 | cfg.fc_gateway = *gwaddr; | 2434 | cfg.fc_gateway = *gwaddr; |
2426 | 2435 | ||
2427 | ip6_route_add(&cfg); | 2436 | if (!ip6_route_add(&cfg)) { |
2437 | struct fib6_table *table; | ||
2438 | |||
2439 | table = fib6_get_table(dev_net(dev), cfg.fc_table); | ||
2440 | if (table) | ||
2441 | table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; | ||
2442 | } | ||
2428 | 2443 | ||
2429 | return rt6_get_dflt_router(gwaddr, dev); | 2444 | return rt6_get_dflt_router(gwaddr, dev); |
2430 | } | 2445 | } |
2431 | 2446 | ||
2432 | void rt6_purge_dflt_routers(struct net *net) | 2447 | static void __rt6_purge_dflt_routers(struct fib6_table *table) |
2433 | { | 2448 | { |
2434 | struct rt6_info *rt; | 2449 | struct rt6_info *rt; |
2435 | struct fib6_table *table; | ||
2436 | |||
2437 | /* NOTE: Keep consistent with rt6_get_dflt_router */ | ||
2438 | table = fib6_get_table(net, RT6_TABLE_DFLT); | ||
2439 | if (!table) | ||
2440 | return; | ||
2441 | 2450 | ||
2442 | restart: | 2451 | restart: |
2443 | read_lock_bh(&table->tb6_lock); | 2452 | read_lock_bh(&table->tb6_lock); |
@@ -2451,6 +2460,27 @@ restart: | |||
2451 | } | 2460 | } |
2452 | } | 2461 | } |
2453 | read_unlock_bh(&table->tb6_lock); | 2462 | read_unlock_bh(&table->tb6_lock); |
2463 | |||
2464 | table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; | ||
2465 | } | ||
2466 | |||
2467 | void rt6_purge_dflt_routers(struct net *net) | ||
2468 | { | ||
2469 | struct fib6_table *table; | ||
2470 | struct hlist_head *head; | ||
2471 | unsigned int h; | ||
2472 | |||
2473 | rcu_read_lock(); | ||
2474 | |||
2475 | for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { | ||
2476 | head = &net->ipv6.fib_table_hash[h]; | ||
2477 | hlist_for_each_entry_rcu(table, head, tb6_hlist) { | ||
2478 | if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) | ||
2479 | __rt6_purge_dflt_routers(table); | ||
2480 | } | ||
2481 | } | ||
2482 | |||
2483 | rcu_read_unlock(); | ||
2454 | } | 2484 | } |
2455 | 2485 | ||
2456 | static void rtmsg_to_fib6_config(struct net *net, | 2486 | static void rtmsg_to_fib6_config(struct net *net, |