xbox-kernel/private/ntos/net/xnetp.cpp
2020-09-30 17:17:25 +02:00

1379 lines
44 KiB
C++

// ---------------------------------------------------------------------------------------
// xnetp.cpp
//
// Implementation of private XNet API's
//
// Copyright (C) Microsoft Corporation
// ---------------------------------------------------------------------------------------
#include "xnp.h"
#include "xnver.h"
// ---------------------------------------------------------------------------------------
// Trace Tags
// ---------------------------------------------------------------------------------------
DefineTag(NatPort, 0);
DefineTag(NatFlow, 0);
// ---------------------------------------------------------------------------------------
// Reading and Writing Configuration Sectors
// ---------------------------------------------------------------------------------------
HANDLE _WSAAPI_ XNetOpenConfigVolume()
{
HANDLE hVolume;
OBJECT_ATTRIBUTES oa;
OBJECT_STRING os;
IO_STATUS_BLOCK statusBlock;
NTSTATUS status;
RtlInitObjectString(&os, OTEXT("\\Device\\Harddisk0\\partition0"));
InitializeObjectAttributes(&oa, &os, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenFile(&hVolume, SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, &oa, &statusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
if (!NT_SUCCESS(status))
{
TraceSz1(Warning, "XNetOpenConfigVolume: NtOpenFile returned %08lX", status);
hVolume = INVALID_HANDLE_VALUE;
}
return(hVolume);
}
BOOL _WSAAPI_ XNetLoadConfigSector(HANDLE hVolume, UINT iSector, BYTE * pbData, UINT cbData)
{
BYTE abSector[XBOX_HD_SECTOR_SIZE];
PXBOX_CONFIG_SECTOR pxbcs = (PXBOX_CONFIG_SECTOR)abSector;
LARGE_INTEGER liOffset;
IO_STATUS_BLOCK statusBlock;
NTSTATUS status;
Assert(hVolume != INVALID_HANDLE_VALUE);
Assert(cbData == sizeof(pxbcs->Data));
if (iSector >= XBOX_NUM_CONFIG_SECTORS)
{
TraceSz2(Warning, "XNetSaveConfigSector (%d): iSector must be between 0 and %d",
iSector, XBOX_NUM_CONFIG_SECTORS);
return(FALSE);
}
liOffset.QuadPart = (XBOX_CONFIG_SECTOR_INDEX + iSector) * XBOX_HD_SECTOR_SIZE;
status = NtReadFile(hVolume, 0, NULL, NULL, &statusBlock, abSector, sizeof(abSector), &liOffset);
if (!NT_SUCCESS(status))
{
TraceSz2(Warning, "XNetLoadConfigSector (%d): NtReadFile returned %08lX", iSector, status);
return(FALSE);
}
if ( pxbcs->SectorBeginSignature != XBOX_CONFIG_SECTOR_BEGIN_SIGNATURE
|| pxbcs->SectorEndSignature != XBOX_CONFIG_SECTOR_END_SIGNATURE
|| pxbcs->Version < XBOX_CONFIG_VERSION
|| pxbcs->SectorCount < XBOX_CONFIG_SECTOR_COUNT)
{
return(FALSE);
}
memcpy(pbData, pxbcs->Data, sizeof(pxbcs->Data));
return(TRUE);
}
BOOL _WSAAPI_ XNetSaveConfigSector(HANDLE hVolume, UINT iSector, const BYTE * pbData, UINT cbData)
{
BYTE abSector[XBOX_HD_SECTOR_SIZE];
PXBOX_CONFIG_SECTOR pxbcs = (PXBOX_CONFIG_SECTOR)abSector;
LARGE_INTEGER liOffset;
IO_STATUS_BLOCK statusBlock;
NTSTATUS status;
Assert(hVolume != INVALID_HANDLE_VALUE);
Assert(cbData == sizeof(pxbcs->Data));
if (iSector >= XBOX_NUM_CONFIG_SECTORS)
{
TraceSz2(Warning, "XNetSaveConfigSector (%d): iSector must be between 0 and %d",
iSector, XBOX_NUM_CONFIG_SECTORS);
return(FALSE);
}
liOffset.QuadPart = (XBOX_CONFIG_SECTOR_INDEX + iSector) * XBOX_HD_SECTOR_SIZE;
pxbcs->SectorBeginSignature = XBOX_CONFIG_SECTOR_BEGIN_SIGNATURE;
pxbcs->SectorEndSignature = XBOX_CONFIG_SECTOR_END_SIGNATURE;
pxbcs->Version = XBOX_CONFIG_VERSION;
pxbcs->SectorCount = XBOX_CONFIG_SECTOR_COUNT;
memcpy(pxbcs->Data, pbData, sizeof(pxbcs->Data));
// Recompute the checksum of the configuration sector
pxbcs->Checksum = 0;
pxbcs->Checksum = ~XConfigChecksum(pxbcs, sizeof(*pxbcs));
// Write the config sector back to the disk
status = NtWriteFile(hVolume, 0, NULL, NULL, &statusBlock, abSector, sizeof(abSector), &liOffset);
if (!NT_SUCCESS(status))
{
TraceSz2(Warning, "XNetSaveConfigSector (%d): NtWriteFile returned %08lX", iSector, status);
return(FALSE);
}
return(TRUE);
}
BOOL _WSAAPI_ XNetCloseConfigVolume(HANDLE hVolume)
{
if (hVolume != INVALID_HANDLE_VALUE)
{
NtClose(hVolume);
}
return(TRUE);
}
BOOL _WSAAPI_ XNetLoadConfigParams(XNetConfigParams * pxncp)
{
HANDLE hVolume = XNetOpenConfigVolume();
if (hVolume != INVALID_HANDLE_VALUE)
{
pxncp->dwSigEnd = 0;
XNetLoadConfigSector(hVolume, 0, (BYTE *)pxncp, sizeof(XNetConfigParams));
if (pxncp->dwSigEnd != XNET_CONFIG_PARAMS_SIGEND)
{
memset(pxncp, 0, sizeof(pxncp));
pxncp->dwSigEnd = XNET_CONFIG_PARAMS_SIGEND;
}
XNetCloseConfigVolume(hVolume);
}
return(TRUE);
}
BOOL _WSAAPI_ XNetSaveConfigParams(const XNetConfigParams * pxncp)
{
BOOL fResult = FALSE;
HANDLE hVolume = XNetOpenConfigVolume();
if (hVolume != INVALID_HANDLE_VALUE)
{
Assert(pxncp->dwSigEnd == XNET_CONFIG_PARAMS_SIGEND);
fResult = XNetSaveConfigSector(hVolume, 0, (BYTE *)pxncp, sizeof(XNetConfigParams));
XNetCloseConfigVolume(hVolume);
}
return(fResult);
}
// ---------------------------------------------------------------------------------------
// XoBase Support
// ---------------------------------------------------------------------------------------
#ifdef XNET_FEATURE_ONLINE
void CXoBase::XnSetXoBase(CXoBase * pXoBase)
{
GetXn()->IpSetXoBase(pXoBase);
}
void CXoBase::XnLogon(IN_ADDR inaLogon, ULONGLONG * pqwUserId, WSAEVENT hEventLogon)
{
GetXn()->IpLogon(CIpAddr(inaLogon.s_addr), pqwUserId, hEventLogon);
}
DWORD CXoBase::XnLogonGetStatus(SGADDR * psgaddr)
{
return(GetXn()->IpLogonGetStatus(psgaddr));
}
BOOL CXoBase::XnLogonGetQFlags(UINT iUserId, ULONGLONG * pqwUserId, DWORD * pdwQFlags, DWORD * pdwSeqQFlags)
{
return(GetXn()->IpLogonGetQFlags(iUserId, pqwUserId, pdwQFlags, pdwSeqQFlags));
}
BOOL CXoBase::XnLogonSetQFlags(UINT iUserId, DWORD dwQFlagsSet, DWORD dwQFlagsClr)
{
return(GetXn()->IpLogonSetQFlags(iUserId, dwQFlagsSet, dwQFlagsClr));
}
BOOL CXoBase::XnLogonSetPState(UINT iUserId, DWORD dwPState, const XNKID * pxnkid, UINT cbData, BYTE * pbData)
{
return(GetXn()->IpLogonSetPState(iUserId, dwPState, pxnkid, cbData, pbData));
}
void CXoBase::XnLogoff()
{
GetXn()->IpLogoff();
}
void CXoBase::XnSetEventTimer(BYTE * pbEventTimer, WSAEVENT hEvent, DWORD dwTimeout)
{
GetXn()->IpSetEventTimer((CEventTimer *)pbEventTimer, hEvent, dwTimeout);
}
void CXoBase::XnRaiseToDpc(BOOL fRaise)
{
GetXn()->IpRaiseToDpc(fRaise);
}
#ifdef XNET_FEATURE_ASSERT
void * CXoBase::XnLeakAdd(CLeakInfo * pli, void * pv, UINT cb, ULONG tag)
{
return(GetXn()->LeakAdd(pli, pv, cb, tag));
}
void * CXoBase::XnLeakDel(CLeakInfo * pli, void * pv)
{
return(GetXn()->LeakDel(pli, pv));
}
void CXoBase::XnLeakTerm(CLeakInfo * pli)
{
GetXn()->LeakTerm(pli);
}
#endif
#endif
// ---------------------------------------------------------------------------------------
// Simulated Network Address Translator
// ---------------------------------------------------------------------------------------
#ifdef XNET_FEATURE_WINDOWS
#include <pshpack1.h>
struct CDhcpHdr
{
// Definitions -----------------------------------------------------------------------
#define DHCP_OP_BOOTREQUEST 1
#define DHCP_OP_BOOTREPLY 2
#define DHCP_HWTYPE_ETHERNET 1
#define DHCP_FLAGS_BROADCAST 0x0008
#define DHCP_MAGIC_COOKIE 0x63538263
#define DHCP_TYPE_DISCOVER 1
#define DHCP_TYPE_OFFER 2
#define DHCP_TYPE_REQUEST 3
#define DHCP_TYPE_DECLINE 4
#define DHCP_TYPE_ACK 5
#define DHCP_TYPE_NAK 6
#define DHCP_TYPE_RELEASE 7
#define DHCP_TYPE_INFORM 8
#define DHCP_OPT_PAD 0
#define DHCP_OPT_SUBNET_MASK 1
#define DHCP_OPT_ROUTERS 3
#define DHCP_OPT_DNS_SERVERS 6
#define DHCP_OPT_DOMAIN_NAME 15
#define DHCP_OPT_REQUESTED_IPADDR 50
#define DHCP_OPT_IPADDR_LEASE_TIME 51
#define DHCP_OPT_FIELD_OVERLOAD 52
#define DHCP_OPT_MESSAGE_TYPE 53
#define DHCP_OPT_SERVERID 54
#define DHCP_OPT_PARAM_REQUEST_LIST 55
#define DHCP_OPT_T1_INTERVAL 58
#define DHCP_OPT_T2_INTERVAL 59
#define DHCP_OPT_CLIENTID 61
#define DHCP_OPT_END 255
#define DHCP_OVERLOAD_FILE 1
#define DHCP_OVERLOAD_SERVER 2
// Functions -------------------------------------------------------------------------
BOOL GetOpt(UINT cbLen, BYTE bOpt, UINT cbOpt, BOOL fVariable, BYTE ** ppbOpt, UINT * pcbOpt);
static BOOL GetOpt(BYTE * pb, UINT cb, BYTE bOpt, UINT cbOpt, BOOL fVar, BYTE ** ppbOpt, UINT * pcbOpt, BYTE * pbOver);
// Data ------------------------------------------------------------------------------
BYTE _bOp; // DHCP_OP_*
BYTE _bHwType; // DHCP_HWTYPE_*
BYTE _bHwLen; // hardware address length
BYTE _bHops; // relay hops
DWORD _dwXid; // random transaction identifier
WORD _wSecs; // seconds since address acquisition process began
WORD _wFlags; // DHCP_FLAGS_*
CIpAddr _ipaC; // IP address requested by client
CIpAddr _ipaY; // IP address assigned to client
CIpAddr _ipaS; // IP address of DHCP server
CIpAddr _ipaG; // IP address of gateway in trans-router booting
CEnetAddr _ea; // Ethernet Address of client
BYTE _abHwPad[10]; // Pad hardware address to full 16 bytes
char _achServer[64]; // Null-terminated server hostname
char _achFile[128]; // Null-terminated name of bootfile
DWORD _dwMagicCookie; // DHCP_MAGIC_COOKIE
};
#include <poppack.h>
BOOL CDhcpHdr::GetOpt(UINT cbLen, BYTE bOpt, UINT cbOpt, BOOL fVar, BYTE ** ppbOpt, UINT * pcbOpt)
{
BYTE bOver = 0;
if (GetOpt((BYTE *)(this + 1), cbLen - sizeof(CDhcpHdr), bOpt, cbOpt, fVar, ppbOpt, pcbOpt, &bOver))
{
return(TRUE);
}
if (bOver & DHCP_OVERLOAD_FILE)
{
if (GetOpt((BYTE *)_achFile, sizeof(_achFile), bOpt, cbOpt, fVar, ppbOpt, pcbOpt, NULL))
{
return(TRUE);
}
}
if (bOver & DHCP_OVERLOAD_SERVER)
{
if (GetOpt((BYTE *)_achServer, sizeof(_achServer), bOpt, cbOpt, fVar, ppbOpt, pcbOpt, NULL))
{
return(TRUE);
}
}
return(FALSE);
}
BOOL CDhcpHdr::GetOpt(BYTE * pb, UINT cb, BYTE bOpt, UINT cbOpt, BOOL fVar, BYTE ** ppbOpt, UINT * pcbOpt, BYTE * pbOver)
{
BYTE b1, b2;
while (cb > 0)
{
b1 = *pb++;
cb -= 1;
if (b1 == DHCP_OPT_END)
break;
if (b1 == DHCP_OPT_PAD)
continue;
if (cb == 0)
{
TraceSz1(Warning, "CDhcpHdr::GetOpt(%d) - Premature end of option list", bOpt);
break;
}
b2 = *pb++;
cb -= 1;
if (b2 == 0 || b2 > cb)
{
TraceSz3(Warning, "CDhcpHdr::GetOpt(%d) - Option length (%d) is invalid (cb %d)", bOpt, b2, cb);
break;
}
if (b1 == DHCP_OPT_FIELD_OVERLOAD && pbOver && *pbOver == 0 && b2 == 1)
{
*pbOver = *pb;
}
if (b1 == bOpt)
{
if ( (!fVar && b2 != cbOpt)
|| ( fVar && (b2 < cbOpt || (b2 % cbOpt) != 0)))
{
TraceSz4(Warning, "CDhcpHdr::GetOpt(%d) - Option length (%d) is invalid (%d%s)",
bOpt, b2, cbOpt, fVar ? " variable" : "");
break;
}
*ppbOpt = pb;
if (pcbOpt)
{
*pcbOpt = b2;
}
return(TRUE);
}
pb += b2;
cb -= b2;
}
return(FALSE);
}
class CXnNat
{
// Definitions -----------------------------------------------------------------------
struct CDhcpLease
{
CEnetAddr _ea; // Ethernet address of client
CIpAddr _ipa; // IP address assigned to client
FILETIME _ftExpire; // Expiration time of the lease
};
struct CNatFilt
{
CIpAddr _ipaOuter; // IP address of server on outer lan
CIpPort _ipportOuter; // IP port of server on outer lan
DWORD _dwTick; // Tick of last packet activity through this filter
};
struct CNatPort
{
LIST_ENTRY _leHash; // Link into hash table bucket
LIST_ENTRY _leLru; // Link into LRU list
CIpAddr _ipaInner; // IP address of client on inner lan
CIpPort _ipportInner; // IP port of client on inner lan
CIpPort _ipportNat; // IP port assigned by NAT
CIpAddr _ipaOuter; // IP address of server on outer lan (aggressive)
CIpPort _ipportOuter; // IP port of server on outer lan (aggressive)
CNatFilt * _pNatFilt; // Vector of sorted CNatFilt structures
UINT _cNatFilt; // Count of CNatFilt structures in _pNatFilt in use
UINT _cNatFiltAlloc; // Count of CNatFilt structures in _pNatFilt
DWORD _dwTick; // Tick of last packet activity through this mapping
};
class CXnIpNat : public CXnIp
{
public:
HAL_DECLARE_NEW_DELETE(CXnIpNat)
BOOL Init(CXnNat * pXnNat, XNetStartupParams * pxnsp, XNetConfigParams * pxncp, char * pszXbox);
void Stop();
void Term();
void UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen);
void TcpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CTcpHdr * pTcpHdr, UINT cbHdrLen, UINT cbLen);
void SockReset(CIpAddr ipa) {}
BOOL IsGateway(CIpPort ipport);
void IpXmitIp(CPacket * ppkt) { CXnIp::IpXmitIp(ppkt); }
CIpAddr GetIpa() { return(_ipa); }
CIpAddr GetIpaMask() { return(_ipaMask); }
CIpAddr GetIpaDns(UINT iDns) { return(iDns < _options._dnsServerCount ? _options._dnsServers[iDns] : 0); }
private:
CXnNat * _pXnNat;
};
public:
HAL_DECLARE_NEW_DELETE(CXnNat)
BOOL Init(NATCREATE * pNatCreate);
void Term();
void Recv(CXnIpNat * pXnIpNat, CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen);
void RecvDhcp(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen);
void RecvInner(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen);
void RecvOuter(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen);
BOOL IsGateway(CIpPort ipport, CXnIpNat * pXnIpNat);
CNatPort * NatPortLookup(CIpAddr ipaInner, CIpPort ipportInner, CIpAddr ipaOuter, CIpPort ipportOuter);
CNatPort * NatPortLookup(CIpPort ipportNat);
BOOL NatPortExpire(CNatPort * pNatPort);
void NatPortFree(CNatPort * pNatPort);
CNatFilt * NatFiltLookup(CNatPort * pNatPort, CIpAddr ipaOuter, CIpPort ipportOuter, BOOL fAlloc);
private:
CXnIpNat * _pXnIpNatInner; // Inner network stack
CXnIpNat * _pXnIpNatOuter; // Outer network stack
DWORD _dwIpaBase; // Base IP address (host order) of DHCP server
DWORD _dwIpaLast; // Last IP address (host order) of DHCP server
DWORD _dwIpaNext; // Next IP address (host order) of DHCP server
DWORD _dwLeaseTime; // Lease time in seconds for DHCP server
CDhcpLease * _pDhcpLease; // Vector of CDhcpLease structures
UINT _cDhcpLease; // Count of CDhcpLease structures in use
UINT _cDhcpLeaseAlloc; // Count of CDhcpLease structures allocated
UINT _iAssign; // See NAT_ASSIGN_*
UINT _iFilter; // See NAT_FILTER_*
UINT _iTimeout; // NAT timeout interval in ticks
LIST_ENTRY * _pleHash; // Vector of hash buckets of CNatPort structures
UINT _cBucket; // Number of buckets in the hash table
LIST_ENTRY _leLru; // CNatPort structures in LRU order
CNatPort ** _ppNatPort; // Mapping from ipportNat to CNatPort
UINT _iNatPortBase; // Base ipportNat to assign
UINT _iNatPortLast; // Last ipportNat to assign
UINT _iNatPortNext; // Next ipportNat to assign
UINT _cNatPortFree; // Count of free ipportNat
BOOL _fInitialized; // TRUE when CXnNat::Init has completed
};
BOOL CXnNat::CXnIpNat::Init(CXnNat * pXnNat, XNetStartupParams * pxnsp, XNetConfigParams * pxncp, char * pszXbox)
{
_pXnNat = pXnNat;
XNetInitParams xnip;
xnip.pxnp = (XNetParams *)pxnsp;
xnip.pszXbox = pszXbox;
HalQueryTsc(&xnip.liTime);
NTSTATUS status = IpInit(&xnip);
if (!NT_SUCCESS(status))
{
TraceSz1(Warning, "CXnNat::CXnIpNat::Init - IpInit failed (%08lX)", status);
return(FALSE);
}
cfgFlags |= XNET_STARTUP_BYPASS_SECURITY;
INT err = IpConfig(pxncp, XNET_CONFIG_NORMAL);
if (err != 0)
{
TraceSz1(Warning, "CXnNat::CXnIpNat::Init - IpConfig failed (%08lX)", err);
return(FALSE);
}
IpStart();
return(TRUE);
}
void CXnNat::CXnIpNat::Stop()
{
if (TestInitFlag(INITF_HAL))
{
RaiseToDpc();
IpStop();
}
}
void CXnNat::CXnIpNat::Term()
{
if (TestInitFlag(INITF_HAL))
{
RaiseToDpc();
IpTerm();
}
_pXnNat = NULL;
}
void CXnNat::CXnIpNat::UdpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
_pXnNat->Recv(this, ppkt, pIpHdr, pUdpHdr, cbLen);
}
void CXnNat::CXnIpNat::TcpRecv(CPacket * ppkt, CIpHdr * pIpHdr, CTcpHdr * pTcpHdr, UINT cbHdrLen, UINT cbLen)
{
_pXnNat->Recv(this, ppkt, pIpHdr, (CUdpHdr *)pTcpHdr, cbLen);
}
BOOL CXnNat::CXnIpNat::IsGateway(CIpPort ipport)
{
return(_pXnNat->IsGateway(ipport, this));
}
BOOL CXnNat::Init(NATCREATE * pNatCreate)
{
_dwIpaBase = NTOHL(pNatCreate->_inaBase.s_addr);
_dwIpaLast = NTOHL(pNatCreate->_inaLast.s_addr);
_dwIpaNext = _dwIpaBase;
_dwLeaseTime = pNatCreate->_dwLeaseTime;
_iAssign = pNatCreate->_iAssign;
_iFilter = pNatCreate->_iFilter;
_iTimeout = (pNatCreate->_iTimeout ? pNatCreate->_iTimeout : 60) * TICKS_PER_SECOND;
_iNatPortBase = pNatCreate->_iNatPortBase ? pNatCreate->_iNatPortBase : 1000;
_iNatPortLast = _iNatPortBase + (pNatCreate->_cNatPort ? pNatCreate->_cNatPort : 1000) - 1;
_iNatPortNext = _iNatPortBase;
if (_iAssign < NAT_ASSIGN_MINIMAL && _iAssign > NAT_ASSIGN_AGGRESSIVE)
_iAssign = NAT_ASSIGN_MINIMAL;
if (_iFilter < NAT_FILTER_NONE || _iFilter > NAT_FILTER_ADDRESS_PORT)
_iFilter = NAT_FILTER_NONE;
if (_iNatPortLast > 0xFFFF)
_iNatPortLast = 0xFFFF;
_cNatPortFree = _iNatPortLast - _iNatPortBase + 1;
_cBucket = _cNatPortFree * 160 / 100;
static DWORD s_adwPrimes[] = { 37,59,89,139,227,359,577,929,1499,2423,3919,6337,10253,16573,
26821,43391,70207,113591,183797,297377,481171,778541,1259701,2038217,3297913,5336129,
8633983,13970093,22604069,36574151,59178199,95752333,154930511,250682837,405613333,
656296153,1061909479,1718205583,2780115059,0xFFFFFFFF};
DWORD * pdwPrime = s_adwPrimes;
for (; _cBucket < *pdwPrime; pdwPrime++) ;
_cBucket = *pdwPrime;
_pleHash = (LIST_ENTRY *)HalAllocZ(_cBucket * sizeof(LIST_ENTRY), PTAG_CNatPort);
_ppNatPort = (CNatPort **)HalAllocZ(_cNatPortFree * sizeof(CNatPort *), PTAG_CNatPort);
_pXnIpNatInner = new CXnIpNat;
_pXnIpNatOuter = new CXnIpNat;
if (_pleHash == NULL || _ppNatPort == NULL || _pXnIpNatInner == NULL || _pXnIpNatOuter == NULL)
{
TraceSz(Warning, "CXnNat::Init - Out of memory");
return(FALSE);
}
InitializeListHead(&_leLru);
LIST_ENTRY * ple = _pleHash;
UINT cBucket = _cBucket;
for (; cBucket > 0; --cBucket, ++ple)
{
InitializeListHead(ple);
}
if (!_pXnIpNatInner->Init(this, pNatCreate->_pxnsp, &pNatCreate->_xncpInner, pNatCreate->_achXboxInner))
{
return(FALSE);
}
if (!_pXnIpNatOuter->Init(this, pNatCreate->_pxnsp, &pNatCreate->_xncpOuter, pNatCreate->_achXboxOuter))
{
return(FALSE);
}
_fInitialized = TRUE;
return(TRUE);
}
void CXnNat::Term()
{
if (_pXnIpNatInner)
{
_pXnIpNatInner->Stop();
}
if (_pXnIpNatOuter)
{
_pXnIpNatOuter->Stop();
}
if (_pXnIpNatInner)
{
_pXnIpNatInner->Term();
delete _pXnIpNatInner;
_pXnIpNatInner = NULL;
}
if (_pXnIpNatOuter)
{
_pXnIpNatOuter->Term();
delete _pXnIpNatOuter;
_pXnIpNatOuter = NULL;
}
if (!IsListNull(&_leLru))
{
while (!IsListEmpty(&_leLru))
{
CNatPort * pNatPort = (CNatPort *)((BYTE *)_leLru.Flink - offsetof(CNatPort, _leLru));
NatPortFree(pNatPort);
}
}
if (_ppNatPort)
{
HalFree(_ppNatPort);
}
if (_pDhcpLease)
{
HalFree(_pDhcpLease);
}
}
void CXnNat::Recv(CXnIpNat * pXnIpNat, CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
if (!_fInitialized)
return;
if (pXnIpNat == _pXnIpNatOuter)
RecvOuter(ppkt, pIpHdr, pUdpHdr, cbLen);
else if (pIpHdr->_bProtocol == IPPROTOCOL_UDP && pUdpHdr->_ipportDst == DHCP_SERVER_PORT)
RecvDhcp(ppkt, pIpHdr, pUdpHdr, cbLen);
else
RecvInner(ppkt, pIpHdr, pUdpHdr, cbLen);
}
void CXnNat::RecvDhcp(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
if (cbLen < sizeof(CDhcpHdr))
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - DHCP payload is too small");
return;
}
CDhcpHdr * pDhcpHdr = (CDhcpHdr *)(pUdpHdr + 1);
if ( pDhcpHdr->_bOp != DHCP_OP_BOOTREQUEST
|| pDhcpHdr->_bHwType != DHCP_HWTYPE_ETHERNET
|| pDhcpHdr->_bHwLen != sizeof(CEnetAddr)
|| pDhcpHdr->_dwMagicCookie != DHCP_MAGIC_COOKIE)
{
TraceSz4(pktWarn, "[DISCARD] CXnNat::RecvDhcp - DHCP request is invalid (%d,%d,%d,%d)",
(pDhcpHdr->_bOp != DHCP_OP_BOOTREQUEST),
(pDhcpHdr->_bHwType != DHCP_HWTYPE_ETHERNET),
(pDhcpHdr->_bHwLen != sizeof(CEnetAddr)),
(pDhcpHdr->_dwMagicCookie != DHCP_MAGIC_COOKIE));
return;
}
BYTE * pbOpt;
UINT cbOpt;
BYTE bType;
if (!pDhcpHdr->GetOpt(cbLen, DHCP_OPT_MESSAGE_TYPE, 1, FALSE, &pbOpt, NULL))
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - DHCP_OPT_MESSAGE_TYPE not found");
return;
}
bType = *pbOpt;
if (bType != DHCP_TYPE_DISCOVER && bType != DHCP_TYPE_REQUEST)
{
TraceSz1(pktWarn, "[DISCARD] CXnNat::RecvDhcp - DHCP_OPT_MESSAGE_TYPE (%d) not supported", bType);
return;
}
if (_pXnIpNatOuter->GetIpa() == 0)
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - NAT hasn't acquired outer IP address yet");
return;
}
CDhcpLease * pDhcpLease = _pDhcpLease;
UINT cDhcpLease = _cDhcpLease;
for (; cDhcpLease > 0; ++pDhcpLease, --cDhcpLease)
{
if (pDhcpLease->_ea.IsEqual(pDhcpHdr->_ea))
break;
}
if (cDhcpLease == 0)
{
if (bType != DHCP_TYPE_DISCOVER)
{
TraceSz1(pktWarn, "[DISCARD] CXnNat::RecvDhcp - No lease found for %s", pDhcpHdr->_ea.Str());
return;
}
for (; _dwIpaNext <= _dwIpaLast; ++_dwIpaNext)
{
if (CIpAddr(HTONL(_dwIpaNext)).IsValidAddr())
break;
}
if (_dwIpaNext > _dwIpaLast)
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - Out of IP addresses");
return;
}
if (_cDhcpLease == _cDhcpLeaseAlloc)
{
UINT cDhcpLeaseAlloc = _cDhcpLeaseAlloc + 64;
CDhcpLease * pDhcpLease = (CDhcpLease *)HalAllocZ(cDhcpLeaseAlloc * sizeof(CDhcpLease), PTAG_CDhcpLease);
if (pDhcpLease == NULL)
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - Out of memory allocating CDhcpLease vector");
return;
}
if (_cDhcpLease > 0)
{
memcpy(pDhcpLease, _pDhcpLease, _cDhcpLease * sizeof(CDhcpLease));
HalFree(_pDhcpLease);
}
_pDhcpLease = pDhcpLease;
_cDhcpLeaseAlloc = cDhcpLeaseAlloc;
}
pDhcpLease = &_pDhcpLease[_cDhcpLease];
pDhcpLease->_ea = pDhcpHdr->_ea;
pDhcpLease->_ipa = HTONL(_dwIpaNext);
_dwIpaNext += 1;
_cDhcpLease += 1;
}
if (bType == DHCP_TYPE_REQUEST)
{
if (!pDhcpHdr->GetOpt(cbLen, DHCP_OPT_REQUESTED_IPADDR, sizeof(CIpAddr), FALSE, &pbOpt, NULL))
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - REQUESTED_IPADDR not supplied");
return;
}
if (*(CIpAddr *)pbOpt != pDhcpLease->_ipa)
{
TraceSz2(pktWarn, "[DISCARD] CXnNat::RecvDhcp - REQUESTED_IPADDR is %s, expecting %s",
*(CIpAddr *)pbOpt, pDhcpLease->_ipa);
return;
}
}
CPacket * ppktReply = _pXnIpNatInner->PacketAlloc(PTAG_CDhcpPacket,
PKTF_TYPE_UDP|PKTF_XMIT_INSECURE|PKTF_POOLALLOC, sizeof(CDhcpHdr) + 128);
if (ppktReply == NULL)
{
TraceSz(pktWarn, "[DISCARD] CXnNat::RecvDhcp - Out of memory allocating CDhcpPacket");
return;
}
CIpHdr * pIpHdrReply = ppktReply->GetIpHdr();
CUdpHdr * pUdpHdrReply = (CUdpHdr *)(pIpHdrReply + 1);
CDhcpHdr * pDhcpHdrReply = (CDhcpHdr *)(pUdpHdrReply + 1);
BYTE * pbOptReply = (BYTE *)(pDhcpHdrReply + 1);
pUdpHdrReply->_ipportSrc = DHCP_SERVER_PORT;
pUdpHdrReply->_ipportDst = DHCP_CLIENT_PORT;
memset(pDhcpHdrReply, 0, sizeof(CDhcpHdr));
pDhcpHdrReply->_bOp = DHCP_OP_BOOTREPLY;
pDhcpHdrReply->_bHwType = DHCP_HWTYPE_ETHERNET;
pDhcpHdrReply->_bHwLen = sizeof(CEnetAddr);
pDhcpHdrReply->_dwXid = pDhcpHdr->_dwXid;
pDhcpHdrReply->_wFlags = pDhcpHdr->_wFlags;
pDhcpHdrReply->_ipaY = pDhcpLease->_ipa;
pDhcpHdrReply->_ea = pDhcpLease->_ea;
pDhcpHdrReply->_dwMagicCookie = DHCP_MAGIC_COOKIE;
*pbOptReply++ = DHCP_OPT_MESSAGE_TYPE;
*pbOptReply++ = 1;
*pbOptReply++ = bType == DHCP_TYPE_DISCOVER ? DHCP_TYPE_OFFER : DHCP_TYPE_ACK;
*pbOptReply++ = DHCP_OPT_IPADDR_LEASE_TIME;
*pbOptReply++ = sizeof(DWORD);
*(DWORD *)pbOptReply = HTONL(_dwLeaseTime);
pbOptReply += sizeof(DWORD);
*pbOptReply++ = DHCP_OPT_SERVERID;
*pbOptReply++ = sizeof(CIpAddr);
*(CIpAddr *)pbOptReply = _pXnIpNatInner->GetIpa();
pbOptReply += sizeof(CIpAddr);
if (pDhcpHdr->GetOpt(cbLen, DHCP_OPT_PARAM_REQUEST_LIST, 1, TRUE, &pbOpt, &cbOpt))
{
for (; cbOpt > 0; ++pbOpt, --cbOpt)
{
switch (*pbOpt)
{
case DHCP_OPT_SUBNET_MASK:
*pbOptReply++ = DHCP_OPT_SUBNET_MASK;
*pbOptReply++ = sizeof(CIpAddr);
*(CIpAddr *)pbOptReply = _pXnIpNatInner->GetIpaMask();
pbOptReply += sizeof(CIpAddr);
break;
case DHCP_OPT_ROUTERS:
*pbOptReply++ = DHCP_OPT_ROUTERS;
*pbOptReply++ = sizeof(CIpAddr);
*(CIpAddr *)pbOptReply = _pXnIpNatInner->GetIpa();
pbOptReply += sizeof(CIpAddr);
break;
case DHCP_OPT_DNS_SERVERS:
{
CIpAddr * pipa = (CIpAddr *)(pbOptReply + 2);
UINT iDns;
for (iDns = 0; iDns < MAX_DEFAULT_DNSSERVERS; ++iDns)
{
CIpAddr ipaDns = _pXnIpNatOuter->GetIpaDns(iDns);
if (ipaDns == 0)
break;
*pipa++ = ipaDns;
}
if (iDns > 0)
{
*pbOptReply++ = DHCP_OPT_DNS_SERVERS;
*pbOptReply++ = iDns * sizeof(CIpAddr);
pbOptReply += iDns * sizeof(CIpAddr);
}
break;
}
}
}
}
*pbOptReply++ = DHCP_OPT_END;
ppktReply->SetCb(pbOptReply - (BYTE *)pIpHdrReply);
pUdpHdrReply->_wLen = NTOHS(pbOptReply - (BYTE *)pUdpHdrReply);
_pXnIpNatInner->IpFillAndXmit(ppktReply, pIpHdr->_ipaSrc ? pIpHdr->_ipaSrc : IPADDR_BROADCAST, IPPROTOCOL_UDP);
}
void CXnNat::RecvInner(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
if ( ppkt->TestFlags(PKTF_RECV_BROADCAST|PKTF_RECV_LOOPBACK)
|| ppkt->GetHdrOptLen() > 0
|| pIpHdr->_ipaDst == _pXnIpNatInner->GetIpa())
return;
CIpAddr ipaInner = pIpHdr->_ipaSrc;
CIpPort ipportInner = pUdpHdr->_ipportSrc;
CIpAddr ipaOuter = pIpHdr->_ipaDst;
CIpPort ipportOuter = pUdpHdr->_ipportDst;
DWORD dwTick = _pXnIpNatInner->TimerTick();
CNatPort * pNatPort = NatPortLookup(ipaInner, ipportInner, ipaOuter, ipportOuter);
if (pNatPort == NULL)
{
TraceSz5(Warning, "[DISCARD] Cannot allocate CNatPort for outbound %s [%s:%d %s:%d]",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
ipaInner.Str(), NTOHS(ipportInner), ipaOuter.Str(), NTOHS(ipportOuter));
return;
}
pNatPort->_dwTick = dwTick;
if (_iFilter)
{
CNatFilt * pNatFilt = NatFiltLookup(pNatPort, ipaOuter, ipportOuter, TRUE);
if (pNatFilt == NULL)
{
TraceSz5(Warning, "[DISCARD] Out of memory allocating CNatFilt for outbound %s [%s:%d %s:%d]",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
ipaInner.Str(), NTOHS(ipportInner), ipaOuter.Str(), NTOHS(ipportOuter));
return;
}
pNatFilt->_dwTick = dwTick;
}
// Copy the packet, change source address and port, and forward to _pXnIpNatOuter
CPacket * ppktNat = _pXnIpNatOuter->PacketAlloc(PTAG_CNatPacket,
PKTF_TYPE_IP|PKTF_POOLALLOC|PKTF_XMIT_INSECURE, ppkt->GetCb() - sizeof(CIpHdr));
if (ppktNat == NULL)
{
TraceSz5(pktWarn, "[DISCARD] Out of memory allocating CNatPacket for outbound %s [%s:%d %s:%d]",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
ipaInner.Str(), NTOHS(ipportInner), ipaOuter.Str(), NTOHS(ipportOuter));
return;
}
ppktNat->SetType((WORD)(pIpHdr->_bProtocol == IPPROTOCOL_UDP ? PKTF_TYPE_UDP : PKTF_TYPE_TCP));
CIpHdr * pIpHdrNat = ppktNat->GetIpHdr();
CUdpHdr * pUdpHdrNat = (CUdpHdr *)(pIpHdrNat + 1);
Assert(ppkt->GetCb() == ppktNat->GetCb());
memcpy(pIpHdrNat, pIpHdr, ppkt->GetCb());
pIpHdrNat->_ipaSrc = _pXnIpNatOuter->GetIpa();
pUdpHdrNat->_ipportSrc = pNatPort->_ipportNat;
TraceSz10(NatFlow, ">> %s [%s:%d %s:%d] -> [%s:%d %s:%d] (+%d)",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
pIpHdr->_ipaSrc.Str(), NTOHS(pUdpHdr->_ipportSrc),
pIpHdr->_ipaDst.Str(), NTOHS(pUdpHdr->_ipportDst),
pIpHdrNat->_ipaSrc.Str(), NTOHS(pUdpHdrNat->_ipportSrc),
pIpHdrNat->_ipaDst.Str(), NTOHS(pUdpHdrNat->_ipportDst),
cbLen);
_pXnIpNatOuter->IpXmit(ppktNat);
}
void CXnNat::RecvOuter(CPacket * ppkt, CIpHdr * pIpHdr, CUdpHdr * pUdpHdr, UINT cbLen)
{
if (ppkt->TestFlags(PKTF_RECV_BROADCAST|PKTF_RECV_LOOPBACK) || ppkt->GetHdrOptLen() > 0)
return;
CIpAddr ipaOuter = pIpHdr->_ipaSrc;
CIpPort ipportOuter = pUdpHdr->_ipportSrc;
CIpAddr ipaNat = pIpHdr->_ipaDst;
CIpPort ipportNat = pUdpHdr->_ipportDst;
DWORD dwTick = _pXnIpNatInner->TimerTick();
CNatPort * pNatPort = NatPortLookup(ipportNat);
if (pNatPort == NULL)
{
TraceSz5(NatPort, "!! No port mapping found for inbound %s [%s:%d %s:%d]",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
ipaOuter.Str(), NTOHS(ipportOuter), ipaNat.Str(), NTOHS(ipportNat));
return;
}
pNatPort->_dwTick = dwTick;
RemoveEntryList(&pNatPort->_leLru);
InsertTailList(&_leLru, &pNatPort->_leLru);
if (_iFilter)
{
CNatFilt * pNatFilt = NatFiltLookup(pNatPort, ipaOuter, ipportOuter, FALSE);
if (pNatFilt == NULL)
{
TraceSz10(NatPort, "!! %s filter discarding inbound %s [%s:%d %s:%d] -> [%s:%d %s:%d]",
_iFilter == NAT_FILTER_ADDRESS ? "Addr" : _iFilter == NAT_FILTER_PORT ? "Port" : "AddrPort",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
ipaOuter.Str(), NTOHS(ipportOuter),
ipaNat.Str(), NTOHS(ipportNat),
ipaOuter.Str(), NTOHS(ipportOuter),
pNatPort->_ipaInner.Str(), NTOHS(pNatPort->_ipportInner));
return;
}
pNatFilt->_dwTick = dwTick;
}
// Copy the packet, change source address and port, and forward to _pXnIpNatOuter
CPacket * ppktNat = _pXnIpNatInner->PacketAlloc(PTAG_CNatPacket,
PKTF_TYPE_IP|PKTF_POOLALLOC|PKTF_XMIT_INSECURE, ppkt->GetCb() - sizeof(CIpHdr));
if (ppktNat == NULL)
{
TraceSz5(pktWarn, "[DISCARD] Out of memory allocating CNatPacket for inbound %s [%s:%d %s:%d]",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
ipaOuter.Str(), NTOHS(ipportOuter), ipaNat.Str(), NTOHS(ipportNat));
return;
}
ppktNat->SetType((WORD)(pIpHdr->_bProtocol == IPPROTOCOL_UDP ? PKTF_TYPE_UDP : PKTF_TYPE_TCP));
CIpHdr * pIpHdrNat = ppktNat->GetIpHdr();
CUdpHdr * pUdpHdrNat = (CUdpHdr *)(pIpHdrNat + 1);
Assert(ppkt->GetCb() == ppktNat->GetCb());
memcpy(pIpHdrNat, pIpHdr, ppkt->GetCb());
pIpHdrNat->_ipaDst = pNatPort->_ipaInner;
pUdpHdrNat->_ipportDst = pNatPort->_ipportInner;
TraceSz10(NatFlow, "<< %s [%s:%d %s:%d] -> [%s:%d %s:%d] (+%d)",
pIpHdr->_bProtocol == IPPROTOCOL_UDP ? "UDP" : "TCP",
pIpHdr->_ipaSrc.Str(), NTOHS(pUdpHdr->_ipportSrc),
pIpHdr->_ipaDst.Str(), NTOHS(pUdpHdr->_ipportDst),
pIpHdrNat->_ipaSrc.Str(), NTOHS(pUdpHdrNat->_ipportSrc),
pIpHdrNat->_ipaDst.Str(), NTOHS(pUdpHdrNat->_ipportDst),
cbLen);
_pXnIpNatInner->IpXmitIp(ppktNat);
}
CXnNat::CNatPort * CXnNat::NatPortLookup(CIpAddr ipaInner, CIpPort ipportInner, CIpAddr ipaOuter, CIpPort ipportOuter)
{
CNatPort * pNatPort;
while (!IsListEmpty(&_leLru))
{
pNatPort = (CNatPort *)((BYTE *)_leLru.Flink - offsetof(CNatPort, _leLru));
if (!NatPortExpire(pNatPort))
break;
}
DWORD dwHash = ipaInner ^ (ipportInner << 3);
if (_iAssign == NAT_ASSIGN_AGGRESSIVE)
{
dwHash ^= (ipaOuter << 13);
dwHash ^= (ipaOuter >> 19);
dwHash ^= (ipportOuter << 7);
}
else
{
ipaOuter = 0;
ipportOuter = 0;
}
LIST_ENTRY * pleHead = &_pleHash[dwHash % _cBucket];
for (LIST_ENTRY * ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
{
pNatPort = (CNatPort *)ple;
if ( pNatPort->_ipaInner == ipaInner
&& pNatPort->_ipportInner == ipportInner
&& pNatPort->_ipaOuter == ipaOuter
&& pNatPort->_ipportOuter == ipportOuter)
{
return(pNatPort);
}
}
if (_cNatPortFree == 0)
{
return(NULL);
}
pNatPort = (CNatPort *)HalAllocZ(sizeof(CNatPort), PTAG_CNatPort);
if (pNatPort != NULL)
{
while (1)
{
if (_iNatPortNext > _iNatPortLast)
_iNatPortNext = _iNatPortBase;
if (_ppNatPort[_iNatPortNext - _iNatPortBase] == NULL)
break;
_iNatPortNext += 1;
}
pNatPort->_ipaInner = ipaInner;
pNatPort->_ipportInner = ipportInner;
pNatPort->_ipportNat = HTONS((WORD)_iNatPortNext);
pNatPort->_ipaOuter = ipaOuter;
pNatPort->_ipportOuter = ipportOuter;
pNatPort->_dwTick = _pXnIpNatInner->TimerTick();
InsertTailList(pleHead, &pNatPort->_leHash);
InsertTailList(&_leLru, &pNatPort->_leLru);
_ppNatPort[_iNatPortNext - _iNatPortBase] = pNatPort;
_cNatPortFree -= 1;
TraceSz5(NatPort, "** Port mapping %d [%s:%d %s:%d] allocated",
NTOHS(pNatPort->_ipportNat), pNatPort->_ipaInner.Str(), NTOHS(pNatPort->_ipportInner),
pNatPort->_ipaOuter.Str(), NTOHS(pNatPort->_ipportOuter));
}
return(pNatPort);
}
CXnNat::CNatPort * CXnNat::NatPortLookup(CIpPort ipportNat)
{
UINT iNatPort = NTOHS(ipportNat);
if (iNatPort < _iNatPortBase || iNatPort > _iNatPortLast)
return(NULL);
else
return(_ppNatPort[iNatPort - _iNatPortBase]);
}
BOOL CXnNat::NatPortExpire(CNatPort * pNatPort)
{
if (pNatPort->_dwTick + _iTimeout > _pXnIpNatInner->TimerTick())
{
return(FALSE);
}
TraceSz5(NatPort, "** Port mapping %d [%s:%d %s:%d] has timed out",
NTOHS(pNatPort->_ipportNat), pNatPort->_ipaInner.Str(), NTOHS(pNatPort->_ipportInner),
pNatPort->_ipaOuter.Str(), NTOHS(pNatPort->_ipportOuter));
NatPortFree(pNatPort);
return(TRUE);
}
void CXnNat::NatPortFree(CNatPort * pNatPort)
{
UINT iNatPort = NTOHS(pNatPort->_ipportNat);
TraceSz5(NatPort, "** Port mapping %d [%s:%d %s:%d] freed",
iNatPort, pNatPort->_ipaInner.Str(), NTOHS(pNatPort->_ipportInner),
pNatPort->_ipaOuter.Str(), NTOHS(pNatPort->_ipportOuter));
Assert(iNatPort >= _iNatPortBase && iNatPort <= _iNatPortLast);
Assert(_ppNatPort[iNatPort - _iNatPortBase] == pNatPort);
_ppNatPort[iNatPort - _iNatPortBase] = NULL;
RemoveEntryList(&pNatPort->_leHash);
RemoveEntryList(&pNatPort->_leLru);
if (pNatPort->_pNatFilt)
{
HalFree(pNatPort->_pNatFilt);
}
HalFree(pNatPort);
_cNatPortFree += 1;
}
CXnNat::CNatFilt * CXnNat::NatFiltLookup(CNatPort * pNatPort, CIpAddr ipaOuter, CIpPort ipportOuter, BOOL fAlloc)
{
CNatFilt * pNatFilt;
UINT idx, idxIns;
DWORD dwTimeout = _pXnIpNatInner->TimerTick() - _iTimeout;
if ((_iFilter & NAT_FILTER_ADDRESS) == 0)
{
ipaOuter = 0;
}
if ((_iFilter & NAT_FILTER_PORT) == 0)
{
ipportOuter = 0;
}
for (int iLo = 0, iHi = (int)pNatPort->_cNatFilt, iMid; iLo < iHi; )
{
iMid = (iLo + iHi) >> 1;
pNatFilt = &pNatPort->_pNatFilt[iMid];
if (pNatFilt->_ipaOuter == ipaOuter && pNatFilt->_ipportOuter == ipportOuter)
{
if (!fAlloc && pNatFilt->_dwTick <= dwTimeout)
{
return(NULL);
}
return(pNatFilt);
}
if (pNatFilt->_ipaOuter < ipaOuter || (pNatFilt->_ipaOuter == ipaOuter && pNatFilt->_ipportOuter < ipportOuter))
iLo = iMid + 1;
else
iHi = iMid;
}
idxIns = iLo;
if (!fAlloc)
{
return(NULL);
}
if (pNatPort->_cNatFilt > 0 && pNatPort->_cNatFilt == pNatPort->_cNatFiltAlloc)
{
CNatFilt * pNatFiltDst = pNatPort->_pNatFilt;
CNatFilt * pNatFiltEnd = pNatFiltDst + pNatPort->_cNatFilt;
pNatFilt = pNatFiltDst;
for (idx = 0; pNatFilt < pNatFiltEnd; ++pNatFilt)
{
if (pNatFilt->_dwTick > dwTimeout)
{
if (pNatFiltDst < pNatFilt)
{
memcpy(pNatFiltDst, pNatFilt, sizeof(CNatFilt));
TraceSz7(NatPort, "** Port filter %d [%s:%d %s:%d] <--> [%s:%d] has timed out",
NTOHS(pNatPort->_ipportNat), pNatPort->_ipaInner.Str(), NTOHS(pNatPort->_ipportInner),
pNatPort->_ipaOuter.Str(), NTOHS(pNatPort->_ipportOuter),
pNatFilt->_ipaOuter.Str(), NTOHS(pNatFilt->_ipportOuter));
}
pNatFiltDst += 1;
idx += 1;
}
else
{
pNatPort->_cNatFilt -= 1;
if (idxIns > idx)
idxIns -= 1;
}
}
}
if (pNatPort->_cNatFilt == pNatPort->_cNatFiltAlloc)
{
UINT cNatFiltAlloc = pNatPort->_cNatFiltAlloc + 8;
pNatFilt = (CNatFilt *)HalAlloc(cNatFiltAlloc * sizeof(CNatFilt), PTAG_CNatFilt);
if (pNatFilt == NULL)
{
return(NULL);
}
if (pNatPort->_cNatFilt > 0)
{
memcpy(pNatFilt, pNatPort->_pNatFilt, pNatPort->_cNatFilt * sizeof(CNatFilt));
HalFree(pNatPort->_pNatFilt);
}
pNatPort->_pNatFilt = pNatFilt;
pNatPort->_cNatFiltAlloc = cNatFiltAlloc;
}
Assert(idxIns <= pNatPort->_cNatFilt);
Assert(idxIns < pNatPort->_cNatFiltAlloc);
pNatFilt = &pNatPort->_pNatFilt[idxIns];
if (idxIns < pNatPort->_cNatFilt)
{
memmove(pNatFilt + 1, pNatFilt, (pNatPort->_cNatFilt - idxIns) * sizeof(CNatFilt));
}
pNatFilt->_ipaOuter = ipaOuter;
pNatFilt->_ipportOuter = ipportOuter;
pNatFilt->_dwTick = _pXnIpNatInner->TimerTick();
pNatPort->_cNatFilt += 1;
TraceSz7(NatPort, "** Port filter %d [%s:%d %s:%d] <--> [%s:%d] allocated",
NTOHS(pNatPort->_ipportNat), pNatPort->_ipaInner.Str(), NTOHS(pNatPort->_ipportInner),
pNatPort->_ipaOuter.Str(), NTOHS(pNatPort->_ipportOuter),
pNatFilt->_ipaOuter.Str(), NTOHS(pNatFilt->_ipportOuter));
return(pNatFilt);
}
BOOL CXnNat::IsGateway(CIpPort ipport, CXnIpNat * pXnIpNat)
{
return(pXnIpNat == _pXnIpNatInner || ipport == DNS_CLIENT_PORT);
}
// ---------------------------------------------------------------------------------------
// XNetNatCreate / XNetNatDelete
// ---------------------------------------------------------------------------------------
void * WSAAPI XNetNatCreate(NATCREATE * pNatCreate)
{
CXnNat * pXnNat = new CXnNat;
if (pXnNat == NULL)
{
TraceSz(Warning, "XNetNatCreate - Out of memory allocating CXnNat");
}
else if (!pXnNat->Init(pNatCreate))
{
XNetNatDelete(pXnNat);
pXnNat = NULL;
}
return(pXnNat);
}
void WSAAPI XNetNatDelete(void * pvNat)
{
CXnNat * pXnNat = (CXnNat *)pvNat;
pXnNat->Term();
delete pXnNat;
}
void WSAAPI XNetNatXmit(void * pvNat, void * pvPkt, BOOL fDiscard)
{
//@@@ ((CXnNat *)pvNat)->NatXmit(pvPkt, fDiscard);
}
#endif