Introduction
Snort_inline is a project to make the popular IDS product Snort (http://www.snort.org) interface with a firewall. Instead of sniffing packets using the Berkeley packet filter (bpf), snort_inline reads packets from the firewall. The first creation of snort_inline was done in Linux using IPTables. This document outlines how to get snort_inline working with FreeBSD, using IPFW and divert sockets.
Concept
The concept of how this works is quite simple. I should note that a certain level of understanding is required when it comes to FreeBSD. That is, I'm assuming you understand how ipfw works. You can review some of the concepts for FreeBSD and ipfw at: http://freebsd.rogness.net
First lets look at how a packet flows through ipfw->divert->snort_inline:
As you can see the packet is "handed off" from ipfw to the userland application listening on a specific divert port. In the case of this example, the divert port is 8000. The port is signified by the ipfw divert rule and can be any port. It is important to note that the kernel does not keep track of whether a packet is successfully received by the userland app (snort_inline). So if you have a ipfw divert rule sending packets to port 8000 for example, and nothing is listening on port 8000, the packets are dropped. That means that if snort_inline dies or you add the ipfw rule before you start up snort_inline, packets that match on that ipfw divert rule will be dropped. Keep this in mind when experimenting with snort_inline.
BEFORE YOU START
If you are using bridging, snort_inline will not work with IPFW. This is due interaction of DIVERT sockets and bridging in the kernel. I get this question quite frequently and until the interface gets moved into netgraph I don't forsee this changing in the near future. If you want bridging and snort_inline, you are stuck with Linux. Sorry :-(
Compiling
If you have the FreeBSD Ports Collection installed, then you should simply have to: cd /usr/ports/security/snort_inline && make install. If you don't have the Ports Collection installed you can build things manually. I strongly urge you to use the ports system as I will only be patching that code. However, the following instructions shows you how to download things manually and kinda make things work. The only thing you need to do different on a FreeBSD box (vs Linux) is to supply the --enable-ipfw flag during the configure process. An example compile and install would look something like:
shell> tar -zxvf snort_inline-LATESTVERSION.tar.gz shell> cd snort_inline-LATESTVERSION shell> ./configure --enable-inline --enable-ipfw shell> make shell> make install
Executing
A special command line flag has been added to specify the divert port for snort_inline to listen on. The -J port command line argument should be given on the command line to tell snort_inline to setup, bind, and listen for packets from ipfw on divert 'port'. For example, to tell snort_inline to receive packets from divert port 500, you would do the following steps:
shell# snort_inline -J 500 -c snort_inline.conf
At this point if you try and send packets through snort_inline, nothing will happen. Why? because you have not told the kernel (via ipfw) to send packets to divert port 500. To do this, we first look at our ipfw firewall ruleset:
shell# ipfw list 00100 allow ip from any to any via lo0 00200 deny ip from any to 127.0.0.0/8 00300 deny ip from 127.0.0.0/8 to any 65000 allow ip from any to any 65535 deny ip from any to any
We need to find a spot to insert the ipfw divert rule. Ipfw uses a linear ruleset processing order identifed by the rule numbers. In the above ruleset, rule 100 is processed first, then rule 200, then 300, then 65000, the finally 65535. Notice that rule number 65000 allows 'ip from any to any'. We want to insert BEFORE 65000 or packets will not reach snort_inline. This is due to the fact that once a rule is matched, the packet does not get checked against anything else. So from this ruleset, it appears we should add our ipfw divert rule somewhere between rule 300 and 65500. I will choose an arbitrary rule number of 2000. Let's add the rule:
shell# ipfw add 2000 divert 500 ip from any to any shell# ipfw list 00100 allow ip from any to any via lo0 00200 deny ip from any to 127.0.0.0/8 00300 deny ip from 127.0.0.0/8 to any 02000 divert 500 ip from any to any 65000 allow ip from any to any 65535 deny ip from any to any
At this point, we should see traffic running through snort_inline. Try sending a few packets through to see if your snort_inline rules work. If packets are allowed through, they re-enter the ipfw firewall at rule 65000. If they are dropped or rejected, they will not re-enter the ipfw ruleset.
More more!
With the example above, you have seen how we can direct traffic to snort_inline using ipfw divert. The flexibility of ipfw divert gives you the ability to do several things. One thing you may want to do is to distribute the load across several snort_inline processes. Let's try an example where we send all port 80 traffic to one snort_inline process and all ftp traffic to another snort_inline process. First, lets get two snort_inline processes running:
shell# ipfw list 00100 allow ip from any to any via lo0 00200 deny ip from any to 127.0.0.0/8 00300 deny ip from 127.0.0.0/8 to any 65000 allow ip from any to any 65535 deny ip from any to any shell# snort_inline -J 8100 -D -c snort_inline-web-rules.conf shell# ipfw add 1000 divert 8100 tcp from any to any 80 shell# snort_inline -J 8300 -D -c snort_inline-ftp-rules.conf shell# ipfw add 1500 divert 8300 tcp from any to any 21 shell# ipfw list 00100 allow ip from any to any via lo0 00200 deny ip from any to 127.0.0.0/8 00300 deny ip from 127.0.0.0/8 to any 01000 divert 8100 tcp from any to any 80 01500 divert 8300 tcp from any to any 21 65000 allow ip from any to any 65535 deny ip from any to any
For web traffic, this is what is happening:
1) port 80 traffic is matching on ipfw rule number 1000 and being sent to snort_inline listening on 8100 2) snort_inline analyzes the packet and determines it is OK, so it reinjectes it back into ipfw at rule number 1500 3) ipfw does NOT match on rule number 1500, so it proceeds and matches on the next rule 65000.For ftp traffic, this is what is happening:
1) ipfw does NOT match on rule number 1000, so it proceeds 2) port 21 traffic is matching on ipfw rule number 1500 and being sent to snort_inline listening on 8300 3) snort_inline analyzes the packet and determines it is OK, so it reinjectes it back into ipfw at rule number 65000
With this flexibility you can send different pieces of traffic to different snort_inline processes. By formulating specific ipfw rules, you can do several things:
1) Send all traffic to one snort_inline process 2) Allow snort_inline to ignore certain types of traffic 3) Send specific source/destination IP traffic to different snort_inline processes (e.g. from/to 10.0.0.3) 4) Send specific source/destination Protocol traffic to different snort_inline processes (e.g. web, ftp, dns, etc) 5) Send specific types of protocol specific traffic to different snort_inline processes (e.g. tcp with SYN set, icmp echo, etc) 6) Send all traffic to different snort_inline processes (e.g. experimental rulesets, one snort logging & one not)
For now, I leave these exercises to you. If you have any questions feel free to email the snort_inline list (link below) and I will advise when I can.
Other readings
Snort Inline mailling list: snort-inline-users@lists.sourceforge.net
FreeBSD man pages: ipfw(8), divert(4)
Online resources:
Snort Inline Website,
FreeBSD website,
My FreeBSD website