Depends if you actually follow the standard, or invent your own.
If you are connecting to something that is a "standard" then there isn't much I can do to help.
I usually treat CAN like serial, but with the ability to use additional priority IDs and using extended IDs.
I don't use the normal library either, I use my own, but it is only for Teensy 3.x, and a different MCU (that Paul isn't using on any of his boards, and beyond the scope of the question).
This is beyond the scope of the question as well, but worth mentioning:
I reserve the non-extended ID for firmware update. When it sees a packet there requesting a firmware update, it goes to updating.
What I use it for personally, is a reliable serial communication, with the ability to use only 3 wires, and TX/RX on a separate ID console data (debugging, etc).
I am able to talk to 8 other MCU (either between them or to the main host) as well as update software on any of them selectively.
I reserve CAN_SRC_8/CAN_DST_8 for the debug host ID. Debug IMHO isn't something that needs to be a high priority anyway, because of FIFOs.
I spread the message ID into something that is like an address to, an address from, then priorities and the additional function.
Here's the basic layout, which I will share, that has worked very well for me.
Code:
// source message bitmap -- each device has an address.
#define CAN_SRC_0 (0x00000000u) // highest source priority
#define CAN_SRC_1 (0x00000001u)
#define CAN_SRC_2 (0x00000002u)
#define CAN_SRC_3 (0x00000004u)
#define CAN_SRC_4 (0x00000008u)
#define CAN_SRC_5 (0x00000010u)
#define CAN_SRC_6 (0x00000020u)
#define CAN_SRC_7 (0x00000040u)
#define CAN_SRC_8 (0x00000080u) // lowest source priority
#define CAN_SRC_DBUG (0x000000FFu) // rock bottom source, only for debugger
// source message priority.
#define SRC_MSG_PRI0 (0x00000000u) // highest priority
#define SRC_MSG_PRI1 (0x00000100u)
#define SRC_MSG_PRI2 (0x00000200u)
#define SRC_MSG_PRI3 (0x00000400u)
#define SRC_MSG_PRI4 (0x00000800u) // lowest priority
#define SRC_MSG_DBUG (0x00000F00u) // rock bottom priority, only for debug mess
// message types, 5 + one debug type
#define MSG_IS_TYP0 (0x00000000u) // highest priority
#define MSG_IS_TYP1 (0x00001000u)
#define MSG_IS_TYP2 (0x00002000u)
#define MSG_IS_TYP3 (0x00004000u)
#define MSG_IS_TYP4 (0x00008000u) // lowest priority
#define MSG_IS_DBUG (0x0000F000u) // rock bottom priority, only for debug mess
// Destination message bitmap -- each device has an address.
#define CAN_DST_0 (0x00000000u) // highest destination priority
#define CAN_DST_1 (0x00010000u)
#define CAN_DST_2 (0x00020000u)
#define CAN_DST_3 (0x00040000u)
#define CAN_DST_4 (0x00080000u)
#define CAN_DST_5 (0x00100000u)
#define CAN_DST_6 (0x00200000u)
#define CAN_DST_7 (0x00400000u)
#define CAN_DST_8 (0x00800000u) // lowest destination priority
#define CAN_DST_DBUG (0x00FF0000u) // rock bottom destination, only to debugger
// destination message priority.
#define DST_MSG_PRI0 (0x00000000u) // highest priority
#define DST_MSG_PRI1 (0x01000000u)
#define DST_MSG_PRI2 (0x02000000u)
#define DST_MSG_PRI3 (0x04000000u)
#define DST_MSG_PRI4 (0x08000000u) // lowest priority
#define DST_MSG_DBUG (0x0F000000u) // rock bottom priority, debug only
// Message priority
#define DST_MSG_PRIH (0x00000000u) // highest priority
#define DST_MSG_PRIL (0x10000000u) // lowest priority
#define CAN_DEBUG_MESSAGE (SRC_MSG_DBUG | MSG_IS_DBUG | DST_MSG_DBUG | DST_MSG_PRIL)
#define RCV_DFLT (CAN_SRC_DBUG << 16) // Any message for us.
#define CAN_to_ME(FRAME_ID) ((FRAME_ID & CAN_DST_DBUG) == RCV_DFLT)
#define CAN_FROM_DBG (CAN_SRC_DBUG | CAN_DEBUG_MESSAGE)
#define CAN_TO_DBG (CAN_DST_DBUG | CAN_DEBUG_MESSAGE)
#define CANHI_SRC (CAN_SRC_DBUG | DST_MSG_PRIH) // High priority message
#define CANLO_SRC (CAN_SRC_DBUG | DST_MSG_PRIL) // Low priority message
#define TOCANDBUG (CAN_SRC_DBUG | CAN_TO_DBG) // Debug message TO debugger
#define FROMCANDBUG(TARGET_DEST) (CAN_FROM_DBG | TARGET_DEST)
Frames are sent to one of two FIFOs. The first FIFO handles low priority, the second FIFO handles higher priority. When there is a free TX mailbox, higher priority is checked first, then the lower. In Flexcan hardware, it will send the message with the highest priority first.
ALL frames are received, even if not for us. These are decided and sorted in the frame handler.
I still SHOULD do filtering for it, but this works great for me at this time, even at 1mbit rates. This doesn't stress the CPU much, considering the latency between frames.
You do not need to sort out which one came in first: Flexcan does this already, as it has timestamps, and if priority is equal, it will send the message with the lower timestamp.
HTH