Using strace inspect how tcpdump work
-
Build latest strace
$ git clone --depth 1 https://github.com/strace/strace.git $ cd strace $ sudo apt-get install gawk automake gcc-multilib $ ./bootstrap $ ./configure $ make $ sudo make install
-
Using strace trace tcpdump
$ sudo strace -v -e trace=setsockopt,socket tcpdump -i any icmp socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) = 3 setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=6, filter=[BPF_STMT(BPF_LD|BPF_H|BPF_ABS, SKF_AD_OFF+SKF_AD_PROTOCOL), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x800, 0, 0x3), BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0x9), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x1, 0, 0x1), BPF_STMT(BPF_RET|BPF_K, 0x40000), BPF_STMT(BPF_RET|BPF_K, 0)]}, 16) = 0
-
How tcpdump work
- Use socket init AF_PACKET sockfd
- Use setsockopt attach BPF to relate sockfd
- Read from sockfd
-
A packet capture example
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <linux/filter.h> #include <net/if.h> #include <net/ethernet.h> int main(int argc, char *argv[]) { int sockfd; char buf[ETHER_MAX_LEN]; struct ether_header eh; struct sock_filter filter[] = { BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0xc), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x800, 0, 0x3), BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0x17), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x1, 0, 0x1), BPF_STMT(BPF_RET|BPF_K, 0x40000), BPF_STMT(BPF_RET|BPF_K, 0) }; struct sock_fprog bpf = { .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])), .filter = filter }; sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sockfd < 0) { perror("socket"); exit(1); } if (setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)) < 0) { perror("setsockopt"); exit(1); } if (read(sockfd, buf, sizeof(buf)) < 0) { perror("read"); exit(1); } memcpy(&eh, buf, sizeof(eh)); for (int i = 0; i < ETH_ALEN; i++) { printf("%s%X", i == 0 ? "" : ":", eh.ether_shost[i]); } return 0; }
-
What is BPF code [1]
1 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0xc) 2 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x800, 0, 0x3) 3 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0x17) 4 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x1, 0, 0x1) 5 BPF_STMT(BPF_RET|BPF_K, 0x40000) 6 BPF_STMT(BPF_RET|BPF_K, 0)
When recv an ethernet frame, execute BPF code filter the frame, capture it if BPF code return 0x40000.
Translate BPF code to C:
int filter(struct frame f) { int8_t breg; int16_t hreg; int32_t wreg; // 1 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0xc): load to register memcpy(&hreg, (char *) &f + 0xc, sizeof(hreg)); // 2 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x800, 0, 0x3): if true go to next line or go to fourth line if (hreg == 0x800) { goto no_capture; } // 3 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0x17) memcpy(&breg, (char *) &f + 0x17, sizeof(breg)); // 4 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x1, 0, 0x1): if true go to next line or go to second line if (hreg == 0x1) { goto no_capture; } // 5 BPF_STMT(BPF_RET|BPF_K, 0x40000) return 0x40000; no_capture: // 6 BPF_STMT(BPF_RET|BPF_K, 0) return 0; }
-
More
- tcpdump using libpcap capture packet [2]
- BPF also use in other place [3]
links: