Linux Router with VPN on a Raspberry Pi (IPv6)
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.
- Linux IPv6 HOWTO (en) - in particular the "basics" and "address types".
- IPv6
- IPv6 Address
- Prefix delegation we use this with dhcpcd when doing DHCPv6-PD to inform our ISP of our network devices.
- Neighbor Discovery Protocol we use this with radvd to distribute our routes.
- Internet Control Message Protocol version 6 ICMPv6 differs from ICMPv4 and is used for many critical parts of IPv6 infrastructure.
- IPv6-test.com Useful for diagnosing if IPv6 is working.
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 # http://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 this patch 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 iptables 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 # Needs to be changed when change VPN server, use (ip a s tun0) scope global address -A POSTROUTING -o tun0 -j SNAT --to-source fd48:0db8:c66d:8f7d:ffff:ffff:ffff:ffff:ff02 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
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 # http://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" echo "Remember to change ip6tables -A POSTROUTING -o tun0 -j SNAT --to-source (to scope global address for tun0 ie: ip a s tun0"
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