[PATCH RFC] ipv6: Add net.ipv6.conf.*.accept_ra_defrtr_table sysctl.
From: Ben Jencks <hidden>
Date: 2012-08-22 03:48:09
Subsystem:
documentation, networking [general], networking [ipv4/ipv6], proc sysctl, the rest · Maintainers:
Jonathan Corbet, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Kees Cook, Joel Granados, Linus Torvalds
Allows configuration of the routing table to which default routes from RAs are added on a per-interface basis. One use case where this is required is a multi-homed router advertising multiple prefixes, one for each upstream. The proper routing policy is something like: 0: from all lookup local 10: from all lookup main 20: from <prefix1> lookup provider1 30: from <prefix2> lookup provider2 with directly connected and internal routes in main, and default routes from providers 1 and 2 in their respective tables. With static routes this is straightforward; if the route for either or both providers is received via RA this configuration option becomes necessary to put the routes in the appropriate tables. Signed-off-by: Ben Jencks <redacted> --- Patch is against v3.5.2, but applies cleanly to master as of today. Looking for feedback on: * Is this a worthwhile feature? Did I miss a good way to do this in userspace? * Is a sysctl the right place to configure it? I know it's discouraged to add new ones, but it seems to fit best with the other similar configuration options. * Is the rt6_purge_dflt_routers behavior correct? Should addrconf_fixup_forwarding be modified so that in the single device case it only purges from that device's table? Documentation/networking/ip-sysctl.txt | 6 +++++ include/linux/ipv6.h | 2 ++ include/linux/sysctl.h | 1 + kernel/sysctl_binary.c | 1 + net/ipv6/addrconf.c | 10 +++++++ net/ipv6/route.c | 45 ++++++++++++++++++++++---------- 6 files changed, 51 insertions(+), 14 deletions(-)
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 6f896b9..0d0947a 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt@@ -1085,6 +1085,12 @@ accept_ra_defrtr - BOOLEAN Functional default: enabled if accept_ra is enabled. disabled if accept_ra is disabled. +accept_ra_defrtr_table - INTEGER + Routing table into which to put default route learned via Router + Advertisement. + + Functional default: "main" routing table (254) + accept_ra_pinfo - BOOLEAN Learn Prefix Information in Router Advertisement.
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 8260ef7..fa9b4fe 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h@@ -153,6 +153,7 @@ struct ipv6_devconf { #endif __s32 max_addresses; __s32 accept_ra_defrtr; + __s32 accept_ra_defrtr_table; __s32 accept_ra_pinfo; #ifdef CONFIG_IPV6_ROUTER_PREF __s32 accept_ra_rtr_pref;
@@ -202,6 +203,7 @@ enum { DEVCONF_MAX_ADDRESSES, DEVCONF_FORCE_MLD_VERSION, DEVCONF_ACCEPT_RA_DEFRTR, + DEVCONF_ACCEPT_RA_DEFRTR_TABLE, DEVCONF_ACCEPT_RA_PINFO, DEVCONF_ACCEPT_RA_RTR_PREF, DEVCONF_RTR_PROBE_INTERVAL,
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index c34b4c8..80115c8 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h@@ -568,6 +568,7 @@ enum { NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22, NET_IPV6_PROXY_NDP=23, NET_IPV6_ACCEPT_SOURCE_ROUTE=25, + NET_IPV6_ACCEPT_RA_DEFRTR_TABLE=26, __NET_IPV6_MAX };
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index a650694..da964f3 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c@@ -517,6 +517,7 @@ static const struct bin_table bin_net_ipv6_conf_var_table[] = { { CTL_INT, NET_IPV6_MAX_ADDRESSES, "max_addresses" }, { CTL_INT, NET_IPV6_FORCE_MLD_VERSION, "force_mld_version" }, { CTL_INT, NET_IPV6_ACCEPT_RA_DEFRTR, "accept_ra_defrtr" }, + { CTL_INT, NET_IPV6_ACCEPT_RA_DEFRTR_TABLE, "accept_ra_defrtr_table" }, { CTL_INT, NET_IPV6_ACCEPT_RA_PINFO, "accept_ra_pinfo" }, { CTL_INT, NET_IPV6_ACCEPT_RA_RTR_PREF, "accept_ra_rtr_pref" }, { CTL_INT, NET_IPV6_RTR_PROBE_INTERVAL, "router_probe_interval" },
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 8f6411c..2bdb96c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c@@ -188,6 +188,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { #endif .max_addresses = IPV6_MAX_ADDRESSES, .accept_ra_defrtr = 1, + .accept_ra_defrtr_table = RT6_TABLE_DFLT, .accept_ra_pinfo = 1, #ifdef CONFIG_IPV6_ROUTER_PREF .accept_ra_rtr_pref = 1,
@@ -222,6 +223,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { #endif .max_addresses = IPV6_MAX_ADDRESSES, .accept_ra_defrtr = 1, + .accept_ra_defrtr_table = RT6_TABLE_DFLT, .accept_ra_pinfo = 1, #ifdef CONFIG_IPV6_ROUTER_PREF .accept_ra_rtr_pref = 1,
@@ -3894,6 +3896,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, #endif array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr; + array[DEVCONF_ACCEPT_RA_DEFRTR_TABLE] = cnf->accept_ra_defrtr_table; array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; #ifdef CONFIG_IPV6_ROUTER_PREF array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref;
@@ -4496,6 +4499,13 @@ static struct addrconf_sysctl_table .proc_handler = proc_dointvec, }, { + .procname = "accept_ra_defrtr_table", + .data = &ipv6_devconf.accept_ra_defrtr_table, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "accept_ra_pinfo", .data = &ipv6_devconf.accept_ra_pinfo, .maxlen = sizeof(int),
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index becb048..dafcb61 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c@@ -1916,8 +1916,12 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev { struct rt6_info *rt; struct fib6_table *table; + struct inet6_dev *idev; - table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); + idev = __in6_dev_get(dev); + if (!idev) + return NULL; + table = fib6_get_table(dev_net(dev), idev->cnf.accept_ra_defrtr_table); if (!table) return NULL;
@@ -1939,7 +1943,6 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, unsigned int pref) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_DFLT, .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
@@ -1948,7 +1951,12 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, .fc_nlinfo.nlh = NULL, .fc_nlinfo.nl_net = dev_net(dev), }; + struct inet6_dev *idev; + /* idev should not be null, since we were called from + * ndisc_router_discovery which already checked it */ + idev = __in6_dev_get(dev); + cfg.fc_table = idev->cnf.accept_ra_defrtr_table; cfg.fc_gateway = *gwaddr; ip6_route_add(&cfg);
@@ -1960,23 +1968,32 @@ void rt6_purge_dflt_routers(struct net *net) { struct rt6_info *rt; struct fib6_table *table; + struct net_device *dev; + struct inet6_dev *idev; - /* NOTE: Keep consistent with rt6_get_dflt_router */ - table = fib6_get_table(net, RT6_TABLE_DFLT); - if (!table) - return; + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + idev = __in6_dev_get(dev); + if (idev == NULL) { + continue; + } + table = fib6_get_table(net, idev->cnf.accept_ra_defrtr_table); + if (!table) + continue; restart: - read_lock_bh(&table->tb6_lock); - for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { - if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { - dst_hold(&rt->dst); - read_unlock_bh(&table->tb6_lock); - ip6_del_rt(rt); - goto restart; + read_lock_bh(&table->tb6_lock); + for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { + if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { + dst_hold(&rt->dst); + read_unlock_bh(&table->tb6_lock); + ip6_del_rt(rt); + goto restart; + } } + read_unlock_bh(&table->tb6_lock); } - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); } static void rtmsg_to_fib6_config(struct net *net,
--
1.7.9.5