diff options
Diffstat (limited to 'net/can/raw.c')
| -rw-r--r-- | net/can/raw.c | 104 | 
1 files changed, 93 insertions, 11 deletions
diff --git a/net/can/raw.c b/net/can/raw.c index e6b822624ba2..00533f64d69d 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -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; @@ -720,7 +756,6 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,  	struct raw_sock *ro = raw_sk(sk);  	int len;  	void *val; -	int err = 0;  	if (level != SOL_CAN_RAW)  		return -EINVAL; @@ -730,7 +765,9 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,  		return -EINVAL;  	switch (optname) { -	case CAN_RAW_FILTER: +	case CAN_RAW_FILTER: { +		int err = 0; +  		lock_sock(sk);  		if (ro->count > 0) {  			int fsize = ro->count * sizeof(struct can_filter); @@ -755,7 +792,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,  		if (!err)  			err = put_user(len, optlen);  		return err; - +	}  	case CAN_RAW_ERR_FILTER:  		if (len > sizeof(can_err_mask_t))  			len = sizeof(can_err_mask_t); @@ -786,6 +823,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,  		val = &ro->xl_frames;  		break; +	case CAN_RAW_XL_VCID_OPTS: { +		int err = 0; + +		/* 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; +		} +		if (!err) +			err = put_user(len, optlen); +		return err; +	}  	case CAN_RAW_JOIN_FILTERS:  		if (len > sizeof(int))  			len = sizeof(int); @@ -803,23 +859,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 +903,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 +944,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);  | 
