Search code examples
pythonsocketstcpbpf

BPF in python to sniff packets for multiple TCP ports


I got code from http://allanrbo.blogspot.in/2011/12/raw-sockets-with-bpf-in-python.html. It works fine, but I want to sniff the traffic on multiple TCP ports like port 9000, 80, 22...

So I have modified the filter_list like blow

filters_list = [  
    # Must have dst port 67. Load (BPF_LD) a half word value (BPF_H) in   
    # ethernet frame at absolute byte offset 36 (BPF_ABS). If value is equal to  
    # 67 then do not jump, else jump 5 statements.  
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 36),  
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 0, 5), <===== Here I added another port
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 80, 0, 5),


    # Must be UDP (check protocol field at byte offset 23)  
    bpf_stmt(BPF_LD | BPF_B | BPF_ABS, 23),   
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x06, 0, 3), #<==Changed for TCP "0x06"

    # Must be IPv4 (check ethertype field at byte offset 12)  
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 12),   
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 1),  

    bpf_stmt(BPF_RET | BPF_K, 0x0fffffff), # pass  
    bpf_stmt(BPF_RET | BPF_K, 0), # reject   ]

The thing is, sometimes it is working sometimes it is not, like getting traffic only on 9000 but not 80, sometimes getting traffic on 80. I didnt understood the code completely. Any help?


Solution

  • As far as I can tell, the problem seems to come from the logic of your first two conditional jumps. Specifically:

    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 0, 5), # if false, skip 5 instructions
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 80, 0, 5),
    

    An instruction bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, <val>, <jtrue>, <jfalse>) means

    if value currently in register K is equal to <val>
        then add <jtrue> to instruction pointer
            (i.e. skip the next <jtrue> instructions),
        else add <jfalse> instead`
    

    So the two lines mean:

    if port is 9000
        then if port is 80
            then go on with checks…
        else skip 5 instructions (i.e. reject)
    else
        skip 5 instructions (i.e. pass, as jump offset was not updated from 5 to 6)
    

    While you probably want something that looks more like:

    if port is 9000
        then go on with checks…
    else
        if port is 80
            then go on with checks…
        else reject
    

    I have not tested, but to get this logics I would say you need to adapt the jump offsets as follows:

    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0), # if true skip 1 insn
                                                     # (i.e. port 80 check) else 0
                                                     # and check for port 80
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 80, 0, 5),   # if true skip 0 else skip 5
                                                     # (and land on “reject”)
    

    Edit 1: And then for filtering three ports, that would become:

    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 2, 0), # skip the next 2 checks if true
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0), # skip the next check if true
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22,   0, 5), # if true go on else reject
    

    Edit 2: To also filter on source port (in addition to destination port), you could try something like this (still not tested on my side):

    # Load TCP src port into register K, and check port value
    # For packets with IP header len == 20 bytes, TCP src port should be at offset 34
    # We adapt the jump offsets to go to next check if no match (or to “reject” after
    # the last check), or to skip all remaining checks on ports if a match is found.
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 34),           # 34 == offset of src port
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 6, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 5, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22,   4, 0),
    
    # As before: if no match on src port, check on dst port
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 36),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 2, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22,   0, 5),
    
    …