tonton81 / FlexCAN_T4

FlexCAN (CAN 2.0 / CANFD) Library for Teensy 3.x and 4.0
https://forum.pjrc.com/threads/56035-FlexCAN_T4-FlexCAN-for-Teensy-4
MIT License
195 stars 65 forks source link

Masking and Filtering for J1939 Source Addresses #9

Closed jeremy-daily closed 4 years ago

jeremy-daily commented 4 years ago

Hello, I have a need to filter based on SAE J1939 source address. The source address is located in the least significant byte of the extended ID. For example, a message id of XXXXX0B would have a source address of 11 and correspond to a brake controller. However, the current filtering setup is hard to mask off the last byte and apply a filter. To remedy this, I suggest moving the function declaration void setMBFilterProcessing(FLEXCAN_MAILBOX mb_num, uint32_t filter_id, uint32_t calculated_mask); from the private section of the FlexCAN_T4.h file to the public section. There were two locations for this line. This should enable an external program to call the function.

After making this change in the source code header file, I modified the CAN2.0_example_FIFO_with_interrupts.ino sketch to look like this:

#include <FlexCAN_T4.h>
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;

#define NUM_TX_MAILBOXES 2
#define NUM_RX_MAILBOXES 6
void setup(void) {
  Serial.begin(115200); delay(400);
  Can0.begin();
  Can0.setBaudRate(250000);
  Can0.setMaxMB(NUM_TX_MAILBOXES + NUM_RX_MAILBOXES);
  for (int i = 0; i<NUM_RX_MAILBOXES; i++){
    Can0.setMB(i,RX,EXT);
  }
  for (int i = NUM_RX_MAILBOXES; i<(NUM_TX_MAILBOXES + NUM_RX_MAILBOXES); i++){
    Can0.setMB(i,TX,EXT);
  }
  Can0.setMBFilter(REJECT_ALL);
  Can0.enableMBInterrupts();
  Can0.onReceive(MB0,canSniff);
  Can0.onReceive(MB1,canSniff);
  Can0.onReceive(MB2,canSniff);
  Can0.setMBFilterProcessing(MB0,0x00,0xFF);
  Can0.setMBFilterProcessing(MB1,0x03,0xFF);
  Can0.setMBFilterProcessing(MB2,0x0B,0xFF);
  Can0.mailboxStatus();
}

void canSniff(const CAN_message_t &msg) {
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();
}

void loop() {
  Can0.events();
}

Running the example on a fuzzed network, I can get an output on the Arduino Serial Monitor that looks like this:

FIFO Disabled
    Mailboxes:
        MB0 code: RX_EMPTY  (Extended Frame)
        MB1 code: RX_EMPTY  (Extended Frame)
        MB2 code: RX_EMPTY  (Extended Frame)
        MB3 code: RX_EMPTY  (Extended Frame)
        MB4 code: RX_EMPTY  (Extended Frame)
        MB5 code: RX_EMPTY  (Extended Frame)
        MB6 code: TX_INACTIVE
        MB7 code: TX_INACTIVE
MB 0  OVERRUN: 0  LEN: 8 EXT: 1 TS: 19581 ID: 11B33000 Buffer: 51 AA C7 56 ED 40 75 24 
MB 1  OVERRUN: 0  LEN: 2 EXT: 1 TS: 27771 ID: 19016703 Buffer: 61 52 
MB 0  OVERRUN: 0  LEN: 8 EXT: 1 TS: 29119 ID: E653900 Buffer: B6 56 C4 53 9B 66 E1 39 
MB 0  OVERRUN: 0  LEN: 3 EXT: 1 TS: 53592 ID: BFF1800 Buffer: 88 7B 54 
MB 0  OVERRUN: 0  LEN: 0 EXT: 1 TS: 64061 ID: 12AA5800 Buffer: 
MB 1  OVERRUN: 0  LEN: 1 EXT: 1 TS: 36002 ID: 1D0CF403 Buffer: C 
MB 1  OVERRUN: 0  LEN: 2 EXT: 1 TS: 41485 ID: D177303 Buffer: 2C 83 
MB 2  OVERRUN: 0  LEN: 1 EXT: 1 TS: 45003 ID: B4D20B Buffer: 2E 
MB 1  OVERRUN: 0  LEN: 8 EXT: 1 TS: 52213 ID: 19B20803 Buffer: 8F 72 7 30 26 1E 57 1D 
MB 0  OVERRUN: 0  LEN: 8 EXT: 1 TS: 473 ID: 1CA2D500 Buffer: B5 3D 6E 15 3C 33 E9 34 
MB 0  OVERRUN: 0  LEN: 8 EXT: 1 TS: 3891 ID: 3C9D900 Buffer: 3 F7 6D 30 51 92 4D 57 
MB 1  OVERRUN: 0  LEN: 7 EXT: 1 TS: 5862 ID: E813603 Buffer: A6 76 81 3E 9A C6 E2 
MB 2  OVERRUN: 0  LEN: 5 EXT: 1 TS: 10896 ID: 1BA4BF0B Buffer: ED 20 52 45 47 

The input for this case was a fuzzer using Linux SocketCAN running the command cangen can1 -e -v -i -g 0, which floods the bus with random extended ID messages. The filter worked and only captured messages with source addresses of 0, 3, and 11 were processed.

The ability to filter based on arbitrary masks would be very helpful in the J1939 world.

While this seems to work, are there any checks that I'm missing on the calculated_mask? What's the risk of declaring that function as public?

tonton81 commented 4 years ago

There are no reprocussions of using it in public that I see, it was just never intended to use it the way you are using it. The library managed it itself. However, there is one thing that may not work, which is enhanced filtering, but with single ID and the public function you'll be fine without enhancement anyways (which is for multiID input only)

jeremy-daily commented 4 years ago

Thank you. I added an example and submitted a PR.

tonton81 commented 4 years ago

I worked on multiple ID and distribution for the work you provided here. You can try new functions:

bool setMBUserFilter(FLEXCAN_MAILBOX mb_num, uint32_t id1, uint32_t mask);
bool setMBUserFilter(FLEXCAN_MAILBOX mb_num, uint32_t id1, uint32_t id2, uint32_t mask);
bool setMBUserFilter(FLEXCAN_MAILBOX mb_num, uint32_t id1, uint32_t id2, uint32_t id3, uint32_t mask);
bool setMBUserFilter(FLEXCAN_MAILBOX mb_num, uint32_t id1, uint32_t id2, uint32_t id3, uint32_t id4, uint32_t mask);

bool setFIFOUserFilter(uint8_t filter, uint32_t id1, uint32_t mask, const FLEXCAN_IDE &ide, const FLEXCAN_IDE &remote = NONE);
bool setFIFOUserFilter(uint8_t filter, uint32_t id1, uint32_t id2, uint32_t mask, const FLEXCAN_IDE &ide, const FLEXCAN_IDE &remote = NONE);
bool setFIFOUserFilter(uint8_t filter, uint32_t id1, uint32_t id2, uint32_t id3, uint32_t mask, const FLEXCAN_IDE &ide, const FLEXCAN_IDE &remote = NONE);
bool setFIFOUserFilter(uint8_t filter, uint32_t id1, uint32_t id2, uint32_t id3, uint32_t id4, uint32_t mask, const FLEXCAN_IDE &ide, const FLEXCAN_IDE &remote = NONE);

There will be a patch called update7.zip on the forum thread, use that to override the github copy, I don't plan to update the github version until latest build is stable. Theres been a few updates on it :)

This is how I set your 3 IDs to a mailbox:

Can0.setMBUserFilter(MB10, 0x0B, 0x03, 0x0, 0xFF);

You can set up to 4 IDs, using 1ID theres obviously no bleed thru frames, however, with multiple IDs, no worries, after you set the mailbox, run Can0.enhanceFilter(MB10);, and the sub filter will only allow the ones you requested from going to callback.

Same can be done for FIFO, REJECT_ALL the filters, and set them accordingly.