Dual-Stack Router with Ubuntu 20.04 LTS

Motivation

My primary motivation was to have a capable router that supports 25 gigabit (up & down) internet access. Big thanks to my local provide Init7. It’s not because I need it, it’s because I can. 😉 Specially as a network engineer I’m interested in new technologies.

So which router is able to serve 25G throughput? I’ve not found one that is payable for private use and makes not a lot of noise and power costs.

I’d like to thank Marcel M. as well for his support to make the initial setup.

Hardware

I’ve chosen the following componets:

  • Case and Mainboard: Supermicro 5029S-TN2
  • CPU: Intel Core i5 6600K BOX (LGA 1151, 3.50GHz, 4-Core)
  • FAN: Noctua NH-L9i
  • RAM: HyperX Impact 32 GB RAM (2x 16GB)
  • Disk: Samsung 970 Evo Plus 1TB NVME
  • NIC: Lenovo ThinkSystem QLogic QL41262 (PCI-E x8)

Interesting thing is that the NIC shows capabilities for 40 & 50G too:

01:00.0 Ethernet controller: QLogic Corp. FastLinQ QL41000 Series 10/25/40/50GbE Controller (rev 02)
01:00.1 Ethernet controller: QLogic Corp. FastLinQ QL41000 Series 10/25/40/50GbE Controller (rev 02)

Basic software installation

A lot of tools are used for a router:

apt install iftop vnstat ncdu iotop iperf3 igmpproxy isc-dhcp-server htop dfc radvd strongswan wide-dhcpv6-client fail2ban ethtool bind9utils vim

and remove a ugly text editor:

apt --purge remove nano

Interface config with netplan

network:
  version: 2
  renderer: networkd
  ethernets:
    enp1s0f0:
      dhcp4: true
      critical: true #instead of workaround with networkd
      dhcp6: false
      accept-ra: true
      link-local: [ipv6]
      nameservers:
        search: [example.net]
        addresses:
          - "1.1.1.1"
          - "1.0.0.1"
          - "2606:4700:4700::1001"
          - "2606:4700:4700::1111"
    enp1s0f1:
      dhcp4: false
      accept-ra: false
      link-local: []

  vlans:
    wifi:
      accept-ra: false
      id: 666
      link: enp1s0f1
      addresses: ["10.66.66.1/24", "fe80::1/64"]
    mgmt:
      accept-ra: false
      id: 660
      link: enp1s0f1
      addresses: ["10.66.60.1/24", "fe80::1/64"]
    lan:
      accept-ra: false
      id: 661
      link: enp1s0f1
      addresses: ["10.66.61.1/24", "fe80::1/64"]
    dmz-wan:
      accept-ra: false
      id: 699
      link: enp1s0f1
      addresses: ["172.16.66.1/24", "fe80::1/64"]

I’ve used CloudFlare DNS because there is no faster available today. Netplan documentation can found here.

To keep the DHCPv4 lease with netplan, it’s necessary to configure the following in “/etc/systemd/network/default.network”

[Match]
Name=enp1s0f0

[Network]
DHCP=v4
KeepConfiguration=dhcp

[DHCPv4]
UseHostname=false
SendRelease=false
SendHostname=false

More info here.

Update: instead of using this ugly workaround with systemd-networkd, you just could you use netplan “critical”, which is only supported with networkd, but not with NetworkManager.
Source: https://netplan.readthedocs.io/en/stable/netplan-yaml/

DHCPv6PD with wide-dhcpv6-client

here my config file (/etc/wide-dhcpv6/dhcp6c.conf):

# Default dhpc6c configuration: it assumes the address is autoconfigured using
# router advertisements.

profile default
{
  information-only;

  request domain-name-servers;
  request domain-name;

  script "/etc/wide-dhcpv6/dhcp6c-script";
};

interface enp1s0f0 {
	send ia-pd 0;
	#request rapid-commit;
};

id-assoc pd {
	prefix 2001:db8:acdc::/48 3000 4000;
	prefix-interface dmz-wan {
		sla-id 0;
		ifid 1;
	};
	prefix-interface lan {
		sla-id 1;
		ifid 1;
	};
	prefix-interface mgmt {
		sla-id 2;
		ifid 1;
	};
	prefix-interface wifi {
		sla-id 3;
		ifid 1;
	};
};

The interface “enp1s0f0” is my outside facing one.

“sla-id” defines the “interfix” between prefix and host part, 0 means 2001:db8:acdc:0000::1.

“ifid 1” defines the end resp. host partition of the address: “::1”

Bonus information: I had to make a prefix hint, otherwise I got every time a new prefex, when the router gets rebooted or even worst, when the service and/or interface was restarted.

Thanks to Marcel for the hint to use that trick.

Update: this workaround is not anymore needed because Init7 has fixed there DHCPv6 issues and by now you always get your static /48 IPv6 prefix via DHCPv6PD.

DHCP with isc-dhcp-server(6)

modify “/etc/dhcp/dhcpd.conf” for vintage IP:

option domain-name "example.net";
option domain-name-servers 10.66.66.66;
option ntp-servers 10.66.66.66;

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

subnet 10.66.66.0 netmask 255.255.255.0 {
	range 10.66.66.66 10.66.66.166;
	option routers 10.66.66.1;
}

and for IP in actual version “/etc/dhcp/dhcpd6.conf”:

# Server configuration file example for DHCPv6
# From the file used for TAHI tests - addresses chosen
# to match TAHI rather than example block.

# IPv6 address valid lifetime
#  (at the end the address is no longer usable by the client)
#  (set to 30 days, the usual IPv6 default)
default-lease-time 2592000;

# IPv6 address preferred lifetime
#  (at the end the address is deprecated, i.e., the client should use
#   other addresses for new connections)
#  (set to 7 days, the	usual IPv6 default)
preferred-lifetime 604800;

# T1, the delay before Renew
#  (default is 1/2 preferred lifetime)
#  (set to 1 hour)
option dhcp-renewal-time 3600;

# T2, the delay before Rebind (if Renews failed)
#  (default is 3/4 preferred lifetime)
#  (set to 2 hours)
option dhcp-rebinding-time 7200;

# Enable RFC 5007 support (same than for DHCPv4)
allow leasequery;

# Global definitions for name server address(es) and domain search list
option dhcp6.name-servers 2001:db8:6666:66::6;
option dhcp6.domain-search "example.net";

# Set preference to 255 (maximum) in order to avoid waiting for
# additional servers when there is only one
##option dhcp6.preference 255;
option dhcp6.preference 255;

# Server side command to enable rapid-commit (2 packet exchange)
##option dhcp6.rapid-commit;

# The delay before information-request refresh
#  (minimum is 10 minutes, maximum one day, default is to not refresh)
#  (set to 6 hours)
option dhcp6.info-refresh-time 21600;


subnet6 2001:db8:6666:66::/64 {
	range6 2001:db8:6666:66::d:0 2001:db8:6666:66::d:ffff;
}

Route Advertisements with RADVD

To provide “unknown” prefixes from DHCPv6PD, use the trick with “::/64” in prefix, file “/etc/radvd.conf”:

interface lan
{
	AdvSendAdvert on;
	AdvManagedFlag on;
	AdvOtherConfigFlag on;
	prefix ::/64
	{
		AdvOnLink on;
		AdvAutonomous on;
		AdvRouterAddr on;
		AdvValidLifetime 7200;
		AdvPreferredLifetime 3600;
	};
};
interface mgmt
{
	AdvSendAdvert on;
	AdvManagedFlag on;
	AdvOtherConfigFlag on;
	prefix ::/64
	{
		AdvOnLink on;
		AdvAutonomous on;
		AdvValidLifetime 7200;
		AdvPreferredLifetime 3600;
	};
};
interface wifi
{
	AdvSendAdvert on;
	AdvManagedFlag on;
	AdvOtherConfigFlag on;
	prefix ::/64
	{
		AdvOnLink on;
		AdvAutonomous on;
		AdvValidLifetime 7200;
		AdvPreferredLifetime 3600;
	};
};
interface dmz-wan
{
	AdvSendAdvert on;
	AdvManagedFlag on;
	AdvOtherConfigFlag on;
	prefix ::/64
	{
		AdvOnLink on;
		AdvAutonomous on;
		AdvValidLifetime 7200;
		AdvPreferredLifetime 3600;
	};
};

UFW as Firewall

Allow everything from interface “lan” to interface “enp1s0f0”:

ufw route allow in on lan out on enp1s0f0

to check, how UFW is configured, use the following command:

# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)

it shows that incoming traffic is denied by default and outgoing is allowed. Important to know, that routed is also denied by default.

More infos about ufw firewalling here. I tried just to give you an idea how to make your firewall ruleset. 😉

For legacy IP you need to setup NAT, doing this in /etc/ufw/before.rules:

#to avoid multiple entries, flush the table first:
-F POSTROUTING
#do not masquerade everything from 10/8 to 10/8
-A POSTROUTING -o enp1s0f0 -s 10.0.0.0/8 -d 10.0.0.0/8 -j ACCEPT
#masquerade everything that goes to that interface:
-A POSTROUTING -o enp1s0f0 -j MASQUERADE

If you need some port-forwardings, doing this also in /etc/ufw/before.rules

#avoid multiple entries, flush the table first:
-F PREROUTING
# incoming TCP traffic on interface "enp1s0f0" to port 32400 will be translated to 10.66.66.66
-A PREROUTING -i enp1s0f0 -p tcp --dport 32400 -j DNAT --to-destination 10.66.66.66

IGMP Proxy for TV7

for TV7 from Init7, use the following “/etc/igmpproxy.conf” file:

phyint enp1s0f0 upstream ratelimit 0 threshold 1
altnet 77.109.129.0/25

phyint lan downstream ratelimit 0 threshold 1
altnet 10.66.66.0/24

“enp1s0f0” is the my provider “outside” facing interface and I’ll serve Multicast TV on my “lan” interface.

So we need to open the firewall to allow multicast streams:

ufw route allow in on enp1s0f0 out on lan proto udp from 77.109.129.0/25 port 5000 to 239.77.0.0/21 port 5000

for IGMP Proxy are some special UFW Rules needed, specially in “/etc/ufw/before.rules”:

-A ufw-before-input -p igmp -d 224.0.0.0/4 -j ACCEPT

DDNS Script

if you need remote access or do you want to provide services from your own infrastructure, here’s an example script to update the DNS.

#!/bin/sh

# Fully Qualified Domain Name, ending with a dot
fqdn='home.example.net.'

# nsupdate key file
keyf='/opt/scripts/.nskey'

#interface
dev='enp1s0f0'

# DO NOT CHANGE BELOW THIS LINE
# get IPs from interface and remove CDR subnet notation
ip4=`ip a s dev $dev | awk '/inet / {print $2}' | sed -r 's/(\/[0-9]{1,2})'//`
ip6=`ip a s dev $dev | awk '/inet6 2/ {print $2}' | sed -r 's/(\/[0-9]{1,3})'//`

# get IPs from DNS
dns4=`host $fqdn | grep 'has address' | awk '{print $4}'`
dns6=`host $fqdn | grep 'has IPv6 address' | awk '{print $5}'`

# DNS legacy IP update
if [ $dns4 != $ip4 ]; then
 echo "update delete $fqdn A
update add $fqdn 300 A $ip4
send
quit" | nsupdate -k $keyf
fi

# DNS IPv6 update
if [ $dns6 != $ip6 ]; then
 echo "update delete $fqdn AAAA
update add $fqdn 300 AAAA $ip6
send
quit" | nsupdate -k $keyf
fi

Special thanks to Adrian B. for your inputs.

Speedtest

# speedtest

   Speedtest by Ookla

     Server: Init7 AG - Winterthur (id = 43030)
        ISP: Init7 (Switzerland) Ltd.
    Latency:     0.64 ms   (0.05 ms jitter)
   Download: 23440.14 Mbps (data used: 10.6 GB)
     Upload: 22780.67 Mbps (data used: 11.4 GB)
Packet Loss:     0.0%
 Result URL: https://www.speedtest.net/result/c/515d9bf5-2c10-4555-90ef-18e1144399a1

CLI Tool can be found here.

vnstat

# vnstat -i enp1s0f0
Database updated: 2021-09-03 15:50:00

   enp1s0f0 since 2021-07-18

          rx:  2.09 TiB      tx:  1.99 TiB      total:  4.08 TiB

   monthly
                     rx      |     tx      |    total    |   avg. rate
     ------------------------+-------------+-------------+---------------
       2021-08      1.50 TiB |    1.36 TiB |    2.87 TiB |    9.42 Mbit/s
       2021-09    139.58 GiB |  101.30 GiB |  240.88 GiB |    9.00 Mbit/s
     ------------------------+-------------+-------------+---------------
     estimated      1.54 TiB |    1.12 TiB |    2.65 TiB |

   daily
                     rx      |     tx      |    total    |   avg. rate
     ------------------------+-------------+-------------+---------------
     yesterday     67.71 GiB |   37.10 GiB |  104.81 GiB |   10.42 Mbit/s
         today     35.67 GiB |   22.53 GiB |   58.19 GiB |    8.77 Mbit/s
     ------------------------+-------------+-------------+---------------
     estimated     54.06 GiB |   34.15 GiB |   88.21 GiB |

impressing data since 18th of July 2021. 🙂