summaryrefslogtreecommitdiffstats
path: root/roles/gateway
diff options
context:
space:
mode:
authorAhmed Abdelhalim <[email protected]>2026-07-23 00:42:43 +0200
committerAhmed Abdelhalim <[email protected]>2026-07-23 00:42:43 +0200
commitff4eb129eea3f3743f82e5f1f7858d43625dba3d (patch)
treec737e5354cb0622f28db6565c3dfea445e389b06 /roles/gateway
parentf7c8bd8143d6bc1e5b55bfd9591101ed41651d25 (diff)
Disable ipv6 completely and remove related code
Co-Authored-By: Claude.ai
Diffstat (limited to 'roles/gateway')
-rw-r--r--roles/gateway/README.md65
-rw-r--r--roles/gateway/defaults/main.yml1
-rw-r--r--roles/gateway/handlers/main.yml6
-rw-r--r--roles/gateway/meta/argument_specs.yml5
-rw-r--r--roles/gateway/tasks/main.yml37
-rw-r--r--roles/gateway/templates/99-gateway.conf.j23
-rw-r--r--roles/gateway/templates/gateway-apply-rules.sh.j220
-rw-r--r--roles/gateway/templates/radvd.conf.j212
8 files changed, 0 insertions, 149 deletions
diff --git a/roles/gateway/README.md b/roles/gateway/README.md
index 5340cc25..eda1b087 100644
--- a/roles/gateway/README.md
+++ b/roles/gateway/README.md
@@ -17,29 +17,6 @@ gateway_local_ipv4_subnet: "192.168.1.0/24"
gateway_router_interface: "end0"
```
-### IPv6 Support (Optional)
-
-IPv6 gateway is enabled by setting `gateway_local_ipv6_subnet` to the ULA prefix advertised by radvd:
-
-```yaml
-gateway_local_ipv6_subnet: "fd00:xxxx:xxxx:xxxx::/64" # ULA prefix (stable, router-independent)
-```
-
-When set, this role:
-- Enables `net.ipv6.conf.all.forwarding=1` and `accept_ra=2` on `gateway_router_interface`
-- Deploys radvd advertising the ULA prefix with `AdvDefaultPreference high` — Pi wins as IPv6 default router
-- Adds ip6tables MASQUERADE/FORWARD rules so all client IPv6 traffic is forwarded through Pi
-- Drops outbound ICMPv6 Redirect messages — prevents Pi from redirecting clients to bypass the gateway
-
-**ICMPv6 Redirect suppression is critical.** Pi, the ISP router (FritzBox), and LAN clients share the same L2
-segment. When Pi forwards a packet to an external destination and the kernel determines the ISP router is a
-"better" next-hop on the same link, it sends an ICMPv6 Redirect to the client. The client then sends
-subsequent connections directly to the ISP router — bypassing Pi's masquerade and content filtering entirely.
-The first connection attempt fails (Pi forwarded nothing, just redirected), giving the symptom of "first
-request fails every ~30-60s." Dropping Redirect messages in ip6tables OUTPUT fixes this.
-
-To disable IPv6, leave `gateway_local_ipv6_subnet: ""` (default). The role skips all ip6tables rules and disables radvd.
-
## Network Architecture
```txt
@@ -65,40 +42,10 @@ Client Devices (192.168.1.0/24)
- All traffic NAT'd through `wg+` interfaces
- Pi and client traffic both use VPN exit IP
-- Most VPN providers handle IPv4/IPv6 protocol routing automatically
**Direct Mode Characteristics:**
- IPv4 traffic NAT'd through router interface (`end0`)
-- IPv6 traffic masqueraded through router interface — clients appear as Pi's GUA to the ISP router,
- preventing ISP-router-level content filtering from applying per-device rules
-- ICMPv6 Redirects suppressed so clients always route through Pi
-
-## IPv6 Architecture Notes
-
-### Why masquerade IPv6 in direct mode?
-
-Clients have globally routable GUA addresses (assigned by ISP router SLAAC). Without masquerade, the ISP
-router sees each client's real GUA and can apply per-device content filtering. With masquerade, all client
-traffic appears to originate from Pi's GUA — bypassing per-device filtering consistently.
-
-### Why suppress ICMPv6 Redirects?
-
-When Pi, the ISP router, and clients share the same L2 segment (single broadcast domain), the Linux kernel
-sends ICMPv6 Redirect messages when it detects a "better" next-hop for a destination on the same link. For
-external GUA destinations reachable via the ISP router directly, Linux redirects clients from Pi to the ISP
-router. This causes:
-1. First SYN from client arrives at Pi — Pi sends Redirect, does NOT forward the packet
-2. Client receives Redirect, caches ISP router as next-hop for that destination
-3. Subsequent connections go directly to ISP router — masquerade and filtering bypassed
-
-Dropping `icmpv6-type redirect` in ip6tables OUTPUT prevents this entirely.
-
-### Why radvd with `AdvDefaultPreference high`?
-
-The ISP router (FritzBox) also sends RAs with default preference `medium` (or `low` if configured).
-Pi's radvd sends `AdvDefaultPreference high`, so clients prefer Pi as their IPv6 default router.
-Without this, clients split or prefer the ISP router depending on RA timing.
## Requirements
@@ -135,18 +82,6 @@ journalctl -u gateway-direct-mode.service -f
sudo iptables -L -n -v
sudo iptables -t nat -L -n -v
-# Verify ip6tables rules (redirect DROP must be first in OUTPUT)
-sudo ip6tables -L OUTPUT -n -v --line-numbers
-sudo ip6tables -t nat -L -n -v
-
-# Confirm no ICMPv6 Redirects being sent
-sudo tcpdump -i end0 -n 'icmp6 and ip6[40] == 137'
-
-# Verify clients route through Pi (not ISP router)
-# On client: ip -6 route show default
-# On Pi: sudo tcpdump -i end0 -n -e ip6 and host <client-GUA>
-
# Test traffic routing
curl -4 ifconfig.co # Should show VPN IP when VPN active
-curl -6 ifconfig.co # Should show Pi's GUA in direct mode, VPN IPv6 in VPN mode
```
diff --git a/roles/gateway/defaults/main.yml b/roles/gateway/defaults/main.yml
index dad64685..ebe33a12 100644
--- a/roles/gateway/defaults/main.yml
+++ b/roles/gateway/defaults/main.yml
@@ -1,4 +1,3 @@
---
gateway_enabled: false
gateway_router_interface: "eth0"
-gateway_local_ipv6_subnet: ""
diff --git a/roles/gateway/handlers/main.yml b/roles/gateway/handlers/main.yml
index 19088c82..12f901c0 100644
--- a/roles/gateway/handlers/main.yml
+++ b/roles/gateway/handlers/main.yml
@@ -15,9 +15,3 @@
ansible.builtin.command:
cmd: "udevadm control --reload-rules"
changed_when: false
-
-- name: "Restart radvd"
- become: true
- ansible.builtin.systemd_service:
- name: "radvd.service"
- state: "restarted"
diff --git a/roles/gateway/meta/argument_specs.yml b/roles/gateway/meta/argument_specs.yml
index b8efa0b3..d24251e3 100644
--- a/roles/gateway/meta/argument_specs.yml
+++ b/roles/gateway/meta/argument_specs.yml
@@ -11,11 +11,6 @@ argument_specs:
description: "Local subnet CIDR for NAT and routing rules (e.g., '192.168.178.0/24')"
type: "str"
required: true
- gateway_local_ipv6_subnet:
- description: "Local IPv6 subnet CIDR for NAT and routing rules (e.g., '2a02:3102:4ced:ec00::/64')"
- type: "str"
- required: false
- default: ""
gateway_router_interface:
description: "Default route interface for direct routing mode"
type: "str"
diff --git a/roles/gateway/tasks/main.yml b/roles/gateway/tasks/main.yml
index 71484725..661630f7 100644
--- a/roles/gateway/tasks/main.yml
+++ b/roles/gateway/tasks/main.yml
@@ -6,7 +6,6 @@
- "iptables"
- "iproute2"
- "udev"
- - "radvd"
state: "present"
- name: "Configure IP forwarding"
@@ -68,39 +67,3 @@
name: "gateway-init.service"
enabled: "{{ gateway_enabled }}"
daemon_reload: true
-
-- name: "Configure radvd for IPv6 RA"
- become: true
- when: gateway_enabled and gateway_local_ipv6_subnet != ""
- block:
- - name: "Deploy radvd config"
- ansible.builtin.template:
- src: "radvd.conf.j2"
- dest: "/etc/radvd.conf"
- owner: "root"
- group: "root"
- mode: "0644"
- notify: "Restart radvd"
-
- - name: "Enable radvd service"
- ansible.builtin.systemd_service:
- name: "radvd.service"
- enabled: true
- state: "started"
- daemon_reload: true
-
-- name: "Disable radvd when no IPv6 subnet configured"
- become: true
- when: not gateway_enabled or gateway_local_ipv6_subnet == ""
- block:
- - name: "Stop and disable radvd"
- ansible.builtin.systemd_service:
- name: "radvd.service"
- enabled: false
- state: "stopped"
- failed_when: false
-
- - name: "Remove radvd config"
- ansible.builtin.file:
- path: "/etc/radvd.conf"
- state: "absent"
diff --git a/roles/gateway/templates/99-gateway.conf.j2 b/roles/gateway/templates/99-gateway.conf.j2
index b0f99dd4..6f8a3fbd 100644
--- a/roles/gateway/templates/99-gateway.conf.j2
+++ b/roles/gateway/templates/99-gateway.conf.j2
@@ -1,8 +1,5 @@
{% if gateway_enabled %}
net.ipv4.ip_forward=1
-net.ipv6.conf.all.forwarding=1
-net.ipv6.conf.{{ gateway_router_interface }}.accept_ra=2
{% else %}
net.ipv4.ip_forward=0
-net.ipv6.conf.all.forwarding=0
{% endif %}
diff --git a/roles/gateway/templates/gateway-apply-rules.sh.j2 b/roles/gateway/templates/gateway-apply-rules.sh.j2
index 59a960cf..785d595b 100644
--- a/roles/gateway/templates/gateway-apply-rules.sh.j2
+++ b/roles/gateway/templates/gateway-apply-rules.sh.j2
@@ -7,7 +7,6 @@ LOG_FACILITY="local0.info"
# Ansible template variables - configured at deployment time
GATEWAY_SUBNET_V4="{{ gateway_local_ipv4_subnet }}"
-GATEWAY_SUBNET_V6="{{ gateway_local_ipv6_subnet }}"
GATEWAY_INTERFACE="{{ gateway_router_interface }}"
log_message() {
echo "[$SCRIPT_NAME] $1" | logger -t "$SCRIPT_NAME" -p "$LOG_FACILITY"
@@ -25,11 +24,6 @@ clear_rules() {
iptables -t nat -D POSTROUTING -s "$GATEWAY_SUBNET_V4" -o "$GATEWAY_INTERFACE" -j MASQUERADE 2>/dev/null || true
iptables -D OUTPUT -o wg+ -j ACCEPT 2>/dev/null || true
iptables -D OUTPUT -o "$GATEWAY_INTERFACE" -j ACCEPT 2>/dev/null || true
- if [ -n "$GATEWAY_SUBNET_V6" ]; then
- ip6tables -F FORWARD 2>/dev/null || true
- ip6tables -F OUTPUT 2>/dev/null || true
- ip6tables -t nat -F POSTROUTING 2>/dev/null || true
- fi
}
apply_vpn_mode() {
@@ -39,13 +33,6 @@ apply_vpn_mode() {
iptables -A OUTPUT -o wg+ -j ACCEPT
iptables -A FORWARD -s "$GATEWAY_SUBNET_V4" -o wg+ -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
- if [ -n "$GATEWAY_SUBNET_V6" ]; then
- ip6tables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
- ip6tables -I OUTPUT 1 -p ipv6-icmp --icmpv6-type redirect -j DROP
- ip6tables -A OUTPUT -o wg+ -j ACCEPT
- ip6tables -A FORWARD -i "$GATEWAY_INTERFACE" -o wg+ -j ACCEPT
- ip6tables -A FORWARD -i wg+ -o "$GATEWAY_INTERFACE" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
- fi
log_message "VPN mode rules applied - traffic routed through VPN"
}
@@ -55,13 +42,6 @@ apply_direct_rules() {
iptables -A OUTPUT -o "$GATEWAY_INTERFACE" -j ACCEPT
iptables -A FORWARD -s "$GATEWAY_SUBNET_V4" -o "$GATEWAY_INTERFACE" -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
- if [ -n "$GATEWAY_SUBNET_V6" ]; then
- ip6tables -t nat -A POSTROUTING -o "$GATEWAY_INTERFACE" -j MASQUERADE
- ip6tables -I OUTPUT 1 -p ipv6-icmp --icmpv6-type redirect -j DROP
- ip6tables -A OUTPUT -o "$GATEWAY_INTERFACE" -j ACCEPT
- ip6tables -A FORWARD -s "$GATEWAY_SUBNET_V6" -i "$GATEWAY_INTERFACE" -o "$GATEWAY_INTERFACE" -j ACCEPT
- ip6tables -A FORWARD -i "$GATEWAY_INTERFACE" -o "$GATEWAY_INTERFACE" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
- fi
log_message "Direct mode rules applied - normal routing active"
}
diff --git a/roles/gateway/templates/radvd.conf.j2 b/roles/gateway/templates/radvd.conf.j2
deleted file mode 100644
index 49b3047d..00000000
--- a/roles/gateway/templates/radvd.conf.j2
+++ /dev/null
@@ -1,12 +0,0 @@
-interface {{ gateway_router_interface }} {
- AdvSendAdvert on;
- AdvManagedFlag off;
- AdvOtherConfigFlag on;
- AdvDefaultPreference high;
-
- prefix {{ gateway_local_ipv6_subnet }} {
- AdvOnLink on;
- AdvAutonomous on;
- AdvRouterAddr on;
- };
-};