--------------------------Introduction to BPF (ARP) -------------------------- ...or Linux Socket Filtering
Oh, hi!
Welcome back!
We will be covering a small presentation on LSF and BPF, since these will
prove themselves very useful in the future.
I suppose you have prepared our good ol' container.
Do (re)check "Ethernet Frames"
You need to implement the same environment before diving into this zine.
We will have to install a few tools, too: tcpdump (if not installed already) and bpftools
[-------------------Prepare environment
1) Install git:
root@f8918abfeb55:/home# apt-get install git
and then, clone the tools repository
root@f8918abfeb55:/home# git clone https://github.com/cloudflare/bpftools
2) Go under bpftools/linux_tools/
a) Install the necessary packages:
apt-get install libreadline-dev
apt-get install binutils-dev
apt-get install bison
apt-get install flex
b) Now, we are ready to build the executable programs:
root@f8918abfeb55:/home/bpftools/linux_tools# make
c) Check if executable files were generated:
root@f8918abfeb55:/home/bpftools/linux_tools# find . -type f -executable -print
./bpf_jit_disasm
./bpf_dbg
./bpf_asm
We will use bpf_asm tool only, for generating the bytecodes
[--------------BPF cramming
Berkeley Packet Filter (BPF) allows a user-space program to attach a filter onto any socket
and allow/disallow certain types of data to come through the socket.
Linux Socket Filtering(LSF) is just a derived of BPF (almost identical twins!)
Just as BPF, it supports filtering packets, allowing to a user-space process to be picky about
what packets to be received.
This pickiness of BPF/LSF is possible thanks to the instructions of a low level asm-like
filter language.
The BPF instruction set includes most arithmetic operations, loads and stores, and forward jumps
( no backward jumps allowed, in order to allow filter programs to terminate):
Instruction Addressing mode Description
ld 1, 2, 3, 4, 10 Load word into A
ldi 4 Load word into A
ldh 1, 2 Load half-word into A
ldb 1, 2 Load byte into A
ldx 3, 4, 5, 10 Load word into X
ldxi 4 Load word into X
ldxb 5 Load byte into X
st 3 Store A into M[]
stx 3 Store X into M[]
jmp 6 Jump to label
ja 6 Jump to label
jeq 7, 8 Jump on A == k
jneq 8 Jump on A != k
jne 8 Jump on A != k
jlt 8 Jump on A < k
jle 8 Jump on A <= k
jgt 7, 8 Jump on A > k
jge 7, 8 Jump on A >= k
jset 7, 8 Jump on A & k
add 0, 4 A +
sub 0, 4 A -
mul 0, 4 A *
div 0, 4 A /
mod 0, 4 A %
neg !A
and 0, 4 A &
or 0, 4 A |
xor 0, 4 A ^
lsh 0, 4 A <<
rsh 0, 4 A >>
tax Copy A into X
txa Copy X into A
ret 4, 9 Return
Example of low level BPF for ARP filtering
ldh [12]
jne #0x806, drop
ret #-1
drop: ret #0
How to use it, then?
... take a deep breath, yes, that's right, two new structures:
struct sock_filter { /* Filter block */
__u16 code; /* Actual filter code */
__u8 jt; /* Jump true */
__u8 jf; /* Jump false */
__u32 k; /* Generic multiuse field */
};
...and
struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
unsigned short len; /* Number of filter blocks */
struct sock_filter __user *filter;
};
[-------------------Lend me some bytecodes!
Create a file for the ARP filtering - I put everything under ether.txt:
root@f8918abfeb55:/home#more ether.txt
ldh [12]
jne #0x806, drop
ret #-1
drop: ret #0
Generate the bytecodes:
root@f8918abfeb55:/home# bpftools/linux_tools/bpf_asm ether.txt
4,40 0 0 12,21 0 1 2054,6 0 0 4294967295,6 0 0 0,
We will use below output for our example:
root@f8918abfeb55:/home# bpftools/linux_tools/bpf_asm -c ether.txt
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x00000806 },
{ 0x06, 0, 0, 0xffffffff },
{ 0x06, 0, 0, 0000000000 },
Can I haz example?
#include<stdio.h>
#include<stdlib.h>
#include<
#include<sys/ioctl.h>
#include<arpa/inet.h>
#include<linux/if_ether.h>
#include<netpacket/packet.h>
#include<
#include<
#include<linux/kernel.h>
#include<string.h>
struct sock_filter code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x00000806 },
{ 0x06, 0, 0, 0xffffffff },
{ 0x06, 0, 0, 0000000000 },
};
struct sock_fprog bpf = {
.len = sizeof(code)/sizeof(code[0]),
.filter = code,
};
int main(){
int socky;
struct ifreq ifr;
struct sockaddr_ll socket_ll;
unsigned char buf[4096];
memset(&ifr, 0, sizeof(ifr));
memset(&socket_ll, 0, sizeof(socket_ll));
socky = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
ioctl(socky, SIOCGIFINDEX, &ifr);
socket_ll.sll_family = AF_PACKET;
socket_ll.sll_protocol = htons(ETH_P_ALL);
socket_ll.sll_ifindex = ifr.ifr_ifindex;
bind(socky, (struct sockaddr *)&socket_ll, sizeof(socket_ll));
setsockopt(socky, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
while(1){
ssize_t len = recv(soc, buf, sizeof(buf), 0);
struct ethhdr* ethhdr = (struct ethhdr*)buf;
int proto = ntohs(ethhdr->h_proto);
if(len <= 0 ) break;
printf("packet len: %3ld \nprotocol: 0x0%0x %s\n", len, proto,
proto==ETH_P_ARP ? "[ARP]" : "other");
}
return 0;
}
Compile and run:
root@f8918abfeb55:/home# gcc filterz.c -o filterz
root@f8918abfeb55:/home# ./filterz
....it will wait to capture arp packets only...
Leave this here, and open another terminal for a new container session, and
run once again the ethernet frames code ether.c, from our previous zine:
root@tr0n:~# docker exec -ti f8918abfeb55 /bin/bash
root@f8918abfeb55:/home#
Let's send a packet
root@f8918abfeb55:/home#./ether
By now, you should have captured, in the terminal where filterz program
is running, the below result:
root@f8918abfeb55:/home# ./filterz
packet len: 28
protocol: 0x0806 [ARP]
Success! xD
[-------------------Options, you gotta have `em!
Is this used anywhere?!
Once BPF proved itself, it was just a matter of time before it would take
over the network world, and be used for creating tools like libpcap or
tcpdump.
Let's practice a bit on tcpdump; since it's based on bpf, we should easily obtain
the bytecodes for our arp scenario:
root@f8918abfeb55:/home# tcpdump -d arp
(000) ldh [12]
(001) jeq #0x806 jt 2 jf 3
(002) ret #262144
(003) ret #0
...oh, it looks familar xD
root@f8918abfeb55:/home# tcpdump -dd arp
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x00000806 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
...not a stranger anymore
Feelin' lucky?! Let's add one more d:
root@f8918abfeb55:/home# tcpdump -ddd arp
4
40 0 0 12
21 0 1 2054
6 0 0 262144
6 0 0 0
Hmm...you have that feelin` you had seen it somewhere, right?
Well, let's make it look more presentable:
root@f8918abfeb55:/home# tcpdump -p -ni eth0 -ddd "arp" |tr "\n" "," && echo " "
4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0,
Your instincts were on point!
Oh, so we can write filters using tcpdump only?!
If you don't want to write assembler look-alike syntax,that's a big "yup!"
...and how we can do this?
Oh, you so curious!
Let's say I want a filter based just on the protocol (2054 is decimal for 0x0806):
Do not forget - for testing below tcpdump commands, send the ethernet frame
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054"
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
00:58:38.721131 [|ARP]
0x0000: 6e75 6c6c 5120 7761 7320 6865 7265 nullQ.was.here
Let`s explain this ether[12:2] - it takes the next 2 bytes
after the first 12 bytes - which is exactly the Type (0x0806).
Reminder on the Ethernet Header:
6 bytes - destination address
6 bytes - source address
2 bytes - the type
... and what else?
We can also add a limit on packet's length.
We already know that the ethernet frame we crafted last time is 28bytes long.
Try to run this tcpdump line, for packets bigger than 100 bytes:
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054" and greater 100
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
As expected, nothing was captured...
(keep sending that Ethernet Frame, tho! Perseverance will pay off one day!)
Now, let's try with a value closer to our frame/packet's length:
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054" and greater 20
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
01:11:07.250230 [|ARP]
0x0000: 6e75 6c6c 5120 7761 7320 6865 7265 nullQ.was.here
Marvellous, I say!
And let's re-apply the Triple D!
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054" and greater 20 -d
(000) ldh [12]
(001) jeq #0x806 jt 2 jf 5
(002) ld #pktlen
(003) jge #0x14 jt 4 jf 5
(004) ret #262144
(005) ret #0
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054" and greater 20 -dd
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000806 },
{ 0x80, 0, 0, 0x00000000 },
{ 0x35, 0, 1, 0x00000014 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054" and greater 20 -ddd
6
40 0 0 12
21 0 3 2054
128 0 0 0
53 0 1 20
6 0 0 262144
6 0 0 0
... and the final form:
root@f8918abfeb55:/home# tcpdump -p -ni eth0 "ether[12:2]==2054" and greater 20 -ddd |tr "\n" "," && echo " "
6,40 0 0 12,21 0 3 2054,128 0 0 0,53 0 1 20,6 0 0 262144,6 0 0 0,
root@f8918abfeb55:/home#
So, we add that in a piece of code or something?
Yes, the "-dd", you can apply it in a piece of code (as per previous code example)
..or you can simply use the output from "-ddd" with iptables:
iptables -A INPUT \
-m bpf --bytecode "6,40 0 0 12,21 0 3 2054,128 0 0 0,53 0 1 20,6 0 0 262144,6 0 0 0," \
-j DROP
For aesthetic reasons,
a --mac-source options would be nice (&others), too ... overall tho', looking good.
In an utopian world it would work nicely, then again, this is merely an iptables example!
That rule will inform your firewall: "Hey, man! Like a smooth and strong bodyguard that you are,
allow ARP packets only, the 0x0806 kewl type; and they gotta be at least this long - 20bytes-
to get into the nerdz club! No minors allowed!"
Decent stuff, aye?!
I hope you enjoyed this zine. Once we study the IP header,
we will be able to play more with BPF and tcpdump.
have a good week-end!
===========================
Better be reading
https://www.kernel.org/doc/Documentation/networking/filter.txt
https://linux.die.net/man/2/setsockopt
http://man7.org/linux/man-pages/man2/seccomp.2.html
http://leonerds-code.blogspot.ro/2010/05/pfpacket-linux-socket-filters-and-ipv6.html
-------EOF---------
|