]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ipv6: Validate RTA_GATEWAY of RTA_MULTIPATH in rtm_to_fib6_config().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Fri, 18 Apr 2025 00:03:42 +0000 (17:03 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 24 Apr 2025 07:29:55 +0000 (09:29 +0200)
We will perform RTM_NEWROUTE and RTM_DELROUTE under RCU, and then
we want to perform some validation out of the RCU scope.

When creating / removing an IPv6 route with RTA_MULTIPATH,
inet6_rtm_newroute() / inet6_rtm_delroute() validates RTA_GATEWAY
in each multipath entry.

Let's do that in rtm_to_fib6_config().

Note that now RTM_DELROUTE returns an error for RTA_MULTIPATH with
0 entries, which was accepted but should result in -EINVAL as
RTM_NEWROUTE.

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

index 945857a8bfe3804cce76b40e5fc136682a4973b8..369914a3746137a5ddc95145067236e9c3109692 100644 (file)
@@ -5051,6 +5051,44 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
        [RTA_FLOWLABEL]         = { .type = NLA_BE32 },
 };
 
+static int rtm_to_fib6_multipath_config(struct fib6_config *cfg,
+                                       struct netlink_ext_ack *extack)
+{
+       struct rtnexthop *rtnh;
+       int remaining;
+
+       remaining = cfg->fc_mp_len;
+       rtnh = (struct rtnexthop *)cfg->fc_mp;
+
+       if (!rtnh_ok(rtnh, remaining)) {
+               NL_SET_ERR_MSG(extack, "Invalid nexthop configuration - no valid nexthops");
+               return -EINVAL;
+       }
+
+       do {
+               int attrlen = rtnh_attrlen(rtnh);
+
+               if (attrlen > 0) {
+                       struct nlattr *nla, *attrs;
+
+                       attrs = rtnh_attrs(rtnh);
+                       nla = nla_find(attrs, attrlen, RTA_GATEWAY);
+                       if (nla) {
+                               if (nla_len(nla) < sizeof(cfg->fc_gateway)) {
+                                       NL_SET_ERR_MSG(extack,
+                                                      "Invalid IPv6 address in RTA_GATEWAY");
+                                       return -EINVAL;
+                               }
+                       }
+               }
+
+               rtnh = rtnh_next(rtnh, &remaining);
+       } while (rtnh_ok(rtnh, remaining));
+
+       return lwtunnel_valid_encap_type_attr(cfg->fc_mp, cfg->fc_mp_len,
+                                             extack, true);
+}
+
 static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
                              struct fib6_config *cfg,
                              struct netlink_ext_ack *extack)
@@ -5165,9 +5203,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
                cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
                cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
 
-               err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
-                                                    cfg->fc_mp_len,
-                                                    extack, true);
+               err = rtm_to_fib6_multipath_config(cfg, extack);
                if (err < 0)
                        goto errout;
        }
@@ -5287,19 +5323,6 @@ out:
        return should_notify;
 }
 
-static int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla,
-                            struct netlink_ext_ack *extack)
-{
-       if (nla_len(nla) < sizeof(*gw)) {
-               NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY");
-               return -EINVAL;
-       }
-
-       *gw = nla_get_in6_addr(nla);
-
-       return 0;
-}
-
 static int ip6_route_multipath_add(struct fib6_config *cfg,
                                   struct netlink_ext_ack *extack)
 {
@@ -5340,18 +5363,11 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
 
                        nla = nla_find(attrs, attrlen, RTA_GATEWAY);
                        if (nla) {
-                               err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
-                                                       extack);
-                               if (err)
-                                       goto cleanup;
-
+                               r_cfg.fc_gateway = nla_get_in6_addr(nla);
                                r_cfg.fc_flags |= RTF_GATEWAY;
                        }
-                       r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
 
-                       /* RTA_ENCAP_TYPE length checked in
-                        * lwtunnel_valid_encap_type_attr
-                        */
+                       r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
                        nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
                        if (nla)
                                r_cfg.fc_encap_type = nla_get_u16(nla);
@@ -5384,12 +5400,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
                rtnh = rtnh_next(rtnh, &remaining);
        }
 
-       if (list_empty(&rt6_nh_list)) {
-               NL_SET_ERR_MSG(extack,
-                              "Invalid nexthop configuration - no valid nexthops");
-               return -EINVAL;
-       }
-
        /* for add and replace send one notification with all nexthops.
         * Skip the notification in fib6_add_rt2node and send one with
         * the full route when done
@@ -5511,21 +5521,15 @@ static int ip6_route_multipath_del(struct fib6_config *cfg,
 
                        nla = nla_find(attrs, attrlen, RTA_GATEWAY);
                        if (nla) {
-                               err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
-                                                       extack);
-                               if (err) {
-                                       last_err = err;
-                                       goto next_rtnh;
-                               }
-
+                               r_cfg.fc_gateway = nla_get_in6_addr(nla);
                                r_cfg.fc_flags |= RTF_GATEWAY;
                        }
                }
+
                err = ip6_route_del(&r_cfg, extack);
                if (err)
                        last_err = err;
 
-next_rtnh:
                rtnh = rtnh_next(rtnh, &remaining);
        }