How to configure IPS mode with AF-PACKET?

When I test IPS mode with AF_PACKET on ubuntu 20, I only have one network interface.
So I add a virtual network interface by sudo ip link add name bro type dummy and up it firstly, I have two network interface , ens33 and bro now.

bro: flags=195<UP,BROADCAST,RUNNING,NOARP>  mtu 1500
        inet6 fe80::ec93:e9ff:fe01:23cf  prefixlen 64  scopeid 0x20<link>
        ether ee:93:e9:01:23:cf  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 942  bytes 121613 (121.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.17.139  netmask 255.255.255.0  broadcast 192.168.17.255
        inet6 fe80::4db7:e1fe:4257:d794  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:94:b6:e9  txqueuelen 1000  (Ethernet)
        RX packets 1279  bytes 404127 (404.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 704  bytes 113729 (113.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 354  bytes 34195 (34.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 354  bytes 34195 (34.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Then I change the configuration file as flow:

af-packet:
  - interface: bro
    threads: 1
    defrag: no
    cluster-type: cluster_flow
    cluster-id: 98
    copy-mode: ips
    copy-iface: ens33
    buffer-size: 64535
    use-mmap: yes
  - interface: ens33
    threads: 1
    cluster-id: 97
    defrag: no
    cluster-type: cluster_flow
    copy-mode: ips
    copy-iface: bro
    buffer-size: 64535
    use-mmap: yes

And I add a test rules:

drop http any any -> any any (msg:"hit bing.com..."; content:"bing"; reference: url,www.bing.com; sid:2022021701; rev:1;)

I run suricata by:

sudo suricata -c /etc/suricata/suricata.yaml --af-packet

I run curl www.bing.com , and get success response.
A warning log in output in fast.log:

2/16/2022-21:43:40.943398  [Drop] [**] [1:2022021701:1] hit bing.com... [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.17.139:48854 -> 202.89.233.100:80

In order to find why the reason is happing, I use wireshark to capture packet.
The packet was droped when copy from ens33 to bro. But the packet can use ens33 to connect www.bing.com directly.
In my mind, it should be:


But in fact:

How to solve this problem?

1 Like

I’m not actually sure if this is possible. But the first issue is that ens33 has the IP address, not bro, so the kernel will use the interface it knows about as the source. So you’ll have to remove the IP address info from ens33 and give it to bro so the kernel will use bro as an IP source.

Typically AF_PACKET IPS is used between 2 devices without IP addresses, and traffic to/from the host running Suricata does not use these interfaces.

@Ish, thanks for your response. Un…but i do not understand your " 2 devices without IP addresses" means, can you provide a simple network topology diagram including suricata?
Thanks for your help.

I can’t really do a diagram. But imagine a Linux machine with 3 network interfaces: eth0, eth1, eth2.

eth0 is a management interface, it has an IP address, you can ssh to it, etc.

eth1 and eth2 are the IPS interface. They are up, but do not have any IP addresses. You run Suricata in AF_PACKET IPS mode on eth1 and eth2 in copy mode and they form a bridge, much like if the box wasn’t even there but it was a straight cable instead.

Traffic destined to this machine, or coming from this machine always goes out the management interface and is not subject to the IPS. The network bridged between eth1 and eth2 are subject to the IPS, but not the IPS machine itself.

Typically if you want to protect just the host that is running Suricata, I think you’d use nfq.

1 Like

Thanks for your reply again. Your detailed reply has resolved my question.
Express my thanks again to you.

hi jason, I deploy suricata ips at layer 2 on my multi interface compute (named host2).
the network topology like this:

however, I can ping host1 and host2 on host3. but I can not ping host3 on host1 or host2.
Any ideas to solve this problem? thanks

It is strange that I config suricata as IPS, but it show suricata run as IDS.

but in log, log show that suricata set IPS mode

Please paste the content of /etc/init.d/suricata

hi, /etc/init.d/suricata output is

equator@devbox:~$ cat  /etc/init.d/suricata
#!/bin/sh -e
#
### BEGIN INIT INFO
# Provides:          suricata
# Required-Start:    $time $network $local_fs $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Next Generation IDS/IPS
# Description:       Intrusion detection system that will
#                    capture traffic from the network cards and will
#                    match against a set of known attacks.
### END INIT INFO

. /lib/lsb/init-functions

# Source function library.
if test -f /etc/default/suricata; then
    . /etc/default/suricata
else
    echo "/etc/default/suricata is missing... bailing out!"
fi

# We'll add up all the options above and use them
NAME=suricata
DAEMON=/usr/bin/$NAME

if [ -z "$RUN_AS_USER" ]; then
    USER_SWITCH=
  else
    USER_SWITCH=--user=$RUN_AS_USER
fi



# Use this if you want the user to explicitly set 'RUN' in
# /etc/default/
if [ "x$RUN" != "xyes" ] ; then
    log_failure_msg "$NAME disabled, please adjust the configuration to your needs "
    log_failure_msg "and then set RUN to 'yes' in /etc/default/$NAME to enable it."
    exit 0
fi

check_root()  {
    if [ "$(id -u)" != "0" ]; then
        log_failure_msg "You must be root to start, stop or restart $NAME."
        exit 4
    fi
}

check_run_dir() {
    if [ ! -d /var/run/suricata ]; then
        mkdir /var/run/suricata
    if  [ ! -z "$RUN_AS_USER" ]; then
        chown $RUN_AS_USER /var/run/suricata;
    fi
        chmod 0755 /var/run/suricata
    fi
}



check_root

case "$LISTENMODE" in
  nfqueue)
    IDMODE="IPS (nfqueue)"
    LISTEN_OPTIONS=" $NFQUEUE"
    ;;
    custom_nfqueue)
    IDMODE="IPS (custom multi-nfqueue)"
    LISTEN_OPTIONS=" $CUSTOM_NFQUEUE"
    ;;
  pcap)
    IDMODE="IDS (pcap)"
    LISTEN_OPTIONS=" -i $IFACE"
    ;;
  af-packet)
    IDMODE="IDS (af-packet)"
    LISTEN_OPTIONS=" --af-packet"
    ;;
  *)
    echo "Unsupported listen mode $LISTENMODE, aborting"
    exit 1
    ;;
esac

SURICATA_OPTIONS=" -c $SURCONF --pidfile $PIDFILE $LISTEN_OPTIONS -D -vvv $USER_SWITCH"

# See how we were called.
case "$1" in
  start)
       if [ -f $PIDFILE ]; then
           PID1=`cat $PIDFILE`
           if kill -0 "$PID1" 2>/dev/null; then
               echo "$NAME is already running with PID $PID1"
               exit 0
           else
               echo "Likely stale PID `cat $PIDFILE` with $PIDFILE exists, but process is not running!"
               echo "Removing stale PID file $PIDFILE"
               rm -f $PIDFILE
           fi
       fi
       check_run_dir
       echo -n "Starting suricata in $IDMODE mode..."
       $DAEMON $SURICATA_OPTIONS > /var/log/suricata/suricata-start.log  2>&1 &
       echo " done."
       ;;
  stop)
       echo -n "Stopping suricata: "
       if [ -f $PIDFILE ]; then
           PID2=`cat $PIDFILE`
       else
           echo " No PID file found; not running?"
           exit 0;
       fi
       start-stop-daemon --oknodo --stop --quiet --pidfile=$PIDFILE --exec $DAEMON
       if [ -n "$PID2" ]; then
           kill "$PID2"
           ret=$?
           sleep 2
           if kill -0 "$PID2" 2>/dev/null; then
               ret=$?
               echo -n "Waiting . "
               cnt=0
               while kill -0 "$PID2" 2>/dev/null; do
                   ret=$?
                   cnt=`expr "$cnt" + 1`
                   if [ "$cnt" -gt 10 ]; then
                      kill -9 "$PID2"
                      break
                   fi
                   sleep 2
                   echo -n ". "
               done
           fi
       fi
       if [ -e $PIDFILE ]; then
           rm $PIDFILE > /dev/null 2>&1
       fi
       echo " done."
    ;;
  status)
       # Check if running...
       if [ -s $PIDFILE ]; then
           PID3=`cat $PIDFILE`
           if kill -0 "$PID3" 2>/dev/null; then
               echo "$NAME is running with PID $PID3"
               exit 0
           else
               echo "PID file $PIDFILE exists, but process not running!"
           fi
       else
          echo "$NAME not running!"
       fi
    ;;
  restart)
        $0 stop
        $0 start
    ;;
  force-reload)
        $0 stop
        $0 start
    ;;
  *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
esac

exit 0

Did you write this yourself or is it included in some package?