flypig
flypig

Reputation: 43

attach filter to tcp socket in linux

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

Answers (1)

Qeole
Qeole

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:

  • Looking at the ethertype makes no sense, it is unreachable.
  • The offsets to reach the fields to examine in the packet are different (14 bytes lower—the size of the Ethernet header).

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

Related Questions