IPv6 fundamentally changes how we approach network security. Unlike IPv4, where Network Address Translation (NAT) accidentally provided a layer of obscurity, IPv6's massive address space (340 undecillion addresses) eliminates the need for address sharing. Every device can have a globally routable address, which means your firewall configuration becomes your primary defense perimeter.
This is not a security weakness—it's an opportunity to implement proper security controls. In IPv4 networks, many administrators mistakenly believed NAT provided security, when in reality it was the stateful firewall function within the NAT device doing the heavy lifting. With IPv6, you explicitly configure these protections, making your security posture clearer and more maintainable.
The key principle remains unchanged: allow only the traffic you need, deny everything else. However, IPv6 requires more careful configuration, particularly around ICMPv6 messages, which are far more critical to network operation than ICMP was in IPv4.
One of the most persistent myths about IPv6 is that removing NAT makes networks less secure. This misconception stems from conflating two separate functions:
NAT was never designed as a security feature. It emerged as a workaround for IPv4 address exhaustion. The security benefits were incidental side effects of the stateful connection tracking required to make NAT work.
With IPv6, you implement stateful packet filtering directly, without the complexity and limitations of address translation. This actually provides better security because:
The slight increase in attack surface (devices are directly addressable) is more than offset by proper firewall rules, which you should have been implementing anyway.
Stateful firewalls track the state of network connections and automatically allow return traffic for established connections. This is the modern standard and what you should implement for IPv6.
Advantages:
CONFIG_NF_CONNTRACK_IPV6 (since kernel 2.6.20)Connection states:
NEW - First packet of a new connectionESTABLISHED - Part of an existing connectionRELATED - New connection related to an existing one (e.g., FTP data channel)INVALID - Packet doesn't match any known connectionStateless firewalls examine each packet independently without connection context. While simpler, they require bidirectional rules and offer less protection. Only use stateless filtering if you have specific technical constraints or are working with embedded systems with limited resources.
Unlike IPv4, where ICMP was often completely blocked with minimal consequences, IPv6 will not function properly without ICMPv6. RFC 4890 provides authoritative guidance on filtering ICMPv6 messages.
These are the IPv6 equivalent of ARP. Block them and your network breaks:
Security note: NDP messages should only be accepted on local links (link-local addresses fe80::/10) with hop limit 255, ensuring they haven't traversed a router.
Rate limiting - Apply rate limits to prevent ICMPv6 floods:
ip6tables -A INPUT -p ipv6-icmp -m limit --limit 10/sec -j ACCEPT
Scope restrictions - Only accept NDP messages from link-local addresses:
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -s fe80::/10 -j ACCEPT
Drop unnecessary types - Block ICMPv6 types not essential for your network while allowing the critical ones listed above.
A secure IPv6 firewall follows the principle of least privilege: deny everything by default, then explicitly allow only necessary traffic.
Linux (ip6tables):
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT # Allow outbound by default (or DROP for maximum security)
Windows Firewall (PowerShell):
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Block
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultOutboundAction Allow
BSD (pf):
# /etc/pf.conf
block all
pass out all keep state
This approach means any service or protocol you need must be explicitly permitted. While this requires more initial configuration, it significantly reduces attack surface and makes security audits straightforward.
IPv6 firewalling on Linux uses ip6tables, a separate command from iptables. Important: Many systems have iptables configured but ip6tables wide open by default. For detailed IPv6 configuration on Linux, see How to Configure IPv6 on Linux.
Basic stateful firewall configuration:
#!/bin/bash
# Flush existing rules
ip6tables -F
ip6tables -X
# Set default policies
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT
# Allow loopback
ip6tables -A INPUT -i lo -j ACCEPT
# Allow established and related connections
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow essential ICMPv6
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 1 -j ACCEPT # Destination unreachable
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT # Packet too big
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 3 -j ACCEPT # Time exceeded
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 4 -j ACCEPT # Parameter problem
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ACCEPT # Echo request
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 129 -j ACCEPT # Echo reply
# Allow NDP (Neighbor Discovery Protocol) - link-local only
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 133 -s fe80::/10 -j ACCEPT # Router solicitation
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 134 -s fe80::/10 -j ACCEPT # Router advertisement
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -s fe80::/10 -j ACCEPT # Neighbor solicitation
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 136 -s fe80::/10 -j ACCEPT # Neighbor advertisement
# Allow specific services (example: SSH, HTTP, HTTPS)
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT
# Log dropped packets (optional, useful for debugging)
ip6tables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IP6TABLES-DROP: "
# Save rules (Debian/Ubuntu)
ip6tables-save > /etc/iptables/rules.v6
Making rules persistent:
apt install iptables-persistentservice ip6tables saveWindows Firewall handles both IPv4 and IPv6 with the same rules by default. However, you can create IPv6-specific rules:
PowerShell examples:
# Allow ICMPv6 echo requests (ping)
New-NetFirewallRule -DisplayName "ICMPv6 Echo Request" `
-Protocol ICMPv6 -IcmpType 128 `
-Enabled True -Action Allow
# Allow web server (IPv6 only)
New-NetFirewallRule -DisplayName "HTTP IPv6" `
-Direction Inbound -Protocol TCP -LocalPort 80 `
-RemoteAddress Any -Action Allow
# Block specific IPv6 address
New-NetFirewallRule -DisplayName "Block specific IPv6" `
-Direction Inbound -RemoteAddress 2001:db8::bad:actor `
-Action Block
GUI configuration:
OpenBSD, FreeBSD, and macOS use pf, which handles IPv6 naturally:
# /etc/pf.conf
# Basic IPv6 stateful firewall
# Interfaces
ext_if = "em0"
int_if = "em1"
# Default deny
block all
# Allow loopback
pass on lo0 all
# Stateful outbound
pass out on $ext_if inet6 keep state
# Allow essential ICMPv6
pass in inet6 proto icmp6 icmp6-type { echoreq, echorep, unreach, toobig, timex, paramprob }
pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv, routersol, routeradv }
# Allow specific services
pass in on $ext_if inet6 proto tcp to port { 22, 80, 443 } keep state
macOS has two firewalls:
For advanced IPv6 control, use pf as shown in the BSD section above.
# Allow all outbound, established inbound, essential ICMPv6
ip6tables -P INPUT DROP
ip6tables -P OUTPUT ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT # Simplified: allow all ICMPv6
ip6tables -A INPUT -i lo -j ACCEPT
# Add to basic ruleset above
ip6tables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT
# Prevent SSH brute force
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update \
--seconds 60 --hitcount 4 -j DROP
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# Allow DNS queries
ip6tables -A INPUT -p udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
After configuring your IPv6 firewall, thorough testing is essential:
Use test-ipv6.run to validate your IPv6 configuration:
This tool runs entirely in your browser and provides a comprehensive score of your IPv6 readiness.
# From external host, test ping
ping6 your-ipv6-address
# Test Path MTU Discovery (requires Packet Too Big messages)
ping6 -s 1500 your-ipv6-address
# List all IPv6 rules
ip6tables -L -v -n
# Monitor dropped packets
watch -n 1 'ip6tables -L -v -n | grep DROP'
# Check connection tracking
cat /proc/net/nf_conntrack | grep ipv6
# Test SSH from external host
ssh -6 username@your-ipv6-address
# Test web server
curl -6 http://[your-ipv6-address]/
# Test DNS
dig @your-ipv6-address AAAA example.com
# Port scan your own IPv6 address (from external host)
nmap -6 your-ipv6-address
# Test firewall rules
nmap -6 -sS -p- your-ipv6-address # SYN scan all ports
Monitor your firewall logs for:
# Watch firewall logs
tail -f /var/log/syslog | grep IP6TABLES
# Or for systemd-based systems
journalctl -f | grep IP6TABLES
For comprehensive security guidance beyond firewalls, see How Do I Secure My IPv6 Network?
Need to test your IPv6 configuration? Visit test-ipv6.run for a comprehensive, browser-based connectivity test that checks IPv4-only, IPv6-only, and dual-stack scenarios with detailed scoring and latency measurements.