Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: allow user to set metric on default route learned via Router Advertisement

For IPv4, default route is learned via DHCPv4 and user is allowed to change
metric using config etc/network/interfaces. But for IPv6, default route can
be learned via RA, for which, currently a fixed metric value 1024 is used.

Ideally, user should be able to configure metric on default route for IPv6
similar to IPv4. This patch adds sysctl for the same.

Logs:

For IPv4:

Config in etc/network/interfaces:
auto eth0
iface eth0 inet dhcp
metric 4261413864

IPv4 Kernel Route Table:
$ ip route list
default via 172.21.47.1 dev eth0 metric 4261413864

FRR Table, if a static route is configured:
[In real scenario, it is useful to prefer BGP learned default route over DHCPv4 default route.]
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
> - selected route, * - FIB route

S>* 0.0.0.0/0 [20/0] is directly connected, eth0, 00:00:03
K 0.0.0.0/0 [254/1000] via 172.21.47.1, eth0, 6d08h51m

i.e. User can prefer Default Router learned via Routing Protocol in IPv4.
Similar behavior is not possible for IPv6, without this fix.

After fix [for IPv6]:
sudo sysctl -w net.ipv6.conf.eth0.net.ipv6.conf.eth0.ra_defrtr_metric=1996489705

IP monitor: [When IPv6 RA is received]
default via fe80::xx16:xxxx:feb3:ce8e dev eth0 proto ra metric 1996489705 pref high

Kernel IPv6 routing table
$ ip -6 route list
default via fe80::be16:65ff:feb3:ce8e dev eth0 proto ra metric 1996489705 expires 21sec hoplimit 64 pref high

FRR Table, if a static route is configured:
[In real scenario, it is useful to prefer BGP learned default route over IPv6 RA default route.]
Codes: K - kernel route, C - connected, S - static, R - RIPng,
O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
> - selected route, * - FIB route

S>* ::/0 [20/0] is directly connected, eth0, 00:00:06
K ::/0 [119/1001] via fe80::xx16:xxxx:feb3:ce8e, eth0, 6d07h43m

If the metric is changed later, the effect will be seen only when next IPv6
RA is received, because the default route must be fully controlled by RA msg.
Below metric is changed from 1996489705 to 1996489704.

$ sudo sysctl -w net.ipv6.conf.eth0.ra_defrtr_metric=1996489704
net.ipv6.conf.eth0.ra_defrtr_metric = 1996489704

IP monitor:
[On next IPv6 RA msg, Kernel deletes prev route and installs new route with updated metric]

Deleted default via fe80::xx16:xxxx:feb3:ce8e dev eth0 proto ra metric 1996489705 expires 3sec hoplimit 64 pref high
default via fe80::xx16:xxxx:feb3:ce8e dev eth0 proto ra metric 1996489704 pref high

Signed-off-by: Praveen Chaudhary <pchaudhary@linkedin.com>
Signed-off-by: Zhenggen Xu <zxu@linkedin.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://lore.kernel.org/r/20210125214430.24079-1-pchaudhary@linkedin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Praveen Chaudhary and committed by
Jakub Kicinski
6b2e04bc 6626a026

+37 -7
+10
Documentation/networking/ip-sysctl.rst
··· 1871 1871 - enabled if accept_ra is enabled. 1872 1872 - disabled if accept_ra is disabled. 1873 1873 1874 + ra_defrtr_metric - UNSIGNED INTEGER 1875 + Route metric for default route learned in Router Advertisement. This value 1876 + will be assigned as metric for the default route learned via IPv6 Router 1877 + Advertisement. Takes affect only if accept_ra_defrtr is enabled. 1878 + 1879 + Possible values: 1880 + 1 to 0xFFFFFFFF 1881 + 1882 + Default: IP6_RT_PRIO_USER i.e. 1024. 1883 + 1874 1884 accept_ra_from_local - BOOLEAN 1875 1885 Accept RA with source-address that is found on local machine 1876 1886 if the RA is otherwise proper and able to be accepted.
+1
include/linux/ipv6.h
··· 31 31 __s32 max_desync_factor; 32 32 __s32 max_addresses; 33 33 __s32 accept_ra_defrtr; 34 + __u32 ra_defrtr_metric; 34 35 __s32 accept_ra_min_hop_limit; 35 36 __s32 accept_ra_pinfo; 36 37 __s32 ignore_routes_with_linkdown;
+2 -1
include/net/ip6_route.h
··· 174 174 struct net_device *dev); 175 175 struct fib6_info *rt6_add_dflt_router(struct net *net, 176 176 const struct in6_addr *gwaddr, 177 - struct net_device *dev, unsigned int pref); 177 + struct net_device *dev, unsigned int pref, 178 + u32 defrtr_usr_metric); 178 179 179 180 void rt6_purge_dflt_routers(struct net *net); 180 181
+1
include/uapi/linux/ipv6.h
··· 189 189 DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 190 190 DEVCONF_NDISC_TCLASS, 191 191 DEVCONF_RPL_SEG_ENABLED, 192 + DEVCONF_RA_DEFRTR_METRIC, 192 193 DEVCONF_MAX 193 194 }; 194 195
+1
include/uapi/linux/sysctl.h
··· 571 571 NET_IPV6_ACCEPT_SOURCE_ROUTE=25, 572 572 NET_IPV6_ACCEPT_RA_FROM_LOCAL=26, 573 573 NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27, 574 + NET_IPV6_RA_DEFRTR_METRIC=28, 574 575 __NET_IPV6_MAX 575 576 }; 576 577
+11
net/ipv6/addrconf.c
··· 205 205 .max_desync_factor = MAX_DESYNC_FACTOR, 206 206 .max_addresses = IPV6_MAX_ADDRESSES, 207 207 .accept_ra_defrtr = 1, 208 + .ra_defrtr_metric = IP6_RT_PRIO_USER, 208 209 .accept_ra_from_local = 0, 209 210 .accept_ra_min_hop_limit= 1, 210 211 .accept_ra_pinfo = 1, ··· 261 260 .max_desync_factor = MAX_DESYNC_FACTOR, 262 261 .max_addresses = IPV6_MAX_ADDRESSES, 263 262 .accept_ra_defrtr = 1, 263 + .ra_defrtr_metric = IP6_RT_PRIO_USER, 264 264 .accept_ra_from_local = 0, 265 265 .accept_ra_min_hop_limit= 1, 266 266 .accept_ra_pinfo = 1, ··· 5478 5476 array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; 5479 5477 array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; 5480 5478 array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr; 5479 + array[DEVCONF_RA_DEFRTR_METRIC] = cnf->ra_defrtr_metric; 5481 5480 array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit; 5482 5481 array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; 5483 5482 #ifdef CONFIG_IPV6_ROUTER_PREF ··· 6670 6667 .maxlen = sizeof(int), 6671 6668 .mode = 0644, 6672 6669 .proc_handler = proc_dointvec, 6670 + }, 6671 + { 6672 + .procname = "ra_defrtr_metric", 6673 + .data = &ipv6_devconf.ra_defrtr_metric, 6674 + .maxlen = sizeof(u32), 6675 + .mode = 0644, 6676 + .proc_handler = proc_douintvec_minmax, 6677 + .extra1 = (void *)SYSCTL_ONE, 6673 6678 }, 6674 6679 { 6675 6680 .procname = "accept_ra_min_hop_limit",
+8 -4
net/ipv6/ndisc.c
··· 1173 1173 struct neighbour *neigh = NULL; 1174 1174 struct inet6_dev *in6_dev; 1175 1175 struct fib6_info *rt = NULL; 1176 + u32 defrtr_usr_metric; 1176 1177 struct net *net; 1177 1178 int lifetime; 1178 1179 struct ndisc_options ndopts; ··· 1304 1303 return; 1305 1304 } 1306 1305 } 1307 - if (rt && lifetime == 0) { 1306 + /* Set default route metric as specified by user */ 1307 + defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric; 1308 + /* delete the route if lifetime is 0 or if metric needs change */ 1309 + if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) { 1308 1310 ip6_del_rt(net, rt, false); 1309 1311 rt = NULL; 1310 1312 } 1311 1313 1312 - ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, for dev: %s\n", 1313 - rt, lifetime, skb->dev->name); 1314 + ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, metric: %d, for dev: %s\n", 1315 + rt, lifetime, defrtr_usr_metric, skb->dev->name); 1314 1316 if (!rt && lifetime) { 1315 1317 ND_PRINTK(3, info, "RA: adding default router\n"); 1316 1318 1317 1319 rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr, 1318 - skb->dev, pref); 1320 + skb->dev, pref, defrtr_usr_metric); 1319 1321 if (!rt) { 1320 1322 ND_PRINTK(0, err, 1321 1323 "RA: %s failed to add default route\n",
+3 -2
net/ipv6/route.c
··· 4252 4252 struct fib6_info *rt6_add_dflt_router(struct net *net, 4253 4253 const struct in6_addr *gwaddr, 4254 4254 struct net_device *dev, 4255 - unsigned int pref) 4255 + unsigned int pref, 4256 + u32 defrtr_usr_metric) 4256 4257 { 4257 4258 struct fib6_config cfg = { 4258 4259 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 4259 - .fc_metric = IP6_RT_PRIO_USER, 4260 + .fc_metric = defrtr_usr_metric, 4260 4261 .fc_ifindex = dev->ifindex, 4261 4262 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 4262 4263 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),