can: bcm: add recvmsg flags for own, local and remote traffic

CAN RAW sockets allow userspace to tell if a received CAN frame comes
from the same socket, another socket on the same host, or another host.
See commit 1e55659ce6 ("can-raw: add msg_flags to distinguish local
traffic"). However, this feature is missing in CAN BCM sockets.

Add the same feature to CAN BCM sockets. When reading a received frame
(opcode RX_CHANGED) using recvmsg, two flags in msg->msg_flags may be
set following the previous convention (from CAN RAW), to distinguish
between 'own', 'local' and 'remote' CAN traffic.

Update the documentation to reflect this change.

Signed-off-by: Nicolas Maier <nicolas.maier.dev@gmail.com>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/all/20240120081018.2319-1-socketcan@hartkopp.net
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Nicolas Maier 2024-01-20 09:10:18 +01:00 committed by Marc Kleine-Budde
parent 970cb1ceda
commit fec846fa7e
2 changed files with 75 additions and 28 deletions

View File

@ -444,6 +444,24 @@ definitions are specified for CAN specific MTUs in include/linux/can.h:
#define CANFD_MTU (sizeof(struct canfd_frame)) == 72 => CAN FD frame #define CANFD_MTU (sizeof(struct canfd_frame)) == 72 => CAN FD frame
Returned Message Flags
----------------------
When using the system call recvmsg(2) on a RAW or a BCM socket, the
msg->msg_flags field may contain the following flags:
MSG_DONTROUTE:
set when the received frame was created on the local host.
MSG_CONFIRM:
set when the frame was sent via the socket it is received on.
This flag can be interpreted as a 'transmission confirmation' when the
CAN driver supports the echo of frames on driver level, see
:ref:`socketcan-local-loopback1` and :ref:`socketcan-local-loopback2`.
(Note: In order to receive such messages on a RAW socket,
CAN_RAW_RECV_OWN_MSGS must be set.)
.. _socketcan-raw-sockets: .. _socketcan-raw-sockets:
RAW Protocol Sockets with can_filters (SOCK_RAW) RAW Protocol Sockets with can_filters (SOCK_RAW)
@ -693,22 +711,6 @@ where the CAN_INV_FILTER flag is set in order to notch single CAN IDs or
CAN ID ranges from the incoming traffic. CAN ID ranges from the incoming traffic.
RAW Socket Returned Message Flags
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using recvmsg() call, the msg->msg_flags may contain following flags:
MSG_DONTROUTE:
set when the received frame was created on the local host.
MSG_CONFIRM:
set when the frame was sent via the socket it is received on.
This flag can be interpreted as a 'transmission confirmation' when the
CAN driver supports the echo of frames on driver level, see
:ref:`socketcan-local-loopback1` and :ref:`socketcan-local-loopback2`.
In order to receive such messages, CAN_RAW_RECV_OWN_MSGS must be set.
Broadcast Manager Protocol Sockets (SOCK_DGRAM) Broadcast Manager Protocol Sockets (SOCK_DGRAM)
----------------------------------------------- -----------------------------------------------

View File

@ -72,9 +72,11 @@
#define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60) #define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60)
/* use of last_frames[index].flags */ /* use of last_frames[index].flags */
#define RX_LOCAL 0x10 /* frame was created on the local host */
#define RX_OWN 0x20 /* frame was sent via the socket it was received on */
#define RX_RECV 0x40 /* received data for this element */ #define RX_RECV 0x40 /* received data for this element */
#define RX_THR 0x80 /* element not been sent due to throttle feature */ #define RX_THR 0x80 /* element not been sent due to throttle feature */
#define BCM_CAN_FLAGS_MASK 0x3F /* to clean private flags after usage */ #define BCM_CAN_FLAGS_MASK 0x0F /* to clean private flags after usage */
/* get best masking value for can_rx_register() for a given single can_id */ /* get best masking value for can_rx_register() for a given single can_id */
#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \ #define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
@ -138,6 +140,16 @@ static LIST_HEAD(bcm_notifier_list);
static DEFINE_SPINLOCK(bcm_notifier_lock); static DEFINE_SPINLOCK(bcm_notifier_lock);
static struct bcm_sock *bcm_busy_notifier; static struct bcm_sock *bcm_busy_notifier;
/* Return pointer to store the extra msg flags for bcm_recvmsg().
* We use the space of one unsigned int beyond the 'struct sockaddr_can'
* in skb->cb.
*/
static inline unsigned int *bcm_flags(struct sk_buff *skb)
{
/* return pointer after struct sockaddr_can */
return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]);
}
static inline struct bcm_sock *bcm_sk(const struct sock *sk) static inline struct bcm_sock *bcm_sk(const struct sock *sk)
{ {
return (struct bcm_sock *)sk; return (struct bcm_sock *)sk;
@ -325,6 +337,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
struct sock *sk = op->sk; struct sock *sk = op->sk;
unsigned int datalen = head->nframes * op->cfsiz; unsigned int datalen = head->nframes * op->cfsiz;
int err; int err;
unsigned int *pflags;
skb = alloc_skb(sizeof(*head) + datalen, gfp_any()); skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
if (!skb) if (!skb)
@ -332,6 +345,14 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
skb_put_data(skb, head, sizeof(*head)); skb_put_data(skb, head, sizeof(*head));
/* ensure space for sockaddr_can and msg flags */
sock_skb_cb_check_size(sizeof(struct sockaddr_can) +
sizeof(unsigned int));
/* initialize msg flags */
pflags = bcm_flags(skb);
*pflags = 0;
if (head->nframes) { if (head->nframes) {
/* CAN frames starting here */ /* CAN frames starting here */
firstframe = (struct canfd_frame *)skb_tail_pointer(skb); firstframe = (struct canfd_frame *)skb_tail_pointer(skb);
@ -344,8 +365,14 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
* relevant for updates that are generated by the * relevant for updates that are generated by the
* BCM, where nframes is 1 * BCM, where nframes is 1
*/ */
if (head->nframes == 1) if (head->nframes == 1) {
if (firstframe->flags & RX_LOCAL)
*pflags |= MSG_DONTROUTE;
if (firstframe->flags & RX_OWN)
*pflags |= MSG_CONFIRM;
firstframe->flags &= BCM_CAN_FLAGS_MASK; firstframe->flags &= BCM_CAN_FLAGS_MASK;
}
} }
if (has_timestamp) { if (has_timestamp) {
@ -360,7 +387,6 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
* containing the interface index. * containing the interface index.
*/ */
sock_skb_cb_check_size(sizeof(struct sockaddr_can));
addr = (struct sockaddr_can *)skb->cb; addr = (struct sockaddr_can *)skb->cb;
memset(addr, 0, sizeof(*addr)); memset(addr, 0, sizeof(*addr));
addr->can_family = AF_CAN; addr->can_family = AF_CAN;
@ -444,7 +470,7 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
op->frames_filtered = op->frames_abs = 0; op->frames_filtered = op->frames_abs = 0;
/* this element is not throttled anymore */ /* this element is not throttled anymore */
data->flags &= (BCM_CAN_FLAGS_MASK|RX_RECV); data->flags &= ~RX_THR;
memset(&head, 0, sizeof(head)); memset(&head, 0, sizeof(head));
head.opcode = RX_CHANGED; head.opcode = RX_CHANGED;
@ -465,13 +491,17 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
*/ */
static void bcm_rx_update_and_send(struct bcm_op *op, static void bcm_rx_update_and_send(struct bcm_op *op,
struct canfd_frame *lastdata, struct canfd_frame *lastdata,
const struct canfd_frame *rxdata) const struct canfd_frame *rxdata,
unsigned char traffic_flags)
{ {
memcpy(lastdata, rxdata, op->cfsiz); memcpy(lastdata, rxdata, op->cfsiz);
/* mark as used and throttled by default */ /* mark as used and throttled by default */
lastdata->flags |= (RX_RECV|RX_THR); lastdata->flags |= (RX_RECV|RX_THR);
/* add own/local/remote traffic flags */
lastdata->flags |= traffic_flags;
/* throttling mode inactive ? */ /* throttling mode inactive ? */
if (!op->kt_ival2) { if (!op->kt_ival2) {
/* send RX_CHANGED to the user immediately */ /* send RX_CHANGED to the user immediately */
@ -508,7 +538,8 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
* received data stored in op->last_frames[] * received data stored in op->last_frames[]
*/ */
static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index, static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
const struct canfd_frame *rxdata) const struct canfd_frame *rxdata,
unsigned char traffic_flags)
{ {
struct canfd_frame *cf = op->frames + op->cfsiz * index; struct canfd_frame *cf = op->frames + op->cfsiz * index;
struct canfd_frame *lcf = op->last_frames + op->cfsiz * index; struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
@ -521,7 +552,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
if (!(lcf->flags & RX_RECV)) { if (!(lcf->flags & RX_RECV)) {
/* received data for the first time => send update to user */ /* received data for the first time => send update to user */
bcm_rx_update_and_send(op, lcf, rxdata); bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
return; return;
} }
@ -529,7 +560,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
for (i = 0; i < rxdata->len; i += 8) { for (i = 0; i < rxdata->len; i += 8) {
if ((get_u64(cf, i) & get_u64(rxdata, i)) != if ((get_u64(cf, i) & get_u64(rxdata, i)) !=
(get_u64(cf, i) & get_u64(lcf, i))) { (get_u64(cf, i) & get_u64(lcf, i))) {
bcm_rx_update_and_send(op, lcf, rxdata); bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
return; return;
} }
} }
@ -537,7 +568,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
if (op->flags & RX_CHECK_DLC) { if (op->flags & RX_CHECK_DLC) {
/* do a real check in CAN frame length */ /* do a real check in CAN frame length */
if (rxdata->len != lcf->len) { if (rxdata->len != lcf->len) {
bcm_rx_update_and_send(op, lcf, rxdata); bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
return; return;
} }
} }
@ -644,6 +675,7 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
struct bcm_op *op = (struct bcm_op *)data; struct bcm_op *op = (struct bcm_op *)data;
const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data; const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
unsigned int i; unsigned int i;
unsigned char traffic_flags;
if (op->can_id != rxframe->can_id) if (op->can_id != rxframe->can_id)
return; return;
@ -673,15 +705,24 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
return; return;
} }
/* compute flags to distinguish between own/local/remote CAN traffic */
traffic_flags = 0;
if (skb->sk) {
traffic_flags |= RX_LOCAL;
if (skb->sk == op->sk)
traffic_flags |= RX_OWN;
}
if (op->flags & RX_FILTER_ID) { if (op->flags & RX_FILTER_ID) {
/* the easiest case */ /* the easiest case */
bcm_rx_update_and_send(op, op->last_frames, rxframe); bcm_rx_update_and_send(op, op->last_frames, rxframe,
traffic_flags);
goto rx_starttimer; goto rx_starttimer;
} }
if (op->nframes == 1) { if (op->nframes == 1) {
/* simple compare with index 0 */ /* simple compare with index 0 */
bcm_rx_cmp_to_index(op, 0, rxframe); bcm_rx_cmp_to_index(op, 0, rxframe, traffic_flags);
goto rx_starttimer; goto rx_starttimer;
} }
@ -698,7 +739,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) == if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) ==
(get_u64(op->frames, 0) & (get_u64(op->frames, 0) &
get_u64(op->frames + op->cfsiz * i, 0))) { get_u64(op->frames + op->cfsiz * i, 0))) {
bcm_rx_cmp_to_index(op, i, rxframe); bcm_rx_cmp_to_index(op, i, rxframe,
traffic_flags);
break; break;
} }
} }
@ -1675,6 +1717,9 @@ static int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
memcpy(msg->msg_name, skb->cb, msg->msg_namelen); memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
} }
/* assign the flags that have been recorded in bcm_send_to_user() */
msg->msg_flags |= *(bcm_flags(skb));
skb_free_datagram(sk, skb); skb_free_datagram(sk, skb);
return size; return size;