ci7i2en4
ci7i2en4

Reputation: 834

SocketCAN: Filtering of frames with certain CAN IDs not working

I'm trying to filter CAN frames with certain IDs as described here: https://landlock.io/linux-doc/landlock-v8/networking/can.html#raw-protocol-sockets-with-can-filters-sock-raw

Part of my code:

struct can_filter rfilter[4];

if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
    fprintf(stderr, "Error while opening socket.\n");
    exit(EXIT_FAILURE);
}
rfilter[0].can_id   = 0x0D6 | CAN_INV_FILTER;
rfilter[0].can_mask = CAN_SFF_MASK;
rfilter[1].can_id   = 0x0D8 | CAN_INV_FILTER;
rfilter[1].can_mask = CAN_SFF_MASK;
rfilter[2].can_id   = 0x0E4 | CAN_INV_FILTER;
rfilter[2].can_mask = CAN_SFF_MASK;
rfilter[3].can_id   = 0x77F | CAN_INV_FILTER;
rfilter[3].can_mask = CAN_SFF_MASK;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

If I use only one of my four filters and comment out the other three, it's working as expected. If I use all four filters, it's not working at all. In that case I'm still receiving everything on the CANbus interface.

So, my guess is that somehow my filters are neutralising each other?! What do I need to change to filter the CAN IDs 0x0D6, 0x0D8, 0x0E4, and 0x77F?

Upvotes: 6

Views: 10008

Answers (2)

Oliver Hartkopp
Oliver Hartkopp

Reputation: 66

You first need to provide the CAN filtes:

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

and additionally an integer value to enable the CAN_RAW_JOIN_FILTERS via setsockopt()

int join_filter = 1;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS, &join_filter, sizeof(join_filter));

Btw. if you provide CAN filters to the CAN_RAW_JOIN_FILTERS to setsockopt() this is an unexpected length and should produce -EINVAL as return code.

See candump code: https://github.com/linux-can/can-utils/commit/1a2467ed29302149d4d1253888ac1f1dfcc11d3f

And yes, Linux 4.4 supports CAN_RAW_JOIN_FILTERS :-)

Upvotes: 4

Beno&#238;t
Beno&#238;t

Reputation: 416

When using CAN_INV_FILTER like you do, you specify "everything goes through except ID_x".

When using CAN_RAW_FILTER, it will check if there is a rule which let the received ID pass. In your case, your rules are contradicting each others, this is why nothing is filtered.

From the documentation:

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.

To have the expected behavior, you should replace:

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

by:

setsockopt(s, SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS, &rfilter, sizeof(rfilter));

NB: it is possible that the CAN_RAW_JOIN_FILTERS option isn't supported by your Linux kernel

Upvotes: 7

Related Questions