Print View

Build a Home-Office Router Using FreeBSD and PF
Created: 03/22/2006


General Information

This guide will outline the basic steps for building a home-office router for use with a cable modem or DSL line using FreeBSD and Packet Filter (PF).  It will cover updating FreeBSD, building a new kernel, and installing and configuring a DHCP server and DNS server to support a small internal network.

Requirements

  1. Standard installation of FreeBSD 5.3 or greater
  2. Two network cards
  3. A text editor
Before beginning, we need to decide which network adapter is going to be connected to the internal network and which one is going to be connected to the external network (cable modem or DSL line).

Run 'ifconfig' from the command line to list the network adapters:
$ ifconfig
dc0: flags=108843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500
        inet6 fe80::20c:29ff:fefc:800c%lnc0 prefixlen 64 scopeid 0x1
        inet 192.168.130.133 netmask 0xffffff00 broadcast 192.168.130.255
        ether 00:0c:29:fc:80:0c
xl0: flags=108802<BROADCAST,SIMPLEX,MULTICAST,NEEDSGIANT> mtu 1500
        ether 00:0c:29:fc:80:16
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4
        inet 127.0.0.1 netmask 0xff000000
For this document I am going to use the following configuration:

dc0: external interface (IP address provided by ISP's DHCP server)

xl0: internal interface (IP address is static, 192.168.10.1)

Internal network: 192.168.10.0/255.255.255.0

Installation

Kernel source

First, we need to ensure that the kernel source is installed.  We will need the kernel source to recompile the kernel to add PF support.  The kernel source can be installed using 'sysinstall' utility.

From the sysinstall menu:
  1. Select "Configure"
  2. Select "Distributions"
  3. Select "src"
  4. Select "sys"

Update the system

Next we need to update the system (don't compile a kernel yet) and the ports tree.

Install network services

Once the system has been updated, we can install a few necessary services on our system; a DHCP server to provide IP addresses to clients on the internal network, and a DNS server to forward DNS requests from the internal clients to our ISP's DNS servers.

DHCP server:
#
#
cd /usr/ports/net/ics-dhcp3-server
make install clean (the default options are fine)
DNS server:
#
#
cd /usr/ports/dns/bind9
make install clean

Compile a new kernel with PF support

The last part of the installation process requires compiling a new kernel with PF support.

We will use the GENERIC kernel configuration as the basis for our CUSTOM kernel:
#
#
cd /usr/src/sys/i386/conf
cp GENERIC CUSTOM
Edit CUSTOM:

Change the line:
ident GENERIC
to:
ident CUSTOM
Optionally, comment out options INET6 to remove IPv6 support.

Add the following PF options to the end of the file.  You may also want to add ALTQ support.  ALTQ provides traffic shaping and Quality of Service functionality.
# PF
device pf
device pflog
device pfsync

# ALTQ
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_CDNR
options ALTQ_PRIQ
We are now ready to recompile the kernel.

Note:  If you commented out IPv6 support (INET6) you need to add the following line to /etc/make.conf before compiling:
NO_INET6="YES"

cd /usr/src
make buildkernel KERNCONF=CUSTOM
make installkernel KERNCONF=CUSTOM
It may take quite some time to build the kernel.  Once the kernel is installed we need to reboot to activate the new kernel:
# shutdown -r now
Assuming the system reboots without incident, let's login and verify the kernel.
$ uname -a
FreeBSD freebsd6.localdomain 6.0 FreeBSD 6.0 #0: Mon Mar 13 16:29:47 EST 2006
root@freebsd6.localdomain:/usr/obj/usr/src/sys/CUSTOM  i386
Good, looks like it worked.  We are now using our custom kernel: (.../usr/obj/usr/src/sys/CUSTOM)

Let's make sure PF is working:
$ pfctl -e
pf enabled
$ pfctl -d
pf disabled
Installation is now complete.  We can proceed to configuring PF and the network services.

Configuration

DNS Server

We need to edit the BIND configuration file, /var/named/etc/namedb/named.conf.  Set the listen-on IP address to the internal IP address and the loopback address of the FreeBSD box.  Also, configure BIND to only forward DNS requests, and specify the IP addresses of your ISP's DNS servers.  You can also remove all the zone statements other than the "." zone.  Sample file follows:
// named.conf
// Refer to the named.conf(5) and named(8) man pages, and the documentation
// in /usr/share/doc/bind9 for more details.
//
// If you are going to set up an authoritative server, make sure you
// understand the hairy details of how DNS works.  Even with
// simple mistakes, you can break connectivity for affected parties,
// or cause huge amounts of useless Internet traffic.

options {
directory "/etc/namedb";
pid-file "/var/run/named/pid";
dump-file "/var/dump/named_dump.db";
statistics-file "/var/stats/named.stats";

// If named is being used only as a local resolver, this is a safe default.
// For named to be accessible to the network, comment this option, specify
// the proper IP address, or delete this option.

listen-on { 192.168.10.1; 127.0.0.1; };

// In addition to the "forwarders" clause, you can force your name
// server to never initiate queries of its own, but always ask its
// forwarders only, by enabling the following line:
//
      forward only;

// If you've got a DNS server around at your upstream provider, enter
// its IP address here, and enable the line below.  This will make you
// benefit from its cache, thus reduce overall DNS traffic in the Internet.

// forwarders { x.x.x.x; y.y.y.y; };
forwarders { [IP address of ISP DNS server]; [IP address of ISP DNS server]; };

};

// If you enable a local name server, don't forget to enter 127.0.0.1
// first in your /etc/resolv.conf so this server will be queried.
// Also, make sure to enable it in /etc/rc.conf.

zone "." {
type hint;
file "named.root";
};

// end named.conf

DHCP Server

Next we will configure the DHCP service.  Start off by copying a sample configuration file to /usr/local/etc.
# cp /usr/local/etc/dhcpd.conf.sample /usr/local/etc/dhcpd.conf
Then edit /usr/local/etc/dhcpd.conf; we need to specify our local DNS server, the network gateway, and the available IP address range.  Below is an example configuration.
### dhcpd.conf
# option definitions common to all supported networks...
# name server is running on this host
option domain-name-servers 192.168.10.1;
default-lease-time 600;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# ad-hoc DNS update scheme - set to "none" to disable dynamic DNS updates.
ddns-update-style none;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# for small internal network, 100 IPs
subnet 192.168.10.0 netmask 255.255.255.0 {
        range 192.168.10.100 192.168.10.200;
        option routers 192.168.10.1;
        option subnet-mask 255.255.255.0;
}
### end dhcpd.conf

Packet Filter

We will use one of the example PF configuration files as a basis for our PF configuration.  This sample file has everything we need for a basic router.
# cp /usr/share/examples/pf/faq-example1 /etc/pf.conf
You will need to edit /etc/pf.conf, specifying the internal and external interfaces, as well as what services you want to allow external clients to access on the FreeBSD box.  Below is the /usr/share/examples/pf/faq-example1 file with additional comments.
### macros
# internal and external interfaces (run 'ifconfig' to find interfaces)
int_if = "xl0"
ext_if = "dc0"

# Ports we want to allow access to from the outside world on our local
# system (ext_if)
tcp_services = "{ 22, 80 }"

# ping requests
icmp_types = "echoreq"

# Private networks, we are going to block incoming traffic from them
priv_nets = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"

### options
set block-policy return
set loginterface $ext_if
set skip on lo0

### Scrub
# From the PF user's guide (http://www.openbsd.org/faq/pf/index.html):
# "Scrubbing" is the normalization of packets so there are no ambiguities in
# interpretation by the ultimate destination of the packet. The scrub directive
# also reassembles fragmented packets, protecting some operating systems from
# some forms of attack, and # drops TCP packets that have invalid flag
# combinations.
scrub in all

### nat/rdr
# NAT traffic from internal network to external network through external
# interface
nat on $ext_if from $int_if:network to any -> ($ext_if)

# redirect FTP traffic to FTP proxy on localhost:8021
# requires ftp-proxy to be enabled in /etc/inetd.conf
rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021


### filter rules
block all

# block incoming traffic from private networks on external interface
block drop in quick on $ext_if from $priv_nets to any

# block outgoing traffic to private networks on external interface
block drop out quick on $ext_if from any to $priv_nets

# allow access to tcp_services on external interface
pass in on $ext_if inet proto tcp from any to ($ext_if) port
$tcp_services flags S/SA keep state

# allow in FTP control port
pass in on $ext_if inet proto tcp from port 20 to ($ext_if) user proxy
flags S/SA keep state

# allow in ping replies
pass in inet proto icmp all icmp-type $icmp_types keep state

# allow all traffic from internal network to internal interface
pass in  on $int_if from $int_if:network to any keep state
pass out on $int_if from any to $int_if:network keep state

# allow all traffic out via external interface
pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state

rc.conf

The last configuration step is to enable our network services and PF at system startup by modifying /etc/rc.conf.  Below is a sample rc.conf file.
### Networking
# NIC is configured by our ISP's DHCP server
ifconfig_dc0="DHCP"
# Internal NIC has a static IP
ifconfig_xl0="inet 192.168.10.1 netmask 255.255.255.0"
hostname="myhostname"

### Gateway, so we can forward traffic between the int. and ext. networks
gateway_enable="yes"

### PF
pf_enable="YES"                
pf_rules="/etc/pf.conf" # pf rules
pf_flags="" # pfctl flags
pflog_enable="YES" # start pflogd
pflog_logfile="/var/log/pflog" # pf logfile
pflog_flags="" # pflogd flags

### DHCP server
dhcpd_enable="yes"
dhcpd_ifaces="xl0" # enabled on int. interface

### DNS server - BIND
named_enable="YES"

### INETD - need to enable if we use the ftp-proxy
inetd_enable="YES"
After editing rc.conf, reboot the system for the changes to take effect.
# shutdown -r now
When the system comes back up, the internal clients should be able to access the Internet through it.

Conclusion

This guide has just touched on the basics of using FreeBSD and PF as a router.  PF offers many advanced features such as blocking hosts based on their OS and traffic shaping.  For more information see the PF handbook.

Also, please be aware of the security implications of running your own FreeBSD router.  Since the FreeBSD system will be directly connected to the public Internet, it is highly recommended that you take steps to secure the system.  There are several sites with information about hardening a FreeBSD system, please make use of the information.

Author: jdferrell
jdferrell3 at yahoo dot com



15 Comments

Posted by zyxep on July 30, 2006 at 11:50:36 am EEST

i did exactly what you have wrote in this guide.. i can get ip from the dhcp but i can't surfe on the inet :/


Posted by eam404 on August 11, 2006 at 12:47:09 am EEST

To the above poster, here is why...


In named.conf

This section


// forwarders { x.x.x.x; y.y.y.y; };
forwarders { [IP address of ISP DNS server]; [IP address of ISP DNS server]; };


There should not be any brackets  it should read as follows...

// forwarders { x.x.x.x; y.y.y.y; };
forwarders { xxx.xxx.xxx.xxx; xxx.xxx.xxx.xxx; };


Posted by neomaximus2k on August 14, 2006 at 6:13:51 pm EEST

Hiya, we have a gateway server setup here at work in the same way this tutorial states but we have come across a problem, it seems to be caching DNS entries as we can not access our website (the DNS changes every now and again) is there nay fix / command i can give to solve this?


Posted by jopio on October 07, 2006 at 5:57:41 am EEST

followed every bits of these steps laid out using bsd 6.1, but still in the dark.
double-checked the configs as well. Any help available out there?


Posted by uunet on October 08, 2006 at 3:52:07 pm EEST

"DHCP server:
#
# cd /usr/ports/net/ics-dhcp3-server
make install clean (the default options are fine)"

Line 3 should read '/usr/ports/net/isc-dhcp3-server'.

http://www.isc.org/sw/dhcp/


Posted by uunet on October 08, 2006 at 6:19:06 pm EEST

One can use the DHCP server also to pass DNS servers to clients if using BIND is not the first option.  

# vi /usr/local/etc/dhcpd.conf

option domain-name-servers 4.2.2.5, 4.2.2.1;

/usr/local/etc/dhcpd.conf: unmodified: line 1


Posted by zyxep on October 08, 2006 at 8:12:10 pm EEST

now i have tried to change dhcpd.conf with option domain-name-servers to my dns servers from my ISP.

i get ip,dns and gateway from the dhcp server on my server but i can't look on any website..


Posted by jdferrell on October 11, 2006 at 5:17:25 am EEST

zyxep, can you ping an IP address outside your network to verify your connection?  If you can ping, try hard coding your ISP's DNS server on the client side.


Posted by zyxep on October 11, 2006 at 9:24:20 am EEST

jdferrell, nop i can't..

i can get ip,gateway,dns all of the things but i can't connect to the internet :S

but ... yesterday i tried with another guide from another forum.. and its work :D

now i only need to configure my firewall :)


Posted by shaolin on October 13, 2007 at 5:50:33 pm EEST

hey wondering if anyone could point me in the right direction, followed the guide and everything seems to be working fine but im getting this error, only noticed it since i installed webmin and it said my dhcp server was down.

No subnet declaration for rl0 (121.208.131.50).
** Ignoring requests on rl0.  If this is not what
   you want, please write a subnet declaration
   in your dhcpd.conf file for the network segment
   to which interface rl0 is attached. **

thanks in advance!! loving learning BSD so far!!


Posted by jdferrell on October 15, 2007 at 6:16:57 am EEST

Shaolin,

rl0 (121.208.131.50) looks like your external interface and ISP's subnet?  If that is the case, double check your dhcpd.conf to verify that dhcpd is only configured to listen on your internal interface:

dhcpd_ifaces="xl0" # enabled on int. interface

John


Posted by shaolin on October 15, 2007 at 7:46:29 am EEST

heres what i have in my rc.conf my internal device is definately specified

### Netowrking
# NIC is configured by our ISP's DHCP server
ifconfig_rl0="DHCP"
# internal NIC has static IP
ifconfig_xl0="inet 192.168.0.1 netmask 255.255.255.0"
hostname="Snake.qld.bigpond.net.au"

### DHCP server
dhcpd_enable="yes"
dhcpd_ifaces="xl0" # enables on int. interface

is it possible that webmin is trying to execute a second server?
when i (ps aux | grep -i dhcp) i get

_dhcp     308  0.0  0.6  1468   940  ??  Ss   12:31AM   0:00.96 dhclient: rl0 (
_dhcp     722  0.0  0.6  1468   928  ??  Ss   12:31AM   0:01.15 dhclient: rl0 (
dhcpd     963  0.0  1.1  2192  1680  ??  Is   12:32AM   0:00.09 /usr/local/sbin
root     3722  0.0  0.4  1544   636  p0  R+    2:07PM   0:00.01 grep -i dhcp


Posted by jdferrell on October 15, 2007 at 10:54:40 pm EEST

Shaolin,

It looks like only one instance of dhcpd is running, although you may be right, webmin may be doing something strange.  Here is my ps output for dhcpd:

dhcpd   978  0.0  1.3  2180  1576  ??  Is   21Sep07   0:00.34 /usr/local/sbin/dhcpd -cf /usr/local/etc/dhcpd.conf -lf /var/db/dhcpd/dhcpd.leases -pf /var/run/dhcpd/dhcpd.pid -use

Is dhcpd using the config files you edited or maybe a different config file?  (Running out of ideas)

John


Posted by shaolin on February 01, 2008 at 7:13:09 am EET

any ideas how I would go about opening ports in pf.conf for bittorrent?
thanks for your help in the past jdferrell!


Posted by jdferrell on February 18, 2008 at 6:12:58 pm EET

Shaolin,

Not sure if you already found the answer, but...

Use a macro to specify your bittorrent port(s):

bittorrent = "{ 6881, 6882 }"

Then create a rule to allow the bittorrent macro:

pass in on $ext_if inet proto tcp from any to ($ext_if) port $bittorrent flags S/SA keep state

You may have to experiment some with the port ranges.

John


Copyright 2003 - 2008 BSD Guides.  All rights reserved.

About | Terms of Use | Privacy | Contact