🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Dual-stack socket link-local IPv6 address mystery

Started by
10 comments, last by hplus0603 5 years, 2 months ago

I have a multiplayer-enabled PC (Windows 7+, UDP) game project, which I've had IPv4 and IPv6 successfully running on for quite some time (via a config option to select one or the other).  I've finally just now got around to adding dual-stack socket support so that I can accommodate IPv4 and IPv6 players at the same time on the same server.  This was pretty straightforward, and overall things seem to be working as expected...all permutations of socket types (single or dual-stack) can connect to all permutations on the other end, transmit/receive data, etc.

However, I am seeing one strange thing that I have yet to figure out.  Part of the data I provide when advertising a server connection is the *local* IP address, so that clients on the same network as the server can connect directly (try local IP, then remote).  To generate this link-local IP address, I use the standard connect, getsockname, disc-connect method.  This has been working perfectly for eons for both IPv4 and IPv6, and with the dual-stack socket it gives me what I expect in all cases except one.  When I provide an IPv6 destination address to a dual-stack socket for determining the link-local address, getsockname is giving me back a mapped IPv4 address.  Further, that (mapped) address is not even an IPv4 link-local address, it doesn't make much sense (ffff:38.0.23.0, port 51013).  The port is completely sensible (it is one past the last one allocated).

I check all return values on all socket calls, everything is returning success.  As implied above, I do get the proper mapped IPv4 local address when I provide an IPv4 mapped destination address.  But for the life of me I can't figure why I'm getting this result for a straight IPv6 destination address.

It's worth noting that my internet IPv6 connection seems to be a bit flaky at present.  I am able to do IPv6 DNS lookups, but I am seeing that websites are reporting they can't get my IPv6 address (google IPv6 test site, for instance).  Now, I might reasonably expect that my connect call would fail in such a scenario, but that's not the case.  But I can also reason that the IPv6 address doesn't necessarily need to be reachable to determine what the bound local address/port will be...?  Especially since this all works just fine on a straight IPv6-only socket.

Has anyone seen this before?  Am happy to provide more details that might help.

Advertisement

This sounds like a quirk of interface address queries for the particular networking stack you're using. I have not seen this quirk, but I can't say I've gone looking hard.

What OS and what version are you using? (Also, what ISP ?)

By the way, another way of doing local address discovery is to broadcast on your local network segment. You will see your own broadcasts, so you can look up your own address there, too, but the other cool thing about that is that the other client can see you "directly" without having to also go through the matchmaker.

 

enum Bool { True, False, FileNotFound };
1 hour ago, hplus0603 said:

What OS and what version are you using? (Also, what ISP ?)

I'm running on Windows 7 Ultimate 64-bit, SP1.  My ISP is AT&T.  For what it's worth, my network setup is I have my own router chained behind their garbage Pace 5268AC gateway (not DMZ'd, and this thing has no real passthrough mode).  I am configured for native IPv6, am assigned the proper addresses, etc., and I was fully IPv6 accessible the last time I checked explicitly.  However, as mentioned in my original post, something got broken recently and my IPv6 internet discoverability is hosed.

That said, I have also tried the link-local discovery just using the loop-back addresses as the destination (127.0.0.1, and ::1, respectively), and I get the same behavior (everything comes back properly except the dual-stack IPv6, which in this case gives me ::ffff:0.0.0.0,53123).  So I am less inclined to think it's some ISP-related issue, and more likely a quirk of my local network stack, as you say.

1 hour ago, hplus0603 said:

By the way, another way of doing local address discovery is to broadcast on your local network segment. You will see your own broadcasts, so you can look up your own address there, too, but the other cool thing about that is that the other client can see you "directly" without having to also go through the matchmaker.

That's an interesting thought.  While I prefer to keep the client server(s) address discovery homogeneous across remote/local networks (everything goes through the remote lookup, which provides all the remote/local address info), the server broadcast to discover own local address is not a bad take. 

The other distasteful thing to do in my GetBoundLocalAddressForDestination() method on my socket class is when we see that we're dual-stack and the destination address is IPv6, simply create a new temporary IPv6 socket with the same port and use that for discovery.  I suppose it's conceivable that it might get bound to a different network interface (and thus this may not be the most reliable way), but it's certainly a quick-and-dirty fix.  Thoughts?

Well, my IPv6 internet discoverability seems to have fixed itself (test-ipv6.com reports full 10/10 now...yay AT&T).  So I think we're pretty firmly in the land of "OS network stack issue".

I did implement my aforementioned disgusting hack, which gets me around the issue.  But I also find it sort of hard to believe that no one else has encountered this.  Are folks generally not doing dual-stack sockets, and/or is my method of link-local address discovery not common?

My guess is that most people use an existing library, and those libraries either go "single stack with choice" or may even just do IPv4 only.

Of the people who do their own library, many are likely client/server and don't need NAT punch-through, or if they do NAT punch-through, they don't bother with local discovery. Many modern home routers support hairpin now, and dual players in the same hosted games with remote player matchmaking in the same IPv6-capable LAN isn't a super common setup.

Of the people who do their own library, and support 4/6/dual, and need NAT punch through, and do local discovery instead of hairpin, perhaps they haven't really tested this particular setup, or they haven't reported the results publicly? That actually wouldn't be super surprising to me.

One market this may be different in is China, where IPv6 adoption is very high (because Apple has more IPv4 addresses allocated to its employees than all of China gets) and the communications with the US developer scene isn't particularly wide. (Very few speakers at GDC San Francisco are from China, etc.) If you find out more about this in your research, that'd be interesting to hear about :-)

enum Bool { True, False, FileNotFound };

Yes, I suppose on reflection perhaps this is the code path/combination less traveled.  As you stated, routers not supporting hairpin was the impetus for having the local discovery.  That's what I get for trying to support older hardware...no good deed goes unpunished, as they say.

I've put together a repro which I'm planning to send off to Microsoft.  Should anyone have the time to drop it in and see the results on their system, that would be more data I could provide.  I'm particularly interested in the results on Windows 8+ systems.  I expect unless one could contort this into a "security issue", the chances of this getting fixed on Windows 7 are slim to none.

DualStackGetsocknameRepro.cpp

In other news, you do know that Windows 7 is reaching end of life, right? You have nine months to go.

I actually quite enjoy Windows 10, when used in desktop mode. There are benefits to moving with the times, at a measured pace where the crazy people first clear the mines by scouting ahead ?

enum Bool { True, False, FileNotFound };

Oh yes, I am well aware of Windows 7's support status/EOL.  I have nothing against Win10 (although I prefer Win7 overall).  But for the purposes of development (and at present a one machine dev/testing local environment), I find it advantageous to develop on my min supported spec.  Once Microsoft (and more consequentially, publishers) drop Windows 7 support then onwards and upwards to the latest I will go.  I sure as heck am not going to run an OS that isn't getting security updates any longer.  ;)

But I am generally of the mindset that I want to support as many users as possible (read; older hardware/OS) when it's within reason to do so.  I also derive some sort of sadistic satisfaction in finding workarounds to these weird OS-specfiic quirks...at least, when it's not a critical blocking issue.

I managed to get this tested on a Windows 10 machine, and it behaves correctly (returns "::1" as the link-local address).  Another Windows 7 machine exhibits the problem.  So with that small sample size, it appears it's something that's since been fixed after Windows 7.

I've also managed to test this on Windows Server 2012 R2, and it behaved correctly.  One could reasonably extrapolate that this applies to Windows 8.1 as well.

I've provided the repro info/code to Microsoft, we'll what happens (read; I wouldn't hold my breath).  Will update this thread with the resolution if and when such a thing occurs.

This topic is closed to new replies.

Advertisement