Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 28 additions & 21 deletions src/engine/qcommon/net_ip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ static bool winsockInitialized = false;
# endif

# include <arpa/inet.h>
# include <fcntl.h>
# include <netdb.h>
# include <netinet/in.h>
# include <sys/socket.h>
Expand Down Expand Up @@ -149,6 +150,7 @@ static SOCKET socks_socket = INVALID_SOCKET;
static SOCKET multicast6_socket = INVALID_SOCKET;

// Keep track of currently joined multicast group.
// Also stores the multicast address info for clients though they don't join a group
static struct ipv6_mreq curgroup;

// And the currently bound address.
Expand All @@ -159,6 +161,10 @@ static struct sockaddr_in6 boundto;
#endif

// use an admin local address per default so that network admins can decide on how to handle quake3 traffic.
// However, an admin-local address usually doesn't go anywhere besides without special network
// configuration. Could use ff02 instead of ff04 meaning a link-local address more like IPv4, but
// then it doesn't work without manually setting net_mcast6iface.
// The final 4 quartets spell "ioquake3". TODO change for Daemon?
#define NET_MULTICAST_IP6 "ff04::696f:7175:616b:6533"

static const int MAX_IPS = 32;
Expand Down Expand Up @@ -946,6 +952,7 @@ SOCKET NET_IPSocket( const char *net_interface, int port, struct sockaddr_in *bi
newsocket = WSASocketW( PF_INET, SOCK_DGRAM, IPPROTO_UDP, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT );
#else
newsocket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
fcntl( newsocket, F_SETFD, FD_CLOEXEC );
#endif

if ( newsocket == INVALID_SOCKET )
Expand Down Expand Up @@ -1020,7 +1027,7 @@ SOCKET NET_IPSocket( const char *net_interface, int port, struct sockaddr_in *bi
NET_IP6Socket
====================
*/
SOCKET NET_IP6Socket( const char *net_interface, int port, struct sockaddr_in6 *bindto, int *err )
SOCKET NET_IP6Socket( const char *net_interface, int port, bool multicastSend, struct sockaddr_in6 *bindto, int *err )
{
SOCKET newsocket;
struct sockaddr_in6 address;
Expand All @@ -1037,6 +1044,7 @@ SOCKET NET_IP6Socket( const char *net_interface, int port, struct sockaddr_in6 *
newsocket = WSASocketW( PF_INET6, SOCK_DGRAM, IPPROTO_UDP, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT );
#else
newsocket = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP );
fcntl( newsocket, F_SETFD, FD_CLOEXEC );
#endif

if ( newsocket == INVALID_SOCKET )
Expand Down Expand Up @@ -1068,6 +1076,13 @@ SOCKET NET_IP6Socket( const char *net_interface, int port, struct sockaddr_in6 *
}
#endif

if ( multicastSend && curgroup.ipv6mr_interface && SOCKET_ERROR == setsockopt(
newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, reinterpret_cast<const char *>( &curgroup.ipv6mr_interface ),
sizeof( curgroup.ipv6mr_interface ) ) )
{
Log::Warn( "NET_IP6Socket: couldn't set outbound multicast scope for socket: %s", NET_ErrorString() );
}

if ( !net_interface || !net_interface[ 0 ] )
{
memset( &address, 0, sizeof( address ) );
Expand Down Expand Up @@ -1140,6 +1155,7 @@ void NET_SetMulticast6()
curgroup.ipv6mr_interface = net_mcast6iface->integer;
#else
curgroup.ipv6mr_interface = if_nametoindex( net_mcast6iface->string );
Log::Notice( "Parsed IPv6 interface index: %d", curgroup.ipv6mr_interface );
#endif
}
else
Expand All @@ -1163,36 +1179,20 @@ void NET_JoinMulticast6()
return;
}

if ( IN6_IS_ADDR_MULTICAST( &boundto.sin6_addr ) || IN6_IS_ADDR_UNSPECIFIED( &boundto.sin6_addr ) )
if ( IN6_IS_ADDR_UNSPECIFIED( &boundto.sin6_addr ) )
{
// The way the socket was bound does not prohibit receiving multi-cast packets. So we don't need to open a new one.
multicast6_socket = ip6_socket;
}
else
{
if ( ( multicast6_socket = NET_IP6Socket( net_mcast6addr->string, ntohs( boundto.sin6_port ), nullptr, &err ) ) == INVALID_SOCKET )
if ( ( multicast6_socket = NET_IP6Socket( nullptr, ntohs( boundto.sin6_port ), false, nullptr, &err ) ) == INVALID_SOCKET )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why nullptr now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't try to bind to IPv6 multicast address

Internet sources say you are supposed to bind to the all-zeroes address
if you want to receive multicast packets, not the multicast address.
Indeed, when tested on Windows the attempt to bind to a multicast
address produced an error.

And null is treated as the all-zeroes address.

{
// If the OS does not support binding to multicast addresses, like Windows XP, at least try with a non-multicast socket.
multicast6_socket = ip6_socket;
}
}

if ( curgroup.ipv6mr_interface )
{
if ( setsockopt( multicast6_socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
( char * ) &curgroup.ipv6mr_interface, sizeof( curgroup.ipv6mr_interface ) ) < 0 )
{
Log::Notice( "NET_JoinMulticast6: Couldn't set scope on multicast socket: %s", NET_ErrorString() );

if ( multicast6_socket != ip6_socket )
{
closesocket( multicast6_socket );
multicast6_socket = INVALID_SOCKET;
return;
}
}
}

if ( setsockopt( multicast6_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, ( char * ) &curgroup, sizeof( curgroup ) ) )
{
Log::Notice( "NET_JoinMulticast6: Couldn't join multicast group: %s", NET_ErrorString() );
Expand Down Expand Up @@ -1246,6 +1246,7 @@ void NET_OpenSocks( int port )
socks_socket = WSASocketW( AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT );
#else
socks_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
fcntl( socks_socket, F_SETFD, FD_CLOEXEC );
#endif

if ( socks_socket == INVALID_SOCKET )
Expand Down Expand Up @@ -1624,7 +1625,8 @@ static void NET_OpenIP( bool serverMode )
{
for ( i = ( port6 == PORT_ANY ? 1 : MAX_TRY_PORTS ); i; i-- )
{
ip6_socket = NET_IP6Socket( net_ip6->string, port6, &boundto, &err );
bool sendingMulticast = !serverMode && !( net_enabled->integer & NET_DISABLEMCAST );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why conditional here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the client can send multicast packets. This is supposed to be the equivalent of the IPv4 broadcast, to search for servers in a LAN. The server responds with a normal packet.

ip6_socket = NET_IP6Socket( net_ip6->string, port6, sendingMulticast, &boundto, &err );

if ( ip6_socket != INVALID_SOCKET )
{
Expand Down Expand Up @@ -1737,8 +1739,8 @@ void NET_EnableNetworking( bool serverMode )

networkingEnabled = true;

NET_SetMulticast6(); // just parses cvars
NET_OpenIP( serverMode );
NET_SetMulticast6();
SV_NET_Config();
}

Expand Down Expand Up @@ -1888,4 +1890,9 @@ void NET_Restart_f()
#else
NET_EnableNetworking( com_sv_running.Get() );
#endif

if ( com_sv_running.Get() )
{
NET_JoinMulticast6();
}
}
Loading