forked from hush/asmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
4.5 KiB
109 lines
4.5 KiB
import sys
|
|
import re
|
|
import ipaddress
|
|
|
|
IPV4_PREFIX = bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff])
|
|
|
|
def AddEntry(netmask, asn, fnam, linenum, entries):
|
|
loc = "%s:%i" % (fnam, linenum)
|
|
network = ipaddress.ip_network(netmask, True)
|
|
if asn is None:
|
|
print("[WARNING] %s: no ASN for %s" % (loc, netmask), file=sys.stderr)
|
|
return
|
|
if not network:
|
|
print("[WARNING] %s: cannot parse netmask %s for AS%i" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if network.is_multicast:
|
|
print("[WARNING] %s: multicast address %s for AS%i" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if network.is_private:
|
|
print("[WARNING] %s: private address %s for AS%i" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if network.is_unspecified:
|
|
print("[WARNING] %s: address from unspecified range %s for AS%i" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if network.is_reserved:
|
|
print("[WARNING] %s: reserved address %s for AS%i" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if network.is_loopback:
|
|
print("[WARNING] %s: loopback address %s for AS%i" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if asn == 0 or asn == 65535 or (asn >= 65552 and asn <= 131072) or asn == 4294967295:
|
|
print("[WARNING] %s: prefix %s has reserved AS%i (RFC1930)" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if asn == 23456:
|
|
print("[WARNING] %s: prefix %s has transition AS%i (RFC6793)" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if (asn >= 64496 and asn <= 64511) or (asn >= 65536 and asn <= 65551):
|
|
print("[WARNING] %s: prefix %s has documentation AS%i (RFC4893,RFC5398)" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if (asn >= 64512 and asn <= 65534) or (asn >= 4200000000 and asn <= 4294967294):
|
|
print("[WARNING] %s: prefix %s has private AS%i (RFC5398,RFC6996)" % (loc, netmask, asn), file=sys.stderr)
|
|
return
|
|
if isinstance(network, ipaddress.IPv4Network):
|
|
entries.append((IPV4_PREFIX + network.network_address.packed, "%s AS%i # %s:%i" % (network.compressed, asn, fnam, linenum)))
|
|
elif isinstance(network, ipaddress.IPv6Network):
|
|
entries.append((network.network_address.packed, "%s AS%i # %s:%i" % (network.compressed, asn, fnam, linenum)))
|
|
else:
|
|
raise AssertionError("Unknown network type for %s" % netmask)
|
|
|
|
def ParseDump(fnam, entries):
|
|
RE_INITIAL = re.compile(r"^BIRD .* ready.$")
|
|
RE_TABLE = re.compile(r"^Table master(4|6):$")
|
|
RE_HEADER = re.compile(r"^(([0-9.]+|[0-9a-f:]+)/\d+) +unicast +\[.*\] +\* +\(\d+\)( +\[(AS(\d+))?[ie?]?\])?$")
|
|
RE_PATH = re.compile(r"^[\t]BGP\.as_path:(.*)$")
|
|
RE_PATH_DECOMPOSE = re.compile(r"^[0-9 ]*?(\d+)( +\{[0-9 ]*?(\d+)\})?$")
|
|
RE_INNER = re.compile(r"^[\t]")
|
|
RE_INNER_ADDR = re.compile(r"^ +unicast")
|
|
netmask = None
|
|
asn = None
|
|
aslevel = 0
|
|
maskline = None
|
|
with open(fnam) as f:
|
|
linenum = 0
|
|
for line in f:
|
|
linenum += 1
|
|
line = line.rstrip("\n\r")
|
|
if RE_INITIAL.match(line):
|
|
continue
|
|
if RE_TABLE.match(line):
|
|
continue
|
|
match = RE_HEADER.match(line)
|
|
if match:
|
|
if netmask:
|
|
AddEntry(netmask, asn, fnam, maskline, entries)
|
|
netmask = match[1]
|
|
maskline = linenum
|
|
if not match[3] or not match[4]:
|
|
asn = None
|
|
aslevel = 0
|
|
else:
|
|
asn = int(match[5])
|
|
aslevel = 1
|
|
continue
|
|
match = RE_PATH.match(line)
|
|
if match:
|
|
if aslevel < 2:
|
|
decomp = RE_PATH_DECOMPOSE.match(match[1])
|
|
if not decomp:
|
|
print("[WARNING] %s:%i: cannot parse as_path %s" % (fnam, linenum, match[1]))
|
|
asn = int(decomp[1])
|
|
aslevel = 2
|
|
match = RE_INNER.match(line)
|
|
if match:
|
|
continue
|
|
match = RE_INNER_ADDR.match(line)
|
|
if match:
|
|
continue
|
|
print("[WARNING] %s:%i: cannot parse %s" % (fnam, linenum, line), file=sys.stderr)
|
|
if netmask:
|
|
AddEntry(netmask, asn, fnam, maskline, entries)
|
|
|
|
entries = []
|
|
for fnam in sys.argv[1:]:
|
|
print("[INFO] Parsing %s" % fnam, file=sys.stderr)
|
|
ParseDump(fnam, entries)
|
|
print("[INFO] Parsed %i prefixes" % len(entries), file=sys.stderr)
|
|
entries.sort()
|
|
for _, s in entries:
|
|
print(s)
|
|
|