forked from mirrors/linux
		
	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
	
	 Oliver Hartkopp
						Oliver Hartkopp