The Router Project — Part 3: Router interface configuration
Feb 10, 2018
11 minute read

This is part 3 of the router series; part 1, part 2 and part 4 are also available

In this long overdue third part of this series we will demonstrate how to setup all the software components that comprise a modern router. You might want to refer back to part 1 for the network topology we are aiming to build. Bear in mind that this guide is based on an Alpine Linux setup but most of the points mentioned can be easily transferred to any Linux distribution. I will try to highlight the Alpine-specific bits.

Contents

Conventions

Before continuing you will need to decide which ethernet ports will be facing the internet and which the local one. In my case eth0 is facing the internet (or the modem) and eth1 the local network. We will also use the home.net domain for our network and router as the hostname of our gateway.

Basic configuration

Installing the required software

As illustrated in the first part we will need some software to make our linux setup functional. So go ahead and install (apk add) the following packages

  • dhcpcd for IPv6 prefix delegation
  • dnsmasq for DHCP and DNS cache
  • rp-pppoe and ppp-pppoe for PPPoE connection
  • iptables and ip6tables for firewalling
  • shorewall and shorewall6 for firewalling
  • chrony NTP server
  • iputils provides the ip tool for managing interfaces
  • openssh-server for remote administration
  • ethtool (optional) for monitoring the ethernet links
  • drill (optional) for hostname resolving
  • htop (optional) a better top
  • lm_sensors (optional) for temperature monitoring
  • and your favorite $EDITOR!

Required kernel modules

Alpine does not load all kernel modules that are commonly found in more heavyweight distributions so we need to ensure that the necessary modules are loaded. Edit /etc/modules and append the following

# For dhcp
af_packet
# For IPv6 support
ipv6
# For PPPoE
pppoe

You can obviously forego the modules that are not relevant to you. It doesn’t make much sense to load ipv6 on an IPv4-only network.

Configure hostname and /etc/hosts

Let’s assume you have set router as a hostname in /etc/hostname. You will additionally need to create a skeleton /etc/hosts. So edit or create the file and append

127.0.0.1   router  router.home.net

::1         router  ipv6-gateway ipv6-loopback
ff00::0     ipv6-localnet
ff00::0     ipv6-mcastprefix
ff00::1     ipv6-allnodes
ff00::2     ipv6-allrouters
ff00::3     ipv6-allhosts

Kernel parameters

In order to allow for packet forwarding this option will need to be enabled in your kernel parameters. To do so put the following in /etc/sysctl.conf

# Enable IPv4 forwarding
net.ipv4.ip_forward = 1

# Enable IPv6 forwarding across all interfaces
net.ipv6.conf.all.forwarding = 2
net.ipv6.conf.default.forwarding = 2

# Allow router advertisements through all interfaces
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.default.accept_ra = 2

This is the absolute minimum of sysctl parameters that need to be enabled for the device to have router functionality. Most of the above should be self-explanatory except, maybe, for the 2 in router advertisements. Simply put router advertisements are used by the IPv6 stack to convey various configuration aspects to IPv6 clients such as the value and lifetime of the IPv6 prefix used through the network, MTU, hop limit, etc. They are necessary in an IPv6 network for the clients to discover the router and autoconfigure themselves. By default the Linux kernel disables router advertisements if forwarding is enabled (the 1 value). However 2 will force the acceptance of advertisement even if forwarding is enabled. There are a lot of different sysctl parameters to tune to further enhance the security of the router but I elected not to go into them at this point in order not to overcomplicate things and to allow for fewer points of failure if the whole thing is not working as expected. Once you have a basic network up and running you can experiment at your leisure.

Connecting to your WAN

Prepare your xDSL or Cable modem

This guide will assume that you are using PPPoE to connect to your ISP as this is the case for most residential ISPs in Europe. If you got a router from your ISP you will need to set it up in bridge mode. As this is modem/router dependent so you will have to consult the manual of your device on how to do that. After doing so the router capabilities of your equipment will be disabled as only the modem functionality will be used. For those using PPPoA things are more complicated as you will need a PPPoA-to-PPPoE client modem.

At this point network connectivity among your network devices will be lost so you will need to login to your router directly either using a monitor and keyboard or the serial port in the APU2 case.

PPP configuration

There are two relevant configuration files for rp-pppoe. The first is your peer file and it is configured on per ISP basis. So create a new file for your ISP in /etc/ppp/peers and ensure that readable only by root

# touch /etc/ppp/peers/ISP && chmod 600 /etc/ppp/peers/ISP

A basic peer file is provided below. I’ve added comments on the bits you will need to tweak.

# If you need to debug your connection remove this line
nolog
noipdefault

# make the new ppp interface the default external route
defaultroute
defaultroute-metric 300

# detach pppd when connection is established
updetach
# replace eth0 with the interface that is connected to your modem
plugin rp-pppoe.so eth0

# do not log passwords
hide-password

lcp-echo-interval 20
lcp-echo-failure 3

noauth

# as this is a xDSL connection make sure that interface is always up
persist
# this is the number of failed attempts before the PPP daemon gives up on
# connecting to your ISP. 0 means keep trying in perpetuity
maxfail 0

# replace this with the username provided by your ISP
user "user000@ISP"

# compression
bsdcomp 15
deflate 15

# IPv6; if your ISP supports it
+ipv6 ipv6cp-use-ipaddr
# this is not a typo; it is literally "ipv6", space and comma
ipv6 ,

You might notice there is no authentication information on the peer file. This is configured in the secrets file. Depending on whether your ISP uses PAP or CHAP for the authentication you will need to add the following to /etc/ppp/pap-secrets or /etc/ppp/chap-secrets respectively. As PAP is inherently insecure virtually all ISPs use CHAP for authentication.

# do not forget the quotes
# client        server  secret      IP addresses
"user000@ISP"   *       "passw0rd"

Interface configuration

We are now in a point that we can setup the actual network interfaces. In Alpine this configuration lives in /etc/network/interfaces pretty much as it is in Debian-based distributions. There are a series of stanzas that define the IPv4 and IPv6 behaviour of the listed interfaces. Any network cards not mentioned will remain unconfigured (ie. down). In our case we will configure two physical interface, the loopback and the PPP interface. Of course if your connection to your ISP is not PPP you will need to forgo the relevant stanza.


# Loopback interface; nothing complex here
auto lo
iface lo inet loopback
  address 127.0.0.1
  netmask 255.0.0.0

iface eth0 inet static
  address 192.168.0.2
  netmask 255.255.255.0
  broadcast 192.168.0.255
  pre-up /sbin/ip link set eth0 up
  up ifup -f ppp0=ISP
  down ifdown -f ppp0=ISP
  post-down /sbin/ip link set eth0 down

iface eth1 inet static
  address 192.168.1.1
  netmask 255.255.255.0
  broadcast 192.168.1.255

iface eth1 inet6 static
  address 2001:db8::1
  netmask 64

Points of interest

In the configuration file above eth0 is the interface connected to the modem, as per our convention, the same as the one mentioned in the PPP peer file. Although, physically, it is the external interface its role will be superseded by the ppp interface so its only purpose at the moment is to trigger the activation of the ppp client (the up & down parts). There are three caveats here:

  1. You will need to assign an address on a different subnetwork
  2. Once eth0 is brought up it will trigger the creation of the ppp0 interface. The argument of this function (the part after the ppp0=) must be the same as the filename of your peer file.
  3. If you are not using PPP then this is your real external interface and you will need to assign its address in a fashion that your ISP mandates. If this is a direct ethernet link you will need to either assign it the address your ISP gave you or set it to dhcp.

Further down eth1 is the LAN-facing interface. This defines the address and subnet mask of our local (IPv4) network. Nothing complicated here just choose a private subnet of your preference or if you are lucky enough to have IPs to spare a public one.

Although IPv4 is quite straightforward, the IPv6 assignment merits a little more thorough discussion. In the case above I have assigned a static IPv6 prefix that my ISP gave me. If your prefix is dynamic you will need to omit the whole section (we will use DHCP later on) or add a Unique Local Address which is an address from the fc::/7 block. Use this page to generate a subnet. Please note that fc::/7 address are not routable from the open internet and are homologous to the private IPv4 addresses but it will allow you to have your IPv6 network up even if connectivity with the outside world is lost.

IPv6 Prefix delegation

No matter if you have a static or dynamic IPv6 address your ISP will most probably assign it to you through a process called DHCPv6 Prefix Delegation (DHCPv6-PD). DHCPv6-PD is a process through which a delegating router (the one on your ISP side) is assigning and IPv6 prefix to a requesting router (your router) in a fashion similar to how DHCP works for local networks. If you have a static IPv6 prefix you can technically hardcode it and waive the whole process but DHCPv6-PD has the added benefit of providing your router with the necessary routes as well as the default gateway automagically. As an interface can have multiple addresses DHCPv6-PD will not replace the existing IPv6 addresses from the requesting interface but add to them.

There are four different tools that can do DHCPv6-PD. Dibbler, WIDE-DHCPv6, ISC dhclient and dhcpcd. All work as expected but I have opted for dhcpcd. ISC dhclient can’t do DHCPv6-PD over PPP links, WIDE-DHCPv6 is not really maintained and between Dibbler and dhcpcd I went with the most universally accepted option (dhcpcd is literally everywhere); plus it gets very frequent updates.

To enable prefix delegation you will need an instance of dhcpcd running. In my case I only use dhcpcd on my router for PD so I went ahead and edited the main configuration file: /etc/dhcpcd.conf. However if you need a DHCP client for another reason then it’s probably better to use a separate instance of dhcpcd for IPv6 only. I have found this option to be simpler but if you feel adventurous go ahead and try with a simple configuration file. In any case you will need the following absolute minimum configuration to get PD working.

duid
ipv6only
noipv6rs
denyinterfaces eth0

nohook lookup-hostname
nohook resolv.conf
nogateway

interface ppp0
  ipv6rs
  iaid 1
  ia_pd 1/::/64 eth1/0/64

I agree that this is indeed quite cryptic. duid generates a unique identifier for the interface used. Then ipv6only and noipv6rs force dhcpcd into IPv6-only operation and disable router solicitations for the active interface. We then block dhcpcd from using eth0 (the interface connected to the modem) as we will use ppp0 to get our address. Of course, if you are not using PPP you might want to omit this. The following three lines disable some dhcpcd-intrinsic hooks as we don’t want dhcpcd to touch anything other than move the address from our external interface into our network.

The most important bit is the stanza that follows. In summary this will assign an IPv6 address to the eth1 interface (the LAN-facing) by delegating an address from the external interface. In this case is ppp0 but it can be an actual physical interface. For this particular interface we proceed to enable router solicitation (ipv6rs) and assign a unique identifier to it (the iaid bit). The actual delegation is done in the following line. What this line does is take whatever address (the :: bit) that the interface with iaid 1 (ie. ppp0) has and assigns an address to eth1 that with a /64 netmask. The 0 bit here means that the resultant prefix has the same prefix length as the delegation, practically assigning the whole delegated prefix to our network. If you have multiple routers in your network then you will probably need to increase this number to prevent allocation of addresses belonging to a different network. By default dhcpcd will assign the ::1 suffix to your delegated prefix to generate the address for the LAN-facing interface. So if the delegated prefix is 2001:db8::/64 eth1 will get 2001:db8::1 assigned. If you are not happy with that you can change the delegation to include an additional part as in eth1/0/64/0. The final part is the suffix you want eth1 to have. 0 is a special value and will assign an autoconfigured value (through SLAAC) to eth1.

This is the end of part 3. If you are following this guide with Alpine linux this is probably a good time to save your settings. So go ahead and lbu commit them. You can then proceed onto part 4 for the firewall configuration