diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index a835e01c3..b1c4702d9 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -815,6 +815,9 @@ namespace UserConfigParams PARAM_PREFIX BoolUserConfigParam m_race_chat PARAM_DEFAULT(BoolUserConfigParam(true, "race-chat", &m_network_group, "Enable chatting during races.")); + PARAM_PREFIX BoolUserConfigParam m_ipv6_lan + PARAM_DEFAULT(BoolUserConfigParam(true, "ipv6-lan", + &m_network_group, "Enable IPv6 LAN server discovery.")); PARAM_PREFIX IntUserConfigParam m_max_players PARAM_DEFAULT(IntUserConfigParam(8, "max-players", &m_network_group, "Maximum number of players on the server " diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 6fb9c5011..8b856c625 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -29,6 +29,7 @@ #include "network/protocol_manager.hpp" #include "network/servers_manager.hpp" #include "network/server.hpp" +#include "network/socket_address.hpp" #include "network/stk_ipv6.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" @@ -155,7 +156,7 @@ void ConnectToServer::getClientServerInfo() m_server->setPrivatePort(port); if (server_ipv6_socket) { - m_server->setIPV6Address("::1"); + m_server->setIPV6Address(SocketAddress("::1", port)); m_server->setIPV6Connection(true); } if (server_id != 0) @@ -409,38 +410,47 @@ bool ConnectToServer::tryConnect(int timeout, int retry, bool another_port, if (ipv6) { - struct addrinfo hints; - struct addrinfo* res = NULL; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - std::string addr_string = m_server->getIPV6Address(); - std::string port = - StringUtils::toString(m_server_address.getPort()); // Convert to a NAT64 address from IPv4 if (!m_server->useIPV6Connection() && NetworkConfig::get()->getIPType() == NetworkConfig::IP_V6_NAT64) { + struct addrinfo hints; + struct addrinfo* res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; // From IPv4 - addr_string = m_server_address.toString(false/*show_port*/); + std::string addr_string = + m_server_address.toString(false/*show_port*/); addr_string = NetworkConfig::get()->getNAT64Prefix() + addr_string; - } - if (getaddrinfo_compat(addr_string.c_str(), port.c_str(), - &hints, &res) != 0 || res == NULL) - return false; - for (const struct addrinfo* addr = res; addr != NULL; - addr = addr->ai_next) - { - if (addr->ai_family == AF_INET6) + std::string port = + StringUtils::toString(m_server_address.getPort()); + if (getaddrinfo_compat(addr_string.c_str(), port.c_str(), + &hints, &res) != 0 || res == NULL) + return false; + for (const struct addrinfo* addr = res; addr != NULL; + addr = addr->ai_next) { - struct sockaddr_in6* ipv6_sock = - (struct sockaddr_in6*)addr->ai_addr; - ENetAddress en_addr = m_server_address.toEnetAddress(); - addMappedAddress(&en_addr, ipv6_sock); - break; + if (addr->ai_family == AF_INET6) + { + struct sockaddr_in6* ipv6_sock = + (struct sockaddr_in6*)addr->ai_addr; + ENetAddress en_addr = m_server_address.toEnetAddress(); + addMappedAddress(&en_addr, ipv6_sock); + break; + } } + freeaddrinfo(res); + } + else + { + SocketAddress* sa = m_server->getIPV6Address(); + if (!sa) + return false; + ENetAddress en_addr = m_server_address.toEnetAddress(); + struct sockaddr_in6* in6 = (struct sockaddr_in6*)sa->getSockaddr(); + addMappedAddress(&en_addr, in6); } - freeaddrinfo(res); } while (--m_retry_count >= 0 && !ProtocolManager::lock()->isExiting()) diff --git a/src/network/server.cpp b/src/network/server.cpp index 4addaf262..9b2093056 100644 --- a/src/network/server.cpp +++ b/src/network/server.cpp @@ -22,6 +22,7 @@ #include "online/online_profile.hpp" #include "online/profile_manager.hpp" #include "network/network_config.hpp" +#include "network/socket_address.hpp" #include "tracks/track_manager.hpp" #include "utils/constants.hpp" #include "utils/string_utils.hpp" @@ -58,13 +59,16 @@ Server::Server(const XMLNode& server_info) : m_supports_encrytion(true) xml.get("max_players", &m_max_players); xml.get("current_players", &m_current_players); xml.get("current_track", &m_current_track); - xml.get("ipv6", &m_ipv6_address); uint32_t ip; xml.get("ip", &ip); m_address.setIP(ip); uint16_t port; xml.get("port", &port); m_address.setPort(port); + std::string ipv6_address; + xml.get("ipv6", &ipv6_address); + if (!ipv6_address.empty()) + m_ipv6_address.reset(new SocketAddress(ipv6_address, port)); xml.get("private_port", &m_private_port); xml.get("password", &m_password_protected); xml.get("game_started", &m_game_started); @@ -213,3 +217,9 @@ bool Server::searchByName(const std::string& lower_case_word) } return server_name_found; } // searchByName + +// ---------------------------------------------------------------------------- +void Server::setIPV6Address(const SocketAddress& addr) +{ + m_ipv6_address.reset(new SocketAddress(addr)); +} // setIPV6Address diff --git a/src/network/server.hpp b/src/network/server.hpp index 613115573..9661aad48 100644 --- a/src/network/server.hpp +++ b/src/network/server.hpp @@ -36,6 +36,7 @@ class Track; class XMLNode; +class SocketAddress; /** * \ingroup online @@ -55,7 +56,11 @@ protected: std::string m_lower_case_player_names; - std::string m_ipv6_address; + /** We need to use full socket address structure instead of string to hold + * it, because for local link address the scope id matters for + * multicasting. + */ + std::unique_ptr m_ipv6_address; uint32_t m_server_id; uint32_t m_server_owner; @@ -173,7 +178,7 @@ public: // ------------------------------------------------------------------------ void setIPV6Connection(bool val) { - if (m_ipv6_address.empty()) + if (!m_ipv6_address) m_ipv6_connection = false; else m_ipv6_connection = val; @@ -181,8 +186,13 @@ public: // ------------------------------------------------------------------------ bool useIPV6Connection() const { return m_ipv6_connection; } // ------------------------------------------------------------------------ - void setIPV6Address(const std::string& addr) { m_ipv6_address = addr; } + void setIPV6Address(const SocketAddress& addr); // ------------------------------------------------------------------------ - const std::string& getIPV6Address() const { return m_ipv6_address; } + SocketAddress* getIPV6Address() const + { + if (!m_ipv6_address) + return NULL; + return m_ipv6_address.get(); + } }; // Server #endif // HEADER_SERVER_HPP diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index fff40a867..3a525c832 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -25,7 +25,9 @@ #include "network/network_config.hpp" #include "network/network_string.hpp" #include "network/server.hpp" +#include "network/socket_address.hpp" #include "network/stk_host.hpp" +#include "network/stk_ipv6.hpp" #include "online/xml_request.hpp" #include "online/request_manager.hpp" #include "utils/translation.hpp" @@ -33,6 +35,7 @@ #include #include +#include #include #include @@ -42,6 +45,7 @@ # include #else # include +# include #endif const int64_t SERVER_REFRESH_INTERVAL = 5000; @@ -158,8 +162,9 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const ENetAddress addr; addr.host = STKHost::HOST_ANY; addr.port = STKHost::PORT_ANY; + setIPv6Socket(UserConfigParams::m_ipv6_lan ? 1 : 0); Network *broadcast = new Network(1, 1, 0, 0, &addr); - const std::vector &all_bcast = + const std::vector &all_bcast = ServersManager::get()->getBroadcastAddresses(); for (auto &bcast_addr : all_bcast) { @@ -187,7 +192,7 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const std::map > servers_now; while (StkTime::getMonoTimeMs() - start_time < DURATION) { - TransportAddress sender; + SocketAddress sender; int len = broadcast->receiveRawPacket(buffer, LEN, &sender, 1); if (len > 0) { @@ -219,13 +224,20 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const { (void)e; } - servers_now.insert(std::make_pair(name, - std::make_shared(cur_server_id++, name, - max_players, players, difficulty, mode, sender, - password == 1, game_started == 1, current_track))); + auto server = std::make_shared(cur_server_id++, + name, max_players, players, difficulty, mode, + TransportAddress(sender.getIP(), sender.getPort()), + password == 1, game_started == 1, current_track); + if (sender.isIPv6()) + { + server->setIPV6Address(sender); + server->setIPV6Connection(true); + } + servers_now.insert(std::make_pair(name, server)); //all_servers.[name] = servers_now.back(); } // if received_data } // while still waiting + setIPv6Socket(0); m_success = true; ServersManager::get()->setLanServers(servers_now); delete broadcast; @@ -370,14 +382,14 @@ void ServersManager::setDefaultBroadcastAddresses() * to be created. * \param len Number of bits to be or'ed. */ -void ServersManager::addAllBroadcastAddresses(const TransportAddress &a, int len) +void ServersManager::addAllBroadcastAddresses(const SocketAddress &a, int len) { // Try different broadcast addresses - by masking on // byte boundaries while (len > 0) { unsigned int mask = (1 << len) - 1; - TransportAddress bcast(a.getIP() | mask, + SocketAddress bcast(a.getIP() | mask, stk_config->m_server_discovery_port); Log::info("Broadcast", "address %s length %d mask %x --> %s", a.toString().c_str(), @@ -403,59 +415,32 @@ void ServersManager::addAllBroadcastAddresses(const TransportAddress &a, int len void ServersManager::updateBroadcastAddresses() { m_broadcast_address.clear(); - -#ifdef WIN32 - IP_ADAPTER_ADDRESSES *addresses; - int count = 100, return_code; - - int iteration = 0; - do - { - addresses = new IP_ADAPTER_ADDRESSES[count]; - ULONG buf_len = sizeof(IP_ADAPTER_ADDRESSES)*count; - long flags = 0; - return_code = GetAdaptersAddresses(AF_INET, flags, NULL, addresses, - &buf_len); - iteration++; - } while (return_code == ERROR_BUFFER_OVERFLOW && iteration<10); - - if (return_code == ERROR_BUFFER_OVERFLOW) - { - Log::warn("ServerManager", "Can not get broadcast addresses."); - setDefaultBroadcastAddresses(); - return; - } - - for (IP_ADAPTER_ADDRESSES *p = addresses; p; p = p->Next) - { - // Check all operational IP4 adapters - if (p->OperStatus == IfOperStatusUp && - p->FirstUnicastAddress->Address.lpSockaddr->sa_family == AF_INET) - { - const sockaddr_in *sa = (sockaddr_in*)p->FirstUnicastAddress->Address.lpSockaddr; - // Use sa->sin_addr.S_un.S_addr and htonl? - TransportAddress ta(sa->sin_addr.S_un.S_un_b.s_b1, - sa->sin_addr.S_un.S_un_b.s_b2, - sa->sin_addr.S_un.S_un_b.s_b3, - sa->sin_addr.S_un.S_un_b.s_b4); - int len = 32 - p->FirstUnicastAddress->OnLinkPrefixLength; - addAllBroadcastAddresses(ta, len); - } - } -#else + std::vector result; +#ifndef WIN32 struct ifaddrs *addresses, *p; if (getifaddrs(&addresses) == -1) { - Log::warn("ServerManager", "Error in getifaddrs"); + Log::warn("SocketAddress", "Error in getifaddrs"); return; } + std::set used_scope_id; for (p = addresses; p; p = p->ifa_next) { - if (p->ifa_addr != NULL && p->ifa_addr->sa_family == AF_INET) + SocketAddress socket_address; + if (p->ifa_addr == NULL) + continue; + if (p->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in *) p->ifa_addr; - TransportAddress ta(htonl(sa->sin_addr.s_addr), 0); + uint32_t addr = htonl(sa->sin_addr.s_addr); + + // Skip 169.254.*.* local link address + if (((addr >> 24) & 0xff) == 169 && + ((addr >> 16) & 0xff) == 254) + continue; + + SocketAddress saddr(addr, 0); uint32_t u = ((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr; // Convert mask to #bits: SWAT algorithm u = u - ((u >> 1) & 0x55555555); @@ -464,18 +449,108 @@ void ServersManager::updateBroadcastAddresses() Log::debug("ServerManager", "Interface: %s\tAddress: %s\tmask: %x\n", p->ifa_name, - ta.toString().c_str(), u); - addAllBroadcastAddresses(ta, u); + saddr.toString().c_str(), u); + addAllBroadcastAddresses(saddr, u); + } + else if (p->ifa_addr->sa_family == AF_INET6) + { + uint32_t idx = if_nametoindex(p->ifa_name); + if (used_scope_id.find(idx) != used_scope_id.end()) + continue; + used_scope_id.insert(idx); + SocketAddress socket_address("ff02::1", + stk_config->m_server_discovery_port); + sockaddr_in6* in6 = (sockaddr_in6*)socket_address.getSockaddr(); + in6->sin6_scope_id = idx; + m_broadcast_address.push_back(socket_address); } } freeifaddrs(addresses); +#else + // From docs from microsoft it recommends 15k size + const int WORKING_BUFFER_SIZE = 15000; + PIP_ADAPTER_ADDRESSES paddr = NULL; + unsigned long len = WORKING_BUFFER_SIZE; + int return_code = 0; + int iteration = 0; + do + { + paddr = (IP_ADAPTER_ADDRESSES*)malloc(len); + if (paddr == NULL) + return; + long flags = 0; + return_code = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, paddr, + &len); + if (return_code == ERROR_BUFFER_OVERFLOW) + { + free(paddr); + paddr = NULL; + } + else + break; + iteration++; + } while ((return_code == ERROR_BUFFER_OVERFLOW) && (iteration < 10)); + + if (return_code == ERROR_BUFFER_OVERFLOW) + { + Log::warn("ServerManager", "Can not get broadcast addresses."); + setDefaultBroadcastAddresses(); + return; + } + + for (IP_ADAPTER_ADDRESSES *p = paddr; p; p = p->Next) + { + if (p->OperStatus != IfOperStatusUp) + continue; + + std::set used_scope_id; + for (PIP_ADAPTER_UNICAST_ADDRESS unicast = p->FirstUnicastAddress; + unicast != NULL; unicast = unicast->Next) + { + SocketAddress socket_address; + if (unicast->Address.lpSockaddr->sa_family == AF_INET) + { + const sockaddr_in *sa = + (const sockaddr_in*)unicast->Address.lpSockaddr; + + // Skip 169.254.*.* local link address + if (sa->sin_addr.S_un.S_un_b.s_b1 == 169 && + sa->sin_addr.S_un.S_un_b.s_b2 == 254) + continue; + + // Use sa->sin_addr.S_un.S_addr and htonl? + SocketAddress ta(sa->sin_addr.S_un.S_un_b.s_b1, + sa->sin_addr.S_un.S_un_b.s_b2, + sa->sin_addr.S_un.S_un_b.s_b3, + sa->sin_addr.S_un.S_un_b.s_b4); + int len = 32 - unicast->OnLinkPrefixLength; + addAllBroadcastAddresses(ta, len); + } + if (unicast->Address.lpSockaddr->sa_family == AF_INET6) + { + sockaddr_in6* in6 = + (sockaddr_in6*)unicast->Address.lpSockaddr; + const uint32_t scope_id = in6->sin6_scope_id; + if (used_scope_id.find(scope_id) != + used_scope_id.end()) + continue; + used_scope_id.insert(scope_id); + SocketAddress socket_address("ff02::1", + stk_config->m_server_discovery_port); + in6 = (sockaddr_in6*)socket_address.getSockaddr(); + in6->sin6_scope_id = scope_id; + m_broadcast_address.push_back(socket_address); + } + } + } + free(paddr); #endif } // updateBroadcastAddresses // ---------------------------------------------------------------------------- /** Returns a list of all possible broadcast addresses on this machine. */ -const std::vector& ServersManager::getBroadcastAddresses() +const std::vector& ServersManager::getBroadcastAddresses() { if (m_broadcast_address.empty()) { diff --git a/src/network/servers_manager.hpp b/src/network/servers_manager.hpp index 2818a31a3..082fc69c5 100644 --- a/src/network/servers_manager.hpp +++ b/src/network/servers_manager.hpp @@ -29,7 +29,7 @@ namespace Online { class XMLRequest; } class Server; -class TransportAddress; +class SocketAddress; class XMLNode; /** @@ -43,7 +43,7 @@ private: std::vector > m_servers; /** List of broadcast addresses to use. */ - std::vector m_broadcast_address; + std::vector m_broadcast_address; std::atomic m_last_load_time; @@ -63,7 +63,7 @@ private: std::shared_ptr >& servers); void setDefaultBroadcastAddresses(); - void addAllBroadcastAddresses(const TransportAddress &a, int len); + void addAllBroadcastAddresses(const SocketAddress &a, int len); void updateBroadcastAddresses(); public: // ------------------------------------------------------------------------ @@ -80,7 +80,7 @@ public: // ------------------------------------------------------------------------ bool listUpdated() const { return m_list_updated; } // ------------------------------------------------------------------------ - const std::vector& getBroadcastAddresses(); + const std::vector& getBroadcastAddresses(); // ------------------------------------------------------------------------ void reset() { diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index de4d919c4..75c830c4a 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -878,13 +878,8 @@ void STKHost::mainLoop() { TransportAddress address(0, stk_config->m_server_discovery_port); ENetAddress eaddr = address.toEnetAddress(); - bool socket_ipv6 = isIPv6Socket() == 1 ? true : false; - if (socket_ipv6) - setIPv6Socket(0); // direct_socket use IPv4 only atm direct_socket = new Network(1, 1, 0, 0, &eaddr); - if (socket_ipv6) - setIPv6Socket(1); if (direct_socket->getENetHost() == NULL) { Log::warn("STKHost", "No direct socket available, this " diff --git a/src/states_screens/online/create_server_screen.cpp b/src/states_screens/online/create_server_screen.cpp index f575f5a68..5ffcbbfc0 100644 --- a/src/states_screens/online/create_server_screen.cpp +++ b/src/states_screens/online/create_server_screen.cpp @@ -23,6 +23,7 @@ #include "network/network_config.hpp" #include "network/server.hpp" #include "network/server_config.hpp" +#include "network/socket_address.hpp" #include "network/stk_host.hpp" #include "online/online_profile.hpp" #include "states_screens/state_manager.hpp" diff --git a/src/states_screens/online/online_screen.cpp b/src/states_screens/online/online_screen.cpp index 8455d9b19..30fe872cf 100644 --- a/src/states_screens/online/online_screen.cpp +++ b/src/states_screens/online/online_screen.cpp @@ -32,6 +32,7 @@ #include "network/network_config.hpp" #include "network/server.hpp" #include "network/server_config.hpp" +#include "network/socket_address.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" #include "online/request_manager.hpp" diff --git a/src/states_screens/online/server_selection.cpp b/src/states_screens/online/server_selection.cpp index d1cfc7fb7..b6ce23db4 100644 --- a/src/states_screens/online/server_selection.cpp +++ b/src/states_screens/online/server_selection.cpp @@ -347,7 +347,9 @@ void ServerSelection::eventCallback(GUIEngine::Widget* widget, return; } #ifdef ENABLE_IPV6 - if (!m_servers[selected_index]->getIPV6Address().empty()) + // Lan server is set IPv6 during broadcasting + if (NetworkConfig::get()->isWAN() && + m_servers[selected_index]->getIPV6Address()) m_servers[selected_index]->setIPV6Connection(m_ipv6->getState()); #endif new ServerInfoDialog(m_servers[selected_index]); @@ -436,7 +438,7 @@ void ServerSelection::copyFromServersManager() m_servers.erase(std::remove_if(m_servers.begin(), m_servers.end(), [this](const std::shared_ptr& a)->bool { - if (m_ipv6->getState() && a->getIPV6Address().empty()) + if (m_ipv6->getState() && !a->getIPV6Address()) return true; return false; }), m_servers.end());