ANDROID: net: ipv6: autoconf routes into per-device tables
Currently, IPv6 router discovery always puts routes into RT6_TABLE_MAIN. This causes problems for connection managers that want to support multiple simultaneous network connections and want control over which one is used by default (e.g., wifi and wired). To work around this connection managers typically take the routes they prefer and copy them to static routes with low metrics in the main table. This puts the burden on the connection manager to watch netlink to see if the routes have changed, delete the routes when their lifetime expires, etc. Instead, this patch adds a per-interface sysctl to have the kernel put autoconf routes into different tables. This allows each interface to have its own autoconf table, and choosing the default interface (or using different interfaces at the same time for different types of traffic) can be done using appropriate ip rules. The sysctl behaves as follows: - = 0: default. Put routes into RT6_TABLE_MAIN as before. - > 0: manual. Put routes into the specified table. - < 0: automatic. Add the absolute value of the sysctl to the device's ifindex, and use that table. The automatic mode is most useful in conjunction with net.ipv6.conf.default.accept_ra_rt_table. A connection manager or distribution could set it to, say, -100 on boot, and thereafter just use IP rules. Signed-off-by: Lorenzo Colitti <lorenzo@google.com> [AmitP: Refactored original changes to align with the changes introduced by upstream commits830218c1ad
("net: ipv6: Fix processing of RAs in presence of VRF"),8d1c802b28
("net/ipv6: Flip FIB entries to fib6_info"). Also folded following android-4.9 commit changes into this patch be65fb01da4d ("ANDROID: net: ipv6: remove unused variable ifindex in")] Bug: 120445791 Change-Id: I82d16e3737d9cdfa6489e649e247894d0d60cbb1 Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
This commit is contained in:

committed by
Todd Kjos

parent
a773a88f2f
commit
20599bae2f
@@ -42,6 +42,7 @@ struct ipv6_devconf {
|
|||||||
__s32 accept_ra_rt_info_max_plen;
|
__s32 accept_ra_rt_info_max_plen;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
__s32 accept_ra_rt_table;
|
||||||
__s32 proxy_ndp;
|
__s32 proxy_ndp;
|
||||||
__s32 accept_source_route;
|
__s32 accept_source_route;
|
||||||
__s32 accept_ra_from_local;
|
__s32 accept_ra_from_local;
|
||||||
|
@@ -296,6 +296,18 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset)
|
|||||||
void addrconf_prefix_rcv(struct net_device *dev,
|
void addrconf_prefix_rcv(struct net_device *dev,
|
||||||
u8 *opt, int len, bool sllao);
|
u8 *opt, int len, bool sllao);
|
||||||
|
|
||||||
|
/* Determines into what table to put autoconf PIO/RIO/default routes
|
||||||
|
* learned on this device.
|
||||||
|
*
|
||||||
|
* - If 0, use the same table for every device. This puts routes into
|
||||||
|
* one of RT_TABLE_{PREFIX,INFO,DFLT} depending on the type of route
|
||||||
|
* (but note that these three are currently all equal to
|
||||||
|
* RT6_TABLE_MAIN).
|
||||||
|
* - If > 0, use the specified table.
|
||||||
|
* - If < 0, put routes into table dev->ifindex + (-rt_table).
|
||||||
|
*/
|
||||||
|
u32 addrconf_rt_table(const struct net_device *dev, u32 default_table);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* anycast prototypes (anycast.c)
|
* anycast prototypes (anycast.c)
|
||||||
*/
|
*/
|
||||||
|
@@ -220,6 +220,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
|
|||||||
.accept_ra_rt_info_max_plen = 0,
|
.accept_ra_rt_info_max_plen = 0,
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
.accept_ra_rt_table = 0,
|
||||||
.proxy_ndp = 0,
|
.proxy_ndp = 0,
|
||||||
.accept_source_route = 0, /* we do not accept RH0 by default. */
|
.accept_source_route = 0, /* we do not accept RH0 by default. */
|
||||||
.disable_ipv6 = 0,
|
.disable_ipv6 = 0,
|
||||||
@@ -274,6 +275,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
|
|||||||
.accept_ra_rt_info_max_plen = 0,
|
.accept_ra_rt_info_max_plen = 0,
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
.accept_ra_rt_table = 0,
|
||||||
.proxy_ndp = 0,
|
.proxy_ndp = 0,
|
||||||
.accept_source_route = 0, /* we do not accept RH0 by default. */
|
.accept_source_route = 0, /* we do not accept RH0 by default. */
|
||||||
.disable_ipv6 = 0,
|
.disable_ipv6 = 0,
|
||||||
@@ -2310,6 +2312,26 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad
|
|||||||
ipv6_regen_rndid(idev);
|
ipv6_regen_rndid(idev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 addrconf_rt_table(const struct net_device *dev, u32 default_table)
|
||||||
|
{
|
||||||
|
struct inet6_dev *idev = in6_dev_get(dev);
|
||||||
|
int sysctl;
|
||||||
|
u32 table;
|
||||||
|
|
||||||
|
if (!idev)
|
||||||
|
return default_table;
|
||||||
|
sysctl = idev->cnf.accept_ra_rt_table;
|
||||||
|
if (sysctl == 0) {
|
||||||
|
table = default_table;
|
||||||
|
} else if (sysctl > 0) {
|
||||||
|
table = (u32) sysctl;
|
||||||
|
} else {
|
||||||
|
table = (unsigned) dev->ifindex + (-sysctl);
|
||||||
|
}
|
||||||
|
in6_dev_put(idev);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add prefix route.
|
* Add prefix route.
|
||||||
*/
|
*/
|
||||||
@@ -2320,7 +2342,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric,
|
|||||||
u32 flags, gfp_t gfp_flags)
|
u32 flags, gfp_t gfp_flags)
|
||||||
{
|
{
|
||||||
struct fib6_config cfg = {
|
struct fib6_config cfg = {
|
||||||
.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
|
.fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX),
|
||||||
.fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF,
|
.fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF,
|
||||||
.fc_ifindex = dev->ifindex,
|
.fc_ifindex = dev->ifindex,
|
||||||
.fc_expires = expires,
|
.fc_expires = expires,
|
||||||
@@ -2354,7 +2376,7 @@ static struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
|
|||||||
struct fib6_node *fn;
|
struct fib6_node *fn;
|
||||||
struct fib6_info *rt = NULL;
|
struct fib6_info *rt = NULL;
|
||||||
struct fib6_table *table;
|
struct fib6_table *table;
|
||||||
u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX;
|
u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX);
|
||||||
|
|
||||||
table = fib6_get_table(dev_net(dev), tb_id);
|
table = fib6_get_table(dev_net(dev), tb_id);
|
||||||
if (!table)
|
if (!table)
|
||||||
@@ -6346,6 +6368,13 @@ static const struct ctl_table addrconf_sysctl[] = {
|
|||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
.procname = "accept_ra_rt_table",
|
||||||
|
.data = &ipv6_devconf.accept_ra_rt_table,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = proc_dointvec,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.procname = "proxy_ndp",
|
.procname = "proxy_ndp",
|
||||||
.data = &ipv6_devconf.proxy_ndp,
|
.data = &ipv6_devconf.proxy_ndp,
|
||||||
|
@@ -3468,7 +3468,7 @@ static struct fib6_info *rt6_get_route_info(struct net *net,
|
|||||||
const struct in6_addr *gwaddr,
|
const struct in6_addr *gwaddr,
|
||||||
struct net_device *dev)
|
struct net_device *dev)
|
||||||
{
|
{
|
||||||
u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
|
u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO);
|
||||||
int ifindex = dev->ifindex;
|
int ifindex = dev->ifindex;
|
||||||
struct fib6_node *fn;
|
struct fib6_node *fn;
|
||||||
struct fib6_info *rt = NULL;
|
struct fib6_info *rt = NULL;
|
||||||
@@ -3518,7 +3518,7 @@ static struct fib6_info *rt6_add_route_info(struct net *net,
|
|||||||
.fc_nlinfo.nl_net = net,
|
.fc_nlinfo.nl_net = net,
|
||||||
};
|
};
|
||||||
|
|
||||||
cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
|
cfg.fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO),
|
||||||
cfg.fc_dst = *prefix;
|
cfg.fc_dst = *prefix;
|
||||||
cfg.fc_gateway = *gwaddr;
|
cfg.fc_gateway = *gwaddr;
|
||||||
|
|
||||||
@@ -3536,7 +3536,7 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
|
|||||||
const struct in6_addr *addr,
|
const struct in6_addr *addr,
|
||||||
struct net_device *dev)
|
struct net_device *dev)
|
||||||
{
|
{
|
||||||
u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
|
u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_DFLT);
|
||||||
struct fib6_info *rt;
|
struct fib6_info *rt;
|
||||||
struct fib6_table *table;
|
struct fib6_table *table;
|
||||||
|
|
||||||
@@ -3563,7 +3563,7 @@ struct fib6_info *rt6_add_dflt_router(struct net *net,
|
|||||||
unsigned int pref)
|
unsigned int pref)
|
||||||
{
|
{
|
||||||
struct fib6_config cfg = {
|
struct fib6_config cfg = {
|
||||||
.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
|
.fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_DFLT),
|
||||||
.fc_metric = IP6_RT_PRIO_USER,
|
.fc_metric = IP6_RT_PRIO_USER,
|
||||||
.fc_ifindex = dev->ifindex,
|
.fc_ifindex = dev->ifindex,
|
||||||
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
|
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
|
||||||
@@ -3588,47 +3588,24 @@ struct fib6_info *rt6_add_dflt_router(struct net *net,
|
|||||||
return rt6_get_dflt_router(net, gwaddr, dev);
|
return rt6_get_dflt_router(net, gwaddr, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __rt6_purge_dflt_routers(struct net *net,
|
static int rt6_addrconf_purge(struct fib6_info *rt, void *arg)
|
||||||
struct fib6_table *table)
|
|
||||||
{
|
{
|
||||||
struct fib6_info *rt;
|
struct net_device *dev = fib6_info_nh_dev(rt);
|
||||||
|
struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
|
||||||
|
|
||||||
restart:
|
if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
|
||||||
rcu_read_lock();
|
(!idev || idev->cnf.accept_ra != 2)) {
|
||||||
for_each_fib6_node_rt_rcu(&table->tb6_root) {
|
/* Delete this route. See fib6_clean_tree() */
|
||||||
struct net_device *dev = fib6_info_nh_dev(rt);
|
return -1;
|
||||||
struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
|
|
||||||
|
|
||||||
if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
|
|
||||||
(!idev || idev->cnf.accept_ra != 2) &&
|
|
||||||
fib6_info_hold_safe(rt)) {
|
|
||||||
rcu_read_unlock();
|
|
||||||
ip6_del_rt(net, rt);
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
|
/* Continue walking */
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rt6_purge_dflt_routers(struct net *net)
|
void rt6_purge_dflt_routers(struct net *net)
|
||||||
{
|
{
|
||||||
struct fib6_table *table;
|
fib6_clean_all(net, rt6_addrconf_purge, NULL);
|
||||||
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(net, table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rcu_read_unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rtmsg_to_fib6_config(struct net *net,
|
static void rtmsg_to_fib6_config(struct net *net,
|
||||||
|
Reference in New Issue
Block a user