]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
can: netlink: add userland error messages
authorVincent Mailhol <mailhol@kernel.org>
Tue, 23 Sep 2025 06:58:45 +0000 (15:58 +0900)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 24 Sep 2025 15:09:47 +0000 (17:09 +0200)
Use NL_SET_ERR_MSG() and NL_SET_ERR_MSG_FMT() to return meaningful
error messages to the userland whenever a -EOPNOTSUPP error is
returned due to a failed validation of the CAN netlink arguments.

Signed-off-by: Vincent Mailhol <mailhol@kernel.org>
Link: https://patch.msgid.link/20250923-canxl-netlink-prep-v4-20-e720d28f66fe@kernel.org
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev/netlink.c

index 92d8df13e886c8832c4ed8450675c22dc2e5b009..0591406b6f3207f4db5ef1d25dc5e0289a1d06fe 100644 (file)
@@ -64,15 +64,23 @@ static int can_validate_tdc(struct nlattr *data_tdc,
        bool tdc_auto = tdc_flags & CAN_CTRLMODE_TDC_AUTO_MASK;
        int err;
 
-       /* CAN_CTRLMODE_TDC_{AUTO,MANUAL} are mutually exclusive */
-       if (tdc_auto && tdc_manual)
+       if (tdc_auto && tdc_manual) {
+               NL_SET_ERR_MSG(extack,
+                              "TDC manual and auto modes are mutually exclusive");
                return -EOPNOTSUPP;
+       }
 
        /* If one of the CAN_CTRLMODE_TDC_* flag is set then TDC
         * must be set and vice-versa
         */
-       if ((tdc_auto || tdc_manual) != !!data_tdc)
+       if ((tdc_auto || tdc_manual) && !data_tdc) {
+               NL_SET_ERR_MSG(extack, "TDC parameters are missing");
+               return -EOPNOTSUPP;
+       }
+       if (!(tdc_auto || tdc_manual) && data_tdc) {
+               NL_SET_ERR_MSG(extack, "TDC mode (auto or manual) is missing");
                return -EOPNOTSUPP;
+       }
 
        /* If providing TDC parameters, at least TDCO is needed. TDCV
         * is needed if and only if CAN_CTRLMODE_TDC_MANUAL is set
@@ -86,15 +94,23 @@ static int can_validate_tdc(struct nlattr *data_tdc,
                        return err;
 
                if (tb_tdc[IFLA_CAN_TDC_TDCV]) {
-                       if (tdc_auto)
+                       if (tdc_auto) {
+                               NL_SET_ERR_MSG(extack,
+                                              "TDCV is incompatible with TDC auto mode");
                                return -EOPNOTSUPP;
+                       }
                } else {
-                       if (tdc_manual)
+                       if (tdc_manual) {
+                               NL_SET_ERR_MSG(extack,
+                                              "TDC manual mode requires TDCV");
                                return -EOPNOTSUPP;
+                       }
                }
 
-               if (!tb_tdc[IFLA_CAN_TDC_TDCO])
+               if (!tb_tdc[IFLA_CAN_TDC_TDCO]) {
+                       NL_SET_ERR_MSG(extack, "TDCO is missing");
                        return -EOPNOTSUPP;
+               }
        }
 
        return 0;
@@ -105,6 +121,7 @@ static int can_validate_databittiming(struct nlattr *data[],
                                      int ifla_can_data_bittiming, u32 flags)
 {
        struct nlattr *data_tdc;
+       const char *type;
        u32 tdc_flags;
        bool is_on;
        int err;
@@ -120,18 +137,31 @@ static int can_validate_databittiming(struct nlattr *data[],
                data_tdc = data[IFLA_CAN_TDC];
                tdc_flags = flags & CAN_CTRLMODE_FD_TDC_MASK;
                is_on = flags & CAN_CTRLMODE_FD;
+               type = "FD";
        } else {
                return -EOPNOTSUPP; /* Place holder for CAN XL */
        }
 
        if (is_on) {
-               if (!data[IFLA_CAN_BITTIMING] || !data[ifla_can_data_bittiming])
+               if (!data[IFLA_CAN_BITTIMING] || !data[ifla_can_data_bittiming]) {
+                       NL_SET_ERR_MSG_FMT(extack,
+                                          "Provide both nominal and %s data bittiming",
+                                          type);
                        return -EOPNOTSUPP;
-       }
-
-       if (data[ifla_can_data_bittiming] || data_tdc) {
-               if (!is_on)
+               }
+       } else {
+               if (data[ifla_can_data_bittiming]) {
+                       NL_SET_ERR_MSG_FMT(extack,
+                                          "%s data bittiming requires CAN %s",
+                                          type, type);
                        return -EOPNOTSUPP;
+               }
+               if (data_tdc) {
+                       NL_SET_ERR_MSG_FMT(extack,
+                                          "%s TDC requires CAN %s",
+                                          type, type);
+                       return -EOPNOTSUPP;
+               }
        }
 
        err = can_validate_bittiming(data, extack, ifla_can_data_bittiming);
@@ -178,8 +208,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
 {
        struct can_priv *priv = netdev_priv(dev);
        struct can_ctrlmode *cm;
-       u32 maskedflags;
-       u32 ctrlstatic;
+       u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing;
 
        if (!data[IFLA_CAN_CTRLMODE])
                return 0;
@@ -189,20 +218,28 @@ static int can_ctrlmode_changelink(struct net_device *dev,
                return -EBUSY;
 
        cm = nla_data(data[IFLA_CAN_CTRLMODE]);
-       maskedflags = cm->flags & cm->mask;
        ctrlstatic = can_get_static_ctrlmode(priv);
+       maskedflags = cm->flags & cm->mask;
+       notsupp = maskedflags & ~(priv->ctrlmode_supported | ctrlstatic);
+       ctrlstatic_missing = (maskedflags & ctrlstatic) ^ ctrlstatic;
 
-       /* check whether provided bits are allowed to be passed */
-       if (maskedflags & ~(priv->ctrlmode_supported | ctrlstatic))
+       if (notsupp) {
+               NL_SET_ERR_MSG_FMT(extack,
+                                  "requested control mode %s not supported",
+                                  can_get_ctrlmode_str(notsupp));
                return -EOPNOTSUPP;
+       }
 
        /* do not check for static fd-non-iso if 'fd' is disabled */
        if (!(maskedflags & CAN_CTRLMODE_FD))
                ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
 
-       /* make sure static options are provided by configuration */
-       if ((maskedflags & ctrlstatic) != ctrlstatic)
+       if (ctrlstatic_missing) {
+               NL_SET_ERR_MSG_FMT(extack,
+                                  "missing required %s static control mode",
+                                  can_get_ctrlmode_str(ctrlstatic_missing));
                return -EOPNOTSUPP;
+       }
 
        /* If a top dependency flag is provided, reset all its dependencies */
        if (cm->mask & CAN_CTRLMODE_FD)
@@ -234,8 +271,10 @@ static int can_tdc_changelink(struct data_bittiming_params *dbt_params,
        const struct can_tdc_const *tdc_const = dbt_params->tdc_const;
        int err;
 
-       if (!tdc_const)
+       if (!tdc_const) {
+               NL_SET_ERR_MSG(extack, "The device does not support TDC");
                return -EOPNOTSUPP;
+       }
 
        err = nla_parse_nested(tb_tdc, IFLA_CAN_TDC_MAX, nla,
                               can_tdc_policy, extack);
@@ -450,8 +489,11 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
                const unsigned int num_term = priv->termination_const_cnt;
                unsigned int i;
 
-               if (!priv->do_set_termination)
+               if (!priv->do_set_termination) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Termination is not configurable on this device");
                        return -EOPNOTSUPP;
+               }
 
                /* check whether given value is supported by the interface */
                for (i = 0; i < num_term; i++) {