Skip to content

Commit 2afd5f2

Browse files
committed
Filter SocketCAN frames in userland if necessary.
On Linux little-endian hosts the first four bytes of a SocketCAN frame are the opposite byte order at the kernel side of the socket, so a filter program that accesses any of these bytes needs either to account for the byte order difference (if possible) or to run in userland. Implement this logic in fix_dlt_can_socketcan() and call it from fix_program() if necessary. See also GitHub bug report #1247.
1 parent 343af1f commit 2afd5f2

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ DayOfTheWeek, Month DD, YYYY / The Tcpdump Group
5050
Deprecate bpf_filter().
5151
Require a live capture for all Linux BPF extensions.
5252
Have "outbound" mean Tx only for DLT_SLIP.
53+
Filter Linux SocketCAN frames in userland if necessary.
5354
rpcap:
5455
Support user names and passwords in rpcap:// and rpcaps:// URLs.
5556
Add a -t flag to rpcapd to specify the data channel port; from

pcap-linux.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5821,6 +5821,62 @@ iface_get_arptype(int fd, const char *device, char *ebuf)
58215821
return ifr.ifr_hwaddr.sa_family;
58225822
}
58235823

5824+
/*
5825+
* In a DLT_CAN_SOCKETCAN frame the first four bytes are a 32-bit integer
5826+
* value in host byte order if the filter program is running in the kernel and
5827+
* in network byte order if in userland. This applies to both CC, FD and XL
5828+
* frames, see pcap_handle_packet_mmap() for the rationale. Return 1 iff the
5829+
* [possibly modified] filter program can work correctly in the kernel.
5830+
*/
5831+
#if __BYTE_ORDER == __LITTLE_ENDIAN
5832+
static int
5833+
fix_dlt_can_socketcan(const u_int len, struct bpf_insn insn[])
5834+
{
5835+
for (u_int i = 0; i < len; ++i) {
5836+
switch (insn[i].code) {
5837+
case BPF_LD|BPF_B|BPF_ABS: // ldb [k]
5838+
case BPF_LDX|BPF_MSH|BPF_B: // ldxb 4*([k]&0xf)
5839+
if (insn[i].k < 4)
5840+
insn[i].k = 3 - insn[i].k; // Fixed now.
5841+
break;
5842+
case BPF_LD|BPF_H|BPF_ABS: // ldh [k]
5843+
case BPF_LD|BPF_W|BPF_ABS: // ld [k]
5844+
/*
5845+
* A halfword or a word load cannot be fixed by just
5846+
* changing k, even if every required byte is within
5847+
* the byte-swapped part of the frame, even if the
5848+
* load is aligned. The fix would require either
5849+
* rewriting the filter program extensively or
5850+
* generating it differently in the first place.
5851+
*/
5852+
case BPF_LD|BPF_B|BPF_IND: // ldb [x + k]
5853+
case BPF_LD|BPF_H|BPF_IND: // ldh [x + k]
5854+
case BPF_LD|BPF_W|BPF_IND: // ld [x + k]
5855+
/*
5856+
* In addition to the above, a variable offset load
5857+
* cannot be fixed because x can have any value, thus
5858+
* x + k can have any value, but only the first four
5859+
* bytes are swapped. An easy way to demonstrate it
5860+
* is to compile "link[link[4]] == 0", which will use
5861+
* "ldb [x + 0]" to access one of the first four bytes
5862+
* of the frame iff CAN CC/FD payload length is less
5863+
* than 4.
5864+
*/
5865+
if (insn[i].k < 4)
5866+
return 0; // Userland filtering only.
5867+
break;
5868+
}
5869+
}
5870+
return 1;
5871+
}
5872+
#else
5873+
static int
5874+
fix_dlt_can_socketcan(const u_int len _U_, struct bpf_insn insn[] _U_)
5875+
{
5876+
return 1;
5877+
}
5878+
#endif // __BYTE_ORDER == __LITTLE_ENDIAN
5879+
58245880
static int
58255881
fix_program(pcap_t *handle, struct sock_fprog *fcode)
58265882
{
@@ -5847,6 +5903,17 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode)
58475903
fcode->len = len;
58485904
fcode->filter = (struct sock_filter *) f;
58495905

5906+
switch (handle->linktype) {
5907+
case DLT_CAN_SOCKETCAN:
5908+
/*
5909+
* If a similar fix needs to be done for CAN frames that
5910+
* appear on the "any" pseudo-interface, it needs to be done
5911+
* differently because that would be within DLT_LINUX_SLL or
5912+
* DLT_LINUX_SLL2.
5913+
*/
5914+
return fix_dlt_can_socketcan(len, f);
5915+
}
5916+
58505917
for (i = 0; i < len; ++i) {
58515918
p = &f[i];
58525919
/*

0 commit comments

Comments
 (0)