Linux Router with VPN on a Raspberry Pi (IPv6): Difference between revisions

From Alpine Linux
m (Rescued manpage link using a reputable 3rd party site. I seriously only have unkind comments for upstream sources that make it this difficult to find documentation.)
 
(32 intermediate revisions by 5 users not shown)
Line 1: Line 1:
'''Work in Progress: Following does not work and won't work!.'''
{{TOC right}}


I have split this off the main article [[Linux Router with VPN on a Raspberry Pi]] as that works. IPv6 implementation requires a few changes to the initial article to work.
I have split this off the main article [[Linux Router with VPN on a Raspberry Pi]] IPv6 implementation requires a few changes to the initial article to work. I haven't duplicated everything here however, just the stuff that relates to IPv6.


= IPv6 =
= Introduction =
IPv6 introduces a number of new complexities into our network. If you've completed previous IPv4 only guide [[Linux Router with VPN on a Raspberry Pi]] then read on.
IPv6 introduces a number of new complexities into our network. If you've completed previous IPv4 only guide [[Linux Router with VPN on a Raspberry Pi]] then read on.


Line 10: Line 10:
If you don't know much about IPv6 then these pages might be of interest to get you up to speed.
If you don't know much about IPv6 then these pages might be of interest to get you up to speed.


* [http://tldp.org/HOWTO/Linux+IPv6-HOWTO Linux IPv6 HOWTO (en)] - in particular the "basics" and "address types".
* [https://tldp.org/HOWTO/Linux+IPv6-HOWTO Linux IPv6 HOWTO (en)] - in particular the "basics" and "address types".
* [https://en.wikipedia.org/wiki/IPv6 IPv6]
* [https://en.wikipedia.org/wiki/IPv6 IPv6]
* [https://en.wikipedia.org/wiki/IPv6_address IPv6 Address]
* [https://en.wikipedia.org/wiki/IPv6_address IPv6 Address]
Line 16: Line 16:
* [https://en.wikipedia.org/wiki/Neighbor_Discovery_Protocol Neighbor Discovery Protocol] we use this with radvd to distribute our routes.
* [https://en.wikipedia.org/wiki/Neighbor_Discovery_Protocol Neighbor Discovery Protocol] we use this with radvd to distribute our routes.
* [https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol_version_6 Internet Control Message Protocol version 6] ICMPv6 differs from ICMPv4 and is used for many critical parts of IPv6 infrastructure.
* [https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol_version_6 Internet Control Message Protocol version 6] ICMPv6 differs from ICMPv4 and is used for many critical parts of IPv6 infrastructure.
* [http://ipv6-test.com IPv6-test.com] Useful for diagnosing if IPv6 is working.
* [https://test-ipv6.com/ https://test-ipv6.com/] Useful for diagnosing if IPv6 is working.




[[File:Network_Diagram_ipv4_ipv6_with_vlans.svg|900px|center|Network Diagram IPv4 and IPv6]]
[[File:Network_Diagram_ipv4_ipv6_with_vlans.svg|900px|center|Network Diagram IPv4 and IPv6]]


== Enabling IPv6 support ==
= Enabling IPv6 support =


Assuming you're using the Alpine Linux kernel, IPv6 support is available separately as a module.
Assuming you're using the Alpine Linux kernel, IPv6 support is available separately as a module.
Line 29: Line 29:
{{cmd|echo "ipv6" >> /etc/modules}}
{{cmd|echo "ipv6" >> /etc/modules}}


=== /etc/sysctl.conf ===
== /etc/sysctl.d/local.conf ==
Modify the sysctl section to include IPv6 support:
Modify the sysctl section to include IPv6 support:


<pre>########################################
<pre># Controls IP packet forwarding
###              IPv6                ###
net.ipv4.ip_forward = 1
########################################


# http://vk5tu.livejournal.com/37206.html
# Needed to use fwmark
net.ipv4.conf.all.rp_filter = 2
 
# https://vk5tu.livejournal.com/37206.html
# What's this special value "2"? Originally the value was "1", but this  
# What's this special value "2"? Originally the value was "1", but this  
# disabled autoconfiguration on all interfaces. That is, you couldn't appear  
# disabled autoconfiguration on all interfaces. That is, you couldn't appear  
Line 46: Line 48:
net.ipv6.conf.default.forwarding = 2
net.ipv6.conf.default.forwarding = 2


# Enable source validation by reversed path
# Accept Router Advertisments
# Protects from attackers that are using ip spoofing methods to do harm
# net.ipv6.conf.all.rp_filter = 1 ## Disabled, not available with this kernel
 
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.default.accept_ra = 2
net.ipv6.conf.default.accept_ra = 2


# Disable redirects, not a router
# We are a router so disable temporary addresses
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.default.accept_redirects = 0</pre>
net.ipv6.conf.default.use_tempaddr = 0</pre>
 
== /etc/network/interfaces ==
Add an IPv6 interface for each VLAN. Note we don't need to add one for VLAN2 because dhcpcd will take care of that for us using our ISPs router advertisements. Also note the . (dot notation) represents a VLAN interface where as : (colon notation) used in the previous article represented an IP address aliased on an interface.


=== /etc/network/interfaces ===
The reason we need VLANs here is because each VLAN has it's own broadcast and we don't want our router advertisements to be putting routes and addresses on all the interfaces. It also helps us with a more secure design, but requires a managed switch.
Add an IPv6 address under eth0.


<pre>iface eth0 inet6 static
<pre># VLAN 2 - DESTINED FOR ISP
  address 2001:0db8:1234:0001::1
auto eth0.2
  netmask 64
iface eth0.2 inet static
  autoconf 0
    address 192.168.2.1
  accept_ra 0
    netmask 255.255.255.0
  privext 0</pre>
    broadcast 192.168.2.255
    post-up /etc/network/fwmark_rules


===  /etc/ppp/peers/yourISP ===
# VLAN 3 - DESTINED FOR VPN
Add this to your ppp configuration. This tells PPP to get an ipv6 address. Note the comma is needed.
auto eth0.3
iface eth0.3 inet static
    address 192.168.3.1
    netmask 255.255.255.0
    broadcast 192.168.3.255


<pre># Enable IPV6
iface eth0.3 inet6 static
+ipv6 ipv6cp-use-ipaddr
    address fde4:8dba:82e1:fff3::1
ipv6 ,</pre>
    netmask 64
    autoconf 0
    accept_ra 0
    privext 0


=== Check system log ===
# VLAN 4 - LAN ONLY
auto eth0.4
iface eth0.4 inet static
    address 192.168.4.1
    netmask 255.255.255.0
    broadcast 192.168.4.255
    post-up /etc/network/route_LAN
 
iface eth0.4 inet6 static
    address fde4:8dba:82e1:fff4::1
    netmask 64
    autoconf 0
    accept_ra 0
    privext 0</pre>
 
= Configuring PPP =
Next up we need to configure our router to be able to dial a PPP connection with our modem.
 
See [[PPP]], you will want to make sure you set your WAN interface, in this example we used eth1.
 
== Check system log ==
Restart ppp.
Restart ppp.


Line 105: Line 134:
from your router.
from your router.


== Prefix Delegation ==
= Prefix Delegation =


The next step will be to configure DHCPv6 Prefix Delegation with your ISP. Install dhcpcd. While many guides do use the wide-dhcpv6-client [http://bugs.alpinelinux.org/issues/564 it should be noted this is unmaintained] and not included in Alpine Linux.
The next step will be to configure DHCPv6 Prefix Delegation with your ISP. Install dhcpcd. While many guides do use the wide-dhcpv6-client [https://gitlab.alpinelinux.org/alpine/aports/-/issues/564 it should be noted this is unmaintained] and not included in Alpine Linux.


Don't use the ISC's dhclient either as [https://bugs.gentoo.org/show_bug.cgi?id=432652 this does not support Prefix Delegations on PPP links] without a patch.
Don't use the ISC's dhclient either as [https://bugs.gentoo.org/show_bug.cgi?id=432652 this does not support Prefix Delegations on PPP links] without a patch.
Line 113: Line 142:
{{cmd|apk add dhcpcd}}
{{cmd|apk add dhcpcd}}


You can check out the manual for [http://roy.marples.name/man/html5/dhcpcd.conf.html dhcpcd.conf]. Installing dhcpcd-doc will allow you to read the man file. Eg:
You can check out the manual for [https://man.archlinux.org/man/dhcpcd.conf.5.en dhcpcd.conf]. Installing dhcpcd-doc will allow you to read the man file. Eg:


{{cmd|apk add dhcpcd-doc}}
{{cmd|apk add dhcpcd-doc}}
Line 120: Line 149:


{{cmd|apk add dhcpcd}}
{{cmd|apk add dhcpcd}}
If the main repositories have dhcpcd below version 7.0.7 (at time of writing AlpineLinux 3.8 and below) you will need to use the latest version from edge as it fixes a bug with unique link local addresses on our VLANs [https://roy.marples.name/blog/dhcpcd-7-0-7-released dhcpcd ChangeLog]{{dead link}} this [https://patchwork.alpinelinux.org/patch/4016/ patch]{{dead link}} already applied in v7.0.7
{{cmd|apk add dhcpcd@edge}}
If you haven't you may need to add the edge repository for pinning [[Alpine Linux package management#Repository_pinning]]


<pre># Enable extra debugging
<pre># Enable extra debugging
# debug
#debug
#logfile /var/log/dhcpcd.log


# Allow users of this group to interact with dhcpcd via the control
# Allow users of this group to interact with dhcpcd via the control
Line 134: Line 170:
#clientid
#clientid
# or
# or
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as  
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as
# per RFC4361. Some non-RFC compliant DHCP servers do not reply with
# per RFC4361. Some non-RFC compliant DHCP servers do not reply with
# this set. In this case, comment out duid and enable clientid above.
# this set. In this case, comment out duid and enable clientid above.
Line 153: Line 189:
# Most distributions have NTP support.
# Most distributions have NTP support.
option ntp_servers
option ntp_servers
# Respect the network MTU.
# Respect the network MTU.
# Some interface drivers reset when changing the MTU so disabled by
# Some interface drivers reset when changing the MTU so disabled by
Line 161: Line 198:
require dhcp_server_identifier
require dhcp_server_identifier


# Generate Stable Private IPv6 Addresses instead of hardware based  
# Generate Stable Private IPv6 Addresses instead of hardware based
# ones
# ones
slaac private
slaac private
Line 177: Line 214:
# Wait for IP before forking to background
# Wait for IP before forking to background
waitip 6
waitip 6
# Don't install any default routes.
# PPP has already set a default route
nogateway


# Don't touch DNS
# Don't touch DNS
Line 189: Line 222:
     ipv6rs # enable routing solicitation get the default IPv6 route
     ipv6rs # enable routing solicitation get the default IPv6 route
     iaid 1
     iaid 1
     ia_pd 1/::/64 eth0/1/64 # Assign a prefix delegated route to our LAN</pre>
     ia_pd 1/::/56 eth0.2/2/64</pre>


Add dhcpcd to the default run level:
Add dhcpcd to the default run level:
Line 195: Line 228:
{{cmd|rc-update add dhcpcd default}}
{{cmd|rc-update add dhcpcd default}}


== Configure ip6tables with a basic ruleset ==
= Configuring firewall for IPv4 and IPv6 traffic =
 
A basic rule set for ip6tables. It is commented so feel free to read it.


You'll need to modify your prefix in one of the rules.
== iptables ==
Here are some rules for iptables that I am currently using, yours may look something similar.


<pre>#########################################################################
<pre>#########################################################################
# Basic iptables IPv6 routing rule set
# Uses 192.168.1.0 VLAN1 Management Untagged - no route
#      192.168.2.0 VLAN2                    - route to ISP
#      192.168.3.0 VLAN3                    - route to VPN
#      192.168.4.0 VLAN4                    - no route
#
# Packets to/from 192.168.1.0/24 not in any VLAN ie tagged
# Packets to/from 192.168.2.0/24 are marked with 0x1 and routed to ISP
# Packets to/from 192.168.3.0/24 are marked with 0x2 and routed to VPN
# Packets to/from 192.168.4.0/24 are routed to LAN and not forwarded onto
#                                    the internet
#
#
# 2001:0db8:1234:0001::/64 hosts  routed directly to ppp0
# These destinations will always be marked with 0x1 from VLAN3:
#
#
# <ip_of_exception>      some exception
#########################################################################
#########################################################################


#
# Raw Table
#
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Create a log drop chain
:LOG_DROP_BOGON - [0:0]
:LOG_DROP_MSFT - [0:0]
# Create output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]
# Allows traffic from VPN's DNS server
-A PREROUTING -s 172.16.32.1/32 -i tun0 -j ACCEPT
# Block specified bogons coming in from ISP and VPN
# (unlikely to happen as they filter them on their router)
-A PREROUTING -i ppp0 -m set --match-set bogon-bn-nonagg src -j LOG_DROP_BOGON
-A PREROUTING -i tun0 -m set --match-set bogon-bn-nonagg src -j LOG_DROP_BOGON
# Block MSFT known tracking IPs from https://github.com/Nummer/Destroy-Windows-10-Spying
-A PREROUTING -i ppp0 -m set --match-set dropped-msft-ip-ipv4  src -j LOG_DROP_MSFT
-A PREROUTING -i tun0 -m set --match-set dropped-msft-ip-ipv4  src -j LOG_DROP_MSFT
# Allows my excepted ranges
-A PREROUTING -m set --match-set allowed-nets-ipv4 src,src -j ACCEPT
# Allows traffic originating from router to remote address on VPN
-A OUT_TUN0 -d 172.16.32.1 -j ACCEPT
# Pass output interface to corresponding chain
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0
# Log drop chain
-A LOG_DROP_BOGON -j LOG --log-prefix "Dropped Bogon (ipv4) : " --log-level 6
-A LOG_DROP_BOGON -j DROP
-A LOG_DROP_MSFT -j LOG --log-prefix "Dropped MSFT (ipv4) : " --log-level 6
-A LOG_DROP_MSFT -j DROP
# Block packets originating from the router destined to bogon ranges
-A OUT_PPP0 -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON
# Blocks packets originating from the router destined to bogon ranges
-A OUT_TUN0 -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON
# Block packets originating from the router destined to msft ranges
-A OUT_PPP0 -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT
# Blocks packets originating from the router destined to msft ranges
-A OUT_TUN0 -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT
COMMIT
#
# NAT Table
# This is where translation of packets happens and "forwarding" of ports
# to specific hosts.
#
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Port forwarding for Bittorrent through VPN
-A PREROUTING -i tun0 -p tcp -m tcp --dport 20001 -j DNAT --to-destination 192.168.3.30
-A PREROUTING -i tun0 -p udp -m udp --dport 20001 -j DNAT --to-destination 192.168.3.30
# Allows routing to our modem subnet so we can access the web interface
-A POSTROUTING -s 192.168.2.0/24 -d 192.168.0.1/32 -o eth1 -p tcp -m tcp --dport 80 -j MASQUERADE
-A POSTROUTING -s 192.168.3.0/24 -d 192.168.0.1/32 -o eth1 -p tcp -m tcp --dport 80 -j MASQUERADE
# Allows routing to Printer
-A POSTROUTING -s 192.168.2.0/24 -d 192.168.4.9/32 -o eth0 -j MASQUERADE
-A POSTROUTING -s 192.168.3.0/24 -d 192.168.4.9/32 -o eth0 -j MASQUERADE
# Allows hosts of the network to use the VPN tunnel
-A POSTROUTING -o tun0 -j MASQUERADE
# Allows hosts of the network to use the PPP tunnel
-A POSTROUTING -o ppp0 -j MASQUERADE
COMMIT
#
# Filter Table
# This is where we decide to ACCEPT, DROP or REJECT things
#
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Create rule chain per input interface for forwarding packets
:FWD_ETH1 - [0:0]
:FWD_PPP0 - [0:0]
:FWD_TUN0 - [0:0]
:FWD_V1_MGMT - [0:0]
:FWD_V2_ISP - [0:0]
:FWD_V3_VPN - [0:0]
:FWD_V4_LANONLY - [0:0]
# Create rule chain per input interface for input packets (for host itself)
:IN_ETH1 - [0:0]
:IN_PPP0 - [0:0]
:IN_TUN0 - [0:0]
:IN_V1_MGMT - [0:0]
:IN_V2_ISP - [0:0]
:IN_V3_VPN - [0:0]
:IN_V4_LANONLY - [0:0]
# Create a drop/reject chains
:LOG_DROP - [0:0]
:LOG_DROP_BOGON - [0:0]
:LOG_DROP_MSFT - [0:0]
:LOG_REJECT_LANONLY - [0:0]
# Create an output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]
# Pass input packet to corresponding rule chain
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -j IN_V1_MGMT
-A INPUT -i eth0.2 -j IN_V2_ISP
-A INPUT -i eth0.3 -j IN_V3_VPN
-A INPUT -i eth0.4 -j IN_V4_LANONLY
-A INPUT -i eth1 -j IN_ETH1
-A INPUT -i ppp0 -j IN_PPP0
-A INPUT -i tun0 -j IN_TUN0
# Track forwarded packets
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Pass forwarded packet to corresponding rule chain
-A FORWARD -i eth0 -j FWD_V1_MGMT
-A FORWARD -i eth0.2 -j FWD_V2_ISP
-A FORWARD -i eth0.3 -j FWD_V3_VPN
-A FORWARD -i eth0.4 -j FWD_V4_LANONLY
-A FORWARD -i eth1 -j FWD_ETH1
-A FORWARD -i ppp0 -j FWD_PPP0
-A FORWARD -i tun0 -j FWD_TUN0
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0
# Forward HTTP packets from network to modem
-A FWD_ETH1 -s 192.168.0.1/32 -d 192.168.2.0/24 -p tcp -m tcp --sport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A FWD_ETH1 -s 192.168.0.1/32 -d 192.168.3.0/24 -p tcp -m tcp --sport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Forward Bittorrent Port
-A FWD_TUN0 -d 192.168.3.30/32 -p tcp -m tcp --dport 20001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A FWD_TUN0 -d 192.168.3.30/32 -p udp -m udp --dport 20001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Forward established packets to hosts in VLAN2/3 from Printer
-A FWD_V1_MGMT -s 192.168.4.9/32 -d 192.168.2.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A FWD_V1_MGMT -s 192.168.4.9/32 -d 192.168.3.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Refuse to forward bogons from VLAN1 (Untagged Management)
-A FWD_V1_MGMT -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON
# Refuse to forward msft from VLAN1 (Untagged Management)
-A FWD_V1_MGMT -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT
# Forward traffic from VLAN2 to Modem
-A FWD_V2_ISP -d 192.168.0.1/32 -j ACCEPT
# Forward traffic from VLAN2 to Printer
-A FWD_V2_ISP -d 192.168.4.9/32 -j ACCEPT
# Drop bogons from VLAN2
-A FWD_V2_ISP -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON
# Drop msft from VLAN2
-A FWD_V2_ISP -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT
# Allow rest from VLAN2
-A FWD_V2_ISP -s 192.168.2.0/24 -j ACCEPT
# Forward traffic from VLAN3 to Modem
-A FWD_V3_VPN -d 192.168.0.1/32 -j ACCEPT
# Forward traffic from VLAN3 to Printer
-A FWD_V3_VPN -d 192.168.4.9/32 -j ACCEPT
# Allow rest from VLAN3
-A FWD_V3_VPN -s 192.168.3.0/24 -j ACCEPT
# Drop bogons from VLAN3
-A FWD_V3_VPN -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON
# Drop msft from VLAN3
-A FWD_V3_VPN -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT
# Forward some exception to ppp0 from VLAN3
-A FWD_V3_VPN -s 192.168.3.0/24 -d <ip_of_exception>/32 -o ppp0 -j ACCEPT
# Allow in NTP from Router (this machine)
-A IN_ETH1 -s 192.168.0.1/32 -d 192.168.0.0/30 -p udp -m udp --dport 123 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow in HTTP from Router (this machine)
-A IN_ETH1 -s 192.168.0.1/32 -d 192.168.0.0/30 -p tcp -m tcp --sport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Accept incoming tracked PPP0 connection
-A IN_PPP0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Log dropped packets coming in on PPP0
# -A IN_PPP0 -j LOG --log-prefix "DROP:INPUT (ipv4) " --log-level 6
-A IN_PPP0 -j LOG_DROP
# Accept incoming tracked TUN0 connection
-A IN_TUN0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Log dropped packets coming in on TUN0
# -A IN_TUN0 -j LOG --log-prefix "DROP:INPUT (ipv4) " --log-level 6
-A IN_TUN0 -j LOG_DROP
# Allow in established packets from Printer to hosts in VLAN2/3
-A IN_V1_MGMT -s 192.168.4.9/32 -d 192.168.2.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.4.9/32 -d 192.168.3.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# FreeRadius Clients (access point A & B)
-A IN_V1_MGMT -s 192.168.1.10/32 -p tcp -m tcp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.10/32 -p udp -m udp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p tcp -m tcp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p udp -m udp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Ubiquiti UAP Device Discovery Broadcast
-A IN_V1_MGMT -s 192.168.1.10/32 -p udp -m udp --dport 10001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p udp -m udp --dport 10001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.10/32 -p udp -m udp --dport 3478 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p udp -m udp --dport 3478 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow rest in from VLAN1
-A IN_V1_MGMT -s 192.168.1.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow ssh in from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow DNS in from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
# ALLOW NTP in from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -p udp -m udp --dport 123 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow rest from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow ssh in from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow DNS in from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
# Allow NTP in from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -p udp -m udp --dport 123 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow rest from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow some exception direct from ppp0 to VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -d <ip_of_exception>/32 -o ppp0 -j ACCEPT
# Log dropped bogons that never got forwarded
-A LOG_DROP_BOGON -j LOG --log-prefix "Dropped Bogon forward (ipv4) " --log-level 6
-A LOG_DROP_BOGON -j DROP
# Log dropped msft tracking that never got forwarded
-A LOG_DROP_MSFT -j LOG --log-prefix "Dropped MSFT forward(ipv4) " --log-level 6
-A LOG_DROP_MSFT -j DROP
# Log rejected packets
-A LOG_REJECT_LANONLY -j LOG --log-prefix "Rejected packet from LAN only" --log-level 6
-A LOG_REJECT_LANONLY -j REJECT --reject-with icmp-port-unreachable
COMMIT
#
# Mangle Table
# This is the place where our markings happen, whether they be 0x1 or 0x2
#
*mangle
*mangle
:PREROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
Line 214: Line 538:
:OUTPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Restore CONNMARK to the MARK (If one doesn't exist then no mark is set)
-A PREROUTING -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
# If packet MARK is 2, then it means there is already a connection mark and the
# original packet came in on VPN
-A PREROUTING -s 192.168.3.0/24 -m mark --mark 0x2
# Check some exception are 0x1
-A PREROUTING -s 192.168.3.0/24 -d <ip_of_exception>/32 -m mark --mark 0x1
# Mark packets coming from 192.168.3.0/24 are 0x2
-A PREROUTING -s 192.168.3.0/24 -j MARK --set-xmark 0x2/0xffffffff
# If packet MARK is 1, then it means there is already a connection mark and the
# original packet came in on ISP
-A PREROUTING -s 192.168.2.0/24 -m mark --mark 0x1
# Mark packets 192.168.2.0/24 are 0x1
-A PREROUTING -s 192.168.2.0/24 -j MARK --set-xmark 0x1/0xffffffff
# Mark some exception as 0x1
-A PREROUTING -s 192.168.3.0/24 -d <ip_of_exception>/32 -j MARK --set-xmark 0x1/0xffffffff
# Strip mark if packet is destined for modem
-A PREROUTING -d 192.168.0.1/32 -j MARK --set-xmark 0x0/0xffffffff
# Strip mark if packet is destined for printer
-A PREROUTING -d 192.168.4.9/32 -j MARK --set-xmark 0x0/0xffffffff
# Save MARK to CONNMARK (remember iproute can't see CONNMARKs)
-A PREROUTING -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff
COMMIT</pre>
== ip6tables ==
You'll need to modify your prefix in one of the rules.
<pre>#########################################################################
# Uses 2001:0db8:1234:ffff::1/64 VLAN2 - route to ISP
#      fde4:8dba:82e1:fff3::1/64  VLAN3 - route to VPN
#########################################################################
#
# Raw Table
#
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Create a log drop chain
:LOG_DROP_BOGON - [0:0]
# Create output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]
# Allows my excepted ranges
-A PREROUTING -m set --match-set allowed-nets-ipv6 src,src -j ACCEPT
# Pass output interface to corresponding chain
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0
COMMIT
COMMIT


#
# NAT Table
# This is where translation of packets happens and "forwarding" of ports
# to specific hosts.
#
*nat
*nat
:PREROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
Line 221: Line 613:
:OUTPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Allows hosts of the network to use the VPN tunnel for IPv6
-A POSTROUTING -o tun0 -j MASQUERADE
COMMIT
COMMIT


*raw
#
# Mangle Table
#
*mangle
:PREROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Drop unusually large ping packets
-A PREROUTING -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m length --length 170:65535 -j DROP
COMMIT
COMMIT


Line 238: Line 643:


# Create rule chain per input interface for forwarding packets
# Create rule chain per input interface for forwarding packets
:FWD_ETH0 - [0:0]
:FWD_ETH1 - [0:0]
:FWD_ETH1 - [0:0]
:FWD_PPP0 - [0:0]
:FWD_PPP0 - [0:0]
:FWD_TUN0 - [0:0]
:FWD_TUN0 - [0:0]
 
:FWD_V1_MGMT - [0:0]
# Create ICMPFLOOD chain
:FWD_V2_ISP - [0:0]
:ICMPFLOOD - [0:0]
:FWD_V3_VPN - [0:0]
:FWD_V4_LANONLY - [0:0]


# Create rule chain per input interface for input packets (for host itself)
# Create rule chain per input interface for input packets (for host itself)
:IN_ETH0 - [0:0]
:IN_ETH1 - [0:0]
:IN_ETH1 - [0:0]
:IN_PPP0 - [0:0]
:IN_PPP0 - [0:0]
:IN_TUN0 - [0:0]
:IN_V1_MGMT - [0:0]
:IN_V2_ISP - [0:0]
:IN_V3_VPN - [0:0]
:IN_V4_LANONLY - [0:0]


# Create a drop chain
# Create a drop/reject chains
:LOG_DROP - [0:0]
:LOG_DROP - [0:0]
:LOG_DROP_BOGON - [0:0]
:LOG_REJECT_LANONLY - [0:0]
# Create an output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]


# Accept all from localhost
# Pass input packet to corresponding rule chain
-A INPUT -i lo -j ACCEPT
-A INPUT -i lo -j ACCEPT
 
-A INPUT -i eth0 -j IN_V1_MGMT
# Create rule chain per input interface for input packets (for host itself)
-A INPUT -i eth0.2 -j IN_V2_ISP
-A INPUT -i eth0 -j IN_ETH0
-A INPUT -i eth0.3 -j IN_V3_VPN
-A INPUT -i eth0.4 -j IN_V4_LANONLY
-A INPUT -i eth1 -j IN_ETH1
-A INPUT -i eth1 -j IN_ETH1
-A INPUT -i ppp0 -j IN_PPP0
-A INPUT -i ppp0 -j IN_PPP0
 
-A INPUT -i tun0 -j IN_TUN0
# Block remote packets claiming to be from a loopback address
-A INPUT -s ::1/128 ! -i lo -j DROP
 
# Permit needed ICMP packet types for IPv6 per RFC 4890
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 3 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 4 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 133 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 137 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 141 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 142 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 131 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 132 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 143 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 148 -j ACCEPT
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 149 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 151 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 152 -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 153 -j ACCEPT
 
# Permit ICMP echo requests (ping) and use ICMPFLOOD chain for preventing ping flooding.
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j ICMPFLOOD


# Track forwarded packets
# Track forwarded packets
Line 294: Line 683:


# Pass forwarded packet to corresponding rule chain
# Pass forwarded packet to corresponding rule chain
-A FORWARD -i eth0 -j FWD_ETH0
-A FORWARD -i eth0 -j FWD_V1_MGMT
-A FORWARD -i eth0.2 -j FWD_V2_ISP
-A FORWARD -i eth0.3 -j FWD_V3_VPN
-A FORWARD -i eth0.4 -j FWD_V4_LANONLY
-A FORWARD -i eth1 -j FWD_ETH1
-A FORWARD -i eth1 -j FWD_ETH1
-A FORWARD -i ppp0 -j FWD_PPP0
-A FORWARD -i ppp0 -j FWD_PPP0
-A FORWARD -i tun0 -j FWD_TUN0


# Permit needed ICMP packet types for IPv6 per RFC 4890
# Rate limit ICMPv6 PING
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m limit --limit 30/min -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 3 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 4 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 133 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 137 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 141 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 142 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 131 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 132 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 143 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 148 -j ACCEPT
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 149 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 151 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 152 -j ACCEPT
-A FORWARD -s fe80::/10 -p ipv6-icmp -m icmp6 --icmpv6-type 153 -j ACCEPT


# Permit ICMP echo requests (ping) and use ICMPFLOOD chain for preventing ping flooding.
# Pass output interface to corresponding chain
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j ICMPFLOOD
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0


# Forward LAN subnet
# Forward VLAN2 to ISP
-A FWD_ETH0 -s 2001:0db8:1234:0001::/64 -j ACCEPT
-A FWD_V2_ISP -s 2001:0db8:1234:ffff::/64 -j ACCEPT


# Chain for preventing ping flooding - up to 6 pings per second from a single
# Forward VLAN3 to VPN
# source, again with log limiting.
-A FWD_V3_VPN -o tun0 -j ACCEPT
-A ICMPFLOOD -m hashlimit --hashlimit-name ICMP --hashlimit-above 6/second --hashlimit-mode srcip -j DROP
-A ICMPFLOOD -j ACCEPT


# DHCPv6 to Router
# Accept incoming tracked PPP0 connection
-A IN_ETH0 -p udp -m udp --dport 547 -m conntrack --ctstate NEW -j ACCEPT
-A IN_PPP0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT


# LAN traffic out
# Log dropped packets coming in on PPP0
-A IN_ETH0 -s 2001:0db8:1234:0001::/64 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_PPP0 -j LOG_DROP


# Accept tracked connections from outside
# Allow and rate limit ICMP
-A IN_PPP0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A IN_PPP0 -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j ACCEPT
-A IN_PPP0 -p ipv6-icmp -m limit --limit 30/sec -j ACCEPT
 
# Allow DHCPv6 PD on Link Local from ISP
-A IN_PPP0 -s fe80::/10 -p udp -m udp --sport 547 --dport 546 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT


# Drop and log everything else
# Log dropped packets coming in on PPP0
-A IN_PPP0 -j LOG --log-prefix "DROP:INPUT (ipv6) " --log-level 6
-A IN_PPP0 -j LOG_DROP
-A IN_PPP0 -j LOG_DROP
# Accept incoming tracked TUN0 connection
-A IN_TUN0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Log dropped packets on VPN
-A IN_TUN0 -j LOG_DROP
# Allow tracked connections in from ppp0 to VLAN2
-A IN_V2_ISP -s 2001:0db8:1234:ffff::/64 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow ICMP in from VLAN2
-A IN_V2_ISP -p ipv6-icmp -j ACCEPT
# Allow tracked connections in from tun0 to VLAN3
-A IN_V3_VPN -s fde4:8dba:82e1:fff3::/64 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Allow ICMP in from VLAN3
-A IN_V3_VPN -p ipv6-icmp -j ACCEPT
COMMIT</pre>
COMMIT</pre>


Line 349: Line 743:
{{cmd|rc-update add ip6tables default}}
{{cmd|rc-update add ip6tables default}}


== Router Advertisements ==


{{cmd|apk add radvd}}
== nftables ==
 
Optionally one might decide to use nftables instead of old legacy iptables. nftables has a few improvements such as a cleaner rule syntax, ipv4 and ipv6 is all in one table, and the ability to use [https://wiki.nftables.org/wiki-nftables/index.php/Scripting#Defining_variables variables], [https://wiki.nftables.org/wiki-nftables/index.php/Sets sets], [https://wiki.nftables.org/wiki-nftables/index.php/Dictionaries dictionaries] and [https://wiki.nftables.org/wiki-nftables/index.php/Maps maps]. This also means you no longer need to worry about using [[Linux Router with VPN on a Raspberry Pi#Installing_ipset | ipset]].
 
<pre>#!/usr/sbin/nft -f
 
flush ruleset
 
###################################################################################
#
#| Address                            | Route To        | Interface | VLAN | Mark |
#|------------------------------------|-----------------|-----------|------|------|
#| 192.168.0.0/24                    | Modem          | eth1      | 1    |      |
#| 192.168.1.0/24                    | Nowhere        | eth0      | 1    |      |
#| 192.168.2.0/24                    | ISP            | eth0.2    | 2    | 0x1  |
#| 2001:0db8:1234:ffff::/64          | ISP            | eth0.2    | 2    | 0x1  |
#| 192.168.3.0/24                    | VPN            | eth0.3    | 3    | 0x2  |
#| fde4:8dba:82e1:fff3::/64          | VPN            | eth0.3    | 3    | 0x2  |
#| 192.168.4.0/24                    | Nowhere        | eth0.4    | 4    |      |
#| <ip_of_exception>                  | Exception (ISP) | eth0.2    | 4    | 0x1  |
#
###################################################################################
 
define net_v0_ip4 = 192.168.0.0/24
define net_v1_ip4 = 192.168.1.0/24
define net_v2_ip4 = 192.168.2.0/24
define net_v3_ip4 = 192.168.3.0/24
define network_v4_ip4 = 192.168.4.0/24
define mailserver = <ip_of_exception>
define modem = 192.168.0.2
define router = 192.168.1.1
define printer = 192.168.4.9
define workstation = 192.168.3.30
define wifi_aps = { 192.168.1.10, 192.168.1.11 }
define net_ula_v1_ip6 = fde4:8dba:82e1:fff1::/64
define net_gua_v2_ip6 = 2001:0db8:1234:ffff::/64
define net_ula_v3_ip6 = fde4:8dba:82e1:fff3::/64
define net_ula_v4_ip6 = fde4:8dba:82e1:fff4::/64
define vpn_gateway = 172.16.32.1
 
#
# Mangle Table (IPv4)
# Markings happen: whether they be 0x1 or 0x2
#
table ip mangle {
chain PREROUTING {
type filter hook prerouting priority mangle; policy accept;
 
# Restore CONNMARK to the MARK (If one doesn't exist then no mark is set)
mark set ct mark
 
# If packet MARK is 2, then it means there is already a
# connection mark and theoriginal packet came in on VPN
ip saddr $net_v3_ip4 mark 0x00000002
 
# Check mail server are 0x1
ip saddr $net_v3_ip4 ip daddr $mailserver mark 0x00000001
 
# Mark packets coming from VLAN3 as 0x2
ip saddr $net_v3_ip4 mark set 0x00000002
 
# If packet MARK is 1, then it means there is already a
# connection mark and the original packet came in on ISP
ip saddr $net_v2_ip4 mark 0x00000001
 
# Mark packets coming from VLAN2 as 0x1
ip saddr $net_v2_ip4 mark set 0x00000001
 
# Mark mail server as 0x1
ip saddr $net_v3_ip4 ip daddr $mailserver mark set 0x00000001
 
# Strip mark if packet is destined for modem
ip daddr $modem mark set 0x00000000
 
# Strip mark if packet is destined for printer
ip daddr $printer mark set 0x00000000
 
# Save MARK to CONNMARK (remember iproute can't see CONNMARKs)
ct mark set mark
}
}
 
#
# Filter Table (IPv4)
# Filtering things coming IN and OUT of the router
#
table ip filter {
# Create rule chain per input interface for input packets (for host itself)
chain INPUT {
type filter hook input priority filter; policy drop;
iifname "lo" accept
iifname "eth0" jump IN_V1_MGMT
iifname "eth0.2" jump IN_V2_ISP
iifname "eth0.3" jump IN_V3_VPN
iifname "eth1" jump IN_ETH1
iifname "tun0" jump IN_TUN0
}
 
# Create rule chain per input interface for forwarding packets
chain FORWARD {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "eth0" jump FWD_V1_MGMT
iifname "eth0.2" jump FWD_V2_ISP
iifname "eth0.3" jump FWD_V3_VPN
iifname "eth1" jump FWD_ETH1
iifname "tun0" jump FWD_TUN0
}
 
chain FWD_ETH1 {
ip saddr $modem ip daddr $net_v2_ip4 tcp sport http ct state established,new accept
ip saddr $modem ip daddr $net_v3_ip4 tcp sport http ct state established,new accept
}
 
chain FWD_TUN0 {
# Forward bittorrent
ip daddr $workstation tcp dport 20001 ct state established,new accept
ip daddr $workstation udp dport 20001 ct state established,new accept
}
 
chain FWD_V1_MGMT {
# Forward established packets to hosts in VLAN2/3 from printer
ip saddr $printer ip daddr $net_v2_ip4 ct state established,new accept
ip saddr $printer ip daddr $net_v3_ip4 ct state established,new accept
 
# Forward established packets to hosts in VLAN2/3 from modem
ip saddr $modem ip daddr $net_v3_ip4 tcp sport http ct state established,new accept
}
 
chain FWD_V2_ISP {
# Forward traffic from VLAN2 to Modem
ip daddr $modem tcp dport http accept
 
# Forward traffic from VLAN2 to printer
ip daddr $printer accept
 
# Allow rest from VLAN2
ip saddr $net_v2_ip4 accept
}
 
chain FWD_V3_VPN {
# Forward traffic from VLAN3 to Modem
ip daddr $modem tcp dport http accept
 
# Forward traffic from VLAN3 to printer
ip daddr $printer accept
 
# Allow rest from VLAN3
ip saddr $net_v3_ip4 accept
 
# Allow mailserver direct from VLAN3 out
oifname "eth1" ip saddr $net_v3_ip4 ip daddr $mailserver accept
}
 
chain IN_ETH1 {
# Accept incoming tracked connection from eth1
ip saddr $router ip daddr $modem tcp sport http ct state established,new accept
 
# Allow incoming NTP in from VLAN1
ip saddr $net_v0_ip4 udp dport ntp ct state established,new accept
 
# Log dropped packets coming in on eth1
ct state established,related accept
jump LOG_DROP
}
 
chain IN_TUN0 {
# Log dropped packets coming in on tun0
ct state established,related accept
jump LOG_DROP
}
 
chain IN_V1_MGMT {
# Allow in established packets from printer to VLAN2 and VLAN3
ip saddr $printer ip daddr $net_v2_ip4 ct state established,new accept
ip saddr $printer ip daddr $net_v3_ip4 ct state established,new accept
 
# Allow NTP in from VLAN1
ip saddr $net_v1_ip4 udp dport ntp ct state established,new accept
 
# FreeRadius Clients
ip saddr $wifi_aps tcp dport radius ct state established,new accept
ip saddr $wifi_aps udp dport radius ct state established,new accept
 
# Ubiquiti UAP Device Discovery Broadcast
ip saddr $wifi_aps udp dport 10001 ct state established,new accept
ip saddr $wifi_aps udp dport 3478 ct state established,new accept
 
# Allow rest in from VLAN1
ip saddr $net_v1_ip4 ct state established,new accept
}
 
chain IN_V2_ISP {
# Allow ssh in from VLAN2
ip saddr $net_v2_ip4 tcp dport ssh ct state established,new accept
 
# Allow DNS in from VLAN2
ip saddr $net_v2_ip4 udp dport domain ct state new accept
 
# Allow NTP in from VLAN2
ip saddr $net_v2_ip4 udp dport ntp ct state established,new accept
 
# Allow rest from VLAN2
ip saddr $net_v2_ip4 ct state established,new accept
}
 
chain IN_V3_VPN {
# Allow ssh in from VLAN3
ip saddr $net_v3_ip4 tcp dport ssh ct state established,new accept
 
# Allow DNS in from VLAN3
ip saddr $net_v3_ip4 udp dport domain ct state new accept


Once radvd is installed, you may configure it:
# Allow NTP in from VLAN3
ip saddr $net_v3_ip4 udp dport ntp ct state established,new accept


=== /etc/radvd.conf ===
# Allow rest from VLAN3
Note this will cause an IPv6 address to be routed to your systems. Those '''systems will now leak via IPv6 to the Internet''' if you're on a subnet like 192.168.2.0/24 using an aliased connection.
ip saddr $net_v3_ip4 ct state established,new accept


To mitigate this we would use VLANs, which behave as separate network interfaces. For that you need to replaced the unmanaged switch with a managed one, and each interface, eg eth0:2 with eth0.2.
# Allow mailserver direct from eth1 from VLAN3
oifname "eth1" ip saddr $net_v3_ip4 ip daddr $mailserver accept
}


<pre>interface eth0 {
chain LOG_DROP {
log prefix "Dropped v4: " drop
}
}


  # We are sending advertisements (route)
#
  AdvSendAdvert on;
# NAT Table (IPv4)
# Translation of packets happens to our single external address
# Forwarding of ports through our public interfaces
#
table ip nat {
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
# Port forwarding for Bittorrent on workstation through VPN
iifname "tun0" tcp dport 20001 dnat to $workstation
iifname "tun0" udp dport 20001 dnat to $workstation
}
 
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
 
# Allows routing to our modem subnet so we can access the web interface
oifname "eth0" ip saddr $net_v2_ip4 ip daddr $modem tcp dport http masquerade
oifname "eth0" ip saddr $net_v3_ip4 ip daddr $modem tcp dport http masquerade
 
# Allows routing to printer
oifname "eth0" ip saddr $net_v2_ip4 ip daddr $printer masquerade
oifname "eth0" ip saddr $net_v3_ip4 ip daddr $printer masquerade


  # Suggested Maximum Transmission setting for using the
# Masquerade behind NAT
  # Hurricane Electric Tunnel Broker.
oifname "tun0" masquerade
  # AdvLinkMTU 1480;
oifname "eth1" masquerade
}
}


  # We have native Dual Stack IPv6 so we can use the regular MTU
#
  # http://blogs.cisco.com/enterprise/ipv6-mtu-gotchas-and-other-icmp-issues
# Filter Table (IPv6)
  AdvLinkMTU 1500;
# Filtering things coming IN and OUT of the router
#
table ip6 filter {
chain INPUT {
# Create rule chain per input interface for input packets (for host itself)
type filter hook input priority filter; policy drop;
iifname "lo" accept
iifname "eth0.2" jump IN_V2_ISP
iifname "eth0.3" jump IN_V3_VPN
iifname "eth1" jump IN_ETH1
iifname "tun0" jump IN_TUN0
}


  prefix 2001:0db8:1234:b001::/64 {
chain FORWARD {
    AdvOnLink on;
# Track forwarded packets
    AdvAutonomous on; # SLAAC based on EUI
type filter hook forward priority filter; policy drop;
    AdvRouterAddr on;
ct state established,related accept
  };


  RDNSS 2001:0db8:1234:0001::1 {
# Create rule chain per input interface for forwarding packets
  };
iifname "eth0.2" jump FWD_V2_ISP
  # DNSSL example.id.au {
iifname "eth0.3" jump FWD_V3_VPN
  # };
# iifname "tun0" jump FWD_TUN0
};</pre>


Add radvd to the default run level:
# Rate limit ICMPv6 PING
icmpv6 type echo-request limit rate 30/minute accept
}


{{cmd|rc-update add radvd default}}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}


== Enable Privacy extensions in /etc/sysctl.conf ==
# chain FWD_TUN0 {
# We could forward ports IPv6 ports through the VPN here
# }


When a client acquires an address through SLAAC its IPv6 address is derived from the advertised prefix and the MAC address of the network interface of the client. This may raise security concerns as the MAC address of the computer can be easily derived by the IPv6 address. In order to tackle this problem the ''IPv6 Privacy Extensions'' standard ([https://tools.ietf.org/html/rfc4941 RFC 4941]) has been developed. With privacy extensions the kernel generates a ''temporary'' address that is mangled from the original autoconfigured address. Private addresses are preferred when connecting to a remote server so the original address is hidden. To enable Privacy Extensions reproduce the following steps:
chain FWD_V2_ISP {
# Forward VLAN2 to ISP
ip6 saddr $net_gua_v2_ip6 accept
}


<pre># Enable IPv6 Privacy Extensions
chain FWD_V3_VPN {
net.ipv6.conf.all.use_tempaddr = 2
# Forward VLAN3 to VPN
net.ipv6.conf.default.use_tempaddr = 2
ip6 saddr $net_ula_v3_ip6 accept
net.ipv6.conf.nic0.use_tempaddr = 2
}
...
net.ipv6.conf.nicN.use_tempaddr = 2</pre>


== Using DHCPv6 ==
chain IN_ETH1 {
You may decide you want more control over your network address assignment. For this you'll need to use DHCPv6. DHCPv4 and DHCPv6 need to run on separate instances of DHCPD.
# Accept incoming tracked ETH1 connection
ct state established,related accept


Make a symlink for the init script:
# Allow and rate limit ICMP
{{cmd|ln -s /etc/init.d/dhcpd /etc/init.d/dhcpdv6}}
icmpv6 type packet-too-big accept
meta l4proto ipv6-icmp limit rate 30/second accept


Include it in the router provision file:
# Allow DHCPv6 PD on Link Local from ISP
{{cmd|lbu include /etc/init.d/dhcpdv6}}
ip6 saddr fe80::/10 udp sport dhcpv6-server udp dport dhcpv6-client ct state established,new accept


Copy the DHCP Daemon configuration file:
# Allow Router advetisements/solict form ISP
{{cmd|cp /etc/conf.d/dhcpd /etc/conf.d/dhcpdv6}}
ip6 saddr fe80::/10 icmpv6 type nd-router-advert accept
ip6 saddr fe80::/10 icmpv6 type nd-neighbor-solicit accept
ip6 saddr fe80::/10 icmpv6 type nd-neighbor-advert accept


Enable it to run on IPv6. DHCPD can only run on one IP protocol at a time. By default it defaults to IPv4.
# Log dropped packets coming in on ETH1
{{cmd|<nowiki>sed -i 's/# DHCPD_OPTS=""/DHCPD_OPTS="-6"/g' /etc/conf.d/dhcpdv6</nowiki>}}
jump LOG_DROP
}


Copy the DHCP configuration file:
chain IN_TUN0 {
{{cmd|cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpdv6.conf}}
# Accept incoming tracked TUN0 connection
ct state established,related accept


Change the owner of the configurations to the dhcp user and group
# Allow and rate limit ICMP
{{cmd|chown -R dhcp:dhcp /etc/dhcp}}
icmpv6 type packet-too-big accept
meta l4proto ipv6-icmp limit rate 30/second accept


=== /etc/dhcp/dhcpdv6.conf ===
# Log dropped packets on VPN
You will want to edit your MAC address in the host declarations. The client-id or DUID can be found in /etc/dhcpcd.duid when you've installed dhcpcd on your client.
jump LOG_DROP
}


You can also see it in /var/log/messages on your router when a client tries to authenticate on your network eg:
chain IN_V2_ISP {
# Allow tracked connections in from ETH1 to VLAN2
ip6 saddr $net_gua_v2_ip6 ct state established,new accept


<pre>dhcpd: Advertise NA: address 2001:0db8:1234:0001::240 to client with duid <DEVICE DUID> iaid = <DEVICE IAID> valid for 43200 seconds</pre>
# Allow ICMP in from VLAN2
meta l4proto ipv6-icmp accept
}


Currently [https://code.google.com/p/android/issues/detail?id=32621 Android does not have DHCPv6 support] and Google seem unwilling to add it.
chain IN_V3_VPN {
# Allow tracked connections in from tun0 to VLAN3
ip6 saddr $net_ula_v3_ip6 ct state established,new accept


If you're using a version of dhcpcd below 6.9.3 you may need to set "ipv6ra_accept_nopublic" in your /etc/dhcpcd.conf.
# Allow ICMP in from VLAN3
meta l4proto ipv6-icmp accept
}


<pre>authoritative;
chain LOG_DROP {
ddns-update-style interim;
log prefix "Dropped v6: " drop
}
}


shared-network home {
#
  subnet6 2001:0db8:1234:0001::/64 {
# Mangle Table (IPv6)
    range6 2001:0db8:1234:0001::10 2001:0db8:1234:0001::240;
#
    range6 2001:0db8:1234:0001:: temporary;
table ip6 mangle {
    option dhcp6.name-servers 2001:0db8:1234:0001::1;
chain PREROUTING {
    option dhcp6.sntp-servers 2001:0db8:1234:0001::1;
type filter hook prerouting priority mangle; policy accept;
    allow unknown-clients;
  }


  subnet6 fde4:8dba:82e1:ffff::/64 {
# Drop unusually large ping packets
    range6 fde4:8dba:82e1:ffff::10 fde4:8dba:82e1:ffff::240;
icmpv6 type echo-request meta length 170-65535 drop
    range6 fde4:8dba:82e1:ffff:: temporary;
}
    option dhcp6.name-servers 2001:0db8:1234:0001::1;
    option dhcp6.sntp-servers 2001:0db8:1234:0001::1;
    ignore unknown-clients;
  }
}
}


host Gaming_Computer {
#
  hardware ethernet 00:53:00:FF:FF:11;;
# NAT Table (IPv6)
  host-identifier option dhcp6.client-id <YOUR_DUID>;
# Translation of packets happens to our single external address
  fixed-address6 2001:0db8:1234:0001::20;
# only used for the VPN as our ISP give us a /56 range to split up
  fixed-prefix6 2001:0db8:1234:0001::/64;
#
  option dhcp6.name-servers 2001:0db8:1234:0001::1;
table ip6 nat {
  option dhcp6.sntp-servers 2001:0db8:1234:0001::1;
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
oifname "tun0" masquerade
}
}
}


host Linux Workstation {
#
  hardware ethernet 00:53:00:FF:FF:22;;
# Raw Table - IPv4/IPv6
  host-identifier option dhcp6.client-id <YOUR_DUID>;
#
  fixed-address6 fde4:8dba:82e1:ffff::21;
table inet raw {
  fixed-prefix6 2001:0db8:1234:0001::/64;
set bogon-bn-nonagg-set {
  option dhcp6.name-servers 2001:0db8:1234:0001::1;
type ipv4_addr
  option dhcp6.sntp-servers 2001:0db8:1234:0001::1;
flags interval
elements = { 0.0.0.0/8, 10.0.0.0/8,
    100.64.0.0/10, 127.0.0.0/8,
    169.254.0.0/16, 172.16.0.0/12,
    192.0.0.0/24, 192.0.2.0/24,
    192.168.0.0/16, 198.18.0.0/15,
    198.51.100.0/24, 203.0.113.0/24,
    224.0.0.0/4, 240.0.0.0-255.255.255.255 }
}
 
set lo-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { 127.0.0.0/8 }
}
 
set eth0-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { $net_v1_ip4 }
}
 
set eth0.2-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { 192.168.2.0/24, 192.168.3.0/24,
    192.168.4.0/24 }
}
 
set eth0.3-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { 192.168.2.0/24, 192.168.3.0/24,
    192.168.4.0/24 }
}
 
set eth0.4-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { 192.168.2.0/24, 192.168.3.0/24,
    192.168.4.0/24 }
}
 
set eth1-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { 192.168.0.0/30, 255.255.255.255 }
}
 
set tun0-allowed-net-ip4-set {
type ipv4_addr
flags interval
elements = { 172.16.32.0/20, 172.16.48.0/20 }
}
 
set lo-allowed-net-ip6-set {
type ipv6_addr
flags interval
elements = { ::1/128 }
}
 
set eth0-allowed-net-ip6-set {
type ipv6_addr
flags interval
elements = { fde4:8dba:82e1:fff1::/64,
    fe80::/10 }
}
 
set eth0.2-allowed-net-ip6-set {
type ipv6_addr
flags interval
elements = { 2001:0db8:1234:ffff::/64,
    fe80::/10 }
}
 
set eth0.3-allowed-net-ip6-set {
type ipv6_addr
flags interval
elements = { fde4:8dba:82e1:fff3::/64,
    fe80::/10 }
}
 
set eth0.4-allowed-net-ip6-set {
type ipv6_addr
flags interval
elements = { fde4:8dba:82e1:fff4::/64,
    fe80::/10 }
}
 
chain PREROUTING {
type filter hook prerouting priority raw; policy accept;
 
# Allows traffic from NNTP/DNS vpn gateway
iifname "tun0" ip saddr $vpn_gateway accept;
 
# Allows traffic originating from router to vpn gateway
ip daddr $vpn_gateway accept
 
# Allows traffic originating from router to modem
ip daddr $modem accept
 
# Block specified bogons coming in from ISP and VPN
# (unlikely to happen as they filter them on their router)
#iifname "eth1" ip saddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_PR;
#iifname "tun0" ip saddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_PR;
}
 
chain OUTPUT {
type filter hook output priority raw; policy accept;
 
# Allows my excepted ranges
iifname vmap { lo : jump lo-allowed-net, eth0 : jump eth0-allowed-net,
    eth0.2 : jump eth0.2-allowed-net, eth0.3 : jump eth0.3-allowed-net,
    eth0.4 : jump eth0.4-allowed-net, eth1 : jump eth1-allowed-net,
    tun0 : jump tun0-allowed-net };
 
oifname vmap { lo : jump lo-allowed-net, eth0 : jump eth0-allowed-net,
    eth0.2 : jump eth0.2-allowed-net, eth0.3 : jump eth0.3-allowed-net,
    eth0.4 : jump eth0.4-allowed-net, eth1 : jump eth1-allowed-net,
    tun0 : jump tun0-allowed-net };
 
 
# Drop any remaining bogons that try to leave the router
oifname "eth1" ip daddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_IN;
oifname "tun0" ip daddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_IN;
}
 
chain lo-allowed-net {
ip saddr @lo-allowed-net-ip4-set accept
ip6 saddr @lo-allowed-net-ip6-set accept
}
 
chain eth0-allowed-net {
ip saddr @eth0-allowed-net-ip4-set accept
ip6 saddr @eth0-allowed-net-ip6-set accept
#log prefix "Allowed packet allow net 0: " level info
}
 
chain eth0.2-allowed-net {
ip saddr @eth0.2-allowed-net-ip4-set accept
ip6 saddr @eth0.2-allowed-net-ip6-set accept
#log prefix "Allowed packet allow net 2: " level info
}
 
chain eth0.3-allowed-net {
ip saddr @eth0.3-allowed-net-ip4-set accept
ip6 saddr @eth0.3-allowed-net-ip6-set accept
#log prefix "Allowed packet allow net 3: " level info
}
 
chain eth0.4-allowed-net {
ip saddr @eth0.4-allowed-net-ip4-set accept
ip6 saddr @eth0.4-allowed-net-ip6-set accept
#log prefix "Allowed packet allow net 4: " level info
}
 
chain eth1-allowed-net {
ip saddr @eth1-allowed-net-ip4-set accept
#log prefix "Allowed packet allow eth1: " level info
}
 
    chain tun0-allowed-net {
ip saddr @tun0-allowed-net-ip4-set accept
#log prefix "Allowed packet allow tun0: " level info
}
 
chain LOG_DROP_BOGON_IN {
log prefix "Dropped Bogon outgoing " drop
}
chain LOG_DROP_BOGON_OUT {
log prefix "Dropped Bogon incoming " drop
}
chain LOG_DROP_BOGON_PR {
log prefix "Dropped Bogon prerouting " drop
}
}</pre>
}</pre>


=== /etc/radvd.conf ===
Add nftables to the default run level:
Finally you'll want to change add "AdvManagedFlag", and "AdvOtherConfigFlag". You will also want to toggle "AdvAutonomous" to off if you do not want IPs generated by SLAAC based on EUI.
 
{{cmd|rc-update add nftables default}}
 
= Router Advertisements =
 
Now we need to configure radvd to give router advertisements to out VLANs for addressing and routing.
 
{{cmd|apk add radvd}}
 
Once radvd is installed, you may configure it:
 
== /etc/radvd.conf ==


<pre>interface eth0 {
<pre>interface eth0.2 {


   # We are sending advertisements (route)
   # We are sending advertisements (route)
Line 495: Line 1,344:


   # We have native Dual Stack IPv6 so we can use the regular MTU
   # We have native Dual Stack IPv6 so we can use the regular MTU
   # http://blogs.cisco.com/enterprise/ipv6-mtu-gotchas-and-other-icmp-issues
   # https://blogs.cisco.com/enterprise/ipv6-mtu-gotchas-and-other-icmp-issues
   AdvLinkMTU 1500;
   AdvLinkMTU 1500;
 
 
   prefix 2001:0db8:1234:0001::/64 {
   prefix ::/64 {
     AdvOnLink on;
     AdvOnLink on;
     AdvAutonomous off;
     AdvAutonomous on; ## SLAAC based on EUI
     AdvRouterAddr on;
     AdvRouterAddr on;
   };
   };
};


   # RDNSS 2001:0db8:1234:0001::1 {
interface eth0.3 {
  # };
 
  # DNSSL example.id {
  AdvSendAdvert on;
   # };
  AdvManagedFlag on;
  AdvOtherConfigFlag on;
  AdvLinkMTU 1500;
 
   # Helps the route not get lost when on WiFi with packet loss
  MaxRtrAdvInterval 30;
  AdvDefaultLifetime 9000;
 
  prefix fde4:8dba:82e1:fff3::/64 {
    AdvOnLink on;
    AdvAutonomous on; ## SLAAC based on EUI
   };
};</pre>
};</pre>
Add radvd to the default run level:
{{cmd|rc-update add radvd default}}
= DHCP =
You may decide you want more control over your network address assignment. I like to have certain hosts get certain addresses when they connect on a particular VLAN, note v2 and v3. You can do this with:
<pre>authoritative;
ddns-update-style interim;
subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.21 192.168.1.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.1.255;
    option routers 192.168.1.1;
    option ntp-servers 192.168.1.1;
    option domain-name-servers 192.168.1.1;
    allow unknown-clients;
        host wifi_ap {
            hardware ethernet <mac_addess>;
            fixed-address 192.168.1.11;
            option subnet-mask 255.255.255.0;
            option routers 192.168.1.1;
            option host-name "<hostname>";
        }
}
subnet 192.168.2.0 netmask 255.255.255.0 {
    range 192.168.2.40 192.168.2.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.2.255;
    option routers 192.168.2.1;
    option ntp-servers 192.168.2.1;
    option domain-name-servers 192.168.2.1;
    allow unknown-clients;
        host host-v2 {
            hardware ethernet <mac_address>;
            fixed-address 192.168.2.30;
            option subnet-mask 255.255.255.0;
            option broadcast-address 192.168.2.255;
            option routers 192.168.2.1;
            option host-name "<hostname>";
        }
}
subnet 192.168.3.0 netmask 255.255.255.0 {
    range 192.168.3.20 192.168.3.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.3.255;
    option routers 192.168.3.1;
    option ntp-servers 192.168.3.1;
    option domain-name-servers 192.168.3.1;
    ignore unknown-clients;
        host host-v3 {
            hardware ethernet <mac_address>;
            fixed-address 192.168.3.30;
            option subnet-mask 255.255.255.0;
            option broadcast-address 192.168.3.255;
            option routers 192.168.3.1;
            option host-name "<hostname>";
        }
}
subnet 192.168.4.0 netmask 255.255.255.0 {
    range 192.168.4.40 192.168.4.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.4.255;
    option routers 192.168.4.1;
    option ntp-servers 192.168.4.1;
    option domain-name-servers 192.168.4.1;
    host printer {
            hardware ethernet <PRINTER_MAC_ADDRESS>;
            fixed-address 192.168.4.9;
            option subnet-mask 255.255.255.0;
            option broadcast-address 192.168.4.255;
            option routers 192.168.4.1;
            option host-name "My_Printer";
        }  ignore unknown-clients;
}</pre>
For IPv6 I don't use DHCPv6 because Android doesn't support it. I just let SLAAC assign addresses.
= VPN Tunnel VLAN3 =
Install the necessary packages:
{{cmd|apk add openvpn iproute2 iputils}}
== /etc/modules ==
You'll want to add the tun module
<pre>tun</pre>
== /etc/iproute2/rt_tables ==
Add the two routing tables to the bottom of rt_tables. It should look something like this:
<pre>#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep
1 ISP
2 VPN
3 LAN</pre>
== /etc/network/fwmark_rules ==
In this file we want to put the fwmark rules and set the correct priorities.
<pre>#!/bin/sh
# Normal packets to go direct out WAN
/sbin/ip rule add fwmark 1 table ISP prio 100
/sbin/ip -6 rule add fwmark 1 table ISP prio 100
# Put packets destined into VPN when VPN is up
/sbin/ip rule add fwmark 2 table VPN prio 200
/sbin/ip -6 rule add fwmark 2 table VPN prio 200
# Prevent packets from being routed out when VPN is down.
# This prevents packets from falling back to the main table
# that has a priority of 32766
/sbin/ip rule add prohibit fwmark 2 prio 300
/sbin/ip -6 rule add prohibit fwmark 2 prio 300</pre>
== /etc/network/route_LAN ==
<pre>#!/bin/sh
#
# This script adds the LAN routes.
#
# Add routes from ISP to LAN
/sbin/ip route add 192.168.2.0/24 dev eth0.2 table LAN
# Add route from VPN to LAN
/sbin/ip route add 192.168.3.0/24 dev eth0.3 table LAN
# Add route from LAN to it's own table
/sbin/ip route add 192.168.4.0/24 dev eth0.4 table LAN</pre>
== /etc/ppp/ip-up ==
Next up we want to create the routes that should be run when PPP comes online. There are special hooks we can use in ip-up and ip-down to refer to the IP address, [https://ppp.samba.org/pppd.html#sect13 ppp man file - Scripts] You can also read about them in your man file if you have ppp-doc installed.
<pre>#!/bin/sh
#
# This script is run by pppd when there's a successful ppp connection.
#
# Flush out any old rules that might be there
/sbin/ip route flush table ISP
# Add route to table from subnets on LAN
/sbin/ip route add 192.168.2.0/24 dev eth0.2 table ISP
/sbin/ip route add 192.168.3.0/24 dev eth0.3 table ISP
# Add route from IP given by ISP to the table
/sbin/ip rule add from ${IPREMOTE} table ISP prio 100
# Add a default route
/sbin/ip route add table ISP default via ${IPREMOTE} dev ${IFNAME}
# Add route to LAN subnet
/sbin/ip route add 192.168.4.0/24 dev eth0.4 table ISP</pre>
== /etc/ppp/ip-down ==
<pre>#!/bin/sh
#
# This script is run by pppd after the connection has ended.
#
# Delete the rules when we take the interface down
/sbin/ip rule del from ${IPREMOTE} table ISP prio 100</pre>
== /etc/openvpn/route-up-fwmark.sh ==
OpenVPN needs similar routing scripts and it also has it's own special hooks that allow you to specify particular values. A full list is here [https://openvpn.net/index.php/open-source/documentation/manuals/65-openvpn-20x-manpage.html#lbAS OpenVPN man file - Environmental Variables]
<pre>#!/bin/sh
#
# This script is run by OpenVPN when there's a successful VPN connection.
#
# Flush out any old rules that might be there
/sbin/ip route flush table VPN
# Add route to table from 192.168.3.0/24 subnet on LAN
/sbin/ip route add 192.168.3.0/24 dev eth0.3 table VPN
/sbin/ip -6 route add fde4:8dba:82e1:fff3::/64 dev eth0.3 table VPN
# Add route from VPN interface IP to the VPN table
/sbin/ip rule add from ${ifconfig_local} table VPN prio 200
/sbin/ip -6 rule add from fde4:8dba:82e1:fff3::/64 table VPN prio 200
# Add a default route
/sbin/ip route add default via ${ifconfig_local} dev ${dev} table VPN
/sbin/ip -6 route add default dev tun0 table VPN
# Add route to LAN only subnet
/sbin/ip route add 192.168.4.0/24 dev eth0.4 table VPN
# Add route to IP on VPN for traffic originating from the router
/sbin/ip route add 172.16.32.1 dev tun0</pre>
== /etc/openvpn/route-down-fwmark.sh ==
<pre>#!/bin/sh
#
# This script is run by OpenVPN after the connection has ended
#
# Delete the rules when we take the interface down
/sbin/ip rule del from ${ifconfig_local} table VPN prio 200
/sbin/ip -6 rule del from fde4:8dba:82e1:fff3::/64 table VPN prio 200
# Delete route to IP on VPN for traffic originating from the router
/sbin/ip route del 172.16.32.1 dev tun0</pre>
== OpenVPN Routing ==
Usually when you connect with OpenVPN the remote VPN server will push routes down to your system. We don't want this as we still want to be able to access the internet without the VPN. We have also created our own routes that we want to use earlier in this guide.
You'll need to add this to the bottom of your OpenVPN configuration file:
<pre># Prevents default gateway from being set on the default routing table
route-noexec
# Allows route-up script to be executed
script-security 2
# Calls custom shell script after connection to add necessary routes
route-up /etc/openvpn/route-up-fwmark.sh
route-pre-down /etc/openvpn/route-pre-down-fwmark.sh</pre>
My VPNs are arranged like this in /etc/openvpn:
OpenVPN configuration file for that server:
<pre>countrycode.serverNumber.openvpn.conf</pre>
OpenVPN certs for that server:
<pre>countrycode.serverNumber.openvpn/countrycode.serverNumber.openvpn.crt
countrycode.serverNumber.openvpn/countrycode.serverNumber.openvpn.key
countrycode.serverNumber.openvpn/myKey.crt
countrycode.serverNumber.openvpn/myKey.key</pre>
So I use this helpful script to automate the process of changing between servers:
<pre>#!/bin/sh
vpn_server_filename=$1
rm /etc/openvpn/openvpn.conf
ln -s $vpn_server_filename /etc/openvpn/openvpn.conf
chown -R openvpn:openvpn /etc/openvpn
chmod -R a=-rwx,u=+rX /etc/openvpn
chmod u=x /etc/openvpn/*.sh*
if grep -Fxq "#CustomStuffHere" openvpn.conf
then
    echo "Not adding custom routes, this server has been used previously"
else
    echo "Adding custom route rules"
cat <<EOF >> /etc/openvpn/openvpn.conf
#CustomStuffHere
# Prevents default gateway from being set on the default routing table
route-noexec
# Allows route-up script to be executed
script-security 2
# Calls custom shell script after connection to add necessary routes
route-up /etc/openvpn/route-up-fwmark.sh
route-pre-down /etc/openvpn/route-pre-down-fwmark.sh
# Logging of OpenVPN to file
#log /etc/openvpn/openvpn.log
EOF
fi
echo "Remember to set BitTorrent port forward in your VPN control panel"</pre>
That way I can simply change between servers by running:
{{cmd|changevpn.sh countrycode.serverNumber.openvpn}}
and then restart openvpn. I am also reminded to put the port forward through on the VPN control panel so my BitTorrent client is connectable:
{{cmd|service openvpn restart}}
Finally add openvpn to the default run level
{{cmd|rc-update add openvpn default}}
[[category: Raspberry]]
[[category: VPN]]

Latest revision as of 20:28, 30 August 2023

I have split this off the main article Linux Router with VPN on a Raspberry Pi IPv6 implementation requires a few changes to the initial article to work. I haven't duplicated everything here however, just the stuff that relates to IPv6.

Introduction

IPv6 introduces a number of new complexities into our network. If you've completed previous IPv4 only guide Linux Router with VPN on a Raspberry Pi then read on.

Your VPN provider may only offers you a single stack connection (no IPv6). You won't be able to implement IPv6 addressing on VLAN 3 to carry your IPv6 traffic out of the VPN. If your ISP gives you IPv6 addressing you may still implement addressing on VLAN2 to carry traffic directly to your ISP. In this example I do both.

If you don't know much about IPv6 then these pages might be of interest to get you up to speed.


Network Diagram IPv4 and IPv6
Network Diagram IPv4 and IPv6

Enabling IPv6 support

Assuming you're using the Alpine Linux kernel, IPv6 support is available separately as a module.

modprobe ipv6

To add the module to our startup configuration.

echo "ipv6" >> /etc/modules

/etc/sysctl.d/local.conf

Modify the sysctl section to include IPv6 support:

# Controls IP packet forwarding
net.ipv4.ip_forward = 1

# Needed to use fwmark
net.ipv4.conf.all.rp_filter = 2

# https://vk5tu.livejournal.com/37206.html
# What's this special value "2"? Originally the value was "1", but this 
# disabled autoconfiguration on all interfaces. That is, you couldn't appear 
# to be a router on some interfaces and appear to be a host on other 
# interfaces. But that's exactly the mental model of a ADSL router. 

# Controls IP packet forwarding
net.ipv6.conf.all.forwarding = 2
net.ipv6.conf.default.forwarding = 2

# Accept Router Advertisments
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.default.accept_ra = 2

# We are a router so disable temporary addresses
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.default.use_tempaddr = 0

/etc/network/interfaces

Add an IPv6 interface for each VLAN. Note we don't need to add one for VLAN2 because dhcpcd will take care of that for us using our ISPs router advertisements. Also note the . (dot notation) represents a VLAN interface where as : (colon notation) used in the previous article represented an IP address aliased on an interface.

The reason we need VLANs here is because each VLAN has it's own broadcast and we don't want our router advertisements to be putting routes and addresses on all the interfaces. It also helps us with a more secure design, but requires a managed switch.

# VLAN 2 - DESTINED FOR ISP
auto eth0.2
iface eth0.2 inet static
    address 192.168.2.1
    netmask 255.255.255.0
    broadcast 192.168.2.255
    post-up /etc/network/fwmark_rules

# VLAN 3 - DESTINED FOR VPN
auto eth0.3
iface eth0.3 inet static
    address 192.168.3.1
    netmask 255.255.255.0
    broadcast 192.168.3.255

iface eth0.3 inet6 static
    address fde4:8dba:82e1:fff3::1
    netmask 64
    autoconf 0
    accept_ra 0
    privext 0

# VLAN 4 - LAN ONLY
auto eth0.4
iface eth0.4 inet static
    address 192.168.4.1
    netmask 255.255.255.0
    broadcast 192.168.4.255
    post-up /etc/network/route_LAN

iface eth0.4 inet6 static
    address fde4:8dba:82e1:fff4::1
    netmask 64
    autoconf 0
    accept_ra 0
    privext 0

Configuring PPP

Next up we need to configure our router to be able to dial a PPP connection with our modem.

See PPP, you will want to make sure you set your WAN interface, in this example we used eth1.

Check system log

Restart ppp.

poff yourISP

pon yourISP

In /var/log/messages you should see something like

pppd[]: Plugin rp-pppoe.so loaded.
pppd[]: RP-PPPoE plugin version 3.8p compiled against pppd 2.4.7
pppd[]: pppd 2.4.7 started by root, uid 0
pppd[]: PPP session is 49969
pppd[]: Connected to 00:53:00:ff:ff:f0 via interface eth1
pppd[]: Using interface ppp0
pppd[]: Connect: ppp0 <--> eth1
pppd[]: CHAP authentication succeeded
pppd[]: CHAP authentication succeeded
pppd[]: peer from calling number 00:53:00:FF:FF:F0 authorized
pppd[]: local  LL address fe80::0db8:ffff:ffff:fff1
pppd[]: remote LL address fe80::0db8:ffff:ffff:fff0
pppd[]: local  IP address 192.0.2.1
pppd[]: remote IP address 192.0.2.0
pppd[]: primary   DNS address 192.0.2.10
pppd[]: secondary DNS address 192.0.2.20

You should be able to now ping things such as

ping6 ipv6.google.com

from your router.

Prefix Delegation

The next step will be to configure DHCPv6 Prefix Delegation with your ISP. Install dhcpcd. While many guides do use the wide-dhcpv6-client it should be noted this is unmaintained and not included in Alpine Linux.

Don't use the ISC's dhclient either as this does not support Prefix Delegations on PPP links without a patch.

apk add dhcpcd

You can check out the manual for dhcpcd.conf. Installing dhcpcd-doc will allow you to read the man file. Eg:

apk add dhcpcd-doc

/etc/dhcpcd.conf

apk add dhcpcd

If the main repositories have dhcpcd below version 7.0.7 (at time of writing AlpineLinux 3.8 and below) you will need to use the latest version from edge as it fixes a bug with unique link local addresses on our VLANs dhcpcd ChangeLog[Dead Link] this patch[Dead Link] already applied in v7.0.7

apk add dhcpcd@edge

If you haven't you may need to add the edge repository for pinning Alpine Linux package management#Repository_pinning

# Enable extra debugging
#debug
#logfile /var/log/dhcpcd.log

# Allow users of this group to interact with dhcpcd via the control
# socket.
#controlgroup wheel

# Inform the DHCP server of our hostname for DDNS.
hostname gateway

# Use the hardware address of the interface for the Client ID.
#clientid
# or
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as
# per RFC4361. Some non-RFC compliant DHCP servers do not reply with
# this set. In this case, comment out duid and enable clientid above.
duid

# Persist interface configuration when dhcpcd exits.
persistent

# Rapid commit support.
# Safe to enable by default because it requires the equivalent option
# set on the server to actually work.
option rapid_commit

# A list of options to request from the DHCP server.
option domain_name_servers, domain_name, domain_search, host_name
option classless_static_routes

# Most distributions have NTP support.
option ntp_servers

# Respect the network MTU.
# Some interface drivers reset when changing the MTU so disabled by
# default.
#option interface_mtu

# A ServerID is required by RFC2131.
require dhcp_server_identifier

# Generate Stable Private IPv6 Addresses instead of hardware based
# ones
slaac private

# A hook script is provided to lookup the hostname if not set by the
# DHCP server, but it should not be run by default.
nohook lookup-hostname

# IPv6 Only
ipv6only

# Disable solicitations on all interfaces
noipv6rs

# Wait for IP before forking to background
waitip 6

# Don't touch DNS
nohook resolv.conf

# Use the interface connected to WAN
interface ppp0
    ipv6rs # enable routing solicitation get the default IPv6 route
    iaid 1
    ia_pd 1/::/56 eth0.2/2/64

Add dhcpcd to the default run level:

rc-update add dhcpcd default

Configuring firewall for IPv4 and IPv6 traffic

iptables

Here are some rules for iptables that I am currently using, yours may look something similar.

#########################################################################
# Uses 192.168.1.0 VLAN1 Management Untagged - no route
#      192.168.2.0 VLAN2                     - route to ISP
#      192.168.3.0 VLAN3                     - route to VPN
#      192.168.4.0 VLAN4                     - no route
# 
# Packets to/from 192.168.1.0/24 not in any VLAN ie tagged
# Packets to/from 192.168.2.0/24 are marked with 0x1 and routed to ISP
# Packets to/from 192.168.3.0/24 are marked with 0x2 and routed to VPN
# Packets to/from 192.168.4.0/24 are routed to LAN and not forwarded onto
#                                    the internet
#
# These destinations will always be marked with 0x1 from VLAN3:
#
# <ip_of_exception>       some exception
#########################################################################

# 
# Raw Table
#
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# Create a log drop chain
:LOG_DROP_BOGON - [0:0]
:LOG_DROP_MSFT - [0:0]

# Create output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]

# Allows traffic from VPN's DNS server
-A PREROUTING -s 172.16.32.1/32 -i tun0 -j ACCEPT

# Block specified bogons coming in from ISP and VPN
# (unlikely to happen as they filter them on their router)
-A PREROUTING -i ppp0 -m set --match-set bogon-bn-nonagg src -j LOG_DROP_BOGON
-A PREROUTING -i tun0 -m set --match-set bogon-bn-nonagg src -j LOG_DROP_BOGON

# Block MSFT known tracking IPs from https://github.com/Nummer/Destroy-Windows-10-Spying
-A PREROUTING -i ppp0 -m set --match-set dropped-msft-ip-ipv4  src -j LOG_DROP_MSFT
-A PREROUTING -i tun0 -m set --match-set dropped-msft-ip-ipv4  src -j LOG_DROP_MSFT

# Allows my excepted ranges
-A PREROUTING -m set --match-set allowed-nets-ipv4 src,src -j ACCEPT

# Allows traffic originating from router to remote address on VPN
-A OUT_TUN0 -d 172.16.32.1 -j ACCEPT

# Pass output interface to corresponding chain
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0

# Log drop chain
-A LOG_DROP_BOGON -j LOG --log-prefix "Dropped Bogon (ipv4) : " --log-level 6
-A LOG_DROP_BOGON -j DROP
-A LOG_DROP_MSFT -j LOG --log-prefix "Dropped MSFT (ipv4) : " --log-level 6
-A LOG_DROP_MSFT -j DROP

# Block packets originating from the router destined to bogon ranges
-A OUT_PPP0 -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON

# Blocks packets originating from the router destined to bogon ranges
-A OUT_TUN0 -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON

# Block packets originating from the router destined to msft ranges
-A OUT_PPP0 -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT

# Blocks packets originating from the router destined to msft ranges
-A OUT_TUN0 -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT

COMMIT

#
# NAT Table
# This is where translation of packets happens and "forwarding" of ports
# to specific hosts.
#
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Port forwarding for Bittorrent through VPN
-A PREROUTING -i tun0 -p tcp -m tcp --dport 20001 -j DNAT --to-destination 192.168.3.30
-A PREROUTING -i tun0 -p udp -m udp --dport 20001 -j DNAT --to-destination 192.168.3.30

# Allows routing to our modem subnet so we can access the web interface
-A POSTROUTING -s 192.168.2.0/24 -d 192.168.0.1/32 -o eth1 -p tcp -m tcp --dport 80 -j MASQUERADE
-A POSTROUTING -s 192.168.3.0/24 -d 192.168.0.1/32 -o eth1 -p tcp -m tcp --dport 80 -j MASQUERADE

# Allows routing to Printer
-A POSTROUTING -s 192.168.2.0/24 -d 192.168.4.9/32 -o eth0 -j MASQUERADE
-A POSTROUTING -s 192.168.3.0/24 -d 192.168.4.9/32 -o eth0 -j MASQUERADE

# Allows hosts of the network to use the VPN tunnel
-A POSTROUTING -o tun0 -j MASQUERADE

# Allows hosts of the network to use the PPP tunnel
-A POSTROUTING -o ppp0 -j MASQUERADE
COMMIT

#
# Filter Table
# This is where we decide to ACCEPT, DROP or REJECT things
#
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# Create rule chain per input interface for forwarding packets
:FWD_ETH1 - [0:0]
:FWD_PPP0 - [0:0]
:FWD_TUN0 - [0:0]
:FWD_V1_MGMT - [0:0]
:FWD_V2_ISP - [0:0]
:FWD_V3_VPN - [0:0]
:FWD_V4_LANONLY - [0:0]

# Create rule chain per input interface for input packets (for host itself)
:IN_ETH1 - [0:0]
:IN_PPP0 - [0:0]
:IN_TUN0 - [0:0]
:IN_V1_MGMT - [0:0]
:IN_V2_ISP - [0:0]
:IN_V3_VPN - [0:0]
:IN_V4_LANONLY - [0:0]

# Create a drop/reject chains
:LOG_DROP - [0:0]
:LOG_DROP_BOGON - [0:0]
:LOG_DROP_MSFT - [0:0]
:LOG_REJECT_LANONLY - [0:0]

# Create an output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]

# Pass input packet to corresponding rule chain
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -j IN_V1_MGMT
-A INPUT -i eth0.2 -j IN_V2_ISP
-A INPUT -i eth0.3 -j IN_V3_VPN
-A INPUT -i eth0.4 -j IN_V4_LANONLY
-A INPUT -i eth1 -j IN_ETH1
-A INPUT -i ppp0 -j IN_PPP0
-A INPUT -i tun0 -j IN_TUN0

# Track forwarded packets
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Pass forwarded packet to corresponding rule chain
-A FORWARD -i eth0 -j FWD_V1_MGMT
-A FORWARD -i eth0.2 -j FWD_V2_ISP
-A FORWARD -i eth0.3 -j FWD_V3_VPN
-A FORWARD -i eth0.4 -j FWD_V4_LANONLY
-A FORWARD -i eth1 -j FWD_ETH1
-A FORWARD -i ppp0 -j FWD_PPP0
-A FORWARD -i tun0 -j FWD_TUN0
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0

# Forward HTTP packets from network to modem
-A FWD_ETH1 -s 192.168.0.1/32 -d 192.168.2.0/24 -p tcp -m tcp --sport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A FWD_ETH1 -s 192.168.0.1/32 -d 192.168.3.0/24 -p tcp -m tcp --sport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Forward Bittorrent Port
-A FWD_TUN0 -d 192.168.3.30/32 -p tcp -m tcp --dport 20001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A FWD_TUN0 -d 192.168.3.30/32 -p udp -m udp --dport 20001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Forward established packets to hosts in VLAN2/3 from Printer
-A FWD_V1_MGMT -s 192.168.4.9/32 -d 192.168.2.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A FWD_V1_MGMT -s 192.168.4.9/32 -d 192.168.3.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Refuse to forward bogons from VLAN1 (Untagged Management)
-A FWD_V1_MGMT -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON

# Refuse to forward msft from VLAN1 (Untagged Management)
-A FWD_V1_MGMT -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT

# Forward traffic from VLAN2 to Modem
-A FWD_V2_ISP -d 192.168.0.1/32 -j ACCEPT

# Forward traffic from VLAN2 to Printer
-A FWD_V2_ISP -d 192.168.4.9/32 -j ACCEPT

# Drop bogons from VLAN2
-A FWD_V2_ISP -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON

# Drop msft from VLAN2
-A FWD_V2_ISP -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT

# Allow rest from VLAN2
-A FWD_V2_ISP -s 192.168.2.0/24 -j ACCEPT

# Forward traffic from VLAN3 to Modem
-A FWD_V3_VPN -d 192.168.0.1/32 -j ACCEPT

# Forward traffic from VLAN3 to Printer
-A FWD_V3_VPN -d 192.168.4.9/32 -j ACCEPT

# Allow rest from VLAN3
-A FWD_V3_VPN -s 192.168.3.0/24 -j ACCEPT

# Drop bogons from VLAN3
-A FWD_V3_VPN -m set --match-set bogon-bn-nonagg dst -j LOG_DROP_BOGON

# Drop msft from VLAN3
-A FWD_V3_VPN -m set --match-set dropped-msft-ip-ipv4 dst -j LOG_DROP_MSFT

# Forward some exception to ppp0 from VLAN3
-A FWD_V3_VPN -s 192.168.3.0/24 -d <ip_of_exception>/32 -o ppp0 -j ACCEPT

# Allow in NTP from Router (this machine)
-A IN_ETH1 -s 192.168.0.1/32 -d 192.168.0.0/30 -p udp -m udp --dport 123 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow in HTTP from Router (this machine)
-A IN_ETH1 -s 192.168.0.1/32 -d 192.168.0.0/30 -p tcp -m tcp --sport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Accept incoming tracked PPP0 connection
-A IN_PPP0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Log dropped packets coming in on PPP0
# -A IN_PPP0 -j LOG --log-prefix "DROP:INPUT (ipv4) " --log-level 6
-A IN_PPP0 -j LOG_DROP

# Accept incoming tracked TUN0 connection
-A IN_TUN0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Log dropped packets coming in on TUN0
# -A IN_TUN0 -j LOG --log-prefix "DROP:INPUT (ipv4) " --log-level 6
-A IN_TUN0 -j LOG_DROP

# Allow in established packets from Printer to hosts in VLAN2/3
-A IN_V1_MGMT -s 192.168.4.9/32 -d 192.168.2.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.4.9/32 -d 192.168.3.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# FreeRadius Clients (access point A & B)
-A IN_V1_MGMT -s 192.168.1.10/32 -p tcp -m tcp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.10/32 -p udp -m udp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p tcp -m tcp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p udp -m udp --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Ubiquiti UAP Device Discovery Broadcast
-A IN_V1_MGMT -s 192.168.1.10/32 -p udp -m udp --dport 10001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p udp -m udp --dport 10001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.10/32 -p udp -m udp --dport 3478 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
-A IN_V1_MGMT -s 192.168.1.11/32 -p udp -m udp --dport 3478 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow rest in from VLAN1
-A IN_V1_MGMT -s 192.168.1.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow ssh in from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow DNS in from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT

# ALLOW NTP in from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -p udp -m udp --dport 123 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow rest from VLAN2
-A IN_V2_ISP -s 192.168.2.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow ssh in from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow DNS in from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -p udp -m udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT

# Allow NTP in from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -p udp -m udp --dport 123 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow rest from VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow some exception direct from ppp0 to VLAN3
-A IN_V3_VPN -s 192.168.3.0/24 -d <ip_of_exception>/32 -o ppp0 -j ACCEPT

# Log dropped bogons that never got forwarded
-A LOG_DROP_BOGON -j LOG --log-prefix "Dropped Bogon forward (ipv4) " --log-level 6
-A LOG_DROP_BOGON -j DROP

# Log dropped msft tracking that never got forwarded
-A LOG_DROP_MSFT -j LOG --log-prefix "Dropped MSFT forward(ipv4) " --log-level 6
-A LOG_DROP_MSFT -j DROP

# Log rejected packets
-A LOG_REJECT_LANONLY -j LOG --log-prefix "Rejected packet from LAN only" --log-level 6
-A LOG_REJECT_LANONLY -j REJECT --reject-with icmp-port-unreachable
COMMIT

#
# Mangle Table
# This is the place where our markings happen, whether they be 0x1 or 0x2
#
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Restore CONNMARK to the MARK (If one doesn't exist then no mark is set)
-A PREROUTING -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff

# If packet MARK is 2, then it means there is already a connection mark and the
# original packet came in on VPN
-A PREROUTING -s 192.168.3.0/24 -m mark --mark 0x2

# Check some exception are 0x1
-A PREROUTING -s 192.168.3.0/24 -d <ip_of_exception>/32 -m mark --mark 0x1

# Mark packets coming from 192.168.3.0/24 are 0x2
-A PREROUTING -s 192.168.3.0/24 -j MARK --set-xmark 0x2/0xffffffff

# If packet MARK is 1, then it means there is already a connection mark and the
# original packet came in on ISP
-A PREROUTING -s 192.168.2.0/24 -m mark --mark 0x1

# Mark packets 192.168.2.0/24 are 0x1
-A PREROUTING -s 192.168.2.0/24 -j MARK --set-xmark 0x1/0xffffffff

# Mark some exception as 0x1
-A PREROUTING -s 192.168.3.0/24 -d <ip_of_exception>/32 -j MARK --set-xmark 0x1/0xffffffff

# Strip mark if packet is destined for modem
-A PREROUTING -d 192.168.0.1/32 -j MARK --set-xmark 0x0/0xffffffff

# Strip mark if packet is destined for printer
-A PREROUTING -d 192.168.4.9/32 -j MARK --set-xmark 0x0/0xffffffff

# Save MARK to CONNMARK (remember iproute can't see CONNMARKs)
-A PREROUTING -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff
COMMIT

ip6tables

You'll need to modify your prefix in one of the rules.

#########################################################################
# Uses 2001:0db8:1234:ffff::1/64 VLAN2 - route to ISP
#      fde4:8dba:82e1:fff3::1/64  VLAN3 - route to VPN
#########################################################################

#
# Raw Table
#
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# Create a log drop chain
:LOG_DROP_BOGON - [0:0]

# Create output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]

# Allows my excepted ranges
-A PREROUTING -m set --match-set allowed-nets-ipv6 src,src -j ACCEPT

# Pass output interface to corresponding chain
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0
COMMIT

#
# NAT Table
# This is where translation of packets happens and "forwarding" of ports
# to specific hosts.
#
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Allows hosts of the network to use the VPN tunnel for IPv6
-A POSTROUTING -o tun0 -j MASQUERADE

COMMIT

#
# Mangle Table
#
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Drop unusually large ping packets
-A PREROUTING -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m length --length 170:65535 -j DROP
COMMIT

#
# Filter Table
# This is where we decide to ACCEPT, DROP or REJECT things
#
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# Create rule chain per input interface for forwarding packets
:FWD_ETH1 - [0:0]
:FWD_PPP0 - [0:0]
:FWD_TUN0 - [0:0]
:FWD_V1_MGMT - [0:0]
:FWD_V2_ISP - [0:0]
:FWD_V3_VPN - [0:0]
:FWD_V4_LANONLY - [0:0]

# Create rule chain per input interface for input packets (for host itself)
:IN_ETH1 - [0:0]
:IN_PPP0 - [0:0]
:IN_TUN0 - [0:0]
:IN_V1_MGMT - [0:0]
:IN_V2_ISP - [0:0]
:IN_V3_VPN - [0:0]
:IN_V4_LANONLY - [0:0]

# Create a drop/reject chains
:LOG_DROP - [0:0]
:LOG_DROP_BOGON - [0:0]
:LOG_REJECT_LANONLY - [0:0]

# Create an output chains
:OUT_PPP0 - [0:0]
:OUT_TUN0 - [0:0]

# Pass input packet to corresponding rule chain
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -j IN_V1_MGMT
-A INPUT -i eth0.2 -j IN_V2_ISP
-A INPUT -i eth0.3 -j IN_V3_VPN
-A INPUT -i eth0.4 -j IN_V4_LANONLY
-A INPUT -i eth1 -j IN_ETH1
-A INPUT -i ppp0 -j IN_PPP0
-A INPUT -i tun0 -j IN_TUN0

# Track forwarded packets
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Pass forwarded packet to corresponding rule chain
-A FORWARD -i eth0 -j FWD_V1_MGMT
-A FORWARD -i eth0.2 -j FWD_V2_ISP
-A FORWARD -i eth0.3 -j FWD_V3_VPN
-A FORWARD -i eth0.4 -j FWD_V4_LANONLY
-A FORWARD -i eth1 -j FWD_ETH1
-A FORWARD -i ppp0 -j FWD_PPP0
-A FORWARD -i tun0 -j FWD_TUN0

# Rate limit ICMPv6 PING
-A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m limit --limit 30/min -j ACCEPT

# Pass output interface to corresponding chain
-A OUTPUT -o ppp0 -j OUT_PPP0
-A OUTPUT -o tun0 -j OUT_TUN0

# Forward VLAN2 to ISP
-A FWD_V2_ISP -s 2001:0db8:1234:ffff::/64 -j ACCEPT

# Forward VLAN3 to VPN
-A FWD_V3_VPN -o tun0 -j ACCEPT

# Accept incoming tracked PPP0 connection
-A IN_PPP0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Log dropped packets coming in on PPP0
-A IN_PPP0 -j LOG_DROP

# Allow and rate limit ICMP
-A IN_PPP0 -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j ACCEPT
-A IN_PPP0 -p ipv6-icmp -m limit --limit 30/sec -j ACCEPT

# Allow DHCPv6 PD on Link Local from ISP
-A IN_PPP0 -s fe80::/10 -p udp -m udp --sport 547 --dport 546 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Log dropped packets coming in on PPP0
-A IN_PPP0 -j LOG_DROP

# Accept incoming tracked TUN0 connection
-A IN_TUN0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Log dropped packets on VPN
-A IN_TUN0 -j LOG_DROP

# Allow tracked connections in from ppp0 to VLAN2
-A IN_V2_ISP -s 2001:0db8:1234:ffff::/64 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow ICMP in from VLAN2
-A IN_V2_ISP -p ipv6-icmp -j ACCEPT

# Allow tracked connections in from tun0 to VLAN3
-A IN_V3_VPN -s fde4:8dba:82e1:fff3::/64 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Allow ICMP in from VLAN3
-A IN_V3_VPN -p ipv6-icmp -j ACCEPT
COMMIT

Add ip6tables to the default run level:

rc-update add ip6tables default


nftables

Optionally one might decide to use nftables instead of old legacy iptables. nftables has a few improvements such as a cleaner rule syntax, ipv4 and ipv6 is all in one table, and the ability to use variables, sets, dictionaries and maps. This also means you no longer need to worry about using ipset.

#!/usr/sbin/nft -f

flush ruleset

###################################################################################
#
#| Address                            | Route To        | Interface | VLAN | Mark |
#|------------------------------------|-----------------|-----------|------|------|
#| 192.168.0.0/24                     | Modem           | eth1      | 1    |      |
#| 192.168.1.0/24                     | Nowhere         | eth0      | 1    |      |
#| 192.168.2.0/24                     | ISP             | eth0.2    | 2    | 0x1  |
#| 2001:0db8:1234:ffff::/64           | ISP             | eth0.2    | 2    | 0x1  |
#| 192.168.3.0/24                     | VPN             | eth0.3    | 3    | 0x2  |
#| fde4:8dba:82e1:fff3::/64           | VPN             | eth0.3    | 3    | 0x2  |
#| 192.168.4.0/24                     | Nowhere         | eth0.4    | 4    |      |
#| <ip_of_exception>                  | Exception (ISP) | eth0.2    | 4    | 0x1  |
#
###################################################################################

define net_v0_ip4 = 192.168.0.0/24
define net_v1_ip4 = 192.168.1.0/24
define net_v2_ip4 = 192.168.2.0/24
define net_v3_ip4 = 192.168.3.0/24
define network_v4_ip4 = 192.168.4.0/24
define mailserver = <ip_of_exception>
define modem = 192.168.0.2
define router = 192.168.1.1
define printer = 192.168.4.9
define workstation = 192.168.3.30
define wifi_aps = { 192.168.1.10, 192.168.1.11 }
define net_ula_v1_ip6 = fde4:8dba:82e1:fff1::/64
define net_gua_v2_ip6 = 2001:0db8:1234:ffff::/64
define net_ula_v3_ip6 = fde4:8dba:82e1:fff3::/64
define net_ula_v4_ip6 = fde4:8dba:82e1:fff4::/64
define vpn_gateway = 172.16.32.1

#
# Mangle Table (IPv4)
# Markings happen: whether they be 0x1 or 0x2
#
table ip mangle {
	chain PREROUTING {
		type filter hook prerouting priority mangle; policy accept;

		# Restore CONNMARK to the MARK (If one doesn't exist then no mark is set)
		mark set ct mark

		# If packet MARK is 2, then it means there is already a
		# connection mark and theoriginal packet came in on VPN
		ip saddr $net_v3_ip4 mark 0x00000002

		# Check mail server are 0x1
		ip saddr $net_v3_ip4 ip daddr $mailserver mark 0x00000001

		# Mark packets coming from VLAN3 as 0x2
		ip saddr $net_v3_ip4 mark set 0x00000002

		# If packet MARK is 1, then it means there is already a
		# connection mark and the original packet came in on ISP
		ip saddr $net_v2_ip4 mark 0x00000001

		# Mark packets coming from VLAN2 as 0x1
		ip saddr $net_v2_ip4 mark set 0x00000001

		# Mark mail server as 0x1
		ip saddr $net_v3_ip4 ip daddr $mailserver mark set 0x00000001

		# Strip mark if packet is destined for modem
		ip daddr $modem mark set 0x00000000

		# Strip mark if packet is destined for printer
		ip daddr $printer mark set 0x00000000

		# Save MARK to CONNMARK (remember iproute can't see CONNMARKs)
		ct mark set mark
	}
}

#
# Filter Table (IPv4)
# Filtering things coming IN and OUT of the router
#
table ip filter {
	# Create rule chain per input interface for input packets (for host itself)
	chain INPUT {
		type filter hook input priority filter; policy drop;
		iifname "lo" accept
		iifname "eth0" jump IN_V1_MGMT
		iifname "eth0.2" jump IN_V2_ISP
		iifname "eth0.3" jump IN_V3_VPN
		iifname "eth1" jump IN_ETH1
		iifname "tun0" jump IN_TUN0
	}

	# Create rule chain per input interface for forwarding packets
	chain FORWARD {
		type filter hook forward priority filter; policy drop;
		ct state established,related accept
		iifname "eth0" jump FWD_V1_MGMT
		iifname "eth0.2" jump FWD_V2_ISP
		iifname "eth0.3" jump FWD_V3_VPN
		iifname "eth1" jump FWD_ETH1
		iifname "tun0" jump FWD_TUN0
	}

	chain FWD_ETH1 {
		ip saddr $modem ip daddr $net_v2_ip4 tcp sport http ct state established,new accept
		ip saddr $modem ip daddr $net_v3_ip4 tcp sport http ct state established,new accept
	}

	chain FWD_TUN0 {
		# Forward bittorrent
		ip daddr $workstation tcp dport 20001 ct state established,new accept
		ip daddr $workstation udp dport 20001 ct state established,new accept
	}

	chain FWD_V1_MGMT {
		# Forward established packets to hosts in VLAN2/3 from printer
		ip saddr $printer ip daddr $net_v2_ip4 ct state established,new accept
		ip saddr $printer ip daddr $net_v3_ip4 ct state established,new accept

		# Forward established packets to hosts in VLAN2/3 from modem
		ip saddr $modem ip daddr $net_v3_ip4 tcp sport http ct state established,new accept
	}

	chain FWD_V2_ISP {
		# Forward traffic from VLAN2 to Modem
		ip daddr $modem tcp dport http accept

		# Forward traffic from VLAN2 to printer
		ip daddr $printer accept

		# Allow rest from VLAN2
		ip saddr $net_v2_ip4 accept
	}

	chain FWD_V3_VPN {
		# Forward traffic from VLAN3 to Modem
		ip daddr $modem tcp dport http accept

		# Forward traffic from VLAN3 to printer
		ip daddr $printer accept

		# Allow rest from VLAN3
		ip saddr $net_v3_ip4 accept

		# Allow mailserver direct from VLAN3 out
		oifname "eth1" ip saddr $net_v3_ip4 ip daddr $mailserver accept
	}

	chain IN_ETH1 {
		# Accept incoming tracked connection from eth1
		ip saddr $router ip daddr $modem tcp sport http ct state established,new accept

		# Allow incoming NTP in from VLAN1
		ip saddr $net_v0_ip4 udp dport ntp ct state established,new accept

		# Log dropped packets coming in on eth1
		ct state established,related accept
		jump LOG_DROP
	}

	chain IN_TUN0 {
		# Log dropped packets coming in on tun0
		ct state established,related accept
		jump LOG_DROP
	}

	chain IN_V1_MGMT {
		# Allow in established packets from printer to VLAN2 and VLAN3
		ip saddr $printer ip daddr $net_v2_ip4 ct state established,new accept
		ip saddr $printer ip daddr $net_v3_ip4 ct state established,new accept

		# Allow NTP in from VLAN1
		ip saddr $net_v1_ip4 udp dport ntp ct state established,new accept

		# FreeRadius Clients
		ip saddr $wifi_aps tcp dport radius ct state established,new accept
		ip saddr $wifi_aps udp dport radius ct state established,new accept

		# Ubiquiti UAP Device Discovery Broadcast
		ip saddr $wifi_aps udp dport 10001 ct state established,new accept
		ip saddr $wifi_aps udp dport 3478 ct state established,new accept

		# Allow rest in from VLAN1
		ip saddr $net_v1_ip4 ct state established,new accept
	}

	chain IN_V2_ISP {
		# Allow ssh in from VLAN2
		ip saddr $net_v2_ip4 tcp dport ssh ct state established,new accept

		# Allow DNS in from VLAN2
		ip saddr $net_v2_ip4 udp dport domain ct state new accept

		# Allow NTP in from VLAN2
		ip saddr $net_v2_ip4 udp dport ntp ct state established,new accept

		# Allow rest from VLAN2
		ip saddr $net_v2_ip4 ct state established,new accept
	}

	chain IN_V3_VPN {
		# Allow ssh in from VLAN3
		ip saddr $net_v3_ip4 tcp dport ssh ct state established,new accept

		# Allow DNS in from VLAN3
		ip saddr $net_v3_ip4 udp dport domain ct state new accept

		# Allow NTP in from VLAN3
		ip saddr $net_v3_ip4 udp dport ntp ct state established,new accept

		# Allow rest from VLAN3
		ip saddr $net_v3_ip4 ct state established,new accept

		# Allow mailserver direct from eth1 from VLAN3
		oifname "eth1" ip saddr $net_v3_ip4 ip daddr $mailserver accept
	}

	chain LOG_DROP {
		log prefix "Dropped v4: " drop
	}
}

#
# NAT Table (IPv4)
# Translation of packets happens to our single external address
# Forwarding of ports through our public interfaces
#
table ip nat {
	chain PREROUTING {
		type nat hook prerouting priority dstnat; policy accept;
		# Port forwarding for Bittorrent on workstation through VPN
		iifname "tun0" tcp dport 20001 dnat to $workstation
		iifname "tun0" udp dport 20001 dnat to $workstation
	}

	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;

		# Allows routing to our modem subnet so we can access the web interface
		oifname "eth0" ip saddr $net_v2_ip4 ip daddr $modem tcp dport http masquerade
		oifname "eth0" ip saddr $net_v3_ip4 ip daddr $modem tcp dport http masquerade

		# Allows routing to printer
		oifname "eth0" ip saddr $net_v2_ip4 ip daddr $printer masquerade
		oifname "eth0" ip saddr $net_v3_ip4 ip daddr $printer masquerade

		# Masquerade behind NAT
		oifname "tun0" masquerade
		oifname "eth1" masquerade
	}
}

#
# Filter Table (IPv6)
# Filtering things coming IN and OUT of the router
#
table ip6 filter {
	chain INPUT {
		# Create rule chain per input interface for input packets (for host itself)
		type filter hook input priority filter; policy drop;
		iifname "lo" accept
		iifname "eth0.2" jump IN_V2_ISP
		iifname "eth0.3" jump IN_V3_VPN
		iifname "eth1" jump IN_ETH1
		iifname "tun0" jump IN_TUN0
	}

	chain FORWARD {
		# Track forwarded packets
		type filter hook forward priority filter; policy drop;
		ct state established,related accept

		# Create rule chain per input interface for forwarding packets
		iifname "eth0.2" jump FWD_V2_ISP
		iifname "eth0.3" jump FWD_V3_VPN
		# iifname "tun0" jump FWD_TUN0

		# Rate limit ICMPv6 PING
		icmpv6 type echo-request limit rate 30/minute accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}

	# chain FWD_TUN0 {
	# We could forward ports IPv6 ports through the VPN here
	# }

	chain FWD_V2_ISP {
		# Forward VLAN2 to ISP
		ip6 saddr $net_gua_v2_ip6 accept
	}

	chain FWD_V3_VPN {
		# Forward VLAN3 to VPN
		ip6 saddr $net_ula_v3_ip6 accept
	}

	chain IN_ETH1 {
		# Accept incoming tracked ETH1 connection
		ct state established,related accept

		# Allow and rate limit ICMP
		icmpv6 type packet-too-big accept
		meta l4proto ipv6-icmp limit rate 30/second accept

		# Allow DHCPv6 PD on Link Local from ISP
		ip6 saddr fe80::/10 udp sport dhcpv6-server udp dport dhcpv6-client ct state established,new accept

		# Allow Router advetisements/solict form ISP
		ip6 saddr fe80::/10 icmpv6 type nd-router-advert accept
		ip6 saddr fe80::/10 icmpv6 type nd-neighbor-solicit accept
		ip6 saddr fe80::/10 icmpv6 type nd-neighbor-advert accept

		# Log dropped packets coming in on ETH1
		jump LOG_DROP
	}

	chain IN_TUN0 {
		# Accept incoming tracked TUN0 connection
		ct state established,related accept

		# Allow and rate limit ICMP
		icmpv6 type packet-too-big accept
		meta l4proto ipv6-icmp limit rate 30/second accept

		# Log dropped packets on VPN
		jump LOG_DROP
	}

	chain IN_V2_ISP {
		# Allow tracked connections in from ETH1 to VLAN2
		ip6 saddr $net_gua_v2_ip6 ct state established,new accept

		# Allow ICMP in from VLAN2
		meta l4proto ipv6-icmp accept
	}

	chain IN_V3_VPN {
		# Allow tracked connections in from tun0 to VLAN3
		ip6 saddr $net_ula_v3_ip6 ct state established,new accept

		# Allow ICMP in from VLAN3
		meta l4proto ipv6-icmp accept
	}

	chain LOG_DROP {
		log prefix "Dropped v6: " drop
	}
}

#
# Mangle Table (IPv6)
#
table ip6 mangle {
	chain PREROUTING {
		type filter hook prerouting priority mangle; policy accept;

		# Drop unusually large ping packets
		icmpv6 type echo-request meta length 170-65535 drop
	}
}

#
# NAT Table (IPv6)
# Translation of packets happens to our single external address
# only used for the VPN as our ISP give us a /56 range to split up
#
table ip6 nat {
	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;
		oifname "tun0" masquerade
	}
}

#
# Raw Table - IPv4/IPv6
#
table inet raw {
 	set bogon-bn-nonagg-set {
		type ipv4_addr
		flags interval
		elements = { 0.0.0.0/8, 10.0.0.0/8,
			     100.64.0.0/10, 127.0.0.0/8,
			     169.254.0.0/16, 172.16.0.0/12,
			     192.0.0.0/24, 192.0.2.0/24,
			     192.168.0.0/16, 198.18.0.0/15,
			     198.51.100.0/24, 203.0.113.0/24,
			     224.0.0.0/4, 240.0.0.0-255.255.255.255 }
	}

	set lo-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { 127.0.0.0/8 }
	}

	set eth0-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { $net_v1_ip4 }
	}

	set eth0.2-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { 192.168.2.0/24, 192.168.3.0/24,
			     192.168.4.0/24 }
	}

	set eth0.3-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { 192.168.2.0/24, 192.168.3.0/24,
			     192.168.4.0/24 }
	}

	set eth0.4-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { 192.168.2.0/24, 192.168.3.0/24,
			     192.168.4.0/24 }
	}

	set eth1-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { 192.168.0.0/30, 255.255.255.255 }
	}

	set tun0-allowed-net-ip4-set {
		type ipv4_addr
		flags interval
		elements = { 172.16.32.0/20, 172.16.48.0/20 }
	}

	set lo-allowed-net-ip6-set {
		type ipv6_addr
		flags interval
		elements = { ::1/128 }
	}

	set eth0-allowed-net-ip6-set {
		type ipv6_addr
		flags interval
		elements = { fde4:8dba:82e1:fff1::/64,
			     fe80::/10 }
	}

	set eth0.2-allowed-net-ip6-set {
		type ipv6_addr
		flags interval
		elements = { 2001:0db8:1234:ffff::/64,
			     fe80::/10 }
	}

	set eth0.3-allowed-net-ip6-set {
		type ipv6_addr
		flags interval
		elements = { fde4:8dba:82e1:fff3::/64,
			     fe80::/10 }
	}

	set eth0.4-allowed-net-ip6-set {
		type ipv6_addr
		flags interval
		elements = { fde4:8dba:82e1:fff4::/64,
			     fe80::/10 }
	}

	chain PREROUTING {
		type filter hook prerouting priority raw; policy accept;

		# Allows traffic from NNTP/DNS vpn gateway
		iifname "tun0" ip saddr $vpn_gateway accept;

		# Allows traffic originating from router to vpn gateway
		ip daddr $vpn_gateway accept

		# Allows traffic originating from router to modem
		ip daddr $modem accept

		# Block specified bogons coming in from ISP and VPN
		# (unlikely to happen as they filter them on their router)
		#iifname "eth1" ip saddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_PR;
		#iifname "tun0" ip saddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_PR;
	}

	chain OUTPUT {
		type filter hook output priority raw; policy accept;

		# Allows my excepted ranges
		iifname vmap { lo : jump lo-allowed-net, eth0 : jump eth0-allowed-net,
		    eth0.2 : jump eth0.2-allowed-net, eth0.3 : jump eth0.3-allowed-net,
		    eth0.4 : jump eth0.4-allowed-net, eth1 : jump eth1-allowed-net,
		    tun0 : jump tun0-allowed-net };

		oifname vmap { lo : jump lo-allowed-net, eth0 : jump eth0-allowed-net,
		    eth0.2 : jump eth0.2-allowed-net, eth0.3 : jump eth0.3-allowed-net,
		    eth0.4 : jump eth0.4-allowed-net, eth1 : jump eth1-allowed-net,
		    tun0 : jump tun0-allowed-net };


		# Drop any remaining bogons that try to leave the router
		oifname "eth1" ip daddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_IN;
		oifname "tun0" ip daddr @bogon-bn-nonagg-set jump LOG_DROP_BOGON_IN;
	}

	chain lo-allowed-net {
		ip saddr @lo-allowed-net-ip4-set accept
		ip6 saddr @lo-allowed-net-ip6-set accept
	}

	chain eth0-allowed-net {
		ip saddr @eth0-allowed-net-ip4-set accept
		ip6 saddr @eth0-allowed-net-ip6-set accept
		#log prefix "Allowed packet allow net 0: " level info
	}

	chain eth0.2-allowed-net {
		ip saddr @eth0.2-allowed-net-ip4-set accept
		ip6 saddr @eth0.2-allowed-net-ip6-set accept
		#log prefix "Allowed packet allow net 2: " level info
	}

	chain eth0.3-allowed-net {
		ip saddr @eth0.3-allowed-net-ip4-set accept
		ip6 saddr @eth0.3-allowed-net-ip6-set accept
		#log prefix "Allowed packet allow net 3: " level info
	}

	chain eth0.4-allowed-net {
		ip saddr @eth0.4-allowed-net-ip4-set accept
		ip6 saddr @eth0.4-allowed-net-ip6-set accept
		#log prefix "Allowed packet allow net 4: " level info
	}

	chain eth1-allowed-net {
		ip saddr @eth1-allowed-net-ip4-set accept
		#log prefix "Allowed packet allow eth1: " level info
	}

    chain tun0-allowed-net {
		ip saddr @tun0-allowed-net-ip4-set accept
		#log prefix "Allowed packet allow tun0: " level info
	}

	chain LOG_DROP_BOGON_IN {
		log prefix "Dropped Bogon outgoing " drop
	}
	chain LOG_DROP_BOGON_OUT {
		log prefix "Dropped Bogon incoming " drop
	}
	chain LOG_DROP_BOGON_PR {
		log prefix "Dropped Bogon prerouting " drop
	}
}

Add nftables to the default run level:

rc-update add nftables default

Router Advertisements

Now we need to configure radvd to give router advertisements to out VLANs for addressing and routing.

apk add radvd

Once radvd is installed, you may configure it:

/etc/radvd.conf

interface eth0.2 {

  # We are sending advertisements (route)
  AdvSendAdvert on;

  # When set, host use the administered (stateful) protocol
  # for address autoconfiguration. The use of this flag is
  # described in RFC 4862
  AdvManagedFlag on;

  # When set, host use the administered (stateful) protocol
  # for address autoconfiguration. For other (non-address)
  # information.
  # The use of this flag is described in RFC 4862
  AdvOtherConfigFlag on;

  # Suggested Maximum Transmission setting for using the
  # Hurricane Electric Tunnel Broker.
  # AdvLinkMTU 1480;

  # We have native Dual Stack IPv6 so we can use the regular MTU
  # https://blogs.cisco.com/enterprise/ipv6-mtu-gotchas-and-other-icmp-issues
  AdvLinkMTU 1500;
  
  prefix ::/64 {
    AdvOnLink on;
    AdvAutonomous on; ## SLAAC based on EUI
    AdvRouterAddr on;
  };
};

interface eth0.3 {

  AdvSendAdvert on;
  AdvManagedFlag on;
  AdvOtherConfigFlag on;
  AdvLinkMTU 1500;

  # Helps the route not get lost when on WiFi with packet loss
  MaxRtrAdvInterval 30;
  AdvDefaultLifetime 9000;

  prefix fde4:8dba:82e1:fff3::/64 {
    AdvOnLink on;
    AdvAutonomous on; ## SLAAC based on EUI
  };
};

Add radvd to the default run level:

rc-update add radvd default

DHCP

You may decide you want more control over your network address assignment. I like to have certain hosts get certain addresses when they connect on a particular VLAN, note v2 and v3. You can do this with:

authoritative;
ddns-update-style interim;

subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.21 192.168.1.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.1.255;
    option routers 192.168.1.1;
    option ntp-servers 192.168.1.1;
    option domain-name-servers 192.168.1.1;
    allow unknown-clients;

        host wifi_ap {
            hardware ethernet <mac_addess>;
            fixed-address 192.168.1.11;
            option subnet-mask 255.255.255.0;
            option routers 192.168.1.1;
            option host-name "<hostname>";
        }
}

subnet 192.168.2.0 netmask 255.255.255.0 {
    range 192.168.2.40 192.168.2.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.2.255;
    option routers 192.168.2.1;
    option ntp-servers 192.168.2.1;
    option domain-name-servers 192.168.2.1;
    allow unknown-clients;

        host host-v2 {
            hardware ethernet <mac_address>;
            fixed-address 192.168.2.30;
            option subnet-mask 255.255.255.0;
            option broadcast-address 192.168.2.255;
            option routers 192.168.2.1;
            option host-name "<hostname>";
        }
}

subnet 192.168.3.0 netmask 255.255.255.0 {
    range 192.168.3.20 192.168.3.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.3.255;
    option routers 192.168.3.1;
    option ntp-servers 192.168.3.1;
    option domain-name-servers 192.168.3.1;
    ignore unknown-clients;

        host host-v3 {
            hardware ethernet <mac_address>;
            fixed-address 192.168.3.30;
            option subnet-mask 255.255.255.0;
            option broadcast-address 192.168.3.255;
            option routers 192.168.3.1;
            option host-name "<hostname>";
        }
}

subnet 192.168.4.0 netmask 255.255.255.0 {
    range 192.168.4.40 192.168.4.240;
    option subnet-mask 255.255.255.0;
    option broadcast-address 192.168.4.255;
    option routers 192.168.4.1;
    option ntp-servers 192.168.4.1;
    option domain-name-servers 192.168.4.1;

    host printer {
            hardware ethernet <PRINTER_MAC_ADDRESS>;
            fixed-address 192.168.4.9;
            option subnet-mask 255.255.255.0;
            option broadcast-address 192.168.4.255;
            option routers 192.168.4.1;
            option host-name "My_Printer";
        }   ignore unknown-clients;
}

For IPv6 I don't use DHCPv6 because Android doesn't support it. I just let SLAAC assign addresses.

VPN Tunnel VLAN3

Install the necessary packages:

apk add openvpn iproute2 iputils

/etc/modules

You'll want to add the tun module

tun

/etc/iproute2/rt_tables

Add the two routing tables to the bottom of rt_tables. It should look something like this:

#
# reserved values
#
255	local
254	main
253	default
0	unspec
#
# local
#
#1	inr.ruhep
1 ISP
2 VPN
3 LAN

/etc/network/fwmark_rules

In this file we want to put the fwmark rules and set the correct priorities.

#!/bin/sh

# Normal packets to go direct out WAN
/sbin/ip rule add fwmark 1 table ISP prio 100
/sbin/ip -6 rule add fwmark 1 table ISP prio 100

# Put packets destined into VPN when VPN is up
/sbin/ip rule add fwmark 2 table VPN prio 200
/sbin/ip -6 rule add fwmark 2 table VPN prio 200

# Prevent packets from being routed out when VPN is down.
# This prevents packets from falling back to the main table
# that has a priority of 32766
/sbin/ip rule add prohibit fwmark 2 prio 300
/sbin/ip -6 rule add prohibit fwmark 2 prio 300

/etc/network/route_LAN

#!/bin/sh
#
# This script adds the LAN routes.
#

# Add routes from ISP to LAN
/sbin/ip route add 192.168.2.0/24 dev eth0.2 table LAN

# Add route from VPN to LAN
/sbin/ip route add 192.168.3.0/24 dev eth0.3 table LAN

# Add route from LAN to it's own table
/sbin/ip route add 192.168.4.0/24 dev eth0.4 table LAN

/etc/ppp/ip-up

Next up we want to create the routes that should be run when PPP comes online. There are special hooks we can use in ip-up and ip-down to refer to the IP address, ppp man file - Scripts You can also read about them in your man file if you have ppp-doc installed.

#!/bin/sh
#
# This script is run by pppd when there's a successful ppp connection.
#

# Flush out any old rules that might be there
/sbin/ip route flush table ISP

# Add route to table from subnets on LAN
/sbin/ip route add 192.168.2.0/24 dev eth0.2 table ISP
/sbin/ip route add 192.168.3.0/24 dev eth0.3 table ISP

# Add route from IP given by ISP to the table
/sbin/ip rule add from ${IPREMOTE} table ISP prio 100

# Add a default route
/sbin/ip route add table ISP default via ${IPREMOTE} dev ${IFNAME}

# Add route to LAN subnet
/sbin/ip route add 192.168.4.0/24 dev eth0.4 table ISP

/etc/ppp/ip-down

#!/bin/sh
#
# This script is run by pppd after the connection has ended.
#

# Delete the rules when we take the interface down
/sbin/ip rule del from ${IPREMOTE} table ISP prio 100

/etc/openvpn/route-up-fwmark.sh

OpenVPN needs similar routing scripts and it also has it's own special hooks that allow you to specify particular values. A full list is here OpenVPN man file - Environmental Variables

#!/bin/sh
#
# This script is run by OpenVPN when there's a successful VPN connection.
#

# Flush out any old rules that might be there
/sbin/ip route flush table VPN

# Add route to table from 192.168.3.0/24 subnet on LAN
/sbin/ip route add 192.168.3.0/24 dev eth0.3 table VPN
/sbin/ip -6 route add fde4:8dba:82e1:fff3::/64 dev eth0.3 table VPN

# Add route from VPN interface IP to the VPN table
/sbin/ip rule add from ${ifconfig_local} table VPN prio 200
/sbin/ip -6 rule add from fde4:8dba:82e1:fff3::/64 table VPN prio 200

# Add a default route
/sbin/ip route add default via ${ifconfig_local} dev ${dev} table VPN
/sbin/ip -6 route add default dev tun0 table VPN 

# Add route to LAN only subnet
/sbin/ip route add 192.168.4.0/24 dev eth0.4 table VPN

# Add route to IP on VPN for traffic originating from the router
/sbin/ip route add 172.16.32.1 dev tun0

/etc/openvpn/route-down-fwmark.sh

#!/bin/sh
#
# This script is run by OpenVPN after the connection has ended
#

# Delete the rules when we take the interface down
/sbin/ip rule del from ${ifconfig_local} table VPN prio 200
/sbin/ip -6 rule del from fde4:8dba:82e1:fff3::/64 table VPN prio 200

# Delete route to IP on VPN for traffic originating from the router
/sbin/ip route del 172.16.32.1 dev tun0

OpenVPN Routing

Usually when you connect with OpenVPN the remote VPN server will push routes down to your system. We don't want this as we still want to be able to access the internet without the VPN. We have also created our own routes that we want to use earlier in this guide.

You'll need to add this to the bottom of your OpenVPN configuration file:

# Prevents default gateway from being set on the default routing table
route-noexec

# Allows route-up script to be executed
script-security 2

# Calls custom shell script after connection to add necessary routes
route-up /etc/openvpn/route-up-fwmark.sh
route-pre-down /etc/openvpn/route-pre-down-fwmark.sh

My VPNs are arranged like this in /etc/openvpn:

OpenVPN configuration file for that server:

countrycode.serverNumber.openvpn.conf

OpenVPN certs for that server:

countrycode.serverNumber.openvpn/countrycode.serverNumber.openvpn.crt
countrycode.serverNumber.openvpn/countrycode.serverNumber.openvpn.key
countrycode.serverNumber.openvpn/myKey.crt
countrycode.serverNumber.openvpn/myKey.key

So I use this helpful script to automate the process of changing between servers:

#!/bin/sh

vpn_server_filename=$1

rm /etc/openvpn/openvpn.conf
ln -s $vpn_server_filename /etc/openvpn/openvpn.conf
chown -R openvpn:openvpn /etc/openvpn
chmod -R a=-rwx,u=+rX /etc/openvpn
chmod u=x /etc/openvpn/*.sh*

if grep -Fxq "#CustomStuffHere" openvpn.conf
then
    echo "Not adding custom routes, this server has been used previously"
else
    echo "Adding custom route rules"
cat <<EOF >> /etc/openvpn/openvpn.conf

#CustomStuffHere
# Prevents default gateway from being set on the default routing table
route-noexec

# Allows route-up script to be executed
script-security 2

# Calls custom shell script after connection to add necessary routes
route-up /etc/openvpn/route-up-fwmark.sh
route-pre-down /etc/openvpn/route-pre-down-fwmark.sh

# Logging of OpenVPN to file
#log /etc/openvpn/openvpn.log
EOF

fi
echo "Remember to set BitTorrent port forward in your VPN control panel"

That way I can simply change between servers by running:

changevpn.sh countrycode.serverNumber.openvpn

and then restart openvpn. I am also reminded to put the port forward through on the VPN control panel so my BitTorrent client is connectable:

service openvpn restart

Finally add openvpn to the default run level

rc-update add openvpn default