Reputation: 2273
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?
Upvotes: 3
Views: 3216
Reputation: 141
I know you want to attach a filter to your raw socket, it's what I recently work with. Thanks to more than weeks of hard work, I got an easy way to attach a filter to a raw socket. I would like to share with yours :)
First make sure you already install tcpdump
(It's a Linux system manager tools), if your platform is one of distribution in Linux, let's go to next step.
Second you need to have a sudo or root
permission to execute the tool.
Third follow demo example, change it to suit your situation.
$ sudo tcpdump -i enp4s0 -dd 'tcp and (port 9000 or port 80 or port 22)'
Let me explain the parameter first.
tcpdump
--> Dump traffic on a network
-i
--> Specific the interface
enp4s0
--> Network Interface
-dd
--> Dump packet-matching code as a C program fragment.
tcp and (port 9000 or port 80 or port 22)
--> Berkeley Packet Filter (BPF) syntax
After this command you should generate some code same like this:
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 8, 0x000086dd },
{ 0x30, 0, 0, 0x00000014 },
{ 0x15, 0, 21, 0x00000006 },
{ 0x28, 0, 0, 0x00000036 },
....
It looks like a mess, but don't worry, let's go on with brave.
First let me clear what it's, this is a the packet filter code, if you already learn assembly language before, you will feel familiar with it.
Second because it's C stylish code, it doesn't fit our python usage, so we need to polish the data, use follow python code
import subprocess
cmd = "sudo tcpdump -i enp4s0 -dd 'tcp and (port 9000 or port 80 or port 22)'"
tcpdumpBinary = subprocess.check_output(cmd, shell=True)
macroString = '( ' + tcpdumpBinary.decode('utf-8').replace(
'\n', '').replace('{', '[').replace('}', ']') + ')'
macroString = eval(macroString)
Third now you can use the macroString
same like the filters_list
.
This method help me get rid of the filter code dilemma, I hope it will be helpful to you.
Upvotes: 4
Reputation: 9184
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),
…
Upvotes: 4