Zone identifiers (RFC 4007) disambiguate IPv6 addresses with non-global scope. Since link-local addresses can be identical on different interfaces, the zone ID specifies which interface to use.
Zone ID Syntax:
- Format: address%zone_id
- Example: fe80::1%eth0, fe80::1%2, 2001:db8::1%site1
- Zone ID can be interface name, numeric index, or scope name
- Percent sign (%) separates address from zone identifier
When Zone IDs Are Required:
- Link-local (fe80::/10): Always require zone for communication
- Site-local (fec0::/10, deprecated): Required for multi-site hosts
- Multicast link-local (ff02::/16): May require zone
- Global unicast: Never require zone (globally unique)
Common Zone ID Forms:
- Linux: eth0, eth1, wlan0, docker0, etc.
- macOS: en0, en1, lo0, etc.
- Windows: Numeric index (e.g., %1, %2, %11)
- Generic: Interface name or scope identifier
Examples of Zone ID Usage:
- ping fe80::1%eth0 (specify which interface to ping through)
- ssh [fe80::1%en0] (SSH to link-local address on en0)
- curl http://[fe80::1%wlan0]:8080 (HTTP with zone in URL)
- nc fe80::1%2 80 (netcat with numeric zone ID)
URL Encoding:
In URLs, the % sign must be percent-encoded as %25:
- Correct: http://[fe80::1%25eth0]:80
- Incorrect: http://[fe80::1%eth0]:80 (browser interprets % as URL encoding)
Common Errors:
- "Invalid argument": Missing zone ID for link-local address
- "Network unreachable": Wrong zone ID or interface down
- "No route to host": Zone ID doesn't match actual interface
Programming Considerations:
- getaddrinfo() requires zone ID in sin6_scope_id field for link-local
- Zone IDs are system-specific (interface names differ across hosts)
- Applications should accept zone IDs in user input for link-local addresses
- Some libraries auto-parse zone IDs from address%zone format
Why Global Addresses Don't Need Zones:
Global unicast addresses (2000::/3) are globally unique, so there's no ambiguity about which interface to use. The routing table determines the outbound interface automatically.