can: raw: fix CAN FD frame transmissions over CAN XL devices
authorOliver Hartkopp <socketcan@hartkopp.net>
Tue, 31 Jan 2023 10:56:13 +0000 (11:56 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Thu, 2 Feb 2023 09:33:26 +0000 (10:33 +0100)
A CAN XL device is always capable to process CAN FD frames. The former
check when sending CAN FD frames relied on the existence of a CAN FD
device and did not check for a CAN XL device that would be correct
too.

With this patch the CAN FD feature is enabled automatically when CAN
XL is switched on - and CAN FD cannot be switch off while CAN XL is
enabled.

This precondition also leads to a clean up and reduction of checks in
the hot path in raw_rcv() and raw_sendmsg(). Some conditions are
reordered to handle simple checks first.

changes since v1: https://lore.kernel.org/all/20230131091012.50553-1-socketcan@hartkopp.net
- fixed typo: devive -> device
changes since v2: https://lore.kernel.org/all/20230131091824.51026-1-socketcan@hartkopp.net/
- reorder checks in if statements to handle simple checks first

Fixes: 626332696d75 ("can: raw: add CAN XL support")
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/all/20230131105613.55228-1-socketcan@hartkopp.net
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
net/can/raw.c

index 81071cdb03010193bb0e90086659fab6d9aea5bb..ba86782ba8bb3cb9c1d4234016ec6c030449eb6c 100644 (file)
@@ -132,8 +132,8 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
                return;
 
        /* make sure to not pass oversized frames to the socket */
-       if ((can_is_canfd_skb(oskb) && !ro->fd_frames && !ro->xl_frames) ||
-           (can_is_canxl_skb(oskb) && !ro->xl_frames))
+       if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
+           (!ro->xl_frames && can_is_canxl_skb(oskb)))
                return;
 
        /* eliminate multiple filter matches for the same skb */
@@ -670,6 +670,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                if (copy_from_sockptr(&ro->fd_frames, optval, optlen))
                        return -EFAULT;
 
+               /* Enabling CAN XL includes CAN FD */
+               if (ro->xl_frames && !ro->fd_frames) {
+                       ro->fd_frames = ro->xl_frames;
+                       return -EINVAL;
+               }
                break;
 
        case CAN_RAW_XL_FRAMES:
@@ -679,6 +684,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                if (copy_from_sockptr(&ro->xl_frames, optval, optlen))
                        return -EFAULT;
 
+               /* Enabling CAN XL includes CAN FD */
+               if (ro->xl_frames)
+                       ro->fd_frames = ro->xl_frames;
                break;
 
        case CAN_RAW_JOIN_FILTERS:
@@ -786,6 +794,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
        return 0;
 }
 
+static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
+{
+       /* Classical CAN -> no checks for flags and device capabilities */
+       if (can_is_can_skb(skb))
+               return false;
+
+       /* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */
+       if (ro->fd_frames && can_is_canfd_skb(skb) &&
+           (mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu)))
+               return false;
+
+       /* CAN XL -> needs to be enabled and a CAN XL device */
+       if (ro->xl_frames && can_is_canxl_skb(skb) &&
+           can_is_canxl_dev_mtu(mtu))
+               return false;
+
+       return true;
+}
+
 static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 {
        struct sock *sk = sock->sk;
@@ -833,20 +860,8 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
                goto free_skb;
 
        err = -EINVAL;
-       if (ro->xl_frames && can_is_canxl_dev_mtu(dev->mtu)) {
-               /* CAN XL, CAN FD and Classical CAN */
-               if (!can_is_canxl_skb(skb) && !can_is_canfd_skb(skb) &&
-                   !can_is_can_skb(skb))
-                       goto free_skb;
-       } else if (ro->fd_frames && dev->mtu == CANFD_MTU) {
-               /* CAN FD and Classical CAN */
-               if (!can_is_canfd_skb(skb) && !can_is_can_skb(skb))
-                       goto free_skb;
-       } else {
-               /* Classical CAN */
-               if (!can_is_can_skb(skb))
-                       goto free_skb;
-       }
+       if (raw_bad_txframe(ro, skb, dev->mtu))
+               goto free_skb;
 
        sockcm_init(&sockc, sk);
        if (msg->msg_controllen) {