]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/can/raw.c
Merge branch 'armadillo800eva' of git://git.kernel.org/pub/scm/linux/kernel/git/horms...
[karo-tx-linux.git] / net / can / raw.c
index 46cca3a91d198093b86ea4d5701ecd42ce9dbce7..3e9c89356a939db3901d6ee664873691ac2accb1 100644 (file)
@@ -82,6 +82,7 @@ struct raw_sock {
        struct notifier_block notifier;
        int loopback;
        int recv_own_msgs;
+       int fd_frames;
        int count;                 /* number of active filters */
        struct can_filter dfilter; /* default/single filter */
        struct can_filter *filter; /* pointer to filter(s) */
@@ -119,6 +120,14 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
        if (!ro->recv_own_msgs && oskb->sk == sk)
                return;
 
+       /* do not pass frames with DLC > 8 to a legacy socket */
+       if (!ro->fd_frames) {
+               struct canfd_frame *cfd = (struct canfd_frame *)oskb->data;
+
+               if (unlikely(cfd->len > CAN_MAX_DLEN))
+                       return;
+       }
+
        /* clone the given skb to be able to enqueue it into the rcv queue */
        skb = skb_clone(oskb, GFP_ATOMIC);
        if (!skb)
@@ -291,6 +300,7 @@ static int raw_init(struct sock *sk)
        /* set default loopback behaviour */
        ro->loopback         = 1;
        ro->recv_own_msgs    = 0;
+       ro->fd_frames        = 0;
 
        /* set notifier */
        ro->notifier.notifier_call = raw_notifier;
@@ -569,6 +579,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
 
                break;
 
+       case CAN_RAW_FD_FRAMES:
+               if (optlen != sizeof(ro->fd_frames))
+                       return -EINVAL;
+
+               if (copy_from_user(&ro->fd_frames, optval, optlen))
+                       return -EFAULT;
+
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -627,6 +646,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
                val = &ro->recv_own_msgs;
                break;
 
+       case CAN_RAW_FD_FRAMES:
+               if (len > sizeof(int))
+                       len = sizeof(int);
+               val = &ro->fd_frames;
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -662,8 +687,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
        } else
                ifindex = ro->ifindex;
 
-       if (size != sizeof(struct can_frame))
-               return -EINVAL;
+       if (ro->fd_frames) {
+               if (unlikely(size != CANFD_MTU && size != CAN_MTU))
+                       return -EINVAL;
+       } else {
+               if (unlikely(size != CAN_MTU))
+                       return -EINVAL;
+       }
 
        dev = dev_get_by_index(&init_net, ifindex);
        if (!dev)
@@ -705,7 +735,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
                       struct msghdr *msg, size_t size, int flags)
 {
        struct sock *sk = sock->sk;
+       struct raw_sock *ro = raw_sk(sk);
        struct sk_buff *skb;
+       int rxmtu;
        int err = 0;
        int noblock;
 
@@ -716,10 +748,20 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (!skb)
                return err;
 
-       if (size < skb->len)
+       /*
+        * when serving a legacy socket the DLC <= 8 is already checked inside
+        * raw_rcv(). Now check if we need to pass a canfd_frame to a legacy
+        * socket and cut the possible CANFD_MTU/CAN_MTU length to CAN_MTU
+        */
+       if (!ro->fd_frames)
+               rxmtu = CAN_MTU;
+       else
+               rxmtu = skb->len;
+
+       if (size < rxmtu)
                msg->msg_flags |= MSG_TRUNC;
        else
-               size = skb->len;
+               size = rxmtu;
 
        err = memcpy_toiovec(msg->msg_iov, skb->data, size);
        if (err < 0) {