From 0137c5f9293a150247c2e7802bff90e07063c1a1 Mon Sep 17 00:00:00 2001 From: Lukas Werling Date: Fri, 6 Jan 2017 13:51:51 +0100 Subject: [PATCH] Fix host connections on link-local IPv6 addresses Link-local IPv6 addresses are valid on all interfaces and thus need an interface specifier / scope id, e.g. fe80::1%eth0. This commit adds scope ids for initial host connections only. While not optimal, this is probably enough in practise as the link-local addresses are likely only important when there is no internet connectivity. In this case, connecting clients directly is less of an advantage. --- src/network/C4NetIO.cpp | 10 ++++++++++ src/network/C4NetIO.h | 1 + src/network/C4Network2.cpp | 20 ++++++++++++++++++-- src/network/C4Network2Client.cpp | 4 ++++ src/network/C4Network2Client.h | 5 +++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/network/C4NetIO.cpp b/src/network/C4NetIO.cpp index 84b93aafa..a341b1d1c 100644 --- a/src/network/C4NetIO.cpp +++ b/src/network/C4NetIO.cpp @@ -244,6 +244,16 @@ bool C4NetIO::HostAddress::IsLoopback() const return false; } +bool C4NetIO::HostAddress::IsLocal() const +{ + if (gen.sa_family == AF_INET6) + return IN6_IS_ADDR_LINKLOCAL(&v6.sin6_addr) != 0; + // We don't really care about local 169.256.0.0/16 addresses here as users will either have a + // router doing DHCP (which will prevent usage of these addresses) or have a network that + // doesn't care about IP and IPv6 link-local addresses will work. + return false; +} + void C4NetIO::HostAddress::SetScopeId(int scopeId) { if (gen.sa_family != AF_INET6) return; diff --git a/src/network/C4NetIO.h b/src/network/C4NetIO.h index c58b2e558..7837dcc1d 100644 --- a/src/network/C4NetIO.h +++ b/src/network/C4NetIO.h @@ -111,6 +111,7 @@ public: bool IsNull() const; bool IsMulticast() const; bool IsLoopback() const; + bool IsLocal() const; // bool IsBroadcast() const; StdStrBuf ToString(int flags = 0) const; diff --git a/src/network/C4Network2.cpp b/src/network/C4Network2.cpp index 1ef12d313..0a1d0613f 100644 --- a/src/network/C4Network2.cpp +++ b/src/network/C4Network2.cpp @@ -310,9 +310,25 @@ C4Network2::InitResult C4Network2::InitClient(const class C4Network2Address *pAd for (int i = 0; i < iAddrCount; i++) if (!pAddrs[i].isIPNull()) { + auto addr = pAddrs[i].getAddr(); + std::vector addrs; + if (addr.IsLocal()) + { + // Local IPv6 addresses need a scope id. + for (auto& id : Clients.GetLocal()->getInterfaceIDs()) + { + addr.SetScopeId(id); + addrs.push_back(addr); + } + } + else + addrs.push_back(addr); // connection - if (!NetIO.Connect(pAddrs[i].getAddr(), pAddrs[i].getProtocol(), HostCore, szPassword)) - continue; + int cnt = 0; + for (auto& a : addrs) + if (NetIO.Connect(a, pAddrs[i].getProtocol(), HostCore, szPassword)) + cnt++; + if (cnt == 0) continue; // format for message if (strAddresses.getLength()) strAddresses.Append(", "); diff --git a/src/network/C4Network2Client.cpp b/src/network/C4Network2Client.cpp index c5dea2bcc..f239fd8b4 100644 --- a/src/network/C4Network2Client.cpp +++ b/src/network/C4Network2Client.cpp @@ -231,6 +231,8 @@ void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP) addr.SetPort(iPortUDP); AddAddr(C4Network2Address(addr, P_UDP), false); } + if (addr.GetScopeId()) + InterfaceIDs.insert(addr.GetScopeId()); } } } @@ -257,6 +259,8 @@ void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP) addr.SetPort(iPortUDP); AddAddr(C4Network2Address(addr, P_UDP), false); } + if (addr.GetScopeId()) + InterfaceIDs.insert(addr.GetScopeId()); } } freeifaddrs(addrs); diff --git a/src/network/C4Network2Client.h b/src/network/C4Network2Client.h index 0f47c9729..7ada241d2 100644 --- a/src/network/C4Network2Client.h +++ b/src/network/C4Network2Client.h @@ -57,6 +57,9 @@ protected: int32_t AddrAttempts[C4ClientMaxAddr]; int32_t iAddrCnt; + // interface ids + std::set InterfaceIDs; + // status C4Network2ClientStatus eStatus; @@ -88,6 +91,8 @@ public: int32_t getAddrCnt() const { return iAddrCnt; } const C4Network2Address &getAddr(int32_t i) const { return Addr[i]; } + const std::set &getInterfaceIDs() const { return InterfaceIDs; } + C4Network2ClientStatus getStatus() const { return eStatus; } bool hasJoinData() const { return getStatus() != NCS_Joining; } bool isChasing() const { return getStatus() == NCS_Chasing; }