Add ipv6 geolocation table

This commit is contained in:
Benau 2020-01-16 11:28:52 +08:00
parent 0ef632114b
commit 9085658653
6 changed files with 115 additions and 1 deletions

View File

@ -183,6 +183,9 @@ The current server configuration xml looks like this:
<!-- IP geolocation table, you only need this table if you want to geolocate IP from non-stk-addons connection, as all validated players connecting from stk-addons will provide the location info, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. -->
<ip-geolocation-table value="ip_mapping" />
<!-- IPv6 geolocation table, you only need this table if you want to geolocate IP from non-stk-addons connection, as all validated players connecting from stk-addons will provide the location info, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. -->
<ipv6-geolocation-table value="ipv6_mapping" />
<!-- If true this server will auto add / remove AI connected with network-ai=x, which will kick N - 1 bot(s) where N is the number of human players. Only use this for non-GP racing server. -->
<ai-handling value="false" />
@ -341,6 +344,15 @@ CREATE TABLE ip_mapping
longitude REAL NOT NULL, -- Longitude of this IP range
country_code TEXT NOT NULL -- 2-letter country code
) WITHOUT ROWID;
CREATE TABLE ipv6_mapping
(
ip_start INTEGER UNSIGNED NOT NULL PRIMARY KEY UNIQUE, -- IP decimal (upper 64bit) start
ip_end INTEGER UNSIGNED NOT NULL UNIQUE, -- IP decimal (upper 64bit) end
latitude REAL NOT NULL, -- Latitude of this IP range
longitude REAL NOT NULL, -- Longitude of this IP range
country_code TEXT NOT NULL -- 2-letter country code
)
```
For initialization of `ip_mapping` table, check [this script](tools/generate-ip-mappings.py).

View File

@ -63,6 +63,26 @@
#ifdef ENABLE_SQLITE3
// ----------------------------------------------------------------------------
static void upperIPv6SQL(sqlite3_context* context, int argc,
sqlite3_value** argv)
{
if (argc != 1)
{
sqlite3_result_int64(context, 0);
return;
}
char* ipv6 = (char*)sqlite3_value_text(argv[0]);
if (ipv6 == NULL)
{
sqlite3_result_int64(context, 0);
return;
}
sqlite3_result_int64(context, upperIPv6(ipv6));
}
// ----------------------------------------------------------------------------
void insideIPv6CIDRSQL(sqlite3_context* context, int argc,
sqlite3_value** argv)
@ -97,6 +117,8 @@ sqlite3_extension_init(sqlite3* db, char** pzErrMsg,
SQLITE_EXTENSION_INIT2(pApi)
sqlite3_create_function(db, "insideIPv6CIDR", 2, SQLITE_UTF8, NULL,
insideIPv6CIDRSQL, NULL, NULL);
sqlite3_create_function(db, "upperIPv6", 1, SQLITE_UTF8, 0, upperIPv6SQL,
0, 0);
return 0;
} // sqlite3_extension_init
*/
@ -256,6 +278,7 @@ void ServerLobby::initDatabase()
m_ipv6_ban_table_exists = false;
m_online_id_ban_table_exists = false;
m_ip_geolocation_table_exists = false;
m_ipv6_geolocation_table_exists = false;
if (!ServerConfig::m_sql_management)
return;
const std::string& path = ServerConfig::getConfigDirectory() + "/" +
@ -285,6 +308,8 @@ void ServerLobby::initDatabase()
}, NULL);
sqlite3_create_function(m_db, "insideIPv6CIDR", 2, SQLITE_UTF8, NULL,
&insideIPv6CIDRSQL, NULL, NULL);
sqlite3_create_function(m_db, "upperIPv6", 1, SQLITE_UTF8, NULL,
&upperIPv6SQL, NULL, NULL);
checkTableExists(ServerConfig::m_ip_ban_table, m_ip_ban_table_exists);
checkTableExists(ServerConfig::m_ipv6_ban_table, m_ipv6_ban_table_exists);
checkTableExists(ServerConfig::m_online_id_ban_table,
@ -293,6 +318,8 @@ void ServerLobby::initDatabase()
m_player_reports_table_exists);
checkTableExists(ServerConfig::m_ip_geolocation_table,
m_ip_geolocation_table_exists);
checkTableExists(ServerConfig::m_ipv6_geolocation_table,
m_ipv6_geolocation_table_exists);
#endif
} // initDatabase
@ -1144,6 +1171,47 @@ std::string ServerLobby::ip2Country(const TransportAddress& addr) const
return cc_code;
} // ip2Country
//-----------------------------------------------------------------------------
std::string ServerLobby::ipv62Country(const std::string& ipv6) const
{
if (!m_db || !m_ipv6_geolocation_table_exists)
return "";
std::string cc_code;
std::string query = StringUtils::insertValues(
"SELECT country_code FROM %s "
"WHERE `ip_start` <= upperIPv6(\"%s\") AND `ip_end` >= upperIPv6(\"%s\") "
"ORDER BY `ip_start` DESC LIMIT 1;",
ServerConfig::m_ipv6_geolocation_table.c_str(), ipv6.c_str(),
ipv6.c_str());
sqlite3_stmt* stmt = NULL;
int ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0);
if (ret == SQLITE_OK)
{
ret = sqlite3_step(stmt);
if (ret == SQLITE_ROW)
{
const char* country_code = (char*)sqlite3_column_text(stmt, 0);
cc_code = country_code;
}
ret = sqlite3_finalize(stmt);
if (ret != SQLITE_OK)
{
Log::error("ServerLobby",
"Error finalize database for query %s: %s",
query.c_str(), sqlite3_errmsg(m_db));
}
}
else
{
Log::error("ServerLobby", "Error preparing database for query %s: %s",
query.c_str(), sqlite3_errmsg(m_db));
return "";
}
return cc_code;
} // ipv62Country
#endif
//-----------------------------------------------------------------------------
@ -3317,8 +3385,10 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
}
#ifdef ENABLE_SQLITE3
if (country_code.empty())
if (country_code.empty() && peer->getIPV6Address().empty())
country_code = ip2Country(peer->getAddress());
if (country_code.empty() && !peer->getIPV6Address().empty())
country_code = ipv62Country(peer->getIPV6Address());
#endif
auto red_blue = STKHost::get()->getAllPlayersTeamInfo();

View File

@ -91,6 +91,8 @@ private:
bool m_ip_geolocation_table_exists;
bool m_ipv6_geolocation_table_exists;
uint64_t m_last_poll_db_time;
void pollDatabase();
@ -101,6 +103,8 @@ private:
void checkTableExists(const std::string& table, bool& result);
std::string ip2Country(const TransportAddress& addr) const;
std::string ipv62Country(const std::string& ipv6) const;
#endif
void initDatabase();

View File

@ -412,6 +412,16 @@ namespace ServerConfig
"empty to disable. "
"This table can be shared for all servers if you use the same name."));
SERVER_CFG_PREFIX StringServerConfigParam m_ipv6_geolocation_table
SERVER_CFG_DEFAULT(StringServerConfigParam("ipv6_mapping",
"ipv6-geolocation-table",
"IPv6 geolocation table, you only need this table if you want to "
"geolocate IP from non-stk-addons connection, as all validated "
"players connecting from stk-addons will provide the location info, "
"you need to create the table first, see NETWORKING.md for details, "
"empty to disable. "
"This table can be shared for all servers if you use the same name."));
SERVER_CFG_PREFIX BoolServerConfigParam m_ai_handling
SERVER_CFG_DEFAULT(BoolServerConfigParam(false, "ai-handling",
"If true this server will auto add / remove AI connected with "

View File

@ -138,6 +138,23 @@ void andIPv6(struct in6_addr* ipv6, const struct in6_addr* mask)
ipv6->s6_addr[i] &= mask->s6_addr[i];
} // andIPv6
// ----------------------------------------------------------------------------
extern "C" int64_t upperIPv6(const char* ipv6)
{
struct in6_addr v6_in;
if (inet_pton(AF_INET6, ipv6, &v6_in) != 1)
return 0;
uint64_t result = 0;
unsigned shift = 56;
for (unsigned i = 0; i < 8; i++)
{
uint64_t val = v6_in.s6_addr[i];
result += val << shift;
shift -= 8;
}
return result;
}
// ----------------------------------------------------------------------------
extern "C" int insideIPv6CIDR(const char* ipv6_cidr, const char* ipv6_in)
{

View File

@ -29,6 +29,7 @@ void getMappedFromIPV6(const struct sockaddr_in6* in6, ENetAddress* ea);
void addMappedAddress(const ENetAddress* ea, const struct sockaddr_in6* in6);
int getaddrinfo_compat(const char* hostname, const char* servname,
const struct addrinfo* hints, struct addrinfo** res);
int64_t upperIPv6(const char* ipv6);
int insideIPv6CIDR(const char* ipv6_cidr, const char* ipv6_in);
#ifdef __cplusplus
}