|
@@ -102,11 +102,13 @@ static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
|
|
|
#ifdef CONFIG_IPV6_ROUTE_INFO
|
|
|
static struct rt6_info *rt6_add_route_info(struct net *net,
|
|
|
const struct in6_addr *prefix, int prefixlen,
|
|
|
- const struct in6_addr *gwaddr, int ifindex,
|
|
|
+ const struct in6_addr *gwaddr,
|
|
|
+ struct net_device *dev,
|
|
|
unsigned int pref);
|
|
|
static struct rt6_info *rt6_get_route_info(struct net *net,
|
|
|
const struct in6_addr *prefix, int prefixlen,
|
|
|
- const struct in6_addr *gwaddr, int ifindex);
|
|
|
+ const struct in6_addr *gwaddr,
|
|
|
+ struct net_device *dev);
|
|
|
#endif
|
|
|
|
|
|
struct uncached_list {
|
|
@@ -803,7 +805,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
|
|
|
rt = rt6_get_dflt_router(gwaddr, dev);
|
|
|
else
|
|
|
rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
|
|
|
- gwaddr, dev->ifindex);
|
|
|
+ gwaddr, dev);
|
|
|
|
|
|
if (rt && !lifetime) {
|
|
|
ip6_del_rt(rt);
|
|
@@ -811,8 +813,8 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
|
|
|
}
|
|
|
|
|
|
if (!rt && lifetime)
|
|
|
- rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
|
|
|
- pref);
|
|
|
+ rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
|
|
|
+ dev, pref);
|
|
|
else if (rt)
|
|
|
rt->rt6i_flags = RTF_ROUTEINFO |
|
|
|
(rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
|
|
@@ -2325,13 +2327,16 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
|
|
|
#ifdef CONFIG_IPV6_ROUTE_INFO
|
|
|
static struct rt6_info *rt6_get_route_info(struct net *net,
|
|
|
const struct in6_addr *prefix, int prefixlen,
|
|
|
- const struct in6_addr *gwaddr, int ifindex)
|
|
|
+ const struct in6_addr *gwaddr,
|
|
|
+ struct net_device *dev)
|
|
|
{
|
|
|
+ u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
|
|
|
+ int ifindex = dev->ifindex;
|
|
|
struct fib6_node *fn;
|
|
|
struct rt6_info *rt = NULL;
|
|
|
struct fib6_table *table;
|
|
|
|
|
|
- table = fib6_get_table(net, RT6_TABLE_INFO);
|
|
|
+ table = fib6_get_table(net, tb_id);
|
|
|
if (!table)
|
|
|
return NULL;
|
|
|
|
|
@@ -2357,12 +2362,13 @@ out:
|
|
|
|
|
|
static struct rt6_info *rt6_add_route_info(struct net *net,
|
|
|
const struct in6_addr *prefix, int prefixlen,
|
|
|
- const struct in6_addr *gwaddr, int ifindex,
|
|
|
+ const struct in6_addr *gwaddr,
|
|
|
+ struct net_device *dev,
|
|
|
unsigned int pref)
|
|
|
{
|
|
|
struct fib6_config cfg = {
|
|
|
.fc_metric = IP6_RT_PRIO_USER,
|
|
|
- .fc_ifindex = ifindex,
|
|
|
+ .fc_ifindex = dev->ifindex,
|
|
|
.fc_dst_len = prefixlen,
|
|
|
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
|
|
|
RTF_UP | RTF_PREF(pref),
|
|
@@ -2371,7 +2377,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net,
|
|
|
.fc_nlinfo.nl_net = net,
|
|
|
};
|
|
|
|
|
|
- cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
|
|
|
+ cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
|
|
|
cfg.fc_dst = *prefix;
|
|
|
cfg.fc_gateway = *gwaddr;
|
|
|
|
|
@@ -2381,16 +2387,17 @@ static struct rt6_info *rt6_add_route_info(struct net *net,
|
|
|
|
|
|
ip6_route_add(&cfg);
|
|
|
|
|
|
- return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
|
|
|
+ return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
|
|
|
{
|
|
|
+ u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
|
|
|
struct rt6_info *rt;
|
|
|
struct fib6_table *table;
|
|
|
|
|
|
- table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
|
|
|
+ table = fib6_get_table(dev_net(dev), tb_id);
|
|
|
if (!table)
|
|
|
return NULL;
|
|
|
|
|
@@ -2424,20 +2431,20 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
|
|
|
|
|
|
cfg.fc_gateway = *gwaddr;
|
|
|
|
|
|
- ip6_route_add(&cfg);
|
|
|
+ if (!ip6_route_add(&cfg)) {
|
|
|
+ struct fib6_table *table;
|
|
|
+
|
|
|
+ table = fib6_get_table(dev_net(dev), cfg.fc_table);
|
|
|
+ if (table)
|
|
|
+ table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
|
|
|
+ }
|
|
|
|
|
|
return rt6_get_dflt_router(gwaddr, dev);
|
|
|
}
|
|
|
|
|
|
-void rt6_purge_dflt_routers(struct net *net)
|
|
|
+static void __rt6_purge_dflt_routers(struct fib6_table *table)
|
|
|
{
|
|
|
struct rt6_info *rt;
|
|
|
- struct fib6_table *table;
|
|
|
-
|
|
|
- /* NOTE: Keep consistent with rt6_get_dflt_router */
|
|
|
- table = fib6_get_table(net, RT6_TABLE_DFLT);
|
|
|
- if (!table)
|
|
|
- return;
|
|
|
|
|
|
restart:
|
|
|
read_lock_bh(&table->tb6_lock);
|
|
@@ -2451,6 +2458,27 @@ restart:
|
|
|
}
|
|
|
}
|
|
|
read_unlock_bh(&table->tb6_lock);
|
|
|
+
|
|
|
+ table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
|
|
|
+}
|
|
|
+
|
|
|
+void rt6_purge_dflt_routers(struct net *net)
|
|
|
+{
|
|
|
+ struct fib6_table *table;
|
|
|
+ struct hlist_head *head;
|
|
|
+ unsigned int h;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+
|
|
|
+ for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
|
|
|
+ head = &net->ipv6.fib_table_hash[h];
|
|
|
+ hlist_for_each_entry_rcu(table, head, tb6_hlist) {
|
|
|
+ if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
|
|
|
+ __rt6_purge_dflt_routers(table);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
}
|
|
|
|
|
|
static void rtmsg_to_fib6_config(struct net *net,
|