IPv6 Private Addressing: ULA vs Link-Local for Home Networks – A Practical Guide for Developers


2 views

The IPv6 equivalent to IPv4's RFC1918 private addresses is indeed Unique Local Addresses (ULA) defined in RFC4193. While IPv6 design philosophy differs from IPv4, ULAs serve similar purposes for internal networking while preventing conflicts when networks merge.

# Example ULA address generation (Linux)
uuidgen | sha1sum | cut -c 1-10
# Output: 1a2b3c4d5e
# Resulting ULA prefix: fd1a:2b3c:4d5e::/48

For small networks, you can simplify ULA usage while maintaining standards compliance:

# Recommended simple home ULA scheme
Network: fd00:0:0:4::/64
First host: fd00:0:0:4::1
Last host: fd00:0:0:4::ffff:ffff:ffff:ffff

The fc00::/7 range is split into two parts:

  • fc00::/8 - Reserved for future definition
  • fd00::/8 - Currently defined for ULAs with L=1 bit set

These fe80::/10 addresses serve critical functions even in home networks:

# View link-local addresses on Linux
ip -6 addr show scope link
# Example output: fe80::1a2b:3cff:fe4d:5e6f/64

# Ping test using scope identifier
ping6 fe80::1a2b:3cff:fe4d:5e6f%eth0

Scope identifiers (%ifname or %number) are crucial for distinguishing identical addresses on different interfaces:

# Python example using scope IDs
import socket

addr = socket.getaddrinfo("fe80::1%eth0", None, socket.AF_INET6)
print(addr[0][4])  # Returns ('fe80::1', 0, 0, 2)

Prevent accidental ULA routing with these iptables rules:

# Block ULA from being routed externally
ip6tables -A FORWARD -s fd00::/8 -j DROP
ip6tables -A OUTPUT -s fd00::/8 -j DROP

Here's a complete home router configuration snippet:

# Enable ULA addressing
echo 1 > /proc/sys/net/ipv6/conf/all/use_tempaddr
echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra

# Configure radvd for SLAAC
cat < /etc/radvd.conf
interface eth0 {
    AdvSendAdvert on;
    prefix fd00:0:0:4::/64 {
        AdvOnLink on;
        AdvAutonomous on;
        AdvRouterAddr on;
    };
};
EOF

For developers transitioning from IPv4, the IPv6 equivalent of RFC1918 private addresses is Unique Local Addresses (ULAs) defined in RFC4193. Unlike IPv4's three predefined ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), ULAs use fd00::/8 with a mandatory 40-bit random Global ID:

# Standard ULA format (RFC4193 compliant)
fd[XX:XXXX:XXXX]::/48
  │   └─ 40-bit random Global ID
  └─ First octet must be 'fd' (not fc)

For a home network equivalent to 192.168.4.0/24, you should generate a proper ULA rather than using fc00::4:0/120. Here's why:

  • fc00::/7 is technically reserved (not just fd00::/8), but only fd00::/8 is implemented
  • Using non-random prefixes breaks RFC4193's collision avoidance design
  • Modern IPv6 stacks may flag fc00:: as invalid

Every IPv6 interface must have a link-local address (fe80::/10). These are automatically configured using the interface's MAC address (EUI-64 format):

# Example link-local address generation
MAC Address:    00:1a:2b:3c:4d:5e
EUI-64:        fe80::21a:2bff:fe3c:4d5e

Key characteristics:

  • Only valid on the physical link (not routable)
  • Required for neighbor discovery (NDP) and router advertisements
  • Always has /64 prefix length

The scope ID (% notation) resolves ambiguity when multiple interfaces have identical link-local addresses. This commonly occurs on:

  • Devices with dual network interfaces (WiFi + Ethernet)
  • Virtual machines sharing a host
  • Containers using bridge networking
# Windows example with scope ID
ping fe80::1%4  # Interface index 4

# Linux equivalent
ping6 fe80::1%eth0

Here's a complete configuration example for a home router:

# Generate ULA prefix (Linux/Mac)
openssl rand -hex 5 | sed 's/$..$$..$$..$$..$$..$/fd\1:\2\3:\4\5::/48'

# Router configuration (example output)
ip -6 addr add fd12:3456:789a::1/64 dev lan0
ip -6 route add fd12:3456:789a::/64 dev lan0

# Client configuration (DHCPv6/SLAAC will assign fd12:3456:789a::/64 addresses)

Security considerations:

# Block ULA outbound traffic (iptables example)
ip6tables -A FORWARD -s fd00::/8 -j DROP

# Prevent accidental ULA routing advertisements
ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 134 -j DROP
Type Prefix Scope Example
Global Unicast 2000::/3 Internet 2600:1f18:1234::1
Unique Local fd00::/8 Private fd12:3456:789a::1
Link-Local fe80::/10 Link fe80::1%eth0