OpenWrt guide
What you get from us
Replace these placeholders with the details you received with your FixedIP.be order.
PRIVATE_KEY | the private key of your tunnels (keep it secret) |
PORT | the UDP port of our tunnel servers |
ENDPOINT_1 | the address of our tunnel server in Antwerp |
SERVER_PUBLIC_KEY_1 | the public key of our tunnel server in Antwerp |
TUNNEL_IP_1 | the tunnel address of your router towards Antwerp |
SERVER_TUNNEL_IP_1 | the tunnel address on our side in Antwerp (gateway) |
ENDPOINT_2 | the address of our tunnel server in Zaventem |
SERVER_PUBLIC_KEY_2 | the public key of our tunnel server in Zaventem |
TUNNEL_IP_2 | the tunnel address of your router towards Zaventem |
FIXED_IP | your fixed IPv4 address |
FIXED_SUBNET | your IPv4 subnet, for example /29 |
IPV6_PREFIX | your IPv6 subnet |
IPV6_LAN | a /64 from your IPv6 subnet for your network |
TUNNEL_IP6_1 | the IPv6 tunnel address of your router towards Antwerp |
TUNNEL_IP6_2 | the IPv6 tunnel address of your router towards Zaventem |
LAN_HOST | the internal address of your server or NAS, for example 192.168.1.10 |
This guide sets up your fixed IP on a router running OpenWrt 23.05 or 24.10 (firewall4/nftables). The router builds two tunnels, one to each PoP, and fails over automatically if one of them drops. Only the traffic of your fixed IP runs through the tunnels; all other traffic keeps going over your regular WAN.
Your fixed IP arrives on the router and is forwarded to a device on your network (LAN_HOST). You run all commands over SSH on the router.
1. The packages
opkg update
opkg install wireguard-tools luci-proto-wireguard
2. The two tunnels
uci set network.fixedip1=interface
uci set network.fixedip1.proto='wireguard'
uci set network.fixedip1.private_key='PRIVATE_KEY'
uci set network.fixedip1.listen_port='51821'
uci set network.fixedip1.mtu='1420'
uci add_list network.fixedip1.addresses='TUNNEL_IP_1/32'
uci add_list network.fixedip1.addresses='FIXED_IP/32'
uci set network.fixedip1_peer=wireguard_fixedip1
uci set network.fixedip1_peer.description='fixedip.be PoP 1'
uci set network.fixedip1_peer.public_key='SERVER_PUBLIC_KEY_1'
uci set network.fixedip1_peer.endpoint_host='ENDPOINT_1'
uci set network.fixedip1_peer.endpoint_port='PORT'
uci set network.fixedip1_peer.persistent_keepalive='25'
uci add_list network.fixedip1_peer.allowed_ips='0.0.0.0/0'
uci add_list network.fixedip1_peer.allowed_ips='::/0'
uci set network.fixedip1_peer.route_allowed_ips='0'
uci set network.fixedip2=interface
uci set network.fixedip2.proto='wireguard'
uci set network.fixedip2.private_key='PRIVATE_KEY'
uci set network.fixedip2.listen_port='51822'
uci set network.fixedip2.mtu='1420'
uci add_list network.fixedip2.addresses='TUNNEL_IP_2/32'
uci set network.fixedip2_peer=wireguard_fixedip2
uci set network.fixedip2_peer.description='fixedip.be PoP 2'
uci set network.fixedip2_peer.public_key='SERVER_PUBLIC_KEY_2'
uci set network.fixedip2_peer.endpoint_host='ENDPOINT_2'
uci set network.fixedip2_peer.endpoint_port='PORT'
uci set network.fixedip2_peer.persistent_keepalive='25'
uci add_list network.fixedip2_peer.allowed_ips='0.0.0.0/0'
uci add_list network.fixedip2_peer.allowed_ips='::/0'
uci set network.fixedip2_peer.route_allowed_ips='0'
route_allowed_ips='0' ensures OpenWrt does not put a default route through the tunnels: your regular traffic stays where it was. Your fixed IP is on fixedip1; a WireGuard interface stays up, even if PoP 1 is briefly unreachable.
3. Routing
Replies go back through the tunnel the connection came in on (tables 101 and 102). New traffic from your fixed IP uses table 100, which step 6 points to the working tunnel.
uci set network.fixedip1_route=route
uci set network.fixedip1_route.interface='fixedip1'
uci set network.fixedip1_route.target='0.0.0.0/0'
uci set network.fixedip1_route.table='101'
uci set network.fixedip2_route=route
uci set network.fixedip2_route.interface='fixedip2'
uci set network.fixedip2_route.target='0.0.0.0/0'
uci set network.fixedip2_route.table='102'
uci set network.fixedip_mark1=rule
uci set network.fixedip_mark1.mark='0x101'
uci set network.fixedip_mark1.lookup='101'
uci set network.fixedip_mark2=rule
uci set network.fixedip_mark2.mark='0x102'
uci set network.fixedip_mark2.lookup='102'
uci set network.fixedip_src=rule
uci set network.fixedip_src.src='FIXED_IP/32'
uci set network.fixedip_src.lookup='100'
uci commit network
/etc/init.d/network reload
4. Firewall zone and forwarding
uci add firewall zone
uci set firewall.@zone[-1].name='fixedip'
uci add_list firewall.@zone[-1].network='fixedip1'
uci add_list firewall.@zone[-1].network='fixedip2'
uci set firewall.@zone[-1].input='REJECT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].forward='REJECT'
uci add firewall redirect
uci set firewall.@redirect[-1].name='fixedip-https'
uci set firewall.@redirect[-1].src='fixedip'
uci set firewall.@redirect[-1].src_dip='FIXED_IP'
uci set firewall.@redirect[-1].src_dport='443'
uci set firewall.@redirect[-1].dest='lan'
uci set firewall.@redirect[-1].dest_ip='LAN_HOST'
uci set firewall.@redirect[-1].dest_port='443'
uci set firewall.@redirect[-1].proto='tcp'
uci set firewall.@redirect[-1].target='DNAT'
uci commit firewall
Also allow ping from the tunnels. Our tunnel servers ping each tunnel to know which one works; without a reply, we don't send your fixed IP to that tunnel.
uci add firewall rule
uci set firewall.@rule[-1].name='fixedip-ping'
uci set firewall.@rule[-1].src='fixedip'
uci set firewall.@rule[-1].proto='icmp'
uci set firewall.@rule[-1].icmp_type='echo-request'
uci set firewall.@rule[-1].target='ACCEPT'
uci commit firewall
5. Marking replies from your server
When your server replies, that packet still has the internal address LAN_HOST as its source. To send it back through the right tunnel, you mark the connection when it comes in. Create /etc/nftables.d/10-fixedip.nft:
chain fixedip_mark {
type filter hook prerouting priority mangle; policy accept;
iifname "fixedip1" ct state new ct mark set 0x101
iifname "fixedip2" ct state new ct mark set 0x102
ct mark { 0x101, 0x102 } meta mark set ct mark
}
/etc/init.d/firewall restart
Are you already using marks (for example for mwan3 or SQM)? Then choose values that are still free.
6. Automatic failover
A small script picks the tunnel for new traffic every minute: PoP 1 if it replies, otherwise PoP 2. Create /usr/sbin/fixedip-failover:
#!/bin/sh
if ping -c 2 -W 2 -I fixedip1 SERVER_TUNNEL_IP_1 >/dev/null 2>&1; then
ip route replace default dev fixedip1 table 100
else
ip route replace default dev fixedip2 table 100
fi
chmod 755 /usr/sbin/fixedip-failover
echo '* * * * * /usr/sbin/fixedip-failover' >> /etc/crontabs/root
/etc/init.d/cron restart
/usr/sbin/fixedip-failover
Incoming connections do not need the script: they come in through whichever tunnel is working, and the replies follow the same path.
7. Outgoing traffic from your fixed IP too (optional)
uci set network.fixedip_host=rule
uci set network.fixedip_host.src='LAN_HOST/32'
uci set network.fixedip_host.lookup='100'
uci commit network
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='lan'
uci set firewall.@forwarding[-1].dest='fixedip'
uci add firewall redirect
uci set firewall.@redirect[-1].name='fixedip-snat'
uci set firewall.@redirect[-1].src='lan'
uci set firewall.@redirect[-1].dest='fixedip'
uci set firewall.@redirect[-1].src_ip='LAN_HOST'
uci set firewall.@redirect[-1].src_dip='FIXED_IP'
uci set firewall.@redirect[-1].proto='all'
uci set firewall.@redirect[-1].target='SNAT'
uci commit firewall
/etc/init.d/network reload
/etc/init.d/firewall restart
8. IPv6
Add TUNNEL_IP6_1/128 and TUNNEL_IP6_2/128 to the addresses of fixedip1 and fixedip2, put a /64 on your LAN and create the same rules for IPv6:
uci add_list network.lan.ip6addr='IPV6_LAN::1/64'
uci set network.fixedip_src6=rule6
uci set network.fixedip_src6.src='IPV6_PREFIX'
uci set network.fixedip_src6.lookup='100'
uci commit network
/etc/init.d/network reload
In fixedip-failover, also add ip -6 route replace default dev fixedip1 table 100 (or fixedip2) in both branches. Open incoming IPv6 traffic per device with a traffic rule from zone fixedip to lan.
Multiple addresses
Do you have a subnet (FIXED_SUBNET)? Then you can use it in two ways:
- Through your router (all addresses usable): add each address as a
/32tofixedip1and create a port forward per address as in step 4. - Directly on your network: put the subnet on its own interface or VLAN with the first usable address, and in step 3 replace
FIXED_IP/32withFIXED_SUBNET. The devices use their public address directly, without NAT; use traffic rules from zonefixedipto open up whatever may come in.
Checking
wg show
ip rule
ip route show table 100
For both tunnels, latest handshake should show a recent time and table 100 points to fixedip1. Then test from outside, for example with your phone on mobile data: https://FIXED_IP should end up at your server.
Testing failover: temporarily block outgoing UDP traffic to ENDPOINT_1 (for example with a traffic rule) and wait a minute: table 100 points to fixedip2 and your fixed IP stays reachable. Then remove the block. Do not use ifdown fixedip1 for this: your fixed IP is on that interface.
Rolling back
for s in fixedip1 fixedip1_peer fixedip2 fixedip2_peer fixedip1_route fixedip2_route fixedip_mark1 fixedip_mark2 fixedip_src fixedip_host fixedip_src6; do uci -q delete network.$s; done
uci commit network
rm /etc/nftables.d/10-fixedip.nft /usr/sbin/fixedip-failover
sed -i '/fixedip-failover/d' /etc/crontabs/root
Then, in LuCI (Network › Firewall), remove the fixedip zone and the associated port forwards, and restart network, firewall and cron.
Problems?
- No handshake: check
ENDPOINT_1/ENDPOINT_2,PORTand the keys. - Handshake OK, but connections hang: set
mtuto1360for both tunnels. - Nothing comes back from outside: check with
nft list chain inet fw4 fixedip_markthat the marking is counting, and withip rulethat the rules for0x101/0x102are in place.
Stuck? E-mail us at info@fixedip.be
Ready for your fixed IP?
Start today and be reachable everywhere.