[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PF extension for address/network tables



Hi Daniel & other PF coders:

As you might remember, I was interrested in an extension to PF
to be able to assign and manage a huge list of addresses to the
source or destination of any PF rule. I've been thinking a lot
about that, and I decided this week to implements something,
with the following goals:
  - Be as unintrusive as possible with current PF code.
  - Accept both host and network addresses in each table.
  - Write simple code by reusing kernel code (radix tree and
    red black tree)

I've attached here a prototype of that code, which I believe is
fully functional on 3.2-stable. My goal here is to see if there
is interrest in the OpenBSD community for that code, and if I
should try to clean it up and port it to -CURRENT.

README following, and code attached.
Sould I forward it to [email protected] too?
Comments and questions welcome!
Cedric



--README:
This code implements an extension to PF to be able to attach a table of IP
addresses and/or networks to the source and destination of each PF rules.

This extension is designed to be logically separated from PF itself: it is
possible to add and remove elements in tables or tables themselves without
touching PF rules or structures. Likewise, this extension has a very little
impact on the PF code: only a total of 100 lines are added to the kernel and
userland side of PF, with no changes to existing structures.

The kernel code maintain 2 structures: first a red/black trees of tables, each
table being identified by an identifier composed of a 16 byte type and a 128
byte name. Each of theses tables create 2 PATRICIA tree, one for IPv4 and one
for IPv6, reusing the net/radix.c code. That mean that each "table" behaves in
fact like a routing table, accepting both host and network addresses.


Tables are manipulated in userland using an utility called "pfradix", which
talk to the kernel using ioctls on the /dev/radix special device. I've also
developped a small library wich wraps the ioctls in an easy to use interface.


EXAMPLE 1:

This first example just create an (incomplete) unroutable table and prevents
us from sending packets to these addresses.

test# pfradix -vA system:unroutable
1/1 tables added.
test# pfradix -va system:unroutable 10.0.0.0/8 172.16.0.0/16 127.0.0.1 ::1
4/4 addresses added.
test# pfradix -g system:unroutable
10.0.0.0/8
127.0.0.1
172.16.0.0/16
::1
test# echo 'block out on !lo0 from any to <system:unroutable>' | pfctl -f -
test# pfctl -s r
@0 block out on ! lo0 from any to <system:unroutable>


Rules could also be loaded from a file, with the following command:
test# pfradix -F sample.radix


Ok, now let's test it and get statistics for both the table as a whole
and each individual address/network in the table:

test# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1): 56 data bytes
ping: sendto: No route to host
ping: wrote 10.1.1.1 64 chars, ret=-1
--- 10.1.1.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
test# pfradix -Gv
system:unroutable
Addresses: 4
Evaluations: [ NoMatch: 78 Match: 1 ]
In/Block: [ Packets: 0 Bytes: 0 ]
In/Pass: [ Packets: 0 Bytes: 0 ]
Out/Block: [ Packets: 1 Bytes: 84 ]
Out/Pass: [ Packets: 0 Bytes: 0 ]
test# pfradix -gv system:unroutable
system:unroutable
Addresses: 4
Evaluations: [ NoMatch: 78 Match: 1 ]
In/Block: [ Packets: 0 Bytes: 0 ]
In/Pass: [ Packets: 0 Bytes: 0 ]
Out/Block: [ Packets: 1 Bytes: 84 ]
Out/Pass: [ Packets: 0 Bytes: 0 ]
test# pfradix -gv system:unroutable
10.0.0.0/8
In/Block: [ Packets: 0 Bytes: 0 ]
In/Pass: [ Packets: 0 Bytes: 0 ]
Out/Block: [ Packets: 1 Bytes: 84 ]
Out/Pass: [ Packets: 0 Bytes: 0 ]
127.0.0.1
In/Block: [ Packets: 0 Bytes: 0 ]
In/Pass: [ Packets: 0 Bytes: 0 ]
Out/Block: [ Packets: 0 Bytes: 0 ]
Out/Pass: [ Packets: 0 Bytes: 0 ]
172.16.0.0/16
In/Block: [ Packets: 0 Bytes: 0 ]
In/Pass: [ Packets: 0 Bytes: 0 ]
Out/Block: [ Packets: 0 Bytes: 0 ]
Out/Pass: [ Packets: 0 Bytes: 0 ]
::1
In/Block: [ Packets: 0 Bytes: 0 ]
In/Pass: [ Packets: 0 Bytes: 0 ]
Out/Block: [ Packets: 0 Bytes: 0 ]
Out/Pass: [ Packets: 0 Bytes: 0 ]


EXAMPLE 2:

In this example, I want to block ftp access to ftp.sun.com and http access
to www.lemonde.fr. However, the problem is that theses IP addresses often
change. one way to solve that would be to create a table for each of theses
host names, and update at a regular interval. this can be done the following
way:

 test# pfradix -vA resolv:ftp.sun.com resolv:www.lemonde.fr
 2/2 tables added.
 test# pfctl -f sample.rules
 test# pfctl -s r
 @0 block out proto tcp from any to <resolv:ftp.sun.com> port = ftp
 @1 block out proto tcp from any to <resolv:www.lemonde.fr> port = www

Now running the following code will update theses addresses in the background
(sample.pfresolvd):


 while true; do
       NAMES=`pfradix -G resolv | awk '{print substr($1, 8)}'`
       for NAME in $NAMES; do  pfradix -s resolv:$NAME $NAME; done
       sleep 60
 done

INSTALLATION:

 test# cd /usr/src
 test# patch < ~/pfradix-0.1/obsd.diff

recompile and install new kernel and /usr/src/sbin/pfctl, set securelevel to
-1 in /etc/rc.securelevel and reboot.

 test# cd ~/pfradix-0.1
 test# ./u-make.sh
 test# ./k-make.sh
 test# ./k-load.sh
 Module loaded as ID 0
 Dec 20 16:33:24 home /bsd: loading module radix
 Dec 20 16:33:24 home /bsd: DDB symbols added: 148200 bytes
 test# ln -s ~/pfradix-0.1/pfradix /sbin/pfradix
 test# rehash
 test# pfradix -F sample.radix