../
// IPv4/IPv6 Dual Stack Soft Router Based on Debian 12
#import "/template-en.typ":*
#doc-template(
title: "IPv4/IPv6 Dual Stack Soft Router Based on Debian 12",
date: "March 24, 2024",
body: [

= Origin

Recently, I bought a cheap industrial control mini-PC with an Intel Celeron processor. This host has two network cards, which makes it very suitable for use as a router. Since my old router was quite aged and did not support IPv6, I decided to replace it with this machine. The original wireless router is now only used as an access point. Normally, one would choose a dedicated router operating system like OpenWRT, but I also want this machine to handle many server functions. Debian is more familiar to me, though the trade-off is that there is no convenient one-stop web configuration interface, and everything must be done manually.

So, here I record how to use Debian to configure a usable server as a router.

= Basic Settings

This host has two network cards, named `enp2s0` and `enp4s0` in the operating system. Theoretically, with VLANs, a single network port could serve as a router. However, in my experience, a one-arm router based on VLAN is troublesome to configure. Following the principle of "don't overcomplicate things," I recommend using two network cards. If the motherboard has only one network card, a USB 3.0 network card can serve the same purpose.

Here, I use `enp2s0` as the WAN port, with the network segment assigned by the ISP. `enp4s0` is the LAN port, with the IPv4 segment `192.168.31.0/24` and the router's IP address `192.168.31.63`. The IPv6 segment is `fc61:5887:1acd:4260::/64`, and the router's own IPv6 address is `fc61:5887:1acd:4260::1`.

The IPv4 segment can be chosen from `192.168.*.*` as long as there is no conflict. The private IPv6 segment starts with `fc`, so any segment starting with `fc` will work, and the rest can be filled arbitrarily. I used a randomly generated 64-bit segment. If you want to use `fc11:4514:1919:8100::/64`, that's fine too.

= WAN Settings

Debian now uses the `networking` service for network configuration. Edit `/etc/network/interfaces` and add:

```
allow-hotplug enp2s0
iface enp2s0 inet dhcp
iface enp2s0 inet6 dhcp
```

Then run:

```
sudo systemctl restart networking
```

= LAN Settings

Similarly, edit `/etc/network/interfaces` and add:

```
allow-hotplug enp4s0
iface enp4s0 inet static
    address 192.168.31.63/24
iface enp4s0 inet6 static
    address fc61:5887:1acd:4260::1/64
```

Then run:

```
sudo systemctl restart networking
```

= IP Forwarding

Edit `/etc/sysctl.conf` and add:

```
net.ipv4.ip_forward = 1
net.ipv6.conf.enp2s0.accept_ra = 2
net.ipv6.conf.all.forwarding=1
```

Apply the configuration immediately:

```
sudo sysctl -p
```

However, after this takes effect, for some reason, the default routing rule might disappear. I am not sure what the problem is, so I choose to manually add the routing rule:

```
sudo ip -6 route add \
    default via fe80::1 \
        dev enp2s0 \
        proto ra \
        metric 1024 \
        hoplimit 255 \
        pref medium
```

The routing rules may vary in different network environments. You can use the following command to see what the current routing rule is before enabling IPv6 forwarding:

```
sudo ip -6 route | grep default
```

Also, this command should be added to a startup script.

= NAT

Linux firewall configuration has moved into the era of `nftables`, but I haven't learned `nftables` much yet, so I chose to use the `iptables` compatibility layer.

IPv4 NAT configuration:

```
IPT=/usr/sbin/iptables
SUB_NET=192.168.31.0/24
WAN_FACE=enp2s0
LAN_FACE=enp4s0

$IPT -t nat -I POSTROUTING 1 -s $SUB_NET -o $WAN_FACE -j MASQUERADE
$IPT -I INPUT -i $LAN_FACE -j ACCEPT
$IPT -I FORWARD -i $WAN_FACE -o $LAN_FACE -j ACCEPT
$IPT -I FORWARD -i $LAN_FACE -o $WAN_FACE -j ACCEPT
```

IPv6 NAT configuration:

```
IPT=/usr/sbin/ip6tables
SUB_NET=fc61:5887:1acd:4260::/64
WAN_FACE=enp2s0

$IPT -t nat -A POSTROUTING -o $WAN_FACE -j MASQUERADE
```

These are bash scripts and should also be added to a startup script.

In theory, IPv6 does not need NAT at all, but because I can't quite figure out my ISP's IPv6 address assignment rules, I chose a safe NAT instead.

= DHCP Server

Debian 12 provides a DHCP server `isc-dhcp-server`:

```
sudo apt install isc-dhcp-server
```

First, modify `/etc/default/isc-dhcp-server`. Here we only need IPv4:

```
INTERFACESv4="enp4s0"
INTERFACESv6=""
```

Then edit `/etc/dhcp/dhcpd.conf`:

```
option domain-name-servers 223.5.5.5;

subnet 192.168.31.0 netmask 255.255.255.0 {
    range 192.168.31.100 192.168.31.200;
    option routers 192.168.31.63;
}
```

The DNS server chosen here is Alibaba Cloud's server, mainly for use in China. If abroad, you can directly use `8.8.8.8` or `1.1.1.1`.

For IPv6, DHCP is not needed, and you can directly use the #link("https://en.wikipedia.org/wiki/IPv6")[Stateless Address Autoconfiguration (SLAAC)] of IPv6.

First, install `radvd`:

```
sudo apt install radvd
```

Then create the configuration `/etc/radvd.conf`:

```
interface enp4s0 {
    AdvSendAdvert on;
    MinRtrAdvInterval 30;
    MaxRtrAdvInterval 100;
    prefix fc61:5887:1acd:4260::/64 {
        AdvOnLink on;
        AdvAutonomous on;
        AdvRouterAddr off;
    };
};
```

Finally, restart the DHCP server and `radvd`:

```
sudo systemctl restart isc-dhcp-server
sudo systemctl restart radvd
```

If these two servers can operate normally, the soft router is successfully completed.
])

Email: i (at) mistivia (dot) com