]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: mctp: allow NL parsing directly into a struct mctp_route
authorJeremy Kerr <jk@codeconstruct.com.au>
Wed, 2 Jul 2025 06:20:12 +0000 (14:20 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 8 Jul 2025 10:39:24 +0000 (12:39 +0200)
The netlink route parsing functions end up setting a bunch of output
variables from the rt attributes. This will get messy when the routes
become more complex.

So, split the rt parsing into two types: a lookup (returning route
target data suitable for a route lookup, like when deleting a route) and
a populate (setting fields of a struct mctp_route).

In doing this, we need to separate the route allocation from
mctp_route_add, so add some comments on the lifetime semantics for the
latter.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Link: https://patch.msgid.link/20250702-dev-forwarding-v5-12-1468191da8a4@codeconstruct.com.au
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/mctp/route.c

index f96265acf16f4ecedad7a3edf2367cfc7f56be7f..5eca3ce0ba3ceac2ea0567d4042a298abcf3c674 100644 (file)
@@ -1091,25 +1091,32 @@ static unsigned int mctp_route_netid(struct mctp_route *rt)
 }
 
 /* route management */
-static int mctp_route_add(struct net *net, struct mctp_dev *mdev,
-                         mctp_eid_t daddr_start, unsigned int daddr_extent,
-                         unsigned int mtu, unsigned char type)
+
+/* mctp_route_add(): Add the provided route, previously allocated via
+ * mctp_route_alloc(). On success, takes ownership of @rt, which includes a
+ * hold on rt->dev for usage in the route table. On failure a caller will want
+ * to mctp_route_release().
+ *
+ * We expect that the caller has set rt->type, rt->min, rt->max, rt->dev and
+ * rt->mtu, and that the route holds a reference to rt->dev (via mctp_dev_hold).
+ * Other fields will be populated.
+ */
+static int mctp_route_add(struct net *net, struct mctp_route *rt)
 {
-       int (*rtfn)(struct mctp_dst *dst, struct sk_buff *skb);
-       struct mctp_route *rt, *ert;
+       struct mctp_route *ert;
 
-       if (!mctp_address_unicast(daddr_start))
+       if (!mctp_address_unicast(rt->min) || !mctp_address_unicast(rt->max))
                return -EINVAL;
 
-       if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
+       if (!rt->dev)
                return -EINVAL;
 
-       switch (type) {
+       switch (rt->type) {
        case RTN_LOCAL:
-               rtfn = mctp_dst_input;
+               rt->output = mctp_dst_input;
                break;
        case RTN_UNICAST:
-               rtfn = mctp_dst_output;
+               rt->output = mctp_dst_output;
                break;
        default:
                return -EINVAL;
@@ -1117,22 +1124,9 @@ static int mctp_route_add(struct net *net, struct mctp_dev *mdev,
 
        ASSERT_RTNL();
 
-       rt = mctp_route_alloc();
-       if (!rt)
-               return -ENOMEM;
-
-       rt->min = daddr_start;
-       rt->max = daddr_start + daddr_extent;
-       rt->mtu = mtu;
-       rt->dev = mdev;
-       mctp_dev_hold(rt->dev);
-       rt->type = type;
-       rt->output = rtfn;
-
        /* Prevent duplicate identical routes. */
        list_for_each_entry(ert, &net->mctp.routes, list) {
                if (mctp_rt_compare_exact(rt, ert)) {
-                       mctp_route_release(rt);
                        return -EEXIST;
                }
        }
@@ -1174,7 +1168,25 @@ static int mctp_route_remove(struct net *net, unsigned int netid,
 
 int mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr)
 {
-       return mctp_route_add(dev_net(mdev->dev), mdev, addr, 0, 0, RTN_LOCAL);
+       struct mctp_route *rt;
+       int rc;
+
+       rt = mctp_route_alloc();
+       if (!rt)
+               return -ENOMEM;
+
+       rt->min = addr;
+       rt->max = addr;
+       rt->dev = mdev;
+       rt->type = RTN_LOCAL;
+
+       mctp_dev_hold(rt->dev);
+
+       rc = mctp_route_add(dev_net(mdev->dev), rt);
+       if (rc)
+               mctp_route_release(rt);
+
+       return rc;
 }
 
 int mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr)
@@ -1286,13 +1298,16 @@ static const struct nla_policy rta_mctp_policy[RTA_MAX + 1] = {
        [RTA_OIF]               = { .type = NLA_U32 },
 };
 
-/* Common part for RTM_NEWROUTE and RTM_DELROUTE parsing.
- * tb must hold RTA_MAX+1 elements.
- */
-static int mctp_route_nlparse(struct net *net, struct nlmsghdr *nlh,
-                             struct netlink_ext_ack *extack,
-                             struct nlattr **tb, struct rtmsg **rtm,
-                             struct mctp_dev **mdev, mctp_eid_t *daddr_start)
+static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
+       [RTAX_MTU]              = { .type = NLA_U32 },
+};
+
+/* base parsing; common to both _lookup and _populate variants */
+static int mctp_route_nlparse_common(struct net *net, struct nlmsghdr *nlh,
+                                    struct netlink_ext_ack *extack,
+                                    struct nlattr **tb, struct rtmsg **rtm,
+                                    struct mctp_dev **mdev,
+                                    mctp_eid_t *daddr_start)
 {
        struct net_device *dev;
        unsigned int ifindex;
@@ -1323,61 +1338,126 @@ static int mctp_route_nlparse(struct net *net, struct nlmsghdr *nlh,
                return -EINVAL;
        }
 
+       if ((*rtm)->rtm_type != RTN_UNICAST) {
+               NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
+               return -EINVAL;
+       }
+
        dev = __dev_get_by_index(net, ifindex);
        if (!dev) {
                NL_SET_ERR_MSG(extack, "bad ifindex");
                return -ENODEV;
        }
+
        *mdev = mctp_dev_get_rtnl(dev);
        if (!*mdev)
                return -ENODEV;
 
-       if (dev->flags & IFF_LOOPBACK) {
-               NL_SET_ERR_MSG(extack, "no routes to loopback");
-               return -EINVAL;
-       }
-
        return 0;
 }
 
-static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
-       [RTAX_MTU]              = { .type = NLA_U32 },
-};
-
-static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
-                        struct netlink_ext_ack *extack)
+/* Route parsing for lookup operations; we only need the "route target"
+ * components (ie., network and dest-EID range).
+ */
+static int mctp_route_nlparse_lookup(struct net *net, struct nlmsghdr *nlh,
+                                    struct netlink_ext_ack *extack,
+                                    unsigned char *type, unsigned int *netid,
+                                    mctp_eid_t *daddr_start,
+                                    unsigned int *daddr_extent)
 {
-       struct net *net = sock_net(skb->sk);
        struct nlattr *tb[RTA_MAX + 1];
+       struct mctp_dev *mdev;
+       struct rtmsg *rtm;
+       int rc;
+
+       rc = mctp_route_nlparse_common(net, nlh, extack, tb, &rtm,
+                                      &mdev, daddr_start);
+       if (rc)
+               return rc;
+
+       *netid = mdev->net;
+       *type = rtm->rtm_type;
+       *daddr_extent = rtm->rtm_dst_len;
+
+       return 0;
+}
+
+/* Full route parse for RTM_NEWROUTE: populate @rt. On success, the route will
+ * hold a reference to the dev.
+ */
+static int mctp_route_nlparse_populate(struct net *net, struct nlmsghdr *nlh,
+                                      struct netlink_ext_ack *extack,
+                                      struct mctp_route *rt)
+{
        struct nlattr *tbx[RTAX_MAX + 1];
+       struct nlattr *tb[RTA_MAX + 1];
+       unsigned int daddr_extent;
        mctp_eid_t daddr_start;
-       struct mctp_dev *mdev;
+       struct mctp_dev *dev;
        struct rtmsg *rtm;
-       unsigned int mtu;
+       u32 mtu = 0;
        int rc;
 
-       rc = mctp_route_nlparse(net, nlh, extack, tb,
-                               &rtm, &mdev, &daddr_start);
-       if (rc < 0)
+       rc = mctp_route_nlparse_common(net, nlh, extack, tb, &rtm,
+                                      &dev, &daddr_start);
+       if (rc)
                return rc;
 
-       if (rtm->rtm_type != RTN_UNICAST) {
-               NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
+       daddr_extent = rtm->rtm_dst_len;
+
+       if (daddr_extent > 0xff || daddr_extent + daddr_start >= 255) {
+               NL_SET_ERR_MSG(extack, "invalid eid range");
                return -EINVAL;
        }
 
-       mtu = 0;
        if (tb[RTA_METRICS]) {
                rc = nla_parse_nested(tbx, RTAX_MAX, tb[RTA_METRICS],
                                      rta_metrics_policy, NULL);
-               if (rc < 0)
+               if (rc < 0) {
+                       NL_SET_ERR_MSG(extack, "incorrect RTA_METRICS format");
                        return rc;
+               }
                if (tbx[RTAX_MTU])
                        mtu = nla_get_u32(tbx[RTAX_MTU]);
        }
 
-       rc = mctp_route_add(net, mdev, daddr_start, rtm->rtm_dst_len, mtu,
-                           rtm->rtm_type);
+       rt->type = rtm->rtm_type;
+       rt->min = daddr_start;
+       rt->max = daddr_start + daddr_extent;
+       rt->mtu = mtu;
+       rt->dev = dev;
+       mctp_dev_hold(rt->dev);
+
+       return 0;
+}
+
+static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
+                        struct netlink_ext_ack *extack)
+{
+       struct net *net = sock_net(skb->sk);
+       struct mctp_route *rt;
+       int rc;
+
+       rt = mctp_route_alloc();
+       if (!rt)
+               return -ENOMEM;
+
+       rc = mctp_route_nlparse_populate(net, nlh, extack, rt);
+       if (rc < 0)
+               goto err_free;
+
+       if (rt->dev->dev->flags & IFF_LOOPBACK) {
+               NL_SET_ERR_MSG(extack, "no routes to loopback");
+               rc = -EINVAL;
+               goto err_free;
+       }
+
+       rc = mctp_route_add(net, rt);
+       if (!rc)
+               return 0;
+
+err_free:
+       mctp_route_release(rt);
        return rc;
 }
 
@@ -1385,23 +1465,21 @@ static int mctp_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
                         struct netlink_ext_ack *extack)
 {
        struct net *net = sock_net(skb->sk);
-       struct nlattr *tb[RTA_MAX + 1];
+       unsigned int netid, daddr_extent;
+       unsigned char type = RTN_UNSPEC;
        mctp_eid_t daddr_start;
-       struct mctp_dev *mdev;
-       struct rtmsg *rtm;
        int rc;
 
-       rc = mctp_route_nlparse(net, nlh, extack, tb,
-                               &rtm, &mdev, &daddr_start);
+       rc = mctp_route_nlparse_lookup(net, nlh, extack, &type, &netid,
+                                      &daddr_start, &daddr_extent);
        if (rc < 0)
                return rc;
 
        /* we only have unicast routes */
-       if (rtm->rtm_type != RTN_UNICAST)
+       if (type != RTN_UNICAST)
                return -EINVAL;
 
-       rc = mctp_route_remove(net, mdev->net, daddr_start, rtm->rtm_dst_len,
-                              RTN_UNICAST);
+       rc = mctp_route_remove(net, netid, daddr_start, daddr_extent, type);
        return rc;
 }