user7490387
user7490387

Reputation: 31

Parse IPv6 addresses from /proc/net/tcp6 (python 2.7)

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.

Upvotes: 2

Views: 2995

Answers (2)

user7490387
user7490387

Reputation: 31

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))

Upvotes: 1

Harald Nordgren
Harald Nordgren

Reputation: 12409

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))

Upvotes: 0

Related Questions