Vuurmuur
From 3DN
| This site is part of the 3DN Network. Every content on the 3DN Network is subject to the [3DN Terms of Service]. Other sites in the 3DN Network include: 3DN Politics 3DN Technology 3DN Voetbal 3DN Politicap and Dutchie |
Contents |
Vuurmuur
Somewhere around oct. 25th 2009 I started helping a little bit with developing vuurmuur, a firewall application. I've written an article on this choice on my [wordpress server]. Here on the Wiki I will mostly document coding and configuration issues for vuurmuur, publishing this info in the hope that somebody might find it useful.
vim
Here are some handy vim settings to use for vuurmuur development:
syntax on set et set ts=4 set sw=4 set ai set modeline set tags=tags;/
The 'tags=tags;/' line searches for a tag file in the current directory. If it's not there it will look up one directory etc. In the toplevel directory I use 'ctags -R' to make a tagfile for all the sources.
Ticket #72: Handle logrotation
[Handle logrotation ] This is the first ticket I decided to act on, I also posted some comments on it on the vuurmuur trac. The issue here was that sometimes vuurmuur_conf would keep the logfile open fairly long. If the logfile then gets rotated, vuurmuur_conf would not notice this.
I proposed two possible ways:
- vuurmuur_conf should listen to signals. If it got a HUP signal it should close the current log filedescriptor and reopen it again.
- vuurmuur_conf might use the [inotify] API to get events from the filesystem. It would then get an event when the file gets deleted and act on that.
A hybrid combination is probably best as not all systems may support inotify. This also means we need to somehow build a check into [autoconf] to test for inotify capability.
Testing for sys/inotify.h with autoconf
To make autoconf do anything useful for you, you need to add a test case for something in configure.in. I added the following early in the configure.in file:
AC_INIT(configure.in) AC_USE_SYSTEM_EXTENSIONS
And later on in the configure.in file:
# inotify check
if test x$enable_inotify = xno; then
have_inotify=no;
else
AC_CHECK_HEADERS(sys/inotify.h, have_inotify=no, have_inotify=yes)
fi
Through autoconf's black voodoo magic, running autoconf again results in a testcase added in the configure script:
# inotify check
if test x$enable_inotify = xno; then
have_inotify=no;
else
for ac_header in sys/inotify.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_inotify_h" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SYS_INOTIFY_H 1
_ACEOF
have_inotify=no
else
have_inotify=yes
fi
done
fi
This is very nice ofcourse, but running the configure script again gives some more tangible results in config.h:
/* Define to 1 if you have the <sys/inotify.h> header file. */ #define HAVE_SYS_INOTIFY_H 1
The toplevel config.h gets included by src/main.h so now we can include the inotify code in the C programs that include main.h and #ifdef them with #ifdef HAVE_SYS_NOTIFY_H
C code
The only file I'll need to modify seems to be the logview_section.c file, which builds an ncurses logviewer window, fopen()'s one of the selected logfiles and displays the info in this logfile in a loop. First I'll include some variables that I need to use inotify functions:
#ifdef HAVE_SYS_INOTIFY_H
#include <sys/inotify.h>
int ifd;
int wd;
#define EVENT_SIZE (sizeof (struct inotify_event))
#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
#define ACTION_LENGTH (50+FILENAME_MAX)
ssize_t len, evcnt;
#endif
Later on, just before the loop reading logfiles I'll initialize the inotify handler:
// If we have INOTIFY, set it up for the logfile
#ifdef HAVE_SYS_INOTIFY_H
if ((ifd = inotify_init1(IN_NONBLOCK)) == -1)
{
(void)vrprint.error(-1, VR_ERR, gettext("inotify_init failed: %s (in: %s
:%d)."), strerror(errno), __FUNCTION__, __LINE__);
return (-1);
} else {
(void)vrprint.debug(__FUNC__, "initialized inotify fd %d", ifd);
}
if ((wd = inotify_add_watch(ifd, logfile, (IN_ALL_EVENTS))) == -1)
{
(void)vrprint.error(-1, VR_ERR, gettext("inotify_add_watch failed: %s (i
n: %s:%d)."), strerror(errno), __FUNCTION__, __LINE__);
return (-1);
}
#endif
Notice that:
- I'm using IN_NONBLOCK. I don't want a read on the inotify instance to block the program and make filterselection, pausing etc. impossible for obvious reasons.
- Right here in this example code I'm using IN_ALL_EVENTS. I really only want to check whether the logfile being read has been moved by some logfile rotation script so technically I only need the IN_MOVE_SELF flag. Conceivably I could also make the reading of the file more event driven by using the IN_MODIFY flag to check if we should fgets() to begin with but that's of later concern.
Finally inside the main loop I'm adding this (tentative) code:
/* the main loop */
while(quit == 0)
{
/* read line from log */
if(!(line = malloc(READLINE_LEN)))
{
(void)vrprint.error(-1, VR_ERR, gettext("malloc failed: %s (in: %s:%
d)."), strerror(errno), __FUNCTION__, __LINE__);
d_list_cleanup(debuglvl, buffer_ptr);
return(-1);
}
#ifdef HAVE_SYS_INOTIFY_H
char buff[EVENT_BUF_LEN] = {0};
if ((len = read (ifd, buff, EVENT_BUF_LEN -1)) == -1) {
if (errno != EAGAIN) {
(void)vrprint.error(-1,VR_ERR, "ifd read failed: %s (in %s:%d)",
strerror(errno), __FUNCTION__, __LINE__);
return (-1);
} else {
// EAGAIN, nothing happened.
}
} else {
// Hey, something is up, let's check what it is!
evcnt = 0;
while (evcnt < len)
{
struct inotify_event *pevent = (struct inotify_event *) & buf
f[evcnt];
char action[ACTION_LENGTH] = {0};
(void)vrprint.debug(__FUNC__, "got %d", pevent->mask);
evcnt += sizeof(struct inotify_event) + pevent->len;
}
}
/* read a line if we are not in pause mode */
if(!control.pause && fgets(line, READLINE_LEN, fp) != NULL)
#else
/* read a line if we are not in pause mode */
if(!control.pause && fgets(line, READLINE_LEN, fp) != NULL)
#endif
This is tentative for now as it does not seem to work well for some reason. Additionally, the fgets() line inside the HAVE_SYS_INOTIFY_H block should obviously have some functionality for reopening the file when an IN_MOVE_SELF does get detected properly.
NOTE: I've temporarily put this effort on the back burner when realizing that the logfiles vuurmuur_conf watches are simply the logfile created by vuurmuur_log. Those files are owned by vuurmuur so should never be rotated or deleted unexpectedly.
Ticket #6: Ulog or libnetfilter_log support to replace syslog
I started looking in the vuurmuur logfiles generated by vuurmuur_log because of ticket #72 but stalled that effort for a while. vuurmuur_log right now parses lines in the syslog files, filtering out lines related to the firewalling and writing these lines to the vuurmuur logfiles. This involves a lot of text file parsing.
[Ticket #6] aims to replace the syslog mechanism by either using ulogd or using libnetfilter_log. libnetfilter_log is a library through which we could easily subscribe to and parse packets sent to the NFLOG layer by the kernel. ulogd is itself a libnetfilter_log subscriber that optionally generates a logfile.
Vuurmuur_log will:
- become a libnetfilter_log subscriber and parse raw packets instead, not having a dependency on ulogd
We have agreed however to add a global config option 'Use NFLOG'. When a user uses NFLOG, vuurmuur_log will get it's input from libnetfilter_log. The rule 'Logging' checkbox will remain there as it is, but depending on the global option it will mean either syslog or NFLOG logging.
We've created a branch vuurmuur-ulog in which I will do the work for nflog. Some code that TigerP already committed will also be put in this branch.
Using libnetfilter_log is not trivial, nor is using the NFLOG target in iptables as most of the code is still under development at the [Netfilter] site. Adding an option to vuurmuur_conf was relatively simple and straightforward after getting some traction on the code. Making vuurmuur create rules that use NFLOG and the --nflog-group was also not very hard to do, but the changes to vuurmuur_log can be tricky. I will check out the source code of the lib from GIT for this:
omega:/usr/src# git://git.netfilter.org/libnetfilter_log.git
Scratchpad:
<fredl> d_sun: do you know why I always seem to get payload_len == header length? That seems to imply there's no additional data in the packets I'm getting in my libnetfile_log callback. <fredl> that doesn't sound right to me... I guess I must be interpreting one or the other incorrectly <d_sun> hm <d_sun> it's correct. <d_sun> Most well-formed IPv4 packets are no longer than indicated. <fredl> :) <fredl> sorry I guess I'm expressing myself poorly trying too hard to be to the point. <d_sun> Somebody could send a faulty IPv4 packet, then the payload_len can be smaller than iphdr->tot_len <fredl> let me try this again... in the payload there's a variable amount of ip header fields. Is the header length inside the packet the lenght of the *ip header* ? <d_sun> (short of such packets already filtered by ipv4/input.c) <fredl> so |---- header lenght ---|---more data----| <d_sun> The payload length is whatever was received from the network card, minus L2 header. <fredl> so in most cases, should payload lenght not be > IP header length? <d_sun> There is nothing that prohibits you from sending <L2 hdr len=1418><L4 hdr tot_len=20><1400 bytes> <d_sun> prohibit... prevent <d_sun> <L2=1418><L3 len=20><1380 data> or so <fredl> in the latter case, payload len should be L3 len + 1380, correct? <fredl> *if* ofcourse I also get all the packet's data in the callback. <fredl> which I'm hoping I will since I did nflog_set_mode (qh, NFULNL_COPY_PACKET, 0xffff) <d_sun> I am just saying that payload_len is what you get in terms of data. Whether the IPv4 header makes any sense is yoru job. <fredl> I agree, and so far the IPv4 header is making perfect sense. <fredl> all I'm saying is that it appears to me as if I'm *only* getting the IPv4 header in the callback. <d_sun> but it's not required to. that's all. <d_sun> Or when you get something that is not an IPv4 packet - such as ARP, IPv6, things go different <d_sun> IPv6 and ARP do not have any size field IIRC, so payload_len minus sizeof(struct ipv6hdr) is what you have to use to fnid the L4 size <fredl> ok, I can work with that. <d_sun> except you should not do that with IPV4, where you should use its tot_len hdr <fredl> yeah that's what I'm using right now and tot_len == payload_len all the time so far. <fredl> oooh wait, I get it. <fredl> *blink* <Devastator> oh well, my soccer team lost :( <d_sun> if (proto == IPV4) l4len = ntohl(tot_len)*4; else if (proto ==IPv6) l4len = payload_len - sizeof(struct ipv6hdr); <d_sun> fredl: The added fun is that tot_len could also be larger than payload_len, which you have to check for <d_sun> or that payload_len is smaller than struct ipv6hdr, where you have to be aware of underflowing arithmetic <fredl> oh gosh, and I thought I was almost there :P
VuurMuur Configuration
I've gone through some pains trying to subnet different classes of devices and creating vuurmuur config rules for these networks. The problems were not so much with vuurmuur as they were with creating a working configuration for my DHCP server, creating different subnets and maybe most of all how to deal with the ethernet bridging device I need to facilitate the virtual servers I intend to be running.