Reputation: 43
I create a tcp socket:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
and attach a filter to it
setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter))
I produce the filter use tcpdump as the following:
sudo tcpdump tcp -d port 9000
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 8
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 19
(004) ldh [54]
(005) jeq #0x2328 jt 18 jf 6
(006) ldh [56]
(007) jeq #0x2328 jt 18 jf 19
(008) jeq #0x800 jt 9 jf 19
(009) ldb [23]
(010) jeq #0x6 jt 11 jf 19
(011) ldh [20]
(012) jset #0x1fff jt 19 jf 13
(013) ldxb 4*([14]&0xf)
(014) ldh [x + 14]
(015) jeq #0x2328 jt 18 jf 16
(016) ldh [x + 16]
(017) jeq #0x2328 jt 18 jf 19
(018) ret #65535
(019) ret #0
If I attach this filter, the program can't send nothing to port 9000. But if I leave only one instruction:
(018) ret #65535
everything is OK. How to produce the correct filter code?
Upvotes: 0
Views: 1485
Reputation: 9114
I believe the problem is the same as for this question.
Tcpdump works on an ethernet frame, starting at the first ethernet header byte. So with these BPF instructions:
(000) ldh [12] // Look at ethertype
(001) jeq #0x86dd jt 2 jf 8 // If IPv6 jump to (002), else to (008)
[…]
(008) jeq #0x800 jt 9 jf 19 // If IPv4 jump to (009), else to (019)
… tcpdump checks that the packet is IP (4 or 6).
On a raw socket, you cannot do exactly the same, because you work on an IP packet, i.e. Ethernet payload, which means:
I have not tested, but you may try with something like this. Since you seem to work with IPv4 and TCP, I dropped the instructions for the IPv6 part and to check if packet is TCP (if the socket receives it, we already know it is). This means dropping the first eleven instructions.
(000) ldh [6] // Load fragment offset field
(001) jset #0x1fff jt 8 jf 2 // Jump to end if packet is a fragment
(002) ldxb 4*([0]&0xf) // Add size of IP header (inc. options) to X
// Now X points to the beginning of TCP hdr
(003) ldh [x + 0] // Load src port field
(004) jeq #0x2328 jt 7 jf 5 // Is it equal to 9000?
(005) ldh [x + 2] // Else jump to dst port field
(006) jeq #0x2328 jt 7 jf 8 // Is it equal to 9000?
(007) ret #65535 // (If port 9000) return 0x65535
(008) ret #0 // Return 0
So I just kept the instructions for fragment offset check, and dst port number check. I adapted the offsets, and the references for the jump instructions. The rest of the instructions is unchanged. But again I have not tested it, so no guarantee.
Upvotes: 1