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.
- Basic configuration
- Connecting to your WAN
- Interface configuration
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
home.net domain for our network and
router as the hostname of our
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
dhcpcdfor IPv6 prefix delegation
dnsmasqfor DHCP and DNS cache
ppp-pppoefor PPPoE connection
iptool for managing interfaces
openssh-serverfor remote administration
ethtool(optional) for monitoring the ethernet links
drill(optional) for hostname resolving
htop(optional) a better
lm_sensors(optional) for temperature monitoring
- and your favorite
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
/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
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
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
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
# 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
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.
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/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"
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
- You will need to assign an address on a different subnetwork
eth0is brought up it will trigger the creation of the
ppp0interface. The argument of this function (the part after the
ppp0=) must be the same as the filename of your peer file.
- 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
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
/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
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
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
eth1 will get
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
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
commit them. You can then proceed onto part 4 for the