Subnet with DHCP and DNS

2013-04-07 18:47 UTC
  • Xyne

About

I often use quickserve for ad-hoc file sharing, but getting a reliable connection through WLAN is not always possible. To save myself time and frustration I finally ended up writing some scripts to launch a matching ad-hoc DHCP server so that I can quickly and easily set up a direct connection to someone else's computer regardless of what OS they use. Once I had that working I figured I may as well learn how to set up a DNS server on the subnet and then forward an internet connection from one subnet to another. This is how I did it.

Throughout this guide I will refer to the network interface card facing the external router as nic1 and the network interface card facing the created subnet as nic2.

Some examples of what nic1 could be:

  • a wired adapter named eth0 that connects to a router
  • a wireless adapter named wlan0 that connects to a local wifi network
  • a tunnel adapter named tun0 that connects to a VPN and to which you wish to forward connections from the subnet you will create

Likewise, some examples of what nic2 could be:

  • a wired adapter named eth1 that connects to another computer directly or to a switch to which other computers are connected
  • a wireless adapter named wlan1 configured as a wireless access point to which other computers will connect
  • a tunnel adapter named tun1 connected to a VPN with other computers on it1

Of course, depending on the actual setup you may not need or want DHCP, DNS and forwarding. If you just want to connect to another computer directly with a cable on e.g. eth0 then you would only need the DHCP server, for example. It is left to the user to determine which parts of this guide are relevant in each case.

Network Topology Example

Here is a graph to visualize what this achieves in the simplest case:

svg image

Here nic1 might be wlan0 and nic2 might be eth0. The client might be the laptop of a friend to whom you don't want to give your super-secret WPA key, or with whom you want to share some large files quickly over a cabled connection. The client will have access to subnet 1 and thus the internet via your computer and you will be able to communicate directly with the client.

This setup is obviously unaffected by the addition of peers to subnet 1. To add more clients to subnet 2 a switch could be used if nic2 is a wired interface, for example. If it is a wireless access point then no additional hardware would be needed. Regardless of the physical connections, the configuration is the same and can be used for topologies such as the following example.

svg image

Packages

You will need the following packages:

DHCP server

A DHCP server will let different systems connect automatically to the subnet without manual network configuration.

DNS server

The DNS server is optional. Use one of the following:

idemptables

This is an optional wrapper for iptables to ensure that no duplicate rules are added.

hostapd

If you wish to create a wireless subnet then you will require a wireless access point on nic2.

Procedure

DHCP

The following is the dhcpd configuration file that I used:

default-lease-time 600;
max-lease-time 7200;
#authoritative;

option domain-name-servers 10.0.0.100;
option subnet-mask 255.255.255.0;
option routers 10.0.0.100;
subnet 10.0.0.0 netmask 255.255.255.0 {
  range 10.0.0.150 10.0.0.250;
}

This creates a subnet with address 10.0.0.0 and a DHCP and DNS server at 10.0.0.100. If you do not plan to run a local DNS server then change the domain-name-servers setting to a publically available DNS server (e.g. opendns), or grab one from /etc/resolv.conf.

DNS

Unbound

If you are running the Unbound DNS server then you will require the following lines, which should match the subnet IP address and mask below:

server:
  #...
  interface: 10.0.0.100
  access-control: 10.0.0.0/24 allow

See the Arch Linux Unbound wiki page for more information.

BIND

If you are running the Berkeley Internet Name Daemon (BIND), then the following settings are required in /etc/named.conf for the DNS server to resolve queries from this subnet:

options {
//...
listen-on { 10.0.0.100; };
allow-recursion { localnets; };

See the Arch Linux BIND wiki page for more information.

Firewall Settings

Now comes the tricky part if you have a restrictive firewall. If not, you can probably skip most of this except for enabling forwarding.

The following script will make the necessary changes to Netfilter (iptables) to get things running. Note that it makes some assumptions:

  • these rules do not overlap with existing rules
  • forwarding is disabled by default and should be disabled when the server is brought down

Also note that on my system I actually use idemptables, which is an iptables wrapper that checks for existing rules to avoid appending duplicates to a table and then checks that all matching rules have been removed when deleting them. This is not necessary but it ensures consistency.

launch.sh

#!/bin/bash

function print_usage()
{
  echo "usage: $0 <WAN interface> <subnet interface> <up|down>"
}

if [[ -z $3 ]]
then
  print_usage
  exit 1
else
  wan_nic="$1"
  subnet_nic="$2"
  action="$3"
fi


mask=/24
subnet_ip=10.0.0.0$mask
server_ip=10.0.0.100$mask
iptables=/usr/bin/idemptables
dhcpd_conf=dhcpd.conf
dhcpd_lease=/tmp/dhcpd.lease
dhcpd_pid=/tmp/dhcpd.pid

source launch_subnet.sh

launch_subnet "$action"

Invoke the script with the name of the interface that connects to the internet, the name of the interface that connects to the subnet, and either "up" or "down" to bring up or shut down the subnet, respectively.

The script expects the following files to be in the same directory:

  • launch_subnet.sh. The function contains comments to explain exactly what it is doing. Read them to understand how to set up everything manually. Also note that the function is simply a starting point and you should adapt it to suit your specific needs. See the highlighted code below.

  • dhcpd.conf (see above). Note that the IP addresses in the script must match those in the dhcpd.conf file.

The script also expects the local DNS server to be running.

launch_subnet.sh

You can find the source here.

#!/bin/bash


function print_launch_subnet_usage()
{
  echo "USAGE"
  echo "  $0 <up|down>"
  cat <<'CONFIG'

REQUIRED VARIABLES
  # The network interface card (NIC) that is connected to the internet or other
  # wide area network.
  wan_nic="wlan0"

  # The network interface card connected to the subnet.
  subnet_nic="eth0"

  # The subnet IP mask.
  mask=/24

  # The subnet IP range.
  subnet_ip=10.0.0.0$mask

  # The IP of the subnet NIC on the subnet.
  server_ip=10.0.0.100$mask

  # The IP tables binary to use.
  iptables=/usr/bin/idemptables

  # The dhcpd configuration, lease and PID files to use.
  dhcpd_conf=/etc/conf.d/dhcp
  dhcpd_lease=/tmp/dhcpd.lease
  dhcpd_pid=/tmp/dhcpd.pid

OPTIONAL VARIABLES
  # Function or external scripts to run before before and after bringing the
  # subnet NIC up or down: pre_up, post_up, pre_down, post_down

  # pre_up as a function:
  # function pre_up()
  # {
  # }

  # pre_up as a script:
  # pre_up=/path/to/script

  # ip_forward=0
  # The value of /proc/sys/net/ipv4/ip_forward to restore when shutting down
  # the subnet.
CONFIG
}

function launch_subnet()
{
  set -e

  if [[ -z $1 ]]
  then
    print_launch_subnet_usage
    exit 1
  else
    action="$1"
  fi

  if [[ -z $wan_nic ]]
  then
    echo "wan_nic is undefined"
    exit 1
  fi

  if [[ -z $subnet_nic ]]
  then
    echo "subnet_nic is undefined"
    exit 1
  fi

  if [[ -z $mask ]]
  then
    echo "mask is undefined"
    exit 1
  fi

  if [[ -z $subnet_ip ]]
  then
    echo "subnet_ip is undefined"
    exit 1
  fi

  if [[ -z $server_ip ]]
  then
    echo "server_ip is undefined"
    exit 1
  fi

  if [[ -z $iptables ]]
  then
    echo "iptables is undefined"
    exit 1
  fi

  if [[ -z $dhcpd_conf ]]
  then
    echo "dhcpd_conf is undefined"
    exit 1
  fi

  if [[ -z $dhcpd_lease ]]
  then
    echo "dhcpd_lease is undefined"
    exit 1
  fi

  if [[ -z $dhcpd_pid ]]
  then
    echo "dhcpd_pid is undefined"
    exit 1
  fi


  case "$action" in
    up)

      # Enable IP forwarding.
      echo 1 > /proc/sys/net/ipv4/ip_forward

      # Open up DNS (53) and DHCP (67) ports on subnet_nic.
      "$iptables" -A INPUT -i "$subnet_nic" -s "$subnet_ip" -p tcp --dport 53 -j ACCEPT
      "$iptables" -A INPUT -i "$subnet_nic" -s "$subnet_ip" -p udp --dport 53 -j ACCEPT
      "$iptables" -A INPUT -i "$subnet_nic" -p udp --dport 67 -j ACCEPT

      # Accept ICMP (ping) request packets so clients can check their connections.
      "$iptables" -A INPUT -i "$subnet_nic" -p icmp --icmp-type echo-request -j ACCEPT
      #"$iptables" -A OUTPUT -i "$subnet_nic" -p icmp --icmp-type echo-reply -j ACCEPT

      # Allow postrouting to wan_nic (for e.g. internet access on the subnet).
      "$iptables" -t nat -A POSTROUTING -s "$subnet_ip" -o "$wan_nic" -j MASQUERADE

      # Enable forwarding from subnet_nic to wan_nic (and back via related and established connections).
      "$iptables" -A FORWARD -i "$subnet_nic" -s "$subnet_ip" -o "$wan_nic" -j ACCEPT
      "$iptables" -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

      # Bring down subnet_nic, configure it and bring it up again.
      if [[ ! -z $pre_up ]]
      then
        ip link set dev "$subnet_nic" down
        "$pre_up"
      fi
      ip link set dev "$subnet_nic" up
      if [[ ! -z $post_up ]]
      then
        "$post_up"
      fi

      # Set the static IP for subnet_nic.
      ip addr add "$server_ip" dev "$subnet_nic"

      # Ensure the lease file exists.
      mkdir -p -- "${dhcpd_lease%/*}"
      [[ -f $dhcpd_lease ]] || touch "$dhcpd_lease"

      # Launch the DHCP server
      dhcpd -4 -q -cf "$dhcpd_conf" -lf "$dhcpd_lease" -pf "$dhcpd_pid" "$subnet_nic"
    ;;

    down)
      # Kill the DHCP server.
      if [[ -f $dhcpd_pid ]]
      then
        kill $(cat "$dhcpd_pid") && rm "$dhcpd_pid" && echo "killed server"
      fi

      if [[ ! -z $pre_down ]]
      then
        "$pre_down"
      fi
      ip link set dev "$subnet_nic" down
      if [[ ! -z $post_down ]]
      then
        "$post_down"
      fi

      # Undo all of the changes above in reverse order.
      "$iptables" -D FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
      "$iptables" -D FORWARD -i "$subnet_nic" -s "$subnet_ip" -o "$wan_nic" -j ACCEPT
      "$iptables" -t nat -D POSTROUTING -s "$subnet_ip" -o "$wan_nic" -j MASQUERADE
      #"$iptables" -D OUTPUT -i "$subnet_nic" -p icmp --icmp-type echo-reply -j ACCEPT
      "$iptables" -D INPUT -i "$subnet_nic" -p icmp --icmp-type echo-request -j ACCEPT
      "$iptables" -D INPUT -i "$subnet_nic" -p udp --dport 67 -j ACCEPT
      "$iptables" -D INPUT -i "$subnet_nic" -s "$subnet_ip" -p udp --dport 53 -j ACCEPT
      "$iptables" -D INPUT -i "$subnet_nic" -s "$subnet_ip" -p tcp --dport 53 -j ACCEPT


      if [[ ! -z $ip_forward ]]
      then
        if [[ $ip_forward != $(cat /proc/sys/net/ipv4/ip_forward) ]]
        then
          echo $ip_forward > /proc/sys/net/ipv4/ip_forward
        fi
      else
        echo 0 > /proc/sys/net/ipv4/ip_forward
      fi
    ;;

    *)
      print_launch_subnet_usage
      exit 1
    ;;
  esac
}

Launching The Server

With the previous script, bringing up the subnet is then as simple as

launch nic1 nic2 up

and stopping it as simple as

launch nic1 nic2 down

If you do not have a DNS server running on the local host by default then you can add the necessary systemctl commands to the end of the script above with e.g.

case "$action" in
  up)
    systemctl start ...
  ;;
  down)
    systemctl stop ...
  ;;
esac

where ... will be e.g. unbound.service or named.service.

That's it. You should now have a subnet with DHCP, DNS and internet forwarding through nic1.

If your subnet is wired, then enjoy your stable and incredibly fast ad-hoc file transfers and gaming.

Subnet Via Wireless Access Point

To create a wireless subnet you will need to configure nic2 as a wireless access point via hostapd.

Hostapd Configuration Example

The following is a hostapd configuration file that I have successfully used with 300 Mb/s D-Link wireless network card. It should provide a starting point to configure your own wireless access point in conjunction with the settings above. In this case "nic2" would be "wlan0" and "nic1" would be something else (e.g. "eth0").

interface=wlan0
driver=nl80211
eapol_version=2

ssid=Foonet
#ignore_broadcast_ssid=1
#bridge=br0

#auth_algs=1
wpa=2
wpa_passphrase=foopassphrase
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP CCMP
rsn_pairwise=CCMP

channel=11
# some laptops will only connect if the mode is set to b
#hw_mode=b
hw_mode=g

#wme_enabled=1
wmm_enabled=1
ieee80211n=1
# required for full speed
ht_capab=[HT40+][SHORT-GI-40][DSSS_CCK-40]

Errors

If you find errors on this page or something that could be improved, please send me an email. Of course, you can also send me an email just to let me know if you found this useful.


  1. If this is the case then you should not run a DHCP server unless it is the only one on the VPN, which in most cases means that it is your VPN.

Contact
echo xyne.archlinux.ca | sed 's/\./@/'
Feeds
Blog News
Validation
XHTML 1.0 Strict CSS level 3 Atom 1.0