Commit a5581ef4 authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

can: introduce new raw socket option to join the given 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 socket option joines the given CAN filters in the way that only CAN frames
are passed to user space that matched *all* given CAN filters. The semantic for
the applied filters is therefore changed to a logical AND.

This is useful especially when the filterset is a combination of filters where
the CAN_INV_FILTER flag is set in order to notch single CAN IDs or CAN ID
ranges from the incoming traffic.

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: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 514ac99c
...@@ -22,7 +22,8 @@ This file contains ...@@ -22,7 +22,8 @@ This file contains
4.1.3 RAW socket option CAN_RAW_LOOPBACK 4.1.3 RAW socket option CAN_RAW_LOOPBACK
4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS 4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
4.1.5 RAW socket option CAN_RAW_FD_FRAMES 4.1.5 RAW socket option CAN_RAW_FD_FRAMES
4.1.6 RAW socket returned message flags 4.1.6 RAW socket option CAN_RAW_JOIN_FILTERS
4.1.7 RAW socket returned message flags
4.2 Broadcast Manager protocol sockets (SOCK_DGRAM) 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
4.2.1 Broadcast Manager operations 4.2.1 Broadcast Manager operations
4.2.2 Broadcast Manager message flags 4.2.2 Broadcast Manager message flags
...@@ -601,7 +602,22 @@ solution for a couple of reasons: ...@@ -601,7 +602,22 @@ solution for a couple of reasons:
CAN FD frames by checking if the device maximum transfer unit is CANFD_MTU. CAN FD frames by checking if the device maximum transfer unit is CANFD_MTU.
The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall. The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall.
4.1.6 RAW socket returned message flags 4.1.6 RAW socket option CAN_RAW_JOIN_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 (see 4.1.1).
This socket option joines the given CAN filters in the way that only CAN
frames are passed to user space that matched *all* given CAN filters. The
semantic for the applied filters is therefore changed to a logical AND.
This is useful especially when the filterset is a combination of filters
where the CAN_INV_FILTER flag is set in order to notch single CAN IDs or
CAN ID ranges from the incoming traffic.
4.1.7 RAW socket returned message flags
When using recvmsg() call, the msg->msg_flags may contain following flags: When using recvmsg() call, the msg->msg_flags may contain following flags:
......
...@@ -57,6 +57,7 @@ enum { ...@@ -57,6 +57,7 @@ enum {
CAN_RAW_LOOPBACK, /* local loopback (default:on) */ CAN_RAW_LOOPBACK, /* local loopback (default:on) */
CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */ CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */
CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */ CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */
CAN_RAW_JOIN_FILTERS, /* all filters must match to trigger */
}; };
#endif /* !_UAPI_CAN_RAW_H */ #endif /* !_UAPI_CAN_RAW_H */
...@@ -77,6 +77,7 @@ MODULE_ALIAS("can-proto-1"); ...@@ -77,6 +77,7 @@ MODULE_ALIAS("can-proto-1");
struct uniqframe { struct uniqframe {
ktime_t tstamp; ktime_t tstamp;
const struct sk_buff *skb; const struct sk_buff *skb;
unsigned int join_rx_count;
}; };
struct raw_sock { struct raw_sock {
...@@ -87,6 +88,7 @@ struct raw_sock { ...@@ -87,6 +88,7 @@ struct raw_sock {
int loopback; int loopback;
int recv_own_msgs; int recv_own_msgs;
int fd_frames; int fd_frames;
int join_filters;
int count; /* number of active filters */ int count; /* number of active filters */
struct can_filter dfilter; /* default/single filter */ struct can_filter dfilter; /* default/single filter */
struct can_filter *filter; /* pointer to filter(s) */ struct can_filter *filter; /* pointer to filter(s) */
...@@ -132,10 +134,21 @@ static void raw_rcv(struct sk_buff *oskb, void *data) ...@@ -132,10 +134,21 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
/* eliminate multiple filter matches for the same skb */ /* eliminate multiple filter matches for the same skb */
if (this_cpu_ptr(ro->uniq)->skb == oskb && if (this_cpu_ptr(ro->uniq)->skb == oskb &&
ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) { ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) {
return; 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 { } else {
this_cpu_ptr(ro->uniq)->skb = oskb; this_cpu_ptr(ro->uniq)->skb = oskb;
this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp; 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 */ /* clone the given skb to be able to enqueue it into the rcv queue */
...@@ -311,6 +324,7 @@ static int raw_init(struct sock *sk) ...@@ -311,6 +324,7 @@ static int raw_init(struct sock *sk)
ro->loopback = 1; ro->loopback = 1;
ro->recv_own_msgs = 0; ro->recv_own_msgs = 0;
ro->fd_frames = 0; ro->fd_frames = 0;
ro->join_filters = 0;
/* alloc_percpu provides zero'ed memory */ /* alloc_percpu provides zero'ed memory */
ro->uniq = alloc_percpu(struct uniqframe); ro->uniq = alloc_percpu(struct uniqframe);
...@@ -604,6 +618,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, ...@@ -604,6 +618,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
break; 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: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
...@@ -668,6 +691,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, ...@@ -668,6 +691,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
val = &ro->fd_frames; val = &ro->fd_frames;
break; break;
case CAN_RAW_JOIN_FILTERS:
if (len > sizeof(int))
len = sizeof(int);
val = &ro->join_filters;
break;
default: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment