Skip to content

Commit 6b2e04b

Browse files
praveen-likuba-moo
authored andcommitted
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 <[email protected]> Signed-off-by: Zhenggen Xu <[email protected]> Reviewed-by: David Ahern <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 6626a02 commit 6b2e04b

File tree

8 files changed

+37
-7
lines changed

8 files changed

+37
-7
lines changed

Documentation/networking/ip-sysctl.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,16 @@ accept_ra_defrtr - BOOLEAN
18711871
- enabled if accept_ra is enabled.
18721872
- disabled if accept_ra is disabled.
18731873

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+
18741884
accept_ra_from_local - BOOLEAN
18751885
Accept RA with source-address that is found on local machine
18761886
if the RA is otherwise proper and able to be accepted.

include/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct ipv6_devconf {
3131
__s32 max_desync_factor;
3232
__s32 max_addresses;
3333
__s32 accept_ra_defrtr;
34+
__u32 ra_defrtr_metric;
3435
__s32 accept_ra_min_hop_limit;
3536
__s32 accept_ra_pinfo;
3637
__s32 ignore_routes_with_linkdown;

include/net/ip6_route.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
174174
struct net_device *dev);
175175
struct fib6_info *rt6_add_dflt_router(struct net *net,
176176
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);
178179

179180
void rt6_purge_dflt_routers(struct net *net);
180181

include/uapi/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ enum {
189189
DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
190190
DEVCONF_NDISC_TCLASS,
191191
DEVCONF_RPL_SEG_ENABLED,
192+
DEVCONF_RA_DEFRTR_METRIC,
192193
DEVCONF_MAX
193194
};
194195

include/uapi/linux/sysctl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ enum {
571571
NET_IPV6_ACCEPT_SOURCE_ROUTE=25,
572572
NET_IPV6_ACCEPT_RA_FROM_LOCAL=26,
573573
NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
574+
NET_IPV6_RA_DEFRTR_METRIC=28,
574575
__NET_IPV6_MAX
575576
};
576577

net/ipv6/addrconf.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
205205
.max_desync_factor = MAX_DESYNC_FACTOR,
206206
.max_addresses = IPV6_MAX_ADDRESSES,
207207
.accept_ra_defrtr = 1,
208+
.ra_defrtr_metric = IP6_RT_PRIO_USER,
208209
.accept_ra_from_local = 0,
209210
.accept_ra_min_hop_limit= 1,
210211
.accept_ra_pinfo = 1,
@@ -260,6 +261,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
260261
.max_desync_factor = MAX_DESYNC_FACTOR,
261262
.max_addresses = IPV6_MAX_ADDRESSES,
262263
.accept_ra_defrtr = 1,
264+
.ra_defrtr_metric = IP6_RT_PRIO_USER,
263265
.accept_ra_from_local = 0,
264266
.accept_ra_min_hop_limit= 1,
265267
.accept_ra_pinfo = 1,
@@ -5476,6 +5478,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
54765478
array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
54775479
array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
54785480
array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
5481+
array[DEVCONF_RA_DEFRTR_METRIC] = cnf->ra_defrtr_metric;
54795482
array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit;
54805483
array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
54815484
#ifdef CONFIG_IPV6_ROUTER_PREF
@@ -6668,6 +6671,14 @@ static const struct ctl_table addrconf_sysctl[] = {
66686671
.mode = 0644,
66696672
.proc_handler = proc_dointvec,
66706673
},
6674+
{
6675+
.procname = "ra_defrtr_metric",
6676+
.data = &ipv6_devconf.ra_defrtr_metric,
6677+
.maxlen = sizeof(u32),
6678+
.mode = 0644,
6679+
.proc_handler = proc_douintvec_minmax,
6680+
.extra1 = (void *)SYSCTL_ONE,
6681+
},
66716682
{
66726683
.procname = "accept_ra_min_hop_limit",
66736684
.data = &ipv6_devconf.accept_ra_min_hop_limit,

net/ipv6/ndisc.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
11731173
struct neighbour *neigh = NULL;
11741174
struct inet6_dev *in6_dev;
11751175
struct fib6_info *rt = NULL;
1176+
u32 defrtr_usr_metric;
11761177
struct net *net;
11771178
int lifetime;
11781179
struct ndisc_options ndopts;
@@ -1303,18 +1304,21 @@ static void ndisc_router_discovery(struct sk_buff *skb)
13031304
return;
13041305
}
13051306
}
1306-
if (rt && lifetime == 0) {
1307+
/* Set default route metric as specified by user */
1308+
defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric;
1309+
/* delete the route if lifetime is 0 or if metric needs change */
1310+
if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) {
13071311
ip6_del_rt(net, rt, false);
13081312
rt = NULL;
13091313
}
13101314

1311-
ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, for dev: %s\n",
1312-
rt, lifetime, skb->dev->name);
1315+
ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, metric: %d, for dev: %s\n",
1316+
rt, lifetime, defrtr_usr_metric, skb->dev->name);
13131317
if (!rt && lifetime) {
13141318
ND_PRINTK(3, info, "RA: adding default router\n");
13151319

13161320
rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr,
1317-
skb->dev, pref);
1321+
skb->dev, pref, defrtr_usr_metric);
13181322
if (!rt) {
13191323
ND_PRINTK(0, err,
13201324
"RA: %s failed to add default route\n",

net/ipv6/route.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4252,11 +4252,12 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
42524252
struct fib6_info *rt6_add_dflt_router(struct net *net,
42534253
const struct in6_addr *gwaddr,
42544254
struct net_device *dev,
4255-
unsigned int pref)
4255+
unsigned int pref,
4256+
u32 defrtr_usr_metric)
42564257
{
42574258
struct fib6_config cfg = {
42584259
.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
4259-
.fc_metric = IP6_RT_PRIO_USER,
4260+
.fc_metric = defrtr_usr_metric,
42604261
.fc_ifindex = dev->ifindex,
42614262
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
42624263
RTF_UP | RTF_EXPIRES | RTF_PREF(pref),

0 commit comments

Comments
 (0)