mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	can: canxl: add virtual CAN network identifier support
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 sent, 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) are delivered to the user space (default). 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> Link: https://lore.kernel.org/all/20240212213550.18516-1-socketcan@hartkopp.net Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
		
							parent
							
								
									383de5664c
								
							
						
					
					
						commit
						c83c22ec14
					
				
					 4 changed files with 110 additions and 10 deletions
				
			
		| 
						 | 
				
			
			@ -193,9 +193,14 @@ struct canfd_frame {
 | 
			
		|||
#define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
 | 
			
		||||
#define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +210,7 @@ struct canfd_frame {
 | 
			
		|||
 * @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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,6 +65,22 @@ enum {
 | 
			
		|||
	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 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* configuration for CAN XL virtual CAN identifier (VCID) handling */
 | 
			
		||||
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 */
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* can_raw_vcid_options.flags for CAN XL virtual CAN identifier handling */
 | 
			
		||||
#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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -865,6 +865,8 @@ static __init int can_init(void)
 | 
			
		|||
	/* check for correct padding to be able to use the structs similarly */
 | 
			
		||||
	BUILD_BUG_ON(offsetof(struct can_frame, len) !=
 | 
			
		||||
		     offsetof(struct canfd_frame, len) ||
 | 
			
		||||
		     offsetof(struct can_frame, len) !=
 | 
			
		||||
		     offsetof(struct canxl_frame, flags) ||
 | 
			
		||||
		     offsetof(struct can_frame, data) !=
 | 
			
		||||
		     offsetof(struct canfd_frame, data));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,10 @@ struct raw_sock {
 | 
			
		|||
	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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -134,10 +138,29 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
 | 
			
		|||
		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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -698,6 +721,19 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
 | 
			
		|||
			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;
 | 
			
		||||
| 
						 | 
				
			
			@ -786,6 +822,21 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
 | 
			
		|||
		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);
 | 
			
		||||
| 
						 | 
				
			
			@ -803,23 +854,41 @@ 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)
 | 
			
		||||
static void raw_put_canxl_vcid(struct raw_sock *ro, struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned int raw_check_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;
 | 
			
		||||
		return CAN_MTU;
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
		return CANFD_MTU;
 | 
			
		||||
 | 
			
		||||
	/* 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 CANXL_MTU;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 | 
			
		||||
| 
						 | 
				
			
			@ -829,6 +898,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 | 
			
		|||
	struct sockcm_cookie sockc;
 | 
			
		||||
	struct sk_buff *skb;
 | 
			
		||||
	struct net_device *dev;
 | 
			
		||||
	unsigned int txmtu;
 | 
			
		||||
	int ifindex;
 | 
			
		||||
	int err = -EINVAL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -869,9 +939,16 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 | 
			
		|||
		goto free_skb;
 | 
			
		||||
 | 
			
		||||
	err = -EINVAL;
 | 
			
		||||
	if (raw_bad_txframe(ro, skb, dev->mtu))
 | 
			
		||||
 | 
			
		||||
	/* check for valid CAN (CC/FD/XL) frame content */
 | 
			
		||||
	txmtu = raw_check_txframe(ro, skb, dev->mtu);
 | 
			
		||||
	if (!txmtu)
 | 
			
		||||
		goto free_skb;
 | 
			
		||||
 | 
			
		||||
	/* only CANXL: clear/forward/set VCID value */
 | 
			
		||||
	if (txmtu == CANXL_MTU)
 | 
			
		||||
		raw_put_canxl_vcid(ro, skb);
 | 
			
		||||
 | 
			
		||||
	sockcm_init(&sockc, sk);
 | 
			
		||||
	if (msg->msg_controllen) {
 | 
			
		||||
		err = sock_cmsg_send(sk, msg, &sockc);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue