Add IPv6 LAN server discovery

Broadcasting to windows doesn't work as the windows firewall seems
to block all IPv6 multicast
This commit is contained in:
Benau 2020-01-27 13:48:57 +08:00
parent 9132fba714
commit e18bcd62e6
10 changed files with 201 additions and 94 deletions

View File

@ -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 "

View File

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

View File

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

View File

@ -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<SocketAddress> 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

View File

@ -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 <assert.h>
#include <functional>
#include <set>
#include <string>
#include <thread>
@ -42,6 +45,7 @@
# include <iphlpapi.h>
#else
# include <ifaddrs.h>
# include <net/if.h>
#endif
const int64_t SERVER_REFRESH_INTERVAL = 5000;
@ -158,8 +162,9 @@ std::shared_ptr<Online::XMLRequest> 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<TransportAddress> &all_bcast =
const std::vector<SocketAddress> &all_bcast =
ServersManager::get()->getBroadcastAddresses();
for (auto &bcast_addr : all_bcast)
{
@ -187,7 +192,7 @@ std::shared_ptr<Online::XMLRequest> ServersManager::getLANRefreshRequest() const
std::map<irr::core::stringw, std::shared_ptr<Server> > 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<Online::XMLRequest> ServersManager::getLANRefreshRequest() const
{
(void)e;
}
servers_now.insert(std::make_pair(name,
std::make_shared<Server>(cur_server_id++, name,
max_players, players, difficulty, mode, sender,
password == 1, game_started == 1, current_track)));
auto server = std::make_shared<Server>(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<SocketAddress> 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<uint32_t> 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<uint32_t> 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<TransportAddress>& ServersManager::getBroadcastAddresses()
const std::vector<SocketAddress>& ServersManager::getBroadcastAddresses()
{
if (m_broadcast_address.empty())
{

View File

@ -29,7 +29,7 @@
namespace Online { class XMLRequest; }
class Server;
class TransportAddress;
class SocketAddress;
class XMLNode;
/**
@ -43,7 +43,7 @@ private:
std::vector<std::shared_ptr<Server> > m_servers;
/** List of broadcast addresses to use. */
std::vector<TransportAddress> m_broadcast_address;
std::vector<SocketAddress> m_broadcast_address;
std::atomic<int64_t> m_last_load_time;
@ -63,7 +63,7 @@ private:
std::shared_ptr<Server> >& 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<TransportAddress>& getBroadcastAddresses();
const std::vector<SocketAddress>& getBroadcastAddresses();
// ------------------------------------------------------------------------
void reset()
{

View File

@ -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 "

View File

@ -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"

View File

@ -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"

View File

@ -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<Server>& a)->bool
{
if (m_ipv6->getState() && a->getIPV6Address().empty())
if (m_ipv6->getState() && !a->getIPV6Address())
return true;
return false;
}), m_servers.end());