Tuesday, November 14, 2006

PF_RING and Snort

In the hopes of minimizing packet loss on my Snort IDS sensors, I've been experimenting with the PF_RING Linux kernel patches. My initial results have been impressive as both sensors report 0% packet loss (I'm of course presuming it isn't a blatant lie :-) ). I'll try to document my experiences here.

UPDATE: An anonymous commenter (thanks!) noted that PF_RING doesn't report lost packets through libpcap (as Snort expects), but rather through files in /proc/net/pf_ring. I'm still working out the details on how to use that information effectively.

My work is on RedHat Enterprise 4, so if you are using a different distro, your mileage may vary, however, it shouldn't be drastically different.

UPDATE: Here's a Debian Sarge oriented howto:
http://bjou.homeunix.net/blog/2006/12/advanced-packet-capturing-howto-pf_ring-napi-and-extended-libpcap-on-debian-sarge/

There are 2 Fedora Core (4 and 5) instruction pages at:

http://wiki.ntop.org/mediawiki/index.php/Installing_PF_RING_and_nProbe_on_Fedora_Core_4_%28FC4%29

and

http://wiki.ntop.org/mediawiki/index.php/Installing_on_Fedora_Core_5_%28FC5%29

that these are based upon, and (hopefully) expand upon. Thanks to the authors of those.

Before beginning, make sure that your system is fully up to date with patches. These instructions presume that you have at least 2+ GBs of free disk space, primarily in /usr/src.

1. Software packages needed (in addition to a minimal install on RHEL):

glibc-kernheaders
autoconf
automake
bison
flex
cvs
gcc
gcc-c++
libtool
tcl (note this version is compiled without threading, so good for Sguil)
tcl-devel
tclx
rpm-build
redhat-rpm-config
openssl-devel
pcre-devel
ncurses-devel

On a registered RHEL, you'd run the command:

up2date -f install [package_name]

to download package_name and required dependancies, and install them.

* Note: not all of these are neccessary for just PF_RING, however, are needed for other things like Snort, Sguil, barnyard, sancp, etc.

2. Remove software:

At a bare minimum, the following software packages need to be removed:
libpcap
ppp *
rp-pppoe *
wvdial *

The command to do this is:

rpm -e

* relies on libpcap. If needed, will need to be recompiled with the new libpcap that we'll compile and install later.

3. Install the kernel src.rpm of the latest kernel (on RHEL, kernel-2.6.9-42.0.3.EL.src.rpm at the time of this writing).

For RHEL 4, the file will be located ftp.redhat.com. For example, to get the latest kernel for RHEL as of today, you'd run the command:

wget ftp://ftp.redhat.com:/pub/redhat/linux/updates/enterprise/4XX/en/os/SRPMS/kernel-2.6.9-42.0.3.EL.src.rpm

where XX is the type of RHEL 4 you're running: Desktop (uhh, desktop), WS (workstation), ES (enterprise server), or AS (advanced server). If you don't know, run the command:

[root@hostname ~]# cat /etc/redhat-release
Red Hat Enterprise Linux WS release 4 (Nahant Update 4)

In this case, this machine was a workstation, so use WS.

Now install the kernel with the command:

rpm -ivh kernel-2.6.9-42.0.3.EL.src.rpm

This will place the vanilla 2.6.9 kernel src, RedHat's patches, spec file, etc. into /usr/src/redhat/{SOURCES,SPECS}.

4. Prep the kernel for the PF_RING patches.

Now we need to prep the kernel. First, change to the appropiate directory:

cd /usr/src/redhat/SPECS

Then run the command:

rpmbuild -bp --target $(arch) kernel-2.6.spec

If this works, you should see something similar to the following:

Building target platforms: i686
Building for target i686
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.7797
+ umask 022
+ cd /usr/src/redhat/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ cd /usr/src/redhat/BUILD
+ rm -rf kernel-2.6.9
+ /bin/mkdir -p kernel-2.6.9
+ cd kernel-2.6.9
+ /usr/bin/bzip2 -dc /usr/src/redhat/SOURCES/linux-2.6.9.tar.bz2
+ tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
++ /usr/bin/id -u
+ '[' 0 = 0 ']'
+ /bin/chown -Rhf root .
++ /usr/bin/id -u
+ '[' 0 = 0 ']'
+ /bin/chgrp -Rhf root .
+ /bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ cd linux-2.6.9
+ echo 'Patch #3 (patch-2.6.9-ac11.bz2):'
Patch #3 (patch-2.6.9-ac11.bz2):
+ /usr/bin/bzip2 -d
+ patch -p1 -s
[snip]
removed `./net/xfrm/xfrm_state.c.orig'
removed `./net/socket.c.orig'
removed `./net/netlink/af_netlink.c.orig'
removed `./net/ipv6/ip6_output.c.orig'
removed `./net/ipv6/addrconf.c.orig'
removed `./net/ipv6/netfilter/ip6_tables.c.orig'
removed `./net/bluetooth/af_bluetooth.c.orig'
removed `./net/8021q/vlan.c.orig'
removed `./net/sched/sch_api.c.orig'
+ find . -name '*~' -exec rm -fv '{}' ';'
+ exit 0

This applies all of the various patches (nearly 1000) that RedHat applies to it's kernels.

Now we need to copy that patched kernel source to /usr/src with the command:

cp -a /usr/src/redhat/BUILD/kernel-2.6.X/linux-2.6.X /usr/src

where X is the subversion. In this case, for RHEL 4, X = 9.

Now, create a symlink from the actual kernel to the generic linux:

cd /usr/src
ln -s ./linux-2.6.X linux

where X is the subversion. Again, in this case, for RHEL 4, X = 9.

5. Download and build the PF_RING patch for your kernel.

Now we need to get the PF_RING patches downloaded and built for our kernel. PF_RING is currently only available via CVS. To do this, run the following:

cd /usr/src
CVSROOT=:pserver:anonymous@cvs.ntop.org:/export/home/ntop;export CVSROOT
mkdir pf_ring && cd pf_ring
cvs login

which should produce the following output:

Logging in to :pserver:anonymous@cvs.ntop.org:2401/export/home/ntop
CVS password:

At the prompt, type "ntop" (no quotes), and hit Enter. (Note: ntop will not appear on the screen.) Next type the following:

cvs checkout PF_RING

which, if it works, should produce something simliar to the following:

cvs checkout: Updating PF_RING
U PF_RING/README
U PF_RING/mkpatch.sh
cvs checkout: Updating PF_RING/kernel
U PF_RING/kernel/README
cvs checkout: Updating PF_RING/kernel/include
cvs checkout: Updating PF_RING/kernel/include/linux
U PF_RING/kernel/include/linux/ring.h
cvs checkout: Updating PF_RING/kernel/include/net
U PF_RING/kernel/include/net/PATCH-to-sock.h
cvs checkout: Updating PF_RING/kernel/net
U PF_RING/kernel/net/PATCH-to-Config.in
U PF_RING/kernel/net/PATCH-to-netsyms.c
cvs checkout: Updating PF_RING/kernel/net/core
U PF_RING/kernel/net/core/PATCH-1-to-dev.c
U PF_RING/kernel/net/core/PATCH-2-to-dev.c
U PF_RING/kernel/net/core/PATCH-3-to-dev.c
cvs checkout: Updating PF_RING/kernel/net/ring
U PF_RING/kernel/net/ring/Config.in
U PF_RING/kernel/net/ring/Kconfig
U PF_RING/kernel/net/ring/Makefile
U PF_RING/kernel/net/ring/Makefile-2.4.X
U PF_RING/kernel/net/ring/Makefile-2.6.X
U PF_RING/kernel/net/ring/ring_packet.c
cvs checkout: Updating PF_RING/userland
cvs checkout: Updating PF_RING/userland/libpcap-0.9.4-ring
U PF_RING/userland/libpcap-0.9.4-ring/README
U PF_RING/userland/libpcap-0.9.4-ring/pcap-int.h
U PF_RING/userland/libpcap-0.9.4-ring/pcap-linux.c
cvs checkout: Updating PF_RING/userland/libpfring
U PF_RING/userland/libpfring/Makefile
U PF_RING/userland/libpfring/pfcount.c
U PF_RING/userland/libpfring/pfring.c
U PF_RING/userland/libpfring/pfring.h
cvs checkout: Updating PF_RING/userland/pcount
U PF_RING/userland/pcount/Makefile
U PF_RING/userland/pcount/pcount.c

The current version of PF_RING comes with a script that creates a patch specific to your kernel. Now we'll get things in order to run that script with the following commands:

cd /usr/src/pf_ring/PF_RING
mkdir workspace
cd workspace
cp /usr/src/redhat/SOURCES/linux-2.6.9.tar.bz2 .
bunzip2 linux-2.6.9.tar.bz2
gzip linux-2.6.9.tar
cd ..

Now edit the file mkpatch.sh, and adjust the following variables to match your environment:

SUBLEVEL=${SUBLEVEL:-18.1}

EXTRAVERSION=${EXTRAVERSION:--i686-smp-$PATCH}


For example, on RHEL 4, you'd change to the variables to:

SUBLEVEL=${SUBLEVEL:-9}

EXTRAVERSION=${EXTRAVERSION:--42.0.3.ELsmp-$PATCH}


Save your changes, and run the script:

sh ./mkpatch.sh

If all goes well, the output should look similar to the following:

Creating patch for Linux kernel linux-2.6.9 ...
Edit this file (mkpatch.sh) for a different kernel version
Kernel build area is /usr/src/pf_ring/PF_RING/workspace
rm: cannot remove `/usr/src/pf_ring/PF_RING/workspace/ring3': No such file or directory
Creating link to /usr/src/pf_ring/PF_RING in /usr/src/pf_ring/PF_RING/workspace called ring3
Found linux-2.6.9.tar.gz in source directory /usr/src/pf_ring/PF_RING/workspace
Untarring Linux sources (read-only tree) in /usr/src/pf_ring/PF_RING/workspace/linux-2.6.9
Cloning Linux sources (read-write tree) in /usr/src/pf_ring/PF_RING/workspace
Patching Linux sources ...
1. Install additional file include/linux/ring.h with definitions
for packet ring.
done
2. Install the ring sources under the kernel tree.
Installing kernel ring sources in
linux-2.6.9-42.0.3.ELsmp-ring3/net/ring ... done
3. Patch net/core/dev.c ...
Patch #1 (define ring_handler)
Patch #2 (modify function netif_rx and netif_receive_skb)
Patch #3 (modify dev_queue_xmit, found in PATCH-3-to-dev.c)
... done
4. Patching file net/Makefile ... done
5. Copy net/ring/Kconfig to linux-2.6.9-42.0.3.ELsmp-ring3/net/ring/Kconfig done
6. Patching file net/Kconfig ... done
diff --unified --recursive --new-file linux-2.6.9 linux-2.6.9-42.0.3.ELsmp-ring3 > linux-2.6.9-42.0.3.ELsmp-ring3.patch
Making Linux patch file. This could take some time, please wait ... done
Your patch file is now in /usr/src/pf_ring/PF_RING/workspace/linux-2.6.9-42.0.3.ELsmp-ring3.patch.gz

6. Apply the patch and build the kernel.

To apply the patch, run the following:

cd /usr/src
zcat /usr/src/pf_ring/PF_RING/workspace/linux-2.6.*patch.gz | patch --dry-run -p0

If the last command doesn't report any errors, then run the following:

zcat /usr/src/pf_ring/PF_RING/workspace/linux-2.6.*patch.gz | patch -p0

The output should look similar to:

patching file linux-2.6.9/include/linux/ring.h
patching file linux-2.6.9/net/core/dev.c
Hunk #2 succeeded at 1302 (offset -56 lines).
Hunk #3 succeeded at 1519 with fuzz 2 (offset -60 lines).
Hunk #4 succeeded at 1738 with fuzz 2 (offset -43 lines).
patching file linux-2.6.9/net/core/dev.c.ORG
patching file linux-2.6.9/net/Kconfig
patching file linux-2.6.9/net/Makefile
Hunk #1 succeeded at 41 (offset 1 line).
patching file linux-2.6.9/net/Makefile.ORG
patching file linux-2.6.9/net/ring/Kconfig
patching file linux-2.6.9/net/ring/Makefile
patching file linux-2.6.9/net/ring/ring_packet.c

Next, run the following:

cd /usr/src/linux
make menuconfig

When the configuration menu appears, change the following:

Processor type and features -> High Memory Support -> 64GB
Device Drivers -> Networking Support -> Networking Options -> PF_RING (module)

Exit and save the configuration. Now, run the following to compile the kernel:

make
make modules
make modules_install
make install

UPDATE: If the first command 'make' fails with:


net/ring/ring_packet.c:15:26: linux/config.h: No such file or directory


then, as documented here, edit the file net/ring/ring_packet.c, and replace:

#include

with

#include


This sequence of commands will take a long time to complete, which is normal.

The command "make install" will place the new kernel in /boot, typically with a -prep tag:

[root@idsext ~]# ls -l /boot/*prep*
-rw-r--r-- 1 root root 492685 Nov 18 17:52 /boot/initrd-2.6.9-prep.img
-rw-r--r-- 1 root root 750587 Nov 18 17:52 /boot/System.map-2.6.9-prep
-rw-r--r-- 1 root root 1509678 Nov 18 17:52 /boot/vmlinuz-2.6.9-prep

as well as add an entry to /etc/grub.conf:

title Linux (2.6.9-prep)
root (hd0,0)
kernel /vmlinuz-2.6.9-prep ro root=/dev/md1
initrd /initrd-2.6.9-prep.img

Now, reboot the machine into the new kernel. NOTE: Do NOT remove older kernels until you are positive this one works. In fact, probably best to leave the old kernel there, so up2date doesn't get confused.

7. Compile libpfring, and test that the module works.

First off, start with doing the following:

cp /usr/src/linux/include/linux/ring.h /usr/include/linux

which will add the neccessary header file, ring.h, to the standard include directory.

Next, run the following:

cd /usr/src/pf_ring/PF_RING/userland/libpfring
make

If all goes well, you should see something similar to:

gcc -g -c -I/lib/modules/2.6.9-prep/source/include pfcount.c
gcc -g -c -I/lib/modules/2.6.9-prep/source/include pfring.c
ar rc libpfring.a pfring.o
ranlib libpfring.a
gcc -g pfcount.o pfring.o -o pfcount

Now, we are going to generate a .so from these files, by running the following:

gcc -shared -Wl,-soname -Wl,libpfring.so.0.9.4 -o libpfring.so.0.9.4 *.o -lc

Now, we copy the files we just created to a common system directory:

cp libpfring.a libpfring.so.0.9.4 /usr/local/lib
cp pfring.h /usr/local/include

Next, we need to add /usr/local/lib to the list of directories the dynamic loader will search:

echo "/usr/local/lib" >> /etc/ld.so.conf
ldconfig

To check that the dynamic loader sees the libraries, run the following:

ldconfig -v |grep pfring

which should produce the following output:

libpfring.so.0.9.4 -> libpfring.so.0.9.4

Now, to test the ring module we added to the kernel earlier, run the following:

./pfcount -v -i [interface]

where [interface] is the interface that you will be monitoring traffic on (and hopefully has traffic on it already, otherwise, this test won't be very interesting). For example, that interface for me is eth1, so I would type:

./pfcount -v -i eth1

and the output should look something similar to:

14:23:04.794105 [00:30:48:2B:EF:76 -> 00:07:E9:47:DD:5F] [192.168.36.39 -> 192.168.156.118] [caplen=66][len=66]
14:23:04.794162 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794252 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=338][len=338]
14:23:04.794341 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794426 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794514 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794597 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794680 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794767 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794849 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794932 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.794995 [00:30:48:2B:EF:76 -> 00:07:E9:47:DD:5F] [192.168.36.39 -> 192.168.156.118] [caplen=66][len=66]
14:23:04.795030 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.795101 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=226][len=226]
14:23:04.795355 [00:07:E9:47:DD:5F -> 00:30:48:2B:EF:76] [192.168.156.118 -> 192.168.36.39] [caplen=146][len=146]

Hit ctrl-C, and the last few lines of the output should give you summary similar to:

=========================
Absolute Stats: [3672 pkts rcvd][47 pkts dropped]
Total Pkts=3625/Dropped=1.3 %
3672 pkts [4116.2 pkt/sec] - 871764 bytes [7.82 Mbit/sec]
=========================
Actual Stats: 3672 pkts [1599568.0 ms][2.3 pkt/sec]
=========================

Now, run the following:

dmesg

and the last few lines of output should look similar to the following:

RING: succesfully allocated 128 KB [tot_mem=26509372][order=5]
RING: allocated 80 slots [slot_len=1618][tot_mem=131072]
device eth1 entered promiscuous mode

which is a good indication that the kernel module is working.

8. Next step is to build libpcap to use the PF_RING interface to the kernel.

First, run the following:

cd /usr/src/pf_ring/PF_RING/userland/
ls |grep pcap

The output should be something similar to:

libpcap-0.9.4-ring

which indicates that this version of PF_RING was built to work with a patched version of libpcap-0.9.4, so we need to download that version.

The simplest way to download that version is to run the following:

wget http://www.tcpdump.org/release/libpcap-0.9.4.tar.gz

which should produce output similar to:

--14:37:06-- http://www.tcpdump.org/release/libpcap-0.9.4.tar.gz
=> `libpcap-0.9.4.tar.gz'
Resolving www.tcpdump.org... 205.150.200.214
Connecting to www.tcpdump.org|205.150.200.214|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 425,887 (416K) [application/x-tar]

100%[===================================================>] 425,887 78.37K/s ETA 00:00

14:37:15 (79.23 KB/s) - `libpcap-0.9.4.tar.gz' saved [425887/425887]

If the above command doesn't work, you'll need to download it by hand. I'll leave that as an exercise for the reader to figure out.

Now, unpack the gzip'd tarball:

tar -zxvf libpcap-0.9.4.tar.gz

which should produce output similar to:

libpcap-0.9.4/./
libpcap-0.9.4/./ChmodBPF/
libpcap-0.9.4/./ChmodBPF/ChmodBPF
libpcap-0.9.4/./ChmodBPF/StartupParameters.plist
libpcap-0.9.4/./.cvsignore
libpcap-0.9.4/./CHANGES
libpcap-0.9.4/./CREDITS
libpcap-0.9.4/./FILES
libpcap-0.9.4/./INSTALL.txt
libpcap-0.9.4/./LICENSE
libpcap-0.9.4/./Makefile.in
libpcap-0.9.4/./README
[snip]
libpcap-0.9.4/./msdos/readme.dos
libpcap-0.9.4/./packaging/
libpcap-0.9.4/./packaging/pcap.spec
libpcap-0.9.4/./packaging/pcap.spec.in

Now, do the following:

cd libpcap-0.9.4
mv pcap-int.h pcap-int.h.orig
mv pcap-linux.c pcap-linux.c.orig
cp ../libpcap-0.9.4-ring/pcap* .
./configure CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64"

If the ./configure command completes without any errors, run the following:

make && gcc -shared -Wl,-soname -Wl,libpcap.so.`cat VERSION` -o libpcap.so.`cat VERSION` *.o -lc

If the above commands complete successfully, you should see something similar to:

gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./pcap-linux.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./fad-getad.c
sed -e 's/.*/static const char pcap_version_string[] = "libpcap version &";/' ./VERSION > version.h
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./pcap.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./inet.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./gencode.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./optimize.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./nametoaddr.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./etherent.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./savefile.c
rm -f bpf_filter.c
ln -s ./bpf/net/bpf_filter.c bpf_filter.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c bpf_filter.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./bpf_image.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c ./bpf_dump.c
flex -Ppcap_ -t scanner.l > $$.scanner.c; mv $$.scanner.c scanner.c
bison -y -p pcap_ -d grammar.y
mv y.tab.c grammar.c
mv y.tab.h tokdefs.h
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c scanner.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -Dyylval=pcap_lval -c grammar.c
sed -e 's/.*/char pcap_version[] = "&";/' ./VERSION > version.c
gcc -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I. -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -c version.c
ar rc libpcap.a pcap-linux.o fad-getad.o pcap.o inet.o gencode.o optimize.o nametoaddr.o etherent.o savefile.o bpf_filter.o bpf_image.o bpf_dump.o scanner.o grammar.o version.o
ranlib libpcap.a

Now, run the following:

make install && cp libpcap.so.0.9.4 /usr/local/lib

Next, make sure the dynamic loader sees this new library:

ldconfig -v |grep pcap

the output should look similar to:

libpcap.so.0.9.4 -> libpcap.so.0.9.4

9. Now, we build Snort 2.4.5 using the new libpcap and libpfring.

First, download snort-2.4.5:

cd /usr/src
wget http://www.snort.org/dl/old/snort-2.4.5.tar.gz

Now, unpack the gzip'd tarball:

tar -zxvf snort-2.4.5.tar.gz && cd snort-2.4.5

Next, use ./configure to setup the compile:

./configure --enable-timestats --enable-perfmonitor --enable-linux-smp-stats CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib -lpfring -lpcap"

The output should look similar to:

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether to enable maintainer-specific portions of Makefiles... no
[snip]
config.status: creating m4/Makefile
config.status: creating etc/Makefile
config.status: creating templates/Makefile
config.status: creating src/win32/Makefile
config.status: creating config.h
config.status: executing depfiles commands

Now, run the following:

make && make install

If the above command completes successfully, then the command:

ldd /usr/local/bin/snort

should produce output similar to:

libpfring.so.0.9.4 => /usr/local/lib/libpfring.so.0.9.4 (0x00143000)
libpcap.so.0.9.4 => /usr/local/lib/libpcap.so.0.9.4 (0x00725000)
libpcre.so.0 => /lib/libpcre.so.0 (0x00760000)
libm.so.6 => /lib/tls/libm.so.6 (0x00111000)
libnsl.so.1 => /lib/libnsl.so.1 (0x007f2000)
libc.so.6 => /lib/tls/libc.so.6 (0x00147000)
/lib/ld-linux.so.2 (0x00615000)

where the line containing libpfring and libpcap are of particular importance.

At this point, you have a version of snort that will use the PF_RING ring module in the kernel. Congrats.

Some things of note:
- the ring module has some load options that affect it's behavior. You'll need to tweak these to your environment, however, most folks will want to add the following to the end of /etc/modprobe.conf:

options ring transparent_mode=0 bucket_len=1600

- if you disable transparent_mode (as I have with transparent_mode=0), do not run a program that uses the PF_RING interface (Snort, tcpdump, pfcount, etc.) on the management interface by accident, as you will lose connectivity to the machine.

Enjoy.

Labels: , , ,