#!/usr/bin/env python3 ############### # RFC7050 ipv6 prefix discovery # Prefixes can have lengths 32, 40, 48, 56, 64, or 96 per RFC6052 # IMPORTANT: THIS DOES NOT HANDLE THE SUFFIX ASPECTS THAT MAY COME INTO PLAY ON SOME NETWORKS ############### ########## # Dependencies ########## # pip3 install dnspython ########## # Sample T-Mobile record ########## # ipv4only.arpa # 192.0.0.170 # 192.0.0.171 # 2607:7700:0:26::c000:aa # 2607:7700:0:26::c000:ab ########## import ipaddress import dns.resolver ########## # Discovery of DNS records ########## #TODO: Swap this to a well-known address that is ISP independent # Sub domain with only an A record somewhere? Update to handle the tweaked approach resolver = dns.resolver.Resolver() a_recs = resolver.query("ipv4only.arpa", "A") aaaa_recs = resolver.query("ipv4only.arpa", "AAAA") ########## # Process DNS records for prefix discovery process ########## a_addresses = [] aaaa_addresses = [] for a in a_recs: print(a) a_addresses.append(ipaddress.IPv4Address(a.to_text())) for aaaa in aaaa_recs: print(aaaa) aaaa_addresses.append(ipaddress.IPv6Address(aaaa.to_text())) ########## # Figure out published prefixes ########## prefixes = [] for aaaa in aaaa_addresses: ipv6_bytes = aaaa.packed for a in a_addresses: ipv4_bytes = a.packed # Split the bytes so we can find the prefix minus the ipv4 address and following info partition = ipv6_bytes.partition(ipv4_bytes) if partition[0] != ipv6_bytes: # Padded to a full 16 bytes needed by the Python ipaddress module mask_number_bytes = 16 - len(partition[0]) # Figure out the actual integer mask mask = 128 - (mask_number_bytes * 8) # Pad the prefix with 0's prefix_bytes = partition[0] + bytearray(mask_number_bytes) # Get the /128 address address = ipaddress.IPv6Address(prefix_bytes) # Build the compressed address + mask for output prefix = '/'.join([str(address), str(mask)]) # De-duplicate the prefixes # Some ISPs publish multiple records that result in identical prefixes if prefix not in prefixes: prefixes.append(prefix) print(prefixes)