|
| 1 | +From 23d559efa7ddfccb29dfe2df772217e55402cd71 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Praveen Chaudhary < [email protected]> |
| 3 | +Date: Mon, 25 Jan 2021 13:44:30 -0800 |
| 4 | +Subject: [PATCH] net: allow user to set metric on default route learned via |
| 5 | + Router Advertisement |
| 6 | + |
| 7 | +For IPv4, default route is learned via DHCPv4 and user is allowed to change |
| 8 | +metric using config etc/network/interfaces. But for IPv6, default route can |
| 9 | +be learned via RA, for which, currently a fixed metric value 1024 is used. |
| 10 | + |
| 11 | +Ideally, user should be able to configure metric on default route for IPv6 |
| 12 | +similar to IPv4. This patch adds sysctl for the same. |
| 13 | + |
| 14 | +Logs: |
| 15 | + |
| 16 | +For IPv4: |
| 17 | + |
| 18 | +Config in etc/network/interfaces: |
| 19 | +auto eth0 |
| 20 | +iface eth0 inet dhcp |
| 21 | + metric 4261413864 |
| 22 | + |
| 23 | +IPv4 Kernel Route Table: |
| 24 | +$ ip route list |
| 25 | +default via 172.21.47.1 dev eth0 metric 4261413864 |
| 26 | + |
| 27 | +FRR Table, if a static route is configured: |
| 28 | +[In real scenario, it is useful to prefer BGP learned default route over DHCPv4 default route.] |
| 29 | +Codes: K - kernel route, C - connected, S - static, R - RIP, |
| 30 | + O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP, |
| 31 | + T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP, |
| 32 | + > - selected route, * - FIB route |
| 33 | + |
| 34 | +S>* 0.0.0.0/0 [20/0] is directly connected, eth0, 00:00:03 |
| 35 | +K 0.0.0.0/0 [254/1000] via 172.21.47.1, eth0, 6d08h51m |
| 36 | + |
| 37 | +i.e. User can prefer Default Router learned via Routing Protocol in IPv4. |
| 38 | +Similar behavior is not possible for IPv6, without this fix. |
| 39 | + |
| 40 | +After fix [for IPv6]: |
| 41 | +sudo sysctl -w net.ipv6.conf.eth0.net.ipv6.conf.eth0.ra_defrtr_metric=1996489705 |
| 42 | + |
| 43 | +IP monitor: [When IPv6 RA is received] |
| 44 | +default via fe80::xx16:xxxx:feb3:ce8e dev eth0 proto ra metric 1996489705 pref high |
| 45 | + |
| 46 | +Kernel IPv6 routing table |
| 47 | +$ ip -6 route list |
| 48 | +default via fe80::be16:65ff:feb3:ce8e dev eth0 proto ra metric 1996489705 expires 21sec hoplimit 64 pref high |
| 49 | + |
| 50 | +FRR Table, if a static route is configured: |
| 51 | +[In real scenario, it is useful to prefer BGP learned default route over IPv6 RA default route.] |
| 52 | +Codes: K - kernel route, C - connected, S - static, R - RIPng, |
| 53 | + O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table, |
| 54 | + v - VNC, V - VNC-Direct, A - Babel, D - SHARP, |
| 55 | + > - selected route, * - FIB route |
| 56 | + |
| 57 | +S>* ::/0 [20/0] is directly connected, eth0, 00:00:06 |
| 58 | +K ::/0 [119/1001] via fe80::xx16:xxxx:feb3:ce8e, eth0, 6d07h43m |
| 59 | + |
| 60 | +If the metric is changed later, the effect will be seen only when next IPv6 |
| 61 | +RA is received, because the default route must be fully controlled by RA msg. |
| 62 | +Below metric is changed from 1996489705 to 1996489704. |
| 63 | + |
| 64 | +$ sudo sysctl -w net.ipv6.conf.eth0.ra_defrtr_metric=1996489704 |
| 65 | +net.ipv6.conf.eth0.ra_defrtr_metric = 1996489704 |
| 66 | + |
| 67 | +IP monitor: |
| 68 | +[On next IPv6 RA msg, Kernel deletes prev route and installs new route with updated metric] |
| 69 | + |
| 70 | +Deleted default via fe80::xx16:xxxx:feb3:ce8e dev eth0 proto ra metric 1996489705 expires 3sec hoplimit 64 pref high |
| 71 | +default via fe80::xx16:xxxx:feb3:ce8e dev eth0 proto ra metric 1996489704 pref high |
| 72 | + |
| 73 | +Signed-off-by: Praveen Chaudhary < [email protected]> |
| 74 | +Signed-off-by: Zhenggen Xu < [email protected]> |
| 75 | +Reviewed-by: David Ahern < [email protected]> |
| 76 | +Link: https://lore.kernel.org/r/ [email protected] |
| 77 | +Signed-off-by: Jakub Kicinski < [email protected]> |
| 78 | +--- |
| 79 | + Documentation/networking/ip-sysctl.rst | 10 ++++++++++ |
| 80 | + include/linux/ipv6.h | 1 + |
| 81 | + include/net/ip6_route.h | 3 ++- |
| 82 | + include/uapi/linux/ipv6.h | 1 + |
| 83 | + include/uapi/linux/sysctl.h | 1 + |
| 84 | + net/ipv6/addrconf.c | 11 +++++++++++ |
| 85 | + net/ipv6/ndisc.c | 12 ++++++++---- |
| 86 | + net/ipv6/route.c | 5 +++-- |
| 87 | + 8 files changed, 37 insertions(+), 7 deletions(-) |
| 88 | + |
| 89 | +diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst |
| 90 | +index bf1f0ec9740b..e4e1216e6a7d 100644 |
| 91 | +--- a/Documentation/networking/ip-sysctl.rst |
| 92 | ++++ b/Documentation/networking/ip-sysctl.rst |
| 93 | +@@ -1876,6 +1876,16 @@ accept_ra_defrtr - BOOLEAN |
| 94 | + - enabled if accept_ra is enabled. |
| 95 | + - disabled if accept_ra is disabled. |
| 96 | + |
| 97 | ++ra_defrtr_metric - UNSIGNED INTEGER |
| 98 | ++ Route metric for default route learned in Router Advertisement. This value |
| 99 | ++ will be assigned as metric for the default route learned via IPv6 Router |
| 100 | ++ Advertisement. Takes affect only if accept_ra_defrtr is enabled. |
| 101 | ++ |
| 102 | ++ Possible values: |
| 103 | ++ 1 to 0xFFFFFFFF |
| 104 | ++ |
| 105 | ++ Default: IP6_RT_PRIO_USER i.e. 1024. |
| 106 | ++ |
| 107 | + accept_ra_from_local - BOOLEAN |
| 108 | + Accept RA with source-address that is found on local machine |
| 109 | + if the RA is otherwise proper and able to be accepted. |
| 110 | +diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h |
| 111 | +index 7a918daa3b19..88641e3b1cc4 100644 |
| 112 | +--- a/include/linux/ipv6.h |
| 113 | ++++ b/include/linux/ipv6.h |
| 114 | +@@ -31,6 +31,7 @@ struct ipv6_devconf { |
| 115 | + __s32 max_desync_factor; |
| 116 | + __s32 max_addresses; |
| 117 | + __s32 accept_ra_defrtr; |
| 118 | ++ __u32 ra_defrtr_metric; |
| 119 | + __s32 accept_ra_min_hop_limit; |
| 120 | + __s32 accept_ra_pinfo; |
| 121 | + __s32 ignore_routes_with_linkdown; |
| 122 | +diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h |
| 123 | +index 44969d03debf..0bf09a9bca4e 100644 |
| 124 | +--- a/include/net/ip6_route.h |
| 125 | ++++ b/include/net/ip6_route.h |
| 126 | +@@ -174,7 +174,8 @@ struct fib6_info *rt6_get_dflt_router(struct net *net, |
| 127 | + struct net_device *dev); |
| 128 | + struct fib6_info *rt6_add_dflt_router(struct net *net, |
| 129 | + const struct in6_addr *gwaddr, |
| 130 | +- struct net_device *dev, unsigned int pref); |
| 131 | ++ struct net_device *dev, unsigned int pref, |
| 132 | ++ u32 defrtr_usr_metric); |
| 133 | + |
| 134 | + void rt6_purge_dflt_routers(struct net *net); |
| 135 | + |
| 136 | +diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h |
| 137 | +index 9cdbc5b04855..a5e93062315b 100644 |
| 138 | +--- a/include/uapi/linux/ipv6.h |
| 139 | ++++ b/include/uapi/linux/ipv6.h |
| 140 | +@@ -190,6 +190,7 @@ enum { |
| 141 | + DEVCONF_NDISC_TCLASS, |
| 142 | + DEVCONF_RPL_SEG_ENABLED, |
| 143 | + DEVCONF_ACCEPT_UNTRACKED_NA, |
| 144 | ++ DEVCONF_RA_DEFRTR_METRIC, |
| 145 | + DEVCONF_MAX |
| 146 | + }; |
| 147 | + |
| 148 | +diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h |
| 149 | +index 09dabb76a113..6a3b194c50fe 100644 |
| 150 | +--- a/include/uapi/linux/sysctl.h |
| 151 | ++++ b/include/uapi/linux/sysctl.h |
| 152 | +@@ -572,6 +572,7 @@ enum { |
| 153 | + NET_IPV6_ACCEPT_SOURCE_ROUTE=25, |
| 154 | + NET_IPV6_ACCEPT_RA_FROM_LOCAL=26, |
| 155 | + NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27, |
| 156 | ++ NET_IPV6_RA_DEFRTR_METRIC=28, |
| 157 | + __NET_IPV6_MAX |
| 158 | + }; |
| 159 | + |
| 160 | +diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c |
| 161 | +index 038708de6ef9..cac1e63dac54 100644 |
| 162 | +--- a/net/ipv6/addrconf.c |
| 163 | ++++ b/net/ipv6/addrconf.c |
| 164 | +@@ -205,6 +205,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { |
| 165 | + .max_desync_factor = MAX_DESYNC_FACTOR, |
| 166 | + .max_addresses = IPV6_MAX_ADDRESSES, |
| 167 | + .accept_ra_defrtr = 1, |
| 168 | ++ .ra_defrtr_metric = IP6_RT_PRIO_USER, |
| 169 | + .accept_ra_from_local = 0, |
| 170 | + .accept_ra_min_hop_limit= 1, |
| 171 | + .accept_ra_pinfo = 1, |
| 172 | +@@ -260,6 +261,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { |
| 173 | + .max_desync_factor = MAX_DESYNC_FACTOR, |
| 174 | + .max_addresses = IPV6_MAX_ADDRESSES, |
| 175 | + .accept_ra_defrtr = 1, |
| 176 | ++ .ra_defrtr_metric = IP6_RT_PRIO_USER, |
| 177 | + .accept_ra_from_local = 0, |
| 178 | + .accept_ra_min_hop_limit= 1, |
| 179 | + .accept_ra_pinfo = 1, |
| 180 | +@@ -5512,6 +5514,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, |
| 181 | + array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; |
| 182 | + array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; |
| 183 | + array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr; |
| 184 | ++ array[DEVCONF_RA_DEFRTR_METRIC] = cnf->ra_defrtr_metric; |
| 185 | + array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit; |
| 186 | + array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; |
| 187 | + #ifdef CONFIG_IPV6_ROUTER_PREF |
| 188 | +@@ -6705,6 +6708,14 @@ static const struct ctl_table addrconf_sysctl[] = { |
| 189 | + .mode = 0644, |
| 190 | + .proc_handler = proc_dointvec, |
| 191 | + }, |
| 192 | ++ { |
| 193 | ++ .procname = "ra_defrtr_metric", |
| 194 | ++ .data = &ipv6_devconf.ra_defrtr_metric, |
| 195 | ++ .maxlen = sizeof(u32), |
| 196 | ++ .mode = 0644, |
| 197 | ++ .proc_handler = proc_douintvec_minmax, |
| 198 | ++ .extra1 = (void *)SYSCTL_ONE, |
| 199 | ++ }, |
| 200 | + { |
| 201 | + .procname = "accept_ra_min_hop_limit", |
| 202 | + .data = &ipv6_devconf.accept_ra_min_hop_limit, |
| 203 | +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c |
| 204 | +index f236ed938efd..85cd34710a04 100644 |
| 205 | +--- a/net/ipv6/ndisc.c |
| 206 | ++++ b/net/ipv6/ndisc.c |
| 207 | +@@ -1199,6 +1199,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) |
| 208 | + struct neighbour *neigh = NULL; |
| 209 | + struct inet6_dev *in6_dev; |
| 210 | + struct fib6_info *rt = NULL; |
| 211 | ++ u32 defrtr_usr_metric; |
| 212 | + struct net *net; |
| 213 | + int lifetime; |
| 214 | + struct ndisc_options ndopts; |
| 215 | +@@ -1329,18 +1330,21 @@ static void ndisc_router_discovery(struct sk_buff *skb) |
| 216 | + return; |
| 217 | + } |
| 218 | + } |
| 219 | +- if (rt && lifetime == 0) { |
| 220 | ++ /* Set default route metric as specified by user */ |
| 221 | ++ defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric; |
| 222 | ++ /* delete the route if lifetime is 0 or if metric needs change */ |
| 223 | ++ if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) { |
| 224 | + ip6_del_rt(net, rt, false); |
| 225 | + rt = NULL; |
| 226 | + } |
| 227 | + |
| 228 | +- ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, for dev: %s\n", |
| 229 | +- rt, lifetime, skb->dev->name); |
| 230 | ++ ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, metric: %d, for dev: %s\n", |
| 231 | ++ rt, lifetime, defrtr_usr_metric, skb->dev->name); |
| 232 | + if (!rt && lifetime) { |
| 233 | + ND_PRINTK(3, info, "RA: adding default router\n"); |
| 234 | + |
| 235 | + rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr, |
| 236 | +- skb->dev, pref); |
| 237 | ++ skb->dev, pref, defrtr_usr_metric); |
| 238 | + if (!rt) { |
| 239 | + ND_PRINTK(0, err, |
| 240 | + "RA: %s failed to add default route\n", |
| 241 | +diff --git a/net/ipv6/route.c b/net/ipv6/route.c |
| 242 | +index cdf215442d37..2a32cc7db014 100644 |
| 243 | +--- a/net/ipv6/route.c |
| 244 | ++++ b/net/ipv6/route.c |
| 245 | +@@ -4269,11 +4269,12 @@ struct fib6_info *rt6_get_dflt_router(struct net *net, |
| 246 | + struct fib6_info *rt6_add_dflt_router(struct net *net, |
| 247 | + const struct in6_addr *gwaddr, |
| 248 | + struct net_device *dev, |
| 249 | +- unsigned int pref) |
| 250 | ++ unsigned int pref, |
| 251 | ++ u32 defrtr_usr_metric) |
| 252 | + { |
| 253 | + struct fib6_config cfg = { |
| 254 | + .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, |
| 255 | +- .fc_metric = IP6_RT_PRIO_USER, |
| 256 | ++ .fc_metric = defrtr_usr_metric, |
| 257 | + .fc_ifindex = dev->ifindex, |
| 258 | + .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | |
| 259 | + RTF_UP | RTF_EXPIRES | RTF_PREF(pref), |
| 260 | +-- |
| 261 | +2.25.1 |
| 262 | + |
0 commit comments