Setting up a DNS and DHCP server in a FreeBSD jail

The software

BIND for DNS – the most widely used DNS software on the internet, developed at Berkeley.

ICS-DHCP for DHCP – A DHCP server implementation by the Internet Systems Consortium.

The Plan

Configure two highly available BIND DNS servers in jails running on separate physical hosts and two highly available DHCP servers that will dynamically update our DNS servers. I’m going to split this over two posts, in this one we’re going to get our DNS and DHCP servers working inside our jail.


For each jail you want to use (i’m going to use 2, each with a DNS and DHCP server) you’re going to need to do some prep.

Create a loopback interface:

# service netif cloneup lo1

Add the loopback interface to your rc.conf:

# echo 'cloned_interfaces="${cloned_interfaces} lo1"' >> /­etc/rc.conf

Now create a IP alias to use:

# ifconfig bge1 alias netmask broadcast

Add your IP alias to your rc.conf:

# echo 'ifconfig_bge1_alias="inet netmask broadcast"' >> /­etc/rc.conf

Next up let’s create our jail using ezjail, specifying the IP alias and loopback device we created earlier:

# ezjail-admin create Ares 'bge1|,lo1|'

Don’t start it up just yet! We have more config to do first. Copy your resolv.conf into your jail so it can use your current DNS server to resolve the BIND and DHCPD software downloads:

# cp /­etc/resolv.conf /usr/jails/Ares/­etc/

Now let’s edit our jails hosts file to reflect our different loopback address – keep your localhost entry but add the FQDN name in:

# vi /usr/jails/Ares/­etc/hosts
::1 localhost localhost

Next up we need to allow access to the BPF (Berkeley packet filter device) this allows the DHCP server to avoid copying uninteresting packets from the kernel to the software running in user mode. Let’s create a devfs rule:

# vi /­etc/devfs.rules
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path 'bpf*' unhide

Now we can add the rule into our jails config file:

# echo 'export jail_Ares_devfs_ruleset="6"' >> /usr/local/­etc/ezjail/Ares

Next up we’re going to need to allow our jail access to raw sockets for the DHCP server to capture broadcast requests. Add the rule to your jail config file:

# echo 'export jail_Ares_parameters="allow.raw_sockets allow.sysvipc' >> /usr/local/­etc/ezjail/Ares

Now bring up the loopback device you created earlier:

# service netif cloneup lo1

Start up your Jail and log in:

# ezjail-admin start Ares
# ezjail-admin console Ares

Now you’re in your prepped up jail, install BIND (remove -DBATCH if you want to set any specific compile options):

# cd /usr/ports/dns/bind99/
# make install -DBATCH

Now let’s edit the config file at /usr/local/­etc/namedb/named.conf:

Set a ACL right at the top of the config file so we only accept requests from hosts on our subnet:

acl "trusted" {;

Next section to edit is the forwarders, I just go with Google’s DNS servers, make sure you limit these using the ACL we set up earlier:

forwarders {;;
allow-query       { any; };
allow-recursion   { trusted; };
allow-query-cache { trusted; };

Thats it for a very basic usable config – enable BIND in the rc.conf file:

# echo 'named_enable="YES"'>> /­etc/rc.conf

Start BIND and test it on your favourite web server (set the IP to your new DNS server):

# service named start
# dig @

The line you are looking for is:

;; Got answer:

So you have a working DNS server, now we’re going to get a working DHCP server and then next post we’ll make it play together and get it failing over. Let’s get compiling:

# cd /usr/ports/net/isc-dhcp43-server
# make install -DBATCH

Luckily there’s not much config needed for a simple set up, the file is in /usr/local/­etc/dhcpd.conf here is mine, I think it’s all pretty self-explanatory:

default-lease-time 600;
max-lease-time 7200;
# Enable this only option only if it's your only DHCP server:
option subnet-mask;
option broadcast-address;
option routers;
option domain-name-servers,;
option domain-name "";
subnet netmask {


Now let’s enable DHCPD and some options in our /­etc/rc.conf file:


Now fire it up with:

# service ics-dhcpd start

Now we need to try it out, before you do let’s get tcpdump running:

# tcpdump -envvvi bge1 port 67 or port 68

That should show you all the DHCP traffic on the network, at this point I grabbed my iPhone and renewed the DHCP lease, you can should see the request and the response in your tcpdump. Here’s my iPhone asking for a DHCP lease:

20:57:17.896014 9c:f3:87:37:ec:bd > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 342: (tos 0x0, ttl 255, id 3969, offset 0, flags [none], proto UDP (17), length 328) > [udp sum ok] BOOTP/DHCP, Request from 9c:f3:87:37:ec:bd, length 300, xid 0x564e0c0e, Flags [none] (0x0000)
Client-Ethernet-Address 9c:f3:87:37:ec:bd
Vendor-rfc1048 Extensions
Magic Cookie 0x63825363
DHCP-Message Option 53, length 1: Request
Parameter-Request Option 55, length 6:
Subnet-Mask, Default-Gateway, Domain-Name-Server, Domain-Name
Option 119, Option 252
MSZ Option 57, length 2: 1500
Client-ID Option 61, length 7: ether 9c:f3:87:37:ec:bd
Requested-IP Option 50, length 4:
Lease-Time Option 51, length 4: 7776000
Hostname Option 12, length 15: "GuyTabrrsiPhone"
END Option 255, length 0
PAD Option 0, length 0, occurs 6

Here’s my DHCP server responding:

20:57:17.896215 3c:07:54:03:57:f0 > 9c:f3:87:37:ec:bd, ethertype IPv4 (0x0800), length 342: (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328) > [udp sum ok] BOOTP/DHCP, Reply, length 300, xid 0x564e0c0e, Flags [none] (0x0000)
Client-Ethernet-Address 9c:f3:87:37:ec:bd
Vendor-rfc1048 Extensions
Magic Cookie 0x63825363
DHCP-Message Option 53, length 1: ACK
Server-ID Option 54, length 4:
Lease-Time Option 51, length 4: 6688
Subnet-Mask Option 1, length 4:
Default-Gateway Option 3, length 4:
Domain-Name-Server Option 6, length 8:,
Domain-Name Option 15, length 8: ""
END Option 255, length 0
PAD Option 0, length 0, occurs 12

Published by

Guy Tabrar

*NIX Admin.