@@ -109,11 +109,22 @@ bool IsRouteToAddressProbablyLocal( netadr_t addr )
109109{
110110
111111 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_MOCK
112- // In mock mode 127.0.100.x is the simulated public internet, not a local route,
113- // even though it falls in the reserved 127.x.x.x range.
114- if ( TEST_mocknetwork_active && addr.GetType () == k_EIPTypeV4
115- && ( addr.GetIPv4 () & 0xFF00 ) == ( 100 << 8 ) )
116- return false ;
112+ // In mock mode the public-network ranges are simulated internet, not local routes,
113+ // even though they fall in reserved address space.
114+ // IPv4 public: 127.0.100.x (third octet == 100)
115+ // IPv6 public: fd7f:0:100::x (bytes [4:6] == 0x0100)
116+ if ( TEST_mocknetwork_active )
117+ {
118+ if ( addr.GetType () == k_EIPTypeV4 && ( addr.GetIPv4 () & 0xFF00 ) == ( 100 << 8 ) )
119+ return false ;
120+ if ( addr.GetType () == k_EIPTypeV6 )
121+ {
122+ const uint8 *b = addr.GetIPV6Bytes ();
123+ if ( b[0 ] == 0xfd && b[1 ] == 0x7f && b[2 ] == 0x00 && b[3 ] == 0x00
124+ && b[4 ] == 0x01 && b[5 ] == 0x00 )
125+ return false ;
126+ }
127+ }
117128 #endif
118129
119130 // Assume that if we are able to send to any "reserved" route, that is is local.
@@ -1726,6 +1737,19 @@ static TEST_mocknetwork_config_t s_mockNetworkConfig;
17261737// Third octet = 100 means public/gateway network (127.0.100.x)
17271738const uint32 k_nMockPublicIPv4Net = (100 << 8 );
17281739
1740+ // IPv6 mock addresses use fd7f:0:X::Y. Groups [4:6] (bytes 4-5) identify the network,
1741+ // mirroring the IPv4 third-octet scheme. 0x0100 ("100" in hex) = public network.
1742+ const uint16 k_nMockPublicIPv6NetID = 0x0100 ;
1743+
1744+ // Returns the network ID from bytes [4:6] of a mock IPv6 address (fd7f:0:X::Y),
1745+ // or 0 if the address is not in the mock IPv6 range.
1746+ static inline uint16 GetMockIPv6NetID ( const uint8 *b )
1747+ {
1748+ if ( b[0 ] != 0xfd || b[1 ] != 0x7f || b[2 ] != 0x00 || b[3 ] != 0x00 )
1749+ return 0 ;
1750+ return ( uint16 (b[4 ]) << 8 ) | b[5 ];
1751+ }
1752+
17291753// Custom implementation of IRawUDPSocket that applies the appropriate routing
17301754// rules from the mocked network environment
17311755class CUDPSocketMock : public IRawUDPSocket
@@ -1782,6 +1806,29 @@ class CUDPSocketMock : public IRawUDPSocket
17821806 return SendDelayed ( m_pSockLocal, nChunks, pChunks, adrTo, ecn, m_ifaceConfig.m_nSendLatencyMS );
17831807 }
17841808 }
1809+ else if ( adrTo.GetType () == k_EIPTypeV6 )
1810+ {
1811+ if ( m_boundAddr.IsIPv4 () )
1812+ {
1813+ // Can't send to IPv6 address from an IPv4 socket
1814+ return false ;
1815+ }
1816+
1817+ const uint16 net_remote = GetMockIPv6NetID ( adrTo.GetIPV6Bytes () );
1818+ if ( net_remote == 0 )
1819+ return false ; // Not a mock IPv6 address
1820+
1821+ // Check if this is a 'public IP'; if so, route via NAT
1822+ if ( net_remote == k_nMockPublicIPv6NetID )
1823+ return const_cast <CUDPSocketMock *>(this )->BCreateNATAndSend ( nChunks, pChunks, adrTo, ecn );
1824+
1825+ const uint16 net_local = GetMockIPv6NetID ( m_ifaceConfig.m_ip .m_ipv6 );
1826+ if ( net_local == net_remote )
1827+ {
1828+ // Same LAN — send directly
1829+ return SendDelayed ( m_pSockLocal, nChunks, pChunks, adrTo, ecn, m_ifaceConfig.m_nSendLatencyMS );
1830+ }
1831+ }
17851832
17861833 // No route
17871834 return false ;
@@ -1847,9 +1894,8 @@ class CUDPSocketMock : public IRawUDPSocket
18471894 CRawUDPSocketImpl *CreateExternalSock ( CRecvPacketCallback callback )
18481895 {
18491896 Assert ( m_pGatewayConfig );
1850- Assert ( m_pGatewayConfig->m_ipv4_public .IsIPv4 () );
1851- Assert ( m_pGatewayConfig->m_ipv4_public .m_port == 0 );
1852- SteamNetworkingIPAddr addrGateway = m_pGatewayConfig->m_ipv4_public ;
1897+ Assert ( m_pGatewayConfig->m_public_ip .m_port == 0 );
1898+ SteamNetworkingIPAddr addrGateway = m_pGatewayConfig->m_public_ip ;
18531899 SteamNetworkingErrMsg errMsg;
18541900 CRawUDPSocketImpl *pSock = OpenRawUDPSocketInternal ( callback, errMsg, &addrGateway, nullptr );
18551901 if ( !pSock )
@@ -2067,24 +2113,55 @@ IRawUDPSocket *OpenRawUDPSocket( CRecvPacketCallback callback, SteamNetworkingEr
20672113 {
20682114 // Find the matching interface config by address
20692115 const TEST_mocknetwork_interface_t *pIfaceConfig = nullptr ;
2070- if ( pAddrLocal && pAddrLocal-> IsIPv4 () && pAddrLocal-> GetIPv4 () != 0 )
2116+ if ( pAddrLocal )
20712117 {
2072- uint32 nLookupIP = pAddrLocal->GetIPv4 ();
2073- for ( const TEST_mocknetwork_interface_t &iface : s_mockNetworkConfig.m_vecInterfaces )
2118+ if ( pAddrLocal->IsIPv4 () )
20742119 {
2075- if ( iface.m_ip .IsIPv4 () && iface.m_ip .GetIPv4 () == nLookupIP )
2120+ uint32 nLookupIP = pAddrLocal->GetIPv4 ();
2121+ if ( nLookupIP != 0 )
20762122 {
2077- pIfaceConfig = &iface;
2078- break ;
2123+ for ( const TEST_mocknetwork_interface_t &iface : s_mockNetworkConfig.m_vecInterfaces )
2124+ {
2125+ if ( iface.m_ip .IsIPv4 () && iface.m_ip .GetIPv4 () == nLookupIP )
2126+ {
2127+ pIfaceConfig = &iface;
2128+ break ;
2129+ }
2130+ }
2131+ if ( !pIfaceConfig )
2132+ {
2133+ V_sprintf_safe ( errMsg, " Mock: no interface configured for %s" , SteamNetworkingIPAddrRender ( *pAddrLocal ).c_str () );
2134+ return nullptr ;
2135+ }
20792136 }
20802137 }
2081- if ( !pIfaceConfig )
2138+ else
20822139 {
2083- V_sprintf_safe ( errMsg, " Mock: no interface configured for %s" , SteamNetworkingIPAddrRender ( *pAddrLocal ).c_str () );
2084- return nullptr ;
2140+ // IPv6: check if not the unspecified address (all zeros)
2141+ bool bHasAddr = false ;
2142+ for ( int i = 0 ; i < 16 ; ++i )
2143+ {
2144+ if ( pAddrLocal->m_ipv6 [i] != 0 ) { bHasAddr = true ; break ; }
2145+ }
2146+ if ( bHasAddr )
2147+ {
2148+ for ( const TEST_mocknetwork_interface_t &iface : s_mockNetworkConfig.m_vecInterfaces )
2149+ {
2150+ if ( !iface.m_ip .IsIPv4 () && memcmp ( iface.m_ip .m_ipv6 , pAddrLocal->m_ipv6 , 16 ) == 0 )
2151+ {
2152+ pIfaceConfig = &iface;
2153+ break ;
2154+ }
2155+ }
2156+ if ( !pIfaceConfig )
2157+ {
2158+ V_sprintf_safe ( errMsg, " Mock: no interface configured for %s" , SteamNetworkingIPAddrRender ( *pAddrLocal ).c_str () );
2159+ return nullptr ;
2160+ }
2161+ }
20852162 }
20862163 }
2087- else
2164+ if ( !pIfaceConfig )
20882165 {
20892166 Assert ( !s_mockNetworkConfig.m_vecInterfaces .empty () );
20902167 pIfaceConfig = &s_mockNetworkConfig.m_vecInterfaces [0 ];
@@ -2129,7 +2206,7 @@ IRawUDPSocket *OpenRawUDPSocket( CRecvPacketCallback callback, SteamNetworkingEr
21292206 if ( pAddrLocal )
21302207 *pAddrLocal = pMock->m_boundAddr ;
21312208 if ( pnAddressFamilies )
2132- *pnAddressFamilies = k_nAddressFamily_IPv4;
2209+ *pnAddressFamilies = pIfaceConfig-> m_ip . IsIPv4 () ? k_nAddressFamily_IPv4 : k_nAddressFamily_IPv6 ;
21332210
21342211 return pMock;
21352212 }
@@ -3966,10 +4043,18 @@ bool GetLocalAddresses( CUtlVector<LocalAddress_t> *pAddrs )
39664043 {
39674044 LocalAddress_t &entry = *pAddrs->AddToTailGetPtr ();
39684045 entry.m_addr = iface.m_ip ;
3969- // All mock private LANs are /24s identified by the third octet.
3970- // The public "internet" range (third octet == 100) is not a local LAN.
3971- bool bPublic = iface.m_ip .IsIPv4 () && ( ( iface.m_ip .GetIPv4 () & 0xFF00 ) == k_nMockPublicIPv4Net );
3972- entry.m_nPrefixLen = bPublic ? 0 : 24 ;
4046+ if ( iface.m_ip .IsIPv4 () )
4047+ {
4048+ // IPv4 mock private LANs are /24s; public range (third octet == 100) is not local.
4049+ bool bPublic = ( iface.m_ip .GetIPv4 () & 0xFF00 ) == k_nMockPublicIPv4Net;
4050+ entry.m_nPrefixLen = bPublic ? 0 : 24 ;
4051+ }
4052+ else
4053+ {
4054+ // IPv6 mock private LANs are /112s; public range (net ID == 0x0100) is not local.
4055+ bool bPublic = GetMockIPv6NetID ( iface.m_ip .m_ipv6 ) == k_nMockPublicIPv6NetID;
4056+ entry.m_nPrefixLen = bPublic ? 0 : 112 ;
4057+ }
39734058 }
39744059 }
39754060 return true ;
@@ -4200,7 +4285,7 @@ void TEST_mocknetwork_init( const TEST_mocknetwork_config_t &config )
42004285 case TEST_mocknetwork_nat_type::Symmetric: pszNATType = " symmetric" ; break ;
42014286 }
42024287 SpewMsg ( " Gateway[%d]: %s NAT=%s int=%dms ext=%dms\n " ,
4203- i, SteamNetworkingIPAddrRender ( gw.m_ipv4_public , false ).c_str (),
4288+ i, SteamNetworkingIPAddrRender ( gw.m_public_ip , false ).c_str (),
42044289 pszNATType, gw.m_nInternalLatencyMS , gw.m_nExternalLatencyMS );
42054290 }
42064291 for ( const TEST_mocknetwork_interface_t &iface : config.m_vecInterfaces )
0 commit comments