Setting Up a Network Gateway Using IPFW and NATD

This sheet describes how to set up a dual-homed host (gateway) using kernel packet filtering (IPFW) and the network address traslation daemon (NATD). This procedure assumes the FreeBSD machine is equipped with two ethernet cards. If the outside interface is a dial-up connection or a PPP-over-Ethernet (PPPoE) DSL connection, use ppp(8)’s NAT facility instead.

Before performing this procedure, you should read the FreeBSD Handbook sections Firewalls and Gateways and Routes, and review the ipfw(4), ipfw(8), and natd(8) manual pages.

For the purpose of this procedure, the following assumptions have been made:

  1. The outside (Internet) interface is device ‘dc0’ and is assigned the IP address 123.45.67.89/22, either static or dynamic.
  2. The inside interface is device ‘ep0’ and is assigned the IP address 10.20.155.0/24.

If your outside interface is a DSL connection or a cable modem, please refer to Setting-up a DSL Connection or Setting-up a Cable Modem, as appropriate, before beginning this procedure.

  1. Edit the kernel configuration file, /usr/src/sys/i386/conf/YOURKERNEL, and add firewall/NAT support:
  2. options     IPFIREWALL           #firewall
    options     IPFIREWALL_VERBOSE   #print information about dropped packets
    options     IPFIREWALL_VERBOSE_LIMIT=50 #limit verbosity
    options     IPDIVERT             #divert sockets

    Recompile the kernel, but do not reboot yet. Refer to Building a Custom Kernel for information on recompiling your kernel.

  3. Edit /etc/rc.conf:
  4. gateway_enable="YES"           # Set to YES if this host will be a gateway.
    firewall_enable="YES"          # Set to YES to enable firewall functionality
    firewall_script="/etc/rc.firewall"  # Which script to run to set up the firewall
    firewall_type="open"           # Firewall type (see /etc/rc.firewall)
    firewall_quiet="NO"            # Set to YES to suppress rule display
    natd_program="/sbin/natd"      # path to natd, if you want a different one.
    natd_enable="YES"              # Enable natd (if firewall_enable == YES).
    natd_interface="dc0"           # Public interface or IPaddress to use.
    natd_flags="-f /etc/natd.conf" # Additional flags for natd.
    tcp_drop_synfin="YES"          # Set to YES to drop TCP packets with SYN+FIN
  5. Create /etc/natd.conf:
    dynamic yes   (If your outside interface address is assigned using DHCP, otherwise omit)
    use_sockets yes
    same_ports yes
  6. Verify that the following line appears in /etc/services:

    natd 8668/divert # Network Address Translation

  7. Shutdown and reboot.At this point, you should have a working gateway machine. Test to make sure machines on the local network can access the Internet.
  8. Check your firewall rules:

    # ipfw list

    You should see something like:

    00100 divert 8668 ip from any to any via dc0
    00100 allow ip from any to any via lo0
    00200 deny ip from any to 127.0.0.0/8
    65000 allow ip from any to any

As you might have guessed, this is not a very secure firewall; so we’ll tighten it up:

  1. First, copy /etc/rc.firewall to /etc/rc.ipfw, so we don’t have to touch the default file:

    # cp /etc/rc.firewall /etc/rc.ipfw

  2. Edit /etc/rc.ipfw to reflect the outside and inside interface addresses and to include the following rules as you see fit:
    # rc.ipfw - Firewall Rules
    #
    # This file is a modified version of /etc/rc.firewall.
    
    # Suck in the configuration variables.
    if [ -r /etc/defaults/rc.conf ]; then
            . /etc/defaults/rc.conf
            source_rc_confs
    elif [ -r /etc/rc.conf ]; then
            . /etc/rc.conf
    fi
    
    if [ -n "${1}" ]; then
            firewall_type="${1}"
    fi
    
    # Firewall program
    fwcmd="/sbin/ipfw"
    
    # Outside interface network and netmask and ip
    oif="dc0"
    onet="123.45.67.0"
    omask="255.255.252.0"
    oip="123.45.67.89"
    
    # Inside interface network and netmask and ip
    iif="ep0"
    inet="10.20.155.0"
    imask="255.255.255.0"
    iip="10.20.155.1"
    
    # My ISP's DNS servers
    dns1="123.45.67.8"
    dns2="98.76.54.123"
    
    # Flush previous rules
    ${fwcmd} -f flush
    
    # Allow loopbacks, deny imposters
    ${fwcmd} add 100 pass all from any to any via lo0
    ${fwcmd} add 200 deny all from any to 127.0.0.0/8
    ${fwcmd} add 300 deny ip from 127.0.0.0/8 to any
    
    # Stop spoofing
    ${fwcmd} add deny all from ${inet}:${imask} to any in via ${oif}
    ${fwcmd} add deny all from ${onet}:${omask} to any in via ${iif}
    
    # Stop RFC1918 nets on the outside interface
    ${fwcmd} add deny all from any to 10.0.0.0/8 via ${oif}
    ${fwcmd} add deny all from any to 172.16.0.0/12 via ${oif}
    ${fwcmd} add deny all from any to 192.168.0.0/16 via ${oif}
    
    # Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1,
    # DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E)
    # on the outside interface
    ${fwcmd} add deny all from any to 0.0.0.0/8 via ${oif}
    ${fwcmd} add deny all from any to 169.254.0.0/16 via ${oif}
    ${fwcmd} add deny all from any to 192.0.2.0/24 via ${oif}
    ${fwcmd} add deny all from any to 224.0.0.0/4 via ${oif}
    ${fwcmd} add deny all from any to 240.0.0.0/4 via ${oif}
    
    # Network Address Translation.  This rule is placed here deliberately
    # so that it does not interfere with the surrounding address-checking
    # rules.  If for example one of your internal LAN machines had its IP
    # address set to 192.0.2.1 then an incoming packet for it after being
    # translated by natd(8) would match the `deny' rule above.  Similarly
    # an outgoing packet originated from it before being translated would
    # match the `deny' rule below.
    ${fwcmd} add divert natd all from any to any via ${natd_interface}
    
    # Stop RFC1918 nets on the outside interface
    ${fwcmd} add deny all from 10.0.0.0/8 to any via ${oif}
    ${fwcmd} add deny all from 172.16.0.0/12 to any via ${oif}
    ${fwcmd} add deny all from 192.168.0.0/16 to any via ${oif}
    
    # Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes RESERVED-1,
    # DHCP auto-configuration, NET-TEST, MULTICAST (class D), and class E)
    # on the outside interface
    ${fwcmd} add deny all from 0.0.0.0/8 to any via ${oif}
    ${fwcmd} add deny all from 169.254.0.0/16 to any via ${oif}
    ${fwcmd} add deny all from 192.0.2.0/24 to any via ${oif}
    ${fwcmd} add deny all from 224.0.0.0/4 to any via ${oif}
    ${fwcmd} add deny all from 240.0.0.0/4 to any via ${oif}
    
    # Allow TCP through if setup succeeded
    ${fwcmd} add pass tcp from any to any established
    
    # Allow IP fragments to pass through
    ${fwcmd} add pass all from any to any frag
    
    ### TCP RULES
    
    # HTTP - Allow access to our web server
    ${fwcmd} add pass tcp from any to any 80 setup
    
    # SMTP - Allow access to sendmail for incoming e-mail
    ${fwcmd} add pass tcp from any to any 25 setup
    
    # FTP - Allow incoming data channel for outgoing connections,
    # reject & log all incoming control connections
    ${fwcmd} add pass tcp from any 20 to any 1024-65535 setup
    ${fwcmd} add deny log tcp from any to any 21 in via ${oif} setup
    
    # SSH Login - Allow & Log all incoming
    ${fwcmd} add pass log tcp from any to any 22 in via ${oif} setup
    
    # IDENT - Reset incoming connections
    ${fwcmd} add reset tcp from any to any 113 in via ${oif} setup
    
    # Reject&Log all setup of incoming connections from the outside
    ${fwcmd} add deny log tcp from any to any in via ${oif} setup
    
    # Allow setup of any other TCP connection
    ${fwcmd} add pass tcp from any to any setup
    
    ### UDP RULES
    
    # DNS - Allow queries out in the world
    ${fwcmd} add pass udp from any to ${dns1} 53
    ${fwcmd} add pass udp from any to ${dns2} 53
    ${fwcmd} add pass udp from ${dns1} 53 to any
    ${fwcmd} add pass udp from ${dns2} 53 to any
    
    # SMB - Allow local traffic
    ${fwcmd} add pass udp from any to any 137-139 via ${iif}
    
    # SYSLOG - Allow machines on inside net to log to us.
    ${fwcmd} add pass log udp from any to any 514 via ${iif}
    
    # NTP - Allow queries out in the world
    ${fwcmd} add pass udp from any 123 to any 123 via ${oif}
    ${fwcmd} add pass udp from any 123 to any via ${iif}
    ${fwcmd} add pass udp from any to any 123 via ${iif}
    
    # TRACEROUTE - Allow outgoing
    ${fwcmd} add pass udp from any to any 33434-33523 out via ${oif}
    
    ### ICMP RULES
    
    # ICMP packets
    # Allow all ICMP packets on internal interface
    ${fwcmd} add pass icmp from any to any via ${iif}
    
    # Allow outgoing pings
    ${fwcmd} add pass icmp from any to any icmptypes 8 out via ${oif}
    ${fwcmd} add pass icmp from any to any icmptypes 0 in via ${oif}
    
    # Allow Destination Unreachable, Source Quench, Time Exceeded, and Bad Header
    ${fwcmd} add pass icmp from any to any icmptypes 3,4,11,12 via ${oif}
    
    # Deny the rest of them
    ${fwcmd} add deny icmp from any to any
    
    ### MISCELLANEOUS REJECT RULES
    
    # Reject broadcasts from outside interface
    ${fwcmd} add 63000 deny ip from any to 0.0.0.255:0.0.0.255 in via ${oif}
    
    # Reject and Log SMB connections on outside interface
    ${fwcmd} add 64000 deny log udp from any to any 137-139 via ${oif}
    
    # Reject and Log all other connections from outside interface
    ${fwcmd} add 65000 deny log ip from any to any via ${oif}
    
    # Everything else is denied by default, unless the
    # IPFIREWALL_DEFAULT_TO_ACCEPT option is set in your kernel
    # config file.
  3. Edit /etc/rc.conf and change the line:
  4. firewall_script=”/etc/rc.firewall”

    to:

    firewall_script=”/etc/rc.ipfw”

  5. Load the new firewall rules:
  6. # sh /etc/rc.ipfw

    Be sure to do this from the console, or you will lock youself out of the computer!

    Note: You can reload the firewall rules over a network connection (telnet, ssh) by running the command in the background:

    # sh /etc/rc.ipfw &

    You will get logged off as soon as the ‘ipfw -f flush’ command executes, but the script will continue to run.

      Be very careful to ensure that you run the script in the background and that your firewall rules are correct, or you will leave your system in a state with no network access.

Leave a Reply

Your email address will not be published. Required fields are marked *