diff --git a/utils/openbsd/pf-martians/generate-tables.sh b/utils/openbsd/pf-martians/generate-tables.sh
new file mode 100644
index 0000000..c7f4436
--- /dev/null
+++ b/utils/openbsd/pf-martians/generate-tables.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+script=iana-ip-special-registry-parser.py
+
+process_xml()
+{
+ v=$1
+ dir=$2
+ in=iana-ip$v-special-registry.xml
+ out=martians-$v-${dir%bound}.txt
+
+ python3 "$script" "$dir" <"$in" >"$out"
+}
+
+process_xml v4 inbound
+process_xml v4 outbound
+process_xml v6 inbound
+process_xml v6 outbound
diff --git a/utils/openbsd/pf-martians/iana-ip-special-registry-parser.py b/utils/openbsd/pf-martians/iana-ip-special-registry-parser.py
index 87d729b..5c4faff 100644
--- a/utils/openbsd/pf-martians/iana-ip-special-registry-parser.py
+++ b/utils/openbsd/pf-martians/iana-ip-special-registry-parser.py
@@ -1,38 +1,101 @@
#!/usr/bin/env python3
-import csv
-import re
+import argparse
+from enum import Enum
import sys
+import xml.sax
-REGEX_FOOTNOTES = re.compile(r" \[\d+\]")
-REGEX_WHITESPACES = re.compile(r"\s+")
-def parse_iana_csv(csvfile):
- reader = csv.DictReader(csvfile)
- for row in reader:
- fmt = ""
- reachable = row["Globally Reachable"]
- if reachable == "":
- continue
- if "False" in reachable:
- fmt = "{}"
- elif "True" in reachable:
- fmt = "!{}"
- elif "N/A" in reachable:
- fmt = "#{}"
- else:
- fmt = "#[" + reachable + "]: {}"
- blocks = row["Address Block"]
- blocks = REGEX_FOOTNOTES.sub("", blocks)
- blocks = REGEX_WHITESPACES.sub("", blocks)
- for block in blocks.split(","):
- print(fmt.format(block))
+class ParserState(Enum):
+ NOOP = 0
+ DISCARD = 1
+ CAPTURE = 2
+
+
+class IPDisallowedHandler(xml.sax.ContentHandler):
+ TRANSITIONS = {
+ "address": ParserState.CAPTURE,
+ "source": ParserState.CAPTURE,
+ "destination": ParserState.CAPTURE,
+ "global": ParserState.CAPTURE,
+ }
+
+ def __init__(self):
+ self._reset()
+
+ def _reset(self):
+ self._state = ParserState.NOOP
+ self._address = None
+ self._source = None
+ self._destination = None
+ self._global = None
+ self._content = ""
+
+ def _processElement(self, name):
+ pass
+
+ def startElement(self, name, attrs):
+ if name == "record":
+ self._state = ParserState.DISCARD
+ return
+ if self._state == ParserState.NOOP:
+ return
+
+ self._state = self.TRANSITIONS.get(name, ParserState.DISCARD)
+
+ def endElement(self, name):
+ if name == "record":
+ if self._address is not None:
+ self._processElement()
+ self._reset()
+ if self._state == ParserState.NOOP:
+ return
+
+ if self._content == "":
+ return
+ self._content = self._content.strip()
+ if name == "address":
+ self._address = self._content.split(", ")
+ if name == "source":
+ self._source = self._content == "True"
+ if name == "destination":
+ self._destination = self._content == "True"
+ if name == "global":
+ self._global = self._content == "True"
+ self._content = ""
+ self._state = ParserState.DISCARD
+
+ def characters(self, content):
+ if self._state != ParserState.CAPTURE:
+ return
+ self._content += content
+
+
+class IPDisallowedInboundHandler(IPDisallowedHandler):
+ def _processElement(self):
+ if not self._source:
+ for address in self._address:
+ print(address)
+
+
+class IPDisallowedOutboundHandler(IPDisallowedHandler):
+ def _processElement(self):
+ # Document states:
+ # > If the value of "Destination" is FALSE, the values of
+ # > "Forwardable" and "Globally Reachable" must also be false.
+ # So, `self._destination == False` implies `self._global ==
+ # False.` For this reason, it's enough to test for
+ # `self._global == False`.
+ if not self._global:
+ for address in self._address:
+ print(address)
if __name__ == "__main__":
- def usage():
- print("Usage: {}".format(sys.argv[1]), file=sys.stderr)
- sys.exit(1)
+ parser = argparse.ArgumentParser()
+ parser.add_argument("direction", choices=["inbound", "outbound"])
+ args = parser.parse_args()
- if len(sys.argv) != 1:
- usage()
-
- parse_iana_csv(sys.stdin)
+ if args.direction == "inbound":
+ handler = IPDisallowedInboundHandler()
+ else:
+ handler = IPDisallowedOutboundHandler()
+ xml.sax.parse(sys.stdin, handler)
diff --git a/utils/openbsd/pf-martians/iana-ipv4-special-registry-1.csv b/utils/openbsd/pf-martians/iana-ipv4-special-registry-1.csv
deleted file mode 100644
index 356f37f..0000000
--- a/utils/openbsd/pf-martians/iana-ipv4-special-registry-1.csv
+++ /dev/null
@@ -1,25 +0,0 @@
-Address Block,Name,RFC,Allocation Date,Termination Date,Source,Destination,Forwardable,Globally Reachable,Reserved-by-Protocol
-0.0.0.0/8,"""This host on this network""","[RFC1122], Section 3.2.1.3",1981-09,N/A,True,False,False,False,True
-10.0.0.0/8,Private-Use,[RFC1918],1996-02,N/A,True,True,True,False,False
-100.64.0.0/10,Shared Address Space,[RFC6598],2012-04,N/A,True,True,True,False,False
-127.0.0.0/8,Loopback,"[RFC1122], Section 3.2.1.3",1981-09,N/A,False [1],False [1],False [1],False [1],True
-169.254.0.0/16,Link Local,[RFC3927],2005-05,N/A,True,True,False,False,True
-172.16.0.0/12,Private-Use,[RFC1918],1996-02,N/A,True,True,True,False,False
-192.0.0.0/24 [2],IETF Protocol Assignments,"[RFC6890], Section 2.1",2010-01,N/A,False,False,False,False,False
-192.0.0.0/29,IPv4 Service Continuity Prefix,[RFC7335],2011-06,N/A,True,True,True,False,False
-192.0.0.8/32,IPv4 dummy address,[RFC7600],2015-03,N/A,True,False,False,False,False
-192.0.0.9/32,Port Control Protocol Anycast,[RFC7723],2015-10,N/A,True,True,True,True,False
-192.0.0.10/32,Traversal Using Relays around NAT Anycast,[RFC8155],2017-02,N/A,True,True,True,True,False
-"192.0.0.170/32, 192.0.0.171/32",NAT64/DNS64 Discovery,"[RFC-cheshire-sudn-ipv4only-dot-arpa-17][RFC7050], Section 2.2",2013-02,N/A,False,False,False,False,True
-192.0.2.0/24,Documentation (TEST-NET-1),[RFC5737],2010-01,N/A,False,False,False,False,False
-192.31.196.0/24,AS112-v4,[RFC7535],2014-12,N/A,True,True,True,True,False
-192.52.193.0/24,AMT,[RFC7450],2014-12,N/A,True,True,True,True,False
-192.88.99.0/24,Deprecated (6to4 Relay Anycast),[RFC7526],2001-06,2015-03,,,,,
-192.168.0.0/16,Private-Use,[RFC1918],1996-02,N/A,True,True,True,False,False
-192.175.48.0/24,Direct Delegation AS112 Service,[RFC7534],1996-01,N/A,True,True,True,True,False
-198.18.0.0/15,Benchmarking,[RFC2544],1999-03,N/A,True,True,True,False,False
-198.51.100.0/24,Documentation (TEST-NET-2),[RFC5737],2010-01,N/A,False,False,False,False,False
-203.0.113.0/24,Documentation (TEST-NET-3),[RFC5737],2010-01,N/A,False,False,False,False,False
-240.0.0.0/4,Reserved,"[RFC1112], Section 4",1989-08,N/A,False,False,False,False,True
-255.255.255.255/32,Limited Broadcast,"[RFC8190]
- [RFC919], Section 7",1984-10,N/A,False,True,False,False,True
diff --git a/utils/openbsd/pf-martians/iana-ipv4-special-registry.xml b/utils/openbsd/pf-martians/iana-ipv4-special-registry.xml
new file mode 100644
index 0000000..fe28a89
--- /dev/null
+++ b/utils/openbsd/pf-martians/iana-ipv4-special-registry.xml
@@ -0,0 +1,359 @@
+
+
+
+
+ IANA IPv4 Special-Purpose Address Registry
+ Internet Protocol version 4 (IPv4) Address Space
+ 2009-08-19
+ 2020-09-04
+
+
+
+
+ IANA IPv4 Special-Purpose Address Registry
+
+
+
+ IETF Review
+ The IETF has reserved the address block of 192.0.0.0/24 for use for
+special purposes relating to protocol assignments. This registry
+contains the current assignments made by the IETF from this address
+block.
+
+Address prefixes listed in the Special-Purpose Address Registry are
+not guaranteed routability in any particular local or global context.
+
+The IPv4 and IPv6 Special-Purpose Address Registries maintain the
+following information regarding each entry:
+
+ o Address Block - A block of IPv4 or IPv6 addresses that has been
+ registered for a special purpose.
+
+ o Name - A descriptive name for the special-purpose address block.
+
+ o RFC - The RFC through which the special-purpose address block was
+ requested.
+
+ o Allocation Date - The date upon which the special-purpose address
+ block was allocated.
+
+ o Termination Date - The date upon which the allocation is to be
+ terminated. This field is applicable for limited-use allocations
+ only.
+
+ o Source - A boolean value indicating whether an address from the
+ allocated special-purpose address block is valid when used as the
+ source address of an IP datagram that transits two devices.
+
+ o Destination - A boolean value indicating whether an address from
+ the allocated special-purpose address block is valid when used as
+ the destination address of an IP datagram that transits two
+ devices.
+
+ o Forwardable - A boolean value indicating whether a router may
+ forward an IP datagram whose destination address is drawn from the
+ allocated special-purpose address block between external
+ interfaces.
+
+ o Globally Reachable - A boolean value indicating whether an IP
+ datagram whose destination address is drawn from the allocated
+ special-purpose address block is forwardable beyond a specified
+ administrative domain.
+
+ o Reserved-by-Protocol - A boolean value indicating whether the
+ special-purpose address block is reserved by IP, itself. This
+ value is "TRUE" if the RFC that created the special-purpose
+ address block requires all compliant IP implementations to behave
+ in a special way when processing packets either to or from
+ addresses contained by the address block.
+
+ If the value of "Destination" is FALSE, the values of "Forwardable"
+ and "Globally Reachable" must also be false.
+
+
+
+ 0.0.0.0/8
+ "This host on this network"
+ , Section 3.2.1.3
+ 1981-09
+
+ False
+ False
+ False
+ True
+
+
+
+ 10.0.0.0/8
+ Private-Use
+
+ 1996-02
+
+ True
+ True
+ False
+ False
+
+
+
+ 100.64.0.0/10
+ Shared Address Space
+
+ 2012-04
+
+ True
+ True
+ False
+ False
+
+
+
+ 127.0.0.0/8
+ Loopback
+ , Section 3.2.1.3
+ 1981-09
+
+ False
+ False
+ False
+ True
+
+
+
+ 169.254.0.0/16
+ Link Local
+
+ 2005-05
+
+ True
+ False
+ False
+ True
+
+
+
+ 172.16.0.0/12
+ Private-Use
+
+ 1996-02
+
+ True
+ True
+ False
+ False
+
+
+
+ 192.0.0.0/24
+ IETF Protocol Assignments
+ , Section 2.1
+ 2010-01
+
+ False
+ False
+ False
+ False
+
+
+
+ 192.0.0.0/29
+ IPv4 Service Continuity Prefix
+
+ 2011-06
+
+ True
+ True
+ False
+ False
+
+
+
+ 192.0.0.8/32
+ IPv4 dummy address
+
+ 2015-03
+
+ False
+ False
+ False
+ False
+
+
+
+ 192.0.0.9/32
+ Port Control Protocol Anycast
+
+ 2015-10
+
+ True
+ True
+ True
+ False
+
+
+
+ 192.0.0.10/32
+ Traversal Using Relays around NAT Anycast
+
+ 2017-02
+
+ True
+ True
+ True
+ False
+
+
+
+ 192.0.0.170/32, 192.0.0.171/32
+ NAT64/DNS64 Discovery
+ , Section 2.2
+ 2013-02
+
+ False
+ False
+ False
+ True
+
+
+
+ 192.0.2.0/24
+ Documentation (TEST-NET-1)
+
+ 2010-01
+
+ False
+ False
+ False
+ False
+
+
+
+ 192.31.196.0/24
+ AS112-v4
+
+ 2014-12
+
+ True
+ True
+ True
+ False
+
+
+
+ 192.52.193.0/24
+ AMT
+
+ 2014-12
+
+ True
+ True
+ True
+ False
+
+
+
+ 192.88.99.0/24
+ Deprecated (6to4 Relay Anycast)
+
+ 2001-06
+ 2015-03
+
+
+
+
+
+
+
+
+ 192.168.0.0/16
+ Private-Use
+
+ 1996-02
+
+ True
+ True
+ False
+ False
+
+
+
+ 192.175.48.0/24
+ Direct Delegation AS112 Service
+
+ 1996-01
+
+ True
+ True
+ True
+ False
+
+
+
+ 198.18.0.0/15
+ Benchmarking
+
+ 1999-03
+
+ True
+ True
+ False
+ False
+
+
+
+ 198.51.100.0/24
+ Documentation (TEST-NET-2)
+
+ 2010-01
+
+ False
+ False
+ False
+ False
+
+
+
+ 203.0.113.0/24
+ Documentation (TEST-NET-3)
+
+ 2010-01
+
+ False
+ False
+ False
+ False
+
+
+
+ 240.0.0.0/4
+ Reserved
+ , Section 4
+ 1989-08
+
+ False
+ False
+ False
+ True
+
+
+
+ 255.255.255.255/32
+ Limited Broadcast
+
+ , Section 7
+ 1984-10
+
+ True
+ False
+ False
+ True
+
+
+
+
+ Several protocols have been granted exceptions to this rule.
+For examples, see and .
+ Not useable unless by virtue of a more specific reservation.
+
+
+
diff --git a/utils/openbsd/pf-martians/iana-ipv6-special-registry-1.csv b/utils/openbsd/pf-martians/iana-ipv6-special-registry-1.csv
deleted file mode 100644
index cab0a19..0000000
--- a/utils/openbsd/pf-martians/iana-ipv6-special-registry-1.csv
+++ /dev/null
@@ -1,23 +0,0 @@
-Address Block,Name,RFC,Allocation Date,Termination Date,Source,Destination,Forwardable,Globally Reachable,Reserved-by-Protocol
-::1/128,Loopback Address,[RFC4291],2006-02,N/A,False,False,False,False,True
-::/128,Unspecified Address,[RFC4291],2006-02,N/A,True,False,False,False,True
-::ffff:0:0/96,IPv4-mapped Address,[RFC4291],2006-02,N/A,False,False,False,False,True
-64:ff9b::/96,IPv4-IPv6 Translat.,[RFC6052],2010-10,N/A,True,True,True,True,False
-64:ff9b:1::/48,IPv4-IPv6 Translat.,[RFC8215],2017-06,N/A,True,True,True,False,False
-100::/64,Discard-Only Address Block,[RFC6666],2012-06,N/A,True,True,True,False,False
-2001::/23,IETF Protocol Assignments,[RFC2928],2000-09,N/A,False [1],False [1],False [1],False [1],False
-2001::/32,TEREDO,"[RFC4380]
- [RFC8190]",2006-01,N/A,True,True,True,N/A [2],False
-2001:1::1/128,Port Control Protocol Anycast,[RFC7723],2015-10,N/A,True,True,True,True,False
-2001:1::2/128,Traversal Using Relays around NAT Anycast,[RFC8155],2017-02,N/A,True,True,True,True,False
-2001:2::/48,Benchmarking,[RFC5180][RFC Errata 1752],2008-04,N/A,True,True,True,False,False
-2001:3::/32,AMT,[RFC7450],2014-12,N/A,True,True,True,True,False
-2001:4:112::/48,AS112-v6,[RFC7535],2014-12,N/A,True,True,True,True,False
-2001:10::/28,Deprecated (previously ORCHID),[RFC4843],2007-03,2014-03,,,,,
-2001:20::/28,ORCHIDv2,[RFC7343],2014-07,N/A,True,True,True,True,False
-2001:db8::/32,Documentation,[RFC3849],2004-07,N/A,False,False,False,False,False
-2002::/16 [3],6to4,[RFC3056],2001-02,N/A,True,True,True,N/A [3],False
-2620:4f:8000::/48,Direct Delegation AS112 Service,[RFC7534],2011-05,N/A,True,True,True,True,False
-fc00::/7,Unique-Local,"[RFC4193]
- [RFC8190]",2005-10,N/A,True,True,True,False [4],False
-fe80::/10,Link-Local Unicast,[RFC4291],2006-02,N/A,True,True,False,False,True
diff --git a/utils/openbsd/pf-martians/iana-ipv6-special-registry.xml b/utils/openbsd/pf-martians/iana-ipv6-special-registry.xml
new file mode 100644
index 0000000..cfbd8e5
--- /dev/null
+++ b/utils/openbsd/pf-martians/iana-ipv6-special-registry.xml
@@ -0,0 +1,323 @@
+
+
+
+
+ IANA IPv6 Special-Purpose Address Registry
+ Internet Protocol version 6 (IPv6) Global Unicast Allocations
+ 2006-01-10
+ 2019-09-13
+
+
+
+
+ IANA IPv6 Special-Purpose Address Registry
+
+
+
+ IETF Review
+ Address prefixes listed in the Special-Purpose Address Registry are
+not guaranteed routability in any particular local or global context.
+
+The IPv4 and IPv6 Special-Purpose Address Registries maintain the following
+information regarding each entry:
+
+ o Address Block - A block of IPv4 or IPv6 addresses that has been
+ registered for a special purpose.
+
+ o Name - A descriptive name for the special-purpose address block.
+
+ o RFC - The RFC through which the special-purpose address block was
+ requested.
+
+ o Allocation Date - The date upon which the special-purpose address
+ block was allocated.
+
+ o Termination Date - The date upon which the allocation is to be
+ terminated. This field is applicable for limited-use allocations
+ only.
+
+ o Source - A boolean value indicating whether an address from the
+ allocated special-purpose address block is valid when used as the
+ source address of an IP datagram that transits two devices.
+
+ o Destination - A boolean value indicating whether an address from
+ the allocated special-purpose address block is valid when used as
+ the destination address of an IP datagram that transits two
+ devices.
+
+ o Forwardable - A boolean value indicating whether a router may
+ forward an IP datagram whose destination address is drawn from the
+ allocated special-purpose address block between external
+ interfaces.
+
+ o Globally Reachable - A boolean value indicating whether an IP
+ datagram whose destination address is drawn from the allocated
+ special-purpose address block is forwardable beyond a specified
+ administrative domain.
+
+ o Reserved-by-Protocol - A boolean value indicating whether the
+ special-purpose address block is reserved by IP, itself. This
+ value is "TRUE" if the RFC that created the special-purpose
+ address block requires all compliant IP implementations to behave
+ in a special way when processing packets either to or from
+ addresses contained by the address block.
+
+If the value of "Destination" is FALSE, the values of "Forwardable" and
+"Globally Reachable" must also be false.
+
+
+
+
+ ::1/128
+ Loopback Address
+
+ 2006-02
+
+ False
+ False
+ False
+ True
+
+
+
+ ::/128
+ Unspecified Address
+
+ 2006-02
+
+ False
+ False
+ False
+ True
+
+
+
+ ::ffff:0:0/96
+ IPv4-mapped Address
+
+ 2006-02
+
+ False
+ False
+ False
+ True
+
+
+
+ 64:ff9b::/96
+ IPv4-IPv6 Translat.
+
+ 2010-10
+
+ True
+ True
+ True
+ False
+
+
+
+ 64:ff9b:1::/48
+ IPv4-IPv6 Translat.
+
+ 2017-06
+
+ True
+ True
+ False
+ False
+
+
+
+ 100::/64
+ Discard-Only Address Block
+
+ 2012-06
+
+ True
+ True
+ False
+ False
+
+
+
+ 2001::/23
+ IETF Protocol Assignments
+
+ 2000-09
+
+ False
+ False
+ False
+ False
+
+
+
+ 2001::/32
+ TEREDO
+
+
+ 2006-01
+
+ True
+ True
+ N/A
+ False
+
+
+
+ 2001:1::1/128
+ Port Control Protocol Anycast
+
+ 2015-10
+
+ True
+ True
+ True
+ False
+
+
+
+ 2001:1::2/128
+ Traversal Using Relays around NAT Anycast
+
+ 2017-02
+
+ True
+ True
+ True
+ False
+
+
+
+ 2001:2::/48
+ Benchmarking
+
+ 2008-04
+
+ True
+ True
+ False
+ False
+
+
+
+ 2001:3::/32
+ AMT
+
+ 2014-12
+
+ True
+ True
+ True
+ False
+
+
+
+ 2001:4:112::/48
+ AS112-v6
+
+ 2014-12
+
+ True
+ True
+ True
+ False
+
+
+
+ 2001:10::/28
+ Deprecated (previously ORCHID)
+
+ 2007-03
+ 2014-03
+
+
+
+
+
+
+
+
+ 2001:20::/28
+ ORCHIDv2
+
+ 2014-07
+
+ True
+ True
+ True
+ False
+
+
+
+ 2001:db8::/32
+ Documentation
+
+ 2004-07
+
+ False
+ False
+ False
+ False
+
+
+
+ 2002::/16
+ 6to4
+
+ 2001-02
+
+ True
+ True
+ N/A
+ False
+
+
+
+ 2620:4f:8000::/48
+ Direct Delegation AS112 Service
+
+ 2011-05
+
+ True
+ True
+ True
+ False
+
+
+
+ fc00::/7
+ Unique-Local
+
+
+ 2005-10
+
+ True
+ True
+ False
+ False
+
+
+
+ fe80::/10
+ Link-Local Unicast
+
+ 2006-02
+
+ True
+ False
+ False
+ True
+
+
+
+
+
+
+ Unless allowed by a more specific allocation.
+ See Section 5 of for details.
+ See for details.
+ See for more details on the routability of Unique-Local addresses. The Unique-Local prefix is
+drawn from the IPv6 Global Unicast Address range, but is specified as not globally routed.
+
+