Initial work on IPV6 game server

This commit is contained in:
Benau 2019-08-19 00:05:34 +08:00
parent 1551bdaaaa
commit c2c5c20d92
9 changed files with 334 additions and 38 deletions

View File

@ -22,6 +22,7 @@ option(SERVER_ONLY "Create a server only (i.e. no graphics or sound)" OFF)
option(CHECK_ASSETS "Check if assets are installed in ../stk-assets" ON)
option(USE_SYSTEM_ANGELSCRIPT "Use system angelscript instead of built-in angelscript. If you enable this option, make sure to use a compatible version." OFF)
option(USE_SYSTEM_ENET "Use system ENet instead of the built-in version, when available." ON)
option(USE_IPV6 "Allow create or connect to game server with ipv6 address, system enet will not be used." OFF)
option(USE_SYSTEM_GLEW "Use system GLEW instead of the built-in version, when available." ON)
option(USE_SYSTEM_WIIUSE "Use system WiiUse instead of the built-in version, when available." OFF)
option(USE_SQLITE3 "Use sqlite to manage server stats and ban list." ON)
@ -160,11 +161,15 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/lib/bullet")
include_directories("${PROJECT_SOURCE_DIR}/lib/bullet/src")
# Find system ENet library or build it if missing
if((UNIX AND NOT APPLE) AND USE_SYSTEM_ENET)
if((UNIX AND NOT APPLE) AND USE_SYSTEM_ENET AND NOT USE_IPV6)
pkg_check_modules(ENET libenet>=1.3.4)
endif()
if(ENET_FOUND)
if (USE_IPV6)
add_definitions(-DENABLE_IPV6)
endif()
if(ENET_FOUND AND NOT USE_IPV6)
include_directories(${ENET_INCLUDE_DIRS})
else()
# Fallback to built-in version

View File

@ -9,6 +9,10 @@
#include "enet/time.h"
#include "enet/enet.h"
#ifdef ENABLE_IPV6
extern void removeMappedAddress(const ENetAddress* ea);
#endif
static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
{
0,
@ -84,6 +88,10 @@ enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event)
event -> peer = peer;
event -> data = peer -> eventData;
#ifdef ENABLE_IPV6
removeMappedAddress(&peer->address);
#endif
enet_peer_reset (peer);
return 1;
@ -148,6 +156,10 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e
event -> peer = peer;
event -> data = 0;
#ifdef ENABLE_IPV6
removeMappedAddress(&peer->address);
#endif
enet_peer_reset (peer);
}
else

View File

@ -58,18 +58,18 @@ typedef int socklen_t;
static enet_uint32 timeBase = 0;
#ifdef IOS_STK
extern void iOSInitialize(void);
extern int isIPV6Only(void);
extern void getSynthesizedAddress(const ENetAddress* ea, struct sockaddr_in6* in6);
extern void getIPV4FromSynthesized(const struct sockaddr_in6* in6, ENetAddress* ea);
#ifdef ENABLE_IPV6
extern void unixInitialize(void);
extern int isIPV6(void);
extern void getIPV6FromMappedAddress(const ENetAddress* ea, struct sockaddr_in6* in6);
extern void getMappedFromIPV6(const struct sockaddr_in6* in6, ENetAddress* ea);
#endif
int
enet_initialize (void)
{
#ifdef IOS_STK
iOSInitialize();
#ifdef ENABLE_IPV6
unixInitialize();
#endif
return 0;
}
@ -199,9 +199,9 @@ enet_address_get_host (const ENetAddress * address, char * name, size_t nameLeng
int
enet_socket_bind (ENetSocket socket, const ENetAddress * address)
{
#ifdef IOS_STK
#ifdef ENABLE_IPV6
// In STK we only bind port and listen to any address
if (isIPV6Only())
if (isIPV6())
{
struct sockaddr_in6 sin;
memset (&sin, 0, sizeof (struct sockaddr_in6));
@ -261,12 +261,26 @@ enet_socket_listen (ENetSocket socket, int backlog)
ENetSocket
enet_socket_create (ENetSocketType type)
{
#ifdef IOS_STK
int af_family = isIPV6Only() == 1 ? PF_INET6 : PF_INET;
#ifdef ENABLE_IPV6
int af_family = isIPV6() == 1 ? PF_INET6 : PF_INET;
#else
int af_family = PF_INET;
#endif
return socket (af_family, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
int socket_fd = socket (af_family, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
#ifdef ENABLE_IPV6
if (isIPV6())
{
int no = 0;
// Allow ipv6 socket listen to ipv4 connection (as long as the host has ipv4 address)
int ret = setsockopt(socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
if (ret != 0)
{
close(socket_fd);
return -1;
}
}
#endif
return socket_fd;
}
int
@ -408,7 +422,7 @@ enet_socket_send (ENetSocket socket,
{
struct msghdr msgHdr;
struct sockaddr_in sin;
#ifdef IOS_STK
#ifdef ENABLE_IPV6
struct sockaddr_in6 sin6;
#endif
int sentLength;
@ -417,10 +431,10 @@ enet_socket_send (ENetSocket socket,
if (address != NULL)
{
#ifdef IOS_STK
if (isIPV6Only())
#ifdef ENABLE_IPV6
if (isIPV6())
{
getSynthesizedAddress(address, &sin6);
getIPV6FromMappedAddress(address, &sin6);
msgHdr.msg_name = & sin6;
msgHdr.msg_namelen = sizeof (struct sockaddr_in6);
}
@ -462,7 +476,7 @@ enet_socket_receive (ENetSocket socket,
{
struct msghdr msgHdr;
struct sockaddr_in sin;
#ifdef IOS_STK
#ifdef ENABLE_IPV6
struct sockaddr_in6 sin6;
#endif
int recvLength;
@ -471,8 +485,8 @@ enet_socket_receive (ENetSocket socket,
if (address != NULL)
{
#ifdef IOS_STK
if (isIPV6Only())
#ifdef ENABLE_IPV6
if (isIPV6())
{
msgHdr.msg_name = & sin6;
msgHdr.msg_namelen = sizeof (struct sockaddr_in6);
@ -505,10 +519,10 @@ enet_socket_receive (ENetSocket socket,
if (address != NULL)
{
#ifdef IOS_STK
if (isIPV6Only())
#ifdef ENABLE_IPV6
if (isIPV6())
{
getIPV4FromSynthesized(&sin6, address);
getMappedFromIPV6(&sin6, address);
}
else
#endif

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -23,7 +23,7 @@
#include "io/file_manager.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/ios_ipv6.hpp"
#include "network/unix_ipv6.hpp"
#include "network/network_config.hpp"
#include "network/network_console.hpp"
#include "network/network_player_profile.hpp"
@ -370,8 +370,7 @@ void STKHost::shutdown()
*/
void STKHost::setPublicAddress()
{
#ifdef IOS_STK
if (isIPV6Only())
if (isIPV6())
{
// IPV6 only in iOS doesn't support connection to firewalled server,
// so no need to test STUN
@ -379,7 +378,7 @@ void STKHost::setPublicAddress()
m_public_address = TransportAddress("169.254.0.0:65535");
return;
}
#endif
std::vector<std::pair<std::string, uint32_t> > untried_server;
for (auto& p : UserConfigParams::m_stun_servers)
untried_server.push_back(p);
@ -714,8 +713,8 @@ void STKHost::mainLoop()
// A separate network connection (socket) to handle LAN requests.
Network* direct_socket = NULL;
if ((NetworkConfig::get()->isLAN() && is_server) ||
NetworkConfig::get()->isPublicServer())
if (!isIPV6() && ((NetworkConfig::get()->isLAN() && is_server) ||
NetworkConfig::get()->isPublicServer()))
{
TransportAddress address(0, stk_config->m_server_discovery_port);
ENetAddress eaddr = address.toEnetAddress();
@ -811,7 +810,7 @@ void STKHost::mainLoop()
{
Log::info("STKHost", "%s %s with ping %d is higher"
" than %d ms when not in game, kick.",
p.second->getAddress().toString().c_str(),
p.second->getRealAddress().c_str(),
player_name.c_str(), ap, max_ping);
p.second->setWarnedForHighPing(true);
p.second->setDisconnected(true);
@ -824,7 +823,7 @@ void STKHost::mainLoop()
{
Log::info("STKHost", "%s %s with ping %d is higher"
" than %d ms.",
p.second->getAddress().toString().c_str(),
p.second->getRealAddress().c_str(),
player_name.c_str(), ap, max_ping);
p.second->setWarnedForHighPing(true);
NetworkString msg(PROTOCOL_LOBBY_ROOM);
@ -889,7 +888,7 @@ void STKHost::mainLoop()
{
Log::info("STKHost", "%s has not been validated for more"
" than %f seconds, disconnect it by force.",
it->second->getAddress().toString().c_str(),
it->second->getRealAddress().c_str(),
timeout);
enet_host_flush(host);
enet_peer_reset(it->first);
@ -976,7 +975,8 @@ void STKHost::mainLoop()
stk_event = new Event(&event, stk_peer);
TransportAddress addr(event.peer->address);
Log::info("STKHost", "%s has just connected. There are "
"now %u peers.", addr.toString().c_str(), getPeerCount());
"now %u peers.", stk_peer->getRealAddress().c_str(),
getPeerCount());
// Client always trust the server
if (!is_server)
stk_peer->setValidated();
@ -994,15 +994,17 @@ void STKHost::mainLoop()
}
// Use the previous stk peer so protocol can see the network
// profile and handle it for disconnection
std::string addr;
if (m_peers.find(event.peer) != m_peers.end())
{
stk_event = new Event(&event, m_peers.at(event.peer));
std::shared_ptr<STKPeer>& peer = m_peers.at(event.peer);
addr = peer->getRealAddress();
stk_event = new Event(&event, peer);
std::lock_guard<std::mutex> lock(m_peers_mutex);
m_peers.erase(event.peer);
}
TransportAddress addr(event.peer->address);
Log::info("STKHost", "%s has just disconnected. There are "
"now %u peers.", addr.toString().c_str(), getPeerCount());
"now %u peers.", addr.c_str(), getPeerCount());
} // ENET_EVENT_TYPE_DISCONNECT
if (!stk_event && m_peers.find(event.peer) != m_peers.end())

View File

@ -25,7 +25,9 @@
#include "network/network_string.hpp"
#include "network/stk_host.hpp"
#include "network/transport_address.hpp"
#include "network/unix_ipv6.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
#include "utils/time.hpp"
#include <string.h>
@ -35,6 +37,7 @@
STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
: m_peer_address(enet_peer->address), m_host(host)
{
m_ipv6_address = getIPV6ReadableFromMappedAddress(&enet_peer->address);
m_enet_peer = enet_peer;
m_host_id = host_id;
m_connected_time = StkTime::getMonoTimeMs();
@ -198,3 +201,13 @@ void STKPeer::setCrypto(std::unique_ptr<Crypto>&& c)
{
m_crypto = std::move(c);
} // setCrypto
//-----------------------------------------------------------------------------
/* Return an IPV6 or IPV4 address, used for debug printing.
*/
std::string STKPeer::getRealAddress() const
{
return m_ipv6_address.empty() ? m_peer_address.toString() :
std::string("[") + m_ipv6_address + "]:" +
StringUtils::toString(m_peer_address.getPort());
} // getRealAddress

View File

@ -100,6 +100,8 @@ protected:
std::string m_user_version;
std::string m_ipv6_address;
/** List of client capabilities set when connecting it, to determine
* features available in same version. */
std::set<std::string> m_client_capabilities;
@ -120,6 +122,11 @@ public:
// ------------------------------------------------------------------------
bool isConnected() const;
const TransportAddress& getAddress() const { return m_peer_address; }
// ------------------------------------------------------------------------
const std::string& getIPV6Address() const { return m_ipv6_address; }
// ------------------------------------------------------------------------
std::string getRealAddress() const;
// ------------------------------------------------------------------------
bool isSamePeer(const STKPeer* peer) const;
bool isSamePeer(const ENetPeer* peer) const;
// ------------------------------------------------------------------------

210
src/network/unix_ipv6.cpp Normal file
View File

@ -0,0 +1,210 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2019 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef ENABLE_IPV6
#include "network/unix_ipv6.hpp"
// ----------------------------------------------------------------------------
int isIPV6()
{
return 0;
} // isIPV6
// ----------------------------------------------------------------------------
std::string getIPV6ReadableFromMappedAddress(const ENetAddress* ea)
{
return "";
} // getIPV6ReadableFromMappedAddress
#else
#include "network/unix_ipv6.hpp"
#include "network/transport_address.hpp"
#include "utils/string_utils.hpp"
#include "utils/log.hpp"
#include "utils/types.hpp"
#include <arpa/inet.h>
#include <err.h>
#include <netdb.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <algorithm>
#include <utility>
#include <vector>
// ============================================================================
uint32_t g_mapped_ipv6_used;
int g_ipv6;
std::vector<std::pair<ENetAddress, struct sockaddr_in6> > g_mapped_ips;
// ============================================================================
int isIPV6()
{
return g_ipv6;
} // isIPV6
// ----------------------------------------------------------------------------
void setIPV6(int val)
{
g_ipv6 = val;
} // setIPV6
// ----------------------------------------------------------------------------
void unixInitialize()
{
// Clear previous setting, in case user changed wifi or mobile data
g_mapped_ipv6_used = 0;
g_ipv6 = 1;
g_mapped_ips.clear();
} // unixInitialize
// ----------------------------------------------------------------------------
std::string getIPV6ReadableFromIn6(const struct sockaddr_in6* in)
{
std::string result;
char ipv6[INET6_ADDRSTRLEN] = {};
inet_ntop(AF_INET6, &(in->sin6_addr), ipv6, INET6_ADDRSTRLEN);
result = ipv6;
return result;
} // getIPV6ReadableFromIn6
// ----------------------------------------------------------------------------
/* Called when a peer is disconnected and we remove its reference to the ipv6.
*/
void removeMappedAddress(const ENetAddress* ea)
{
auto it = std::find_if(g_mapped_ips.begin(), g_mapped_ips.end(),
[ea](const std::pair<ENetAddress, struct sockaddr_in6>& addr)
{
return ea->host == addr.first.host && ea->port == addr.first.port;
});
if (it != g_mapped_ips.end())
{
TransportAddress addr(it->first);
Log::debug("IPV6", "Removing %s, ipv4 address %s.",
getIPV6ReadableFromIn6(&it->second).c_str(),
addr.toString().c_str());
g_mapped_ips.erase(it);
Log::debug("IPV6", "Mapped address size now: %d.",
g_mapped_ips.size());
}
} // removeMappedAddress
// ----------------------------------------------------------------------------
std::string getIPV6ReadableFromMappedAddress(const ENetAddress* ea)
{
std::string result;
auto it = std::find_if(g_mapped_ips.begin(), g_mapped_ips.end(),
[ea](const std::pair<ENetAddress, struct sockaddr_in6>& addr)
{
return ea->host == addr.first.host && ea->port == addr.first.port;
});
if (it != g_mapped_ips.end())
result = getIPV6ReadableFromIn6(&it->second);
return result;
} // getIPV6ReadableFromMappedAddress
// ----------------------------------------------------------------------------
/** Add a (fake or synthesized by ios / osx) ipv4 address and map it to an ipv6
* one, used in client to set the game server address or server to initialize
* host.
*/
void addMappedAddress(const ENetAddress* ea, const struct sockaddr_in6* in6)
{
g_mapped_ips.emplace_back(*ea, *in6);
} // addMappedAddress
// ----------------------------------------------------------------------------
/* This is called when enet needs to sent to an mapped ipv4 address, we look up
* the map here and get the real ipv6 address, so you need to call
* addMappedAddress above first (for client mostly).
*/
void getIPV6FromMappedAddress(const ENetAddress* ea, struct sockaddr_in6* in6)
{
auto it = std::find_if(g_mapped_ips.begin(), g_mapped_ips.end(),
[ea](const std::pair<ENetAddress, struct sockaddr_in6>& addr)
{
return ea->host == addr.first.host && ea->port == addr.first.port;
});
if (it != g_mapped_ips.end())
memcpy(in6, &it->second, sizeof(struct sockaddr_in6));
else
memset(in6, 0, sizeof(struct sockaddr_in6));
} // getIPV6FromMappedAddress
// ----------------------------------------------------------------------------
bool sameIPV6(const struct sockaddr_in6* in_1, const struct sockaddr_in6* in_2)
{
// Check port first, then address
if (in_1->sin6_port != in_2->sin6_port)
return false;
const struct in6_addr* a = &(in_1->sin6_addr);
const struct in6_addr* b = &(in_2->sin6_addr);
for (unsigned i = 0; i < sizeof(struct in6_addr); i++)
{
if (a->s6_addr[i] != b->s6_addr[i])
return false;
}
return true;
} // sameIPV6
// ----------------------------------------------------------------------------
/* This is called when enet recieved a packet from its socket, we create an
* real ipv4 address out of it or a fake one if it's from ipv6 connection.
*/
void getMappedFromIPV6(const struct sockaddr_in6* in6, ENetAddress* ea)
{
auto it = std::find_if(g_mapped_ips.begin(), g_mapped_ips.end(),
[in6](const std::pair<ENetAddress, struct sockaddr_in6>& addr)
{
return sameIPV6(in6, &addr.second);
});
if (it != g_mapped_ips.end())
{
*ea = it->first;
return;
}
uint16_t w0 = in6->sin6_addr.s6_addr16[0];
uint16_t w1 = in6->sin6_addr.s6_addr16[1];
uint16_t w2 = in6->sin6_addr.s6_addr16[2];
uint16_t w3 = in6->sin6_addr.s6_addr16[3];
uint16_t w4 = in6->sin6_addr.s6_addr16[4];
uint16_t w5 = in6->sin6_addr.s6_addr16[5];
if (w0 == 0 && w1 == 0 && w2 == 0 && w3 == 0 && w4 == 0 && w5 == 0xFFFF)
{
ea->host = ((in_addr*)(in6->sin6_addr.s6_addr + 12))->s_addr;
ea->port = ntohs(in6->sin6_port);
TransportAddress addr(*ea);
addMappedAddress(ea, in6);
}
else
{
// Create a fake ipv4 address of 0.x.x.x if it's a real ipv6 connection
if (g_mapped_ipv6_used >= 16777215)
g_mapped_ipv6_used = 0;
TransportAddress addr(++g_mapped_ipv6_used, ntohs(in6->sin6_port));
*ea = addr.toEnetAddress();
Log::debug("IPV6", "Fake IPV4 address %s mapped to %s",
addr.toString().c_str(), getIPV6ReadableFromIn6(in6).c_str());
addMappedAddress(ea, in6);
}
} // getMappedFromIPV6
#endif

33
src/network/unix_ipv6.hpp Normal file
View File

@ -0,0 +1,33 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2019 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <enet/enet.h>
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
int isIPV6();
void setIPV6(int val);
void unixInitialize();
void removeMappedAddress(const ENetAddress* ea);
void getIPV6FromMappedAddress(const ENetAddress* ea, struct sockaddr_in6* in6);
void getMappedFromIPV6(const struct sockaddr_in6* in6, ENetAddress* ea);
#ifdef __cplusplus
}
#endif
std::string getIPV6ReadableFromMappedAddress(const ENetAddress* ea);