If you've spent any time configuring user authentication on... Full Story
By Manny Fernandez
October 15, 2026
APIPA Explained: The 169.254.0.0/16 Address Space, When It Is a Problem, and When It Is by Design
Executive Summary
Objective: Explain exactly what the 169.254.0.0/16 address space is, how a host ends up with one of its addresses, how to tell a broken DHCP path from an intentional design, and how to fix the broken kind quickly.
Target audience: Network and security engineers, FortiGate administrators, and help desk staff who keep hearing “I have a 169 address.”
APIPA stands for Automatic Private IP Addressing. It is Microsoft’s name for the behavior where a host that cannot reach a DHCP server assigns itself an address from 169.254.0.0/16. The IETF standardized the same behavior in RFC 3927 as IPv4 link-local addressing. macOS calls it a self-assigned IP. Linux tooling calls it link-local or IPv4LL. All four names describe the same block and the same protocol.
Most of the time a 169.254 address on a laptop is a symptom: the DHCP conversation failed. But the same block is also used deliberately, by every major cloud for instance metadata, by AWS, Azure, and Google Cloud for VPN tunnel BGP peering, and by FortiGate for HA heartbeat. Knowing which case you are looking at is the whole job.
Prerequisites and Reference Facts
Assumed knowledge
- The DHCP DORA exchange (Discover, Offer, Request, Acknowledge)
- ARP basics: request, reply, and gratuitous ARP
- Basic CLI comfort on Windows, macOS, Linux, and FortiOS
Lab requirements
- One client on an isolated VLAN with no DHCP server (unplugging the uplink of a small switch works)
- A second client on the same VLAN to prove peer-to-peer reachability
- Optional: a FortiGate acting as the DHCP server so you can watch the failure from the server side
The block at a glance
| Property | Value | Why it matters |
|---|---|---|
| Block | 169.254.0.0/16 |
Registered by IANA as Link Local. Never valid on the Internet. |
| Defining standard | RFC 3927 (May 2005) | Formalized behavior Windows 98 and Mac OS 8.5 had already shipped. |
| Subnet mask | 255.255.0.0 |
Always /16. The host treats the whole block as on-link. |
| Self-assignment range | 169.254.1.0 to 169.254.254.255 |
65,024 addresses. The first and last /24 are reserved. |
| Default gateway | None | No path off the segment. This is the root of “no Internet.” |
| DNS server | None | Name resolution falls back to mDNS, LLMNR, or NetBIOS. |
| Router behavior | Must not forward | Traffic with a link-local source or destination stays on the link. |
| IPv6 equivalent | fe80::/10 |
Same idea, but mandatory on every IPv6 interface instead of a fallback. |

Figure 1. The 169.254.0.0/16 block and the RFC 3927 claim sequence.
How Self-Assignment Works
The process is simple and entirely local. There is no server, no lease, and no coordination beyond ARP.
Step 1: DHCP fails
Goal: Understand the trigger. The host broadcasts DHCP DISCOVER messages and backs off between retries. Nothing answers. On Windows the client gives up on the initial attempt after roughly a minute, then falls back to self-assignment.
Step 2: Pick a candidate address
Goal: Understand why the address is usually the same after every reboot. The host picks a pseudo-random address between 169.254.1.0 and 169.254.254.255. RFC 3927 says the generator should be seeded from the interface MAC address, so a given host tends to land on the same address every time and stable peer-to-peer links survive reboots.
Step 3: ARP probe
Goal: Confirm nobody else owns the candidate. The host waits a random interval of up to 1 second, then sends three ARP probes spaced 1 to 2 seconds apart. A probe is an ARP request with the sender IP set to 0.0.0.0 so it cannot poison anyone’s ARP cache. If any ARP traffic shows the address is in use, the host returns to Step 2 and picks again.
Step 4: ARP announce
Goal: Tell the segment who owns the address. Two seconds after the last probe the host sends two ARP announcements, 2 seconds apart, with both sender and target IP set to the claimed address. Neighbors update their caches.
Step 5: Use it, and keep looking for DHCP
Goal: Know that the state is temporary. The host keeps the link-local address but continues to retry DHCP in the background. Windows retries every 5 minutes. The moment a DHCP server answers, the host drops the 169.254 address and takes the lease.
| RFC 3927 constant | Value | Meaning |
|---|---|---|
PROBE_WAIT |
1 second | Maximum random delay before the first probe |
PROBE_NUM |
3 | Number of ARP probes |
PROBE_MIN / PROBE_MAX |
1 s / 2 s | Spacing between probes |
ANNOUNCE_WAIT |
2 seconds | Delay after the last probe before announcing |
ANNOUNCE_NUM |
2 | Number of ARP announcements |
ANNOUNCE_INTERVAL |
2 seconds | Spacing between announcements |
MAX_CONFLICTS |
10 | Conflicts tolerated before rate limiting begins |
RATE_LIMIT_INTERVAL |
60 seconds | Minimum gap between attempts after 10 conflicts |
DEFEND_INTERVAL |
10 seconds | Window for defending an address against a conflict |
0.0.0.0, then two gratuitous ARPs from the new 169.254 address.sudo tcpdump -ni eth0 'arp or (udp port 67 or udp port 68)'
What It Looks Like on Each Operating System
| OS | What the user sees | Fallback enabled by default | DHCP retry |
|---|---|---|---|
| Windows | “Autoconfiguration IPv4 Address” in ipconfig |
Yes | Every 5 minutes |
| macOS | “Self-assigned IP” with a yellow status dot | Yes | Continuous, in the background |
| Linux (NetworkManager) | Connection stuck “connecting”, then fails | No, by default | Per ipv4.dhcp-timeout |
| Linux (systemd-networkd) | No IPv4 address | No (LinkLocalAddressing=ipv6) |
Continuous |
| iOS / Android | Wi-Fi shows connected, no Internet | Yes on iOS, generally no on Android | Continuous |
Windows
Goal: Identify the state and its origin.
Ethernet adapter Ethernet: DHCP Enabled. . . . . . . . . . . : Yes Autoconfiguration Enabled . . . . : Yes Autoconfiguration IPv4 Address. . : 169.254.83.107(Preferred) Subnet Mask . . . . . . . . . . . : 255.255.0.0 Default Gateway . . . . . . . . . :
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.IPAddress -like '169.254.*' } |
Format-Table InterfaceAlias, IPAddress, PrefixOrigin, SuffixOrigin
An APIPA address reports PrefixOrigin as WellKnown and SuffixOrigin as Link. A DHCP lease reports Dhcp for both.
GUI verification: Settings > Network and Internet > the adapter > properties. The IPv4 address shows 169.254.x.x and the gateway and DNS fields are empty.
Windows also offers an Alternate Configuration tab on the IPv4 properties of any DHCP-enabled adapter. Set a static address there and Windows uses it instead of APIPA when DHCP fails. This is the cleaner option for laptops that move between a DHCP network and a static lab bench.
reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" ^ /v IPAutoconfigurationEnabled /t REG_DWORD /d 0 /f
With APIPA disabled the adapter shows 0.0.0.0 when DHCP fails. That makes the failure louder, which some server teams prefer. The same value can be set per interface under Tcpip\Parameters\Interfaces\{GUID}.
macOS
Goal: Confirm the self-assigned state and force a fresh DHCP attempt.
ipconfig getifaddr en0 ipconfig getpacket en0
If getifaddr returns 169.254.x.x and getpacket returns nothing, the Mac never received a DHCP reply on that interface.
sudo ipconfig set en0 DHCP
GUI verification: System Settings > Network. The interface shows a yellow dot and the text “Self-assigned IP.” Details > TCP/IP > Renew DHCP Lease does the same thing as the command above.
Linux
Goal: Know that Linux usually does not fall back on its own, and how to turn link-local on when you want it.
ip -4 addr show scope link
nmcli connection modify "Wired connection 1" ipv4.method link-local nmcli connection up "Wired connection 1"
NetworkManager 1.40 and later also has a separate ipv4.link-local property, so you can keep ipv4.method auto for DHCP and add a link-local address alongside it by setting ipv4.link-local enabled.
[Match] Name=eth0 [Network] DHCP=ipv4 LinkLocalAddressing=fallback
LinkLocalAddressing=fallback assigns a 169.254 address only when DHCP fails. ipv4 or yes assigns one regardless.
Where 169.254.0.0/16 Is Used on Purpose
Because routers never forward the block and it cannot collide with RFC 1918 space, 169.254.0.0/16 became the default choice for “an address that only has to work on this one hop.” None of the uses below involve RFC 3927 self-assignment. They are static, deliberate assignments.
| Address or range | Used by | Purpose |
|---|---|---|
169.254.169.254 |
AWS, Azure, Google Cloud, OpenStack, others | Instance metadata service (IMDS) |
169.254.169.253 |
AWS | VPC DNS resolver (link-local alias) |
169.254.169.123 |
AWS | Amazon Time Sync Service (NTP) |
169.254.170.2 |
AWS ECS | Task metadata and task IAM credentials |
/30 from 169.254.0.0/16 |
AWS Site-to-Site VPN | Tunnel inside addresses for BGP |
169.254.21.0 to 169.254.22.255 |
Azure VPN Gateway | Custom APIPA BGP peer addresses |
/30 from 169.254.0.0/16 |
Google Cloud Router | BGP session addresses on HA VPN and Interconnect |
169.254.0.x |
FortiGate FGCP HA | Heartbeat interface addresses between cluster members |
169.254.1.1/24 |
FortiGate FortiLink (older FortiOS) | Default FortiLink interface address before the move to 10.255.1.1 |
169.254.20.10 |
Kubernetes NodeLocal DNSCache | Per-node DNS cache listener |
Cloud metadata at 169.254.169.254
Every instance can reach its metadata service at the same address without knowing anything about the VPC it sits in. On AWS, use IMDSv2, which requires a session token:
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \ -H "X-aws-ec2-metadata-token-ttl-seconds: 60") curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \ http://169.254.169.254/latest/meta-data/instance-id
169.254.169.254 as a destination in any proxy or URL-fetching feature that accepts user-supplied URLs.VPN tunnel BGP on a FortiGate
AWS assigns each Site-to-Site VPN tunnel a /30 from 169.254.0.0/16. AWS takes the first usable address and the customer gateway takes the second. On the FortiGate the tunnel interface gets the customer side and BGP peers with the AWS side. The example uses 169.254.44.88/30.
config system interface
edit "aws-tun1"
set ip 169.254.44.90 255.255.255.255
set remote-ip 169.254.44.89 255.255.255.252
set allowaccess ping
next
end
config router bgp
set as 65010
set router-id 10.0.0.1
config neighbor
edit "169.254.44.89"
set remote-as 64512
set update-source "aws-tun1"
set soft-reconfiguration enable
next
end
config network
edit 1
set prefix 10.0.0.0 255.255.0.0
next
end
end
GUI verification: Network > Interfaces shows the tunnel with IP and Remote IP populated. Dashboard > Network > Routing > BGP Neighbors shows the peer as Established.
- AWS reserves
169.254.0.0/30through169.254.5.0/30and169.254.169.252/30. You cannot use them as inside tunnel CIDRs. - Azure VPN Gateway only accepts APIPA BGP addresses from
169.254.21.0through169.254.22.255. - Running several tunnels to different clouds? Track the /30 allocations in IPAM. Nothing stops two providers from handing you the same /30.
FortiGate HA heartbeat
FGCP cluster members assign themselves 169.254.0.x addresses on the heartbeat interfaces, inside the hidden HA management VDOM. Session sync, configuration sync, and execute ha manage all ride on those addresses. You do not configure them, but you will see them:
get system ha status diagnose ip address list | grep 169.254
If you find 169.254.0.x addresses on a FortiGate, that is HA working as designed, not a DHCP failure.
Verification and Validation
Use the lab from the prerequisites to prove the three properties that matter.
Test 1: peers on the same segment can talk
ping 169.254.12.9 arp -a
Expected result: replies succeed and the ARP table shows client B’s MAC address. Two APIPA hosts on one VLAN have full layer 3 reachability to each other.
Test 2: nothing leaves the segment
route print -4 | findstr /C:"0.0.0.0"
Expected result: no 0.0.0.0 route. On macOS or Linux, netstat -rn or ip route shows only the 169.254.0.0/16 connected route.
Test 3: recovery is automatic
Restore the DHCP path and wait, or force it:
ipconfig /release ipconfig /renew
sudo networkctl renew eth0 sudo nmcli connection up "Wired connection 1"
Expected result: the 169.254 address disappears and a leased address, gateway, and DNS server appear.
Troubleshooting and Gotchas
When a client shows 169.254.x.x and should not, the question is always the same: where did the DORA exchange die? Work from the client outward.
| Symptom | Likely cause | Check |
|---|---|---|
| One client affected | Bad cable, wrong VLAN on the port, 802.1X or NAC holding the port | Switch port status, VLAN assignment, authentication state |
| Whole VLAN affected | DHCP server down, scope exhausted, relay missing or wrong | Server lease table, relay configuration on the gateway |
| New clients only | Scope exhausted, existing leases still valid | Free addresses in the pool, lease time |
| Fails right after link up | Spanning tree listening and learning delay outlasts the DHCP attempt | Enable edge port (PortFast) on access ports |
| Wi-Fi clients only | SSID bridged to a VLAN that is not trunked to the AP or has no DHCP scope | AP and controller logs, SSID to VLAN mapping |
| Intermittent | DHCP snooping dropping offers from an untrusted uplink, or a rogue server | Snooping trusted ports, capture for unexpected OFFER sources |
Failure point 1: the DISCOVER never reaches the server
On a FortiGate that is the DHCP server or relay for the VLAN, sniff the client-facing interface:
diagnose sniffer packet port2 'udp port 67 or udp port 68' 4 0 l
No packets at all: the problem is below layer 3. Check the switch port, VLAN tagging, and any NAC or 802.1X policy. DISCOVER arrives but no OFFER leaves: continue to failure point 2.
Failure point 2: the server has nothing to offer
execute dhcp lease-list port2 show system dhcp server diagnose debug application dhcps -1 diagnose debug enable
Look for an exhausted ip-range, a server bound to the wrong interface, or a reserved-address conflict. Turn the debug off with diagnose debug disable and diagnose debug reset when finished.
Resolution: widen the range, shorten lease-time on high-churn networks such as guest Wi-Fi, or clear stale leases with execute dhcp lease-clear (all or a specific address).
Failure point 3: the relay is the problem
If the DHCP server is on another subnet, the gateway interface must relay. On FortiOS:
config system interface
edit "port2"
set dhcp-relay-service enable
set dhcp-relay-ip "10.0.10.5"
next
end
Then confirm the server has a scope matching the relay interface subnet and a route back to it. The server replies to the relay address (giaddr), not to the client.
Gotchas
- A 169.254 source arriving at a firewall is noise, not an attack by default. FortiGate drops it on a routed interface because the reverse path check fails. It shows up in
diagnose debug flowas a reverse path check failure. - APIPA hosts are invisible to your DHCP logs. Two self-assigned hosts on the same VLAN can still reach each other, so a failed DHCP path is not the same thing as an isolated host.
- Multi-homed Windows servers. A disconnected or unused NIC that self-assigns can register a 169.254 address in DNS or become the address a cluster service binds to. Disable unused adapters or disable APIPA on servers.
- Do not assign 169.254 addresses statically on a LAN outside the point-to-point cases above. Many stacks treat the block specially and will refuse to route to or from it.
- Not every 169.254 address is a fault. Check the table in the previous section before opening a ticket about a FortiGate heartbeat interface or a cloud tunnel.
Quick Reference
| Task | Command |
|---|---|
| Windows: show state | ipconfig /all |
| Windows: retry DHCP | ipconfig /release then ipconfig /renew |
| macOS: show address | ipconfig getifaddr en0 |
| macOS: retry DHCP | sudo ipconfig set en0 DHCP |
| Linux: show link-local | ip -4 addr show scope link |
| Linux: retry DHCP | sudo networkctl renew eth0 |
| FortiOS: sniff DHCP | diagnose sniffer packet port2 'udp port 67 or udp port 68' 4 0 l |
| FortiOS: leases | execute dhcp lease-list |
| Any: capture the claim | tcpdump -ni eth0 'arp or (udp port 67 or udp port 68)' |
Bottom line: 169.254.0.0/16 on an end-user device means DHCP failed, so go find where the DORA exchange died. 169.254.0.0/16 on a tunnel interface, an HA heartbeat, or in a cloud instance route table means someone used the block exactly as intended.
References
- RFC 3927, Dynamic Configuration of IPv4 Link-Local Addresses
- RFC 5227, IPv4 Address Conflict Detection
- RFC 6890 and the IANA IPv4 Special-Purpose Address Registry
Recent posts
-
-
DNS is one of those technologies that quietly underpins... Full Story
-
BGP issues on FortiGate firewalls usually trace back to... Full Story
-
Every time your laptop talks to your router, a... Full Story
-
If you've spent any time configuring NAT on a... Full Story
-
If you have spent any time configuring firewall policies... Full Story
-
High availability on FortiGate is one of those features... Full Story
-
If you've configured SD-WAN on a FortiGate, you've almost... Full Story
-
FortiLink is the management protocol that turns a FortiSwitch... Full Story
-
FortiSwitches are pretty rock solid from Mean Time Between... Full Story
-
This is a quicky tip. Have you ever gone... Full Story
-
DNS is one of those quiet pieces of internet... Full Story
-
This article is an updated version of the previous... Full Story
-
You will add ns2 as a secondary (slave) BIND9... Full Story
-
In the process of deploying my lab, I needed... Full Story
-
RFC 8805, used to be known as Self-Correcting IP... Full Story
-
Years back, I wrote an article about certificate pinning. ... Full Story
-
FortiGates have the ability to send alerts to Microsoft... Full Story
-
In this post, I am going to walk through... Full Story
-
Troubleshooting VoIP on a FortiGate can feel like trying... Full Story
-
Prior to FortiOS 7.0, there were three commands to... Full Story
-
In this post, I am going to go over... Full Story
-
What we are going to do: We are going... Full Story
-
Choosing between FGCP (FortiGate Clustering Protocol) and FGSP (FortiGate... Full Story
-
Creating a VLAN on macOS (The "Pro" Move) A... Full Story
-
This blog post explores the logic behind how macOS... Full Story
-
Pretty Fly for a Wi-Fi Tell My Wi-Fi Love... Full Story
-
Part of my daily gig is creating BoMs (Bill-of-Materials)... Full Story
-
ICMP introduces several security risks, but careful filtering, rate... Full Story
-
The command diag debug application dhcps -1 enables full... Full Story
-
In the world of FortiOS, execute tac report is... Full Story
-
LLDP; What is it The Link Layer Discovery Protocol... Full Story
-
What it actually does When you run diagnose fdsm... Full Story
-
Monkey Bites are bite-sized, high-impact security insights designed for... Full Story
-
I have run macOS in macOS with Parallels but... Full Story
-
Don't be confused with my other FortiNAC posts where... Full Story
-
This is the third session in a multi-part article... Full Story
-
Today I was configuring key-based authentication on a FortiGate... Full Story
-
Netcat, often called the "Swiss Army knife" of networking,... Full Story
-
At its core, IEEE 802.1X is a network layer... Full Story
-
In case you did not see the previous FortiNAC... Full Story
-
This is our 5th session where we are going... Full Story
-
Now that we have Wireshark installed and somewhat configured,... Full Story
-
The Philosophy of Packet Analysis Troubleshooting isn't about looking... Full Story
-
Port Control Protocol server, pools, pcp-outbound SNAT and pcp-inbound... Full Story
-
The Tuesday Afternoon Call It is a quiet Tuesday... Full Story
-
A practitioner breakdown of every TLS version: handshakes, cipher... Full Story