====== Minix Firewall ======
===== About =====
This is the //GSoC 2010 - Minix 3 Firewall// project page. This page describes the design evolution and provides a documentation of the developed features. \\ \\ Student: Stefano Cordio [ stefano (dot) cordio (at) gmail (dot) com ] \\ Mentors: Cristiano Giuffrida and Lorenzo Cavallaro \\ SVN branch name: [[https://gforge.cs.vu.nl/gf/project/minix/scmsvn/?action=browse&path=/branches/src.r7062.firewall/|src.r7062.firewall]] ([[.:#fw_tracking_current|Tracking Current]]) \\ Last compiled trunk: r9022 \\ Last branch rebase: r9022 \\ NetBSD version: 5.1 (GENERIC)
===== Abstract =====
The goal of this project is to implement a firewall for Minix 3. It is preferable to port an existing firewall from another operating system or, alternatively, make a new one that has a similar approach to one that can not be ported.
===== Project Idea =====
The NetBSD kernel provides a Packet Filter Interface called [[http://www.netbsd.org/docs/internals/en/chap-networking-services.html#netserv-filters-pfil|pfil]] which supports the registration of callback functions to special hooks inside of the management logic of network packets. \\ This project can be divided in two steps:
* The first one is to port //pfil// on Minix, making appropriate links between it and the //inet// server and exporting functions that allow packet filters to register themselves.
* The second is to port the [[http://www.netbsd.org/docs/internals/en/chap-networking-services.html#netserv-filters-pf|pf]] packet filter.
===== Tracking Current =====
To follow the firewall development, the guidelines are similar to those described in the [[:DevelopersGuide:TrackingCurrent]] page. You only need a few attentions:
* Prepare a directory for the new ///usr/src// tree and move the current ///usr/src// tree to a safe place. For example, you can create a //src.firewall// directory and use symbolic links to choose the current tree:
# cd /usr
# mv src src.trunk
# mkdir src.firewall
# chown bin src.firewall
# ln -s src.firewall src
* Do the SVN checkout of the branch of the source:
$ svn --username anonymous checkout https://gforge.cs.vu.nl/svn/minix/branches/src.r7062.firewall src.firewall
* Follow the instructions in the next section before recompiling (in addition to those specified in docs/UPDATING).
==== "docs/UPDATING" addendum ====
//Revision 9591//:
* Create the folder for the new header files:
mkdir /usr/include/pfil/opt
//Revision 9586//:
* Create the folder for the new header files:
mkdir /usr/include/pfil/minix
//Revision 9071//:
* Create the folders for the new header files:
mkdir /usr/include/pfil
mkdir /usr/include/pf
mkdir /usr/include/pf/minix
mkdir /usr/include/pf/net
mkdir /usr/include/pf/opt
* Copy the default configuration files:
cp /usr/src/etc/pf.conf /etc/pf.conf
cp /usr/src/etc/pf.os /etc/pf.os
==== Does It Work? ====
Once the system is built and after a reboot, you can run the tests under the //test/pf// folder:
# cd /usr/src/test/pf
# make run
====== NetBSD ======
The //pfil// is a framework that offers two main capabilities:
* Placement of the head on which the hooks could be registered, capability used by the network side of the operating system.
* Registration of the hooks, capability used by the packet filter applications.
To understand how this framework works, its internals must be analyzed. \\ The NetBSD base system source code is in the directory ///usr/src// on a NetBSD box. The kernel is in the directory ///usr/src/sys//. All paths in this chapter are relative to the kernel source directory. //TAILQ_*// and //LIST_*// macros are defined in the <[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/queue.h?rev=1.52|sys/queue.h]]> header file and refer to specific implementation of lists, so there is no need to deepen these now.
==== The Hook ====
A hook is an entity that allows applications to run their callback functions at special points of the networking code. It is defined as following:
struct packet_filter_hook {
TAILQ_ENTRY(packet_filter_hook) pfil_link;
int (*pfil_func)(void *, struct mbuf **, struct ifnet *, int);
void *pfil_arg;
int pfil_flags;
};
The //pfil_link// field is used to insert the //packet_filter_hook// object in the functions list of the head to which it belongs (see [[.:#pfil_lists|below]]). \\ The //pfil_arg// field is the pointer of the function argument. \\ The //pfil_flags// field determines when the function must be called, it may take the following values: <>
PFIL_IN call the function on incoming packets
PFIL_OUT call the function on outgoing packets
PFIL_ALL call the function on all of the above
PFIL_IFADDR call the function on interface reconfiguration
PFIL_IFNET call the function on interface attach/detach
PFIL_WAITOK as reported in the pfil man page: "OK to call malloc with M_WAITOK" -- need more understanding here :)
The //pfil_func// field is the pointer of the associated function. A generic function is declared as following:
int pfil_func(void *, struct mbuf **, struct ifnet *, int);
* The first is a generic parameter which the caller can pass to the function.
* The second is the entity which holds the data received from the network. This structure is defined in the <[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/mbuf.h?rev=1.144|sys/mbuf.h]]> header file.
* The third represents the network device on the system. This structure is defined in the <[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/if.h?rev=1.146|net/if.h]]> header file and contains information like the interface name, the state of the interface, its capabilities and a group of function pointers.
* The last represents when the function was called, according to the //pfil_flag// [[.:#pfil_flags|values]].
The return value should be 0 if the packet processing is to continue or an errno value if the processing is to stop.
==== The Head ====
A head is a filtering point where it is possible to attach a hook. It is defined as following:
typedef TAILQ_HEAD(pfil_list, packet_filter_hook) pfil_list_t;
struct pfil_head {
pfil_list_t ph_in;
pfil_list_t ph_out;
pfil_list_t ph_ifaddr;
pfil_list_t ph_ifnetevent;
int ph_type;
union {
u_long phu_val;
void *phu_ptr;
} ph_un;
LIST_ENTRY(pfil_head) ph_list;
};
PFIL_TYPE_AF address family hook
PFIL_TYPE_IFNET interface hook
Depending on the value assumed by the previous field, the //ph_un// union contains different values and the following macros simplify the access to it:
#define ph_af ph_un.phu_val /* the ph_un field contains an AF_* type (e.g. IPv4 type is AF_INET, IPv6 type is AF_INET6) */
#define ph_ifnet ph_un.phu_ptr /* the ph_un field contains an ifnet pointer */
Finally, the //ph_list// field is used to insert the //pfil_head// object in the global list of head objects named //pfil_head_list// and defined as follow:
LIST_HEAD(, pfil_head) pfil_head_list = LIST_HEAD_INITIALIZER(&pfil_head_list);
==== Placement of a Head ====
The framework offers two functions to place a new head:
int pfil_head_register(struct pfil_head *ph);
int pfil_head_unregister(struct pfil_head *ph);
In both functions, the //ph// parameter is the object pointer to be register. \\ A network component can place a new head by setting the //ph_type// and //ph_un// fields and calling the //pfil_head_register()// function. In this way the new head is added to the global list of head and it is possible getting it to attach a new hook. \\ Instead, the call of the //pfil_head_unregister()// function causes the removal from the global list of the specified head .
==== Registration of a Hook ====
An application can register its callback function by registering a new hook with an existing head. The functions provided for this purpose by the framework are:
struct pfil_head * pfil_head_get(int type, u_long val);
int pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg, int flags, struct pfil_head *ph);
int pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg, int flags, struct pfil_head *ph);
At first, the app must identify the head that should be used by calling the //pfil_head_get()// function and specifying its type and value. Once the head is obtained, the recording of the new hook is done by calling the //pfil_add_hook()// function and passing as parameter the name of the target callback function. \\ Removing a registered hook is done by calling the //pfil_remove_hook()// function.
==== Execution of the Hooks ====
The framework provides the function //pfil_run_hooks()// to perform the callback functions registered to a head. It is the following:
int pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, int dir);
The //ph// parameter is the target head. \\ The //mp// parameter holds the data received from the network (refer to the second argument explained [[.:#pfil_func_args|here]] for further informations). \\ The //ifp// parameter represents the network device logically connected with this head (refer to the third argument explained [[.:#pfil_func_args|here]] for further informations). \\ The //dir// parameter determines which list must be traversed (refer to the //pfil_flags// [[.:#pfil_flags|values]] of the //packet_filter_hook// structure). \\ This function loads the head and runs through the list of hooks, launching the associated functions.
==== Example: IPv4 Head in the Kernel Networking Module ====
Part of the IPv4 networking logic is implemented in the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/netinet/ip_input.c?rev=1.286|netinet/ip_input.c]]" and "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/netinet/ip_output.c?rev=1.205|netinet/ip_output.c]]" source files. This file contains the steps that the kernel module crosses to ensure the support to the packet filters. The steps are:
* declaration of a module's new head:
struct pfil_head inet_pfil_hook;
* insertion of the new head in //pfil_head_list//:
inet_pfil_hook.ph_type = PFIL_TYPE_AF;
inet_pfil_hook.ph_af = AF_INET;
pfil_head_register(&inet_pfil_hook);
* inclusion of the hook execution code in the //ip_input.c::ip_input()// function, performed at each incoming IP packet:
if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN) != 0)
return; /* stop processing the packet */
else
/* continue processing the packet */
* inclusion of the hook execution code in the //ip_output.c::ip_output()// function, performed at each outgoing IP packet:
if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT) != 0)
return; /* stop processing the packet */
else
/* continue processing the packet */
==== Example: Interface Head in the Kernel Networking Module ====
In the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/if.c?rev=1.242|net/if.c]]" source file there is part of the management of network interfaces. Here the kernel registers other heads with pfil, one for each active interface and a global one for all interfaces. \\ The global head is declared as follows:
struct pfil_head if_pfil;
and registered as follows:
if_pfil.ph_type = PFIL_TYPE_IFNET;
if_pfil.ph_ifnet = NULL;
pfil_head_register(&if_pfil);
Note that the field //ph_ifnet// is //NULL// because that head is not associated with any specific interface. \\ The hook execution code of this head can be found in three places:
* the code management block of the ioctl operations in the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/netinet/in.c?rev=1.137|netinet/in.c]]" source file:
int in_control(struct socket *so, u_long cmd, void *data, struct ifnet *ifp, struct lwp *l)
{
switch (cmd) {
*..
case SIOCSIFADDR: /* set interface IP address */
pfil_run_hooks(&if_pfil, (struct mbuf **)SIOCSIFADDR, ifp, PFIL_IFADDR);
*..
case SIOCAIFADDR: /* add interface IP address */
pfil_run_hooks(&if_pfil, (struct mbuf **)SIOCAIFADDR, ifp, PFIL_IFADDR);
*..
case SIOCDIFADDR: /* delete interface IP address */
pfil_run_hooks(&if_pfil, (struct mbuf **)SIOCDIFADDR, ifp, PFIL_IFADDR);
*..
}
}
* the Synchronous PPP/Cisco link level subroutines in the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/if_spppsubr.c?rev=1.120|net/if_spppsubr.c]]" source file:
static void sppp_set_ip_addrs(struct sppp *sp, uint32_t myaddr, uint32_t hisaddr)
{
*..
pfil_run_hooks(&if_pfil, (struct mbuf **)SIOCAIFADDR, ifp, PFIL_IFADDR);
*..
}
static void sppp_clear_ip_addrs(struct sppp *sp)
{
*..
pfil_run_hooks(&if_pfil, (struct mbuf **)SIOCDIFADDR, ifp, PFIL_IFADDR);
*..
}
* the attachment/detachment process of the interfaces (see below).
The "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/if.c?rev=1.242|net/if.c]]" source file contains also the function //if.c::if_attach()// which is called to attach an interface to the list of the active interfaces. The definition is:
void if_attach(struct ifnet *ifp);
The //ifp// argument represents the interface to be enabled. \\ The function initializes and registers one of the field in the //struct ifnet//, i.e. the //if_pfil// field of type //struct pfil_head//, as follows:
ifp->if_pfil.ph_type = PFIL_TYPE_IFNET;
ifp->if_pfil.ph_ifnet = ifp;
pfil_head_register(&ifp->if_pfil);
After the registration, there is the call to //pfil_run_hooks()// on the //if_pfil// because the hooks on this type of head should be invoked whenever the associated interface is attached/detached or reconfigured:
pfil_run_hooks(&if_pfil, (struct mbuf **)PFIL_IFNET_ATTACH, ifp, PFIL_IFNET);
==== Example: Registration of pf Hooks ====
The //pf// packet filter interacts with //pfil// in two ways:
* registers one callback function for incoming and outgoing packets.
* registers two additional callback functions, one for the reconfiguration of the interfaces and one for their attachment/detachment process.
The first type of recording is made in the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/dist/pf/net/pf_ioctl.c?rev=1.42|dist/pf/net/pf_ioctl.c]]" source file. \\ The //pf_ioctl.c::pf_pfil_attach()// function obtains the head pointer and then registers the //pf_ioctl.c::pfil4_wrapper()// function as a hook on it:
struct pfil_head *ph_inet;
ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
pfil_add_hook((void *)pfil4_wrapper, NULL, PFIL_IN|PFIL_OUT, ph_inet);
The other recording type is made in the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/dist/pf/net/pf_if.c?rev=1.21|dist/pf/net/pf_if.c]]" source file by the //pf_if.c::pfi_initialize()// function. Unlike the previous one, there is not the research phase of the head pointer because this is visible through the <[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/pfil.h?rev=1.29|net/pfil.h]]> header file which exports the object declared in the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/if.c?rev=1.242|net/if.c]]" source file. Therefore, it registers //pf_if.c::pfil_ifnet_wrapper// and //pf_if.c::pfil_ifaddr_wrapper// as hooks on the //if_pfil// head:
pfil_add_hook(pfil_ifnet_wrapper, NULL, PFIL_IFNET, &if_pfil);
pfil_add_hook(pfil_ifaddr_wrapper, NULL, PFIL_IFADDR, &if_pfil);
===== Packet Filter Interface and pf on Minix =====
Integrating //pfil// on Minix is achieved primarily by defining a new compile-time flag named //PFIL_HOOKS//, which allows to enable/disable packet filtering module, and by the insertion of the "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/pfil.h?rev=1.29|net/pfil.h]]" and "[[http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/net/pfil.c?rev=1.27|net/pfil.c]]" source files in the inet source directory (//servers/inet///). It is important to carefully choose where to initialize/register the interface/IPv4 heads and where to execute the hook callbacks in the networking flow. In order to reduce the changes to adapt //pf//, the best approach that can be followed is to keep the interface offered to it by //pfil//, using wrapper functions for any conversion between the interface and the real implementation. \\ The Minix3 base system source code is in the directory ///usr/src// on a Minix box, all paths in this chapter are relative to this source directory.
==== Where to Change (draft) ====
Work in progress...
==== Placement of the IPv4 Head ====
Referring to the [[.:#netbsd_ipv4_head|previous example]], the positioning of the IPv4 head is achieved through the following steps:
* inclusion of the //servers/inet/pfil.h// header file and declaration of the head:
//servers/inet/generic/ip.c//:
#ifdef PFIL_HOOKS
#include "pfil.h"
#endif
...
#ifdef PFIL_HOOKS
PUBLIC struct pfil_head inet_pfil_hook;
#endif
* initialization and registration at the end of the //ip.c::ip_init()// function:
//servers/inet/generic/ip.c//:
PUBLIC void ip_init()
{
int i; /* this declaration already exists*/
*..
#ifdef PFIL_HOOKS
inet_pfil_hook.ph_type = PFIL_TYPE_IFNET;
inet_pfil_hook.ph_ifnet = NULL;
if ( (i = pfil_head_register(&inet_pfil_hook)) != 0)
printf("inet: ip_init: WARNING: unable to register pfil hook, error %d\n", i);
#endif
}
* placement of the execution code for incoming packets, just before the delivery decision in the //ip_read.c::ip_arrived()// function and before the //ip_port_arrive()// call in the //ip_read.c::ip_arrived_broadcast()// function:
//servers/inet/generic/ip_read.c//:
#ifdef PFIL_HOOKS
#include "pfil.h"
#endif
#ifdef PFIL_HOOKS
EXTERN struct pfil_head inet_pfil_hook;
#endif
PUBLIC void ip_arrived(ip_port, pack)
ip_port_t *ip_port;
acc_t *pack;
{
#ifdef PFIL_HOOKS
struct mbuf *m;
#endif
*..
#ifdef PFIL_HOOKS
/* mapping the incoming packet and the interface info in the mbuf
* structure */
if (pfil_run_hooks(&inet_pfil_hook, &m, m->mb_pkthdr.rcvif,
PFIL_IN) != 0)
return;
if (m == NULL)
return;
#endif /* PFIL_HOOKS */
dest= ip_hdr->ih_dst;
if (dest == ip_port->ip_ipaddr) {
*..
}
PUBLIC void ip_arrived_broadcast(ip_port, pack)
ip_port_t *ip_port;
acc_t *pack;
{
#ifdef PFIL_HOOKS
struct mbuf *m;
#endif
*..
#ifdef PFIL_HOOKS
/* mapping the incoming packet and the interface info in the mbuf
* structure */
if (pfil_run_hooks(&inet_pfil_hook, &m, m->mb_pkthdr.rcvif,
PFIL_IN) != 0)
return;
if (m == NULL)
return;
#endif /* PFIL_HOOKS */
ip_port_arrive (ip_port, pack, ip_hdr);
}
* placement of the execution code for outgoing packets, just before the checksum calculation in the //ip_write.c::ip_send()// function:
//servers/inet/generic/ip_write.c//:
#ifdef PFIL_HOOKS
#include "pfil.h"
#endif
#ifdef PFIL_HOOKS
EXTERN struct pfil_head inet_pfil_hook;
#endif
PUBLIC int ip_send(fd, data, data_len)
int fd;
acc_t *data;
size_t data_len;
{
int r; /* this declaration already exists*/
#ifdef PFIL_HOOKS
struct mbuf *m;
struct ifnet *ifp;
#endif /* PFIL_HOOKS */
*..
#ifdef PFIL_HOOKS
/* mapping the outgoing packet in the mbuf structure and the
* interface info in the ifnet structure */
/*
* Run through list of hooks for output packets.
*/
if ((r = pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT)) != 0)
return (r);
if (m == NULL)
return (r);
#endif /* PFIL_HOOKS */
ip_hdr_chksum(ip_hdr, hdr_len); /* this function call already exists*/
*..
}
==== Placement of the Interface Head ====
Referring to the [[.:#netbsd_if_head|previous example]], the positioning of the interface heads is achieved through the following steps:
===== To Do on This Page =====
* [The NetBSD Packet Filter Interface] Insert a new subchapter to expand ifnet description - Depeen PFIL_WAITOK.
* [Packet Filter Interface and pf on Minix] Justify the choices of positioning.
* **MASSIVE UPDATE!!!**
===== Weekly Status =====
==== Community Bonding Period (27.4.2010 - 23.5.2010) ====
This period is focused on the study of //pfil// (the NetBSD packet filter interface) and //pf// (the NetBSD packet filter), assessing their true portability on the Minix environment. This period is also focused on the analysis of the Minix networking framework, i.e. the //inet// server source code.
==== Week 1 ====
Completion of the wiki chapter: "The NetBSD Packet Filter Interface".
==== Week 2 ====
Branch rebased to r7148. Created the new //gsoc_test// folder in the branch as standalone environment. Analyzed the dependencies of the NetBSD //mbuf// structure and adapted its source files in the //gsoc_test// folder.
==== Week 3 ====
Completion of the porting of the //pfil// framework in the //gsoc_test// folder, resolving all data dependencies of the structures. Branch rebased to r7268.
==== Week 4 ====
Inactivity due to a medical intervention.
==== Week 5 ====
Depth study of the Minix //ip_send()// and the NetBSD //ip_output()// functions. Completion of the positioning of the //pfil_run_hooks()// calls in the IPv4 packet processing flow. Branch rebased to r7538.
==== Week 6 ====
Merged the changes in inet, adding comments to the //inet::ip_arrive()//, //inet::ip_arrive_broadcast()// and //inet::ip_send()// functions. Branch rebased to r7657.
==== Week 7 ====
Planning of the mid-term project with the mentors: implementation of most of the functionality of the [[http://groups.google.com/group/minix3/msg/f6313a448dfe2dd8?pli=1|firewall sheep/goat project]], using the //pfil// infrastructure. Creation of a new device, ///dev/simplepf//, with no functionality (i.e. no read(), no write(), no ioctl(), etc.).
==== Week 8 (Mid-term Evaluation) ====
Added open(), close and ioctl() support to the new //simplepf// device. Release of the sheep/goat project for the mid-term evaluation. Reorganization of the //pf//-related file hierarchy. Branch rebased to r7778.
==== Week 9 ====