python netaddr find closest Ip address match

1.6k views Asked by At

How can I find the closest match using python netaddr for same prefixlength?

>>> l = ['172.27.145.130/25', '172.27.145.129/25', '172.27.145.131/25']
>>> myip = '172.27.145.129'
>>> netaddr.IPAddress(myip) in netaddr.IPNetwork(l[0])
True
>>> netaddr.IPAddress(myip) in netaddr.IPNetwork(l[1])
True
>>> netaddr.IPAddress(myip) in netaddr.IPNetwork(l[2])
True
>>>
1

There are 1 answers

0
abarnert On BEST ANSWER

You seem to be missing a key point here: You say that "all the three ip subnets are part of the same network 172.27.145.128/25", but that's not true; all of them are that same network, just different nonstandard names for it. That's the way IP networking works: for an N-bit network, the last N bits of the base address don't matter. So there is no way to contrast them with each other and pick out which one is a "longest" or "closest" or any other kind of match, because they will all be exactly the same match.

It's possible that you mean these to be interfaces, not networks. An interface has an address within a network—for example, address 172.27.145.130 in network 172.27.145.128/25. And you can specify that with the shorthand 127.27.145.130/25. Yes, the shorthand form for interfaces looks the same as the shorthand form for networks, but they're not the same thing.

If you still don't get the difference between addresses, networks, and interfaces, the 3.3+ Python docs have a great HOWTO.

While netaddr doesn't have any support for interfaces, the stdlib's ipaddress and the third-party ipaddress backport for Python 2.6-2.7, do. For example:

>>> l = ['172.27.145.130/25', '172.27.145.129/25', '172.27.145.131/25']
>>> interfaces = [ipaddress.ip_interface(x) for x in l]
>>> interfaces[0]
IPv4Interface('172.27.145.130/25')
>>> interfaces[0].ip, interfaces[0].network
(IPv4Address('172.27.145.130'), IPv4Network('172.27.145.128/25'))

So, maybe what you're asking is which interface shares the most bits in common with a given address? (I'm still not sure if this is what you mean by "closest match" or "longest match", but it seems like a reasonable guess.)

That's still an ambiguous question. You could either be asking which interface's address shares more bits period, or which one shares more bits within the subnet. But since they're all in the same subnet, that doesn't matter.

And that means that we can even use the netaddr network objects as ersatz interface objects (although really, you'd be better off using ipaddress or another library that actually supports interface objects).

So:

>>> l = ['172.27.145.130/25', '172.27.145.129/25', '172.27.145.131/25']
>>> interfaces = [netaddr.IPNetwork(interface) for interface in l]
>>> addresses = [interface.ip for interface in interfaces]
>>> bits = [address.bits() for address in addresses]
>>> bits
['10101100.00011011.10010001.10000010',
 '10101100.00011011.10010001.10000001',
 '10101100.00011011.10010001.10000011']
>>> myip = '172.27.145.129'
>>> myaddress = netaddr.IPAddress(myip)
>>> mybits = myaddress.bits()
'10101100.00011011.10010001.10000001'

(Obviously you can merge together most of those steps so the whole thing is just two or three lines.)

And now we're just comparing strings.

But netaddr.IPAddress also has an & operator, so we can make it even simpler:

>>> common_bits = [(address & myaddress).bits() for address in addresses]
>>> common_bits
['10101100.00011011.10010001.10000000',
 '10101100.00011011.10010001.10000001',
 '10101100.00011011.10010001.10000001']
>>> common_bit_counts = [bits.count('1') for bits in common_bits]
>>> common_bit_counts
[12, 13, 13]

There are other ways to tackle this. For example, the value of each IPAddress is a 32-bit int, so you can just and those together and count the bits numerically instead of as a string. But hopefully this shows things explicitly.