Monthly Archives: June 2017

Snort IPS With NFQ (nfqueue) Routing on Ubuntu


This guide will show you how to configure Snort to run inline using the NFQUEUE DAQ (referred to as NFQ). This allows your Snort server to use iptables to route traffic between any number of subnets, with Snort evaluating all traffic passing through the system. This guide will assume some knowledge of routing and IP addressing, especially as it is implemented under Linux, as well as some experience setting up Snort. Configuring Snort as an inline NIPS with NFQ is more complicated than setting snort up as a NIDS, and is more complicated than setting up Snort as a NIPS using the AFPACKET DAQ. The complexity is due to the use of iptables and the need to understand IP routing. I will assume that you have installed Snort before, so I may gloss over some concepts and explanations in this guide. If you find yourself having difficult with installing and configuring Snort, I recommend you first work through my complete guide on installing Snort which should give you a good foundation on understanding how to install and configure Snort.

This guide has been written and tested on Ubuntu 16 x64. It should work on most currently supported versions of Ubuntu and Debian derivatives, but your mileage may vary.


On Ubuntu, you can run Snort two different ways in inline mode, with AFPACKET or with NFQ. AFPACKET is simpler to setup (see my guide here), but only lets you bridge sets of paired interfaces. This means you can bridge eth0 with eth1 (pass traffic between them), and also bridge eth2 with eth3, but you can not pass traffic between eth0 and eth4. A benefit of AFPACKET is that you can install an inline Snort machine without any changes to IP addressing, routing, or networking changes. NFQ on the other hand lets you leverage the power of iptables to make routing decisions. It’s more complicated to setup (it requires you as a systems administrator to understand Linux routing), but is more powerful for network security. You can also use iptables to blottck traffic before Snort even sees it (to filter out noisy traffic sources), or use additional tools that interface with iptables to increase security (like psad or fwsnort).

IPv6 Notes

Enabling NFQ with IPv6 is very similar to IPv4, the only thing to note is that you will have to run a separate instance of Snort to process IPv6 packets, and setup another iptables rule to forward IPv6 packets to a second NFQUEUE using the ip6tables command. Essentially you follow this guide twice, once for IPv4, and once for IPv6, making changes as necessary.


This guide will go through the following steps:

  1. Network Configuration
  2. Route Configuration
  3. Enable Kernel IP forwarding on the System
  4. Software Installation
  5. Software configuration
  6. Enabling NFQUEUE
  7. Enabling additional firewall rules

One thing to note: for simplicity, we will enable routing for all traffic passing through the system before we lock down the firewall rules. Because of this, I highly recommend that you test this on a development network to understand how it works before you implement on a production network. In a real environment, you would want your firewall rules in place before you enabled routing of traffic on a device that bridged networks with different security levels. For this guide, it’s much simpler to apply firewall rules at the end for the purpose of learning. I have noted a number of excellent online resources and books at the end of this guide to help understand networking, iptables, and network security.

Network Configuration

For this guide, we will use the following network, with our Snort router having three interfaces on three different networks:

The network is our internal admin or management network, and will not be routed by Snort. No traffic should pass between the network and the other networks. This is how we will SSH into this snort system for management purposes, and if configured: Snort could send log data back to a logging server on this network. It is good practice to keep a separate network (through VLANS or separate hardware) for management purposes. We won’t lock down this network at first, but will apply firewall rules at the end of this article to prevent traffic from entering this network across our Snort router.

The network is our outbound network. All traffic not destined for a locally connected network will be directed out this network to the next hop gateway (in this case the ISP router). Note that the computer on this network has the Snort router as it’s default gateway. While this works, normally you’d configure the computer to have the actual external gateway ( in this case) as the gateway. It’s set this way to simplify testing.

The network is our client network (screened subnet). Hosts will be configured on this network to use the Snort server as it’s gateway. Traffic passing between this network and the network will be scanned by Snort, and will be dropped and/or logged if suspicious.

Because this server is running Ubuntu 16, the interface names no longer follow the ethX standard (eth0, eth1, …). Instead, interfaces names are assigned as Predictable Network Interface Names. This means you need to check the names of your interfaces using ifconfig -a. In my case, what was originally eth0 is now ens160.

Since you are using your Snort system as a router, you’ll want static interfaces on each address. You don’t want dynamic (DCHP) addresses for the interfaces on this system because any clients on that subnet will use the IP address of the Snort system as their gateway. For any interface that Snort will process traffic on, you need to disable LRO and GRO (there’s an explanation of this in my complete guide on installing Snort). For any interface that sends traffic to an external network, you’ll need a gateway configured.

Edit /etc/network/interfaces as an admin:

sudo vi /etc/network/interfaces

Configure your network interfaces:

# Routed subnet
auto ens160
iface ens160 inet static
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off

# Host Subnet (internal network / screened subnet)
auto ens224
iface ens224 inet static
# no gateway required
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off

# Management Subnet (internal network, not routed)
auto ens192
iface ens192 inet static
# no gateway
# snort not processing traffic on this interface, so no need to disable LRO or GRO

Setting up Routes

Because we don’t have additional subnets connected to the 192.1668.0.0 or networks, we don’t need to specify any routes. We only configured one gateway on this system (, so all traffic not destined to a local subnet will be sent to that gateway. To verify, use the ip route command:

noah@snort-router:~ $ ip route
default via dev ens160 onlink dev ens160  proto kernel  scope link  src dev ens192  proto kernel  scope link  src dev ens224  proto kernel  scope link  src 

If you have multiple subnets configured off of one of those other two subnets, you would need to add a gateway for that subnet in your interfaces file, and you would need to add a static route.

Sidebar: A more complicated example (don’t follow these steps unless you have more complicated routing needs): if you had the subnet connected to your subnet, reachable through, as shown highlighted in red in the image below:

you would need to add two commands to the network interface for ens224: One to specify a gateway, and one to add this route. Since you now have multiple gateways on your Snort server, you want to tell the system which gateway is the default gateway (the gateway for all traffic that it does not have an explicit route for). Interface ens224 on the snort server would now look like:

# Host Subnet (internal network / screened subnet)
# Additional routing example
auto ens224
iface ens224 inet static
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off
up ip route add via || true

While ens160 with the default route will look like:

# Routed subnet
# Additional routing example
auto ens160
iface ens160 inet static
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off
up ip route add default via ens160 || yes

At this time, you should verify that you can ping hosts in every network from your Snort server, as well as external hosts via your gateway. Use the ip route commmand as well as tracepath to verify this is setup right. Do not continue until all this is working.


At this point, even though the system can reach all connected networks, the system will not pass traffic between the networks. Traffic from a host on the management network can not reach any hosts on the screened network, and vice versa. This is by design. Enabling this is easy, however you may want to restrict traffic between certain networks before enabling this option, for security purposes. For simplicity, we will not enable firewall rules at this time, so if your system is a live system connected to both internal sensitive networks and public networks, you may want to read through the entire article first to understand how to add firewall rules to protect the system before enabling routing.

Enable Kernel IP forwarding

Use vi to edit /etc/sysctl.conf

Un-comment the line:


Reboot the system. At this time, you should now be able to ping between two devices connected to different networks. If your routing is setup correctly, you should be able to ping out the default route as well. If pinging between locally connected subnets work, but you can’t ping to external subnets, you probably need to look at the routing further down the line, do those devices have a path back to your locally connected subnets? If you have a large network, you may want to look at enabling a routing protocol on your Snort server to exchange routing information (probably OSPF or RIP, depending on you network configuration and level of comfort).

Installing DAQ and Snort With NFQ support

If you have a Snort system up and running, you will probably need to re-compile DAQ and Snort for this guide. The reason is that to enable NFQ, you need to install libraries prior to compiling DAQ.

Start by installing all pre-requisites for Snort:

sudo apt-get install -y build-essential libpcap-dev libpcre3-dev libdumbnet-dev bison flex zlib1g-dev liblzma-dev openssl libssl-dev

We need the development libraries for Nghttp2. On Ubuntu 16 this is simple:

# Ubuntu 16 only (not Ubuntu 14)
sudo apt-get install -y libnghttp2-dev

On Ubuntu 14, we do this from scratch:

# Ubuntu 14 only (not Ubuntu 16)
sudo apt-get install -y autoconf libtool pkg-config
cd ~/snort_src
tar -xzvf nghttp2-1.17.0.tar.gz
cd nghttp2-1.17.0
autoreconf -i --force
./configure --enable-lib-only
sudo make install

Finally we need the NFQ specific libraries:

sudo apt-get install libnetfilter-queue-dev

We will make a directory for all the source tarball files:

mkdir ~/snort_src
cd ~/snort_src

Download and install Data Acquisition library (DAQ) from the Snort website:

cd ~/snort_src
tar -xvzf daq-2.0.6.tar.gz
cd daq-2.0.6
sudo make install

after the ./configure option, you will see the DAQ modules that are enabled. You must have NFQ enabled here, as seen below:

Build AFPacket DAQ module.. : yes
Build Dump DAQ module...... : yes
Build IPFW DAQ module...... : yes
Build IPQ DAQ module....... : no
Build NFQ DAQ module....... : yes  <<<<< MUST BE YES
Build PCAP DAQ module...... : yes
Build netmap DAQ module.... : no

Now we are ready to install Snort from source:

cd ~/snort_src
tar -xvzf snort-
cd snort-
./configure --enable-sourcefire
sudo make install

Run the following command to update shared libraries:

sudo ldconfig

Since the Snort installation places the Snort binary at /usr/local/bin/snort, it is common to create a symlink to /usr/sbin/snort:

sudo ln -s /usr/local/bin/snort /usr/sbin/snort

The last step of our Snort installation is to test that the Snort binary runs. Execute Snort with the -V flag, which causes Snort to print the current version. You should see output similar to the following:

noah@snort-router:~ $ snort -V

   ,,_     -*> Snort! <*-
  o"  )~   Version GRE (Build 56) 
   ''''    By Martin Roesch & The Snort Team:
           Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using libpcap version 1.7.4
           Using PCRE version: 8.38 2015-11-23
           Using ZLIB version: 1.2.8

noah@snort-router:~ $

Now we want to verify that Snort can see the NFQ DAQ. We do this by running snort with the –daq-list flag:

noah@snort-router:~ $ snort --daq-list
Available DAQ modules:
pcap(v3): readback live multi unpriv
nfq(v7): live inline multi	     <<<<<<  Need this line
ipfw(v3): live inline multi unpriv
dump(v3): readback live inline multi unpriv
afpacket(v5): live inline multi unpriv

At this point, Snort has been compiled and installed with the NFQ DAQ. Next is configuration.

Configuring Snort

Most of this configuration comes from my longer articles on installing Snort. I’ll skip over some of the details of certain steps, but more in-depth information is available in my full Snort install guide. First off, for security reasons we want Snort to run as an unprivileged user. We create a snort user and group for this purpose:

sudo groupadd snort
sudo useradd snort -r -s /sbin/nologin -c SNORT_IDS -g snort

Next, we need to create a number of files and folders that Snort expects when running in NIDS mode.  We will then change the ownership of those files to our new snort user:

# Create the Snort directories:
sudo mkdir /etc/snort
sudo mkdir /etc/snort/rules
sudo mkdir /etc/snort/rules/iplists
sudo mkdir /etc/snort/preproc_rules
sudo mkdir /usr/local/lib/snort_dynamicrules
sudo mkdir /etc/snort/so_rules

# Create some files that stores rules and ip lists
sudo touch /etc/snort/rules/iplists/black_list.rules
sudo touch /etc/snort/rules/iplists/white_list.rules
sudo touch /etc/snort/rules/local.rules
sudo touch /etc/snort/

# Create our logging directories:
sudo mkdir /var/log/snort
sudo mkdir /var/log/snort/archived_logs

# Adjust permissions:
sudo chmod -R 5775 /etc/snort
sudo chmod -R 5775 /var/log/snort
sudo chmod -R 5775 /var/log/snort/archived_logs
sudo chmod -R 5775 /etc/snort/so_rules
sudo chmod -R 5775 /usr/local/lib/snort_dynamicrules

# Change Ownership on folders:
sudo chown -R snort:snort /etc/snort
sudo chown -R snort:snort /var/log/snort
sudo chown -R snort:snort /usr/local/lib/snort_dynamicrules

We now need to move the following files from the extracted Snort tarball to the snort configuration folder:

cd ~/snort_src/snort-
sudo cp *.conf* /etc/snort
sudo cp *.map /etc/snort
sudo cp *.dtd /etc/snort

cd ~/snort_src/snort-
sudo cp * /usr/local/lib/snort_dynamicpreprocessor/

Disable all rule files:

sudo sed -i 's/include \$RULE\_PATH/#include \$RULE\_PATH/' /etc/snort/snort.conf

Now We need to edit our snort configuration file:

sudo vi /etc/snort/snort.conf

Set your internal networks here. I am choosing to have Snort consider the and networks as internal networks. This maters for rule processing, where many rules look for attacks against computers in your HOME_NET subnet ranges:

# line 45 (no spaces between IP addresses)
ipvar HOME_NET [,]

Next we need to tell Snort about the locations of all the folders we created earlier. I have included the line numbers after the hash so you can more easily find the setting (do not write the line number, just change the path to match what is below):

var RULE_PATH /etc/snort/rules						# line 104
var SO_RULE_PATH /etc/snort/so_rules				# line 105
var PREPROC_RULE_PATH /etc/snort/preproc_rules		# line 106

var WHITE_LIST_PATH /etc/snort/rules/iplists		# line 113
var BLACK_LIST_PATH /etc/snort/rules/iplists		# line 114

Enable the Local rules file. Un-comment the following line (line 545) by deleting the hash from the beginning of the line:

include $RULE_PATH/local.rules

Now we need to configure Snort to use the NFQ DAQ an run inline mode. You can choose to pass these options from the command line, but it’s neater to do it here in the configuration file. Add the following three lines to enable the NFQ DAQ in inline mode, looking at NFQ queue number 4 for packets (we will configure iptables later to pass all routed packets to this same numbered queue):

# at line 168, add the following new lines:
config daq: nfq
config daq_mode: inline
config daq_var: queue=4

Save the file, and now we need to test the configuration. To test: we run the following command. The T flag indicates we want to test, the c flag specifies the snort.conf file, and the Q flag tells snort that we’re working in inline mode (required later for Drop rules to actually drop, instead of just alerting):

$ sudo snort -T -c /etc/snort/snort.conf -Q

You should have similar output to the following (truncated for clarity):

Enabling inline operation
Running in Test mode


nfq DAQ configured to inline.
Decoding Raw IP4

        --== Initialization Complete ==--

   ,,_     -*> Snort! <*-
  o"  )~   Version GRE (Build 56) 
   ''''    By Martin Roesch & The Snort Team:
           Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using libpcap version 1.7.4
           Using PCRE version: 8.38 2015-11-23
           Using ZLIB version: 1.2.8

           Rules Engine: SF_SNORT_DETECTION_ENGINE  Version 3.0  <Build 1>
           Preprocessor Object: SF_SIP  Version 1.1  <Build 1>
           Preprocessor Object: SF_SSLPP  Version 1.1  <Build 4>
           Preprocessor Object: SF_SDF  Version 1.1  <Build 1>
           Preprocessor Object: SF_SMTP  Version 1.1  <Build 9>
           Preprocessor Object: SF_GTP  Version 1.1  <Build 1>
           Preprocessor Object: SF_DNP3  Version 1.1  <Build 1>
           Preprocessor Object: SF_DCERPC2  Version 1.0  <Build 3>
           Preprocessor Object: SF_DNS  Version 1.1  <Build 4>
           Preprocessor Object: SF_POP  Version 1.0  <Build 1>
           Preprocessor Object: SF_IMAP  Version 1.0  <Build 1>
           Preprocessor Object: SF_MODBUS  Version 1.1  <Build 1>
           Preprocessor Object: SF_FTPTELNET  Version 1.2  <Build 13>
           Preprocessor Object: SF_SSH  Version 1.1  <Build 3>
           Preprocessor Object: SF_REPUTATION  Version 1.1  <Build 1>

Snort successfully validated the configuration!
Snort exiting

If you are seeing similar output, then you’re in a good place. If not, troubleshoot the errors (usually Snort will output line number where the error was found) before continuing.

Testing a Rule

We will add a simple rule to detect ICMP traffic to verify that we’re detecting and passing traffic correctly. Add the following to our local.rules file:

alert icmp any any -> $HOME_NET any (msg:"ICMP test detected"; GID:1; sid:10000001; rev:001; classtype:icmp-event;)

Since we have made changes to the file that snort loads (local.rules), it is a good idea to test the configuration file again:

sudo snort -T -c /etc/snort/snort.conf -Q

If successful, you should be able to scroll up through the output and see that Snort has loaded our one rule:

		Initializing rule chains...
		1 Snort rules read
			1 detection rules
			0 decoder rules
			0 preprocessor rules
		1 Option Chains linked into 1 Chain Headers
		0 Dynamic rules

		+-------------------[Rule Port Counts]---------------------------------------
		|             tcp     udp    icmp      ip
		|     src       0       0       0       0
		|     dst       0       0       0       0
		|     any       0       0       1       0
		|      nc       0       0       1       0
		|     s+d       0       0       0       0

Configuring NFQUEUE

I will assume you don’t have any entries in your iptables at this time. You can verify with the following command:

$ sudo iptables -L

which should show the following output:

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

We want to add a rule to our iptables that makes every packet that is going to be forwarded (not destined for local delivery) to be scanned by Snort. To do this, we add a single rule that moves all forwarded traffic (from the FORWARD queue) to NFQUEUE queue number 4, which is the same queue we specified in our snort.conf configuration above, where Snort is listening for it (I chose queue 4 arbitrarily, you could use any number, as long as it matched the snort.conf queue). To add this rule to the FORWARD chain, we run the following:

sudo iptables -I FORWARD -j NFQUEUE --queue-num=4

Since Snort is not running right now, the system will deliver traffic to that queue, but it will not be processed and will drop (we can change this later to a fail-open option if wanted). Let’s run Snort with the following flags to see traffic being processed:

-A console                    the console option prints fast mode alerts to stdout
-q                            Quiet. Don't show banner and status report.
-u snort                      run snort as the following user after startup
-g snort                      run snort as the following group after startup
-c /etc/snort/snort.conf      the path to our snort.conf file
-Q                            Run inline mode (required to drop packets)

Run Snort with the command below:

sudo /usr/local/bin/snort -A console -q -u snort -g snort -c /etc/snort/snort.conf -Q

If you ping from one machine to another machine, you should see alerts show on your snort machine. You can’t ping to or from the snort machine directly, because this rule only looks at traffic not destined for the local machine.

06/07-14:38:16.275283  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} ->
06/07-14:38:17.274174  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} ->
06/07-14:38:18.273215  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} ->

You have to use ctrl-c to stop snort from running after the above output.

To test that snort will Drop rather than just alert on your rules, edit your local.rules and replace the first word “alert” with “drop”. Re-run Snort as above and try pinging again. You should see alerts, and your host’s should not be able to ping. Note that all other traffic is permitted, our rule is only blocking ICMP traffic. You should see alerts specifying the Drop action:

06/07-14:42:23.935433  [Drop] [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} ->
06/07-14:42:24.942280  [Drop] [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} ->

If you were to reboot your computer, you would loose all your iptables configurations. To make them persistent, we need to export the current configuration to a text file, and tell the system to load that text file each time the network starts up. To Export the current settings, we use the iptables-save tool as follows:

sudo sh -c "iptables-save > /etc/network/iptables.rules"

To load the settings, we use the iptables-restore tool in our network interfaces file (/etc/network/interfaces) for our primary network interface, so now the configuration for ens160 looks like this:

auto ens160
iface ens160 inet static
pre-up iptables-restore < /etc/network/iptables.rules
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off

Reboot and test. You’ll need to manually re-start Snort for any traffic to be passed at this time. Remember that iptables is placing all routed traffic in NFQUEUE number 4, but if there is no application listening to that queue, traffic will not pass.

Fail open

The following is optional based on your security needs. If you want traffic to pass between networks when Snort is not running (fail-open mode), if Snort crashes or stops running for some reason, you’ll want to enable the –queue-bypass flag for the iptables rule. We will need to modify the current rule. First let’s check the state of all chains:

$ sudo iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         
1    NFQUEUE    all  --  anywhere             anywhere             NFQUEUE num 4

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination  

we want to modify rule number 1 in the FORWARD chain. To do this, we run the following command, which Replaces (the -R flag) this rule, adding the queue-bypass option:

sudo iptables -R FORWARD 1 -j NFQUEUE --queue-num=4 --queue-bypass

you’ll want to re-save the running configuration as above (overwrite the text file), reboot, and test.

Additional Firewall Rules

Rather than try to fit a small guide for iptables on production systems into this guide, I will refer you to a couple of other excellent short guides on how to use iptables to secure your system. You should at a minimum block all access to the system (the INPUT queue) that isn’t management related or from the management network. Configuring Drop by default rather than accept is recommended.
Basic iptables lock-downs for a system.
More in-depth iptables lock-downs for a server.

At a minimum, to block traffic from going to the management network, you would want to insert the following rule:

sudo iptables -I FORWARD -o ens192 -j DROP

this blocks all routed traffic out the management interface. You have to insert this above the NFQUEUE rule, because once Snort permits the traffic, it does not go through other chains.

Final Steps

You will probably also want to setup PulledPork to download rules automatically, install barnyard2 for log processing, and BASE to view alerts in a GUI. All these steps can be found in my guide on snort, here: Installing Snort

Where to go From here

If you found yourself struggling to get routing working even before you got to the Snort portion of this guide, I highly recommend that you look into taking a networking course. I personally took the Cisco CCNA course offered by the local community college (I think it was only $45 USD a unit). I learned an amazing amount about how networks work, and it made me a much better systems administrator. Don’t think that because you’re a computer administrator that Cisco training isn’t for you, it will compliment your knowledge and abilities.

The CCNA Books offered by Cisco are excellent:
The CCNA official Cert Guide is what I used to pass my CCNA years ago, and are very well written textbooks. While it’s Cisco specific, most of the basic knowledge in these books are general enough to be useful to anyone working with networking.
The CCNA Routing and Switching Portable Command Guide is one of the few hard-copy books I keep on my shelf and refer to whenever i’m working on Cisco eqipment.

I found the O’reily Network Warior book to be quite satisfying, it gave a broad overview of a number of technologies related to network technology to really broaden my knowledge.

If you’re really serious about TCP/IP, the gold standard (and really heavy) book to have is the The TCP/IP Guide: A Comprehensive, Illustrated Internet Protocols Reference.

How to Enable IP Forwarding in Linux
Understanding the ip route command and IP routing in Linux.

Massive iptables overview. If you want to know how it works at the architecture level (not the user level, and not the programming level), this is the guide you want to read. I found Chapters three through nine to be most helpful, but if you need some more info on TCP/IP, the first few chapters are quite good as well.

The second flowchart at this page is awesome for understanding how packets flow through iptables. Use it in conjunction with the iptables tutorial above.

the NFQUEUE options for iptables.

This article:
A Deep Dive into Iptables and Netfilter Architecture, may get a little deep into the internal workings of the kernel IP stack, but I found it fascinating. Compliments this SANS article as well.

A few relevant books that get good reviews, but which i haven’t read yet:
Linux iptables Pocket Reference
Linux Firewalls: Attack Detection and Response with iptables, psad, and fwsnort
How Linux Works: What Every Superuser Should Know


I hope this article has been helpful to you. Please feel free to provide feedback, both issues you experienced and recommendations that you have. The goal of this guide was not just for you to create a Snort NIPS with NFQ, but to understand how all the parts work together, and get a deeper understanding of all the components, so that you can troubleshoot and modify your Snort NIDS with confidence.