format_list_bulleted

Linux Router: PPP, Ethernet, NAT, IPv6-PD, DHCP+DNS setup and WiFi Regulatory troubleshooting.

dark_mode
Linux Router: PPP, Ethernet, NAT, IPv6-PD, DHCP+DNS setup and WiFi Regulatory troubleshooting.
Sir, this route to service: PPPoE => IPv4 NAT & IPv6 SLAAC => 1000M Switch & WiFi AP & DNS
hostapd
ipv6
network
pppoe
router
wifi
visibility -- public -- group --

Oh, 8-core single board computer, so cute!

But I don’t want to flash OpenWRT on it. I want to make the most of its performance! So I chose other mainstream distribution.

In this tutorial, I use:

  • Linux computer with at least of 2 Ethernet ports
  • Modem from carrier at upstream
  • Ethernet switch at downstream
  • WiFi adapter
Program & ManualUsage
pppdPPPoE dialer
systemd-networkdNetwork configuration All-in-One
radvdIPv6 prefix delegation via RA
hostapdWiFi provider

PPPoE - Dial to the carrier

Ask your carrier for username and password for PPPoE dialing.

Set PON or ADSL modem to bridge mode and connect the 「modem」 and 「eth0 port on the router」 with an Ethernet cable.

Program pppd is the first choice used to dial to carrier. This is an out-of-the-box configuration below. Modify it with your need.

In my case, my carrier supports global-routable IPv6 prefix so I set +ipv6 up.

# /etc/ppp/peers/eth0

linkname eth0

updetach
plugin pppoe.so
nic-eth0

noauth
maxfail 1
persist
idle 0
lcp-echo-interval 15
lcp-echo-failure 2

+ipv6

user "t000"
password "123123"

For persistent connection, you may have to modify the service entry to let systemd restart the service after a failure.

Here is:

# /etc/systemd/system/[email protected]

[Unit]
Description=PPP link to %I
Before=network.target

[Service]
Type=notify
ExecStart=/usr/sbin/pppd call %I nodetach nolog up_sdnotify

# Restarting is required to reconnect after connection broken.
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target network-online.target
systemctl enable --now ppp@eth0

After the connection established, pppd will prints like this:

Connect: ppp0 <--> eth0
CHAP authentication succeeded: CHAP authentication success
CHAP authentication succeeded
peer from calling number XX:XX:XX:XX:XX:XX authorized
local  LL address fe80::XXXX:XXXX:XXXX:XXXX
remote LL address fe80::XXXX:XXXX:XXXX:XXXX
Started PPP link to eth0.
local  IP address XX.XX.XX.XX
remote IP address XX.XX.XX.XX

Default route

Set ppp0 as the default route of the entire IPv4 network after a successful dial.

ip route del default
ip route add default dev ppp0

[!WARNING]

Each time you restart systemd-networkd may cause change of routes. Make sure that ppp0 is always the default route.

Enable IP forwarding

# /etc/sysctl.d/01-net.conf

net.ipv4.conf.all.forwarding=1
net.ipv4.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1
sysctl --system

Manage your networks with systemd-networkd

Manage the ppp0 interface which from the carrier.

# /etc/systemd/network/ppp0.network

[Match]
Name=ppp0

[Network]
DHCP=yes
KeepConfiguration=static

[DHCPv6]
WithoutRA=solicit

Manage the interface br0 which facing the localhost and downstream.

# /etc/systemd/network/br0.network

[Match]
Name=br0

[Network]
Address=192.168.1.1/24
Address=fd00::1/64
DNS=192.168.1.1

DHCPv6PrefixDelegation=yes

# Enable NAT for IPv4
IPMasquerade=ipv4
systemctl restart systemd-networkd

You can see log like this:

ppp0: Link UP
ppp0: Gained carrier
br0: Gained IPv6LL
ppp0: Gained IPv6LL
ppp0: DHCP: received delegated prefix 2XXX:XXXX:XXXX:XXXX::/64
br0: DHCP-PD address 2XXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 (valid for 23h 59min 59s, preferred for 59min 59s)

Now check your addresses.

1: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether XX:XX:XX:XX:XX:XX brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global br0
       valid_lft forever preferred_lft forever
    inet6 2XXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 metric 256 scope global dynamic mngtmpaddr
       valid_lft 86400sec preferred_lft 3600sec
    inet6 fd00::1/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::XXXX:XXXX:XXXX:XXXX/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
2: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp
    inet XX.XX.XX.XX peer XX.XX.XX.XX/32 scope global ppp0
       valid_lft forever preferred_lft forever
    inet6 2XXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 86400sec preferred_lft 3600sec
    inet6 2XXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 scope global dynamic mngtmpaddr proto kernel_ra
       valid_lft 86400sec preferred_lft 3600sec
    inet6 fe80::XXXX:XXXX:XXXX:XXXX peer fe80::XXXX:XXXX:XXXX:XXXX/128 scope link nodad
       valid_lft forever preferred_lft forever

Multi-homed network

If your router connected more than one gateway, you may want some of them not to be the default one.

[DHCPv4]
UseGateway=no

[IPv6AcceptRA]
UseGateway=no

Just tell it do not use the gateway you don’t want them to route through.

IPv6 Router Advertisement - RAdvd

Your downstream devices requires RA packet to know what IPv6 prefix they have to use for SLAAC.

Here is an out-of-the-box configuration.

# /etc/radvd.conf

interface br0 {
    AdvSendAdvert on;

    MaxRtrAdvInterval 20;
    MinRtrAdvInterval 10;

    prefix ::/64 {
        Base6Interface br0;

        AdvOnLink on;
        AdvAutonomous on;

        AdvValidLifetime 30;
        AdvPreferredLifetime 30;
    };
};
systemctl enable --now radvd

Now check downstream addresses:

1: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether XX:XX:XX:XX:XX:XX brd ff:ff:ff:ff:ff:ff
    inet6 2XXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 30sec preferred_lft 30sec
    inet6 fe80::XXXX:XXXX:XXXX:XXXX/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever

DHCP & DNS

This is the last one step:

  • DHCPv4 server for IPv4 address dynamic allocation
  • DNS server for name resolving

There are some nice solutions for choosing!

Professional solutions

Program & ManualFeatures
dnsmasqDHCP and DNS server written in C
AdGuard HomeDHCP and DNS over UDP/TCP/TLS/QUIC/HTTPS/HTTP3 server with Web UI

systemd-networkd

It’s the simplest way if you don’t want extra software.

Append these options to your existing configuration.

[Network]
DHCPServer=yes

[DHCPServer]
DNS=8.8.8.8

# Address range
# 192.168.1.192/26
PoolOffset=192
PoolSize=63

# Valid time
MaxLeaseTimeSec=86400

Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD)

Install avahi and start it.

systemctl enable --now avahi-daemon

WiFi - HostAPd

You have to search on the web for the corresponding configuration for your WiFi adapter model.

Good configuration should have good description for options.

The options ends with _capab controls adapter features and will influence the adapter’s behavior.

If you want to optimize your WiFi network you must explore the features your adapter supports and fill them into _capab options.

iw list

And here is a configuration for RealTek RTL8852 model.

It’s a dual-band single-modem adapter, which means there can be only one frequency band working at the same time.

For dual-band dual-modem adapter you can see two interfaces wlan0 and wlan1 available. And each can be configured to correspond to different frequency bands.

In the case below, the interface is named as wlan0 and it’s set to 5GHz band.

# /etc/hostapd/hostapd-rtl8852bu.conf
# Documentation: https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf
# 2025-05-16
# Purpose: example hostapd.conf for rtl8852/32bu based USB WiFi adapters.
# Default: WiFi 6 on band 2 (5 GHz), 80 MHz channel width and WPA2.

# Tested with the following Linux vendor driver:
# https://github.com/morrownr/rtl8852bu-20240418
# AP mode requires the following module parameters
# options 8852bu rtw_he_enable=2 rtw_vht_enable=2
# If USB3 mode is desired, also add: rtw_switch_usb_mode=1
# see 8852cu.conf for documentation about the above settings.

# Information:
# - tested on RasPi4B with 64 bit RasPiOS
# - you need hostap 2.10 or later for WiFi 6 support: $ hostapd -v
# - suggested improvements to this document are welcome

# SSID
ssid=

# PASSPHRASE
wpa_passphrase=

# Band
# a = 5 GHz & 6 GHz (a/n/ac/ax), g = 2 Ghz (b/g/n)
hw_mode=a

# Channel
# https://en.wikipedia.org/wiki/List_of_WLAN_channels
# US:
# 2.4 GHz (1-11)
# 5 GHz ((36(42) and 149(155))
# 5 GHz DFS ((52(58), 100(106), 116(122), 132(138))
# 6 GHz (see op_class below)
# Channel (change as needed, only 1 channel line should be active)
# band 1 (2.4 GHz)
#channel=6
# band 2 (5 GHz)
channel=36
#channel=149
# band 4 (6 GHz)
#channel=65

# Operating Class
# Band 4 (6 GHz) only: The operating class is an indication of the 6 GHz
# channel size. Channels listed are based on US availability. For a
# complete list of channels:
# https://en.wikipedia.org/wiki/List_of_WLAN_channels#6_GHz_(802.11ax_and_802.11be)
#
# 131 signifies channel width: 20 MHz.
# Channel
# Lower: U-NII-5
# 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69,
# 73, 77, 81, 85, 89, 93
# Upper: U-NII-6, 7, 8
# 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149,
# 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, 193, 197, 201, 205,
# 209, 213, 217, 221, 225, 229, 233
# PCS Channel Numbers
# 5, 21, 37, 53, 69, 85, 101, 117, 133, 149, 165, 181, 197, 213, 229
#
# 132 signifies channel width: 40 MHz.
# Channel
# Lower: U-NII-5
# 1, 9, 17, 25, 33, 41, 49, 57, 65, 73, 81
# Upper: U-NII-6, 7, 8
# 99, 97, 105, 113, 121, 129, 137, 145, 153, 161, 169, 177, 185, 193,
# 201, 209, 217, 225
# PCS Channel Numbers
# 5, 21, 37, 53, 69, 85, 101, 117, 133, 149, 165, 181, 197, 213, 229
#
# 133 signifies channel width: 80 MHz.
# Channel (Center Frequency - he_oper_centr_freq_seg0_idx)
# Lower: U-NII-5
# 1(7), 17(23), 33(39), 49(55), 65(71), 81(87)
# Upper: U-NII-6, 7, 8
# 97(103), 113(119), 129(135), 145(151), 161(167), 177(183), 193(199),
# 209(215)
# PCS Channel Numbers
# 5, 21, 37, 53, 69, 85, 101, 117, 133, 149, 165, 181, 197, 213
#
# 134 signifies channel width: 160 MHz.
# Channel (Center Frequency - he_oper_centr_freq_seg0_idx)
# Lower: U-NII-5
# 1 (15), 33(47), 65(79)
# Upper: U-NII-6, 7, 8
# 97(111), 129(143), 161(175), 193(207)
# PCS Channel Numbers
# 5/21, 37/53, 69/85, 101/117, 133/149, 165/181, 197/213
#
# Activate a line below with desired setting for 6 GHz operation.
#op_class=131
#op_class=132
#op_class=133
#op_class=134

# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
country_code=
#
# This parameter is used to set the third octet of the country string.
# All environments of the current frequency band and country (default)
#country3=0x20
# Outdoor environment only
#country3=0x4f
# Indoor environment only
#country3=0x49
# Noncountry entity (country_code=XX)
#country3=0x58

# Advertises the country code, allowed channels and transmit power.
ieee80211d=1
# Enables support for 5 GHz DFS channels if supported.
# Requires ieee80211d=1 (above)
ieee80211h=1

# Interfaces
# Bridge interface
bridge=br0
wds_sta=1
# WiFi interface (wlan0 should be changed to your interface name)
interface=wlan0

# Set hostapd driver (nl80211 is used with all Linux mac80211 (in-kernel)
# and modern Realtek drivers)
driver=nl80211

# Event logger configuration
# Module bitfield (ORed bitfield of modules that will be logged; -1 =
#    all modules):
# bit 0 (1) = IEEE 802.11
# bit 1 (2) = IEEE 802.1X
# bit 2 (4) = RADIUS
# bit 3 (8) = WPA
# bit 4 (16) = driver interface
# bit 6 (64) = MLME
#
# Levels (minimum value for logged events):
#  0 = verbose debugging
#  1 = debugging
#  2 = informational messages
#  3 = notification
#  4 = warning
#
logger_syslog=-1
logger_syslog_level=2

# Control interface - (i.e. for hostapd_cli)
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0

# Various settings
ap_max_inactivity=300
beacon_int=100
dtim_period=2

# Security
# authentication algorithms (3 = both are allowed, use 3 for SAE otherwise use 1)
auth_algs=1
# Limit number of concurrent "clients" (stations) and do not restrict them by MAC address
max_num_sta=16
# Station MAC address based authentication
macaddr_acl=0
# hidden SSID (1 = hidden)
ignore_broadcast_ssid=0
# Opportunistic Key Caching (1 = enabled)
okc=1
# Enable WPA. (2 is required for WPA2, mixed and WPA3)
wpa=2
# Pairwise cipher
#wpa_pairwise=CCMP
rsn_pairwise=CCMP CCMP-256 GCMP GCMP-256
#
# Activate only one of the following 3 sections
#
# 1. WPA2 Personal
wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256
#
# 2. WPA2/WPA3 Transitional (mixed)
# Some older devices may not be compatible with WPA2/WPA3.
#wpa_key_mgmt=SAE WPA-PSK WPA-PSK-SHA256
#ieee80211w=1
#sae_require_mfp=1
#
# 3. WPA3 Personal (required for band 4, 6 GHz)
# Some older devices may not be compatible with WPA3.
#wpa_key_mgmt=SAE
#ieee80211w=2
#sae_pwe=2

# Wireless Multimedia Extensions (WME)
# The below line is mandatory for 6 GHz.
#wme_enabled=1
# Wi-Fi Multimedia (WMM)
# The below line is mandatory for WiFi 4 and above.
wmm_enabled=1

# IEEE 802.11n (WiFi 4) configuration
# This section is required when operating WiFi 6 on band 1, 2.4 GHz
ieee80211n=1
#
# Band 1: HT Capabilities: 19e3
# hw ht capab: 19e3
#ht_capab=[LDPC][HT40+][HT40-][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]
#
# Band 2: HT Capabilities: 19e3
# hw ht capab: 19e3
ht_capab=[LDPC][HT40+][HT40-][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]
#
#
# IEEE 802.11ac (WiFi 5) configuration
# When using band 1 (2.4 GHz) comment out the below line
ieee80211ac=1
# Channel width (0 = 40 MHz, 1 = 80 MHz, 2 = 160 MHz)
# When using band 1 (2.4 GHz) comment out the below line
vht_oper_chwidth=1
# VHT center channel = (chan + 2 for 40 MHz) (chan + 6 for 80 MHz) (chan + 14 for 160 MHz)
# When using band 1 (2.4 GHz) comment out the below line
vht_oper_centr_freq_seg0_idx=42
#
# Band 2: VHT Capabilities: 03c011b1
# hw vht capab: 03c011b1
vht_capab=[MAX-MPDU-7991][RXLDPC][SHORT-GI-80][TX-STBC-2BY1][SU-BEAMFORMEE][HTC-VHT]
#
#
# IEEE 802.11ax (WiFi 6) configuration
ieee80211ax=1
# Channel width (0 = 40 MHz, 1 = 80 Mhz, 2 = 160 MHz)
# For WiFi 6 on band 1 (2.4 GHz) comment out the below line
he_oper_chwidth=1
# HE center channel (chan + 2 for 40 MHz) (chan + 6 for 80 MHz) (chan + 14 for 160 MHz)
# For WiFi 6 on band 1 (2.4 GHz) comment out the below line
he_oper_centr_freq_seg0_idx=42
#
# Maximum MPDU Length of HE 6 GHz band capabilities.
# Indicates maximum MPDU length
# 0 = 3895 octets
# 1 = 7991 octets
# 2 = 11454 octets
he_6ghz_max_mpdu=0
#
# Maximum A-MPDU Length Exponent of HE 6 GHz band capabilities. Indicates
# the maximum length of A-MPDU pre-EOF padding that # the STA can receive.
# This field is an integer in the range of 0 to 7. The length defined by
# this field is equal to 2 pow(13 + Maximum A-MPDU Length Exponent) -1
# octets
# 0 = AMPDU length of 8k
# 1 = AMPDU length of 16k
# 2 = AMPDU length of 32k
# 3 = AMPDU length of 65k
# 4 = AMPDU length of 131k
# 5 = AMPDU length of 262k
# 6 = AMPDU length of 524k
# 7 = AMPDU length of 1048k
he_6ghz_max_ampdu_len_exp=2
#
# Rx Antenna Pattern Consistency of HE 6 GHz capability.
# Indicates the possibility of Rx antenna pattern change
# 0 = Rx antenna pattern might change during the lifetime of an association
# 1 = Rx antenna pattern does not change during the lifetime of an association
#     (default)
he_6ghz_rx_ant_pat=0
#
# Tx Antenna Pattern Consistency of HE 6 GHz capability.
# Indicates the possibility of Tx antenna pattern change
# 0 = Tx antenna pattern might change during the lifetime of an association
# 1 = Tx antenna pattern does not change during the lifetime of an association
#     (default)
#he_6ghz_tx_ant_pat=0
#
# 6 GHz Access Point type
# This config is to set the 6 GHz Access Point type. Possible options are:
# 0 = Indoor AP
# 1 = Standard power AP
# 2 = Very low power AP (default)
# 3 = Indoor enabled AP
# 4 = Indoor standard power AP
# This has no impact for operation on other bands.
#he_6ghz_reg_pwr_type=0
#
# used by clients to discern the source of interference
# each AP in your area needs to use a different number
# allowed: 1-63
he_bss_color=37
#
# Activate beamforming capabilities
#he_su_beamformee=1
#
# Basic NSS/MCS set
he_mu_edca_qos_info_param_count=0
he_mu_edca_qos_info_q_ack=0
he_mu_edca_qos_info_queue_request=0
he_mu_edca_qos_info_txop_request=0
he_mu_edca_ac_be_aifsn=8
he_mu_edca_ac_be_aci=0
he_mu_edca_ac_be_ecwmin=9
he_mu_edca_ac_be_ecwmax=10
he_mu_edca_ac_be_timer=255
he_mu_edca_ac_bk_aifsn=15
he_mu_edca_ac_bk_aci=1
he_mu_edca_ac_bk_ecwmin=9
he_mu_edca_ac_bk_ecwmax=10
he_mu_edca_ac_bk_timer=255
he_mu_edca_ac_vi_ecwmin=5
he_mu_edca_ac_vi_ecwmax=7
he_mu_edca_ac_vi_aifsn=5
he_mu_edca_ac_vi_aci=2
he_mu_edca_ac_vi_timer=255
he_mu_edca_ac_vo_aifsn=5
he_mu_edca_ac_vo_aci=3
he_mu_edca_ac_vo_ecwmin=5
he_mu_edca_ac_vo_ecwmax=7
he_mu_edca_ac_vo_timer=255

# End
systemctl enable --now hostapd

Now you can access this WiFi network.

Troubleshooting: WiFi Regulatory Domain is incorrect & country code can’t be changed & device is self-managed

00 is the Global Regulatory Domain and it’s also the most restricted. You can’t use any high band except 2.4GHz unless it be changed.

iw reg get
phy#0 (self-managed)
country 00: DFS-UNSET

If your device firmware show self-managed like above, it means the device will reject country changes from the host.

You have to know it’s behaivior.

Intel

The newer Intel WiFi adapters have a builtin feature called Location Aware Regulatory and you can do nothing to bypass it in fact.

In the firmware, their engineer disabled the option which is used to disable LAR.

Consider other vendors.

RealTek

For RealTek products, there is must a way to set the country code in kernel module parameter.

In this case you can find its corresponding parameter of kernel module, like 8852bs rtw88 rtw89 rtl8xxxu rtl818x rtlwifi

modinfo 8852bs

Find parameter like this:

parm:           rtw_country_code:The default country code (in alpha2) (charp)

Pass it via modprobe

# /etc/modprobe.d/8852bs.conf

options 8852bs rtw_country_code=US

Load the kernel module. And the correct country code should now be set.

phy#0 (self-managed)
country US: DFS-UNSET

Launch your AP again.

Surf!

Connect clients to the the Ethernet switch or WiFi network.

Now your devices can access the Internet!