2
votes

I'm trying to get the current public ipv6 address of an interface in python. By searching, I discovered the netifaces package, which can give me a list of all ipv6 addresses of the device.

This will include

  • a link-local address (which I could filter out with a simple hack, which isn't pretty but well...)
  • several global ipv6 addresses, one of which is the current one

Now what do I mean by current? Through privacy extensions (RFC 4941), a new address is assigned to the interface in regular intervals. Old global ipv6 addresses are still valid, but flagged as deprecated. I want this most recent one.

Here's what I tried (obfuscated parts of the addresses for security reasons):

>>> import netifaces 
>>> addrs = netifaces.ifaddresses('en0')
>>> for i in addrs[netifaces.AF_INET6]:
...     print(i)
...
{'addr': 'fe80::caa:452a:b8cb:862e%en0', 'netmask': 'ffff:ffff:ffff:ffff::/64', 'flags': 1024}
{'addr': '2a02:____:____:9f1:cd8:e__e:e57b:eaa8', 'netmask': 'ffff:ffff:ffff:ffff::/64', 'flags': 1088}
{'addr': '2a02:____:____:9f1:3489:b__7:fb34:101', 'netmask': 'ffff:ffff:ffff:ffff::/64', 'flags': 192}

Obviously, the first is the link-local address. The second and third are the global ones, and they have a flag, of which I have no idea what it means.

Is there any way (perhaps through the flag, or another method in python) to figure out the current (temporary, non-deprecated) global address?

In Linux, I know I could parse the output of ip -o -6 addr show dev interface and parse that output, but that won't work e.g. on a Mac, and parsing output of other tools is something I dislike if there's a python package for it.

EDIT: The flags seem not to be present on every OS. The output above was on a Mac. Here's the output on a Linux machine in the same network (much longer uptime):

{'addr': '2a02:____:____:9f1:940c:f__f:f9c0:c274', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:395a:6__d:650a:3a11', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:d08a:6__d:d967:d28a', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:9d20:b__7:3edc:96a4', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:b584:d__b:16bf:5dd2', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:61ba:d__b:78d8:197f', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:c1df:7__7:236d:4219', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': '2a02:____:____:9f1:d021:4__3:1a44:4947', 'netmask': 'ffff:ffff:ffff:ffff::/64'}
{'addr': 'fe80::17ed:fbf5:a707:2a42%enp4s0', 'netmask': 'ffff:ffff:ffff:ffff::/64'}

EDIT 2:

Since there was some confusion what I mean by "current" addresses, here's what I mean. On the Linux machine of the previous example:

% ip -o -6 addr show dev enp4s0
2: enp4s0    inet6 2a02:____:____:9f1:940c:f__f:f9c0:c274/64 scope global temporary dynamic \       valid_lft 530760sec preferred_lft 12247sec
2: enp4s0    inet6 2a02:____:____:9f1:395a:6__d:650a:3a11/64 scope global temporary deprecated dynamic \       valid_lft 444473sec preferred_lft 0sec
2: enp4s0    inet6 2a02:____:____:9f1:d08a:6__d:d967:d28a/64 scope global temporary deprecated dynamic \       valid_lft 358187sec preferred_lft 0sec
2: enp4s0    inet6 2a02:____:____:9f1:9d20:b__7:3edc:96a4/64 scope global temporary deprecated dynamic \       valid_lft 271901sec preferred_lft 0sec
2: enp4s0    inet6 2a02:____:____:9f1:b584:d__b:16bf:5dd2/64 scope global temporary deprecated dynamic \       valid_lft 185616sec preferred_lft 0sec
2: enp4s0    inet6 2a02:____:____:9f1:61ba:d__b:78d8:197f/64 scope global temporary deprecated dynamic \       valid_lft 99330sec preferred_lft 0sec
2: enp4s0    inet6 2a02:____:____:9f1:c1df:7__7:236d:4219/64 scope global temporary deprecated dynamic \       valid_lft 13044sec preferred_lft 0sec
2: enp4s0    inet6 2a02:____:____:9f1:d021:4__3:1a44:4947/64 scope global dynamic mngtmpaddr noprefixroute \       valid_lft 2591735sec preferred_lft 604535sec
2: enp4s0    inet6 fe80::17ed:fbf5:a707:2a42/64 scope link noprefixroute \       valid_lft forever preferred_lft forever

Obviously, all the "temporary" addresses are valid addresses. But only one is not flagged deprecated. This address will be used for outbound connections. And this is the one I want.

1
What OS is this? I haven't got any flags on Linux.Michael Hampton
Good point, that's on a Mac. Apparently there's no flags on linux, which does not make things easier... I'm adding this to the question.Demosthenes
If you're going to parse something, I'd parse the output of ip route get instead, e.g. ip -6 route get to 2001:4860:4860::8888 This will show the actual source address that will be used for a connection to the given remote address.Michael Hampton
This will work on Linux, true, but on a Mac, for instance, there's no ip command (by default, you can probably install it). Ideally I'd like a portable solution , and without parsing. But helpful enough I'll give the comment an upvote.Demosthenes
"several global ipv6 addresses, one of which is the current one" Actually, IPv6 can have multiple current global addresses on a single interface. With IPv4, you had to jump through some hoops to put multiple addresses on a single interface, but that is built right into IPv6. You will have a link-local address, but you can have multiple of each global and ULA addresses on the same interface.Ron Maupin

1 Answers

1
votes

If you want a public IPv6 address then try this.

socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET6)[1][4][0]