--------------------------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---------
 
     |