NetFlow: installation and configuration of NFDUMP and NfSen on Debian

After the brief overview about the installation of flow-tools and FlowViewer, in this post I’d like to share my experience about the setup of a basic solution based on another pair of tools: NFDUMP and NfSen. As always on my posts, the starting point is a fresh Debian 5.0 setup.

Components used in the solution

As it was for the previous solution, two main components are involved, one to acquire NetFlow data, the other to analyze them in an easy way.

NFDUMP is a suite of tools composed by many programs; nfcapd is one of them and it is the daemon which listens for incoming NetFlow data. It is launched by NfSen.

NfSen is the web based front end we use to analyze NFDUMP NetFlow data. It is splitted up in two parts: a Perl program which runs in the background and launches nfcapd, and some PHP web pages.

The biggest difference between a NFDUMP/NfSen based solution and a flow-tools/FlowViewer solution is that the former does support NetFlow v9, while the latter may result in a simpler and easier tool to work with. Anyway, there is not one perfect solution for all the needs!

In this post I’ll use NFDUMP 1.6.1 and NfSen 1.3.2, the latest stable releases I can find on SourceForge while I’m writing this blog entry; you can find their web pages in the References section of this post. I’ll build them from source code.

NFDUMP

Let’s add some tools we’ll need to build the package:

apt-get install gcc flex librrd-dev make

We are ready to start! The steps are very simple: download source code and extract the tar:

cd /usr/local/src/
wget http://sourceforge.net/projects/nfdump/files/stable/nfdump-1.6.1/nfdump-1.6.1.tar.gz/download
gzip -dc nfdump-1.6.1.tar.gz | tar -xf -
cd nfdump-1.6.1

In order to use NFDUMP with NfSen we need to enable nfprofile in the configure step; then we have to make && make install it:

./configure --enable-nfprofile 
make
make install

Now it is ready to be ran.

If we want to use it without NfSen we can stop here and create a simple init.d script to launch NFDUMP at startup; it will acquire NetFlow data and we can use command line tools (such as nfdump) to analyze them. You can find a basic init.d script at the end of this post (“Annex A: stand-alone NFDUMP”).

But that’s not our goal, we want NfSen!

NfSen

As said, NfSen has a PHP front-end so it needs a web server with PHP support in order to be used. It also uses RRD to store data and paint nice graphs and some Perl modules to produce alerts: let’s satisfy its prerequisites:

apt-get install apache2 libapache2-mod-php5 php5-common libmailtools-perl rrdtool librrds-perl

Now it’s time to download and configure the program:

cd /usr/local/src/
wget http://sourceforge.net/projects/nfsen/files/stable/nfsen-1.3.2/nfsen-1.3.2.tar.gz/download
gzip -dc nfsen-1.3.2.tar.gz | tar -xf -
cd nfsen-1.3.2

Once extracted, we have to copy the config file into /etc and modify it:

cp etc/nfsen-dist.conf /etc/nfsen.conf
nano /etc/nfsen.conf

As you can see from the support web page it presents a lot of parameters, but many of them may be left unchanged; in this example I chose to leave as many parameters as possible at their default settings, so we have all the files in a single directory: /data/nfsen. Here the parameters I changed:

$USER    = "www-data";
$WWWUSER  = "www-data";
$WWWGROUP = "www-data";

%sources = (
    'MYROUTER'    => { 'port'    => '9995', 'col' => '#0000ff', 'type' => 'netflow' },
);

$MAIL_FROM   = 'MYEMAIL@MYDOMAIN.COM';
$SMTP_SERVER = 'MY.SMTPSERVER.COM';

For a better setup I suggest you to read the configuration guide on the project’s web site.

Let’s make the destination directory and complete the installation by running the install.pl script:

mkdir -p /data/nfsen
./install.pl /etc/nfsen.conf

We are ready to run the nfsen program and let it executes nfcapd daemon:

cd /data/nfsen/bin
./nfsen start

We can also schedule it to be ran at startup:

ln -s /data/nfsen/bin/nfsen /etc/init.d/nfsen
update-rc.d nfsen defaults 20

It’s ready: point your browser at http://YOUR_IP_ADDRESS/nfsen/nfsen.php and enjoy! Of course, this is just a very basic setup, both NFDUMP and NfSen have many options you can set and use to have better performances and to improve stability and scalability. If you want to go deep into their configuration I suggest you to use the links you can find at the end of this post.

P.S.: Don’t forget to configure your routers to export NetFlow data! ;-)

Annex A: stand-alone NFDUMP

As said, NFDUMP may be used in a stand-alone way, without the help of NfSen.

We just have to build a destination directory for NetFlow data…

mkdir -p /var/flows/MYROUTER
mkdir -p /var/flows/MYSECONDROUTER

and add an init.d script like the following one:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          nfcapd
# Required-Start:    $network
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: netflow capture daemon
# Description:       nfcapd is the netflow capture daemon of the nfdump tools.
### END INIT INFO

# Author: Erik Wenzel <erik@debian.org>
# Edited by Pierky for the blog post "NetFlow: installation and configuration of 
# NFDUMP and NfSen on Debian" on http://pierky.wordpress.com

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="netflow capture daemon"
NAME=nfcapd
DAEMON=/usr/local/bin/$NAME
DATA_BASE_DIR="/var/flows"
PIDFILE=/var/run/$NAME.pid
DAEMON_ARGS="-D -w -S 1 -P $PIDFILE -n MYROUTER,192.168.0.1,$DATA_BASE_DIR/MYROUTER"
SCRIPTNAME=/etc/init.d/nfdump

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                || return 1
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
                $DAEMON_ARGS \
                || return 2
        # Add code here, if necessary, that waits for the process to be ready
        # to handle requests from services started subsequently which depend
        # on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
        #
        # Disabled second call, because is kills nfsen controlled nfcapd
        #start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
        #[ "$?" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
        #
        # If the daemon can reload its configuration without
        # restarting (for example, when it is sent a SIGHUP),
        # then implement that here.
        #
        start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
        return 0
}

case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ q"$VERBOSE" != qno ] && log_end_msg 0 ;;
                2)   [ p"$VERBOSE" != pno ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "r$VERBOSE" != rno ] && log_end_msg 0 ;;
                2) [ "s$VERBOSE" != sno ] && log_end_msg 1 ;;
        esac
        ;;
  #reload|force-reload)
        #
        # If do_reload() is not implemented then leave this commented out
        # and leave 'force-reload' as an alias for 'restart'.
        #
        #log_daemon_msg "Reloading $DESC" "$NAME"
        #do_reload
        #log_end_msg $?
        #;;
  restart|force-reload)
        #
        # If the "reload" option is implemented then remove the
        # 'force-reload' alias
        #
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0|1) log_end_msg 0 ;;
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac
exit 0

I edited this script starting from the one included in the Debian’s package.

Please note the DATA_BASE_DIR and the DAEMON_ARGS variables. The first contains the base destination directory for our NetFlow data. The second represents arguments we want to use when launching nfcapd; you can add as many -n options as you want, one for each NetFlow source you want to collect data from. Remember: man nfcapd is your friend! ;)

Finally:

chmod a+x /etc/init.d/nfdump
update-rc.d nfdump defaults 20
/etc/init.d/nfdump start

References

Cisco.com: NetFlow version 9

NFDUMP: http://nfdump.sourceforge.net/

NfSen: http://nfsen.sourceforge.net/

NetFlow: weird TCP flags in FlowViewer and flow-print?

Working with FlowViewer and flow-print (from the flow-tools suite), if you filter some NetFlow data by TCP flags you may notice a weird behaviour, like the one in the following picture:

FlowViewer - TCP Flags

Here I applied a filter on TCP Flags = 27, but on the output I had the “Fl” (Flag) column reporting 3! What’s up? Is 3 a kind of alias for 27? Is this a math puzzle? None of this!

First off, why did I filter for 27?

Well, NetFlow records contain a field reporting the cumulative OR-ed TCP flags seen on the flow. For example, in a regular TCP connection a client would send a SYN, then an ACK, then other optional PSHs and ACKs and finally a FIN. The table on the left reports a summary of TCP flags with their binary and decimal values.

If we “sum” (using the OR binary operator) all the flags used in a TCP connection (SYN [2] + ACK [16] + PSH [8] + FIN [1]) we have 27.

For example, if we try to connect to a port where no daemon is listening on, our client would send a SYN packet while the server would reply with a RST/ACK packet: in this scenario we would have 2 flows:

1) client-to-server: TCP Flags = 2 (just SYN [2]);
2) server-to-client: TCP Flags = 20 (RST [4] + ACK [16])

Now we know how TCP Flags work in NetFlow, but why 27 = 3???

Let’s start debugging FlowViewer using the DEBUG_VIEWER file into the Flow_Working directory; it tells us what FlowViewer executes in the background:

NETFLOWBOX:/usr/lib/cgi-bin/FlowViewer_3.3.1# cat Flow_Working/DEBUG_VIEWER

The output tells us the “hidden” command it uses, and we try to run it:

NETFLOWBOX:/usr/lib/cgi-bin/FlowViewer_3.3.1# /usr/bin/flow-cat -t "03/08/2010 08:44:59" -T "03/08/2010 16:50:01" /var/flows/MYROUTER/2010/2010-03/2010-03-08 | /usr/bin/flow-nfilter -f /usr/lib/cgi-bin/FlowViewer_3.3.1/Flow_Working/FlowViewer_filter_165206 -FFlow_Filter | /usr/bin/flow-print -f5
Start End Sif SrcIPaddress SrcP DIf DstIPaddress DstP P Fl Pkts Octets

0308.10:34:43.799 0308.10:34:44.995 66 192.168.0.154 1888 59 192.168.1.184 1433 6 3 15 2032
0308.11:00:31.339 0308.11:00:32.419 66 192.168.0.154 2472 59 192.168.1.184 1433 6 3 15 2034
0308.11:03:15.899 0308.11:03:16.715 66 192.168.0.154 2533 59 192.168.1.184 1433 6 3 15 2034
0308.11:18:00.811 0308.11:18:01.719 66 192.168.0.154 2877 59 192.168.1.184 1433 6 3 15 2042
0308.11:22:52.407 0308.11:22:53.483 66 192.168.0.154 3004 59 192.168.1.184 1433 6 3 15 2034
0308.11:26:47.751 0308.11:27:28.367 66 192.168.0.154 3107 59 192.168.1.184 1433 6 3 1420 1963241
0308.11:36:46.107 0308.11:36:47.195 66 192.168.0.154 3287 59 192.168.1.184 1433 6 3 15 2034
0308.11:41:42.115 0308.11:41:43.815 66 192.168.0.154 3297 59 192.168.1.184 1433 6 3 15 2034

Here it is, the antepenultimate column is the “Fl” (Flag), and it is = 3!

So, let’s go deep in the command; it uses flow-cat to merge some NetFlow data files, then it passes the output to the flow-nfilter and, finally, it runs flow-print with the -f5 argument.
The f option just tells flow-print how to format data… Well, let’s try to use another format (man flow-print is our friend here!): let’s switch to -f1!

NETFLOWBOX:/usr/lib/cgi-bin/FlowViewer_3.3.1# /usr/bin/flow-cat -t "03/08/2010 08:44:59" -T "03/08/2010 16:50:01" /var/flows/MYROUTER/2010/2010-03/2010-03-08 | /usr/bin/flow-nfilter -f /usr/lib/cgi-bin/FlowViewer_3.3.1/Flow_Working/FlowViewer_filter_165206 -FFlow_Filter | /usr/bin/flow-print -f1
Sif SrcIPaddress DIf DstIPaddress Pr SrcP DstP Pkts Octets
StartTime EndTime Active B/Pk Ts Fl

0042 192.168.0.154 003b 192.168.1.184 06 760 599 15 2032
0308.10:34:43.799 0308.10:34:44.995 1.196 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 9a8 599 15 2034
0308.11:00:31.339 0308.11:00:32.419 1.080 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 9e5 599 15 2034
0308.11:03:15.899 0308.11:03:16.715 0.816 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 b3d 599 15 2042
0308.11:18:00.811 0308.11:18:01.719 0.908 136 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 bbc 599 15 2034
0308.11:22:52.407 0308.11:22:53.483 1.076 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 c23 599 1420 1963241
0308.11:26:47.751 0308.11:27:28.367 40.616 1382 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 cd7 599 15 2034
0308.11:36:46.107 0308.11:36:47.195 1.088 135 00 1b

0042 192.168.0.154 003b 192.168.1.184 06 ce1 599 15 2034
0308.11:41:42.115 0308.11:41:43.815 1.700 135 00 1b

This format is not on a single line, but two lines are used for each NetFlow record; the TCP Flags values are in the second line of each record, in the last field, and they are in hex, and … well, they are set on “1b” = 27 (decimal)! So, it’s “just” an output issue. If we check the source code, we can understand why we see 3 instead of 27:

from flow-print.c:

int format5(struct ftio *ftio, int options)
{

[...]

  while ((rec = ftio_read(ftio))) {

    [...]

    cur.tcp_flags = ((u_int8*)(rec+fo.tcp_flags));

    [...]

    printf("%-5u %-15.15s %-5u %-5u %-15.15s %-5u %-3u %-2d %-10lu %-10lu\n",

           (u_int)*cur.input, fmt_buf1, (u_int)*cur.srcport,
           (u_int)*cur.output, fmt_buf2, (u_int)*cur.dstport,
           (u_int)*cur.prot,
           (u_int)*cur.tcp_flags & 0x7,
           (u_long)*cur.dPkts,
           (u_long)*cur.dOctets);

This is the format5 routine used to print NetFlow data when the -f5 option is given; please note line 19. It takes the full TCP Flags value, applies a 0×7 mask with the AND binary operator and finally it prints the result. The 0×7 value in binary is 0000 0111; it means that, whatever original flag is, the routine always uses the last 3 bit only. Here it is the “27 = 3″ puzzle’s solution: 27 (decimal) = 0001 1011, so it only considers last 3 bits, 011 = 3 (decimal).

Back to our previous examples…

A client connecting to a “closed” port will produce a flow record with just a SYN: binary 0000 0010, decimal 2, no problems here, 3 bits are enough for a full representation even in flow-print.

The server receiving a connection request for a closed port will produce a flow with a RST + ACK: binary 0000 0100 + 0001 0000 = 0001 0100, decimal 20; here we’ll have just the 3-bits view of the number, so flow-print will report decimal 4 (binary 100).

Of course, this output format issue may make troubleshooting and analysis quite difficult; every suspect behaviour has to be analyzed through the command line with the -f1 option of flow-print.

NetFlow: how to install and configure flow-tools and FlowViewer on a fresh Debian setup

NetFlow is a very useful tool/protocol to monitor network traffic’s patterns. Many tools have been developed to collect and analyze NetFlow data, here I chose flow-tools and FlowViewer packages, and I would like to show how to get them work on a fresh Debian 5.0 (Lenny) setup.

Components used in the solution

There are two main packages involved in the solution: flow-tools, used to acquire and collect NetFlow data generated by our routers, and FlowViewer, used to process, view and paint them on nice graphs.

The flow-tools package is built up by a lot of components, many of them are transparently used by FlowViewer; here I’ll focus on the flow-capture program, the one which acquires and collect the data.

The FlowViewer package is splitted up on 3 CGIs: FlowViewer, FlowGrapher and FlowTracker. They can be used through a web-server and they let us analyze data collected by flow-capture. The package contains 2 programs also, FlowTracker_Collector and FlowTracker_Grapher, which run periodically and build MRTG-like graphs, storing data in RRD databases.

Acquiring data

First off, we need to acquire NetFlow data generated by our routers; flow-tools is the package we need:

apt-get install flow-tools

Let’s edit the main configuration file, flow-capture.conf, where we tell flow-capture what we want to acquire and how we want to store it:

cd /etc/flow-tools
nano flow-capture.conf

Its configuration file is pretty simple; it’s built up by many lines containing the command line arguments of flow-capture: each line is used to run an instance of the program.

You can execute man flow-capture to view all the arguments it accepts; here I used the following example:

# MYROUTER
-V 5 -E 5G -N 3 -w /var/flows/MYROUTER 0.0.0.0/192.168.0.1/3001

# MYSECONDROUTER
-V 5 -E 5G -N 3 -w /var/flows/MYSECONDROUTER 0.0.0.0/192.168.0.2/3002

So, I acquire data from MYROUTER, which sends NetFlow version 5 data from 192.168.0.1; this flow-capture instance will be listening on port 3001 of every local IP address (0.0.0.0) and it will store data on the /var/flows/MYROUTER directory, with a nesting level of type 3, that is directories like /var/flows/MYROUTER/YYYY/YYYY-MM/YYYY-MM-DD/. It will keep files up to a maximum of 5 GB.

This may be a sample configuration for MYROUTER (Cisco), where 192.168.0.9 is the IP address of our NetFlow box:

ip flow-export source FastEthernet0/0.1
ip flow-export version 5
ip flow-export destination 192.168.0.9 3001

interface FastEthernet0/0
 description LAN facing
 no ip address

interface FastEthernet0/0.1
 encapsulation dot1Q 1 native
 ip address 192.168.0.1 255.255.255.0

interface FastEthernet0/1
 description WAN facing
 ip address 10.0.0.1 255.0.0.0
 ip route-cache flow

So on for MYSECONDROUTER…

We just have to build the destination directories and then run the program:

mkdir -p /var/flows/MYROUTER
mkdir -p /var/flows/MYSECONDROUTER

/etc/init.d/flow-capture start

The tool is now working:

ls -l -R /var/flows/MYROUTER/
/var/flows/MYROUTER/:
totale 4
drwxr-xr-x 3 root root 4096  5 mar 10:11 2010

/var/flows/MYROUTER/2010:
totale 4
drwxr-xr-x 3 root root 4096  5 mar 10:11 2010-03

/var/flows/MYROUTER/2010/2010-03:
totale 4
drwxr-xr-x 2 root root 4096  5 mar 10:11 2010-03-05

/var/flows/MYROUTER/2010/2010-03/2010-03-05:
totale 4
-rw-r--r-- 1 root root 92  5 mar 10:11 ft-v05.2010-03-05.101125+0100

FlowViewer

Once we are collecting data using flow-tools we want to analyze them, so we need to get FlowViewer up and running!

Requirements

As we can see from the FlowViewer web site we have to satisfy some requirements in order to run it:

- a web server with CGI support;

- Perl 5.0 or later;

- FlowTools;

- GD and GD:Graph;

- RRDTool.

Let’s start installing them:

apt-get install apache2

apt-get install libgd-graph-perl

apt-get install rrdtool

As we’ll see later in the FlowViewer configuration, it uses also another utility to resolve IP addresses in host names: dig. We have to install the dnsutils Debian package in order to have it:

apt-get install dnsutils

Installation and configuration

Download and untar the package:

cd /usr/local/src
wget http://ensight.eos.nasa.gov/FlowViewer/FlowViewer_3.3.1.tar
tar -xf FlowViewer_3.3.1.tar

Now that all requiremets are met and FlowViewer is on the disk, let’s start configuring it!

In order to get FlowViewer up and running we have to edit its configuration file and build some directories it needs. The user guide provided by the author is very complete, you can find there any information you need. The web site’s FAQ section is very useful too. Here I’ll just provide a basic configuration and layout.

In this sample configuration I use the Apache’s default web site as starting point, so I have:

- / (the root) on /var/www/

- /cgi-bin/ on /usr/lib/cgi-bin/

Let’s move FlowViewer into the cgi-bin directory…

mv FlowViewer_3.3.1 /usr/lib/cgi-bin/

… and edit the configuration file:

cd /usr/lib/cgi-bin/FlowViewer_3.3.1
nano FlowViewer_Configuration.pm

There are a bit of parameters to change, but remember: the user guide is your friend.

Here is the diff of my file against the original:

diff -y --suppress-common-lines -W 250 FlowViewer_Configuration.pm FlowViewer_Configuration.pm.ORIG
$FlowViewer_server       = "192.168.0.9";                                                             |   $FlowViewer_server       = "www.yourcompany.com";     # (IP address or hostname)
$FlowViewer_service      = "http";                                                                    |   $FlowViewer_service      = "https";           # (http, or https)
$reports_directory       = "/var/www/FlowViewer";                                                     |   $reports_directory       = "/htp/htdocs/FlowViewer_3.3.1";
$reports_short           = "/FlowViewer";                                                             |   $reports_short           = "/FlowViewer_3.3.1";
$graphs_directory        = "/var/www/FlowGrapher";                                                    |   $graphs_directory        = "/htp/htdocs/FlowGrapher_3.3.1";
$graphs_short            = "/FlowGrapher";                                                            |   $graphs_short            = "/FlowGrapher_3.3.1";
$tracker_directory       = "/var/www/FlowTracker";                                                    |   $tracker_directory       = "/htp/htdocs/FlowTracker_3.3.1";
$tracker_short           = "/FlowTracker";                                                            |   $tracker_short           = "/FlowTracker_3.3.1";
$cgi_bin_directory       = "/usr/lib/cgi-bin/FlowViewer_3.3.1";                                       |   $cgi_bin_directory       = "/htp/cgi-bin/FlowViewer_3.3.1";
$work_directory          = "/usr/lib/cgi-bin/FlowViewer_3.3.1/Flow_Working";                          |   $work_directory          = "/htp/cgi-bin/FlowViewer_3.3.1/Flow_Working";
$save_directory          = "/var/www/FlowViewer_Saves";                                               |   $save_directory          = "/htp/htdocs/FlowViewer_Saves";
$names_directory         = "/usr/lib/cgi-bin/FlowViewer_3.3.1";                                       |   $names_directory         = "/htp/cgi-bin/FlowViewer_3.3.1";
$filter_directory        = "/usr/lib/cgi-bin/FlowViewer_3.3.1/FlowTracker_Files/FlowTracker_Filters"; |   $filter_directory        = "/htp/cgi-bin/FlowTracker_Files/FlowTracker_Filters";
$rrdtool_directory       = "/usr/lib/cgi-bin/FlowViewer_3.3.1/FlowTracker_Files/FlowTracker_RRDtool"; |   $rrdtool_directory       = "/htp/cgi-bin/FlowTracker_Files/FlowTracker_RRDtool";
$flow_data_directory     = "/var/flows";                                                              |   $flow_data_directory     = "/htp/flows";
$exporter_directory      = "/var/flows/all_routers";                                                  |   $exporter_directory      = "/htp/flows/all_routers";
$rrdtool_bin_directory   = "/usr/bin";                                                                |   $rrdtool_bin_directory   = "/usr/local/rrdtool-1.2.12/bin";
$trackings_title         = "FlowViewer Saves";                                                        |   $trackings_title         = "Your Company Name";
$user_hyperlink          = "/FlowViewer_Saves";                                                       |   $user_hyperlink          = "http://www.yourcompany.com/";
@devices                 = ("MYROUTER","MYSECONDROUTER");                                             |   @devices                 = ("router_1","router_2","router_3");
$log_directory      = "/usr/lib/cgi-bin/FlowViewer_3.3.1";                                            |   $log_directory      = "/htp/cgi-bin/FlowViewer_3.3.1";

And here is every “directory” or “short” parameter:

cat FlowViewer_Configuration.pm | grep "directory\|short"
$reports_directory       = "/var/www/FlowViewer";
$reports_short           = "/FlowViewer";
$graphs_directory        = "/var/www/FlowGrapher";
$graphs_short            = "/FlowGrapher";
$tracker_directory       = "/var/www/FlowTracker";
$tracker_short           = "/FlowTracker";
$cgi_bin_directory       = "/usr/lib/cgi-bin/FlowViewer_3.3.1";
$cgi_bin_short           = "/cgi-bin/FlowViewer_3.3.1";
$work_directory          = "/usr/lib/cgi-bin/FlowViewer_3.3.1/Flow_Working";
$work_short              = "/cgi-bin/FlowViewer_3.3.1/Flow_Working";
$save_directory          = "/var/www/FlowViewer_Saves";
$save_short              = "/FlowViewer_Saves";
$names_directory         = "/usr/lib/cgi-bin/FlowViewer_3.3.1";
$filter_directory        = "/usr/lib/cgi-bin/FlowViewer_3.3.1/FlowTracker_Files/FlowTracker_Filters";
$rrdtool_directory       = "/usr/lib/cgi-bin/FlowViewer_3.3.1/FlowTracker_Files/FlowTracker_RRDtool";
$flow_data_directory     = "/var/flows";
$exporter_directory      = "/htp/flows/all_routers";
$flow_bin_directory      = "/usr/bin";
$rrdtool_bin_directory   = "/usr/bin";
$log_directory      = "/usr/lib/cgi-bin/FlowViewer_3.3.1";
$log_collector_short= "Y";
$log_grapher_short  = "Y";

Apart from the directories, please note the $FlowViewer_server and $FlowViewer_service parameters, and the @devices array, containing the comma-separated list of routers we already configured in flow-captures.

Now, we have to build the directories used by FlowViewer and, of course, we have to set the needed permissions on them.

Here they are:

for d in `cat FlowViewer_Configuration.pm | grep directory | awk -F \" '{print $2}'`; do mkdir -p $d/; done
for d in `cat FlowViewer_Configuration.pm | grep directory | awk -F \" '{print $2}'`; do chmod -R a=rwx $d/; done

It just remains to copy some images into the proper directories…

cp Generic_Logo.jpg /var/www/FlowViewer/
cp FlowViewer_Save.png /var/www/FlowViewer/
cp FlowViewer.png /var/www/FlowViewer_Saves/

… and we are ready to use our NetFlow solution!! Simply point your browser at http://your_server_IP_address/cgi-bin/FlowViewer_3.3.1/FlowViewer.cgi

Is that all? No, it isn’t! :)

FlowTracker Grapher and Collector

FlowViewer is not just a CGIs collection, it also includes two programs intended to be ran continously on background: they are FlowTracker_Grapher and FlowTracker_Collector.

Once you defined some trackings in FlowTracker they constantly grab data from flow-capture files, store them in RRD databases and build graphs, like MRTG does with SNMP data. They also send you a warning when traffic goes over the thresholds you defined.

To start them up automatically I edited the flowcap script included within FlowViewer package (/usr/lib/cgi-bin/FlowViewer_3.3.1/flowcap); here it is:

#!/bin/sh
#
# FlowTracker:      Starts all processes concerning FlowTracker
#
# description:  This script starts up the the FlowTracker tools (Collector and Grapher)
#
# processname:  There is not a single process associated with these
#               actions, rather there are multiple processes. This
#               script takes care of all of them.
#
# can be restarted by using the following command:
#
# sudo /etc/init.d/FlowTracker restart

RETVAL=0

start() {
    echo -n $"Starting FlowTracker processes: "

    echo -n "FlowTracker_Collector "

    cd /usr/lib/cgi-bin/FlowViewer_3.3.1
    ./FlowTracker_Collector &> /dev/null &
    RETVAL=$?

    echo -n "FlowTracker_Grapher "

    cd /usr/lib/cgi-bin/FlowViewer_3.3.1
    ./FlowTracker_Grapher &> /dev/null &
    RETVAL=$?

    echo ""
}
stop() {
    echo -n $"Stopping FlowTracker processes: "
    RETVAL=0

    for p in `pidof perl`
    do
        ps $p | grep FlowTracker_Collector > /dev/null
        if [ $? -eq 0 ]; then
            echo -n "FlowTracker_Collector "
            kill $p
        fi

        ps $p | grep FlowTracker_Grapher > /dev/null
        if [ $? -eq 0 ]; then
            echo -n "FlowTracker_Grapher "
            kill $p
        fi
    done

    echo ""
}

# See how we were called.

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart}"
        ;;
esac
exit $RETVAL

Then I moved it in the /etc/init.d directory as FlowTracker, made it executable and scheduled it to be executed at startup:

mv flowcap /etc/init.d/FlowTracker
chmod a+x /etc/init.d/FlowTracker
update-rc.d FlowTracker defaults 30

Finally:

/etc/init.d/FlowTracker

Housekeeping

Just to clean some files now and then, add an entry in your crontab file pointing to a cleanup script, like the following one:

cd /usr/lib/cgi-bin/FlowViewer_3.3.1/
/usr/lib/cgi-bin/FlowViewer_3.3.1/FlowViewer_CleanFiles &> /usr/lib/cgi-bin/FlowViewer_3.3.1/cleanup.log

Give it execution permissions:

chmod a+x /usr/lib/cgi-bin/FlowViewer_3.3.1/cleanup

Then put it in your crontab:

# m h dom mon dow user  command
5   0    *    *    *     /usr/lib/cgi-bin/FlowViewer_3.3.1/cleanup

Everything is done! Enjoy exploring your network traffic and stay tuned for more NetFlow posts! ;)

References

Cisco.com: Cisco IOS NetFlow

Wikipedia: Netflow

Flow-tools: http://www.splintered.net/sw/flow-tools/

FlowViewer: FlowViewer Web Site and F.A.Q.

Switch.ch: List of NetFlow related software

Automatically filtering bogons with BGP and Team Cymru Bogon Route Server Project

ThiefWhat do you do if someone knocks at your door, you see him through the peephole, and you notice he has a black cowl, a crowbar and a big bag? I’m quite sure: you do not open that door! Why? Because, sometimes, the gown does make the friar! And, actually, he’s really not a friar!

Same thing should happen in a network! If you know a packet is a bad packet, why should you let it go through?

In this (stupid) example bad packets are those coming from or going toward bogon IP addresses, that is, addresses picked up from unallocated or private (so, not routable) address spaces. As you know, there are many subnets used only for private addressing (RFC 1918), other are reserved (RFC 3330), and few subnets are still not allocated by IANA… so, if your router is sending or receiving a packet to/from one of these addresses, there is something you should be concerned about (of course, some restrictions may apply!)

How to identify bogons

In order to identify and catch bogon packets we need to know which subnets have to be marked as bogon.

One way to achieve this goal is to deploy static ACLs or route filters on every router we want to secure… a quite boring job on its own! But, what happens when IANA allocate one of the previously unallocated (so, bogon) subnets? Well, we have to change those ACLs on every router! This is a very boring job! Someone at RIPE knows that well!

Do you really want this?

Fortunately, we can use a great utility by Team Cymru: Bogon Route Server Project.

What is Bogon Route Server Project?

For the sake of our sanity, Team Cymru deployed some route servers running BGP which announce bogon prefixes over eBGP peering sessions. Once we have a BGP-aware box, we can get those prefixes and do what we want of them.

Here, I’ll show how to automatically handle bogon prefixes using a Linux box running Quagga, a routing software suite providing implementation of BGP.

What can we do with bogon prefixes?

From our central Quagga box we can distribute those prefixes to our routers, or to customers CPEs, and here we can filter traffic on the basis of source or destination addresses.

For example, we can avoid to route packets with a bogon destination address, simply by adding a route toward bogon prefixes with a null-pointing next-hop; it’s a kind of blackhole filtering:

ip route 192.0.2.1 255.255.255.255 Null0
ip route 1.0.0.0 255.0.0.0 192.0.2.1

With Cymru Bogon Route Server Project we can do this job in a fully automatic and central way.

Of course, we can also drop incoming packets presenting bogon source IP addresses; we can accomplish this using uRPF in a kind of source-based black hole filtering:

interface Serial1/0
 ip verify unicast source reachable-via any

Anyway, you, and only you know what’s better for your network!

Now, I would like to show you how to setup a Linux box running Quagga, with a peer session with Cymru routers, and how to use it to distribute bogon prefixes to our routers.

Peering with Cymru Bogon Route Server

At first, we have to ask Team Cymru to setup a BGP session with our Quagga box: to do so we have to contact them on the basis of what we can find on their website. We have to tell them our AS number, our peering IP addresses and optional MD5 passwords and GPG/PGP public key.

You don’t need to have a public AS number, you can also peer with them using a private AS number.

Quagga: bgpd configuration

Once we have installed Quagga and received Cymru details, we just have to edit the bgpd.conf file and add a few lines.

Quagga bgpd.conf:

router bgp YOUR_AS_NUMBER
 bgp router-id YOUR_ROUTER_ID
 neighbor cymru-bogon peer-group
 neighbor cymru-bogon remote-as 65333
 neighbor cymru-bogon ebgp-multihop
 neighbor cymru-bogon prefix-list cymru-out out
 neighbor cymru-bogon route-map CYMRUBOGONS in
 neighbor cymru-bogon maximum-prefix 100

 neighbor CYMRU_PEER_1_IP peer-group cymru-bogon
 neighbor CYMRU_PEER_2_IP peer-group cymru-bogon
!
ip community-list 10 permit 65333:888
!
route-map CYMRUBOGONS permit 10
  match community 10
  set ip next-hop 192.0.2.1
!
ip prefix-list cymru-out seq 5 deny 0.0.0.0/0 le 32

Prefix-list prevents any update to leave our box toward Cymru routers; route-map allows marked prefixes and set their next-hop to 192.0.2.1, null-pointing route.

Automagically our Quagga box gets bogon prefixes:

QuaggaBox#show ip bgp
BGP table version is 0, local router ID is YOUR_ROUTER_ID
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*  1.0.0.0          192.0.2.1                0             0 65333 i
*>                  192.0.2.1                0             0 65333 i
*  5.0.0.0          192.0.2.1                0             0 65333 i
*>                  192.0.2.1                0             0 65333 i
[cut]
*  192.168.0.0/16   192.0.2.1                0             0 65333 i
*>                  192.0.2.1                0             0 65333 i
*  198.18.0.0/15    192.0.2.1                0             0 65333 i
*>                  192.0.2.1                0             0 65333 i
*  223.0.0.0/8      192.0.2.1                0             0 65333 i
*>                  192.0.2.1                0             0 65333 i

Total number of prefixes 32

Now, setup our Quagga box to bring up peering with your routers; just add few config lines to bgpd.conf:

router bgp YOUR_AS_NUMBER
 neighbor Clients peer-group
 neighbor Clients prefix-list DoNotAcceptAnything in
 neighbor Clients route-map CYMRUBOGONS out
 neighbor Clients ebgp-multihop
 neighbor Clients send-community

 neighbor YOUR_ROUTER1_IP remote-as YOUR_AS_NUMBER
 neighbor YOUR_ROUTER1_IP peer-group Clients

 neighbor YOUR_ROUTER2_IP remote-as YOUR_AS_NUMBER
 neighbor YOUR_ROUTER2_IP peer-group Clients
!
ip prefix-list DoNotAcceptAnything seq 5 deny 0.0.0.0/0 le 32

Routers configuration

We can deploy iBGP or eBGP configuration on our routers and automatically get bogon prefixes.

This is a sample configuration:

ip route 192.0.2.1 255.255.255.255 Null0
!
ip bgp-community new-format
!
router bgp YOUR_AS_NUMBER
 no synchronization
 bgp log-neighbor-changes
 neighbor YOUR_QUAGGA_BOX_IP remote-as YOUR_AS_NUMBER
 neighbor YOUR_QUAGGA_BOX_IP update-source Loopback0
 neighbor YOUR_QUAGGA_BOX_IP prefix-list DoNotAnnounceAnything out
 neighbor YOUR_QUAGGA_BOX_IP route-map CymruBogons in
 no auto-summary
!
ip community-list standard CymruBogons permit 65333:888
!
ip prefix-list DoNotAnnounceAnything seq 5 deny 0.0.0.0/0 le 32
!
route-map CymruBogons permit 10
 match community CymruBogons
 set ip next-hop 192.0.2.1

Tests

And now, let’s verify our job…

Router#show ip bgp
BGP table version is 163, local router ID is X.Y.Z.W
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*>i1.0.0.0          192.0.2.1                0    100      0 65333 i
*>i5.0.0.0          192.0.2.1                0    100      0 65333 i
[cut]
*>i192.168.0.0/16   192.0.2.1                0    100      0 65333 i
*>i198.18.0.0/15    192.0.2.1                0    100      0 65333 i
*>i223.0.0.0/8      192.0.2.1                0    100      0 65333 i

BGP gets bogon prefixes from our Quagga box; let’s take a look at our routing table, with focus on 192.168.0.0:

Router#show ip route | i 192.168
O IA 192.168.4.0/24 [110/196] via 192.168.254.3, 5d05h, Tunnel1
O IA 192.168.5.0/24 [110/196] via 192.168.254.1, 5d05h, Tunnel0
[cut]
O IA 192.168.1.0/24 [110/196] via 192.168.254.1, 5d05h, Tunnel0
B    192.168.0.0/16 [200/0] via 192.0.2.1, 1d01h

Here we have some private prefixes from OSPF, used for branch offices, and they have the right paths; in the last line, the whole 192.168.0.0/16 goes toward the null-pointing route.
Remember: more specific route wins; until we have our private prefixes from OSPF no packets will be filtered out.

Router#sh ip cef 192.168.222.1
192.168.0.0/16, version 411, epoch 0
0 packets, 0 bytes
  via 192.0.2.1, 0 dependencies, recursive
    next hop 192.0.2.1, Null0 via 192.0.2.1/32
    valid null (drop) adjacency

Well done: it’s simple and it works fine! Anyway, remember to take precautions: do no send bogon prefixes to your BGP peers (no-export plus safeguard communities) and keep in mind every network is different from others!

References

Team Cymru: Bogon Route Server Project

Quagga: Quagga Software Routing Suite

Andree Toonk: Bong Traffic Analysis in 2006

Rob Thomas (on Team Cymru website): 60 Days of Basic Naughtiness, a study conducted in 2001.

GNS3 Labs: Source-based Remote Triggered Black Hole (RTBH) Filtering with Unicast Reverse Path Forwarding (uRPF)

This post is part of a series about “ISP Security Tools and Techniques“; in this series I talk about some (I think) useful practices:

1. Remote Triggered Black Holing

2. BGP Customer triggered black holing

3. BGP triggered rate limiting and less-than-best-effort (LBE) with QPPB

4. Source-based RTBH with Unicast Reverse Path Forwarding (uRPF)

Stay tuned! ;)

Today I drew inspiration from a brand new RFC to add a post to this little series: RFC-5635, Remote Triggered Black Hole Filtering with Unicast Reverse Path Forwarding (uRPF).

Especially, I would like to focus on section 4 of this RFC, Source Address RTBH Filtering.

To fully understand this post I would suggest to read my previous post Remote Triggered Black Holing.

What is source-based RTBH? and what are the differences with destination-based RTBH?

Source and destination based RTBH differences Until now, in my previous posts, I always talked about destination-based RTBH. But a source-based RTBH filtering exists too.

This mitigation technique allows an ISP to stop malicious traffic (let’s think to DDOS) on the basis of the source address it comes from. Indeed, destination-based RTBH can just be used to stop traffic on the basis of the attacked hosts addresses, or in the best case, on the couple attacked-hosts/incoming-upstream-provider (you can use different BGP communities to black-hole a prefix only on specific edge routers).

As mentioned in the RFC, if a DDOS software is instructed to attack an host name rather than an IP address, even if you change the IP address resolved by that host name, the attack won’t stop. In this scenario you just have to disrupt the whole service by filtering out the attacked prefix. Source-based RTBH can help you!

How does it work?

Source-based RTBH uses Unicast Reverse Path Forwarding (uRPF) as background mechanism to work.
As RFC says…

uRPF performs a route lookup of the source address of the packet and checks to see if the ingress interface of the packet is a valid egress interface for the packet source address (strict mode) or if any route to the source address of the packet exists (loose mode). If the check fails, the packet is typically dropped.

So, in order to stop incoming traffic from hosts A, B and C toward host Z through router R we just need to add discard routes for A, B and C on router R. And, as seen for destination-based RTBH, we can do it using BGP and communities.

Is that all?

Well, no! Some policy enforcements are due!

As first, in the same way we did for destination-based RTBH, we must use the no-export community to be sure our black-holed prefix doesn’t leave our AS.

Our policy must accept prefixes outside our network (while destination-based RTBH only accepts prefixes within our network) and, as a rule of thumb, we should not accept source-based RTBH prefixes from our customers, but we should just use this tool from management workstations under our control.

Configuration

Source-based RTBH The starting configuration is the one we left on the post BGP triggered rate limiting and less-than-best-effort (LBE) with QPPB.

In our scenario source-based RTBH is triggered from the Core router, using tagged static routes redistribution in BGP; nothing different from destination-based RTBH. So, let’s define the tags and communities:

300:300 - tag 300 - global source-based black-hole
300:301 - tag 301 - ISP1 source-based black-hole
300:302 - tag 302 - ISP2 source-based black-hole

On the Core router, here used as RTBH trigger, we have to enable BGP redistribution of static routes tagged with the news tags; indeed, we will trigger RTBH by adding tagged static routes:

route-map RTBH permit 50
 match tag 300
 set local-preference 200
 set origin igp
 set community 300:300 no-export
route-map RTBH permit 60
 match tag 301
 set local-preference 200
 set origin igp
 set community 300:301 no-export
route-map RTBH permit 70
 match tag 302
 set local-preference 200
 set origin igp
 set community 300:302 no-export

On edge routers facing upstream providers we have to implement our new communities in the route-map:

Edge1:

ip community-list 5 permit 19661100     ! 300:300
ip community-list 5 permit 19661101     ! 300:301
ip community-list 6 permit 19661102     ! 300:302
!
ip as-path access-list 2 permit ^$
!
ip access-list extended InternalNetworks
 permit ip 192.168.0.0 0.0.255.255 255.255.0.0 0.0.255.255
 deny   ip any any
!
route-map FROM_RR deny 30
 match community 6
!
route-map FROM_RR deny 35
 match ip address InternalNetworks
 match community 5
!
route-map FROM_RR permit 40
 match as-path 2
 match community 5
 set community no-export no-advertise
 set ip next-hop 192.0.2.1

Here, 300:300 and 300:301 communities (community-list 5) are welcome, while 300:302 community is not accepted – it’s only for ISP2 facing router. Moreover, we just accept prefixes external to our network (192.168.0.0/16).

Similar config is on Edge2:

ip community-list 5 permit 19661100     ! 300:300
ip community-list 5 permit 19661102     ! 300:302
ip community-list 6 permit 19661101     ! 300:301
!
ip as-path access-list 2 permit ^$
!
ip access-list extended InternalNetworks
 permit ip 192.168.0.0 0.0.255.255 255.255.0.0 0.0.255.255
 deny   ip any any
!
route-map FROM_RR deny 30
 match community 6
!
route-map FROM_RR deny 35
 match ip address InternalNetworks
 match community 5
!
route-map FROM_RR permit 40
 match as-path 2
 match community 5
 set community no-export no-advertise
 set ip next-hop 192.0.2.1

Finally, we have to enable uRPF on ISP-facing interfaces; we use the loose mode here:

Edge1:

interface Serial1/0
 ip address 172.16.1.1 255.255.255.252
 ip verify unicast source reachable-via any

Edge2:

interface Serial1/0
 ip address 172.16.2.1 255.255.255.252
 ip verify unicast source reachable-via any

Tests

To test the solution we have to add another loopback interface to ISPs routers, in order to have two different source IP addresses for each ISP:

ISP1:

interface Loopback1
 ip address 10.0.1.2 255.255.255.255
!
router bgp 100
 network 10.0.1.2 mask 255.255.255.255

ISP2:

interface Loopback1
 ip address 10.0.2.2 255.255.255.255
!
router bgp 200
 network 10.0.2.2 mask 255.255.255.255

Now, suppose we have an attack from ISP1 Loopback1 toward Cust10 (so, from 10.0.1.2 to 192.168.10.1).

We can stop the attack using destination-based RTBH, but traffic from Loopback0 will be disrupted too:

Core(config)#ip route 192.168.10.1 255.255.255.255 null0 tag 101
ISP1#ping 192.168.10.1 so lo1

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.10.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.1.2
.....
Success rate is 0 percent (0/5)


ISP1#ping 192.168.10.1 so lo0

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.10.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.1.1
.....
Success rate is 0 percent (0/5)

As you can see, both source addresses can’t reach our host.

So, let’s try our new solution; as first remove the previous RTBH filter:

Core(config)#no ip route 192.168.10.1 255.255.255.255 null0 tag 101

then trigger the source-based filtering for the attacking IP address:

Core(config)#ip route 10.0.1.2 255.255.255.255 null0 tag 301

Edge1 receives the new BGP announcement and it adds the discard interface route:

Edge1#sh ip route bgp | i 10.0.1.2
B       10.0.1.2 [200/0] via 192.0.2.1, 00:01:03

Edge1#sh ip cef 10.0.1.2
10.0.1.2/32, version 32, epoch 0
0 packets, 0 bytes
  via 192.0.2.1, 0 dependencies, recursive
    next hop 192.0.2.1, Null0 via 192.0.2.1/32
    valid null adjacency

As we can see, packets from ISP1 Loopback1 are dropped at Edge1, while packets from Loopback0 pass:

ISP1#ping 192.168.10.1 so lo1

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.10.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.1.2
.....
Success rate is 0 percent (0/5)


ISP1#ping 192.168.10.1 so lo0

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.10.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.1.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 476/608/724 ms

Download

You can download the updated GNS3 file and configs here. The ZIP file contains multiple config subdirectories, one for each step covered on previous posts, and the new “6. Source-based RTBH filtering”.

References

IETF: RFC-5635, Remote Triggered Black Hole Filtering with Unicast Reverse Path Forwarding (uRPF)

Follow

Get every new post delivered to your Inbox.