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: Factorise ip6_route_multipath_add().

We will get rid of RTNL from RTM_NEWROUTE and SIOCADDRT and rely
on RCU to guarantee dev and nexthop lifetime.

Then, the RCU section will start before ip6_route_info_create_nh()
in ip6_route_multipath_add(), but ip6_route_info_create() is called
in the same loop and will sleep.

Let's split the loop into ip6_route_mpath_info_create() and
ip6_route_mpath_info_create_nh().

Note that ip6_route_info_append() is now integrated into
ip6_route_mpath_info_create_nh() because we need to call different
free functions for nexthops that passed ip6_route_info_create_nh().

In case of failure, the remaining nexthops that ip6_route_info_create_nh()
has not been called for will be freed by ip6_route_mpath_info_cleanup().

OTOH, if a nexthop passes ip6_route_info_create_nh(), it will be linked
to a local temporary list, which will be spliced back to rt6_nh_list.
In case of failure, these nexthops will be released by fib6_info_release()
in ip6_route_multipath_add().

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250418000443.43734-12-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Kuniyuki Iwashima and committed by
Paolo Abeni
71c0efb6 5a1ccff5

+132 -77
+132 -77
net/ipv6/route.c
··· 5316 5316 struct fib6_info *fib6_info; 5317 5317 struct fib6_config r_cfg; 5318 5318 struct list_head list; 5319 + int weight; 5319 5320 }; 5320 5321 5321 - static int ip6_route_info_append(struct list_head *rt6_nh_list, 5322 - struct fib6_info *rt, 5323 - struct fib6_config *r_cfg) 5322 + static void ip6_route_mpath_info_cleanup(struct list_head *rt6_nh_list) 5324 5323 { 5325 - struct rt6_nh *nh; 5326 - int err = -EEXIST; 5324 + struct rt6_nh *nh, *nh_next; 5327 5325 5328 - list_for_each_entry(nh, rt6_nh_list, list) { 5329 - /* check if fib6_info already exists */ 5330 - if (rt6_duplicate_nexthop(nh->fib6_info, rt)) 5331 - return err; 5326 + list_for_each_entry_safe(nh, nh_next, rt6_nh_list, list) { 5327 + struct fib6_info *rt = nh->fib6_info; 5328 + 5329 + if (rt) { 5330 + free_percpu(rt->fib6_nh->nh_common.nhc_pcpu_rth_output); 5331 + free_percpu(rt->fib6_nh->rt6i_pcpu); 5332 + ip_fib_metrics_put(rt->fib6_metrics); 5333 + kfree(rt); 5334 + } 5335 + 5336 + list_del(&nh->list); 5337 + kfree(nh); 5338 + } 5339 + } 5340 + 5341 + static int ip6_route_mpath_info_create(struct list_head *rt6_nh_list, 5342 + struct fib6_config *cfg, 5343 + struct netlink_ext_ack *extack) 5344 + { 5345 + struct rtnexthop *rtnh; 5346 + int remaining; 5347 + int err; 5348 + 5349 + remaining = cfg->fc_mp_len; 5350 + rtnh = (struct rtnexthop *)cfg->fc_mp; 5351 + 5352 + /* Parse a Multipath Entry and build a list (rt6_nh_list) of 5353 + * fib6_info structs per nexthop 5354 + */ 5355 + while (rtnh_ok(rtnh, remaining)) { 5356 + struct fib6_config r_cfg; 5357 + struct fib6_info *rt; 5358 + struct rt6_nh *nh; 5359 + int attrlen; 5360 + 5361 + nh = kzalloc(sizeof(*nh), GFP_KERNEL); 5362 + if (!nh) { 5363 + err = -ENOMEM; 5364 + goto err; 5365 + } 5366 + 5367 + list_add_tail(&nh->list, rt6_nh_list); 5368 + 5369 + memcpy(&r_cfg, cfg, sizeof(*cfg)); 5370 + if (rtnh->rtnh_ifindex) 5371 + r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 5372 + 5373 + attrlen = rtnh_attrlen(rtnh); 5374 + if (attrlen > 0) { 5375 + struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 5376 + 5377 + nla = nla_find(attrs, attrlen, RTA_GATEWAY); 5378 + if (nla) { 5379 + r_cfg.fc_gateway = nla_get_in6_addr(nla); 5380 + r_cfg.fc_flags |= RTF_GATEWAY; 5381 + } 5382 + 5383 + r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 5384 + nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 5385 + if (nla) 5386 + r_cfg.fc_encap_type = nla_get_u16(nla); 5387 + } 5388 + 5389 + r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK); 5390 + 5391 + rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack); 5392 + if (IS_ERR(rt)) { 5393 + err = PTR_ERR(rt); 5394 + goto err; 5395 + } 5396 + 5397 + nh->fib6_info = rt; 5398 + nh->weight = rtnh->rtnh_hops + 1; 5399 + memcpy(&nh->r_cfg, &r_cfg, sizeof(r_cfg)); 5400 + 5401 + rtnh = rtnh_next(rtnh, &remaining); 5332 5402 } 5333 5403 5334 - nh = kzalloc(sizeof(*nh), GFP_KERNEL); 5335 - if (!nh) 5336 - return -ENOMEM; 5337 - nh->fib6_info = rt; 5338 - memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 5339 - list_add_tail(&nh->list, rt6_nh_list); 5340 - 5341 5404 return 0; 5405 + err: 5406 + ip6_route_mpath_info_cleanup(rt6_nh_list); 5407 + return err; 5408 + } 5409 + 5410 + static int ip6_route_mpath_info_create_nh(struct list_head *rt6_nh_list, 5411 + struct netlink_ext_ack *extack) 5412 + { 5413 + struct rt6_nh *nh, *nh_next, *nh_tmp; 5414 + LIST_HEAD(tmp); 5415 + int err; 5416 + 5417 + list_for_each_entry_safe(nh, nh_next, rt6_nh_list, list) { 5418 + struct fib6_info *rt = nh->fib6_info; 5419 + 5420 + err = ip6_route_info_create_nh(rt, &nh->r_cfg, extack); 5421 + if (err) { 5422 + nh->fib6_info = NULL; 5423 + goto err; 5424 + } 5425 + 5426 + rt->fib6_nh->fib_nh_weight = nh->weight; 5427 + 5428 + list_move_tail(&nh->list, &tmp); 5429 + 5430 + list_for_each_entry(nh_tmp, rt6_nh_list, list) { 5431 + /* check if fib6_info already exists */ 5432 + if (rt6_duplicate_nexthop(nh_tmp->fib6_info, rt)) { 5433 + err = -EEXIST; 5434 + goto err; 5435 + } 5436 + } 5437 + } 5438 + out: 5439 + list_splice(&tmp, rt6_nh_list); 5440 + return err; 5441 + err: 5442 + ip6_route_mpath_info_cleanup(rt6_nh_list); 5443 + goto out; 5342 5444 } 5343 5445 5344 5446 static void ip6_route_mpath_notify(struct fib6_info *rt, ··· 5499 5397 { 5500 5398 struct fib6_info *rt_notif = NULL, *rt_last = NULL; 5501 5399 struct nl_info *info = &cfg->fc_nlinfo; 5502 - struct fib6_config r_cfg; 5503 - struct rtnexthop *rtnh; 5504 - struct fib6_info *rt; 5505 - struct rt6_nh *err_nh; 5506 5400 struct rt6_nh *nh, *nh_safe; 5507 - __u16 nlflags; 5508 - int remaining; 5509 - int attrlen; 5510 - int err = 1; 5511 - int nhn = 0; 5512 - int replace = (cfg->fc_nlinfo.nlh && 5513 - (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 5514 5401 LIST_HEAD(rt6_nh_list); 5402 + struct rt6_nh *err_nh; 5403 + __u16 nlflags; 5404 + int nhn = 0; 5405 + int replace; 5406 + int err; 5407 + 5408 + replace = (cfg->fc_nlinfo.nlh && 5409 + (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 5515 5410 5516 5411 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE; 5517 5412 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND) 5518 5413 nlflags |= NLM_F_APPEND; 5519 5414 5520 - remaining = cfg->fc_mp_len; 5521 - rtnh = (struct rtnexthop *)cfg->fc_mp; 5415 + err = ip6_route_mpath_info_create(&rt6_nh_list, cfg, extack); 5416 + if (err) 5417 + return err; 5522 5418 5523 - /* Parse a Multipath Entry and build a list (rt6_nh_list) of 5524 - * fib6_info structs per nexthop 5525 - */ 5526 - while (rtnh_ok(rtnh, remaining)) { 5527 - memcpy(&r_cfg, cfg, sizeof(*cfg)); 5528 - if (rtnh->rtnh_ifindex) 5529 - r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 5530 - 5531 - attrlen = rtnh_attrlen(rtnh); 5532 - if (attrlen > 0) { 5533 - struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 5534 - 5535 - nla = nla_find(attrs, attrlen, RTA_GATEWAY); 5536 - if (nla) { 5537 - r_cfg.fc_gateway = nla_get_in6_addr(nla); 5538 - r_cfg.fc_flags |= RTF_GATEWAY; 5539 - } 5540 - 5541 - r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 5542 - nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 5543 - if (nla) 5544 - r_cfg.fc_encap_type = nla_get_u16(nla); 5545 - } 5546 - 5547 - r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK); 5548 - rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack); 5549 - if (IS_ERR(rt)) { 5550 - err = PTR_ERR(rt); 5551 - rt = NULL; 5552 - goto cleanup; 5553 - } 5554 - 5555 - err = ip6_route_info_create_nh(rt, &r_cfg, extack); 5556 - if (err) { 5557 - rt = NULL; 5558 - goto cleanup; 5559 - } 5560 - 5561 - rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1; 5562 - 5563 - err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); 5564 - if (err) { 5565 - fib6_info_release(rt); 5566 - goto cleanup; 5567 - } 5568 - 5569 - rtnh = rtnh_next(rtnh, &remaining); 5570 - } 5419 + err = ip6_route_mpath_info_create_nh(&rt6_nh_list, extack); 5420 + if (err) 5421 + goto cleanup; 5571 5422 5572 5423 /* for add and replace send one notification with all nexthops. 5573 5424 * Skip the notification in fib6_add_rt2node and send one with