A /31 has the netmask 255.255.255.254 and holds 2 addresses, and both are usable hosts. By the usual rule a /31 would have nothing left after its network and broadcast addresses, but RFC 3021 defines /31s for point-to-point links, which need neither: 10.0.0.0/31 puts one end on 10.0.0.0 and the other on 10.0.0.1.
The numbers
First host 10.0.0.0
Last host 10.0.0.1
Netmask 255.255.255.254
Wildcard 0.0.0.1
Addresses 2
Usable hosts 2
The tool, and its API, leave out the broadcast address for a /31 and add RFC 3021’s note.
How it fits
A /30 holds two /31s and a /24 holds 128. A /31 splits into 2 /32s.
Where you’ll see it
A link between two routers has exactly two ends, so a /31 numbers it with no waste. Data center fabrics, where every leaf switch links to every spine, use /31s for those links and the BGP sessions on them: a /24 numbers 128 links as /31s, against 64 as /30s. Most numbering that was done with /30s can move to /31s once both ends support them.
Gotchas
- Point-to-point only. RFC 3021 is for a link with exactly two ends. A LAN segment with a third device, such as a monitoring host or a second router, needs a /29 or larger.
- Both ends must be /31. If one side is configured
10.0.0.1/30, it treats10.0.0.0as a network address rather than its neighbor, and the link misbehaves. - Not every platform. AWS subnets stop at /28 and Azure and Google Cloud at /29, so cloud subnets can’t be /31s. Older software that computes usable hosts as 2^(32 − n) − 2 gets 0 and rejects them. Python’s
ipaddressfollows RFC 3021:hosts()returns both addresses. - IPv6 does it differently. The counterpart on an IPv6 link is a /127, which RFC 6164 recommends for links between routers; the tool counts both of its addresses as usable, with a note saying why.
In your stack
! Cisco IOS
interface Ethernet1/1
ip address 10.0.0.0 255.255.255.254
# Junos
set interfaces xe-0/0/0 unit 0 family inet address 10.0.0.0/31
ip addr add 10.0.0.1/31 dev eth1 # Linux, the other end
import ipaddress
list(ipaddress.ip_network("10.0.0.0/31").hosts())
# [IPv4Address('10.0.0.0'), IPv4Address('10.0.0.1')]
# Terraform: the second /31 of a /30, and the netmask
cidrsubnet("10.0.0.0/30", 1, 1) # "10.0.0.2/31"
cidrnetmask("10.0.0.0/31") # "255.255.255.254"