NAT traversal in Netplay

For the time being, if NAT traversal is successful it simply announces
it as an OSD message. In the future it will be used to inform a
matchmaking server of the public port.

This patch also included minor fixes to the NAT traversal implementation
to make the select it demands actually doable.
This commit is contained in:
Gregor Richards 2016-11-29 22:59:46 -05:00
parent 48240c2806
commit 42da0a0184
7 changed files with 108 additions and 7 deletions

View File

@ -22,6 +22,10 @@ MSG_HASH(
MSG_GOT_CONNECTION_FROM,
"Got connection from"
)
MSG_HASH(
MSG_PUBLIC_ADDRESS,
"Public address"
)
MSG_HASH(
MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN,
"No arguments supplied and no menu builtin, displaying help..."

View File

@ -27,9 +27,15 @@
#include <net/net_socket.h>
struct natt_status {
/** nfds for select when checking for input */
int nfds;
/** The fdset to be selected upon to check for responses */
fd_set fds;
/** True if there might be a request outstanding */
bool request_outstanding;
/** True if we've resolved an external IPv4 address */
bool have_inet4;

View File

@ -173,6 +173,7 @@ enum msg_hash_enums
MSG_NO_STATE_HAS_BEEN_LOADED_YET,
MSG_GOT_CONNECTION_FROM,
MSG_CONNECTION_SLOT,
MSG_PUBLIC_ADDRESS,
MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET,
MSG_CANNOT_INFER_NEW_CONFIG_PATH,
MSG_UNDID_LOAD_STATE,

View File

@ -63,6 +63,8 @@ static netplay_t *netplay_data = NULL;
/* Used to avoid recursive netplay calls */
static bool in_netplay = false;
static void announce_nat_traversal(netplay_t *netplay);
static int init_tcp_connection(const struct addrinfo *res,
bool server, bool spectate,
struct sockaddr *other_addr, socklen_t addr_size)
@ -172,6 +174,22 @@ static bool init_tcp_socket(netplay_t *netplay, const char *server,
return ret;
}
static void init_nat_traversal(netplay_t *netplay)
{
natt_init();
if (!natt_new(&netplay->nat_traversal_state))
{
netplay->nat_traversal = false;
return;
}
natt_open_port_any(&netplay->nat_traversal_state, netplay->tcp_port, SOCKET_PROTOCOL_TCP);
if (!netplay->nat_traversal_state.request_outstanding)
announce_nat_traversal(netplay);
}
static bool init_ad_socket(netplay_t *netplay, uint16_t port)
{
int fd = socket_init((void**)&netplay->addr, port, NULL, SOCKET_TYPE_DATAGRAM);
@ -202,6 +220,9 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port)
if (!init_tcp_socket(netplay, server, port, netplay->spectate.enabled))
return false;
if (netplay->is_server && netplay->nat_traversal)
init_nat_traversal(netplay);
return true;
}
@ -1047,6 +1068,36 @@ void netplay_log_connection(const struct sockaddr_storage *their_addr,
#endif
static void announce_nat_traversal(netplay_t *netplay)
{
char msg[512], host[PATH_MAX_LENGTH], port[6];
if (netplay->nat_traversal_state.have_inet4)
{
if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet4_addr,
sizeof(struct sockaddr_in),
host, PATH_MAX_LENGTH, port, 6, 0) != 0)
return;
}
#ifdef AF_INET6
else if (netplay->nat_traversal_state.have_inet6)
{
if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet6_addr,
sizeof(struct sockaddr_in6),
host, PATH_MAX_LENGTH, port, 6, 0) != 0)
return;
}
#endif
else return;
snprintf(msg, sizeof(msg), "%s: %s:%s\n",
msg_hash_to_str(MSG_PUBLIC_ADDRESS),
host, port);
runloop_msg_queue_push(msg, 1, 180, false);
RARCH_LOG("%s\n", msg);
}
bool netplay_try_init_serialization(netplay_t *netplay)
@ -1166,6 +1217,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
* @check_frames : Frequency with which to check CRCs.
* @cb : Libretro callbacks.
* @spectate : If true, enable spectator mode.
* @nat_traversal : If true, attempt NAT traversal.
* @nick : Nickname of user.
* @quirks : Netplay quirks required for this session.
*
@ -1174,10 +1226,9 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames)
*
* Returns: new netplay handle.
**/
netplay_t *netplay_new(const char *server, uint16_t port,
unsigned frames, unsigned check_frames,
const struct retro_callbacks *cb,
bool spectate, const char *nick, uint64_t quirks)
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames,
unsigned check_frames, const struct retro_callbacks *cb, bool spectate,
bool nat_traversal, const char *nick, uint64_t quirks)
{
netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay));
if (!netplay)
@ -1189,6 +1240,7 @@ netplay_t *netplay_new(const char *server, uint16_t port,
netplay->port = server ? 0 : 1;
netplay->spectate.enabled = spectate;
netplay->is_server = server == NULL;
netplay->nat_traversal = netplay->is_server ? nat_traversal : false;
netplay->stall_frames = frames;
netplay->check_frames = check_frames;
netplay->quirks = quirks;
@ -1324,6 +1376,9 @@ void netplay_free(netplay_t *netplay)
free(netplay->spectate.input);
}
if (netplay->nat_traversal)
natt_free(&netplay->nat_traversal_state);
if (netplay->buffer)
{
for (i = 0; i < netplay->buffer_size; i++)
@ -1369,11 +1424,26 @@ bool netplay_pre_frame(netplay_t *netplay)
netplay_try_init_serialization(netplay);
}
/* Advertise our server if applicable */
if (netplay->is_server)
{
/* Advertise our server if applicable */
if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT))
netplay_ad_server(netplay, netplay_ad_fd);
/* NAT traversal if applicable */
if (netplay->nat_traversal &&
netplay->nat_traversal_state.request_outstanding &&
!netplay->nat_traversal_state.have_inet4)
{
struct timeval tmptv = {0};
fd_set fds = netplay->nat_traversal_state.fds;
if (socket_select(netplay->nat_traversal_state.nfds, &fds, NULL, NULL, &tmptv) > 0)
natt_read(&netplay->nat_traversal_state);
if (!netplay->nat_traversal_state.request_outstanding ||
netplay->nat_traversal_state.have_inet4)
announce_nat_traversal(netplay);
}
}
if (!netplay->net_cbs->pre_frame(netplay))
@ -1599,7 +1669,8 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port)
netplay_is_client ? server : NULL,
port ? port : RARCH_DEFAULT_PORT,
settings->netplay.sync_frames, settings->netplay.check_frames, &cbs,
is_spectate, settings->username, quirks);
is_spectate, settings->netplay.nat_traversal, settings->username,
quirks);
if (netplay_data)
return true;

View File

@ -137,6 +137,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames);
* @check_frames : Frequency with which to check CRCs.
* @cb : Libretro callbacks.
* @spectate : If true, enable spectator mode.
* @nat_traversal : If true, attempt NAT traversal.
* @nick : Nickname of user.
* @quirks : Netplay quirks.
*
@ -147,7 +148,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames);
**/
netplay_t *netplay_new(const char *server,
uint16_t port, unsigned frames, unsigned check_frames,
const struct retro_callbacks *cb, bool spectate,
const struct retro_callbacks *cb, bool spectate, bool nat_traversal,
const char *nick, uint64_t quirks);
/**

View File

@ -20,6 +20,7 @@
#include <net/net_compat.h>
#include <net/net_socket.h>
#include <net/net_natt.h>
#include "netplay_private.h"
@ -337,6 +338,19 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames)
netplay->has_connection = true;
}
{
struct natt_status status;
natt_init();
if (natt_new(&status) && natt_open_port_any(&status, netplay->tcp_port, SOCKET_PROTOCOL_TCP))
{
fprintf(stderr, "Forwarded to %d!\n", status.ext_inet4_addr.sin_port);
}
else
{
fprintf(stderr, "Forwarding failed :(\n");
}
}
return true;
}

View File

@ -20,6 +20,7 @@
#include "netplay.h"
#include <net/net_compat.h>
#include <net/net_natt.h>
#include <features/features_cpu.h>
#include <streams/trans_stream.h>
#include <retro_endianness.h>
@ -124,6 +125,9 @@ struct netplay
int fd;
/* TCP port (if serving) */
uint16_t tcp_port;
/* NAT traversal info (if NAT traversal is used and serving) */
bool nat_traversal;
struct natt_status nat_traversal_state;
/* Which port is governed by netplay (other user)? */
unsigned port;
bool has_connection;