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.

ipv6: Get rid of RTNL for SIOCDELRT and RTM_DELROUTE.

Basically, removing an IPv6 route does not require RTNL because
the IPv6 routing tables are protected by per table lock.

inet6_rtm_delroute() calls nexthop_find_by_id() to check if the
nexthop specified by RTA_NH_ID exists. nexthop uses rbtree and
the top-down walk can be safely performed under RCU.

ip6_route_del() already relies on RCU and the table lock, but we
need to extend the RCU critical section a bit more to cover
__ip6_del_rt(). For example, nexthop_for_each_fib6_nh() and
inet6_rt_notify() needs RCU.

Let's call nexthop_find_by_id() and __ip6_del_rt() under RCU and
get rid of RTNL from inet6_rtm_delroute() and SIOCDELRT.

Even if the nexthop is removed after rcu_read_unlock() in
inet6_rtm_delroute(), __remove_nexthop_fib() cleans up the routes
tied to the nexthop, and ip6_route_del() returns -ESRCH. So the
request was at least valid as of nexthop_find_by_id(), and it's just
a matter of timing.

Note that we need to pass false to lwtunnel_valid_encap_type_attr().
The following patches also use the newroute bool.

Note also that fib6_get_table() does not require RCU because once
allocated fib6_table is not freed until netns dismantle. I will
post a follow-up series to convert such callers to RCU-lockless
version. [0]

Link: https://lore.kernel.org/netdev/20250417174557.65721-1-kuniyu@amazon.com/ #[0]
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250418000443.43734-3-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Kuniyuki Iwashima and committed by
Paolo Abeni
bd11ff42 4cb4861d

+28 -20
+28 -20
net/ipv6/route.c
··· 4125 4125 if (rt->nh) { 4126 4126 if (!fib6_info_hold_safe(rt)) 4127 4127 continue; 4128 - rcu_read_unlock(); 4129 4128 4130 - return __ip6_del_rt(rt, &cfg->fc_nlinfo); 4129 + err = __ip6_del_rt(rt, &cfg->fc_nlinfo); 4130 + break; 4131 4131 } 4132 4132 if (cfg->fc_nh_id) 4133 4133 continue; ··· 4142 4142 continue; 4143 4143 if (!fib6_info_hold_safe(rt)) 4144 4144 continue; 4145 - rcu_read_unlock(); 4146 4145 4147 4146 /* if gateway was specified only delete the one hop */ 4148 4147 if (cfg->fc_flags & RTF_GATEWAY) 4149 - return __ip6_del_rt(rt, &cfg->fc_nlinfo); 4150 - 4151 - return __ip6_del_rt_siblings(rt, cfg); 4148 + err = __ip6_del_rt(rt, &cfg->fc_nlinfo); 4149 + else 4150 + err = __ip6_del_rt_siblings(rt, cfg); 4151 + break; 4152 4152 } 4153 4153 } 4154 4154 rcu_read_unlock(); ··· 4517 4517 4518 4518 rtmsg_to_fib6_config(net, rtmsg, &cfg); 4519 4519 4520 - rtnl_lock(); 4521 4520 switch (cmd) { 4522 4521 case SIOCADDRT: 4522 + rtnl_lock(); 4523 4523 /* Only do the default setting of fc_metric in route adding */ 4524 4524 if (cfg.fc_metric == 0) 4525 4525 cfg.fc_metric = IP6_RT_PRIO_USER; 4526 4526 err = ip6_route_add(&cfg, GFP_KERNEL, NULL); 4527 + rtnl_unlock(); 4527 4528 break; 4528 4529 case SIOCDELRT: 4529 4530 err = ip6_route_del(&cfg, NULL); 4530 4531 break; 4531 4532 } 4532 - rtnl_unlock(); 4533 + 4533 4534 return err; 4534 4535 } 4535 4536 ··· 5053 5052 }; 5054 5053 5055 5054 static int rtm_to_fib6_multipath_config(struct fib6_config *cfg, 5056 - struct netlink_ext_ack *extack) 5055 + struct netlink_ext_ack *extack, 5056 + bool newroute) 5057 5057 { 5058 5058 struct rtnexthop *rtnh; 5059 5059 int remaining; ··· 5088 5086 } while (rtnh_ok(rtnh, remaining)); 5089 5087 5090 5088 return lwtunnel_valid_encap_type_attr(cfg->fc_mp, cfg->fc_mp_len, 5091 - extack, true); 5089 + extack, newroute); 5092 5090 } 5093 5091 5094 5092 static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 5095 5093 struct fib6_config *cfg, 5096 5094 struct netlink_ext_ack *extack) 5097 5095 { 5098 - struct rtmsg *rtm; 5096 + bool newroute = nlh->nlmsg_type == RTM_NEWROUTE; 5099 5097 struct nlattr *tb[RTA_MAX+1]; 5098 + struct rtmsg *rtm; 5100 5099 unsigned int pref; 5101 5100 int err; 5102 5101 ··· 5206 5203 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 5207 5204 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 5208 5205 5209 - err = rtm_to_fib6_multipath_config(cfg, extack); 5206 + err = rtm_to_fib6_multipath_config(cfg, extack, newroute); 5210 5207 if (err < 0) 5211 5208 goto errout; 5212 5209 } ··· 5226 5223 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 5227 5224 5228 5225 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, 5229 - extack, true); 5226 + extack, newroute); 5230 5227 if (err < 0) 5231 5228 goto errout; 5232 5229 } ··· 5549 5546 if (err < 0) 5550 5547 return err; 5551 5548 5552 - if (cfg.fc_nh_id && 5553 - !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) { 5554 - NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 5555 - return -EINVAL; 5549 + if (cfg.fc_nh_id) { 5550 + rcu_read_lock(); 5551 + err = !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id); 5552 + rcu_read_unlock(); 5553 + 5554 + if (err) { 5555 + NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 5556 + return -EINVAL; 5557 + } 5556 5558 } 5557 5559 5558 - if (cfg.fc_mp) 5560 + if (cfg.fc_mp) { 5559 5561 return ip6_route_multipath_del(&cfg, extack); 5560 - else { 5562 + } else { 5561 5563 cfg.fc_delete_all_nh = 1; 5562 5564 return ip6_route_del(&cfg, extack); 5563 5565 } ··· 6774 6766 {.owner = THIS_MODULE, .protocol = PF_INET6, .msgtype = RTM_NEWROUTE, 6775 6767 .doit = inet6_rtm_newroute}, 6776 6768 {.owner = THIS_MODULE, .protocol = PF_INET6, .msgtype = RTM_DELROUTE, 6777 - .doit = inet6_rtm_delroute}, 6769 + .doit = inet6_rtm_delroute, .flags = RTNL_FLAG_DOIT_UNLOCKED}, 6778 6770 {.owner = THIS_MODULE, .protocol = PF_INET6, .msgtype = RTM_GETROUTE, 6779 6771 .doit = inet6_rtm_getroute, .flags = RTNL_FLAG_DOIT_UNLOCKED}, 6780 6772 };