diff options
Diffstat (limited to 'net/can/raw.c')
| -rw-r--r-- | net/can/raw.c | 63 | 
1 files changed, 56 insertions, 7 deletions
diff --git a/net/can/raw.c b/net/can/raw.c index 00c13ef23661..31b9748cbb4e 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -74,6 +74,12 @@ MODULE_ALIAS("can-proto-1");   * storing the single filter in dfilter, to avoid using dynamic memory.   */ +struct uniqframe { +	ktime_t tstamp; +	const struct sk_buff *skb; +	unsigned int join_rx_count; +}; +  struct raw_sock {  	struct sock sk;  	int bound; @@ -82,10 +88,12 @@ struct raw_sock {  	int loopback;  	int recv_own_msgs;  	int fd_frames; +	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; +	struct uniqframe __percpu *uniq;  };  /* @@ -95,8 +103,8 @@ struct raw_sock {   */  static inline unsigned int *raw_flags(struct sk_buff *skb)  { -	BUILD_BUG_ON(sizeof(skb->cb) <= (sizeof(struct sockaddr_can) + -					 sizeof(unsigned int))); +	sock_skb_cb_check_size(sizeof(struct sockaddr_can) + +			       sizeof(unsigned int));  	/* return pointer after struct sockaddr_can */  	return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]); @@ -123,6 +131,26 @@ static void raw_rcv(struct sk_buff *oskb, void *data)  	if (!ro->fd_frames && oskb->len != CAN_MTU)  		return; +	/* eliminate multiple filter matches for the same skb */ +	if (this_cpu_ptr(ro->uniq)->skb == oskb && +	    ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) { +		if (ro->join_filters) { +			this_cpu_inc(ro->uniq->join_rx_count); +			/* drop frame until all enabled filters matched */ +			if (this_cpu_ptr(ro->uniq)->join_rx_count < ro->count) +				return; +		} else { +			return; +		} +	} else { +		this_cpu_ptr(ro->uniq)->skb = oskb; +		this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp; +		this_cpu_ptr(ro->uniq)->join_rx_count = 1; +		/* drop first frame to check all enabled filters? */ +		if (ro->join_filters && ro->count > 1) +			return; +	} +  	/* clone the given skb to be able to enqueue it into the rcv queue */  	skb = skb_clone(oskb, GFP_ATOMIC);  	if (!skb) @@ -135,7 +163,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data)  	 *  containing the interface index.  	 */ -	BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can)); +	sock_skb_cb_check_size(sizeof(struct sockaddr_can));  	addr = (struct sockaddr_can *)skb->cb;  	memset(addr, 0, sizeof(*addr));  	addr->can_family  = AF_CAN; @@ -296,6 +324,12 @@ static int raw_init(struct sock *sk)  	ro->loopback         = 1;  	ro->recv_own_msgs    = 0;  	ro->fd_frames        = 0; +	ro->join_filters     = 0; + +	/* alloc_percpu provides zero'ed memory */ +	ro->uniq = alloc_percpu(struct uniqframe); +	if (unlikely(!ro->uniq)) +		return -ENOMEM;  	/* set notifier */  	ro->notifier.notifier_call = raw_notifier; @@ -339,6 +373,7 @@ static int raw_release(struct socket *sock)  	ro->ifindex = 0;  	ro->bound   = 0;  	ro->count   = 0; +	free_percpu(ro->uniq);  	sock_orphan(sk);  	sock->sk = NULL; @@ -583,6 +618,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,  		break; +	case CAN_RAW_JOIN_FILTERS: +		if (optlen != sizeof(ro->join_filters)) +			return -EINVAL; + +		if (copy_from_user(&ro->join_filters, optval, optlen)) +			return -EFAULT; + +		break; +  	default:  		return -ENOPROTOOPT;  	} @@ -647,6 +691,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,  		val = &ro->fd_frames;  		break; +	case CAN_RAW_JOIN_FILTERS: +		if (len > sizeof(int)) +			len = sizeof(int); +		val = &ro->join_filters; +		break; +  	default:  		return -ENOPROTOOPT;  	} @@ -658,8 +708,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,  	return 0;  } -static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, -		       struct msghdr *msg, size_t size) +static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)  {  	struct sock *sk = sock->sk;  	struct raw_sock *ro = raw_sk(sk); @@ -728,8 +777,8 @@ send_failed:  	return err;  } -static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, -		       struct msghdr *msg, size_t size, int flags) +static int raw_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, +		       int flags)  {  	struct sock *sk = sock->sk;  	struct sk_buff *skb;  |