ipv6: Move nexthop_find_by_id() after fib6_info_alloc().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Fri, 18 Apr 2025 00:03:46 +0000 (17:03 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 24 Apr 2025 07:29:56 +0000 (09:29 +0200)
We will get rid of RTNL from RTM_NEWROUTE and SIOCADDRT.

Then, we must perform two lookups for nexthop and dev under RCU
to guarantee their lifetime.

ip6_route_info_create() calls nexthop_find_by_id() first if
RTA_NH_ID is specified, and then allocates struct fib6_info.

nexthop_find_by_id() must be called under RCU, but we do not want
to use GFP_ATOMIC for memory allocation here, which will be likely
to fail in ip6_route_multipath_add().

Let's move nexthop_find_by_id() after the memory allocation so
that we can later split ip6_route_info_create() into two parts:
the sleepable part and the RCU part.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Link: https://patch.msgid.link/20250418000443.43734-6-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/ipv6/route.c

index 88d2f85ed69dcb411e3f7dca95f986a31d7f5827..f66f90f8f15317d49ca830fe8d0e2ca5eccddf4f 100644 (file)
@@ -3734,24 +3734,11 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
 {
        struct net *net = cfg->fc_nlinfo.nl_net;
        struct fib6_info *rt = NULL;
-       struct nexthop *nh = NULL;
        struct fib6_table *table;
        struct fib6_nh *fib6_nh;
-       int err = -EINVAL;
+       int err = -ENOBUFS;
        int addr_type;
 
-       if (cfg->fc_nh_id) {
-               nh = nexthop_find_by_id(net, cfg->fc_nh_id);
-               if (!nh) {
-                       NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
-                       goto out;
-               }
-               err = fib6_check_nexthop(nh, cfg, extack);
-               if (err)
-                       goto out;
-       }
-
-       err = -ENOBUFS;
        if (cfg->fc_nlinfo.nlh &&
            !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
                table = fib6_get_table(net, cfg->fc_table);
@@ -3767,7 +3754,7 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
                goto out;
 
        err = -ENOMEM;
-       rt = fib6_info_alloc(gfp_flags, !nh);
+       rt = fib6_info_alloc(gfp_flags, !cfg->fc_nh_id);
        if (!rt)
                goto out;
 
@@ -3803,12 +3790,27 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
        ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
        rt->fib6_src.plen = cfg->fc_src_len;
 #endif
-       if (nh) {
+
+       if (cfg->fc_nh_id) {
+               struct nexthop *nh;
+
+               nh = nexthop_find_by_id(net, cfg->fc_nh_id);
+               if (!nh) {
+                       err = -EINVAL;
+                       NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
+                       goto out_free;
+               }
+
+               err = fib6_check_nexthop(nh, cfg, extack);
+               if (err)
+                       goto out_free;
+
                if (!nexthop_get(nh)) {
                        NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
                        err = -ENOENT;
                        goto out_free;
                }
+
                rt->nh = nh;
                fib6_nh = nexthop_fib6_nh(rt->nh);
        } else {