diff options
-rw-r--r-- | inc/shared/game.h | 1 | ||||
-rw-r--r-- | src/server/ac.c | 2 | ||||
-rw-r--r-- | src/server/main.c | 60 | ||||
-rw-r--r-- | src/server/mvd.c | 12 |
4 files changed, 56 insertions, 19 deletions
diff --git a/inc/shared/game.h b/inc/shared/game.h index c7cef58..5e5c779 100644 --- a/inc/shared/game.h +++ b/inc/shared/game.h @@ -52,6 +52,7 @@ typedef enum { #define GMF_ENHANCED_SAVEGAMES 0x00000400 #define GMF_VARIABLE_FPS 0x00000800 #define GMF_EXTRA_USERINFO 0x00001000 +#define GMF_IPV6_ADDRESS_AWARE 0x00002000 //=============================================================== diff --git a/src/server/ac.c b/src/server/ac.c index c50edb1..8bc6425 100644 --- a/src/server/ac.c +++ b/src/server/ac.c @@ -1112,7 +1112,7 @@ char *AC_ClientConnect(client_t *cl) } } - if (ac.ready) { + if (ac.ready && net_from.type == NA_IP) { MSG_WriteShort(15); MSG_WriteByte(ACC_REQUESTCHALLENGE); MSG_WriteData(net_from.ip.u8, 4); diff --git a/src/server/main.c b/src/server/main.c index aaf2321..468cd39 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -688,21 +688,34 @@ static qboolean permit_connection(conn_params_t *p) if (sv_locked->integer) return reject("Server is locked.\n"); - // limit number of connections from single IP + // link-local IPv6 addresses are permitted without sv_iplimit check + if (net_from.type == NA_IP6 && NET_IsLanAddress(&net_from)) + return qtrue; + + // limit number of connections from single IPv4 address or /48 IPv6 network if (sv_iplimit->integer > 0) { count = 0; FOR_EACH_CLIENT(cl) { - if (NET_IsEqualBaseAdr(&net_from, &cl->netchan->remote_address)) { - if (cl->state == cs_zombie) { - count++; - } else { - count += 2; - } - } + netadr_t *adr = &cl->netchan->remote_address; + + if (net_from.type != adr->type) + continue; + if (net_from.type == NA_IP && net_from.ip.u32[0] != adr->ip.u32[0]) + continue; + if (net_from.type == NA_IP6 && memcmp(net_from.ip.u8, adr->ip.u8, 48 / CHAR_BIT)) + continue; + + if (cl->state == cs_zombie) + count += 1; + else + count += 2; + } + if (count / 2 >= sv_iplimit->integer) { + if (net_from.type == NA_IP6) + return reject("Too many connections from your IPv6 network.\n"); + else + return reject("Too many connections from your IP address.\n"); } - count >>= 1; - if (count >= sv_iplimit->integer) - return reject("Too many connections from your IP address.\n"); } return qtrue; @@ -794,6 +807,26 @@ static qboolean parse_enhanced_params(conn_params_t *p) return qtrue; } +static char *userinfo_ip_string(void) +{ + static char s[MAX_QPATH]; + + // fake up reserved IPv4 address to prevent IPv6 unaware mods from exploding + if (net_from.type == NA_IP6 && !(g_features->integer & GMF_IPV6_ADDRESS_AWARE)) { + uint8_t res = 0; + int i; + + // stuff /48 network part into the last byte + for (i = 0; i < 48 / CHAR_BIT; i++) + res ^= net_from.ip.u8[i]; + + Q_snprintf(s, sizeof(s), "198.51.100.%u:%u", res, BigShort(net_from.port)); + return s; + } + + return NET_AdrToString(&net_from); +} + static qboolean parse_userinfo(conn_params_t *params, char *userinfo) { char *info, *s; @@ -848,8 +881,7 @@ static qboolean parse_userinfo(conn_params_t *params, char *userinfo) } // force the IP key/value pair so the game can filter based on ip - s = NET_AdrToString(&net_from); - if (!Info_SetValueForKey(userinfo, "ip", s)) + if (!Info_SetValueForKey(userinfo, "ip", userinfo_ip_string())) return reject("Oversize userinfo string.\n"); } @@ -1005,7 +1037,7 @@ static void append_extra_userinfo(conn_params_t *params, char *userinfo) "\\challenge\\%d\\ip\\%s" "\\major\\%d\\minor\\%d\\netchan\\%d" "\\packetlen\\%d\\qport\\%d\\zlib\\%d", - params->challenge, NET_AdrToString(&net_from), + params->challenge, userinfo_ip_string(), params->protocol, params->version, params->nctype, params->maxlength, params->qport, params->has_zlib); } diff --git a/src/server/mvd.c b/src/server/mvd.c index da0ecee..1e7d973 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -1730,14 +1730,18 @@ static void accept_client(netstream_t *stream) gtv_client_t *client; netstream_t *s; - // limit number of connections from single IP + // limit number of connections from single IPv4 address or /48 IPv6 network if (sv_iplimit->integer > 0) { int count = 0; FOR_EACH_GTV(client) { - if (NET_IsEqualBaseAdr(&client->stream.address, &stream->address)) { - count++; - } + if (stream->address.type != client->stream.address.type) + continue; + if (stream->address.type == NA_IP && stream->address.ip.u32[0] != client->stream.address.ip.u32[0]) + continue; + if (stream->address.type == NA_IP6 && memcmp(stream->address.ip.u8, client->stream.address.ip.u8, 48 / CHAR_BIT)) + continue; + count++; } if (count >= sv_iplimit->integer) { Com_Printf("TCP client [%s] rejected: too many connections\n", |