0

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
>>>
amulllb
  • 3,036
  • 7
  • 50
  • 87
  • What do you mean by "closest match"? Those are all either illegal networks, or different names for the same network, depending on how strict you want to define things, so none of them are any closer than any other. – abarnert Nov 25 '14 at 01:00
  • well by closest match i meant longest match. sorry for confusion – amulllb Nov 25 '14 at 01:12
  • 1
    OK, so what does "longest match" mean? Again, all three of those are defining either the exact same CIDR network 172.27.145.128/25, or nothing at all, so I don't know what different results you're expecting out of them. – abarnert Nov 25 '14 at 01:18
  • thing is... all the three ip subnets are part of same network 172.27.145.128/25. Thats why netaddr returns True, which makes sense. But I guess I was asking if there is a way to compare just the IP addresses bit by bit to find the closest match. its not entirely a "is IP in subnet" question; but more of do the IP addresses match. From the looks of it, I guess I need to compare the network and host bits. anyways, thanks for ur help. – amulllb Nov 25 '14 at 05:12

1 Answers1

1

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.

abarnert
  • 354,177
  • 51
  • 601
  • 671