Search code examples
c++linuxsocketscan-bus

How to set CAN Filters on Linux? Do they work on virtual CANs?


I just programming a ROS node where I'm trying to set up filters on a CAN Socket. I would like to get the messages with CAN_Id 0x6A0 to 0x6A4 as well as the CAN_Id 0x0CF02980.

I'm using the socket can from linux. Here an extract of my code :

#include <linux/can.h>
#include <linux/can/raw.h>


can_filter m_rfilter[2];

// Open can socket
struct ifreq ifr = {};
struct sockaddr_can addr = {};
if ((g_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
{
perror("Socket");
}

strcpy(static_cast<char*>(ifr.ifr_name), "can0");
ioctl(g_socket, SIOCGIFINDEX, &ifr);

memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

if (bind(g_socket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0)
{ 
perror("Bind");
}

m_rfilter[0].can_id = 0x6A0;
m_rfilter[0].can_mask = 0xFF8; //I already tried 0x958
m_rfilter[1].can_id = 0x0CF02980;
m_rfilter[1].can_mask = 0xFFFFFFFF;

// Set the CAN filters to receive only the desired CAN IDs
setsockopt(g_socket, SOL_CAN_RAW, CAN_RAW_FILTER, &m_rfilter, 2);`

I tried my algo using a recorded camdump containing the expected messages and some others on a virtual machine (virtual CAN):

can0       784   \[8\]  B0 84 25 00 00 00 00 00
can0  0CF02980   \[8\]  00 00 78 00 00 82 00 00
can0       6A0   \[8\]  F9 18 6A 50 89 67 40 0D
can0       784   \[8\]  B0 84 25 00 00 00 00 00
can0  0CF02980   \[8\]  00 00 78 00 00 82 00 00
can0       780   \[8\]  00 00 00 00 98 21 00 00
can0  0CF02980   \[8\]  00 00 78 00 00 82 00 00
can0       780   \[8\]  00 00 00 00 98 21 00 00
can0  0CF02980   \[8\]  00 00 78 00 00 82 00 00
can0       784   \[8\]  B0 84 25 00 00 00 00 00
can0  0CF02980   \[8\]  00 00 78 00 00 82 00 00
can0       784   \[8\]  B0 84 25 00 00 00 00 00
can0  0CF02980   \[8\]  00 00 78 00 00 82 00 00
can0       780   \[8\]  00 00 00 00 98 21 00 00
can0       6A0   \[8\]  F9 18 6A 50 89 68 4C 86

I expected to only receive the filtered Id but I get all of them. I also tried to set the filters to where I expected to discard every incoming CAN message. But also here I get all of them!!!

It really seems, the filtering doesn't work at all!!! Did somebody already implement some CAN filtering on Linux and encounter these problems? According the Linux Doc, it should work as implemented...

Many Thanks in advance for your help


Solution

  • Many thanks for all your answers. I finally find out my mistakes.

    • The first one was the last arguments by setsockopt()
    • To set the filter for 11bit ids, the mask must be the complement of the canid if only this canid must be filtered out. if you want the get a couple of canid as in this example:
    CanFilterId = 0x6A0; // 6A* (e.g. 6A0, 6A1, 6A2, 6A3..)
    MaskCanId =0x95E; // 6A0 - 6A7
    
    • To set the filter for extended IDs (29bits), you must set the EFF Flag. In my case, I just need 0x8cf02980 :
    CanFilterId = 0x8cf02980 | CAN_EFF_FLAG;
    MaskCanId  = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_EFF_MASK);