Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: fix multiple delivery of a single CAN frame for overlapping CAN filters

The CAN_RAW socket can set multiple CAN identifier specific filters that lead
to multiple filters in the af_can.c filter processing. These filters are
indenpendent from each other which leads to logical OR'ed filters when applied.

This patch makes sure that every CAN frame which is filtered for a specific
socket is only delivered once to the user space. This is independent from the
number of matching CAN filters of this socket.

As the raw_rcv() function is executed from NET_RX softirq the introduced
variables are implemented as per-CPU variables to avoid extensive locking at
CAN frame reception time.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

authored by

Oliver Hartkopp and committed by
Marc Kleine-Budde
514ac99c a0bc163a

+21
+21
net/can/raw.c
··· 74 74 * storing the single filter in dfilter, to avoid using dynamic memory. 75 75 */ 76 76 77 + struct uniqframe { 78 + ktime_t tstamp; 79 + const struct sk_buff *skb; 80 + }; 81 + 77 82 struct raw_sock { 78 83 struct sock sk; 79 84 int bound; ··· 91 86 struct can_filter dfilter; /* default/single filter */ 92 87 struct can_filter *filter; /* pointer to filter(s) */ 93 88 can_err_mask_t err_mask; 89 + struct uniqframe __percpu *uniq; 94 90 }; 95 91 96 92 /* ··· 128 122 /* do not pass non-CAN2.0 frames to a legacy socket */ 129 123 if (!ro->fd_frames && oskb->len != CAN_MTU) 130 124 return; 125 + 126 + /* eliminate multiple filter matches for the same skb */ 127 + if (this_cpu_ptr(ro->uniq)->skb == oskb && 128 + ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) { 129 + return; 130 + } else { 131 + this_cpu_ptr(ro->uniq)->skb = oskb; 132 + this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp; 133 + } 131 134 132 135 /* clone the given skb to be able to enqueue it into the rcv queue */ 133 136 skb = skb_clone(oskb, GFP_ATOMIC); ··· 312 297 ro->recv_own_msgs = 0; 313 298 ro->fd_frames = 0; 314 299 300 + /* alloc_percpu provides zero'ed memory */ 301 + ro->uniq = alloc_percpu(struct uniqframe); 302 + if (unlikely(!ro->uniq)) 303 + return -ENOMEM; 304 + 315 305 /* set notifier */ 316 306 ro->notifier.notifier_call = raw_notifier; 317 307 ··· 359 339 ro->ifindex = 0; 360 340 ro->bound = 0; 361 341 ro->count = 0; 342 + free_percpu(ro->uniq); 362 343 363 344 sock_orphan(sk); 364 345 sock->sk = NULL;