* [PATCH] canxl: add virtual CAN network identifier support
@ 2024-01-06 19:28 Oliver Hartkopp
2024-01-06 22:03 ` Oliver Hartkopp
2024-01-07 6:28 ` Vincent MAILHOL
0 siblings, 2 replies; 4+ messages in thread
From: Oliver Hartkopp @ 2024-01-06 19:28 UTC (permalink / raw)
To: linux-can; +Cc: mkl, mailhol.vincent, Oliver Hartkopp
CAN XL data frames contain an 8-bit virtual CAN network identifier (VCID).
A VCID value of zero represents an 'untagged' CAN XL frame.
To receive and send these optional VCIDs via CAN_RAW sockets a new socket
option CAN_RAW_XL_VCID_OPTS is introduced to define/access VCID content:
- tx: set the outgoing VCID value by the kernel (one fixed 8-bit value)
- tx: pass through VCID values from the user space (e.g. for traffic replay)
- rx: apply VCID receive filter (value/mask) to be passed to the user space
With the 'tx pass through' option CAN_RAW_XL_VCID_TX_PASS all valid VCID
values can be send, e.g. to replay full qualified CAN XL traffic.
The VCID value provided for the CAN_RAW_XL_VCID_TX_SET option will
override the VCID value in the struct canxl_frame.prio defined for
CAN_RAW_XL_VCID_TX_PASS when both flags are set.
With a rx_vcid_mask of zero all possible VCID values (0x00 - 0xFF) are passed
to the user space when the CAN_RAW_XL_VCID_RX_FILTER flag is set. Without this
flag only untagged CAN XL frames (VCID = 0x00, CANXL_VCID flag unset) are
delivered to the user space.
The 8-bit VCID is stored inside the CAN XL prio element (only in CAN XL
frames!) to not interfere with other CAN content or the CAN filters
provided by the CAN_RAW sockets and kernel infrastruture.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
include/uapi/linux/can.h | 14 +++++--
include/uapi/linux/can/raw.h | 14 +++++++
net/can/raw.c | 79 ++++++++++++++++++++++++++++++++++--
3 files changed, 100 insertions(+), 7 deletions(-)
diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h
index 939db2388208..30453905fbd2 100644
--- a/include/uapi/linux/can.h
+++ b/include/uapi/linux/can.h
@@ -180,34 +180,40 @@ struct canfd_frame {
};
/*
* defined bits for canxl_frame.flags
*
- * The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC
- * and shares the relative position of the struct can[fd]_frame.len element.
+ * The canxl_frame.flags element contains CAN XL frame specific bits and
+ * shares the relative position of the struct can[fd]_frame.len element.
* The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame.
* As a side effect setting this bit intentionally breaks the length checks
* for Classical CAN and CAN FD frames.
*
* Undefined bits in canxl_frame.flags are reserved and shall be set to zero.
*/
#define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
#define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
+#define CANXL_VCID 0x02 /* prio contains the virtual CAN network identifier */
+
+/* the 8-bit VCID is optionally placed in the canxl_frame.prio element */
+#define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */
+#define CANXL_VCID_VAL_MASK 0xFFUL /* VCID is an 8-bit value */
+#define CANXL_VCID_MASK (CANXL_VCID_VAL_MASK << CANXL_VCID_OFFSET)
/**
* struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure
- * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags
+ * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags / VCID
* @flags: additional flags for CAN XL
* @sdt: SDU (service data unit) type
* @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN)
* @af: acceptance field
* @data: CAN XL frame payload (CANXL_MIN_DLEN .. CANXL_MAX_DLEN byte)
*
* @prio shares the same position as @can_id from struct can[fd]_frame.
*/
struct canxl_frame {
- canid_t prio; /* 11 bit priority for arbitration (canid_t) */
+ canid_t prio; /* 11 bit priority for arbitration / 8 bit VCID */
__u8 flags; /* additional flags for CAN XL */
__u8 sdt; /* SDU (service data unit) type */
__u16 len; /* frame payload length in byte */
__u32 af; /* acceptance field */
__u8 data[CANXL_MAX_DLEN];
diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h
index 31622c9b7988..8890b0d2fd48 100644
--- a/include/uapi/linux/can/raw.h
+++ b/include/uapi/linux/can/raw.h
@@ -63,8 +63,22 @@ enum {
CAN_RAW_LOOPBACK, /* local loopback (default:on) */
CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */
CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */
CAN_RAW_JOIN_FILTERS, /* all filters must match to trigger */
CAN_RAW_XL_FRAMES, /* allow CAN XL frames (default:off) */
+ CAN_RAW_XL_VCID_OPTS, /* CAN XL VCID configuration options */
};
+struct can_raw_vcid_options {
+
+ __u8 flags; /* flags for vcid (filter) behaviour */
+ __u8 tx_vcid; /* VCID value set into canxl_frame.prio */
+ __u8 rx_vcid; /* VCID value for VCID filter */
+ __u8 rx_vcid_mask; /* VCID mask for VCID filter */
+
+};
+
+#define CAN_RAW_XL_VCID_TX_SET 0x01
+#define CAN_RAW_XL_VCID_TX_PASS 0x02
+#define CAN_RAW_XL_VCID_RX_FILTER 0x04
+
#endif /* !_UAPI_CAN_RAW_H */
diff --git a/net/can/raw.c b/net/can/raw.c
index e6b822624ba2..a6f0b6a0a934 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -89,10 +89,14 @@ struct raw_sock {
struct list_head notifier;
int loopback;
int recv_own_msgs;
int fd_frames;
int xl_frames;
+ struct can_raw_vcid_options raw_vcid_opts;
+ canid_t tx_vcid_shifted;
+ canid_t rx_vcid_shifted;
+ canid_t rx_vcid_mask_shifted;
int join_filters;
int count; /* number of active filters */
struct can_filter dfilter; /* default/single filter */
struct can_filter *filter; /* pointer to filter(s) */
can_err_mask_t err_mask;
@@ -132,14 +136,33 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
/* check the received tx sock reference */
if (!ro->recv_own_msgs && oskb->sk == sk)
return;
/* make sure to not pass oversized frames to the socket */
- if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
- (!ro->xl_frames && can_is_canxl_skb(oskb)))
+ if (!ro->fd_frames && can_is_canfd_skb(oskb))
return;
+ if (can_is_canxl_skb(oskb)) {
+ struct canxl_frame *cxl = (struct canxl_frame *)oskb->data;
+
+ /* make sure to not pass oversized frames to the socket */
+ if (!ro->xl_frames)
+ return;
+
+ /* filter CAN XL VCID content */
+ if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_RX_FILTER) {
+ /* apply VCID filter if user enabled the filter */
+ if ((cxl->prio & ro->rx_vcid_mask_shifted) !=
+ (ro->rx_vcid_shifted & ro->rx_vcid_mask_shifted))
+ return;
+ } else {
+ /* no filter => do not forward VCID tagged frames */
+ if (cxl->prio & CANXL_VCID_MASK)
+ return;
+ }
+ }
+
/* eliminate multiple filter matches for the same skb */
if (this_cpu_ptr(ro->uniq)->skb == oskb &&
this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) {
if (!ro->join_filters)
return;
@@ -696,10 +719,23 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
/* Enabling CAN XL includes CAN FD */
if (ro->xl_frames)
ro->fd_frames = ro->xl_frames;
break;
+ case CAN_RAW_XL_VCID_OPTS:
+ if (optlen != sizeof(ro->raw_vcid_opts))
+ return -EINVAL;
+
+ if (copy_from_sockptr(&ro->raw_vcid_opts, optval, optlen))
+ return -EFAULT;
+
+ /* prepare 32 bit values for handling in hot path */
+ ro->tx_vcid_shifted = ro->raw_vcid_opts.tx_vcid << CANXL_VCID_OFFSET;
+ ro->rx_vcid_shifted = ro->raw_vcid_opts.rx_vcid << CANXL_VCID_OFFSET;
+ ro->rx_vcid_mask_shifted = ro->raw_vcid_opts.rx_vcid_mask << CANXL_VCID_OFFSET;
+ break;
+
case CAN_RAW_JOIN_FILTERS:
if (optlen != sizeof(ro->join_filters))
return -EINVAL;
if (copy_from_sockptr(&ro->join_filters, optval, optlen))
@@ -784,10 +820,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
if (len > sizeof(int))
len = sizeof(int);
val = &ro->xl_frames;
break;
+ case CAN_RAW_XL_VCID_OPTS:
+ /* user space buffer to small for VCID opts? */
+ if (len < sizeof(ro->raw_vcid_opts)) {
+ /* return -ERANGE and needed space in optlen */
+ err = -ERANGE;
+ if (put_user(sizeof(ro->raw_vcid_opts), optlen))
+ err = -EFAULT;
+ } else {
+ if (len > sizeof(ro->raw_vcid_opts))
+ len = sizeof(ro->raw_vcid_opts);
+ if (copy_to_user(optval, &ro->raw_vcid_opts, len))
+ err = -EFAULT;
+ }
+ break;
+
case CAN_RAW_JOIN_FILTERS:
if (len > sizeof(int))
len = sizeof(int);
val = &ro->join_filters;
break;
@@ -814,12 +865,34 @@ static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
(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))
+ can_is_canxl_dev_mtu(mtu)) {
+ struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
+
+ /* sanitize non CAN XL bits */
+ cxl->prio &= (CANXL_PRIO_MASK | CANXL_VCID_MASK);
+
+ /* clear VCID in CAN XL frame if pass through is disabled */
+ if (!(ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_PASS))
+ cxl->prio &= CANXL_PRIO_MASK;
+
+ /* set VCID in CAN XL frame if enabled */
+ if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_SET) {
+ cxl->prio &= CANXL_PRIO_MASK;
+ cxl->prio |= ro->tx_vcid_shifted;
+ }
+
+ /* set flag only for 'tagged' VCID values */
+ if (cxl->prio & CANXL_VCID_MASK)
+ cxl->flags |= CANXL_VCID;
+ else
+ cxl->flags &= ~CANXL_VCID;
+
return false;
+ }
return true;
}
static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] canxl: add virtual CAN network identifier support
2024-01-06 19:28 [PATCH] canxl: add virtual CAN network identifier support Oliver Hartkopp
@ 2024-01-06 22:03 ` Oliver Hartkopp
2024-01-07 6:28 ` Vincent MAILHOL
1 sibling, 0 replies; 4+ messages in thread
From: Oliver Hartkopp @ 2024-01-06 22:03 UTC (permalink / raw)
To: linux-can, mkl, mailhol.vincent
Hi all,
I've been working some time how to integrate the CAN XL VCID stuff into
the Linux CAN infrastructure.
Going for a complete IEEE 802.1Q VLAN-style was not possible due to
several reasons. Getting the VCID from the system via cmsg could have
been done with a new extended cmsg analogue to the packet socket. But
'sending' different VCIDs is not intended and would really lead to a
complete mess.
To be able to read/write complete CAN XL frames and to store and replay
the entire CAN traffic (with candump/canplayer) the VCID is now stored
in an unused space in the prio element. Looks a bit ugly but is handy to
use and does not interfere the filter infrastructure and CAN/CANFD frames.
Defining VLAN-like CAN network devices like canxl0.123 could still be
done later. Same applies to Ethernet-like software interfaces to carry
IP over CAN XL.
Some PoC tools and a WireShark plugin to 'decode' CAN XL traffic can be
found here:
https://github.com/hartkopp/canxl-utils
Thanks for review and feedback.
Best regards,
Oliver
On 06.01.24 20:28, Oliver Hartkopp wrote:
> CAN XL data frames contain an 8-bit virtual CAN network identifier (VCID).
> A VCID value of zero represents an 'untagged' CAN XL frame.
>
> To receive and send these optional VCIDs via CAN_RAW sockets a new socket
> option CAN_RAW_XL_VCID_OPTS is introduced to define/access VCID content:
>
> - tx: set the outgoing VCID value by the kernel (one fixed 8-bit value)
> - tx: pass through VCID values from the user space (e.g. for traffic replay)
> - rx: apply VCID receive filter (value/mask) to be passed to the user space
>
> With the 'tx pass through' option CAN_RAW_XL_VCID_TX_PASS all valid VCID
> values can be send, e.g. to replay full qualified CAN XL traffic.
>
> The VCID value provided for the CAN_RAW_XL_VCID_TX_SET option will
> override the VCID value in the struct canxl_frame.prio defined for
> CAN_RAW_XL_VCID_TX_PASS when both flags are set.
>
> With a rx_vcid_mask of zero all possible VCID values (0x00 - 0xFF) are passed
> to the user space when the CAN_RAW_XL_VCID_RX_FILTER flag is set. Without this
> flag only untagged CAN XL frames (VCID = 0x00, CANXL_VCID flag unset) are
> delivered to the user space.
>
> The 8-bit VCID is stored inside the CAN XL prio element (only in CAN XL
> frames!) to not interfere with other CAN content or the CAN filters
> provided by the CAN_RAW sockets and kernel infrastruture.
>
> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
> ---
> include/uapi/linux/can.h | 14 +++++--
> include/uapi/linux/can/raw.h | 14 +++++++
> net/can/raw.c | 79 ++++++++++++++++++++++++++++++++++--
> 3 files changed, 100 insertions(+), 7 deletions(-)
>
> diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h
> index 939db2388208..30453905fbd2 100644
> --- a/include/uapi/linux/can.h
> +++ b/include/uapi/linux/can.h
> @@ -180,34 +180,40 @@ struct canfd_frame {
> };
>
> /*
> * defined bits for canxl_frame.flags
> *
> - * The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC
> - * and shares the relative position of the struct can[fd]_frame.len element.
> + * The canxl_frame.flags element contains CAN XL frame specific bits and
> + * shares the relative position of the struct can[fd]_frame.len element.
> * The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame.
> * As a side effect setting this bit intentionally breaks the length checks
> * for Classical CAN and CAN FD frames.
> *
> * Undefined bits in canxl_frame.flags are reserved and shall be set to zero.
> */
> #define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
> #define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
> +#define CANXL_VCID 0x02 /* prio contains the virtual CAN network identifier */
> +
> +/* the 8-bit VCID is optionally placed in the canxl_frame.prio element */
> +#define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */
> +#define CANXL_VCID_VAL_MASK 0xFFUL /* VCID is an 8-bit value */
> +#define CANXL_VCID_MASK (CANXL_VCID_VAL_MASK << CANXL_VCID_OFFSET)
>
> /**
> * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure
> - * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags
> + * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags / VCID
> * @flags: additional flags for CAN XL
> * @sdt: SDU (service data unit) type
> * @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN)
> * @af: acceptance field
> * @data: CAN XL frame payload (CANXL_MIN_DLEN .. CANXL_MAX_DLEN byte)
> *
> * @prio shares the same position as @can_id from struct can[fd]_frame.
> */
> struct canxl_frame {
> - canid_t prio; /* 11 bit priority for arbitration (canid_t) */
> + canid_t prio; /* 11 bit priority for arbitration / 8 bit VCID */
> __u8 flags; /* additional flags for CAN XL */
> __u8 sdt; /* SDU (service data unit) type */
> __u16 len; /* frame payload length in byte */
> __u32 af; /* acceptance field */
> __u8 data[CANXL_MAX_DLEN];
> diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h
> index 31622c9b7988..8890b0d2fd48 100644
> --- a/include/uapi/linux/can/raw.h
> +++ b/include/uapi/linux/can/raw.h
> @@ -63,8 +63,22 @@ enum {
> CAN_RAW_LOOPBACK, /* local loopback (default:on) */
> CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */
> CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */
> CAN_RAW_JOIN_FILTERS, /* all filters must match to trigger */
> CAN_RAW_XL_FRAMES, /* allow CAN XL frames (default:off) */
> + CAN_RAW_XL_VCID_OPTS, /* CAN XL VCID configuration options */
> };
>
> +struct can_raw_vcid_options {
> +
> + __u8 flags; /* flags for vcid (filter) behaviour */
> + __u8 tx_vcid; /* VCID value set into canxl_frame.prio */
> + __u8 rx_vcid; /* VCID value for VCID filter */
> + __u8 rx_vcid_mask; /* VCID mask for VCID filter */
> +
> +};
> +
> +#define CAN_RAW_XL_VCID_TX_SET 0x01
> +#define CAN_RAW_XL_VCID_TX_PASS 0x02
> +#define CAN_RAW_XL_VCID_RX_FILTER 0x04
> +
> #endif /* !_UAPI_CAN_RAW_H */
> diff --git a/net/can/raw.c b/net/can/raw.c
> index e6b822624ba2..a6f0b6a0a934 100644
> --- a/net/can/raw.c
> +++ b/net/can/raw.c
> @@ -89,10 +89,14 @@ struct raw_sock {
> struct list_head notifier;
> int loopback;
> int recv_own_msgs;
> int fd_frames;
> int xl_frames;
> + struct can_raw_vcid_options raw_vcid_opts;
> + canid_t tx_vcid_shifted;
> + canid_t rx_vcid_shifted;
> + canid_t rx_vcid_mask_shifted;
> int join_filters;
> int count; /* number of active filters */
> struct can_filter dfilter; /* default/single filter */
> struct can_filter *filter; /* pointer to filter(s) */
> can_err_mask_t err_mask;
> @@ -132,14 +136,33 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
> /* check the received tx sock reference */
> if (!ro->recv_own_msgs && oskb->sk == sk)
> return;
>
> /* make sure to not pass oversized frames to the socket */
> - if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
> - (!ro->xl_frames && can_is_canxl_skb(oskb)))
> + if (!ro->fd_frames && can_is_canfd_skb(oskb))
> return;
>
> + if (can_is_canxl_skb(oskb)) {
> + struct canxl_frame *cxl = (struct canxl_frame *)oskb->data;
> +
> + /* make sure to not pass oversized frames to the socket */
> + if (!ro->xl_frames)
> + return;
> +
> + /* filter CAN XL VCID content */
> + if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_RX_FILTER) {
> + /* apply VCID filter if user enabled the filter */
> + if ((cxl->prio & ro->rx_vcid_mask_shifted) !=
> + (ro->rx_vcid_shifted & ro->rx_vcid_mask_shifted))
> + return;
> + } else {
> + /* no filter => do not forward VCID tagged frames */
> + if (cxl->prio & CANXL_VCID_MASK)
> + return;
> + }
> + }
> +
> /* eliminate multiple filter matches for the same skb */
> if (this_cpu_ptr(ro->uniq)->skb == oskb &&
> this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) {
> if (!ro->join_filters)
> return;
> @@ -696,10 +719,23 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
> /* Enabling CAN XL includes CAN FD */
> if (ro->xl_frames)
> ro->fd_frames = ro->xl_frames;
> break;
>
> + case CAN_RAW_XL_VCID_OPTS:
> + if (optlen != sizeof(ro->raw_vcid_opts))
> + return -EINVAL;
> +
> + if (copy_from_sockptr(&ro->raw_vcid_opts, optval, optlen))
> + return -EFAULT;
> +
> + /* prepare 32 bit values for handling in hot path */
> + ro->tx_vcid_shifted = ro->raw_vcid_opts.tx_vcid << CANXL_VCID_OFFSET;
> + ro->rx_vcid_shifted = ro->raw_vcid_opts.rx_vcid << CANXL_VCID_OFFSET;
> + ro->rx_vcid_mask_shifted = ro->raw_vcid_opts.rx_vcid_mask << CANXL_VCID_OFFSET;
> + break;
> +
> case CAN_RAW_JOIN_FILTERS:
> if (optlen != sizeof(ro->join_filters))
> return -EINVAL;
>
> if (copy_from_sockptr(&ro->join_filters, optval, optlen))
> @@ -784,10 +820,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
> if (len > sizeof(int))
> len = sizeof(int);
> val = &ro->xl_frames;
> break;
>
> + case CAN_RAW_XL_VCID_OPTS:
> + /* user space buffer to small for VCID opts? */
> + if (len < sizeof(ro->raw_vcid_opts)) {
> + /* return -ERANGE and needed space in optlen */
> + err = -ERANGE;
> + if (put_user(sizeof(ro->raw_vcid_opts), optlen))
> + err = -EFAULT;
> + } else {
> + if (len > sizeof(ro->raw_vcid_opts))
> + len = sizeof(ro->raw_vcid_opts);
> + if (copy_to_user(optval, &ro->raw_vcid_opts, len))
> + err = -EFAULT;
> + }
> + break;
> +
> case CAN_RAW_JOIN_FILTERS:
> if (len > sizeof(int))
> len = sizeof(int);
> val = &ro->join_filters;
> break;
> @@ -814,12 +865,34 @@ static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
> (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))
> + can_is_canxl_dev_mtu(mtu)) {
> + struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
> +
> + /* sanitize non CAN XL bits */
> + cxl->prio &= (CANXL_PRIO_MASK | CANXL_VCID_MASK);
> +
> + /* clear VCID in CAN XL frame if pass through is disabled */
> + if (!(ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_PASS))
> + cxl->prio &= CANXL_PRIO_MASK;
> +
> + /* set VCID in CAN XL frame if enabled */
> + if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_SET) {
> + cxl->prio &= CANXL_PRIO_MASK;
> + cxl->prio |= ro->tx_vcid_shifted;
> + }
> +
> + /* set flag only for 'tagged' VCID values */
> + if (cxl->prio & CANXL_VCID_MASK)
> + cxl->flags |= CANXL_VCID;
> + else
> + cxl->flags &= ~CANXL_VCID;
> +
> return false;
> + }
>
> return true;
> }
>
> static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] canxl: add virtual CAN network identifier support
2024-01-06 19:28 [PATCH] canxl: add virtual CAN network identifier support Oliver Hartkopp
2024-01-06 22:03 ` Oliver Hartkopp
@ 2024-01-07 6:28 ` Vincent MAILHOL
2024-01-07 10:21 ` Oliver Hartkopp
1 sibling, 1 reply; 4+ messages in thread
From: Vincent MAILHOL @ 2024-01-07 6:28 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: linux-can, mkl
On Sun. 7 Jan. 2024 at 04:47, Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>
> CAN XL data frames contain an 8-bit virtual CAN network identifier (VCID).
> A VCID value of zero represents an 'untagged' CAN XL frame.
>
> To receive and send these optional VCIDs via CAN_RAW sockets a new socket
> option CAN_RAW_XL_VCID_OPTS is introduced to define/access VCID content:
>
> - tx: set the outgoing VCID value by the kernel (one fixed 8-bit value)
> - tx: pass through VCID values from the user space (e.g. for traffic replay)
> - rx: apply VCID receive filter (value/mask) to be passed to the user space
>
> With the 'tx pass through' option CAN_RAW_XL_VCID_TX_PASS all valid VCID
> values can be send, e.g. to replay full qualified CAN XL traffic.
^^^^
sent
>
> The VCID value provided for the CAN_RAW_XL_VCID_TX_SET option will
> override the VCID value in the struct canxl_frame.prio defined for
> CAN_RAW_XL_VCID_TX_PASS when both flags are set.
>
> With a rx_vcid_mask of zero all possible VCID values (0x00 - 0xFF) are passed
> to the user space when the CAN_RAW_XL_VCID_RX_FILTER flag is set. Without this
> flag only untagged CAN XL frames (VCID = 0x00, CANXL_VCID flag unset) are
> delivered to the user space.
>
> The 8-bit VCID is stored inside the CAN XL prio element (only in CAN XL
> frames!) to not interfere with other CAN content or the CAN filters
> provided by the CAN_RAW sockets and kernel infrastruture.
^^^^^^^^^^^^^
infrastructure
>
> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
> ---
> include/uapi/linux/can.h | 14 +++++--
> include/uapi/linux/can/raw.h | 14 +++++++
> net/can/raw.c | 79 ++++++++++++++++++++++++++++++++++--
> 3 files changed, 100 insertions(+), 7 deletions(-)
>
> diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h
> index 939db2388208..30453905fbd2 100644
> --- a/include/uapi/linux/can.h
> +++ b/include/uapi/linux/can.h
> @@ -180,34 +180,40 @@ struct canfd_frame {
> };
>
> /*
> * defined bits for canxl_frame.flags
> *
> - * The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC
> - * and shares the relative position of the struct can[fd]_frame.len element.
> + * The canxl_frame.flags element contains CAN XL frame specific bits and
> + * shares the relative position of the struct can[fd]_frame.len element.
> * The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame.
> * As a side effect setting this bit intentionally breaks the length checks
> * for Classical CAN and CAN FD frames.
> *
> * Undefined bits in canxl_frame.flags are reserved and shall be set to zero.
> */
> #define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
> #define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
> +#define CANXL_VCID 0x02 /* prio contains the virtual CAN network identifier */
> +
> +/* the 8-bit VCID is optionally placed in the canxl_frame.prio element */
> +#define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */
> +#define CANXL_VCID_VAL_MASK 0xFFUL /* VCID is an 8-bit value */
> +#define CANXL_VCID_MASK (CANXL_VCID_VAL_MASK << CANXL_VCID_OFFSET)
>
> /**
> * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure
> - * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags
> + * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags / VCID
> * @flags: additional flags for CAN XL
> * @sdt: SDU (service data unit) type
> * @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN)
> * @af: acceptance field
> * @data: CAN XL frame payload (CANXL_MIN_DLEN .. CANXL_MAX_DLEN byte)
> *
> * @prio shares the same position as @can_id from struct can[fd]_frame.
> */
> struct canxl_frame {
> - canid_t prio; /* 11 bit priority for arbitration (canid_t) */
> + canid_t prio; /* 11 bit priority for arbitration / 8 bit VCID */
Isn't this a UAPI breaking change? Prior to this patch, the applications may do:
canxl_frame.prio
to get the prio, but after this patch, applications are required to do:
canxl_frame.prio & CANXL_PRIO_MASK
in order to mask out the VCID (currently, there are no requirements
that canxl_frame.prio must be masked before use).
In the past, I was reluctant to acknowledge the introduction of CANXL
in the kernel prior to reading the ISO standard because I was afraid
of such UAPI stability issues. Now we have to deal with it.
What do you think of:
struct canxl_frame {
#if defined(__LITTLE_ENDIAN)
__u16 prio; /* 11 bit priority for arbitration */
__u8 vcid; /* 8 bit VCID */
__u8 __reserved; /* must be 0 */
/* ... */
#elif defined(__BIG_ENDIAN)
__u8 __reserved; /* must be 0 */
__u8 vcid; /* 8 bit VCID */
__u16 prio; /* 11 bit priority for arbitration */
#else
#error "Unknown endianness"
#endif
}
Here, canxl_frame.prio always gives a correct value without need for
CANXL_PRIO_MASK masking. The big/little endianness checks are needed
to maintain the ABI compatibility. Not yet tested, so forgive if there
is a mistake. Getting the endianness logic correct on a first try is
not easy.
Also, the VCID can now be accessed through canxl_frame.vcid instead of
relying on some mask and shift logic.
The drawback is that you lose the can_id type. For what I understand,
this is only used for filtering. If we absolutely need to maintain the
canid_t, then maybe:
struct canxl_frame {
union {
canid_t filter;
struct {
#if defined(__LITTLE_ENDIAN)
__u16 prio; /* 11 bit priority for arbitration */
__u8 vcid; /* 8 bit VCID */
__u8 __reserved; /* must be 0 */
#elif defined(__BIG_ENDIAN)
__u8 __reserved; /* must be 0 */
__u8 vcid; /* 8 bit VCID */
__u16 prio; /* 11 bit priority for arbitration */
#else
#error "Unknown endianness"
#endif
};
};
/* ... */
}
But I think it is better to drop it. If someone wants a canid_t, then
he or she can just cast the XL frame to either struct can_frame or
struct canfd_frame.
Though?
> __u8 flags; /* additional flags for CAN XL */
If CANXL_VCID is set, can vcid be zero? If not, no need for a flag.
Just need to check if canxl_frame.vcid is not zero.
> __u8 sdt; /* SDU (service data unit) type */
> __u16 len; /* frame payload length in byte */
> __u32 af; /* acceptance field */
> __u8 data[CANXL_MAX_DLEN];
> diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h
> index 31622c9b7988..8890b0d2fd48 100644
> --- a/include/uapi/linux/can/raw.h
> +++ b/include/uapi/linux/can/raw.h
> @@ -63,8 +63,22 @@ enum {
> CAN_RAW_LOOPBACK, /* local loopback (default:on) */
> CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */
> CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */
> CAN_RAW_JOIN_FILTERS, /* all filters must match to trigger */
> CAN_RAW_XL_FRAMES, /* allow CAN XL frames (default:off) */
> + CAN_RAW_XL_VCID_OPTS, /* CAN XL VCID configuration options */
> };
>
> +struct can_raw_vcid_options {
> +
> + __u8 flags; /* flags for vcid (filter) behaviour */
> + __u8 tx_vcid; /* VCID value set into canxl_frame.prio */
> + __u8 rx_vcid; /* VCID value for VCID filter */
> + __u8 rx_vcid_mask; /* VCID mask for VCID filter */
> +
> +};
> +
> +#define CAN_RAW_XL_VCID_TX_SET 0x01
> +#define CAN_RAW_XL_VCID_TX_PASS 0x02
> +#define CAN_RAW_XL_VCID_RX_FILTER 0x04
> +
> #endif /* !_UAPI_CAN_RAW_H */
> diff --git a/net/can/raw.c b/net/can/raw.c
> index e6b822624ba2..a6f0b6a0a934 100644
> --- a/net/can/raw.c
> +++ b/net/can/raw.c
> @@ -89,10 +89,14 @@ struct raw_sock {
> struct list_head notifier;
> int loopback;
> int recv_own_msgs;
> int fd_frames;
> int xl_frames;
> + struct can_raw_vcid_options raw_vcid_opts;
> + canid_t tx_vcid_shifted;
> + canid_t rx_vcid_shifted;
> + canid_t rx_vcid_mask_shifted;
> int join_filters;
> int count; /* number of active filters */
> struct can_filter dfilter; /* default/single filter */
> struct can_filter *filter; /* pointer to filter(s) */
> can_err_mask_t err_mask;
> @@ -132,14 +136,33 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
> /* check the received tx sock reference */
> if (!ro->recv_own_msgs && oskb->sk == sk)
> return;
>
> /* make sure to not pass oversized frames to the socket */
> - if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
> - (!ro->xl_frames && can_is_canxl_skb(oskb)))
> + if (!ro->fd_frames && can_is_canfd_skb(oskb))
> return;
>
> + if (can_is_canxl_skb(oskb)) {
> + struct canxl_frame *cxl = (struct canxl_frame *)oskb->data;
> +
> + /* make sure to not pass oversized frames to the socket */
> + if (!ro->xl_frames)
> + return;
> +
> + /* filter CAN XL VCID content */
> + if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_RX_FILTER) {
> + /* apply VCID filter if user enabled the filter */
> + if ((cxl->prio & ro->rx_vcid_mask_shifted) !=
> + (ro->rx_vcid_shifted & ro->rx_vcid_mask_shifted))
> + return;
> + } else {
> + /* no filter => do not forward VCID tagged frames */
> + if (cxl->prio & CANXL_VCID_MASK)
> + return;
> + }
> + }
> +
> /* eliminate multiple filter matches for the same skb */
> if (this_cpu_ptr(ro->uniq)->skb == oskb &&
> this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) {
> if (!ro->join_filters)
> return;
> @@ -696,10 +719,23 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
> /* Enabling CAN XL includes CAN FD */
> if (ro->xl_frames)
> ro->fd_frames = ro->xl_frames;
> break;
>
> + case CAN_RAW_XL_VCID_OPTS:
> + if (optlen != sizeof(ro->raw_vcid_opts))
> + return -EINVAL;
> +
> + if (copy_from_sockptr(&ro->raw_vcid_opts, optval, optlen))
> + return -EFAULT;
> +
> + /* prepare 32 bit values for handling in hot path */
> + ro->tx_vcid_shifted = ro->raw_vcid_opts.tx_vcid << CANXL_VCID_OFFSET;
> + ro->rx_vcid_shifted = ro->raw_vcid_opts.rx_vcid << CANXL_VCID_OFFSET;
> + ro->rx_vcid_mask_shifted = ro->raw_vcid_opts.rx_vcid_mask << CANXL_VCID_OFFSET;
> + break;
> +
> case CAN_RAW_JOIN_FILTERS:
> if (optlen != sizeof(ro->join_filters))
> return -EINVAL;
>
> if (copy_from_sockptr(&ro->join_filters, optval, optlen))
> @@ -784,10 +820,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
> if (len > sizeof(int))
> len = sizeof(int);
> val = &ro->xl_frames;
> break;
>
> + case CAN_RAW_XL_VCID_OPTS:
> + /* user space buffer to small for VCID opts? */
> + if (len < sizeof(ro->raw_vcid_opts)) {
> + /* return -ERANGE and needed space in optlen */
> + err = -ERANGE;
> + if (put_user(sizeof(ro->raw_vcid_opts), optlen))
> + err = -EFAULT;
> + } else {
> + if (len > sizeof(ro->raw_vcid_opts))
> + len = sizeof(ro->raw_vcid_opts);
> + if (copy_to_user(optval, &ro->raw_vcid_opts, len))
> + err = -EFAULT;
> + }
> + break;
> +
> case CAN_RAW_JOIN_FILTERS:
> if (len > sizeof(int))
> len = sizeof(int);
> val = &ro->join_filters;
> break;
> @@ -814,12 +865,34 @@ static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
> (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))
> + can_is_canxl_dev_mtu(mtu)) {
> + struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
> +
> + /* sanitize non CAN XL bits */
> + cxl->prio &= (CANXL_PRIO_MASK | CANXL_VCID_MASK);
> +
> + /* clear VCID in CAN XL frame if pass through is disabled */
> + if (!(ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_PASS))
> + cxl->prio &= CANXL_PRIO_MASK;
> +
> + /* set VCID in CAN XL frame if enabled */
> + if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_SET) {
> + cxl->prio &= CANXL_PRIO_MASK;
> + cxl->prio |= ro->tx_vcid_shifted;
> + }
> +
> + /* set flag only for 'tagged' VCID values */
> + if (cxl->prio & CANXL_VCID_MASK)
> + cxl->flags |= CANXL_VCID;
> + else
> + cxl->flags &= ~CANXL_VCID;
> +
> return false;
> + }
>
> return true;
> }
>
> static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] canxl: add virtual CAN network identifier support
2024-01-07 6:28 ` Vincent MAILHOL
@ 2024-01-07 10:21 ` Oliver Hartkopp
0 siblings, 0 replies; 4+ messages in thread
From: Oliver Hartkopp @ 2024-01-07 10:21 UTC (permalink / raw)
To: Vincent MAILHOL; +Cc: linux-can, mkl
Hi Vincent,
thanks for your review!
On 07.01.24 07:28, Vincent MAILHOL wrote:
> On Sun. 7 Jan. 2024 at 04:47, Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>> values can be send, e.g. to replay full qualified CAN XL traffic.
> ^^^^
> sent
ACK
>> provided by the CAN_RAW sockets and kernel infrastruture.
> ^^^^^^^^^^^^^
> infrastructure
ACK
>> struct canxl_frame {
>> - canid_t prio; /* 11 bit priority for arbitration (canid_t) */
>> + canid_t prio; /* 11 bit priority for arbitration / 8 bit VCID */
>
> Isn't this a UAPI breaking change? Prior to this patch, the applications may do:
>
> canxl_frame.prio
>
> to get the prio, but after this patch, applications are required to do:
>
> canxl_frame.prio & CANXL_PRIO_MASK
>
Not really. I also thought about it but you *only* need to take care
about the VCID content when you have enabled it explicitly with the new
sockopt. Otherwise you will never see anything beyond the 32 bit prio.
> in order to mask out the VCID (currently, there are no requirements
> that canxl_frame.prio must be masked before use).
> In the past, I was reluctant to acknowledge the introduction of CANXL
> in the kernel prior to reading the ISO standard because I was afraid
> of such UAPI stability issues. Now we have to deal with it.
Yes, but that kind of extension would be backwards compatible.
> What do you think of:
>
> struct canxl_frame {
> #if defined(__LITTLE_ENDIAN)
> __u16 prio; /* 11 bit priority for arbitration */
> __u8 vcid; /* 8 bit VCID */
> __u8 __reserved; /* must be 0 */
> /* ... */
> #elif defined(__BIG_ENDIAN)
> __u8 __reserved; /* must be 0 */
> __u8 vcid; /* 8 bit VCID */
> __u16 prio; /* 11 bit priority for arbitration */
> #else
> #error "Unknown endianness"
> #endif
> }
>
> Here, canxl_frame.prio always gives a correct value without need for
> CANXL_PRIO_MASK masking. The big/little endianness checks are needed
> to maintain the ABI compatibility. Not yet tested, so forgive if there
> is a mistake. Getting the endianness logic correct on a first try is
> not easy.
Yes, I tested such approach too (with little endian only) and it worked
great - and of course looked better in the code.
> Also, the VCID can now be accessed through canxl_frame.vcid instead of
> relying on some mask and shift logic.
Right. That looked nice.
> The drawback is that you lose the can_id type. For what I understand,
> this is only used for filtering. If we absolutely need to maintain the
> canid_t, then maybe:
>
> struct canxl_frame {
> union {
> canid_t filter;
> struct {
> #if defined(__LITTLE_ENDIAN)
> __u16 prio; /* 11 bit priority for arbitration */
> __u8 vcid; /* 8 bit VCID */
> __u8 __reserved; /* must be 0 */
> #elif defined(__BIG_ENDIAN)
> __u8 __reserved; /* must be 0 */
> __u8 vcid; /* 8 bit VCID */
> __u16 prio; /* 11 bit priority for arbitration */
> #else
> #error "Unknown endianness"
> #endif
> };
> };
> /* ... */
> }
>
> But I think it is better to drop it. If someone wants a canid_t, then
> he or she can just cast the XL frame to either struct can_frame or
> struct canfd_frame.
>
> Though?
My only concern is that it looks really ugly :-/
The change of the prio element from u32 to u16 will also not harm anyone
as I assume to be the only person who's currently working with CAN XL
frames on virtual CAN interfaces:
https://github.com/hartkopp?tab=repositories&q=can-cia
I'll prepare a patch that picks up this suggestion of an __u16 prio etc.
Maybe we can add some compile time checks to ensure the correct struct
layout for this case.
>
>> __u8 flags; /* additional flags for CAN XL */
>
> If CANXL_VCID is set, can vcid be zero? If not, no need for a flag.
> Just need to check if canxl_frame.vcid is not zero.
>
This is probably indeed a leftover which can be removed with my latest
implementation. Will recheck.
Many thanks,
Oliver
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-01-07 10:24 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-06 19:28 [PATCH] canxl: add virtual CAN network identifier support Oliver Hartkopp
2024-01-06 22:03 ` Oliver Hartkopp
2024-01-07 6:28 ` Vincent MAILHOL
2024-01-07 10:21 ` Oliver Hartkopp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox