Chapter 1. Introduction

This book is about one of the native firewalls included with FreeBSD, ipfw(8) - the Internet Protocol FireWall. The book makes frequent reference to the ipfw(8) manual page and the reader is advised to become familiar with the manual page alongside this book. There is also a section on ipfw in the FreeBSD Handbook Page on IPFW. The intent with this book is to provide examples and informative material beyond the manual page and handbook to increase understanding and usage of ipfw.

It is, of course, entirely possible to perform all the examples in this book with real hardware. QEMU provides a way to perform the examples without spending any money for hardware. In either case, some setup is required.

Throughout this book are many examples of using ipfw with virtual machines to simulate actual hardware. These examples were developed with QEMU version 9.0.2. Note that QEMU command syntax with some of the examples may have changed slightly by the time this book becomes available. Use the latest QEMU release where possible, and check the QEMU documentation if the examples in this book do not work correctly.

Also used are a number of scripts that allow easy if_bridge(4) and tap(4) setup, virtual machine setup, and data transfer from external VMs to or through a firewall VM. In the early examples, data transfer is accomplished with the netcat program, specifically the version distributed with the nmap package (www.nmap.org). This version, ncat(1), has the best coverage of features that are used throughout the book. A familiarity with the man page for ncat(1) is helpful, but not required.

All scripts used in this book are found in Appendix B and published under the BSD 3-clause license. The scripts are also available on the Github IPFW Primer page.

When copy/pasting examples, be aware that some desktop copy/paste functions add an extra space (or multiple spaces) to the end of a line, messing up the Unix continuation character convention ' …​ \' at the end of a line. You should ensure that the paste function does not introduce extra spaces at the end of the line.

The lab examples in this book involve passing data between interfaces on the host system. A running firewall on the host such as pf, ipfw, or ipfilter (also known as ipf) may interfere with data transfer, so ensure that any host system firewall is disabled. In addition, take any necessary steps to ensure that this does not compromise the security of the host.

1.1. Quick Start

This section details the steps to quickly begin using two QEMU virtual machines, one named "firewall" and the other named "external1". These are the first two of several that will be used throughout this book. Additional detail, along with setup instructions for all virtual machines, is provided in Appendix A.

Setting Up the Initial Virtual Machines
Figure 1. Setting Up the Initial Virtual Machines

The initial setup is that shown in Figure 1.

1.1.1. QEMU VM Installation Process

Follow the steps below to install and configure the QEMU virtual machines for this example.

  1. Install the necessary packages - qemu(1), nmap(1), sudo(1) (or doas(1)). Sudo, (or doas) is necessary for running the virtual machines as these QEMU configurations open a separate console window through SDL. The examples in this book use sudo.

    # pkg install qemu nmap sudo

    Configure sudo as desired.

  2. Create a directory layout for virtual machines and scripts, and download an install ISO for FreeBSD.

    % mkdir -p ~/ipfw/VM ~/ipfw/SCRIPTS ~/ipfw/ISO
    % cd ~/ipfw/ISO
    % fetch https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/<latest version>/FreeBSD-<latest-version>-RELEASE-amd64-dvd1.iso
  3. Create the bridge and tap devices for the virtual machines (VMs) to use.

    # ifconfig tap0 create
    # ifconfig tap1 create
    # sysctl net.link.tap.up_on_open=1
    net.link.tap.up_on_open: 0 -> 1
    # sysctl net.link.tap.user_open=1
    net.link.tap.user_open: 0 -> 1
    # ifconfig bridge0 create
    # ifconfig bridge0 addm tap0 addm tap1 addm  hostintf   <--- replace hostintf with host network interface (em0, bge0, etc.)
    # ifconfig bridge0 up

    A script for creating and managing bridge and tap devices will be introduced in the next section.

  4. Create two new VM image files and install FreeBSD on one.

    % cd ~/ipfw/VM
    % qemu-img create -f qcow2 -o preallocation=full firewall.qcow2 8G
    % qemu-img create -f qcow2 -o preallocation=full external1.qcow2 8G
    % cd ~/ipfw/ISO
    % ln -s FreeBSD-<latest-version>-RELEASE-amd64-dvd1.iso fbsd.iso
    
    Copy the below text into a file (say, firewall.sh) and run:
    
    % sudo /bin/sh firewall.sh
    
    -------
    #!/bin/sh
    # firewall.sh
    
    /usr/local/bin/qemu-system-x86_64  -monitor stdio \
      -cpu qemu64 \
      -vga std \
      -m 4096 \
      -smp 4   \
      -cdrom ../ISO/fbsd.iso \
      -boot order=cd,menu=on \
      -blockdev driver=file,aio=threads,node-name=imgright,filename=../VM/firewall.qcow2 \
      -blockdev driver=qcow2,node-name=drive0,file=imgright \
      -device virtio-blk-pci,drive=drive0,bootindex=1  \
      -netdev tap,id=nd0,ifname=tap0,script=no,downscript=no,br=bridge0 \
      -device e1000,netdev=nd0,mac=02:69:70:66:77:00 \
      -name \"Firewall\"
    
    exit
    -------

    The FreeBSD installer should boot. Perform a standard installation of FreeBSD.

    During the installation note the following:

    • Select to use UFS as the filesystem. ZFS does not perform well with small memory sizes.

    • In this Quick Start, use DHCP for networking. If desired, configure IPv6 if supported by the local LAN.

    • When adding the default user, ensure they are a member of the wheel group.

      Once the installation completes, the virtual machine reboots into the newly installed FreeBSD image.

  5. Login as root and update the system if desired.

  6. Repeat the above step to create another QEMU script file, and perform another installation with these changes:

    Copy the below text into a file (say, external1.sh) and run:
    
    % sudo /bin/sh external1.sh
    
    -------
    #!/bin/sh
    # external1.sh
    
    /usr/local/bin/qemu-system-x86_64  -monitor stdio \
      -cpu qemu64 \
      -vga std \
      -m 4096 \
      -smp 4   \
      -cdrom ../ISO/fbsd.iso \
      -boot order=cd,menu=on \
      -blockdev driver=file,aio=threads,node-name=imgleft,filename=../VM/external1.qcow2 \
      -blockdev driver=qcow2,node-name=drive0,file=imgleft \
      -device virtio-blk-pci,drive=drive0,bootindex=1  \
      -netdev tap,id=nd0,ifname=tap1,script=no,downscript=no,br=bridge0 \
      -device e1000,netdev=nd0,mac=02:20:65:78:74:31 \
      -name \"External 1\"
    
    exit
    -------
  7. As above, login and update the system if desired.

  8. On both virtual machines (and all later installed VMs) , install the nmap(1) package. This brings in the version of ncat(1) used by scripts on the firewall and external VMs.

    # pkg install nmap
  9. Finally, download IPFW_root_bin.tgz file to both VMs. This tar file has a number of scripts needed for the virtual machines.

    Move the tarzip file into /root and:

    # mv IPFW_root_bin.tgz /root
    #
    # cd /root
    #
    # tar xvzf IPFW_root_bin.tgz
    ... files are extracted into /root/bin
    #
    # chmod +x /root/bin/*.sh

(End installation procedure.)

For this Quick Start, it is Ok to use DHCP for both VMs. In later examples there will be multiple external VMs using the 203.0.113.0/24 network, all set up the same way and attached via tap(4) interfaces to a if_bridge(4) on the FreeBSD host.

To ensure you have the first two VMs setup correctly, ping the firewall VM from the external1 VM and vice-versa. Communications should be successful. If not, check the above installation details and troubleshoot any network issues. It should be possible ping in both directions, and even ssh(1) from one VM to the other.

If you are having difficulty getting QEMU set up correctly, check the QEMU virtualization section in the FreeBSD Handbook.

If the mouse is clicked in the QEMU console window, QEMU will “grab” the mouse. If this happens,type, Ctl+Alt+G to release the mouse.

If suddenly, the QEMU console window is full screen, you have accidently typed Ctl+Alt+F. If this happens, retype Ctl+Alt+F to restore the desktop screen.

1.1.2. Disabling Syslog Messages to the Console in the Virtual Machines

You may find it advantageous (even necessary) to stop syslog messages from being sent to the console (either the QEMU console, or the serial port).

To configure syslog to stop logging to the console, configure a file to receive console messages:

# touch /var/log/console.log
#
# chmod 0600 /var/log/console.log

Then, as root, modify the line in /etc/syslog.conf to read (instead of /dev/console:

*.err;kern.warning;auth.notice;mail.crit        /var/log/console.log

And, if necessary:

# service syslogd restart

All messages previously bound for the console, will be directed to /var/log/console.log instead.

Before continuing, we have one more piece to add to each VM - a serial console. We will need a serial console to examine the state of each VM independent of the main console.

1.1.3. Adding the Serial Console Interface to the VMs

To add a serial console to each QEMU VM, start up the VM and edit the file /boot/loader.conf to add console=“comconsole” to allow use of the serial console. QEMU redirects the serial port I/O to a TCP port on the host system at VM startup, and the QEMU monitor waits to activate the guest VM until a telnet(1) connection occurs on that port on the host. Once the FreeBSD system starts booting and recognizes the console directive in /boot/loader.conf it starts up a serial console. The QEMU monitor detects this and directs the necessary character I/O on that serial port to the TCP port on the host.

It is important to note that the this serial redirect over TCP takes place outside the virtual machine. There is no interaction with any network on the virtual machine and thus it is not subject to any firewall rules. Think of it just like a "dumb terminal" sitting on an RS-232 serial port on a real machine.

Edits for the firewall VM are shown below:

#!/bin/sh
## firewall.sh

echo
echo "NOTE!!! telnet server running!"
echo "To start QEMU, start another session and telnet to localhost 4450"
echo

/usr/local/bin/qemu-system-x86_64 -monitor stdio \
 -serial telnet:localhost:4450,server=on,wait=on \
 -cpu qemu64 \
 -vga std \
 -m 4096 \
 -smp 4   \
 -cdrom ../ISO/fbsd.iso \
 -boot order=cd,menu=on \
 -blockdev driver=file,aio=threads,node-name=imgright,filename=../VM/firewall.qcow2 \
 -blockdev driver=qcow2,node-name=drive0,file=imgright \
 -device virtio-blk-pci,drive=drive0,bootindex=1  \
 -netdev tap,id=nd0,ifname=tap0,script=no,downscript=no,br=bridge0 \
 -device e1000,netdev=nd0,mac=02:69:70:66:77:00 \
 -name \"Firewall\"

exit
-------

Edits for the external1 VM:

-------
#!/bin/sh
## external1.sh

echo
echo "NOTE!!! telnet server running!"
echo "To start QEMU, start another session and telnet to localhost 4410"
echo

/usr/local/bin/qemu-system-x86_64 -monitor stdio \
 -serial telnet:localhost:4410,server=on,wait=on \
 -cpu qemu64 \
 -vga std \
 -m 4096 \
 -smp 4   \
 -cdrom ../ISO/fbsd.iso \
 -boot order=cd,menu=on \
 -blockdev driver=file,aio=threads,node-name=imgleft,filename=../VM/external1.qcow2 \
 -blockdev driver=qcow2,node-name=drive0,file=imgleft \
 -device virtio-blk-pci,drive=drive0,bootindex=1  \
 -netdev tap,id=nd0,ifname=tap1,script=no,downscript=no,br=bridge0 \
 -device e1000,netdev=nd0,mac=02:20:65:78:74:31 \
 -name \"External 1\"

exit
-------

Manually configure the FreeBSD host, firewall VM, and external1 VM with the addressing as shown in the figure above. You should have full connectivity between your FreeBSD host, the firewall VM and the external1 VM.

.Firewall and External1 VMs Startup with Serial Console
Figure 2. External1 and Firewall VMs Startup with Serial Console

Figure Figure 2 shows both VMs starting up with (from the bottom up):

  • The QEMU monitor window running on the host.

  • The serial console (ttyu0) running on the VM serial console

  • The main console (ttyv0) running on the VM main console

Scripts used by the two VMs are discussed in the next section.