diff options
author | Marc Kleine-Budde <mkl@pengutronix.de> | 2023-05-18 22:46:08 +0200 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2023-05-23 14:59:05 +0200 |
commit | 78f1f58dba2bfcafe5affbedd074a0a84dea2dc6 (patch) | |
tree | 80727ee7956beb1dde535c42aac8c22c080c9427 | |
parent | b3cfa83eaa4ae2161a39ac56c74f1a5480e0db73 (diff) | |
download | linux-can-next-bcm.tar.gz |
can: bcm: add support for MSG_CMSG_COMPATbcm
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r-- | net/can/bcm.c | 98 |
1 files changed, 89 insertions, 9 deletions
diff --git a/net/can/bcm.c b/net/can/bcm.c index a24232a81ff972..953d0c7aa68851 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -40,6 +40,7 @@ * */ +#include <linux/compat.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -61,6 +62,27 @@ #include <net/sock.h> #include <net/net_namespace.h> +/** + * struct bcm_msg_head_compat - head of messages to/from the broadcast manager + * @opcode: opcode, see enum below. + * @flags: special flags, see below. + * @count: number of frames to send before changing interval. + * @ival1: interval for the first @count frames (using s32). + * @ival2: interval for the following frames (using s32). + * @can_id: CAN ID of frames to be sent or received. + * @nframes: number of frames appended to the message head. + * @frames: array of CAN frames. + */ +struct bcm_msg_head_compat { + __u32 opcode; + __u32 flags; + __u32 count; + struct old_timeval32 ival1, ival2; + canid_t can_id; + __u32 nframes; + struct can_frame frames[]; +}; + /* * To send multiple CAN frame content within TX_SETUP or to filter * CAN messages with multiplex index within RX_SETUP, the number of @@ -1302,16 +1324,41 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if (!bo->bound) return -ENOTCONN; - msg_head_size = sizeof(msg_head); + if (msg->msg_flags & MSG_CMSG_COMPAT) { + struct bcm_msg_head_compat msg_head_compat; - /* check for valid message length from userspace */ - if (size < msg_head_size) - return -EINVAL; + msg_head_size = sizeof(msg_head_compat); - /* read message head information */ - ret = memcpy_from_msg((u8 *)&msg_head, msg, msg_head_size); - if (ret < 0) - return ret; + /* check for valid message length from userspace */ + if (size < msg_head_size) + return -EINVAL; + + /* read message head information */ + ret = memcpy_from_msg(&msg_head_compat, msg, msg_head_size); + if (ret < 0) + return ret; + + msg_head.opcode = msg_head_compat.opcode; + msg_head.flags = msg_head_compat.flags; + msg_head.count = msg_head_compat.count; + msg_head.ival1.tv_sec = msg_head_compat.ival1.tv_sec; + msg_head.ival1.tv_usec = msg_head_compat.ival1.tv_usec; + msg_head.ival2.tv_sec = msg_head_compat.ival2.tv_sec; + msg_head.ival2.tv_usec = msg_head_compat.ival2.tv_usec; + msg_head.can_id = msg_head_compat.can_id; + msg_head.nframes = msg_head_compat.nframes; + } else { + msg_head_size = sizeof(msg_head); + + /* check for valid message length from userspace */ + if (size < msg_head_size) + return -EINVAL; + + /* read message head information */ + ret = memcpy_from_msg((u8 *)&msg_head, msg, msg_head_size); + if (ret < 0) + return ret; + } cfsiz = CFSIZ(msg_head.flags); if ((size - msg_head_size) % cfsiz) @@ -1657,6 +1704,7 @@ static int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, struct sock *sk = sock->sk; struct sk_buff *skb; int error = 0; + int rem_len; int err; skb = skb_recv_datagram(sk, flags, &error); @@ -1666,7 +1714,39 @@ static int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, if (skb->len < size) size = skb->len; - err = memcpy_to_msg(msg, skb->data, size); + rem_len = size; + + if (flags & MSG_CMSG_COMPAT) { + struct bcm_msg_head_compat msg_head_compat; + struct bcm_msg_head *msg_head; + + if (skb->len < sizeof(*msg_head)) { + skb_free_datagram(sk, skb); + return -EINVAL; + } + + msg_head = skb_pull_data(skb, sizeof(*msg_head)); + msg_head_compat.opcode = msg_head->opcode; + msg_head_compat.flags = msg_head->flags; + msg_head_compat.count = msg_head->count; + msg_head_compat.ival1.tv_sec = msg_head->ival1.tv_sec; + msg_head_compat.ival1.tv_usec = msg_head->ival1.tv_usec; + msg_head_compat.ival2.tv_sec = msg_head->ival2.tv_sec; + msg_head_compat.ival2.tv_usec = msg_head->ival2.tv_usec; + msg_head_compat.can_id = msg_head->can_id; + msg_head_compat.nframes = msg_head->nframes; + + err = memcpy_to_msg(msg, &msg_head_compat, sizeof(msg_head_compat)); + if (err < 0) { + skb_free_datagram(sk, skb); + return err; + } + + rem_len = size - sizeof(*msg_head); + size = rem_len + sizeof(msg_head_compat); + } + + err = memcpy_to_msg(msg, skb->data, rem_len); if (err < 0) { skb_free_datagram(sk, skb); return err; |