2
votes

I'm trying to read IPv6 addresses from /proc/net/tcp6 in Python 2.7. Here's a sample of the IPv6 representation that is used in /proc/net/tcp6:

00000000000000000000000000000000
00000000000000000000000001000000
0000000000000000FFFF00001F00C80A

What I'm having trouble with is converting these to a "proper" IPv6 representation, as described on Wikipedia:

An IPv6 address is represented as eight groups of four hexadecimal digits, each group representing 16 bits (two octets). The groups are separated by colons (:). An example of an IPv6 address is:

2001:0db8:85a3:0000:0000:8a2e:0370:7334

[...] the example address can be further simplified:

2001:db8:85a3::8a2e:370:7334

It seems this can be done most easily with the help of socket.inet_ntop and struct.pack.

However I don't know which steps exactly to take to produce the specific format that these two functions require.

Any other methods using something else from Python 2.7 stdlib are just as welcome of course.


Just for comparison, here's how I'm doing it right now for IPv4 addresses that are read from /proc/net/tcp:

>>> addr = '0101007F'
>>> addr = int(addr, 16)
>>> addr = struct.pack('<I', addr)
>>> addr = socket.inet_ntoa(addr)
>>> print addr
127.0.1.1

Unfortunately inet_ntoa doesn't support IPv6 addresses and you have to use inet_ntop(AF_INET6, packed_ipv6) instead.

However I don't know how to convert those strings above to a in6_addr struct using the struct.pack function.

2
There is a standard, RFC 5952, A Recommendation for IPv6 Address Text Representation. It does not exactly match what Wikipedia tells you. The two zero words in the middle should be replaced with a double colon (::).Ron Maupin
Thanks for the note, that's actually what I had in mind, which is specified further in that section: en.wikipedia.org/wiki/IPv6_address#Representationuser7490387
Also, note the requirement of lower-case characters. What you are getting right now has upper-case characters, so you will need to convert them to lower-case.Ron Maupin
I'll probably need to convert that string to decimal anyway (int(addr, 16)), so such conversion to lowercase will be premature.user7490387
Is this the strange word order? serverfault.com/questions/592574/…Josh Lee

2 Answers

1
votes

Here's one way that works, and handles native endianness automatically:

def ipv6(addr):
    addr = addr.decode('hex')
    addr = struct.unpack('>IIII', addr)
    addr = struct.pack('@IIII', *addr)
    addr = socket.inet_ntop(socket.AF_INET6, addr)
    return addr

Sample:

>>> ipv6('00000000000000000000000001000000')
'::1'
>>> ipv6('B80D01200000000067452301EFCDAB89')
'2001:db8::123:4567:89ab:cdef'

Steps:

  1. the address is converted to byte string (decode('hex')),
  2. split into 4 slices, maintaining the byte order as it is (>IIII),
  3. repacked, with each slice being converted to native byte order (@IIII),
  4. passed to socket.inet_ntop, which finally produces the proper IPv6 representation

On systems that use little endian, the byte string will be converted from DCBA-HGFE-LKJI-PONM to ABCD-EFGH-IJKL-MNOP. See struct module's byte order notation.


An alternative to the unpack & pack part, but twice as slow on my system, and it doesn't automatically handle native endianness (here little endian is assumed):

addr = ''.join(addr[i:i+4][::-1] for i in range(0, 16, 4))
0
votes

Something like this?

from ipaddress import ip_address

components = [line[i:i+4] for i in range(0, len(line), 4)]
ipv6_string = ":".join(components)
print(ip_address(ipv6_string))