2025-04-17 RFC 6666 blackhole (IPv6 100::/64 prefix) OS behaviour

From Wikistix

RFC 6666 allocates an IPv6 prefix 100::/64 for blackhole routing. On the broader Internet, IPv6 packets to addresses under this prefix are generally dropped within ISP networks.

I recently had the need to be able to blackhole IPv6 packets on an isolated network; I specifically did not want to have a quick response to connect in the form of ENETUNREACH, ECONNREFUSED, EHOSTUNREACH, etc. I needed the behaviour of silent blackholing of traffic.

After discovering RFC 6666 "A Discard Prefix for IPv6", this seemed like the perfect solution. However, it seems the behaviour of various operating systems with their support for blackhole/discard IPv6 routes ended up being a problem.

Testing on three OSes I had ready root access to shows strongly differing behaviour.

NetBSD

My home OS of choice, this ended up being the best of the three I've checked to date. I believe the IPv6 stack is based on KAME, and the end behaviour was exactly what I was expecting: the silent dropping of packets.

ksh# route add -inet6 100::/64 ::1 -blackhole
add net 100::/64: gateway ::1
ksh# route get -inet6 100::
   route to: 100::
destination: 100::
       mask: ffff:ffff:ffff:ffff::
    gateway: localhost
 local addr: localhost
  interface: lo0
      flags: 0x1843<UP,GATEWAY,DONE,STATIC,BLACKHOLE>
 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
       0         0         0         0         0         0     33624         0 
ksh# ping6 -c 2 100::
PING6(56=40+8+8 bytes) 2001:44b8:30e5:c2:87b2:2783:139a:ff15 --> 100::

--- 100:: ping6 statistics ---
2 packets transmitted, 0 packets received, 100.0% packet loss
ksh# route delete -inet6 100::/64
delete net 100::/64

macOS

I figured macOS would be the same as NetBSD, given its largely BSD roots, but not so. The response is an immediate and unexpected EHOSTUNREACH.

ksh# route add -inet6 100::/64 ::1 -blackhole
add net 100::/64: gateway ::1
ksh# route get -inet6 100::
   route to: 100::
destination: 100::
       mask: ffff:ffff:ffff:ffff::
    gateway: localhost
  interface: lo0
      flags: <UP,GATEWAY,DONE,STATIC,BLACKHOLE,PRCLONING>
 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
       0         0         0         0         0         0     16384         0 
ksh# ping6 -c 2 100::
ping6: UDP connect: No route to host
ksh# route delete -inet6 100::/64
delete net 100::/64

Linux

Of all the OSes, I expected Linux to just Do The Right Thing. But this was not the case, with the strangest error of all, EINVAL. This also means that Linux behaves very differently for local programs and as a remote router. As you can see, even route get fails with EINVAL, which made me double check another local route.

ksh# ip route add blackhole 100::/64
ksh# ip route get 100::
RTNETLINK answers: Invalid argument
ksh# ip route get ::1
local ::1 from :: dev lo table local proto kernel src ::1 metric 0 pref medium
ksh# ping6 -c 2 100::
connect: Invalid argument
ksh# ip route del 100::/64

This behaviour has been around a long time, with the IPv6 behaviour introduced by this commit all the way back in 2012, copying from the IPv4 behaviour introduced before the initial git commit with Linux-2.6.12-rc2 in 2005.