Skip to content

IPv6 in Practice: SLAAC Autoconfiguration, NDP, Dual-Stack & ICMPv6 Firewalling

Created: 2026-11-08  ·  Last updated: 2026-11-08  ·  Nejat Hakan

Executive Summary: IPv6 is not merely IPv4 with longer addresses; it represents a fundamental modernization of the internet protocol suite. Broadcast traffic and ARP have been eliminated in favor of multicast-driven Neighbor Discovery Protocol (NDP). Rather than relying solely on centralized DHCP daemons, nodes can configure their own addresses dynamically via Stateless Address Autoconfiguration (SLAAC). This guide details 128-bit address scoping, NDP message mechanics, deploying a Debian Linux IPv6 router using radvd, configuring dual-stack systemd-networkd, and crafting resilient nftables rules that do not break Path MTU Discovery.

  • 128-Bit Address Scopes


    Understand the structural difference between Link-Local (fe80::/10), Unique Local (fc00::/7), and Global Unicast (2000::/3) prefixes.

  • NDP Replaces ARP


    Trace how ICMPv6 Router Solicitations (RS), Router Advertisements (RA), and Neighbor Solicitations (NS) resolve L2 MAC addresses without broadcasts.

  • Linux Router & radvd


    Turn a Debian server into an autonomous IPv6 router: enable kernel forwarding, configure the Router Advertisement Daemon (radvd), and distribute RDNSS.

  • ICMPv6 Firewalling


    Avoid the catastrophic mistake of blocking all ICMP in IPv6: configure stateful nftables rules to permit Path MTU Discovery and Neighbor Discovery.


1. 128-Bit Address Architecture & Scoping

An IPv6 address comprises 128 binary bits, represented as eight groups of four hexadecimal digits separated by colons:

Full Format:    2001 : 0db8 : 0000 : 0042 : 0000 : 0000 : 0000 : 0001
Shortened:      2001:db8:0:42::1

The Two Golden Compression Rules (RFC 5952)

  1. Omit Leading Zeros: 0042 becomes 42; 0000 becomes 0.
  2. Double Colon (::) Substitution: A contiguous run of all-zero groups can be replaced with :: exactly once per address to prevent ambiguity.

Standard Subnet Sizing: The Universal /64 Prefix

In IPv6, individual subnets are almost universally allocated with a prefix length of /64: * Network Prefix (Leading 64 bits): Routed by ISPs, enterprise firewalls, and data centers. * Interface Identifier (Trailing 64 bits): Uniquely identifies the host interface (2^64 addresses per subnet).

+------------------------------------+------------------------------------+
|   64-bit Network Routing Prefix    |   64-bit Interface Identifier      |
|   (Assigned by ISP / Router)       |   (EUI-64 or RFC 7217 Stable-Priv) |
+------------------------------------+------------------------------------+

Essential Address Scopes & Well-Known Prefixes

IPv6 Scope Prefix Range IPv4 Equivalent Scope & Routing Behavior
Loopback ::1/128 127.0.0.1 Local host only; never transmitted across physical links.
Link-Local fe80::/10 169.254.0.0/16 Mandatory on every IPv6 interface. Non-routable; valid only on the local physical link. Requires a scope ID (e.g., fe80::1%eth0).
Unique Local (ULA) fc00::/7 (fd00::/8) RFC 1918 Private Routable across private corporate WANs, site-to-site VPNs, and homelabs. Not routable on the public internet (RFC 4193).
Global Unicast (GUA) 2000::/3 Public IPv4 Globally unique and publicly routable across the global internet.
Multicast ff00::/8 224.0.0.0/4 One-to-many communication. Subsumes all legacy broadcast functions.

2. Neighbor Discovery Protocol (NDP): The Modern Replacement for ARP

IPv6 completely eliminates Layer-2 broadcast storms. There is no such thing as an ARP request in IPv6. Instead, the Neighbor Discovery Protocol (NDP) operates using dedicated ICMPv6 messages sent over efficient multicast groups:

%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
    A["Host A joins Network"] --> B["1. Router Solicitation (RS)<br/>ICMPv6 Type 133 sent to ff02::2 (All Routers)"]
    B --> C["2. Router Advertisement (RA)<br/>ICMPv6 Type 134 replied by Router with /64 Prefix"]
    C --> D["3. SLAAC Autoconfiguration<br/>Host combines /64 Prefix + Interface ID"]
    D --> E["4. Duplicate Address Detection (DAD)<br/>Sends Neighbor Solicitation (NS) Type 135 to verify uniqueness"]
    E --> F["5. L2 MAC Address Resolution<br/>Neighbor Solicitation (NS) & Neighbor Advertisement (NA) Type 136"]

Core ICMPv6 NDP Message Types

  1. Router Solicitation (RS - Type 133): Sent by hosts upon link startup to prompt local routers to advertise themselves immediately.
  2. Router Advertisement (RA - Type 134): Sent periodically (and in response to RS) by routers. Announces the /64 prefix, default gateway lifetime, MTU, and DNS servers.
  3. Neighbor Solicitation (NS - Type 135): Replaces ARP Request. Sent to the target's Solicited-Node Multicast Address (ff02::1:ffxx:xxxx) to discover its MAC address or verify reachability.
  4. Neighbor Advertisement (NA - Type 136): Replaces ARP Reply. Informs the requesting node of the target MAC address.
  5. Redirect (Type 137): Informs a host of a better first-hop router for a specific destination.

3. Autoconfiguration: SLAAC vs. DHCPv6

IPv6 provides two distinct mechanisms for dynamic address assignment:

%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
    RA["Router Advertisement (RA) Received"] --> FLAG{"Check M and O Flags in RA"}
    FLAG -->|"M=0, O=0: Pure SLAAC"| SLAAC["Host configures IP autonomously from /64 prefix.<br/>Uses RDNSS (RFC 8106) for DNS."]
    FLAG -->|"M=0, O=1: Stateless DHCPv6"| STATEL["Host configures IP via SLAAC.<br/>Queries DHCPv6 server for DNS/NTP options."]
    FLAG -->|"M=1: Stateful DHCPv6"| STATEF["Host requests IP and options<br/>directly from DHCPv6 server (like IPv4)."]
  • SLAAC (Stateless Address Autoconfiguration): The router does not maintain a database of assigned IPs. Hosts generate their own 64-bit host IDs (either randomly via RFC 7217 Stable Privacy to prevent hardware MAC tracking, or via legacy EUI-64).
  • DHCPv6 (Stateful): A central server leases individual addresses and tracks MAC-to-IP bindings.

4. Deploying an Autonomous Linux IPv6 Router (radvd)

You can easily configure a Debian Linux server to serve as an IPv6 router and announce network prefixes to all client machines on eth1.

Step 1: Enable IPv6 Forwarding in the Linux Kernel

Create /etc/sysctl.d/70-ipv6-routing.conf:

# File: /etc/sysctl.d/70-ipv6-routing.conf
# Enable IPv6 packet forwarding across all network interfaces
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.default.forwarding = 1

# Accept Router Advertisements even if forwarding is enabled on WAN interface
net.ipv6.conf.eth0.accept_ra = 2

Apply changes immediately:

sudo sysctl --system

Step 2: Install and Configure radvd

Install the Router Advertisement Daemon:

sudo apt update && sudo apt install -y radvd

Edit /etc/radvd.conf:

# File: /etc/radvd.conf
interface eth1 {
    # Send periodic Router Advertisements (every 200-600 seconds)
    AdvSendAdvert on;
    MinRtrAdvInterval 60;
    MaxRtrAdvInterval 200;

    # M-Flag = off (Use SLAAC), O-Flag = off (No DHCPv6 needed)
    AdvManagedFlag off;
    AdvOtherConfigFlag off;

    # Advertise local /64 prefix for SLAAC autoconfiguration
    prefix 2001:db8:cafe:1::/64 {
        AdvOnLink on;
        AdvAutonomous on;
        AdvRouterAddr on;
    };

    # Distribute recursive DNS servers via RDNSS (RFC 8106)
    RDNSS 2001:4860:4860::8888 2606:4700:4700::1111 {
        AdvRDNSSLifetime 600;
    };
};

Enable and start the daemon:

sudo systemctl enable --now radvd
sudo systemctl status radvd

5. Dual-Stack Host Configuration with systemd-networkd

Configure a robust dual-stack Debian server supporting both static IPv4 and automated SLAAC IPv6:

# File: /etc/systemd/network/10-dualstack-eth0.network
[Match]
Name=eth0

[Network]
# Static IPv4 Configuration
Address=192.168.1.50/24
Gateway=192.168.1.1
DNS=192.168.1.1

# IPv6 Configuration: Enable SLAAC & Accept Router Advertisements
IPv6AcceptRA=yes
DHCP=no

# Optional: Assign a static ULA address for internal communications
Address=fd00:dead:beef:1::50/64

[IPv6AcceptRA]
# Accept DNS servers advertised in Router Advertisements
UseDNS=yes
UseAutonomousPrefix=yes

Reload the network configuration:

sudo networkctl reload
networkctl status eth0

6. Stateful nftables Firewalling for IPv6: The ICMPv6 Imperative

CRITICAL WARNING: Never Drop All ICMPv6

In legacy IPv4, security guides often recklessly advised DROP icmp. Doing this in IPv6 will completely destroy network connectivity. Without ICMPv6, Neighbor Discovery (NDP) fails (hosts cannot resolve MACs) and Path MTU Discovery (PMTUD) breaks (packets larger than the path MTU are silently dropped, causing TCP connections to hang indefinitely during TLS handshakes).

Here is a hardened, production-grade /etc/nftables.conf for a dual-stack Debian server:

# File: /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;

        # 1. Allow established and related connections
        ct state established,related accept

        # 2. Drop invalid packets
        ct state invalid drop

        # 3. Allow all loopback traffic
        iif "lo" accept

        # 4. ESSENTIAL: Permit essential ICMPv6 types
        ip6 nexthdr icmpv6 icmpv6 type {
            destination-unreachable,   # Type 1: Required for error reporting
            packet-too-big,            # Type 2: MANDATORY for Path MTU Discovery
            time-exceeded,             # Type 3: Traceroute and TTL expiration
            parameter-problem,         # Type 4: Protocol syntax errors
            echo-request,              # Type 128: Ping diagnostic
            nd-router-advert,          # Type 134: SLAAC Router Advertisements
            nd-neighbor-solicit,       # Type 135: NDP MAC resolution
            nd-neighbor-advert         # Type 136: NDP MAC resolution reply
        } accept

        # 5. Allow inbound SSH on port 22 (IPv4 & IPv6)
        tcp dport 22 accept
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
    }

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

Apply and verify the ruleset:

sudo nft -f /etc/nftables.conf
sudo nft list ruleset

7. Practical IPv6 CLI Diagnostics

Always use the -6 flag with standard iproute2 commands:

# 1. Inspect all IPv6 addresses (look for GUA 'global' and Link-Local 'fe80')
ip -6 -br addr show

# 2. Inspect the IPv6 routing table (verify default gateway via fe80::...)
ip -6 route show

# 3. Inspect the Neighbor Cache (replaces 'arp -n')
ip -6 neigh show

# 4. Verify end-to-end IPv6 connectivity and Path MTU
ping -6 -c 3 2606:4700:4700::1111
mtr -6 google.com

8. Next Steps

With your foundational understanding of IPv4 subnetting and IPv6 protocol mechanics in place, master the unified Linux administration suite:

  • The Modern iproute2 Suite in Daily Operations


    Replace legacy ifconfig, netstat, and route commands with Netlink-powered ip addr, ip link, ip route, and ss.

    iproute2 Suite Guide