1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-23 18:09:53 +03:00
SoftEtherVPN/src/Cedar/Virtual.c
Daiyuu Nobori ab54b73737
Merge pull request #511 from dnobori/b4_mac
Improving the compliance of Virtual Network Adapters with the local address bit of the MAC address rule.
2018-05-24 18:04:32 +09:00

10327 lines
226 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module
//
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
//
// Copyright (c) Daiyuu Nobori.
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) SoftEther Corporation.
//
// All Rights Reserved.
//
// http://www.softether.org/
//
// Author: Daiyuu Nobori, Ph.D.
// Comments: Tetsuo Sugiyama, Ph.D.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
//
//
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
//
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
//
//
// SOURCE CODE CONTRIBUTION
// ------------------------
//
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
//
//
// DEAR SECURITY EXPERTS
// ---------------------
//
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
//
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
//
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
//
// Thank you for your cooperation.
//
//
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
//
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.
// Virtual.c
// User-mode virtual host program
#include "CedarPch.h"
static UCHAR broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static char v_vgs_hostname[256] = {0};
static char secure_nat_target_hostname[MAX_SIZE] = {0};
// Specify the destination host name to be used for connectivity testing in SecureNAT
void NnSetSecureNatTargetHostname(char *name)
{
// Validate arguments
if (name == NULL)
{
return;
}
StrCpy(secure_nat_target_hostname, sizeof(secure_nat_target_hostname), name);
}
// Delete the oldest NAT session if necessary
void NnDeleteOldestNatSessionIfNecessary(NATIVE_NAT *t, UINT ip, UINT protocol)
{
UINT current_num;
UINT max_sessions = 0;
// Validate arguments
if (t == NULL)
{
return;
}
if (t->v->HubOption != NULL)
{
HUB_OPTION *o = t->v->HubOption;
switch (protocol)
{
case NAT_TCP:
max_sessions = o->SecureNAT_MaxTcpSessionsPerIp;
break;
case NAT_UDP:
max_sessions = o->SecureNAT_MaxUdpSessionsPerIp;
break;
case NAT_ICMP:
max_sessions = o->SecureNAT_MaxIcmpSessionsPerIp;
break;
}
}
if (max_sessions == 0)
{
return;
}
current_num = NnGetNumNatEntriesPerIp(t, ip, protocol);
if (current_num >= max_sessions)
{
NnDeleteOldestNatSession(t, ip, protocol);
}
}
// Delete the oldest NAT session
void NnDeleteOldestNatSession(NATIVE_NAT *t, UINT ip, UINT protocol)
{
NATIVE_NAT_ENTRY *e;
// Validate arguments
if (t == NULL)
{
return;
}
e = NnGetOldestNatEntryOfIp(t, ip, protocol);
if (e != NULL)
{
NnDeleteSession(t, e);
}
}
// Get the oldest NAT session
NATIVE_NAT_ENTRY *NnGetOldestNatEntryOfIp(NATIVE_NAT *t, UINT ip, UINT protocol)
{
UINT i;
NATIVE_NAT_ENTRY *oldest = NULL;
UINT64 oldest_tick = 0xFFFFFFFFFFFFFFFFULL;
// Validate arguments
if (t == NULL)
{
return NULL;
}
for (i = 0;i < LIST_NUM(t->NatTableForRecv->AllList);i++)
{
NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForRecv->AllList, i);
if (e->SrcIp == ip)
{
if (e->Protocol == protocol)
{
if (e->LastCommTime <= oldest_tick)
{
oldest_tick = e->LastCommTime;
oldest = e;
}
}
}
}
return oldest;
}
// Get the number of NAT sessions per IP address
UINT NnGetNumNatEntriesPerIp(NATIVE_NAT *t, UINT src_ip, UINT protocol)
{
UINT ret = 0;
UINT i;
// Validate arguments
if (t == NULL)
{
return 0;
}
for (i = 0;i < LIST_NUM(t->NatTableForRecv->AllList);i++)
{
NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForRecv->AllList, i);
if (e->SrcIp == src_ip)
{
if (e->Protocol == protocol)
{
ret++;
}
}
}
return ret;
}
// Delete the old NAT sessions
void NnDeleteOldSessions(NATIVE_NAT *t)
{
UINT i;
LIST *o;
UINT64 now;
// Validate arguments
if (t == NULL)
{
return;
}
o = NULL;
now = t->v->Now;
for (i = 0;i < LIST_NUM(t->NatTableForSend->AllList);i++)
{
NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForSend->AllList, i);
UINT64 timeout;
if (e->Status == NAT_TCP_CONNECTED || e->Status == NAT_TCP_ESTABLISHED)
{
timeout = e->LastCommTime + (UINT64)(e->Protocol == NAT_TCP ? t->v->NatTcpTimeout : t->v->NatUdpTimeout);
}
else
{
timeout = e->LastCommTime + (UINT64)NN_TIMEOUT_FOR_UNESTBALISHED_TCP;
}
if (timeout < now)
{
// Time-out occurs
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, e);
}
}
if (o != NULL)
{
for (i = 0;i < LIST_NUM(o);i++)
{
NATIVE_NAT_ENTRY *e = LIST_DATA(o, i);
NnDeleteSession(t, e);
}
ReleaseList(o);
}
}
// Delete the NAT entry
void NnDeleteSession(NATIVE_NAT *t, NATIVE_NAT_ENTRY *e)
{
// Validate arguments
if (t == NULL || e == NULL)
{
return;
}
switch (e->Protocol)
{
case NAT_TCP:
// Send a RST to the client side
SendTcp(t->v, e->DestIp, e->DestPort, e->SrcIp, e->SrcPort,
e->LastAck, e->LastSeq + (e->Status == NAT_TCP_CONNECTING ? 1 : 0), TCP_RST | TCP_ACK, 0, 0, NULL, 0);
NLog(t->v, "LH_NAT_TCP_DELETED", e->Id);
break;
case NAT_UDP:
NLog(t->v, "LH_NAT_UDP_DELETED", e->Id);
break;
case NAT_ICMP:
Debug("NAT ICMP %u Deleted.\n", e->Id);
break;
}
DeleteHash(t->NatTableForSend, e);
DeleteHash(t->NatTableForRecv, e);
Free(e);
}
// Poll the IP combining object
void NnPollingIpCombine(NATIVE_NAT *t)
{
LIST *o;
UINT i;
// Validate arguments
if (t == NULL)
{
return;
}
// Discard the old combining object
o = NULL;
for (i = 0;i < LIST_NUM(t->IpCombine);i++)
{
IP_COMBINE *c = LIST_DATA(t->IpCombine, i);
if (c->Expire < t->v->Now)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, c);
}
}
if (o != NULL)
{
for (i = 0;i < LIST_NUM(o);i++)
{
IP_COMBINE *c = LIST_DATA(o, i);
// Remove from the list
Delete(t->IpCombine, c);
// Release the memory
NnFreeIpCombine(t, c);
}
ReleaseList(o);
}
}
// Combine the IP packet received to the IP combining object
void NnCombineIp(NATIVE_NAT *t, IP_COMBINE *c, UINT offset, void *data, UINT size, bool last_packet, UCHAR *head_ip_header_data, UINT head_ip_header_size)
{
UINT i;
IP_PART *p;
UINT need_size;
UINT data_size_delta;
// Validate arguments
if (c == NULL || data == NULL)
{
return;
}
// Check the size and offset
if ((offset + size) > 65535)
{
// Do not process a packet larger than 64Kbytes
return;
}
if (last_packet == false && c->Size != 0)
{
if ((offset + size) > c->Size)
{
// Do not process a packet larger than the packet size
return;
}
}
if (head_ip_header_data != NULL && head_ip_header_size >= sizeof(IPV4_HEADER))
{
if (c->HeadIpHeaderData == NULL)
{
c->HeadIpHeaderData = Clone(head_ip_header_data, head_ip_header_size);
c->HeadIpHeaderDataSize = head_ip_header_size;
}
}
need_size = offset + size;
data_size_delta = c->DataReserved;
// Ensure sufficient if the buffer is insufficient
while (c->DataReserved < need_size)
{
c->DataReserved = c->DataReserved * 4;
c->Data = ReAlloc(c->Data, c->DataReserved);
}
data_size_delta = c->DataReserved - data_size_delta;
t->CurrentIpQuota += data_size_delta;
// Overwrite the data into the buffer
Copy(((UCHAR *)c->Data) + offset, data, size);
if (last_packet)
{
// If No More Fragment packet arrives, the size of this datagram is finalized
c->Size = offset + size;
}
// Check the overlap between the region which is represented by the offset and size of the
// existing received list and the region which is represented by the offset and size
for (i = 0;i < LIST_NUM(c->IpParts);i++)
{
UINT moving_size;
IP_PART *p = LIST_DATA(c->IpParts, i);
// Check the overlapping between the existing area and head area
if ((p->Offset <= offset) && ((p->Offset + p->Size) > offset))
{
// Compress behind the offset of this packet since a duplication is
// found in the first part with the existing packet and this packet
if ((offset + size) <= (p->Offset + p->Size))
{
// This packet is buried in the existing packet
size = 0;
}
else
{
// Retral region is not overlapped
moving_size = p->Offset + p->Size - offset;
offset += moving_size;
size -= moving_size;
}
}
if ((p->Offset < (offset + size)) && ((p->Offset + p->Size) >= (offset + size)))
{
// Compress the size of this packet forward because a duplication is
// found between the posterior portion the existing packet and this packet
moving_size = p->Offset + p->Size - offset - size;
size -= moving_size;
}
if ((p->Offset >= offset) && ((p->Offset + p->Size) <= (offset + size)))
{
// This packet was overwritten to completely hunched over a existing packet
p->Size = 0;
}
}
if (size != 0)
{
// Register this packet
p = ZeroMalloc(sizeof(IP_PART));
p->Offset = offset;
p->Size = size;
Add(c->IpParts, p);
}
if (c->Size != 0)
{
// Get the total size of the data portion list already received
UINT total_size = 0;
UINT i;
for (i = 0;i < LIST_NUM(c->IpParts);i++)
{
IP_PART *p = LIST_DATA(c->IpParts, i);
total_size += p->Size;
}
if (total_size == c->Size)
{
// Received whole of the IP packet
//Debug("Combine: %u\n", total_size);
NnIpReceived(t, c->SrcIP, c->DestIP, c->Protocol, c->Data, c->Size, c->Ttl,
c->HeadIpHeaderData, c->HeadIpHeaderDataSize, c->MaxL3Size);
// Release the combining object
NnFreeIpCombine(t, c);
// Remove from the combining object list
Delete(t->IpCombine, c);
}
}
}
// Release the IP combining object
void NnFreeIpCombine(NATIVE_NAT *t, IP_COMBINE *c)
{
UINT i;
// Validate arguments
if (c == NULL)
{
return;
}
// Release the data
t->CurrentIpQuota -= c->DataReserved;
Free(c->Data);
// Release the partial list
for (i = 0;i < LIST_NUM(c->IpParts);i++)
{
IP_PART *p = LIST_DATA(c->IpParts, i);
Free(p);
}
Free(c->HeadIpHeaderData);
ReleaseList(c->IpParts);
Free(c);
}
// Search the IP combining list
IP_COMBINE *NnSearchIpCombine(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol)
{
IP_COMBINE *c, tt;
// Validate arguments
if (t == NULL)
{
return NULL;
}
tt.DestIP = dest_ip;
tt.SrcIP = src_ip;
tt.Id = id;
tt.Protocol = protocol;
c = Search(t->IpCombine, &tt);
return c;
}
// Insert by creating a new object to the IP combining list
IP_COMBINE *NnInsertIpCombine(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol, bool mac_broadcast, UCHAR ttl, bool src_is_localmac)
{
IP_COMBINE *c;
// Validate arguments
if (t == NULL)
{
return NULL;
}
// Examine the quota
if ((t->CurrentIpQuota + IP_COMBINE_INITIAL_BUF_SIZE) > IP_COMBINE_WAIT_QUEUE_SIZE_QUOTA)
{
// IP packet can not be stored any more
return NULL;
}
c = ZeroMalloc(sizeof(IP_COMBINE));
c->SrcIsLocalMacAddr = src_is_localmac;
c->DestIP = dest_ip;
c->SrcIP = src_ip;
c->Id = id;
c->Expire = t->v->Now + (UINT64)IP_COMBINE_TIMEOUT;
c->Size = 0;
c->IpParts = NewList(NULL);
c->Protocol = protocol;
c->MacBroadcast = mac_broadcast;
c->Ttl = ttl;
// Secure the memory
c->DataReserved = IP_COMBINE_INITIAL_BUF_SIZE;
c->Data = Malloc(c->DataReserved);
t->CurrentIpQuota += c->DataReserved;
Insert(t->IpCombine, c);
return c;
}
// Initialize the IP combining list
void NnInitIpCombineList(NATIVE_NAT *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
t->IpCombine = NewList(CompareIpCombine);
}
// Release the IP combining list
void NnFreeIpCombineList(NATIVE_NAT *t)
{
UINT i;
// Validate arguments
if (t == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(t->IpCombine);i++)
{
IP_COMBINE *c = LIST_DATA(t->IpCombine, i);
NnFreeIpCombine(t, c);
}
ReleaseList(t->IpCombine);
}
// A TCP packet is received
void NnTcpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, UINT max_l3_size)
{
TCP_HEADER *tcp;
UCHAR *payload;
UINT payload_size;
UINT tcp_header_size;
// Validate arguments
if (t == NULL || data == NULL)
{
return;
}
// TCP header
if (size < sizeof(TCP_HEADER))
{
return;
}
tcp = (TCP_HEADER *)data;
// Get the TCP header size
tcp_header_size = TCP_GET_HEADER_SIZE(tcp) * 4;
if (size < tcp_header_size || tcp_header_size < sizeof(TCP_HEADER))
{
return;
}
// Payload
payload = ((UCHAR *)data) + tcp_header_size;
payload_size = size - tcp_header_size;
// Search the port from the NAT table
if (true)
{
NATIVE_NAT_ENTRY tt;
NATIVE_NAT_ENTRY *e;
NnSetNat(&tt, NAT_TCP, 0, 0, src_ip, Endian16(tcp->SrcPort), dest_ip, Endian16(tcp->DstPort));
e = SearchHash(t->NatTableForRecv, &tt);
if (e != NULL)
{
// Last communication time
e->LastCommTime = t->v->Now;
e->TotalRecv += (UINT64)size;
// Rewrite the TCP header
tcp->Checksum = 0;
tcp->DstPort = Endian16(e->SrcPort);
if (tcp->Flag & TCP_FIN || tcp->Flag & TCP_RST)
{
// Disconnect
e->Status = NAT_TCP_WAIT_DISCONNECT;
}
if (tcp->Flag & TCP_SYN && tcp->Flag & TCP_ACK)
{
// Connection complete
if (e->Status != NAT_TCP_WAIT_DISCONNECT)
{
e->Status = NAT_TCP_ESTABLISHED;
}
}
e->LastSeq = Endian32(tcp->AckNumber);
e->LastAck = Endian32(tcp->SeqNumber);
// Checksum recalculation
tcp->Checksum = CalcChecksumForIPv4(src_ip, e->SrcIp, IP_PROTO_TCP, tcp, size, 0);
// IP transmission
SendIp(t->v, e->SrcIp, src_ip, IP_PROTO_TCP, tcp, size);
}
}
}
// An ICMP packet has been received
void NnIcmpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, UINT max_l3_size)
{
ICMP_HEADER *icmp;
// Validate arguments
if (t == NULL || data == NULL)
{
return;
}
if (ttl == 0)
{
ttl = 1;
}
// ICMP header
if (size < sizeof(ICMP_HEADER))
{
return;
}
icmp = (ICMP_HEADER *)data;
if (icmp->Type == ICMP_TYPE_ECHO_RESPONSE)
{
UCHAR *payload;
UINT payload_size;
ICMP_ECHO *echo;
NATIVE_NAT_ENTRY tt, *e;
// Echo Response
echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER));
if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
{
return;
}
payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO);
payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
// Search the NAT
NnSetNat(&tt, NAT_ICMP, 0, 0, 0, 0, dest_ip, Endian16(echo->Identifier));
e = SearchHash(t->NatTableForRecv, &tt);
if (e != NULL)
{
// Rewrite the header
icmp->Checksum = 0;
echo->Identifier = Endian16(e->SrcPort);
icmp->Checksum = IpChecksum(icmp, size);
e->LastCommTime = t->v->Now;
e->TotalRecv += (UINT64)size;
// Transmission
SendIpEx(t->v, e->SrcIp, src_ip, IP_PROTO_ICMPV4, icmp, size, MAX(ttl - 1, 1));
}
}
else if (icmp->Type == ICMP_TYPE_ECHO_REQUEST)
{
UCHAR *payload;
UINT payload_size;
ICMP_ECHO *echo;
// Echo Response
echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER));
if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
{
return;
}
payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO);
payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
if (dest_ip == t->PublicIP)
{
// Respond as soon as the Echo Request is received at the public side interface
ICMP_HEADER *ret_icmp;
ICMP_ECHO *ret_echo;
UINT ret_size = sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + payload_size;
ret_icmp = ZeroMalloc(ret_size);
ret_echo = (ICMP_ECHO *)(((UCHAR *)ret_icmp) + sizeof(ICMP_HEADER));
ret_icmp->Type = ICMP_TYPE_ECHO_RESPONSE;
ret_icmp->Code = icmp->Code;
ret_echo->Identifier = echo->Identifier;
ret_echo->SeqNo = echo->SeqNo;
Copy((UCHAR *)ret_icmp + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO),
payload, payload_size);
ret_icmp->Checksum = IpChecksum(ret_icmp, ret_size);
NnIpSendForInternet(t, IP_PROTO_ICMPV4, 0, dest_ip, src_ip, ret_icmp, ret_size, max_l3_size);
Free(ret_icmp);
}
}
else
{
if (icmp->Type == ICMP_TYPE_DESTINATION_UNREACHABLE || icmp->Type == ICMP_TYPE_TIME_EXCEEDED)
{
// Rewrite the Src IP of the IPv4 header of the ICMP response packet
if (size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + sizeof(IPV4_HEADER)))
{
IPV4_HEADER *orig_ipv4 = (IPV4_HEADER *)(((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
UINT orig_ipv4_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
UINT orig_ipv4_header_size = GetIpHeaderSize((UCHAR *)orig_ipv4, orig_ipv4_size);
if (orig_ipv4_header_size >= sizeof(IPV4_HEADER) && orig_ipv4_size >= orig_ipv4_header_size)
{
if (orig_ipv4->Protocol == IP_PROTO_ICMPV4)
{
// Search the inner ICMP header
UINT inner_icmp_size = orig_ipv4_size - orig_ipv4_header_size;
if (inner_icmp_size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
{
ICMP_HEADER *inner_icmp = (ICMP_HEADER *)(((UCHAR *)data) +
sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + orig_ipv4_header_size);
if (inner_icmp->Type == ICMP_TYPE_ECHO_REQUEST)
{
ICMP_ECHO *inner_echo = (ICMP_ECHO *)(((UCHAR *)inner_icmp) + sizeof(ICMP_HEADER));
NATIVE_NAT_ENTRY tt, *e;
// Search for the existing NAT table entry
NnSetNat(&tt, NAT_ICMP, 0, 0, 0, 0, orig_ipv4->SrcIP, Endian16(inner_echo->Identifier));
e = SearchHash(t->NatTableForRecv, &tt);
if (e != NULL)
{
e->LastCommTime = t->v->Now;
// Rewrite the inner IP packet and the ICMP header according to the NAT table
inner_echo->Identifier = Endian16(e->SrcPort);
inner_icmp->Checksum = 0;
orig_ipv4->SrcIP = e->SrcIp;
orig_ipv4->Checksum = 0;
orig_ipv4->Checksum = IpChecksum(orig_ipv4, orig_ipv4_header_size);
// Rewrite the outer ICMP header
if (true)
{
UCHAR *payload;
UINT payload_size;
ICMP_ECHO *echo;
// Echo Response
echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER));
if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
{
return;
}
payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO);
payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
// Rewrite the header
icmp->Checksum = 0;
echo->Identifier = Endian16(e->SrcPort);
icmp->Checksum = IpChecksum(icmp, size);
// Transmission
SendIpEx(t->v, e->SrcIp, src_ip, IP_PROTO_ICMPV4, icmp, size, MAX(ttl - 1, 1));
}
}
}
}
}
}
}
}
}
}
// An UDP packet has been received
void NnUdpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, UINT max_l3_size)
{
UDP_HEADER *udp;
UCHAR *payload;
UINT payload_size;
// Validate arguments
if (t == NULL || data == NULL)
{
return;
}
// UDP header
if (size <= sizeof(UDP_HEADER))
{
return;
}
udp = (UDP_HEADER *)data;
// Payload
payload = ((UCHAR *)data) + sizeof(UDP_HEADER);
payload_size = size - sizeof(UDP_HEADER);
// Inspect the payload size
if (payload_size < (Endian16(udp->PacketLength) - sizeof(UDP_HEADER)))
{
return;
}
// Truncate the payload
payload_size = Endian16(udp->PacketLength) - sizeof(UDP_HEADER);
// Search the port number from the NAT table
if (true)
{
NATIVE_NAT_ENTRY tt;
NATIVE_NAT_ENTRY *e;
NnSetNat(&tt, NAT_UDP, 0, 0, 0, 0, dest_ip, Endian16(udp->DstPort));
e = SearchHash(t->NatTableForRecv, &tt);
if (e != NULL)
{
// Last communication time
e->LastCommTime = t->v->Now;
e->TotalRecv += (UINT64)payload_size;
// Deliver to the client by rewriting the port number
SendUdp(t->v, e->SrcIp, e->SrcPort, src_ip, Endian16(udp->SrcPort),
payload, payload_size);
}
}
}
// A combined IP packet is received
void NnIpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, UINT protocol, void *data, UINT size,
UCHAR ttl, UCHAR *ip_header, UINT ip_header_size, UINT max_l3_size)
{
// Validate arguments
if (t == NULL || data == NULL)
{
return;
}
if (dest_ip != t->PublicIP)
{
// Destination IP is not a unicast
return;
}
switch (protocol)
{
case IP_PROTO_UDP:
// UDP
NnUdpReceived(t, src_ip, dest_ip, data, size, ttl, max_l3_size);
break;
case IP_PROTO_TCP:
// TCP
NnTcpReceived(t, src_ip, dest_ip, data, size, ttl, max_l3_size);
break;
case IP_PROTO_ICMPV4:
// ICMP
NnIcmpReceived(t, src_ip, dest_ip, data, size, ttl, max_l3_size);
break;
}
}
// Received an IP packet
void NnFragmentedIpReceived(NATIVE_NAT *t, PKT *packet)
{
IPV4_HEADER *ip;
void *data;
UINT data_size_recved;
UINT size;
UINT ipv4_header_size;
bool last_packet = false;
UINT l3_size = 0;
UCHAR *head_ip_header_data = NULL;
UINT head_ip_header_size = 0;
// Validate arguments
if (t == NULL || packet == NULL)
{
return;
}
ip = packet->L3.IPv4Header;
// Get the size of the IPv4 header
ipv4_header_size = IPV4_GET_HEADER_LEN(packet->L3.IPv4Header) * 4;
head_ip_header_size = ipv4_header_size;
// Get the pointer to the data
data = ((UCHAR *)packet->L3.PointerL3) + ipv4_header_size;
// Get the data size
size = l3_size = Endian16(ip->TotalLength);
if (size <= ipv4_header_size)
{
// There is no data
return;
}
size -= ipv4_header_size;
// Get the size of data actually received
data_size_recved = packet->PacketSize - (ipv4_header_size + MAC_HEADER_SIZE);
if (data_size_recved < size)
{
// Data insufficient (It may be missing on the way)
return;
}
if (IPV4_GET_OFFSET(ip) == 0 && (IPV4_GET_FLAGS(ip) & 0x01) == 0)
{
// Because this packet has not been fragmented, it can be passed to the upper layer immediately
head_ip_header_data = (UCHAR *)packet->L3.IPv4Header;
NnIpReceived(t, ip->SrcIP, ip->DstIP, ip->Protocol, data, size, ip->TimeToLive,
head_ip_header_data, head_ip_header_size, l3_size);
}
else
{
// This packet is necessary to combine because it is fragmented
UINT offset = IPV4_GET_OFFSET(ip) * 8;
IP_COMBINE *c = NnSearchIpCombine(t, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol);
if (offset == 0)
{
head_ip_header_data = (UCHAR *)packet->L3.IPv4Header;
}
last_packet = ((IPV4_GET_FLAGS(ip) & 0x01) == 0 ? true : false);
if (c != NULL)
{
// It is the second or subsequent packet
c->MaxL3Size = MAX(c->MaxL3Size, l3_size);
NnCombineIp(t, c, offset, data, size, last_packet, head_ip_header_data, head_ip_header_size);
}
else
{
// Create a combining object because it is the first packet
c = NnInsertIpCombine(
t, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol, packet->BroadcastPacket,
ip->TimeToLive, false);
c->MaxL3Size = MAX(c->MaxL3Size, l3_size);
if (c != NULL)
{
NnCombineIp(t, c, offset, data, size, last_packet, head_ip_header_data, head_ip_header_size);
}
}
}
}
// Layer 2 packet processing
void NnLayer2(NATIVE_NAT *t, PKT *packet)
{
// Validate arguments
if (t == NULL || packet == NULL)
{
return;
}
if (packet->TypeL3 == L3_IPV4)
{
// IPv4
NnFragmentedIpReceived(t, packet);
}
}
// Extract the received packets of native NAT, and deliver it to the VPN client
void NnPoll(NATIVE_NAT *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
LockQueue(t->RecvQueue);
{
while (true)
{
PKT *pkt = GetNext(t->RecvQueue);
if (pkt == NULL)
{
break;
}
NnLayer2(t, pkt);
FreePacketWithData(pkt);
}
}
UnlockQueue(t->RecvQueue);
if (t->SendStateChanged)
{
TUBE *halt_tube = NULL;
Lock(t->Lock);
{
if (t->HaltTube != NULL)
{
halt_tube = t->HaltTube;
AddRef(halt_tube->Ref);
}
}
Unlock(t->Lock);
if (halt_tube != NULL)
{
TubeFlushEx(halt_tube, true);
t->SendStateChanged = false;
ReleaseTube(halt_tube);
}
}
NnPollingIpCombine(t);
NnDeleteOldSessions(t);
}
// Send a fragmented IP packet to the Internet
void NnIpSendFragmentedForInternet(NATIVE_NAT *t, UCHAR ip_protocol, UINT src_ip, UINT dest_ip, USHORT id, USHORT total_size,
USHORT offset, void *data, UINT size, UCHAR ttl)
{
UCHAR *buf;
IPV4_HEADER *ip;
BLOCK *b;
// Validate arguments
if (t == NULL || data == NULL)
{
return;
}
// Memory allocation
buf = Malloc(size + IP_HEADER_SIZE);
ip = (IPV4_HEADER *)&buf[0];
// IP header construction
ip->VersionAndHeaderLength = 0;
IPV4_SET_VERSION(ip, 4);
IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4));
ip->TypeOfService = DEFAULT_IP_TOS;
ip->TotalLength = Endian16((USHORT)(size + IP_HEADER_SIZE));
ip->Identification = Endian16(id);
ip->FlagsAndFragmentOffset[0] = ip->FlagsAndFragmentOffset[1] = 0;
IPV4_SET_OFFSET(ip, (offset / 8));
if ((offset + size) >= total_size)
{
IPV4_SET_FLAGS(ip, 0x00);
}
else
{
IPV4_SET_FLAGS(ip, 0x01);
}
ip->TimeToLive = (ttl == 0 ? DEFAULT_IP_TTL : ttl);
ip->Protocol = ip_protocol;
ip->Checksum = 0;
ip->SrcIP = src_ip;
ip->DstIP = dest_ip;
// Checksum calculation
ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE);
// Data copy
Copy(buf + IP_HEADER_SIZE, data, size);
// Transmission
b = NewBlock(buf, size + IP_HEADER_SIZE, 0);
LockQueue(t->SendQueue);
{
if (t->SendQueue->num_item <= NN_MAX_QUEUE_LENGTH)
{
InsertQueue(t->SendQueue, b);
t->SendStateChanged = true;
}
else
{
FreeBlock(b);
}
}
UnlockQueue(t->SendQueue);
}
// Send an IP packet to the Internet
void NnIpSendForInternet(NATIVE_NAT *t, UCHAR ip_protocol, UCHAR ttl, UINT src_ip, UINT dest_ip, void *data, UINT size, UINT max_l3_size)
{
UINT mss = 0;
UCHAR *buf;
USHORT offset;
USHORT id;
USHORT total_size;
UINT size_of_this_packet;
// Validate arguments
if (t == NULL || data == NULL)
{
return;
}
// Maximum segment size
if (max_l3_size > IP_HEADER_SIZE)
{
mss = max_l3_size - IP_HEADER_SIZE;
}
if (mss == 0)
{
mss = t->v->IpMss;
}
mss = MAX(mss, 1000);
// Buffer
buf = (UCHAR *)data;
// ID
id = (t->NextId++);
// Total size
total_size = (USHORT)size;
// Start to fragment
offset = 0;
while (true)
{
bool last_packet = false;
// Get the size of this packet
size_of_this_packet = MIN((USHORT)mss, (total_size - offset));
if ((offset + (USHORT)size_of_this_packet) == total_size)
{
last_packet = true;
}
// Transmit the fragmented packet
NnIpSendFragmentedForInternet(t, ip_protocol, src_ip, dest_ip, id, total_size, offset,
buf + offset, size_of_this_packet, ttl);
if (last_packet)
{
break;
}
offset += (USHORT)size_of_this_packet;
}
}
// Communication of ICMP towards the Internet
void NnIcmpEchoRecvForInternet(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, void *icmp_data, UINT icmp_size, UCHAR *ip_header, UINT ip_header_size, UINT max_l3_size)
{
NATIVE_NAT_ENTRY tt;
NATIVE_NAT_ENTRY *e;
NATIVE_NAT *t;
USHORT src_port;
ICMP_HEADER *old_icmp_header;
ICMP_ECHO *old_icmp_echo;
ICMP_HEADER *icmp;
ICMP_ECHO *echo;
UCHAR *payload_data;
UINT payload_size;
// Validate arguments
if (NnIsActive(v) == false || icmp_data == NULL)
{
return;
}
t = v->NativeNat;
old_icmp_header = (ICMP_HEADER *)icmp_data;
old_icmp_echo = (ICMP_ECHO *)(((UCHAR *)icmp_data) + sizeof(ICMP_HEADER));
if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
{
return;
}
payload_data = ((UCHAR *)icmp_data) + (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
payload_size = icmp_size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
if (dest_ip == v->HostIP)
{
// Respond because it is addressed to me
VirtualIcmpEchoSendResponse(v, dest_ip, src_ip, Endian16(old_icmp_echo->Identifier),
Endian16(old_icmp_echo->SeqNo), payload_data, payload_size);
return;
}
if (ttl <= 1)
{
// Reply the Time Exceeded immediately for the packet whose TTL is 1
UINT reply_size = sizeof(ICMP_HEADER) + 4 + ip_header_size + 8;
UCHAR *reply_data = ZeroMalloc(reply_size);
ICMP_HEADER *icmp = (ICMP_HEADER *)reply_data;
icmp->Type = ICMP_TYPE_TIME_EXCEEDED;
icmp->Code = ICMP_CODE_TTL_EXCEEDED_IN_TRANSIT;
Copy(reply_data + sizeof(ICMP_HEADER) + 4, ip_header, ip_header_size);
Copy(reply_data + sizeof(ICMP_HEADER) + 4 + ip_header_size, icmp_data, MIN(icmp_size, 8));
icmp->Checksum = IpChecksum(icmp, reply_size);
SendIp(v, src_ip, v->HostIP, IP_PROTO_ICMPV4, reply_data, reply_size);
Free(reply_data);
return;
}
src_port = Endian16(old_icmp_echo->Identifier);
// Search whether there is an existing session
NnSetNat(&tt, NAT_ICMP, src_ip, src_port, 0, 0, 0, 0);
e = SearchHash(t->NatTableForSend, &tt);
if (e == NULL)
{
// Create a new session because there is no existing one
UINT public_port;
if (CanCreateNewNatEntry(v) == false)
{
// Can not make any more
return;
}
NnDeleteOldestNatSessionIfNecessary(t, src_ip, NAT_ICMP);
// Get a free port
public_port = NnMapNewPublicPort(t, NAT_ICMP, 0, 0, t->PublicIP);
if (public_port == 0)
{
// There are no free ports
return;
}
e = ZeroMalloc(sizeof(NATIVE_NAT_ENTRY));
e->Status = NAT_TCP_ESTABLISHED;
e->HashCodeForSend = INFINITE;
e->HashCodeForRecv = INFINITE;
e->Id = Inc(v->Counter);
e->Protocol = NAT_ICMP;
e->SrcIp = src_ip;
e->SrcPort = src_port;
e->DestIp = 0;
e->DestPort = 0;
e->PublicIp = t->PublicIP;
e->PublicPort = public_port;
e->CreatedTime = v->Now;
e->LastCommTime = v->Now;
// Add to the list
AddHash(t->NatTableForSend, e);
AddHash(t->NatTableForRecv, e);
// Log
if (true)
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
Debug("ICMP Session %u: %s:0x%x -> %s:0x%x\n", e->Id, s1, src_port, s2, public_port);
}
}
// Rebuild the ICMP header
icmp = ZeroMalloc(sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + payload_size);
icmp->Code = old_icmp_header->Code;
icmp->Type = old_icmp_header->Type;
icmp->Checksum = 0;
echo = (ICMP_ECHO *)(((UCHAR *)icmp) + sizeof(ICMP_HEADER));
echo->SeqNo = old_icmp_echo->SeqNo;
echo->Identifier = Endian16(e->PublicPort);
Copy(((UCHAR *)icmp) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO), payload_data, payload_size);
icmp->Checksum = IpChecksum(icmp, sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + payload_size);
e->TotalSent += (UINT64)payload_size;
e->LastCommTime = v->Now;
// Send to the Internet
NnIpSendForInternet(t, IP_PROTO_ICMPV4, ttl - 1, e->PublicIp, dest_ip, icmp, sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + payload_size, max_l3_size);
Free(icmp);
}
// Communication of UDP towards the Internet
void NnUdpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, UINT max_l3_size)
{
NATIVE_NAT_ENTRY tt;
NATIVE_NAT_ENTRY *e;
NATIVE_NAT *t;
UDP_HEADER *udp;
// Validate arguments
if (NnIsActive(v) == false || data == NULL)
{
return;
}
t = v->NativeNat;
// Search whether there is an existing session
NnSetNat(&tt, NAT_UDP, src_ip, src_port, 0, 0, 0, 0);
e = SearchHash(t->NatTableForSend, &tt);
if (e == NULL)
{
// Create a new session because there is no existing one
UINT public_port;
if (CanCreateNewNatEntry(v) == false)
{
// Can not make any more
return;
}
NnDeleteOldestNatSessionIfNecessary(t, src_ip, NAT_UDP);
// Get a free port
public_port = NnMapNewPublicPort(t, NAT_UDP, 0, 0, t->PublicIP);
if (public_port == 0)
{
// There are no free ports
return;
}
e = ZeroMalloc(sizeof(NATIVE_NAT_ENTRY));
e->Status = NAT_TCP_ESTABLISHED;
e->HashCodeForSend = INFINITE;
e->HashCodeForRecv = INFINITE;
e->Id = Inc(v->Counter);
e->Protocol = NAT_UDP;
e->SrcIp = src_ip;
e->SrcPort = src_port;
e->DestIp = 0;
e->DestPort = 0;
e->PublicIp = t->PublicIP;
e->PublicPort = public_port;
e->CreatedTime = v->Now;
e->LastCommTime = v->Now;
// Add to the list
AddHash(t->NatTableForSend, e);
AddHash(t->NatTableForRecv, e);
// Log
if (true)
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
NLog(v, "LH_NAT_UDP_CREATED", e->Id, s1, src_port, s2, dest_port);
}
}
// Rebuild the UDP header
udp = ZeroMalloc(sizeof(UDP_HEADER) + size);
udp->SrcPort = Endian16(e->PublicPort);
udp->DstPort = Endian16(dest_port);
udp->PacketLength = Endian16((USHORT)sizeof(UDP_HEADER) + size);
Copy(((UCHAR *)udp) + sizeof(UDP_HEADER), data, size);
udp->Checksum = CalcChecksumForIPv4(e->PublicIp, dest_ip, IP_PROTO_UDP, udp, sizeof(UDP_HEADER) + size, 0);
e->TotalSent += (UINT64)size;
e->LastCommTime = v->Now;
// Send to the Internet
NnIpSendForInternet(t, IP_PROTO_UDP, 127, e->PublicIp, dest_ip, udp, sizeof(UDP_HEADER) + size, max_l3_size);
Free(udp);
}
// Communication of TCP towards the Internet
void NnTcpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, TCP_HEADER *old_tcp, void *data, UINT size, UINT max_l3_size)
{
NATIVE_NAT_ENTRY tt;
NATIVE_NAT_ENTRY *e;
NATIVE_NAT *t;
UINT tcp_header_size;
TCP_HEADER *tcp;
// Validate arguments
if (NnIsActive(v) == false || old_tcp == NULL || data == NULL)
{
return;
}
t = v->NativeNat;
// Search whether there is an existing session
NnSetNat(&tt, NAT_TCP, src_ip, src_port, dest_ip, dest_port, 0, 0);
e = SearchHash(t->NatTableForSend, &tt);
if (e == NULL)
{
// Create a new session because there is no existing one
UINT public_port;
if (old_tcp->Flag != TCP_SYN)
{
// If there is no existing session, pass through only for SYN packet
return;
}
if (CanCreateNewNatEntry(v) == false)
{
// Can not make any more
return;
}
NnDeleteOldestNatSessionIfNecessary(t, src_ip, NAT_TCP);
// Get a free port
public_port = NnMapNewPublicPort(t, NAT_TCP, dest_ip, dest_port, t->PublicIP);
if (public_port == 0)
{
// There are no free ports
return;
}
e = ZeroMalloc(sizeof(NATIVE_NAT_ENTRY));
e->HashCodeForSend = INFINITE;
e->HashCodeForRecv = INFINITE;
e->Id = Inc(v->Counter);
e->Status = NAT_TCP_CONNECTING;
e->Protocol = NAT_TCP;
e->SrcIp = src_ip;
e->SrcPort = src_port;
e->DestIp = dest_ip;
e->DestPort = dest_port;
e->PublicIp = t->PublicIP;
e->PublicPort = public_port;
e->CreatedTime = v->Now;
e->LastCommTime = v->Now;
// Add to the list
AddHash(t->NatTableForSend, e);
AddHash(t->NatTableForRecv, e);
// Log
if (true)
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
NLog(v, "LH_NAT_TCP_CREATED", e->Id, s1, src_port, s2, dest_port);
}
}
// Update the last communication time
e->LastCommTime = v->Now;
e->TotalSent += (UINT64)size;
tcp_header_size = TCP_GET_HEADER_SIZE(old_tcp) * 4;
// Create a new TCP packet
tcp = ZeroMalloc(tcp_header_size + size);
// Copy the old TCP header
Copy(tcp, old_tcp, tcp_header_size);
if (tcp->Flag & TCP_RST || tcp->Flag & TCP_FIN)
{
// Disconnect
e->Status = NAT_TCP_WAIT_DISCONNECT;
}
// Rewrite the TCP header
tcp->Checksum = 0;
tcp->SrcPort = Endian16(e->PublicPort);
e->LastSeq = Endian32(tcp->SeqNumber);
e->LastAck = Endian32(tcp->AckNumber);
// Payload
Copy(((UCHAR *)tcp) + tcp_header_size, data, size);
// Checksum calculation
tcp->Checksum = CalcChecksumForIPv4(e->PublicIp, dest_ip, IP_PROTO_TCP, tcp, tcp_header_size + size, 0);
// Send to the Internet
NnIpSendForInternet(t, IP_PROTO_TCP, 127, e->PublicIp, dest_ip, tcp, tcp_header_size + size, max_l3_size);
Free(tcp);
}
// Assign a new public-side port
UINT NnMapNewPublicPort(NATIVE_NAT *t, UINT protocol, UINT dest_ip, UINT dest_port, UINT public_ip)
{
UINT i;
UINT base_port;
UINT port_start = 1025;
UINT port_end = 65500;
// Validate arguments
if (t == NULL)
{
return 0;
}
if (t->IsRawIpMode)
{
port_start = NN_RAW_IP_PORT_START;
port_end = NN_RAW_IP_PORT_END;
}
base_port = Rand32() % (port_end - port_start) + port_start;
for (i = 0;i < (port_end - port_start);i++)
{
UINT port;
NATIVE_NAT_ENTRY tt;
NATIVE_NAT *e;
port = base_port + i;
if (port > port_end)
{
port = port - port_end + port_start;
}
// Is this port vacant?
NnSetNat(&tt, protocol, 0, 0, dest_ip, dest_port, public_ip, port);
e = SearchHash(t->NatTableForRecv, &tt);
if (e == NULL)
{
// Free port is found
return port;
}
}
return 0;
}
// Examine whether the native NAT is available
bool NnIsActive(VH *v)
{
return NnIsActiveEx(v, NULL);
}
bool NnIsActiveEx(VH *v, bool *is_ipraw_mode)
{
// Validate arguments
if (v == NULL)
{
return false;
}
if (v->NativeNat == NULL)
{
return false;
}
if (v->NativeNat->PublicIP == 0)
{
return false;
}
if (v->NativeNat->Active)
{
if (is_ipraw_mode != NULL)
{
*is_ipraw_mode = v->NativeNat->IsRawIpMode;
}
}
return v->NativeNat->Active;
}
// Native NAT main loop
void NnMainLoop(NATIVE_NAT *t, NATIVE_STACK *a)
{
IPC *ipc;
TUBE *tubes[3];
UINT num_tubes = 0;
UINT64 next_poll_tick = 0;
INTERRUPT_MANAGER *interrupt;
USHORT dns_src_port = 0;
USHORT dns_tran_id = 0;
USHORT tcp_src_port = 0;
UINT tcp_seq = 0;
IP yahoo_ip;
bool wait_for_dns = false;
UINT64 tcp_last_recv_tick = 0;
UINT dhcp_renew_interval;
UINT64 next_dhcp_renew_tick = 0;
// Validate arguments
if (t == NULL || a == NULL)
{
return;
}
dhcp_renew_interval = a->CurrentDhcpOptionList.LeaseTime;
if (dhcp_renew_interval == 0)
{
dhcp_renew_interval = IPC_DHCP_DEFAULT_LEASE;
}
dhcp_renew_interval = MAX(dhcp_renew_interval, IPC_DHCP_MIN_LEASE) / 2;
interrupt = NewInterruptManager();
ipc = a->Ipc;
tubes[num_tubes++] = ipc->Sock->RecvTube;
//tubes[num_tubes++] = ipc->Sock->SendTube; // bug 2015.10.01 remove
tubes[num_tubes++] = t->HaltTube;
Zero(&yahoo_ip, sizeof(yahoo_ip));
next_poll_tick = Tick64() + (UINT64)NN_POLL_CONNECTIVITY_INTERVAL;
AddInterrupt(interrupt, next_poll_tick);
tcp_last_recv_tick = Tick64();
next_dhcp_renew_tick = Tick64() + (UINT64)dhcp_renew_interval * 1000;
AddInterrupt(interrupt, next_dhcp_renew_tick);
while (t->Halt == false && t->v->UseNat)
{
UINT64 now = Tick64();
bool call_cancel = false;
bool state_changed = false;
UINT wait_interval;
if (t->v->HubOption != NULL)
{
if (t->IsRawIpMode == false && t->v->HubOption->DisableKernelModeSecureNAT)
{
break;
}
if (t->IsRawIpMode && t->v->HubOption->DisableIpRawModeSecureNAT)
{
break;
}
}
IPCFlushArpTable(ipc);
call_cancel = false;
LABEL_RESTART:
state_changed = false;
if (next_poll_tick == 0 || next_poll_tick <= now)
{
BUF *dns_query;
dns_src_port = NnGenSrcPort(a->IsIpRawMode);
dns_tran_id = Rand16();
// Start a connectivity check periodically
dns_query = NnBuildIpPacket(NnBuildUdpPacket(NnBuildDnsQueryPacket(NN_CHECK_HOSTNAME, dns_tran_id),
IPToUINT(&ipc->ClientIPAddress), dns_src_port, IPToUINT(&a->DnsServerIP), 53),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&a->DnsServerIP), IP_PROTO_UDP, 0);
IPCSendIPv4(ipc, dns_query->Buf, dns_query->Size);
wait_for_dns = true;
FreeBuf(dns_query);
next_poll_tick = now + (UINT64)NN_POLL_CONNECTIVITY_INTERVAL;
AddInterrupt(interrupt, next_poll_tick);
}
if (next_dhcp_renew_tick == 0 || next_dhcp_renew_tick <= now)
{
IP ip;
UINTToIP(&ip, a->CurrentDhcpOptionList.ServerAddress);
IPCDhcpRenewIP(ipc, &ip);
next_dhcp_renew_tick = now + (UINT64)dhcp_renew_interval * 1000;
AddInterrupt(interrupt, next_dhcp_renew_tick);
}
// Send an IP packet to IPC
LockQueue(t->SendQueue);
{
while (true)
{
BLOCK *b = GetNext(t->SendQueue);
if (b == NULL)
{
break;
}
IPCSendIPv4(ipc, b->Buf, b->Size);
state_changed = true;
FreeBlock(b);
}
}
UnlockQueue(t->SendQueue);
// Happy processing
IPCProcessL3Events(ipc);
LockQueue(t->RecvQueue);
{
while (true)
{
// Receive an IP packet from IPC
BLOCK *b = IPCRecvIPv4(ipc);
PKT *pkt;
if (b == NULL)
{
// Can not receive any more
break;
}
// Parse the packet
pkt = ParsePacketIPv4WithDummyMacHeader(b->Buf, b->Size);
FreeBlock(b);
if (pkt != NULL)
{
bool no_store = false;
// Read the contents of the packet first, to determine whether it is a response for the connectivity test packet
if (wait_for_dns)
{
if (pkt->TypeL3 == L3_IPV4 && pkt->TypeL4 == L4_UDP &&
pkt->L3.IPv4Header->SrcIP == IPToUINT(&a->DnsServerIP) &&
pkt->L3.IPv4Header->DstIP == IPToUINT(&ipc->ClientIPAddress) &&
pkt->L4.UDPHeader->SrcPort == Endian16(53) && pkt->L4.UDPHeader->DstPort == Endian16(dns_src_port))
{
DNSV4_HEADER *dns_header = (DNSV4_HEADER *)pkt->Payload;
if (pkt->PayloadSize >= sizeof(DNSV4_HEADER))
{
if (dns_header->TransactionId == Endian16(dns_tran_id))
{
IP ret_ip;
if (NnParseDnsResponsePacket(pkt->Payload, pkt->PayloadSize, &ret_ip))
{
BUF *tcp_query;
Copy(&yahoo_ip, &ret_ip, sizeof(IP));
//SetIP(&yahoo_ip, 192, 168, 2, 32);
// DNS response has been received
no_store = true;
tcp_src_port = NnGenSrcPort(a->IsIpRawMode);
// Generate a TCP connection attempt packet
tcp_seq = Rand32();
tcp_query = NnBuildIpPacket(NnBuildTcpPacket(NewBuf(), IPToUINT(&ipc->ClientIPAddress), tcp_src_port,
IPToUINT(&yahoo_ip), 80, tcp_seq, 0, TCP_SYN, 8192, 1414),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&yahoo_ip), IP_PROTO_TCP, 0);
IPCSendIPv4(ipc, tcp_query->Buf, tcp_query->Size);
FreeBuf(tcp_query);
wait_for_dns = false;
}
}
}
}
}
if (pkt->TypeL3 == L3_IPV4 && pkt->TypeL4 == L4_TCP &&
pkt->L3.IPv4Header->SrcIP == IPToUINT(&yahoo_ip) &&
pkt->L3.IPv4Header->DstIP == IPToUINT(&ipc->ClientIPAddress) &&
pkt->L4.TCPHeader->SrcPort == Endian16(80) && pkt->L4.TCPHeader->DstPort == Endian16(tcp_src_port))
{
TCP_HEADER *tcp_header = (TCP_HEADER *)pkt->L4.TCPHeader;
if ((tcp_header->Flag & TCP_SYN) && (tcp_header->Flag & TCP_ACK))
{
// There was a TCP response
BUF *tcp_query;
UINT recv_seq = Endian32(tcp_header->SeqNumber) + 1;
no_store = true;
// Send a RST
tcp_query = NnBuildIpPacket(NnBuildTcpPacket(NewBuf(), IPToUINT(&ipc->ClientIPAddress), tcp_src_port,
IPToUINT(&yahoo_ip), 80, tcp_seq + 1, recv_seq, TCP_RST | TCP_ACK, 8192, 0),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&yahoo_ip), IP_PROTO_TCP, 0);
IPCSendIPv4(ipc, tcp_query->Buf, tcp_query->Size);
FreeBuf(tcp_query);
tcp_last_recv_tick = now;
}
}
if (t->RecvQueue->num_item > NN_MAX_QUEUE_LENGTH)
{
no_store = true;
}
if (no_store == false)
{
// Put in the queue
InsertQueue(t->RecvQueue, pkt);
call_cancel = true;
state_changed = true;
}
else
{
// Release the packet
FreePacketWithData(pkt);
}
}
}
}
UnlockQueue(t->RecvQueue);
if (state_changed)
{
goto LABEL_RESTART;
}
if (call_cancel)
{
CANCEL *c = NULL;
Lock(t->CancelLock);
{
c = t->Cancel;
AddRef(c->ref);
}
Unlock(t->CancelLock);
if (c != NULL)
{
Cancel(c);
ReleaseCancel(c);
}
}
if (IsTubeConnected(ipc->Sock->RecvTube) == false || IsTubeConnected(ipc->Sock->SendTube) == false)
{
// Disconnected
break;
}
if ((tcp_last_recv_tick + (UINT64)NN_POLL_CONNECTIVITY_TIMEOUT) < now)
{
// Connectivity test has timed out because a certain period of time has elapsed
Debug("NN_POLL_CONNECTIVITY_TIMEOUT\n");
break;
}
wait_interval = GetNextIntervalForInterrupt(interrupt);
wait_interval = MIN(wait_interval, 1234);
if (wait_interval != 0)
{
WaitForTubes(tubes, num_tubes, wait_interval);
}
}
FreeInterruptManager(interrupt);
}
// Build an IP packet
BUF *NnBuildIpPacket(BUF *payload, UINT src_ip, UINT dst_ip, UCHAR protocol, UCHAR ttl)
{
BUF *ret = NewBuf();
IPV4_HEADER h;
if (ttl == 0)
{
ttl = 127;
}
// IP header
Zero(&h, sizeof(h));
IPV4_SET_VERSION(&h, 4);
IPV4_SET_HEADER_LEN(&h, sizeof(IPV4_HEADER) / 4);
h.TotalLength = Endian16((USHORT)sizeof(IPV4_HEADER) + payload->Size);
h.Identification = Rand16();
h.TimeToLive = ttl;
h.Protocol = protocol;
h.SrcIP = src_ip;
h.DstIP = dst_ip;
h.Checksum = IpChecksum(&h, sizeof(h));
WriteBuf(ret, &h, sizeof(h));
WriteBufBuf(ret, payload);
SeekBufToBegin(ret);
FreeBuf(payload);
return ret;
}
// Build an UDP packet
BUF *NnBuildUdpPacket(BUF *payload, UINT src_ip, USHORT src_port, UINT dst_ip, USHORT dst_port)
{
BUF *ret = NewBuf();
BUF *phbuf = NewBuf();
UDPV4_PSEUDO_HEADER ph;
UDP_HEADER h;
// UDP pseudo header
Zero(&ph, sizeof(ph));
ph.SrcIP = src_ip;
ph.DstIP = dst_ip;
ph.SrcPort = Endian16(src_port);
ph.DstPort = Endian16(dst_port);
ph.Protocol = IP_PROTO_UDP;
ph.PacketLength1 = ph.PacketLength2 = Endian16(payload->Size + (USHORT)sizeof(UDP_HEADER));
WriteBuf(phbuf, &ph, sizeof(ph));
WriteBufBuf(phbuf, payload);
// UDP header
Zero(&h, sizeof(h));
h.SrcPort = Endian16(src_port);
h.DstPort = Endian16(dst_port);
h.PacketLength = Endian16(payload->Size + (USHORT)sizeof(UDP_HEADER));
h.Checksum = IpChecksum(phbuf->Buf, phbuf->Size);
WriteBuf(ret, &h, sizeof(h));
WriteBuf(ret, payload->Buf, payload->Size);
SeekBufToBegin(ret);
FreeBuf(payload);
FreeBuf(phbuf);
return ret;
}
// Build a TCP packet
BUF *NnBuildTcpPacket(BUF *payload, UINT src_ip, USHORT src_port, UINT dst_ip, USHORT dst_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss)
{
BUF *ret;
IPV4_PSEUDO_HEADER *vh;
TCP_HEADER *tcp;
static UCHAR tcp_mss_option[] = {0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00};
UINT header_size = TCP_HEADER_SIZE;
UINT total_size;
// Memory allocation
vh = Malloc(sizeof(IPV4_PSEUDO_HEADER) + TCP_HEADER_SIZE + payload->Size + 32);
tcp = (TCP_HEADER *)(((UCHAR *)vh) + sizeof(IPV4_PSEUDO_HEADER));
if (mss != 0)
{
USHORT *mss_size;
mss_size = (USHORT *)(&tcp_mss_option[2]);
*mss_size = Endian16((USHORT)mss);
header_size += sizeof(tcp_mss_option);
}
total_size = header_size + payload->Size;
// Pseudo header generation
vh->SrcIP = src_ip;
vh->DstIP = dst_ip;
vh->Reserved = 0;
vh->Protocol = IP_PROTO_TCP;
vh->PacketLength = Endian16((USHORT)total_size);
// TCP header generation
tcp->SrcPort = Endian16((USHORT)src_port);
tcp->DstPort = Endian16((USHORT)dst_port);
tcp->SeqNumber = Endian32(seq);
tcp->AckNumber = Endian32(ack);
tcp->HeaderSizeAndReserved = 0;
TCP_SET_HEADER_SIZE(tcp, (UCHAR)(header_size / 4));
tcp->Flag = (UCHAR)flag;
tcp->WindowSize = Endian16((USHORT)window_size);
tcp->Checksum = 0;
tcp->UrgentPointer = 0;
// Copy the option values
if (mss != 0)
{
Copy(((UCHAR *)tcp) + TCP_HEADER_SIZE, tcp_mss_option, sizeof(tcp_mss_option));
}
// Data copy
Copy(((UCHAR *)tcp) + header_size, payload->Buf, payload->Size);
// Checksum calculation
tcp->Checksum = IpChecksum(vh, total_size + 12);
ret = NewBufFromMemory(tcp, total_size);
Free(vh);
FreeBuf(payload);
return ret;
}
// Build a DNS query packet
BUF *NnBuildDnsQueryPacket(char *hostname, USHORT tran_id)
{
BUF *buf = NewBuf();
DNSV4_HEADER header;
Zero(&header, sizeof(header));
header.TransactionId = Endian16(tran_id);
header.Flag1 = 0x01;
header.Flag2 = 0x00;
header.NumQuery = Endian16(1);
WriteBuf(buf, &header, sizeof(header));
BuildDnsQueryPacket(buf, hostname, false);
SeekBufToBegin(buf);
return buf;
}
// Read a DNS record
BUF *NnReadDnsRecord(BUF *buf, bool answer, USHORT *ret_type, USHORT *ret_class)
{
USHORT type;
USHORT clas;
UINT ttl;
BUF *ret = NULL;
// Validate arguments
if (buf == NULL)
{
return NULL;
}
// Read the DNS label
if (NnReadDnsLabel(buf) == false)
{
return false;
}
// Type and Class
if (ReadBuf(buf, &type, sizeof(USHORT)) != sizeof(USHORT))
{
return false;
}
if (ret_type != NULL)
{
*ret_type = Endian16(type);
}
if (ReadBuf(buf, &clas, sizeof(USHORT)) != sizeof(USHORT))
{
return false;
}
if (ret_class != NULL)
{
*ret_class = Endian16(clas);
}
if (answer)
{
USHORT data_len;
UCHAR *data;
// TTL
if (ReadBuf(buf, &ttl, sizeof(UINT)) != sizeof(UINT))
{
return false;
}
// data_len
if (ReadBuf(buf, &data_len, sizeof(USHORT)) != sizeof(USHORT))
{
return false;
}
data_len = Endian16(data_len);
// data
data = Malloc(data_len);
if (ReadBuf(buf, data, data_len) != data_len)
{
Free(data);
return false;
}
ret = NewBufFromMemory(data, data_len);
Free(data);
}
else
{
ret = NewBuf();
}
return ret;
}
// Read the DNS label
bool NnReadDnsLabel(BUF *buf)
{
UCHAR c;
UCHAR tmp[256];
// Validate arguments
if (buf == NULL)
{
return false;
}
LABEL_START:
if (ReadBuf(buf, &c, 1) != 1)
{
return false;
}
if (c == 0)
{
return true;
}
if (c & 0xC0)
{
// Compression label
if (ReadBuf(buf, &c, 1) != 1)
{
return false;
}
else
{
return true;
}
}
else
{
// Usual label
if (ReadBuf(buf, tmp, c) != c)
{
return false;
}
else
{
goto LABEL_START;
}
}
}
// Parse the DNS response packet
bool NnParseDnsResponsePacket(UCHAR *data, UINT size, IP *ret_ip)
{
BUF *buf = NewBufFromMemory(data, size);
bool ret = false;
DNSV4_HEADER h;
if (ReadBuf(buf, &h, sizeof(h)) == sizeof(h))
{
UINT num_questions = Endian16(h.NumQuery);
UINT num_answers = Endian16(h.AnswerRRs);
UINT i;
for (i = 0;i < num_questions;i++)
{
BUF *r = NnReadDnsRecord(buf, false, NULL, NULL);
if (r != NULL)
{
FreeBuf(r);
}
else
{
goto LABEL_CLEANUP;
}
}
for (i = 0;i < num_answers;i++)
{
USHORT tp, cl;
BUF *r = NnReadDnsRecord(buf, true, &tp, &cl);
if (r != NULL)
{
if (tp == 0x0001 && cl == 0x0001 && r->Size == 4)
{
ret = true;
if (ret_ip != NULL)
{
Zero(ret_ip, sizeof(IP));
Copy(ret_ip->addr, r->Buf, 4);
}
}
FreeBuf(r);
}
else
{
goto LABEL_CLEANUP;
}
}
}
LABEL_CLEANUP:
FreeBuf(buf);
return ret;
}
// Test the connectivity of the stack to the Internet
bool NnTestConnectivity(NATIVE_STACK *a, TUBE *halt_tube)
{
BUF *dns_query;
BUF *dns_query2;
bool ok = false;
USHORT dns_tran_id = Rand16();
UINT64 next_send_tick = 0;
UINT64 giveup_time;
IPC *ipc;
INTERRUPT_MANAGER *interrupt;
TUBE *tubes[3];
UINT num_tubes = 0;
IP yahoo_ip;
IP my_priv_ip;
UINT num_send_dns = 0;
IP using_dns;
UINT src_port = 0;
// Validate arguments
if (a == NULL)
{
return false;
}
src_port = NnGenSrcPort(a->IsIpRawMode);
Copy(&using_dns, &a->DnsServerIP, sizeof(IP));
// Get my physical IP
if (a->IsIpRawMode)
{
if (GetMyPrivateIP(&my_priv_ip, false) == false)
{
Debug("NnTestConnectivity: GetMyPrivateIP failed.\n");
return false;
}
else
{
Debug("NnTestConnectivity: GetMyPrivateIP ok: %r\n", &my_priv_ip);
if (a->Eth != NULL)
{
Copy(&a->Eth->MyPhysicalIPForce, &my_priv_ip, sizeof(IP));
}
}
}
ipc = a->Ipc;
interrupt = NewInterruptManager();
tubes[num_tubes++] = ipc->Sock->RecvTube;
tubes[num_tubes++] = ipc->Sock->SendTube;
if (halt_tube != NULL)
{
tubes[num_tubes++] = halt_tube;
}
Zero(&yahoo_ip, sizeof(yahoo_ip));
// Try to get an IP address of www.yahoo.com
dns_query = NnBuildIpPacket(NnBuildUdpPacket(NnBuildDnsQueryPacket(NN_CHECK_HOSTNAME, dns_tran_id),
IPToUINT(&ipc->ClientIPAddress), src_port, IPToUINT(&a->DnsServerIP), 53),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&a->DnsServerIP), IP_PROTO_UDP, 0);
dns_query2 = NnBuildIpPacket(NnBuildUdpPacket(NnBuildDnsQueryPacket(NN_CHECK_HOSTNAME, dns_tran_id),
IPToUINT(&ipc->ClientIPAddress), src_port, IPToUINT(&a->DnsServerIP), 53),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&a->DnsServerIP2), IP_PROTO_UDP, 0);
giveup_time = Tick64() + NN_CHECK_CONNECTIVITY_TIMEOUT;
AddInterrupt(interrupt, giveup_time);
while (true)
{
UINT64 now = Tick64();
IPCFlushArpTable(a->Ipc);
if (now >= giveup_time)
{
break;
}
// Send a packet periodically
if (next_send_tick == 0 || next_send_tick <= now)
{
next_send_tick = now + (UINT64)NN_CHECK_CONNECTIVITY_INTERVAL;
AddInterrupt(interrupt, next_send_tick);
if ((num_send_dns % 2) == 0)
{
IPCSendIPv4(ipc, dns_query->Buf, dns_query->Size);
}
else
{
IPCSendIPv4(ipc, dns_query2->Buf, dns_query2->Size);
}
num_send_dns++;
}
// Happy processing
IPCProcessL3Events(ipc);
while (true)
{
// Receive a packet
BLOCK *b = IPCRecvIPv4(ipc);
PKT *pkt;
if (b == NULL)
{
break;
}
// Parse the packet
pkt = ParsePacketIPv4WithDummyMacHeader(b->Buf, b->Size);
if (pkt != NULL)
{
if (pkt->TypeL3 == L3_IPV4 && pkt->TypeL4 == L4_UDP &&
(pkt->L3.IPv4Header->SrcIP == IPToUINT(&a->DnsServerIP) ||
pkt->L3.IPv4Header->SrcIP == IPToUINT(&a->DnsServerIP2)) &&
pkt->L3.IPv4Header->DstIP == IPToUINT(&ipc->ClientIPAddress) &&
pkt->L4.UDPHeader->SrcPort == Endian16(53) && pkt->L4.UDPHeader->DstPort == Endian16(src_port))
{
DNSV4_HEADER *dns_header = (DNSV4_HEADER *)pkt->Payload;
if (pkt->PayloadSize >= sizeof(DNSV4_HEADER))
{
if (dns_header->TransactionId == Endian16(dns_tran_id))
{
IP ret_ip;
if (NnParseDnsResponsePacket(pkt->Payload, pkt->PayloadSize, &ret_ip))
{
UINTToIP(&using_dns, pkt->L3.IPv4Header->SrcIP);
Debug("NativeStack: Using DNS: %r\n", &using_dns);
Copy(&yahoo_ip, &ret_ip, sizeof(IP));
}
}
}
}
}
FreePacketWithData(pkt);
FreeBlock(b);
}
if ((halt_tube != NULL && IsTubeConnected(halt_tube) == false) ||
IsTubeConnected(ipc->Sock->SendTube) == false || IsTubeConnected(ipc->Sock->RecvTube) == false)
{
// Disconnected
break;
}
if (IsZeroIP(&yahoo_ip) == false)
{
// There is a response
break;
}
// Keep the CPU waiting
WaitForTubes(tubes, num_tubes, GetNextIntervalForInterrupt(interrupt));
}
FreeBuf(dns_query);
FreeBuf(dns_query2);
if (IsZeroIP(&yahoo_ip) == false)
{
BUF *tcp_query;
UINT seq = Rand32();
bool tcp_get_response = false;
UINT recv_seq = 0;
// Since the IP address of www.yahoo.com has gotten, try to connect by TCP
giveup_time = Tick64() + NN_CHECK_CONNECTIVITY_TIMEOUT;
AddInterrupt(interrupt, giveup_time);
// Generate a TCP packet
tcp_query = NnBuildIpPacket(NnBuildTcpPacket(NewBuf(), IPToUINT(&ipc->ClientIPAddress), src_port,
IPToUINT(&yahoo_ip), 80, seq, 0, TCP_SYN, 8192, 1414),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&yahoo_ip), IP_PROTO_TCP, 0);
Debug("Test TCP to %r\n", &yahoo_ip);
next_send_tick = 0;
while (true)
{
UINT64 now = Tick64();
IPCFlushArpTable(a->Ipc);
if (now >= giveup_time)
{
break;
}
// Send the packet periodically
if (next_send_tick == 0 || next_send_tick <= now)
{
next_send_tick = now + (UINT64)NN_CHECK_CONNECTIVITY_INTERVAL;
AddInterrupt(interrupt, next_send_tick);
IPCSendIPv4(ipc, tcp_query->Buf, tcp_query->Size);
}
// Happy procedure
IPCProcessL3Events(ipc);
while (true)
{
// Receive a packet
BLOCK *b = IPCRecvIPv4(ipc);
PKT *pkt;
if (b == NULL)
{
break;
}
// Parse the packet
pkt = ParsePacketIPv4WithDummyMacHeader(b->Buf, b->Size);
if (pkt != NULL)
{
if (pkt->TypeL3 == L3_IPV4 && pkt->TypeL4 == L4_TCP &&
pkt->L3.IPv4Header->SrcIP == IPToUINT(&yahoo_ip) &&
pkt->L3.IPv4Header->DstIP == IPToUINT(&ipc->ClientIPAddress) &&
pkt->L4.TCPHeader->SrcPort == Endian16(80) && pkt->L4.TCPHeader->DstPort == Endian16(src_port))
{
TCP_HEADER *tcp_header = (TCP_HEADER *)pkt->L4.TCPHeader;
if ((tcp_header->Flag & TCP_SYN) && (tcp_header->Flag & TCP_ACK))
{
// There was a TCP response
tcp_get_response = true;
recv_seq = Endian32(tcp_header->SeqNumber);
}
}
}
FreePacketWithData(pkt);
FreeBlock(b);
}
if ((halt_tube != NULL && IsTubeConnected(halt_tube) == false) ||
IsTubeConnected(ipc->Sock->SendTube) == false || IsTubeConnected(ipc->Sock->RecvTube) == false)
{
// Disconnected
break;
}
if (tcp_get_response)
{
WHERE;
break;
}
// Keep the CPU waiting
WaitForTubes(tubes, num_tubes, GetNextIntervalForInterrupt(interrupt));
}
FreeBuf(tcp_query);
// Send a RST
if (recv_seq != 0)
{
recv_seq++;
}
tcp_query = NnBuildIpPacket(NnBuildTcpPacket(NewBuf(), IPToUINT(&ipc->ClientIPAddress), src_port,
IPToUINT(&yahoo_ip), 80, seq + 1, recv_seq, TCP_RST | TCP_ACK, 8192, 0),
IPToUINT(&ipc->ClientIPAddress), IPToUINT(&yahoo_ip), IP_PROTO_TCP, 0);
IPCSendIPv4(ipc, tcp_query->Buf, tcp_query->Size);
FreeBuf(tcp_query);
SleepThread(100);
if (tcp_get_response)
{
ok = true;
}
}
FreeInterruptManager(interrupt);
if (ok)
{
if (IsZeroIP(&using_dns) == false)
{
Copy(&a->DnsServerIP, &using_dns, sizeof(IP));
}
if (a->IsIpRawMode)
{
if (NsStartIpTablesTracking(a) == false)
{
Debug("NsStartIpTablesTracking failed.\n");
ok = false;
}
}
}
return ok;
}
// Generate source port number by a random number
UINT NnGenSrcPort(bool raw_ip_mode)
{
if (raw_ip_mode == false)
{
return 1025 + Rand32() % (65500 - 1025);
}
else
{
return NN_RAW_IP_PORT_START + Rand32() % (NN_RAW_IP_PORT_END - NN_RAW_IP_PORT_START);
}
}
// Get a next good interface for the native NAT
NATIVE_STACK *NnGetNextInterface(NATIVE_NAT *t)
{
NATIVE_STACK *ret = NULL;
UINT current_hash;
TOKEN_LIST *device_list;
UINT i;
char tmp[MAX_SIZE];
char *dev_name;
UINT current_ip_hash;
// Validate arguments
if (t == NULL)
{
return NULL;
}
t->NextWaitTimeForRetry = NN_NEXT_WAIT_TIME_FOR_DEVICE_ENUM * MIN((t->FailedCount + 1), NN_NEXT_WAIT_TIME_MAX_FAIL_COUNT);
// Get the device list
device_list = GetEthListEx(NULL,
!(t->v->HubOption != NULL && t->v->HubOption->DisableKernelModeSecureNAT),
!(t->v->HubOption != NULL && t->v->HubOption->DisableIpRawModeSecureNAT));
if (device_list == NULL || device_list->NumTokens == 0)
{
// Device list acquisition failure (Or no device acquired as a result)
FreeToken(device_list);
t->FailedCount++;
return NULL;
}
current_hash = GetEthDeviceHash();
current_ip_hash = GetHostIPAddressHash32();
if (t->LastInterfaceDeviceHash != current_hash || t->LastHostAddressHash != current_ip_hash)
{
// Device list is altered from the previous search
t->LastInterfaceIndex = INFINITE;
t->FailedCount = 0;
}
t->LastInterfaceDeviceHash = current_hash;
t->LastHostAddressHash = current_ip_hash;
if (t->LastInterfaceIndex == INFINITE)
{
i = 0;
}
else
{
i = t->LastInterfaceIndex + 1;
if (i >= device_list->NumTokens)
{
i = 0;
}
}
if ((i + 1) == device_list->NumTokens)
{
// Searched to the end
t->LastInterfaceIndex = INFINITE;
// Increase the number of search failures by one
t->FailedCount++;
}
else
{
// It is not the end yet
t->LastInterfaceIndex = i;
t->NextWaitTimeForRetry = 0;
}
dev_name = device_list->Token[i];
if (IsInLinesFile(NN_NO_NATIVE_NAT_FILENAME, dev_name, true) == false)
{
// Try to open the device
BinToStr(tmp, sizeof(tmp), t->v->MacAddress, 6);
ret = NewNativeStack(NULL, dev_name, tmp);
if (ret != NULL)
{
// Test whether an IP address can be obtained from a DHCP server
DHCP_OPTION_LIST opt;
Copy(t->CurrentMacAddress, ret->Ipc->MacAddress, 6);
Zero(&opt, sizeof(opt));
BinToStr(tmp, sizeof(tmp), ret->MacAddress, 6);
Format(ret->Ipc->ClientHostname, sizeof(ret->Ipc->ClientHostname), NN_HOSTNAME_FORMAT, tmp);
StrLower(ret->Ipc->ClientHostname);
Debug("IPCDhcpAllocateIP for %s\n", ret->DeviceName);
if (IPCDhcpAllocateIP(ret->Ipc, &opt, t->HaltTube2))
{
char client_ip[64];
char dhcp_ip[64];
char client_mask[64];
char gateway_ip[64];
IP ip;
IP subnet;
IP gw;
IPToStr32(client_ip, sizeof(client_ip), opt.ClientAddress);
IPToStr32(client_mask, sizeof(client_mask), opt.SubnetMask);
IPToStr32(dhcp_ip, sizeof(dhcp_ip), opt.ServerAddress);
IPToStr32(gateway_ip, sizeof(gateway_ip), opt.Gateway);
Debug("DHCP: client_ip=%s, client_mask=%s, dhcp_ip=%s, gateway_ip=%s\n",
client_ip, client_mask, dhcp_ip, gateway_ip);
Copy(&ret->CurrentDhcpOptionList, &opt, sizeof(DHCP_OPTION_LIST));
// IP parameter settings
UINTToIP(&ip, opt.ClientAddress);
UINTToIP(&subnet, opt.SubnetMask);
UINTToIP(&gw, opt.Gateway);
IPCSetIPv4Parameters(ret->Ipc, &ip, &subnet, &gw, &opt.ClasslessRoute);
// Determine the DNS server to use
UINTToIP(&ret->DnsServerIP, opt.DnsServer);
UINTToIP(&ret->DnsServerIP2, opt.DnsServer2);
if (IsZeroIP(&ret->DnsServerIP))
{
// Use 8.8.8.8 instead If the DNS is not assigned from the DHCP server
SetIP(&ret->DnsServerIP, 8, 8, 8, 8);
}
if (IsZeroIP(&ret->DnsServerIP2))
{
// Use 8.8.4.4 instead If the DNS is not assigned from the DHCP server
SetIP(&ret->DnsServerIP2, 8, 8, 4, 4);
}
// Connectivity test
// (always fail if the default gateway is not set)
if (opt.Gateway != 0 &&
NnTestConnectivity(ret, t->HaltTube2))
{
// Reset the number of search failures
t->FailedCount = 0;
Debug("Connectivity OK.\n");
}
else
{
Debug("Connectivity Failed.\n");
FreeNativeStack(ret);
ret = NULL;
}
}
else
{
Debug("DHCP Failed.\n");
FreeNativeStack(ret);
ret = NULL;
Zero(t->CurrentMacAddress, sizeof(t->CurrentMacAddress));
}
}
}
FreeToken(device_list);
return ret;
}
// Native NAT thread
void NativeNatThread(THREAD *thread, void *param)
{
NATIVE_NAT *t = (NATIVE_NAT *)param;
void *wait_handle = InitWaitUntilHostIPAddressChanged();
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
while (t->Halt == false)
{
NATIVE_STACK *a;
while (t->v->UseNat == false || t->v->HubOption == NULL || (t->v->HubOption->DisableKernelModeSecureNAT && t->v->HubOption->DisableIpRawModeSecureNAT))
{
if (t->Halt)
{
break;
}
// If the NAT is disabled, wait until it becomes enabled
Wait(t->HaltEvent, 1234);
}
if (t->Halt)
{
break;
}
// Get a next good native NAT stack
Debug("NnGetNextInterface Start.\n");
NnClearQueue(t);
a = NnGetNextInterface(t);
if (a != NULL)
{
char macstr[64];
// Acquisition success
Debug("NnGetNextInterface Ok: %s\n", a->DeviceName);
t->IsRawIpMode = a->IsIpRawMode;
Lock(t->Lock);
{
if (a->Sock1 != NULL)
{
t->HaltTube = a->Sock2->RecvTube;
if (t->HaltTube != NULL)
{
AddRef(t->HaltTube->Ref);
}
}
}
Unlock(t->Lock);
NnClearQueue(t);
t->PublicIP = IPToUINT(&a->Ipc->ClientIPAddress);
t->Active = true;
Debug("NnMainLoop Start.\n");
MacToStr(macstr, sizeof(macstr), a->Ipc->MacAddress);
NLog(t->v, "LH_KERNEL_MODE_START", a->DeviceName,
&a->Ipc->ClientIPAddress, &a->Ipc->SubnetMask, &a->Ipc->DefaultGateway, &a->Ipc->BroadcastAddress,
macstr, &a->CurrentDhcpOptionList.ServerAddress, &a->DnsServerIP);
NnMainLoop(t, a);
Debug("NnMainLoop End.\n");
t->IsRawIpMode = false;
t->Active = false;
t->PublicIP = 0;
NnClearQueue(t);
// Close the stack
Lock(t->Lock);
{
if (t->HaltTube != NULL)
{
ReleaseTube(t->HaltTube);
t->HaltTube = NULL;
}
}
Unlock(t->Lock);
FreeNativeStack(a);
Zero(t->CurrentMacAddress, 6);
}
else
{
Debug("NnGetNextInterface Failed.\n");
}
// Wait for a certain period of time
if (t->NextWaitTimeForRetry != 0)
{
WaitUntilHostIPAddressChanged(wait_handle, t->HaltEvent, t->NextWaitTimeForRetry, 1000);
}
}
FreeWaitUntilHostIPAddressChanged(wait_handle);
}
// Erase the contents of the queue for transmission and reception
void NnClearQueue(NATIVE_NAT *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
LockQueue(t->SendQueue);
{
while (true)
{
BLOCK *b = GetNext(t->SendQueue);
if (b == NULL)
{
break;
}
FreeBlock(b);
}
}
UnlockQueue(t->SendQueue);
LockQueue(t->RecvQueue);
{
while (true)
{
PKT *p = GetNext(t->RecvQueue);
if (p == NULL)
{
break;
}
FreePacketWithData(p);
}
}
UnlockQueue(t->RecvQueue);
}
// Structure setting function to search for native NAT
void NnSetNat(NATIVE_NAT_ENTRY *e, UINT protocol, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT pub_ip, UINT pub_port)
{
// Validate arguments
if (e == NULL)
{
return;
}
Zero(e, sizeof(NATIVE_NAT_ENTRY));
e->Protocol = protocol;
e->SrcIp = src_ip;
e->SrcPort = src_port;
e->DestIp = dest_ip;
e->DestPort = dest_port;
e->PublicIp = pub_ip;
e->PublicPort = pub_port;
e->HashCodeForSend = e->HashCodeForRecv = INFINITE;
}
// Get the hash code of the native NAT table (receiving direction)
UINT GetHashNativeNatTableForRecv(void *p)
{
UINT r;
NATIVE_NAT_ENTRY *e = (NATIVE_NAT_ENTRY *)p;
if (e == NULL)
{
return 0;
}
if (e->HashCodeForRecv != INFINITE)
{
return e->HashCodeForRecv;
}
r = 0;
r += e->Protocol;
r += e->PublicIp;
r += e->PublicPort;
if (e->Protocol == NAT_TCP)
{
r += e->DestIp;
r += e->DestPort;
}
e->HashCodeForRecv = r;
return r;
}
// Comparison function of native NAT table (receiving direction)
int CmpNativeNatTableForRecv(void *p1, void *p2)
{
int r;
NATIVE_NAT_ENTRY *e1, *e2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
e1 = *(NATIVE_NAT_ENTRY **)p1;
e2 = *(NATIVE_NAT_ENTRY **)p2;
if (e1 == NULL || e2 == NULL)
{
return 0;
}
r = COMPARE_RET(e1->Protocol, e2->Protocol);
if (r != 0)
{
return r;
}
r = COMPARE_RET(e1->PublicIp, e2->PublicIp);
if (r != 0)
{
return r;
}
r = COMPARE_RET(e1->PublicPort, e2->PublicPort);
if (r != 0)
{
return r;
}
if (e1->Protocol == NAT_TCP)
{
r = COMPARE_RET(e1->DestIp, e2->DestIp);
if (r != 0)
{
return r;
}
r = COMPARE_RET(e1->DestPort, e2->DestPort);
if (r != 0)
{
return r;
}
}
return 0;
}
// Get the hash code of the native NAT table (transmit direction)
UINT GetHashNativeNatTableForSend(void *p)
{
UINT r;
NATIVE_NAT_ENTRY *e = (NATIVE_NAT_ENTRY *)p;
if (e == NULL)
{
return 0;
}
if (e->HashCodeForSend != INFINITE)
{
return e->HashCodeForSend;
}
r = 0;
r += e->Protocol;
r += e->SrcIp;
r += e->SrcPort;
if (e->Protocol == NAT_TCP)
{
r += e->DestIp;
r += e->DestPort;
}
e->HashCodeForSend = r;
return r;
}
// Comparison function of native NAT table (transmit direction)
int CmpNativeNatTableForSend(void *p1, void *p2)
{
int r;
NATIVE_NAT_ENTRY *e1, *e2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
e1 = *(NATIVE_NAT_ENTRY **)p1;
e2 = *(NATIVE_NAT_ENTRY **)p2;
if (e1 == NULL || e2 == NULL)
{
return 0;
}
r = COMPARE_RET(e1->Protocol, e2->Protocol);
if (r != 0)
{
return r;
}
r = COMPARE_RET(e1->SrcIp, e2->SrcIp);
if (r != 0)
{
return r;
}
r = COMPARE_RET(e1->SrcPort, e2->SrcPort);
if (r != 0)
{
return r;
}
if (e1->Protocol == NAT_TCP)
{
r = COMPARE_RET(e1->DestIp, e2->DestIp);
if (r != 0)
{
return r;
}
r = COMPARE_RET(e1->DestPort, e2->DestPort);
if (r != 0)
{
return r;
}
}
return 0;
}
// Start the native NAT
NATIVE_NAT *NewNativeNat(VH *v)
{
NATIVE_NAT *t;
// Validate arguments
if (v == NULL)
{
return NULL;
}
t = ZeroMalloc(sizeof(NATIVE_NAT));
t->v = v;
t->Cancel = v->Cancel;
AddRef(t->Cancel->ref);
// Data structure initialization
t->LastInterfaceIndex = INFINITE;
t->SendQueue = NewQueue();
t->RecvQueue = NewQueue();
NnInitIpCombineList(t);
t->Lock = NewLock();
t->CancelLock = NewLock();
t->HaltEvent = NewEvent();
NewTubePair(&t->HaltTube2, &t->HaltTube3, 0);
// Create a NAT table
t->NatTableForSend = NewHashList(GetHashNativeNatTableForSend, CmpNativeNatTableForSend, 11, true);
t->NatTableForRecv = NewHashList(GetHashNativeNatTableForRecv, CmpNativeNatTableForRecv, 11, true);
t->Thread = NewThread(NativeNatThread, t);
return t;
}
// Stop the native NAT
void FreeNativeNat(NATIVE_NAT *t)
{
TUBE *tube;
UINT i;
// Validate arguments
if (t == NULL)
{
return;
}
t->Halt = true;
Lock(t->Lock);
{
tube = t->HaltTube;
if (tube != NULL)
{
AddRef(tube->Ref);
}
}
Unlock(t->Lock);
if (tube != NULL)
{
TubeFlushEx(tube, true);
SleepThread(100);
TubeDisconnect(tube);
ReleaseTube(tube);
}
TubeDisconnect(t->HaltTube2);
TubeDisconnect(t->HaltTube3);
Set(t->HaltEvent);
WaitThread(t->Thread, INFINITE);
ReleaseThread(t->Thread);
DeleteLock(t->Lock);
DeleteLock(t->CancelLock);
ReleaseEvent(t->HaltEvent);
ReleaseTube(t->HaltTube2);
ReleaseTube(t->HaltTube3);
NnClearQueue(t);
ReleaseQueue(t->RecvQueue);
ReleaseQueue(t->SendQueue);
ReleaseCancel(t->Cancel);
// Release the NAT table
for (i = 0;i < LIST_NUM(t->NatTableForSend->AllList);i++)
{
NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForSend->AllList, i);
Free(e);
}
ReleaseHashList(t->NatTableForSend);
ReleaseHashList(t->NatTableForRecv);
NnFreeIpCombineList(t);
Free(t);
}
// Take the log of Virtual Host
void VLog(VH *v, char *str)
{
// Not take!!
return;
}
// Disconnect the NAT entry immediately
void DisconnectNatEntryNow(VH *v, NAT_ENTRY *e)
{
// Validate arguments
if (v == NULL || e == NULL)
{
return;
}
if (e->DisconnectNow == false)
{
e->DisconnectNow = true;
SetSockEvent(v->SockEvent);
}
}
// Get the NAT entry with specified source IP address and the oldest last communication time
NAT_ENTRY *GetOldestNatEntryOfIp(VH *v, UINT ip, UINT protocol)
{
UINT i;
NAT_ENTRY *oldest = NULL;
UINT64 oldest_tick = 0xFFFFFFFFFFFFFFFFULL;
// Validate arguments
if (v == NULL)
{
return NULL;
}
for (i = 0;i < LIST_NUM(v->NatTable);i++)
{
NAT_ENTRY *e = LIST_DATA(v->NatTable, i);
if (e->DisconnectNow == false)
{
if (e->SrcIp == ip)
{
if (e->Protocol == protocol)
{
if (protocol != NAT_TCP || e->TcpStatus != NAT_TCP_CONNECTING)
{
if (e->LastCommTime <= oldest_tick)
{
oldest_tick = e->LastCommTime;
oldest = e;
}
}
}
}
}
}
return oldest;
}
// Get the number of current NAT entries per IP address
UINT GetNumNatEntriesPerIp(VH *v, UINT ip, UINT protocol, bool tcp_syn_sent)
{
UINT ret = 0;
UINT i;
// Validate arguments
if (v == NULL)
{
return 0;
}
for (i = 0;i < LIST_NUM(v->NatTable);i++)
{
NAT_ENTRY *e = LIST_DATA(v->NatTable, i);
if (e->DisconnectNow == false)
{
if (e->SrcIp == ip)
{
if (e->Protocol == protocol)
{
bool ok = false;
if (protocol == NAT_TCP)
{
if (tcp_syn_sent)
{
if (e->TcpStatus == NAT_TCP_CONNECTING)
{
ok = true;
}
}
else
{
if (e->TcpStatus != NAT_TCP_CONNECTING)
{
ok = true;
}
}
}
else
{
ok = true;
}
if (ok)
{
ret++;
}
}
}
}
}
return ret;
}
// Check whether the NAT is available
bool CanCreateNewNatEntry(VH *v)
{
// Validate arguments
if (v == NULL)
{
return false;
}
if (v->UseNat == false)
{
// NAT stopped
return false;
}
if (NnIsActive(v) && v->NativeNat != NULL && v->NativeNat->NatTableForRecv != NULL)
{
if (v->NativeNat->NatTableForRecv->AllList->num_item > NAT_MAX_SESSIONS_KERNEL)
{
// Number of sessions exceeded (kernel mode)
return false;
}
}
else
{
if (v->NatTable->num_item > NAT_MAX_SESSIONS)
{
// Number of sessions exceeded (user mode)
return false;
}
}
return true;
}
// Set a pointer to the Virtual HUB options
void NatSetHubOption(VH *v, HUB_OPTION *o)
{
// Validate arguments
if (v == NULL)
{
return;
}
v->HubOption = o;
}
// Get a pointer to the Virtual HUB options
HUB_OPTION *NatGetHubOption(VH *v)
{
// Validate arguments
if (v == NULL)
{
return NULL;
}
return v->HubOption;
}
// The main function of NAT processing thread
void NatThreadMain(VH *v)
{
bool halt_flag;
// Validate arguments
if (v == NULL)
{
return;
}
v->TmpBuf = Malloc(NAT_TMPBUF_SIZE);
while (true)
{
// Wait until the next event is set
WaitSockEvent(v->SockEvent, SELECT_TIME);
halt_flag = false;
LockVirtual(v);
{
// Process on all NAT sessions
UINT i, num;
v->Now = Tick64();
v->NatDoCancelFlag = false;
LIST_ELEMENT_DELETED:
num = LIST_NUM(v->NatTable);
for (i = 0;i < num;i++)
{
NAT_ENTRY *n = LIST_DATA(v->NatTable, i);
switch (n->Protocol)
{
case NAT_TCP: // TCP
if (NatTransactTcp(v, n) == false)
{
goto LIST_ELEMENT_DELETED;
}
break;
case NAT_UDP: // UDP
if (NatTransactUdp(v, n) == false)
{
goto LIST_ELEMENT_DELETED;
}
break;
case NAT_ICMP: // ICMP
if (NatTransactIcmp(v, n) == false)
{
goto LIST_ELEMENT_DELETED;
}
break;
case NAT_DNS: // DNS
if (NatTransactDns(v, n) == false)
{
goto LIST_ELEMENT_DELETED;
}
break;
}
}
if (v->NatDoCancelFlag)
{
// Hit the cancel of the parent thread
Cancel(v->Cancel);
}
// Halting flag check
if (v->HaltNat)
{
halt_flag = true;
}
}
UnlockVirtual(v);
if (halt_flag)
{
// Terminate the thread by disconnecting all entries forcibly
LockVirtual(v);
{
UINT num = LIST_NUM(v->NatTable);
NAT_ENTRY **nn = ToArray(v->NatTable);
UINT i;
for (i = 0;i < num;i++)
{
NAT_ENTRY *n = nn[i];
n->DisconnectNow = true;
switch (n->Protocol)
{
case NAT_TCP: // TCP
NatTransactTcp(v, n);
break;
case NAT_UDP: // UDP
NatTransactUdp(v, n);
break;
case NAT_ICMP: // ICMP
NatTransactIcmp(v, n);
break;
case NAT_DNS: // DNS
NatTransactDns(v, n);
break;
}
}
Free(nn);
}
UnlockVirtual(v);
break;
}
}
Free(v->TmpBuf);
}
// DNS: Thread to get the IP address
void NatGetIPThread(THREAD *t, void *param)
{
NAT_DNS_QUERY *q;
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
q = (NAT_DNS_QUERY *)param;
AddWaitThread(t);
q->Ok = GetIP(&q->Ip, q->Hostname);
DelWaitThread(t);
if (Release(q->ref) == 0)
{
Free(q);
}
}
// DNS: Get an IP address from host name
bool NatGetIP(IP *ip, char *hostname)
{
TOKEN_LIST *t;
bool ret = false;
// Validate arguments
if (ip == NULL || hostname == NULL)
{
return false;
}
t = ParseToken(hostname, ".");
if (t == NULL)
{
return false;
}
if (t->NumTokens == 0)
{
FreeToken(t);
return false;
}
if (t->NumTokens == 1)
{
ret = GetIP(ip, hostname);
}
else
{
char *hostname2 = t->Token[0];
NAT_DNS_QUERY *q1, *q2;
THREAD *t1, *t2;
q1 = ZeroMalloc(sizeof(NAT_DNS_QUERY));
q2 = ZeroMalloc(sizeof(NAT_DNS_QUERY));
q1->ref = NewRef();
q2->ref = NewRef();
AddRef(q1->ref);
AddRef(q2->ref);
StrCpy(q1->Hostname, sizeof(q1->Hostname), hostname);
StrCpy(q2->Hostname, sizeof(q2->Hostname), hostname2);
t1 = NewThread(NatGetIPThread, q1);
t2 = NewThread(NatGetIPThread, q2);
WaitThread(t1, NAT_DNS_QUERY_TIMEOUT);
if (q1->Ok)
{
ret = true;
Copy(ip, &q1->Ip, sizeof(IP));
}
else
{
WaitThread(t2, NAT_DNS_QUERY_TIMEOUT);
if (q1->Ok)
{
ret = true;
Copy(ip, &q1->Ip, sizeof(IP));
}
else if (q2->Ok)
{
ret = true;
Copy(ip, &q2->Ip, sizeof(IP));
}
}
ReleaseThread(t1);
ReleaseThread(t2);
if (Release(q1->ref) == 0)
{
Free(q1);
}
if (Release(q2->ref) == 0)
{
Free(q2);
}
}
FreeToken(t);
return ret;
}
// DNS query function
void NatDnsThread(THREAD *t, void *param)
{
NAT_ENTRY *n;
IP ip;
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
n = (NAT_ENTRY *)param;
// Notify the initialization completion
NoticeThreadInit(t);
// Run processing
if (EndWith(n->DnsTargetHostName, ".in-addr.arpa") == false)
{
// Forward resolution
if (NatGetIP(&ip, n->DnsTargetHostName))
{
// Forward resolution success
Copy(&n->DnsResponseIp, &ip, sizeof(IP));
n->DnsOk = true;
}
}
else
{
// Reverse resolution
IP ip;
n->DnsGetIpFromHost = true; // Set the reverse resolution flag
// Convert a *.in-addr.arpa string to an IP address
if (ArpaToIP(&ip, n->DnsTargetHostName))
{
// Reverse resolution process
char tmp[256];
if (GetHostName(tmp, sizeof(tmp), &ip))
{
// Reverse resolution success
n->DnsResponseHostName = CopyStr(tmp);
n->DnsOk = true;
}
}
}
// Notify the results
n->DnsFinished = true;
SetSockEvent(n->v->SockEvent);
}
// Convert a reverse resolution address to an IP address
bool ArpaToIP(IP *ip, char *str)
{
TOKEN_LIST *token;
bool ret = false;
// Validate arguments
if (ip == NULL || str == NULL)
{
return false;
}
// Token conversion
token = ParseToken(str, ".");
if (token->NumTokens == 6)
{
// Convert the token [0, 1, 2, 3] to IP
UINT i;
Zero(ip, sizeof(IP));
for (i = 0;i < 4;i++)
{
ip->addr[i] = (UCHAR)ToInt(token->Token[3 - i]);
}
ret = true;
}
FreeToken(token);
if (IPToUINT(ip) == 0)
{
ret = false;
}
return ret;
}
// Handle a DNS entry
bool NatTransactDns(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return true;
}
if (n->DisconnectNow)
{
goto DISCONNECT;
}
if (n->DnsThread == NULL && n->DnsFinished == false)
{
// Create a thread
THREAD *t = NewThread(NatDnsThread, (void *)n);
WaitThreadInit(t);
n->DnsThread = t;
}
else
{
// Wait for the result
if (n->DnsFinished)
{
// Results have been received
WaitThread(n->DnsThread, INFINITE);
ReleaseThread(n->DnsThread);
n->DnsThread = NULL;
// Notify to the main thread
v->NatDoCancelFlag = true;
}
}
return true;
DISCONNECT:
// Releasing process
if (n->DnsThread != NULL)
{
WaitThread(n->DnsThread, INFINITE);
ReleaseThread(n->DnsThread);
n->DnsThread = NULL;
}
if (n->DnsTargetHostName != NULL)
{
Free(n->DnsTargetHostName);
n->DnsTargetHostName = NULL;
}
if (n->DnsResponseHostName != NULL)
{
Free(n->DnsResponseHostName);
n->DnsResponseHostName = NULL;
}
DeleteLock(n->lock);
Delete(v->NatTable, n);
Free(n);
return false;
}
// ICMP thread procedure
void NatIcmpThreadProc(THREAD *thread, void *param)
{
NAT_ENTRY *n;
ICMP_RESULT *ret = NULL;
USHORT src_id = 0, src_seqno = 0;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
n = (NAT_ENTRY *)param;
if (n->IcmpQueryBlock)
{
UCHAR *data = n->IcmpQueryBlock->Buf;
UINT size = n->IcmpQueryBlock->Size;
if (size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
{
ICMP_HEADER *icmp = (ICMP_HEADER *)data;
ICMP_ECHO *echo = (ICMP_ECHO *)(data + sizeof(ICMP_HEADER));
if (icmp->Type == ICMP_TYPE_ECHO_REQUEST && icmp->Code == 0)
{
UCHAR *icmp_payload = data + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO);
UINT icmp_payload_size = size - sizeof(ICMP_HEADER) - sizeof(ICMP_ECHO);
IP dest_ip;
src_id = Endian16(echo->Identifier);
src_seqno = Endian16(echo->SeqNo);
UINTToIP(&dest_ip, n->DestIp);
// Send a query by using the ICMP API
ret = IcmpApiEchoSend(&dest_ip, n->IcmpQueryBlock->Ttl,
icmp_payload, icmp_payload_size, NAT_ICMP_TIMEOUT_WITH_API);
}
}
}
if (ret != NULL && ret->Timeout == false)
{
// Convert to an IPv4 + ICMP packet since the result of ICMP API was obtained
IPV4_HEADER ipv4;
ICMP_HEADER icmp;
ICMP_ECHO echo;
BUF *buf = NewBuf();
// IPv4 header
Zero(&ipv4, sizeof(ipv4));
IPV4_SET_VERSION(&ipv4, 4);
IPV4_SET_HEADER_LEN(&ipv4, sizeof(IPV4_HEADER) / 4);
ipv4.TimeToLive = ret->Ttl;
ipv4.Protocol = IP_PROTO_ICMPV4;
ipv4.SrcIP = IPToUINT(&ret->IpAddress);
ipv4.DstIP = 0x01010101;
// ICMP header
Zero(&icmp, sizeof(icmp));
Zero(&echo, sizeof(echo));
if (ret->Ok)
{
// Normal response
echo.Identifier = Endian16(src_id);
echo.SeqNo = Endian16(src_seqno);
ipv4.TotalLength = Endian16((USHORT)(sizeof(ipv4) + sizeof(icmp) + sizeof(echo) + ret->DataSize));
WriteBuf(buf, &ipv4, sizeof(ipv4));
WriteBuf(buf, &icmp, sizeof(icmp));
WriteBuf(buf, &echo, sizeof(echo));
WriteBuf(buf, ret->Data, ret->DataSize);
}
else
{
// Error reply
icmp.Type = ret->Type;
icmp.Code = ret->Code;
echo.Identifier = Endian16(src_id);
echo.SeqNo = Endian16(src_seqno);
ipv4.TotalLength = Endian16((USHORT)(sizeof(ipv4) + sizeof(icmp) + sizeof(echo) + n->IcmpOriginalCopySize));
WriteBuf(buf, &ipv4, sizeof(ipv4));
WriteBuf(buf, &icmp, sizeof(icmp));
WriteBuf(buf, &echo, sizeof(echo));
// Copy of the original packet to be included in the response packet
WriteBuf(buf, n->IcmpOriginalCopy, n->IcmpOriginalCopySize);
}
n->IcmpResponseBlock = NewBlock(Clone(buf->Buf, buf->Size), buf->Size, 0);
n->IcmpResponseBlock->Ttl = ret->Ttl;
FreeBuf(buf);
}
IcmpApiFreeResult(ret);
// Inform the completion of the processing
n->IcmpTaskFinished = true;
SetSockEvent(n->v->SockEvent);
}
// Process ICMP entry
bool NatTransactIcmp(VH *v, NAT_ENTRY *n)
{
void *buf;
UINT recv_size;
BLOCK *block;
IP dest_ip;
UINT num_ignore_errors = 0;
UINT dest_port = 0;
// Validate arguments
if (v == NULL || n == NULL)
{
return true;
}
dest_port = n->DestPort;
if (n->DisconnectNow)
{
goto DISCONNECT;
}
if (v->IcmpRawSocketOk)
{
// Environment that the Raw sockets are available
if (n->UdpSocketCreated == false)
{
// Create a UDP socket
n->Sock = NewUDP(MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4));
if (n->Sock == NULL)
{
// Socket creation failure
goto DISCONNECT;
}
else
{
n->PublicIp = IPToUINT(&n->Sock->LocalIP);
n->PublicPort = n->Sock->LocalPort;
JoinSockToSockEvent(n->Sock, v->SockEvent);
n->UdpSocketCreated = true;
}
}
}
else
{
// Create a thread for using ICMP API if Raw sockets are not available
if (n->IcmpThread == NULL)
{
if (n->UdpSendQueue->num_item >= 1)
{
// Since UdpSendQueue contains only 1 query, get a first query
// and create a thread and pass the query to the thread
BLOCK *block = GetNext(n->UdpSendQueue);
n->IcmpQueryBlock = block;
n->IcmpThread = NewThread(NatIcmpThreadProc, n);
}
}
if (n->IcmpTaskFinished)
{
if (n->IcmpResponseBlock != NULL)
{
// Because there was a response from the thread that calls ICMP API, pass this result to the stack
block = n->IcmpResponseBlock;
n->IcmpResponseBlock = NULL;
InsertQueue(n->UdpRecvQueue, block);
v->NatDoCancelFlag = true;
n->LastCommTime = v->Now;
}
else
{
// Disconnect immediately when it fails
goto DISCONNECT;
}
}
// Examine whether this session timed-out
if ((n->LastCommTime + (UINT64)NAT_ICMP_TIMEOUT_WITH_API) < v->Now || n->LastCommTime > v->Now)
{
// Time-out
goto DISCONNECT;
}
return true;
}
// Following are processed only for if the raw sockets are available
buf = v->TmpBuf;
UINTToIP(&dest_ip, n->DestIp);
// Try to receive data from the UDP socket
while (true)
{
IP src_ip;
UINT src_port;
recv_size = RecvFrom(n->Sock, &src_ip, &src_port, buf, 65536);
if (recv_size == SOCK_LATER)
{
// Packet has not arrived
break;
}
else if (recv_size == 0)
{
Debug("ICMP ERROR\n");
// Error?
if (n->Sock->IgnoreRecvErr == false)
{
// A fatal error occurred
goto DISCONNECT;
}
else
{
if ((num_ignore_errors++) >= MAX_NUM_IGNORE_ERRORS)
{
goto DISCONNECT;
}
}
}
else
{
// Analyze the arriving packet
ICMP_RESULT *ret = IcmpParseResult(&dest_ip, n->SrcPort, 0, buf, recv_size);
if (ret != NULL)
{
if ((ret->Ok && CmpIpAddr(&ret->IpAddress, &dest_ip) == 0) ||
(ret->DataSize >= sizeof(IPV4_HEADER) && ((IPV4_HEADER *)ret->Data)->DstIP == n->DestIp))
{
// Insert to the queue
void *data = Malloc(recv_size);
Copy(data, buf, recv_size);
block = NewBlock(data, recv_size, 0);
InsertQueue(n->UdpRecvQueue, block);
v->NatDoCancelFlag = true;
n->LastCommTime = v->Now;
}
IcmpFreeResult(ret);
}
}
}
// Try to send data to the UDP socket
while (block = GetNext(n->UdpSendQueue))
{
// Assemble the Echo header and ICMP header
UINT send_size;
SetTtl(n->Sock, block->Ttl);
send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size);
FreeBlock(block);
if (send_size == 0)
{
Debug("ICMP ERROR\n");
// Determine whether a fatal error
if (n->Sock->IgnoreSendErr == false)
{
// A fatal error occurred
goto DISCONNECT;
}
}
else
{
n->LastCommTime = v->Now;
}
}
// Examine whether this session timed-out
if ((n->LastCommTime + (UINT64)NAT_ICMP_TIMEOUT) < v->Now || n->LastCommTime > v->Now)
{
// Time-out
goto DISCONNECT;
}
return true;
DISCONNECT:
// Disconnect this session
if (n->UdpSocketCreated)
{
// Close the socket
Disconnect(n->Sock);
ReleaseSock(n->Sock);
n->Sock = NULL;
}
// Terminate if the thread has been created
if (n->IcmpThread != NULL)
{
WaitThread(n->IcmpThread, INFINITE);
ReleaseThread(n->IcmpThread);
n->IcmpThread = NULL;
}
// Delete the entry
DeleteNatIcmp(v, n);
return false;
}
// Process the UDP entry
bool NatTransactUdp(VH *v, NAT_ENTRY *n)
{
void *buf;
UINT recv_size;
BLOCK *block;
IP dest_ip;
UINT num_ignore_errors;
UINT dest_port = 0;
// Validate arguments
if (v == NULL || n == NULL)
{
return true;
}
dest_port = n->DestPort;
if (n->DisconnectNow)
{
goto DISCONNECT;
}
if (n->UdpSocketCreated == false)
{
// Create a UDP socket
n->Sock = NewUDP(0);
if (n->Sock == NULL)
{
// Socket creation failure
goto DISCONNECT;
}
else
{
n->PublicIp = IPToUINT(&n->Sock->LocalIP);
n->PublicPort = n->Sock->LocalPort;
JoinSockToSockEvent(n->Sock, v->SockEvent);
n->UdpSocketCreated = true;
}
}
buf = v->TmpBuf;
if (n->ProxyDns == false)
{
UINTToIP(&dest_ip, n->DestIp);
}
else
{
UINTToIP(&dest_ip, n->DestIpProxy);
}
num_ignore_errors = 0;
// Try to receive data from the UDP socket
while (true)
{
IP src_ip;
UINT src_port;
recv_size = RecvFrom(n->Sock, &src_ip, &src_port, buf, 65536);
if (recv_size == SOCK_LATER)
{
// Packet has not arrived
break;
}
else if (recv_size == 0)
{
// Error?
if (n->Sock->IgnoreRecvErr == false)
{
// A fatal error occurred
goto DISCONNECT;
}
else
{
if ((num_ignore_errors++) > MAX_NUM_IGNORE_ERRORS)
{
goto DISCONNECT;
}
}
}
else
{
// Packet arrives. Check the source IP
if (IPToUINT(&src_ip) == n->DestIp || n->DestIp == 0xFFFFFFFF || (IPToUINT(&src_ip) == n->DestIpProxy && n->ProxyDns) && src_port == n->DestPort)
{
// Insert to the queue
void *data = Malloc(recv_size);
Copy(data, buf, recv_size);
block = NewBlock(data, recv_size, 0);
if (block != NULL)
{
if (src_port == SPECIAL_UDP_PORT_WSD || src_port == SPECIAL_UDP_PORT_SSDP)
{
// Make believe there is a response from the host really in the case of WSD packet
block->Param1 = IPToUINT(&src_ip);
}
}
InsertQueue(n->UdpRecvQueue, block);
v->NatDoCancelFlag = true;
n->LastCommTime = v->Now;
}
}
}
// Try to send data to the UDP socket
while (block = GetNext(n->UdpSendQueue))
{
UINT send_size;
bool is_nbtdgm = false;
LIST *local_ip_list = NULL;
if (dest_port == SPECIAL_UDP_PORT_NBTDGM)
{
// Determine whether NetBIOS Datagram packet
NBTDG_HEADER *nh = (NBTDG_HEADER *)block->Buf;
if (nh != NULL && block->Size >= sizeof(NBTDG_HEADER))
{
if (nh->SrcIP == n->SrcIp && Endian16(nh->SrcPort) == n->SrcPort)
{
local_ip_list = GetHostIPAddressList();
if (local_ip_list != NULL)
{
is_nbtdgm = true;
}
}
}
}
if (is_nbtdgm == false)
{
// Normal UDP packet
send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size);
}
else
{
// IP address and port number is embedded in the NetBIOS Datagram Packet.
// Transfer by rewriting it properly
UINT i;
for (i = 0;i < LIST_NUM(local_ip_list);i++)
{
IP *my_ip = LIST_DATA(local_ip_list, i);
if (IsIP4(my_ip) && IsZeroIp(my_ip) == false && IsLocalHostIP(my_ip) == false)
{
NBTDG_HEADER *nh = (NBTDG_HEADER *)block->Buf;
nh->SrcIP = IPToUINT(my_ip);
nh->SrcPort = Endian16(n->PublicPort);
send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size);
}
}
}
if (local_ip_list != NULL)
{
FreeHostIPAddressList(local_ip_list);
}
FreeBlock(block);
if (send_size == 0)
{
// Determining whether a fatal error
if (n->Sock->IgnoreSendErr == false)
{
// A fatal error occurred
goto DISCONNECT;
}
}
else
{
n->LastCommTime = v->Now;
}
}
// Examine whether this session timed-out
if ((n->LastCommTime + (UINT64)v->NatUdpTimeout) < v->Now || n->LastCommTime > v->Now)
{
// Time-out
goto DISCONNECT;
}
return true;
DISCONNECT:
// Disconnect this session
if (n->UdpSocketCreated)
{
// Close the socket
Disconnect(n->Sock);
ReleaseSock(n->Sock);
n->Sock = NULL;
}
// Delete the entry
DeleteNatUdp(v, n);
return false;
}
// Thread to make a connection to the TCP host
void NatTcpConnectThread(THREAD *t, void *p)
{
NAT_ENTRY *n = (NAT_ENTRY *)p;
IP ip;
char hostname[MAX_SIZE];
UINT port_number;
SOCK *sock;
SOCK_EVENT *e;
// Validate arguments
if (n == NULL || t == NULL)
{
return;
}
UINTToIP(&ip, n->DestIp);
IPToStr(hostname, sizeof(hostname), &ip);
port_number = n->DestPort;
e = n->v->SockEvent;
AddRef(e->ref);
// Notify the initialization completion
NoticeThreadInit(t);
// Attempt to connect to the TCP host
Debug("NatTcpConnect Connecting to %s:%u\n", hostname, port_number);
sock = ConnectEx3(hostname, port_number, 0, &n->NatTcpCancelFlag, NULL, NULL, false, false, true);
if (sock == NULL)
{
// Connection failure
n->TcpMakeConnectionFailed = true;
}
else
{
// Successful connection
n->TcpMakeConnectionSucceed = true;
}
n->Sock = sock;
JoinSockToSockEvent(sock, e);
SetSockEvent(e);
ReleaseSockEvent(e);
}
// Create a thread for trying to connect to the TCP host
void CreateNatTcpConnectThread(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
// Create a thread
n->NatTcpConnectThread = NewThread(NatTcpConnectThread, (void *)n);
// Wait for a thread initialization completion
WaitThreadInit(n->NatTcpConnectThread);
}
// Handle the TCP entry
bool NatTransactTcp(VH *v, NAT_ENTRY *n)
{
char str[MAX_SIZE];
bool timeouted = false;
// Validate arguments
if (v == NULL || n == NULL)
{
return false;
}
if (n->DisconnectNow)
{
goto DISCONNECT;
}
// Process by state of the TCP
switch (n->TcpStatus)
{
case NAT_TCP_CONNECTING: // Waiting for connection
if (n->NatTcpConnectThread == NULL)
{
// Start a connection by creating a connection thread
CreateNatTcpConnectThread(v, n);
}
else
{
// Wait for the result of the connection thread that has already started
if (n->TcpMakeConnectionFailed || n->TcpMakeConnectionSucceed)
{
// Use the results because operation thread has already finished
WaitThread(n->NatTcpConnectThread, INFINITE);
ReleaseThread(n->NatTcpConnectThread);
n->NatTcpConnectThread = NULL;
if (n->TcpMakeConnectionSucceed)
{
// Connection is successful, and a Sock was created
n->TcpStatus = NAT_TCP_CONNECTED;
IPToStr32(str, sizeof(str), n->DestIp);
NLog(v, "LH_NAT_TCP_SUCCEED", n->Id, n->Sock->RemoteHostname, str, n->DestPort);
}
else
{
// Failed to connect
n->TcpStatus = NAT_TCP_SEND_RESET;
IPToStr32(str, sizeof(str), n->DestIp);
NLog(v, "LH_NAT_TCP_FAILED", n->Id, str, n->DestPort);
}
v->NatDoCancelFlag = true;
}
}
break;
case NAT_TCP_CONNECTED: // TCP socket connection completed. Negotiating with the client host
break;
case NAT_TCP_SEND_RESET: // TCP communication disconnection: Send a RST to the client host
break;
case NAT_TCP_ESTABLISHED: // TCP connection established
{
UINT old_send_fifo_size = 0;
// Transmit to the socket if there is data in the receive buffer
while (n->RecvFifo->size > 0)
{
UINT sent_size = Send(n->Sock, ((UCHAR *)n->RecvFifo->p) + n->RecvFifo->pos,
n->RecvFifo->size, false);
if (sent_size == 0)
{
// Communication has been disconnected
n->TcpFinished = true;
v->NatDoCancelFlag = true;
break;
}
else if (sent_size == SOCK_LATER)
{
// Blocking
break;
}
else
{
// Successful transmission
ReadFifo(n->RecvFifo, NULL, sent_size);
n->SendAckNext = true;
if (false)
{
IP ip;
n->test_TotalSent += sent_size;
UINTToIP(&ip, n->DestIp);
Debug("TCP %u: %r:%u %u\n", n->Id, &ip, n->DestPort, (UINT)n->test_TotalSent);
}
}
}
old_send_fifo_size = FifoSize(n->SendFifo);
// Write to the transmission buffer by obtaining data from the socket
while (true)
{
void *buf = (void *)v->TmpBuf;
UINT want_to_recv_size = 0;
UINT recv_size;
// Calculate the size of wanting to receive
if (n->SendFifo->size < NAT_SEND_BUF_SIZE)
{
// Still can receive
want_to_recv_size = MIN(NAT_SEND_BUF_SIZE - n->SendFifo->size, NAT_TMPBUF_SIZE);
}
if (want_to_recv_size == 0)
{
SetNoNeedToRead(n->Sock);
break;
}
recv_size = Recv(n->Sock, buf, want_to_recv_size, false);
if (recv_size == 0)
{
// Communication has been disconnected
n->TcpFinished = true;
v->NatDoCancelFlag = true;
if (n->TcpDisconnected == false)
{
Disconnect(n->Sock);
n->TcpDisconnected = true;
}
break;
}
else if (recv_size == SOCK_LATER)
{
// Blocking
break;
}
else
{
// Successful reception
WriteFifo(n->SendFifo, buf, recv_size);
v->NatDoCancelFlag = true;
}
}
if (old_send_fifo_size == 0 && FifoSize(n->SendFifo) != 0)
{
// Reset the time data for timeout when the data is newly queued
// in the empty transmission buffer in the transmission process
n->TcpLastRecvAckTime = v->Now;
}
// Raise a transmission time-out if a certain period of time elapsed
// after receiving the last ACK, and the transmission buffer is not
// empty, and the reception window size of other party is not 0
if ((n->TcpLastRecvAckTime + (UINT64)VIRTUAL_TCP_SEND_TIMEOUT) < v->Now)
{
if (FifoSize(n->SendFifo) != 0 && n->TcpSendWindowSize != 0)
{
timeouted = true;
}
}
}
break;
}
// Timeout Detection
if ((n->LastCommTime + (UINT64)v->NatTcpTimeout) < v->Now || n->LastCommTime > v->Now)
{
timeouted = true;
}
if (timeouted)
{
// Time-out occurs, the session close
n->TcpStatus = NAT_TCP_SEND_RESET;
v->NatDoCancelFlag = true;
}
return true;
DISCONNECT: // Disconnect and session disposal
DeleteNatTcp(v, n);
return false;
}
// Delete the entry of TCP NAT
void DeleteNatTcp(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
NLog(v, "LH_NAT_TCP_DELETED", n->Id);
// Shutdown of connection thread
if (n->NatTcpConnectThread != NULL)
{
n->NatTcpCancelFlag = true;
WaitThread(n->NatTcpConnectThread, INFINITE);
ReleaseThread(n->NatTcpConnectThread);
n->NatTcpConnectThread = NULL;
}
if (n->Sock != NULL)
{
// Disconnect the socket
Disconnect(n->Sock);
ReleaseSock(n->Sock);
n->Sock = NULL;
}
// Release the window memory
if (n->TcpRecvWindow != NULL)
{
ReleaseFifo(n->TcpRecvWindow);
n->TcpRecvWindow = NULL;
}
// Release the window reception list
if (n->TcpRecvList != NULL)
{
UINT i;
for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
{
IP_PART *p = LIST_DATA(n->TcpRecvList, i);
Free(p);
}
ReleaseList(n->TcpRecvList);
n->TcpRecvList = NULL;
}
// FIFO release
ReleaseFifo(n->SendFifo);
ReleaseFifo(n->RecvFifo);
// Delete from the NAT entry
Delete(v->NatTable, n);
DeleteLock(n->lock);
// Release the memory
Free(n);
Debug("NAT_ENTRY: DeleteNatTcp\n");
}
// NAT processing thread
void NatThread(THREAD *t, void *param)
{
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
// Notify the initialization completion
NoticeThreadInit(t);
NatThreadMain((VH *)param);
}
// Send a beacon packet
void SendBeacon(VH *v)
{
UINT dest_ip;
ARPV4_HEADER arp;
static char beacon_str[] =
"SecureNAT Virtual TCP/IP Stack Beacon";
// Validate arguments
if (v == NULL)
{
return;
}
// Send an UDP
dest_ip = (v->HostIP & v->HostMask) | (~v->HostMask);
SendUdp(v, dest_ip, 7, v->HostIP, 7, beacon_str, sizeof(beacon_str));
// Build the ARP header
arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
arp.HardwareSize = 6;
arp.ProtocolSize = 4;
arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
Copy(arp.SrcAddress, v->MacAddress, 6);
arp.SrcIP = v->HostIP;
arp.TargetAddress[0] =
arp.TargetAddress[1] =
arp.TargetAddress[2] =
arp.TargetAddress[3] =
arp.TargetAddress[4] =
arp.TargetAddress[5] = 0xff;
arp.TargetIP = dest_ip;
// Transmission
VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
}
// Send a TCP packet
void SendTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss, void *data, UINT size)
{
static UCHAR tcp_mss_option[] = {0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00};
IPV4_PSEUDO_HEADER *vh;
TCP_HEADER *tcp;
UINT header_size = TCP_HEADER_SIZE;
UINT total_size;
// Validate arguments
if (v == NULL || (size != 0 && data == NULL))
{
return;
}
// Memory allocation
vh = Malloc(sizeof(IPV4_PSEUDO_HEADER) + TCP_HEADER_SIZE + size + 32);
tcp = (TCP_HEADER *)(((UCHAR *)vh) + sizeof(IPV4_PSEUDO_HEADER));
if (mss != 0)
{
USHORT *mss_size;
mss_size = (USHORT *)(&tcp_mss_option[2]);
*mss_size = Endian16((USHORT)mss);
header_size += sizeof(tcp_mss_option);
}
total_size = header_size + size;
if (total_size > 65536)
{
// Packet is too long
Free(vh);
return;
}
// Pseudo header generation
vh->SrcIP = src_ip;
vh->DstIP = dest_ip;
vh->Reserved = 0;
vh->Protocol = IP_PROTO_TCP;
vh->PacketLength = Endian16((USHORT)total_size);
// TCP header generation
tcp->SrcPort = Endian16((USHORT)src_port);
tcp->DstPort = Endian16((USHORT)dest_port);
tcp->SeqNumber = Endian32(seq);
tcp->AckNumber = Endian32(ack);
tcp->HeaderSizeAndReserved = 0;
TCP_SET_HEADER_SIZE(tcp, (UCHAR)(header_size / 4));
tcp->Flag = (UCHAR)flag;
tcp->WindowSize = Endian16((USHORT)window_size);
tcp->Checksum = 0;
tcp->UrgentPointer = 0;
// Copy the option values
if (mss != 0)
{
Copy(((UCHAR *)tcp) + TCP_HEADER_SIZE, tcp_mss_option, sizeof(tcp_mss_option));
}
// Data copy
Copy(((UCHAR *)tcp) + header_size, data, size);
// Checksum calculation
tcp->Checksum = IpChecksum(vh, total_size + 12);
// Submit as an IP packet
SendIp(v, dest_ip, src_ip, IP_PROTO_TCP, tcp, total_size);
// Release the memory
Free(vh);
}
// Polling process of TCP
void PollingNatTcp(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
switch (n->TcpStatus)
{
case NAT_TCP_CONNECTING: // Socket connecting: nothing to do
break;
case NAT_TCP_CONNECTED: // The socket connected: process SYN + ACK, ACK
if ((n->LastSynAckSentTime > v->Now) || n->LastSynAckSentTime == 0 || ((n->LastSynAckSentTime + (UINT64)(NAT_TCP_SYNACK_SEND_TIMEOUT * (UINT64)(n->SynAckSentCount + 1)) <= v->Now)))
{
n->LastSynAckSentTime = v->Now;
// Send a SYN + ACK
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeqInit + n->SendSeq),
(UINT)(n->RecvSeqInit + n->RecvSeq),
TCP_SYN | TCP_ACK, n->TcpRecvWindowSize,
v->TcpMss, NULL, 0);
n->SynAckSentCount++;
}
break;
case NAT_TCP_SEND_RESET: // Reset the connection
// Send a RST
if (n->TcpFinished == false || n->TcpForceReset)
{
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeq + n->SendSeqInit),
(UINT)(n->SendSeq + n->SendSeqInit),
TCP_RST, 0,
0, NULL, 0);
// Disconnect
n->TcpStatus = NAT_TCP_WAIT_DISCONNECT;
n->DisconnectNow = true;
}
else
{
// Send FINs for NAT_FIN_SEND_MAX_COUNT times
if (n->FinSentTime == 0 || (n->FinSentTime > v->Now) || (n->FinSentTime + NAT_FIN_SEND_INTERVAL * (n->FinSentCount + 1)) < v->Now)
{
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeq + n->SendSeqInit),
(UINT)(n->RecvSeq + n->RecvSeqInit),
TCP_ACK | TCP_FIN, 0,
0, NULL, 0);
n->FinSentTime = v->Now;
n->FinSentSeq = (UINT)(n->SendSeq + n->SendSeqInit);
n->FinSentCount++;
if (n->FinSentCount >= NAT_FIN_SEND_MAX_COUNT)
{
n->TcpFinished = false;
}
}
}
break;
case NAT_TCP_ESTABLISHED: // Connection established
{
UINT send_data_size;
UINT current_pointer;
UINT notice_window_size_value = 0;
UINT buf_free_bytes = 0;
// Determine the value of the window size to be notified
if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE)
{
buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo);
}
notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes);
if (n->LastSentKeepAliveTime == 0 ||
(n->LastSentKeepAliveTime + (UINT64)NAT_ACK_KEEPALIVE_SPAN) < v->Now ||
(n->LastSentKeepAliveTime > v->Now))
{
if (n->LastSentKeepAliveTime != 0)
{
// Send an ACK packet for Keep-Alive
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeqInit + n->SendSeq),
(UINT)(n->RecvSeqInit + n->RecvSeq) - 1,
TCP_ACK,
notice_window_size_value,
0,
NULL,
0);
}
n->LastSentKeepAliveTime = v->Now;
}
if (n->TcpLastSentTime == 0 ||
(n->TcpLastSentTime > v->Now) ||
((n->TcpLastSentTime + (UINT64)n->TcpSendTimeoutSpan) < v->Now) ||
n->SendAckNext)
{
// If there is data to send, send the data
// Calculate the segment size to be transmitted
send_data_size = n->TcpSendWindowSize;
if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize))
{
// Apply the cwnd value
send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize;
}
if (send_data_size > n->SendFifo->size)
{
// Can not be sent over the data that is currently held
send_data_size = n->SendFifo->size;
}
if (send_data_size >= 1)
{
// Transmit the fragmented segments
current_pointer = 0;
while (send_data_size > 0)
{
UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size);
void *send_segment = (void *)(((UCHAR *)n->SendFifo->p) + n->SendFifo->pos + current_pointer);
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer),
(UINT)(n->RecvSeqInit + n->RecvSeq),
TCP_ACK | TCP_PSH,
notice_window_size_value,
0,
send_segment,
send_segment_size);
current_pointer += send_segment_size;
send_data_size -= send_segment_size;
}
// Record the transmission time
n->TcpLastSentTime = v->Now;
// Record the stream size to be transmitted this time
n->SendMissionSize = current_pointer;
n->CurrentSendingMission = true;
// RTT measurement
if (n->CalcRTTStartTime == 0)
{
n->CalcRTTStartTime = v->Now;
n->CalcRTTStartValue = n->SendSeq + current_pointer - 1;
}
if (n->RetransmissionUsedFlag == false)
{
n->RetransmissionUsedFlag = true;
}
else
{
// Congestion is detected
if (n->TcpSendCWnd > 2)
{
n->TcpSendCWnd--;
}
}
}
else if (n->SendAckNext)
{
// Send only an ACK
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeqInit + n->SendSeq),
(UINT)(n->RecvSeqInit + n->RecvSeq),
TCP_ACK,
notice_window_size_value,
0,
NULL,
0);
}
n->SendAckNext = false;
}
if (n->TcpFinished)
{
// Disconnect if all data transmission has completed
if (n->SendFifo->size == 0)
{
n->TcpStatus = NAT_TCP_SEND_RESET;
}
}
}
break;
}
}
// Reception of TCP packets addressed to the Internet
void TcpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, TCP_HEADER *tcp, void *data, UINT size, UINT max_l3_size)
{
NAT_ENTRY *n, t;
UINT seq, ack;
UINT64 seq64 = 0, ack64 = 0;
// Validate arguments
if (v == NULL || tcp == NULL || data == NULL)
{
return;
}
if (NnIsActive(v))
{
NnTcpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, tcp, data, size, max_l3_size);
return;
}
seq = Endian32(tcp->SeqNumber);
ack = Endian32(tcp->AckNumber);
if (v->HubOption != NULL && v->HubOption->DisableUserModeSecureNAT)
{
// Disable User-mode NAT
SendTcp(v, dest_ip, dest_port, src_ip, src_port,
0, seq + 1, TCP_RST | TCP_ACK, 0, 0, NULL, 0);
return;
}
// Search for a session for this packet from the NAT table
SetNat(&t, NAT_TCP, src_ip, src_port, dest_ip, dest_port, 0, 0);
n = SearchNat(v, &t);
if (n == NULL)
{
// There is no existing session
// Allow through only SYN packet
if ((tcp->Flag & TCP_SYN) && ((tcp->Flag & TCP_ACK) == false))
{
TCP_OPTION o;
// Create a new session
n = CreateNatTcp(v, src_ip, src_port, dest_ip, dest_port);
if (n == NULL)
{
// Return the RST if it was not possible to create
SendTcp(v, dest_ip, dest_port, src_ip, src_port,
0, seq + 1, TCP_RST | TCP_ACK, 0, 0, NULL, 0);
return;
}
// Get the options
ParseTcpOption(&o, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE);
if (o.MaxSegmentSize == 0)
{
o.MaxSegmentSize = v->TcpMss;
}
Debug("TCP SYN: MSS=%u, WS=%u\n", o.MaxSegmentSize, o.WindowScaling);
// Initial sequence number
n->RecvSeqInit = (UINT64)Endian32(tcp->SeqNumber);
n->RecvSeq = 1;
n->TcpSendMaxSegmentSize = o.MaxSegmentSize;
n->TcpRecvWindowSize = NAT_TCP_RECV_WINDOW_SIZE;
n->TcpSendWindowSize = (UINT)Endian16(tcp->WindowSize);
if (o.WindowScaling != 0)
{
if (o.WindowScaling > 14)
{
o.WindowScaling = 14;
}
n->TcpSendWindowSize = (n->TcpSendWindowSize << o.WindowScaling);
}
}
}
if (n == NULL)
{
// Return a RST since a packet which is not registered in the NAT entry arrived
SendTcp(v, dest_ip, dest_port, src_ip, src_port,
ack, ack, TCP_RST, 0, 0, NULL, 0);
return;
}
n->TcpLastRecvAckTime = v->Now;
switch (n->TcpStatus)
{
case NAT_TCP_SEND_RESET: // Disconnect the connection by sending a RST
if ((tcp->Flag & TCP_ACK) && ((tcp->Flag & TCP_SYN) == false))
{
if (n->FinSentCount >= 1)
{
if (ack == (n->FinSentSeq + 1))
{
n->TcpForceReset = true;
}
}
}
break;
case NAT_TCP_CONNECTED: // Socket connection completion: SYN + ACK, ACK processing
if ((tcp->Flag & TCP_ACK) && ((tcp->Flag & TCP_SYN) == false))
{
if (seq == (UINT)(n->RecvSeqInit + n->RecvSeq) &&
ack == (UINT)(n->SendSeqInit + n->SendSeq + 1))
{
// Handshake complete since the ACK packet came back
n->SendSeq++; // SYN packet consumes the seq by 1
Debug("TCP Connection Established.\n");
n->TcpStatus = NAT_TCP_ESTABLISHED;
// Initialize the congestion window size
n->TcpSendCWnd = 1;
n->LastCommTime = v->Now;
}
else
{
goto TCP_RESET;
}
}
else if (tcp->Flag & TCP_RST)
{
TCP_RESET:
// Receive a RST
Debug("TCP Connection Reseted.\n");
n->TcpStatus = NAT_TCP_SEND_RESET;
}
break;
case NAT_TCP_ESTABLISHED: // Connection established
if (tcp->Flag & TCP_FIN)
{
// Complete the connection
n->TcpFinished = true;
}
if (tcp->Flag & TCP_RST)
{
// Receive a RST
goto TCP_RESET;
}
else if (tcp->Flag & TCP_ACK)
{
TCP_OPTION opt;
n->LastCommTime = v->Now;
// Get the options, such as window size
n->TcpSendWindowSize = Endian16(tcp->WindowSize);
ParseTcpOption(&opt, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE);
if (opt.WindowScaling != 0)
{
if (opt.WindowScaling > 14)
{
opt.WindowScaling = 14;
}
n->TcpSendWindowSize = (n->TcpSendWindowSize << opt.WindowScaling);
}
// First, process the received ACK
// Store the end position of the stream that has received the acknowledgment to ack64
ack64 = n->SendSeq + (UINT64)ack - (n->SendSeqInit + n->SendSeq) % X32;
if ((n->SendSeqInit + n->SendSeq) % X32 > ack)
{
if (((n->SendSeqInit + n->SendSeq) % X32 - ack) >= 0x80000000)
{
ack64 = n->SendSeq + (UINT64)ack + X32 - (n->SendSeqInit + n->SendSeq) % X32;
}
}
if (ack64 > n->SendSeq)
{
// Reception of 1 byte or more seems to have been completed by the client
UINT slide_offset = (UINT)(ack64 - n->SendSeq); // Sliding size of the window
if (slide_offset == 0 || slide_offset > n->TcpSendWindowSize || slide_offset > n->SendFifo->size)
{
// Ignore because the offset value of acknowledgment is
// larger than the size that should have been sent so far
}
else
{
// RTT measurement
if (n->CalcRTTStartTime != 0)
{
if (n->CalcRTTStartValue < ack64)
{
UINT time_span;
if (v->Now > n->CalcRTTStartTime)
{
time_span = (UINT)(v->Now - n->CalcRTTStartTime);
}
else
{
time_span = 100;
}
n->CalcRTTStartTime = 0;
// Smoothing
n->CurrentRTT =
(UINT)
(
((UINT64)n->CurrentRTT * (UINT64)9 +
(UINT64)time_span * (UINT64)1) / (UINT64)10
);
n->TcpSendTimeoutSpan = n->CurrentRTT * 2;
}
}
// Reduce the transmission size
n->SendMissionSize -= slide_offset;
if (n->SendMissionSize == 0)
{
// Try to increase the transmission segment size because
// all segments to be sent this time have been sent
if (n->TcpSendCWnd < 65536)
{
n->TcpSendCWnd++;
}
n->CurrentSendingMission = false;
n->TcpLastSentTime = 0;
n->RetransmissionUsedFlag = false;
}
// Slide the buffer
n->SendSeq += slide_offset;
ReadFifo(n->SendFifo, NULL, slide_offset);
// Send further by the size of confirmed transmission completion by the ACK this time
if (n->SendMissionSize != 0 && false)
{
UINT notice_window_size_value = 0;
UINT send_data_size;
UINT buf_free_bytes;
UINT send_offset = n->SendMissionSize;
// Determine the value of the window size to be notified
if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE)
{
buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo);
}
notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes);
// Calculate the segment size to be transmitted
send_data_size = n->TcpSendWindowSize;
if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize))
{
// Apply the cwnd value
send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize;
}
if (n->SendFifo->size > send_offset)
{
send_data_size = MIN(send_data_size, n->SendFifo->size - send_offset);
send_data_size = MIN(send_data_size, slide_offset);
}
else
{
send_data_size = 0;
}
if (send_data_size >= 1)
{
// Transmit the fragmented segments
UINT current_pointer = 0;
while (send_data_size > 0)
{
UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size);
void *send_segment = (void *)((
(UCHAR *)n->SendFifo->p) + n->SendFifo->pos +
current_pointer + send_offset);
SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,
(UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer
+ (UINT)send_offset),
(UINT)(n->RecvSeqInit + n->RecvSeq),
TCP_ACK | TCP_PSH,
notice_window_size_value,
0,
send_segment,
send_segment_size);
current_pointer += send_segment_size;
send_data_size -= send_segment_size;
}
n->SendMissionSize += current_pointer;
n->CurrentSendingMission = true;
n->TcpLastSentTime = v->Now;
// RTT measurement
if (n->CalcRTTStartTime == 0)
{
n->CalcRTTStartTime = v->Now;
n->CalcRTTStartValue = n->SendSeq + current_pointer - 1;
}
}
}
// Event occurs
SetSockEvent(v->SockEvent);
}
}
// Next, receive the data
seq64 = n->RecvSeq + (UINT64)seq - (n->RecvSeqInit + n->RecvSeq) % X32;
if ((n->RecvSeqInit + n->RecvSeq) % X32 > seq)
{
if (((n->RecvSeqInit + n->RecvSeq) % X32 - ack) >= 0x80000000)
{
seq64 = n->RecvSeq + (UINT64)seq + X32 - (n->RecvSeqInit + n->RecvSeq) % X32;
}
}
// Position of the starting point of the data from the client is in the seq64 at this time
if (seq64 >= n->RecvSeq && (seq64 + size) <= (n->RecvSeq + n->TcpRecvWindowSize))
{
if (size >= 1)
{
// One or more bytes of data has been received within the receive window
UINT offset = (UINT)(seq64 - n->RecvSeq);
UINT i;
IP_PART *me;
if (n->TcpRecvWindow == NULL)
{
n->TcpRecvWindow = NewFifo();
}
if (n->TcpRecvList == NULL)
{
n->TcpRecvList = NewListFast(NULL);
}
// Add to the list by overwriting arriving packets to the buffer
if (FifoSize(n->TcpRecvWindow) < (offset + size))
{
// Buffer size expansion
WriteFifo(n->TcpRecvWindow, NULL, offset + size - FifoSize(n->TcpRecvWindow));
}
Copy(((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos +
offset, data, size);
me = ZeroMalloc(sizeof(IP_PART));
me->Offset = offset;
me->Size = size;
for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
{
IP_PART *p = LIST_DATA(n->TcpRecvList, i);
// If there are overlapped region, remove these
if (p->Size != 0)
{
if (me->Offset <= p->Offset && (me->Offset + me->Size) >= (p->Offset + p->Size))
{
// This packet completely overwrite the existing packet
p->Size = 0;
}
else if (me->Offset >= p->Offset && (me->Offset + me->Size) <= (p->Offset + p->Size))
{
// Existing packet completely override this packet
me->Size = 0;
}
else if (me->Offset > p->Offset && me->Offset < (p->Offset + p->Size) &&
(me->Offset + me->Size) > (p->Offset + p->Size))
{
// Partially overlapped
p->Size -= p->Offset + p->Size - me->Offset;
}
else if (me->Offset < p->Offset && (me->Offset + size) > p->Offset && (me->Offset + size) < (p->Offset + p->Size))
{
// Partially overlapped
me->Size -= me->Offset + me->Size - p->Offset;
}
}
}
if (me->Size == 0)
{
Free(me);
}
else
{
Add(n->TcpRecvList, me);
}
KILL_NULL_FIRST:
// Remove all blank items from reception list
for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
{
IP_PART *p = LIST_DATA(n->TcpRecvList, i);
if (p->Size == 0)
{
Delete(n->TcpRecvList, p);
Free(p);
goto KILL_NULL_FIRST;
}
}
SCAN_FIRST:
// Extract if there is something starting at offset 0 in the received list
for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
{
IP_PART *p = LIST_DATA(n->TcpRecvList, i);
UINT sz;
if (p->Offset == 0)
{
// Since a data block starts with 0 is found,
// slide it left by that amount and write the buffer
// for extracting data to the FIFO
sz = p->Size;
WriteFifo(n->RecvFifo, ((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos, sz);
// Release from the list
Delete(n->TcpRecvList, p);
Free(p);
ReadFifo(n->TcpRecvWindow, NULL, sz);
// Slide all the items to the left
for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)
{
p = LIST_DATA(n->TcpRecvList, i);
p->Offset -= sz;
}
// Update the parameters of the TCB
n->RecvSeq += (UINT64)sz;
SetSockEvent(v->SockEvent);
n->SendAckNext = true;
// Re-scan from the beginning
goto SCAN_FIRST;
}
}
}
}
}
break;
}
SetSockEvent(v->SockEvent);
}
// Parse the TCP options
void ParseTcpOption(TCP_OPTION *o, void *data, UINT size)
{
UCHAR *buf = (UCHAR *)data;
UINT i = 0;
UINT value_size = 0;
UINT value_id = 0;
UCHAR value[128];
// Validate arguments
if (o == NULL || data == NULL)
{
return;
}
Zero(o, sizeof(TCP_OPTION));
while(i < size)
{
if (buf[i] == 0)
{
return;
}
else if (buf[i] == 1)
{
i++;
continue;
}
else
{
value_id = buf[i];
i++;
if (i >= size)
{
return;
}
value_size = buf[i];
if (value_size <= 1 || value_size > sizeof(value))
{
return;
}
i++;
if (i >= size)
{
return;
}
value_size -= 2;
Copy(value, &buf[i], value_size);
i += value_size;
if (i > size)
{
return;
}
switch (value_id)
{
case 2: // MSS
if (value_size == 2)
{
USHORT *mss = (USHORT *)value;
o->MaxSegmentSize = Endian16(*mss);
}
break;
case 3: // WSS
if (value_size == 1)
{
UCHAR *wss = (UCHAR *)value;
o->WindowScaling = *wss;
}
break;
}
}
}
}
// Create a new NAT TCP session
NAT_ENTRY *CreateNatTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port)
{
NAT_ENTRY *n;
HUB_OPTION *o;
// Validate arguments
if (v == NULL)
{
return NULL;
}
if (CanCreateNewNatEntry(v) == false)
{
return NULL;
}
o = NatGetHubOption(v);
// Fail immediately if the connection with SYN_SENT are too many
if (o != NULL && o->SecureNAT_MaxTcpSynSentPerIp != 0)
{
if (GetNumNatEntriesPerIp(v, src_ip, NAT_TCP, true) >= o->SecureNAT_MaxTcpSynSentPerIp)
{
return NULL;
}
}
// If the connections other than SYN_SENT are too many, delete old ones
if (o != NULL && o->SecureNAT_MaxTcpSessionsPerIp != 0)
{
if (GetNumNatEntriesPerIp(v, src_ip, NAT_TCP, false) >= o->SecureNAT_MaxTcpSessionsPerIp)
{
NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_TCP);
if (oldest != NULL)
{
DisconnectNatEntryNow(v, oldest);
}
}
}
// Create a NAT entry
n = ZeroMalloc(sizeof(NAT_ENTRY));
n->Id = Inc(v->Counter);
n->v = v;
n->lock = NewLock();
n->Protocol = NAT_TCP;
n->SrcIp = src_ip;
n->SrcPort = src_port;
n->DestIp = dest_ip;
n->DestPort = dest_port;
n->CreatedTime = n->LastCommTime = v->Now;
n->TcpLastRecvAckTime = v->Now;
n->Sock = NULL;
n->DisconnectNow = false;
n->TcpSendMaxSegmentSize = n->TcpRecvMaxSegmentSize = v->TcpMss;
n->SendFifo = NewFifo();
n->RecvFifo = NewFifo();
n->TcpStatus = NAT_TCP_CONNECTING;
n->SendSeqInit = Rand32();
n->CurrentRTT = NAT_INITIAL_RTT_VALUE;
n->TcpSendTimeoutSpan = n->CurrentRTT * 2;
// Add to the NAT table
Add(v->NatTable, n);
#if 1
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
Debug("NAT_ENTRY: CreateNatTcp %s %u -> %s %u\n", s1, src_port, s2, dest_port);
NLog(v, "LH_NAT_TCP_CREATED", n->Id, s1, src_port, s2, dest_port);
}
#endif
return n;
}
// Received TCP packets from the virtual network
void VirtualTcpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, UINT max_l3_size)
{
TCP_HEADER *tcp;
UINT src_port, dest_port;
UINT header_size, buf_size;
void *buf;
IP ip1, ip2;
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
// Get the header
if (size < TCP_HEADER_SIZE)
{
// Size is too small
return;
}
tcp = (TCP_HEADER *)data;
src_port = Endian16(tcp->SrcPort);
dest_port = Endian16(tcp->DstPort);
if (src_port == 0 || dest_port == 0)
{
// Port number is invalid
return;
}
if (src_ip == dest_ip || src_ip == 0 || src_ip == 0xffffffff || dest_ip == 0 || dest_ip == 0xffffffff)
{
// IP address is invalid
return;
}
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
if (ip1.addr[0] == 127 || ip2.addr[0] == 127)
{
// Loopback IP address can not be specified
return;
}
if (IsInNetwork(dest_ip, v->HostIP, v->HostMask))
{
// Ignore the packets toward the network of the virtual LAN side
return;
}
// Get the header size
header_size = TCP_GET_HEADER_SIZE(tcp) * 4;
if (size < header_size)
{
// Header size is invalid
return;
}
// Get the address and size of the buffer
buf_size = size - header_size;
buf = (void *)(((UCHAR *)data) + header_size);
TcpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, tcp, buf, buf_size, max_l3_size);
}
// NAT ICMP polling
void PollingNatIcmp(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
// Process if there are any packets in the receive queue
if (n->UdpRecvQueue->num_item != 0)
{
BLOCK *block;
// Send all ICMP packets to the virtual network
while (block = GetNext(n->UdpRecvQueue))
{
// Rewrite the destination IP address of the returned packet to the IP address of the client
UCHAR *data;
UINT size;
data = (UCHAR *)block->Buf;
size = block->Size;
if (size >= sizeof(IPV4_HEADER))
{
IPV4_HEADER *ipv4 = (IPV4_HEADER *)data;
UINT ipv4_header_size = GetIpHeaderSize((UCHAR *)ipv4, size);
if (ipv4_header_size >= sizeof(IPV4_HEADER) && (Endian16(ipv4->TotalLength) >= ipv4_header_size))
{
UCHAR *ipv4_payload = data + ipv4_header_size;
UINT ipv4_payload_size = Endian16(ipv4->TotalLength) - ipv4_header_size;
if (ipv4_payload_size >= sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO))
{
ICMP_HEADER *icmp = (ICMP_HEADER *)(data + ipv4_header_size);
UINT icmp_size = ipv4_payload_size;
if (icmp->Type == ICMP_TYPE_DESTINATION_UNREACHABLE || icmp->Type == ICMP_TYPE_TIME_EXCEEDED)
{
// Rewrite the Src IP of the IPv4 header of the ICMP response packet
if (icmp_size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + sizeof(IPV4_HEADER)))
{
IPV4_HEADER *orig_ipv4 = (IPV4_HEADER *)(data + ipv4_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
UINT orig_ipv4_size = icmp_size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
UINT orig_ipv4_header_size = GetIpHeaderSize((UCHAR *)orig_ipv4, orig_ipv4_size);
if (orig_ipv4_header_size >= sizeof(IPV4_HEADER))
{
orig_ipv4->SrcIP = n->SrcIp;
orig_ipv4->Checksum = 0;
orig_ipv4->Checksum = IpChecksum(orig_ipv4, orig_ipv4_header_size);
}
}
}
// Recalculate the checksum of ICMP
icmp->Checksum = IpChecksum(icmp, icmp_size);
SendIpEx(v, n->SrcIp, ipv4->SrcIP, ipv4->Protocol, ipv4_payload, ipv4_payload_size,
MAX(ipv4->TimeToLive - 1, 1));
}
}
}
FreeBlock(block);
}
if (v->IcmpRawSocketOk == false)
{
// Release the NAT entry as soon as the results is received in the case of using ICMP API
n->DisconnectNow = true;
}
}
}
// NAT UDP polling
void PoolingNatUdp(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
// Process if there are any packets in the receive queue
if (n->UdpRecvQueue->num_item != 0)
{
BLOCK *block;
// Send all UDP packets to the virtual network
while (block = GetNext(n->UdpRecvQueue))
{
UINT src_ip = n->DestIp;
if (src_ip == 0xFFFFFFFF)
{
src_ip = v->HostIP;
}
if (block->Param1 != 0)
{
src_ip = block->Param1;
}
SendUdp(v, n->SrcIp, n->SrcPort, src_ip, n->DestPort,
block->Buf, block->Size);
FreeBlock(block);
}
}
}
// NAT polling
void PoolingNat(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
if (NnIsActive(v))
{
// Poll whether the packet comes from native NAT
NnPoll(v->NativeNat);
}
// Process by scanning the all NAT entries
for (i = 0;i < LIST_NUM(v->NatTable);i++)
{
NAT_ENTRY *n = LIST_DATA(v->NatTable, i);
switch (n->Protocol)
{
case NAT_TCP:
PollingNatTcp(v, n);
break;
case NAT_UDP:
PoolingNatUdp(v, n);
break;
case NAT_ICMP:
PollingNatIcmp(v, n);
break;
case NAT_DNS:
PollingNatDns(v, n);
break;
}
}
}
// Comparison function of the NAT table entry
int CompareNat(void *p1, void *p2)
{
NAT_ENTRY *n1, *n2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
n1 = *(NAT_ENTRY **)p1;
n2 = *(NAT_ENTRY **)p2;
if (n1 == n2)
{
return 0;
}
if (n1->SrcIp > n2->SrcIp) return 1;
else if (n1->SrcIp < n2->SrcIp) return -1;
else if (n1->DestIp > n2->DestIp) return 1;
else if (n1->DestIp < n2->DestIp) return -1;
else if (n1->SrcPort > n2->SrcPort) return 1;
else if (n1->SrcPort < n2->SrcPort) return -1;
else if (n1->DestPort > n2->DestPort) return 1;
else if (n1->DestPort < n2->DestPort) return -1;
else if (n1->Protocol > n2->Protocol) return 1;
else if (n1->Protocol < n2->Protocol) return -1;
else return 0;
}
// Configure the NAT structure
void SetNat(NAT_ENTRY *n, UINT protocol, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT public_ip, UINT public_port)
{
// Validate arguments
if (n == NULL)
{
return;
}
n->Protocol = protocol;
n->SrcIp = src_ip;
n->SrcPort = src_port;
n->DestIp = dest_ip;
n->DestPort = dest_port;
n->PublicIp = public_ip;
n->PublicPort = public_port;
}
// Initialize the NAT
void InitNat(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
// Create a NAT table
v->NatTable = NewList(CompareNat);
// Create a socket event
v->SockEvent = NewSockEvent();
// Create the NAT thread
v->HaltNat = false;
v->NatThread = NewThread(NatThread, (void *)v);
WaitThreadInit(v->NatThread);
if (IsEthSupported())
{
// Start a native NAT if access to the layer 2 Ethernet is supported
v->NativeNat = NewNativeNat(v);
}
}
// Release the NAT
void FreeNat(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
// Stop the native NAT
if (v->NativeNat != NULL)
{
FreeNativeNat(v->NativeNat);
v->NativeNat = NULL;
}
// Stop the NAT thread
v->HaltNat = true;
SetSockEvent(v->SockEvent);
WaitThread(v->NatThread, INFINITE);
ReleaseThread(v->NatThread);
v->NatThread = NULL;
ReleaseSockEvent(v->SockEvent);
v->SockEvent = NULL;
// Release the NAT table
ReleaseList(v->NatTable);
}
// Search the NAT table
NAT_ENTRY *SearchNat(VH *v, NAT_ENTRY *target)
{
NAT_ENTRY *n;
// Validate arguments
if (v == NULL || target == NULL)
{
return NULL;
}
// Binary search
n = (NAT_ENTRY *)Search(v->NatTable, target);
return n;
}
// Delete the UDP NAT entry
void DeleteNatUdp(VH *v, NAT_ENTRY *n)
{
BLOCK *block;
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
NLog(v, "LH_NAT_UDP_DELETED", n->Id);
// Release all queues
while (block = GetNext(n->UdpRecvQueue))
{
FreeBlock(block);
}
ReleaseQueue(n->UdpRecvQueue);
while (block = GetNext(n->UdpSendQueue))
{
FreeBlock(block);
}
ReleaseQueue(n->UdpSendQueue);
// Release the socket
if (n->Sock != NULL)
{
Disconnect(n->Sock);
ReleaseSock(n->Sock);
n->Sock = NULL;
}
DeleteLock(n->lock);
// Remove from the table
Delete(v->NatTable, n);
// Release the memory
Free(n);
Debug("NAT: DeleteNatUdp\n");
}
// Delete the ICMP NAT entry
void DeleteNatIcmp(VH *v, NAT_ENTRY *n)
{
BLOCK *block;
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
//NLog(v, "LH_NAT_ICMP_DELETED", n->Id);
// Release all queues
while (block = GetNext(n->UdpRecvQueue))
{
FreeBlock(block);
}
ReleaseQueue(n->UdpRecvQueue);
while (block = GetNext(n->UdpSendQueue))
{
FreeBlock(block);
}
ReleaseQueue(n->UdpSendQueue);
if (n->IcmpQueryBlock != NULL)
{
FreeBlock(n->IcmpQueryBlock);
}
if (n->IcmpResponseBlock != NULL)
{
FreeBlock(n->IcmpResponseBlock);
}
if (n->IcmpOriginalCopy != NULL)
{
Free(n->IcmpOriginalCopy);
}
// Release the socket
if (n->Sock != NULL)
{
Disconnect(n->Sock);
ReleaseSock(n->Sock);
n->Sock = NULL;
}
DeleteLock(n->lock);
// Remove from the table
Delete(v->NatTable, n);
// Release the memory
Free(n);
Debug("NAT: DeleteNatIcmp\n");
}
// Create a NAT ICMP entry
NAT_ENTRY *CreateNatIcmp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UCHAR *original_copy, UINT original_copy_size)
{
NAT_ENTRY *n;
HUB_OPTION *o;
// Validate arguments
if (v == NULL || original_copy == NULL || original_copy_size == 0)
{
return NULL;
}
if (CanCreateNewNatEntry(v) == false)
{
return NULL;
}
o = NatGetHubOption(v);
if (o != NULL && o->SecureNAT_MaxIcmpSessionsPerIp != 0)
{
if (GetNumNatEntriesPerIp(v, src_ip, NAT_ICMP, false) >= o->SecureNAT_MaxIcmpSessionsPerIp)
{
NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_ICMP);
if (oldest != NULL)
{
DisconnectNatEntryNow(v, oldest);
}
}
}
n = ZeroMalloc(sizeof(NAT_ENTRY));
n->Id = Inc(v->Counter);
n->v = v;
n->lock = NewLock();
n->Protocol = NAT_ICMP;
n->SrcIp = src_ip;
n->SrcPort = src_port;
n->DestIp = dest_ip;
n->DestPort = dest_port;
n->CreatedTime = n->LastCommTime = v->Now;
n->UdpSendQueue = NewQueue();
n->UdpRecvQueue = NewQueue();
n->UdpSocketCreated = false;
n->IcmpOriginalCopy = Clone(original_copy, original_copy_size);
n->IcmpOriginalCopySize = original_copy_size;
SetSockEvent(v->SockEvent);
#if 1
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
Debug("NAT_ENTRY: CreateNatIcmp %s %u -> %s %u\n", s1, src_port, s2, dest_port);
//NLog(v, "LH_NAT_ICMP_CREATED", n->Id, s1, s2, src_port);
}
#endif
Add(v->NatTable, n);
return n;
}
// Create a NAT UDP entry
NAT_ENTRY *CreateNatUdp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT dns_proxy_ip)
{
NAT_ENTRY *n;
HUB_OPTION *o;
// Validate arguments
if (v == NULL)
{
return NULL;
}
if (CanCreateNewNatEntry(v) == false)
{
return NULL;
}
o = NatGetHubOption(v);
if (o != NULL && o->SecureNAT_MaxTcpSessionsPerIp != 0)
{
if (GetNumNatEntriesPerIp(v, src_ip, NAT_UDP, false) >= o->SecureNAT_MaxUdpSessionsPerIp)
{
NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_UDP);
if (oldest != NULL)
{
DisconnectNatEntryNow(v, oldest);
}
}
}
n = ZeroMalloc(sizeof(NAT_ENTRY));
n->Id = Inc(v->Counter);
n->v = v;
n->lock = NewLock();
n->Protocol = NAT_UDP;
n->SrcIp = src_ip;
n->SrcPort = src_port;
n->DestIp = dest_ip;
n->DestPort = dest_port;
if (dns_proxy_ip != 0)
{
n->ProxyDns = true;
n->DestIpProxy = dns_proxy_ip;
}
n->CreatedTime = n->LastCommTime = v->Now;
n->UdpSendQueue = NewQueue();
n->UdpRecvQueue = NewQueue();
n->UdpSocketCreated = false;
SetSockEvent(v->SockEvent);
#if 1
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
Debug("NAT_ENTRY: CreateNatUdp %s %u -> %s %u\n", s1, src_port, s2, dest_port);
NLog(v, "LH_NAT_UDP_CREATED", n->Id, s1, src_port, s2, dest_port);
}
#endif
Add(v->NatTable, n);
return n;
}
// Ignore for NetBIOS name registration packet
bool IsNetbiosRegistrationPacket(UCHAR *buf, UINT size)
{
// Validate arguments
if (buf == NULL || size == 0)
{
return false;
}
if (size >= 4)
{
USHORT us = *((USHORT *)(buf + 2));
us = Endian16(us);
if (((us & 0x7800) >> 11) == 5)
{
return true;
}
}
return false;
}
// Generate the encoded NetBIOS name
void EncodeNetBiosName(UCHAR *dst, char *src)
{
char tmp[17];
UINT i;
UINT copy_len;
UINT wp;
// Validate arguments
if (dst == NULL || src == NULL)
{
return;
}
for (i = 0;i < 16;i++)
{
tmp[i] = ' ';
}
tmp[16] = 0;
copy_len = StrLen(src);
if (copy_len > 16)
{
copy_len = 16;
}
Copy(tmp, src, StrLen(src));
wp = 0;
tmp[15] = 0;
for (i = 0;i < 16;i++)
{
char c = tmp[i];
char *s = CharToNetBiosStr(c);
dst[wp++] = s[0];
dst[wp++] = s[1];
}
}
// Convert the string to NetBIOS characters
char *CharToNetBiosStr(char c)
{
c = ToUpper(c);
switch (c)
{
case '\0': return "AA";
case 'A': return "EB";
case 'B': return "EC";
case 'C': return "ED";
case 'D': return "EE";
case 'E': return "EF";
case 'F': return "EG";
case 'G': return "EH";
case 'H': return "EI";
case 'I': return "EJ";
case 'J': return "EK";
case 'K': return "EL";
case 'L': return "EM";
case 'M': return "EN";
case 'N': return "EO";
case 'O': return "EP";
case 'P': return "FA";
case 'Q': return "FB";
case 'R': return "FC";
case 'S': return "FD";
case 'T': return "FE";
case 'U': return "FF";
case 'V': return "FG";
case 'W': return "FH";
case 'X': return "FI";
case 'Y': return "FJ";
case 'Z': return "FK";
case '0': return "DA";
case '1': return "DB";
case '2': return "DC";
case '3': return "DD";
case '4': return "DE";
case '5': return "DF";
case '6': return "DG";
case '7': return "DH";
case '8': return "DI";
case '9': return "DJ";
case ' ': return "CA";
case '!': return "CB";
case '\"': return "CC";
case '#': return "CD";
case '$': return "CE";
case '%': return "CF";
case '&': return "CG";
case '\'': return "CH";
case '(': return "CI";
case ')': return "CJ";
case '*': return "CK";
case '+': return "CL";
case ',': return "CM";
case '-': return "CN";
case '.': return "CO";
case '=': return "DN";
case ':': return "DK";
case ';': return "DL";
case '@': return "EA";
case '^': return "FO";
case '_': return "FP";
case '{': return "HL";
case '}': return "HN";
case '~': return "HO";
}
return "CA";
}
// Process if a NetBIOS name resolution packet for the my host name
bool ProcessNetBiosNameQueryPacketForMyself(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
{
BUF *rb;
USHORT tran_id;
USHORT flags;
USHORT num_query;
USHORT zero1, zero2, zero3;
UCHAR name_size;
UCHAR encoded_name[32];
UCHAR node_type;
USHORT type, classid;
UCHAR my_pc_encoded_name[32];
bool ret = false;
// Validate arguments
if (v == NULL || data == NULL)
{
return false;
}
rb = NewBufFromMemory(data, size);
ReadBuf(rb, &tran_id, sizeof(USHORT));
ReadBuf(rb, &flags, sizeof(USHORT));
flags = Endian16(flags);
ReadBuf(rb, &num_query, sizeof(USHORT));
num_query = Endian16(num_query);
ReadBuf(rb, &zero1, sizeof(USHORT));
ReadBuf(rb, &zero2, sizeof(USHORT));
ReadBuf(rb, &zero3, sizeof(USHORT));
ReadBuf(rb, &name_size, 1);
ReadBuf(rb, encoded_name, 32);
ReadBuf(rb, &node_type, 1);
ReadBuf(rb, &type, sizeof(USHORT));
type = Endian16(type);
if (ReadBuf(rb, &classid, sizeof(USHORT)) == sizeof(USHORT))
{
classid = Endian16(classid);
if (((flags >> 11) & 0x0F) == 0 &&
num_query == 1 && name_size == 0x20 &&
zero1 == 0 && zero2 == 0 && zero3 == 0 && node_type == 0 && type == 0x0020 && classid == 0x0001)
{
char my_pcname[MAX_SIZE];
// Get the encoded name of this PC
Zero(my_pcname, sizeof(my_pcname));
GetMachineHostName(my_pcname, sizeof(my_pcname));
EncodeNetBiosName(my_pc_encoded_name, my_pcname);
if (Cmp(my_pc_encoded_name, encoded_name, 30) == 0)
{
// Assemble the response packet since the name resolution packet which targets this PC name received
BUF *sb = NewBuf();
USHORT us;
UINT ui;
LIST *ip_list;
BUF *ip_list_buf;
bool found = false;
WriteBuf(sb, &tran_id, sizeof(USHORT));
flags = Endian16(0x8500);
WriteBuf(sb, &flags, sizeof(USHORT));
num_query = 0;
WriteBuf(sb, &num_query, sizeof(USHORT));
us = Endian16(1);
WriteBuf(sb, &us, sizeof(USHORT));
us = 0;
WriteBuf(sb, &us, sizeof(USHORT));
WriteBuf(sb, &us, sizeof(USHORT));
name_size = 0x20;
WriteBuf(sb, &name_size, 1);
WriteBuf(sb, encoded_name, 32);
node_type = 0;
WriteBuf(sb, &node_type, 1);
type = Endian16(type);
classid = Endian16(classid);
WriteBuf(sb, &type, sizeof(USHORT));
WriteBuf(sb, &classid, sizeof(USHORT));
ui = Endian32((UINT)(Tick64() / 1000ULL));
WriteBuf(sb, &ui, sizeof(UINT));
ip_list_buf = NewBuf();
ip_list = GetHostIPAddressList();
if (ip_list != NULL)
{
UINT i;
// Return only private IP if there is a private IP
for (i = 0;i < LIST_NUM(ip_list);i++)
{
IP *ip = LIST_DATA(ip_list, i);
if (IsIP4(ip) && IsLocalHostIP4(ip) == false && IsZeroIp(ip) == false)
{
if (IsIPPrivate(ip))
{
USHORT flags = Endian16(0x4000);
UINT ip_uint = IPToUINT(ip);
WriteBuf(ip_list_buf, &flags, sizeof(USHORT));
WriteBuf(ip_list_buf, &ip_uint, sizeof(UINT));
found = true;
}
}
}
if (found == false)
{
// Return all IP if no private IP are found
for (i = 0;i < LIST_NUM(ip_list);i++)
{
IP *ip = LIST_DATA(ip_list, i);
if (IsIP4(ip) && IsLocalHostIP4(ip) == false && IsZeroIp(ip) == false)
{
USHORT flags = Endian16(0x4000);
UINT ip_uint = IPToUINT(ip);
WriteBuf(ip_list_buf, &flags, sizeof(USHORT));
WriteBuf(ip_list_buf, &ip_uint, sizeof(UINT));
found = true;
}
}
}
FreeHostIPAddressList(ip_list);
}
us = Endian16(ip_list_buf->Size);
WriteBuf(sb, &us, sizeof(USHORT));
WriteBufBuf(sb, ip_list_buf);
SendUdp(v, src_ip, src_port, v->HostIP, dest_port, sb->Buf, sb->Size);
FreeBuf(ip_list_buf);
FreeBuf(sb);
WHERE;
}
}
}
FreeBuf(rb);
return ret;
}
// Process the NetBIOS broadcast packet
void UdpRecvForNetBiosBroadcast(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy, bool unicast)
{
// Validate arguments
if (data == NULL || v == NULL)
{
return;
}
// Ignore for NetBIOS name registration packet
if (IsNetbiosRegistrationPacket(data, size) == false)
{
if (unicast == false)
{
dest_ip = 0xFFFFFFFF;
}
if (ProcessNetBiosNameQueryPacketForMyself(v, src_ip, src_port, dest_ip, dest_port, data, size) == false)
{
UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, data, size, false);
}
}
}
// Process the UDP packet to the Internet
void UdpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy)
{
NAT_ENTRY *n, t;
BLOCK *block;
void *buf;
UINT dns_ip = 0;
// Validate arguments
if (data == NULL || v == NULL)
{
return;
}
if (dns_proxy)
{
// Get the DNS server of the proxy to connect to
IP ip;
char tmp[MAX_SIZE];
if (GetDefaultDns(&ip) == false)
{
// Failure
Debug("Failed to GetDefaultDns()\n");
return;
}
dns_ip = IPToUINT(&ip);
IPToStr(tmp, sizeof(tmp), &ip);
Debug("Redirect to DNS Server %s\n", tmp);
}
// Examine whether the NAT entry for this packet has already been created
SetNat(&t, NAT_UDP, src_ip, src_port, dest_ip, dest_port, 0, 0);
n = SearchNat(v, &t);
if (n == NULL)
{
// Create a NAT entry because it is the first packet
n = CreateNatUdp(v, src_ip, src_port, dest_ip, dest_port, dns_proxy ? dns_ip : 0);
if (n == NULL)
{
// Entry creation failed
return;
}
if (dns_proxy)
{
n->ProxyDns = true;
n->DestIpProxy = dns_ip;
}
}
// Set the event by inserting the packet into the queue
buf = Malloc(size);
Copy(buf, data, size);
block = NewBlock(buf, size, 0);
InsertQueue(n->UdpSendQueue, block);
SetSockEvent(v->SockEvent);
}
// Attempt to interpret the DNS packet
bool ParseDnsPacket(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
{
return ParseDnsPacketEx(v, src_ip, src_port, dest_ip, dest_port, data, size, NULL);
}
bool ParseDnsPacketEx(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, DNS_PARSED_PACKET *parsed_result)
{
DNSV4_HEADER *dns;
NAT_ENTRY *nat;
UINT transaction_id;
void *query_data;
UINT query_data_size;
char hostname[256];
// Validate arguments
if (v == NULL || data == NULL || size == 0)
{
return false;
}
// Check the header size
if (size < sizeof(DNSV4_HEADER))
{
// Undersize
return false;
}
// DNS header acquisition
dns = (DNSV4_HEADER *)data;
transaction_id = Endian16(dns->TransactionId);
if ((dns->Flag1 & 78) != 0 || (dns->Flag1 & 0x80) != 0)
{
// Illegal opcode
return false;
}
if (Endian16(dns->NumQuery) != 1)
{
// Number of queries is invalid
return false;
}
query_data = ((UCHAR *)dns) + sizeof(DNSV4_HEADER);
query_data_size = size - sizeof(DNSV4_HEADER);
// Interpret the query
if (ParseDnsQuery(hostname, sizeof(hostname), query_data, query_data_size) == false)
{
// Interpretation fails
return false;
}
if (parsed_result != NULL)
{
// Only analyse without processing
Zero(parsed_result, sizeof(DNS_PARSED_PACKET));
StrCpy(parsed_result->Hostname, sizeof(parsed_result->Hostname), hostname);
parsed_result->TransactionId = transaction_id;
return true;
}
// Create a DNS entry
nat = CreateNatDns(v, src_ip, src_port, dest_ip, dest_port, transaction_id,
false, hostname);
if (nat == false)
{
return false;
}
return true;
}
// Send the NAT DNS response packet
void SendNatDnsResponse(VH *v, NAT_ENTRY *n)
{
BUF *b;
UINT dns_header_size;
DNSV4_HEADER *dns;
UINT src_ip;
// Validate arguments
if (n == NULL || v == NULL)
{
return;
}
// Generate the data
b = NewBuf();
// Add a Query
if (n->DnsGetIpFromHost == false)
{
BuildDnsQueryPacket(b, n->DnsTargetHostName, false);
}
else
{
BuildDnsQueryPacket(b, n->DnsTargetHostName, true);
}
// Add a Response
if (n->DnsOk)
{
if (n->DnsGetIpFromHost == false)
{
BuildDnsResponsePacketA(b, &n->DnsResponseIp);
}
else
{
BuildDnsResponsePacketPtr(b, n->DnsResponseHostName);
}
}
// Generate a DNS header
dns_header_size = sizeof(DNSV4_HEADER) + b->Size;
dns = ZeroMalloc(dns_header_size);
dns->TransactionId = Endian16((USHORT)n->DnsTransactionId);
// Generate a response flag
if (n->DnsOk)
{
dns->Flag1 = 0x85;
dns->Flag2 = 0x80;
}
else
{
dns->Flag1 = 0x85;
dns->Flag2 = 0x83;
}
dns->NumQuery = Endian16(1);
dns->AnswerRRs = Endian16(n->DnsOk != false ? 1 : 0);
dns->AuthorityRRs = 0;
dns->AdditionalRRs = 0;
// Settings, such as the source IP address
src_ip = n->DestIp;
if (src_ip == Endian32(SPECIAL_IPV4_ADDR_LLMNR_DEST) && n->DestPort == SPECIAL_UDP_PORT_LLMNR)
{
// Make a unicast response in the case of LLMNR packet
src_ip = v->HostIP;
dns->Flag1 = 0x84;
dns->Flag2 = 0x00;
}
// Copy data
Copy(((UCHAR *)dns) + sizeof(DNSV4_HEADER), b->Buf, b->Size);
// Send this packet
SendUdp(v, n->SrcIp, n->SrcPort, src_ip, n->DestPort, dns, dns_header_size);
// Release the memory
Free(dns);
FreeBuf(b);
}
// Generate a DNS response packet (host name)
void BuildDnsResponsePacketPtr(BUF *b, char *hostname)
{
USHORT magic;
USHORT type, clas;
UINT ttl;
USHORT len;
BUF *c;
// Validate arguments
if (b == NULL || hostname == NULL)
{
return;
}
magic = Endian16(0xc00c);
type = Endian16(0x000c);
clas = Endian16(0x0001);
ttl = Endian32(NAT_DNS_RESPONSE_TTL);
c = BuildDnsHostName(hostname);
if (c == NULL)
{
return;
}
len = Endian16((USHORT)c->Size);
WriteBuf(b, &magic, 2);
WriteBuf(b, &type, 2);
WriteBuf(b, &clas, 2);
WriteBuf(b, &ttl, 4);
WriteBuf(b, &len, 2);
WriteBuf(b, c->Buf, c->Size);
FreeBuf(c);
}
// Generate a DNS response packet (host IP address)
void BuildDnsResponsePacketA(BUF *b, IP *ip)
{
UINT ip_addr;
USHORT magic;
USHORT type, clas;
UINT ttl;
USHORT len;
// Validate arguments
if (b == NULL || ip == NULL)
{
return;
}
ip_addr = IPToUINT(ip);
magic = Endian16(0xc00c);
type = Endian16(0x0001);
clas = Endian16(0x0001);
ttl = Endian32(NAT_DNS_RESPONSE_TTL);
len = Endian16((USHORT)sizeof(ttl));
WriteBuf(b, &magic, sizeof(magic));
WriteBuf(b, &type, sizeof(type));
WriteBuf(b, &clas, sizeof(clas));
WriteBuf(b, &ttl, sizeof(ttl));
WriteBuf(b, &len, sizeof(len));
WriteBuf(b, &ip_addr, sizeof(ip_addr));
}
// Generate a DNS query data packet
void BuildDnsQueryPacket(BUF *b, char *hostname, bool ptr)
{
USHORT val;
BUF *c;
// Validate arguments
if (b == NULL || hostname == NULL)
{
return;
}
// Convert the host name to a buffer
c = BuildDnsHostName(hostname);
if (c == NULL)
{
return;
}
WriteBuf(b, c->Buf, c->Size);
FreeBuf(c);
// Type and class
if (ptr == false)
{
val = Endian16(0x0001);
}
else
{
val = Endian16(0x000c);
}
WriteBuf(b, &val, 2);
val = Endian16(0x0001);
WriteBuf(b, &val, 2);
}
// Generate a DNS host name buffer
BUF *BuildDnsHostName(char *hostname)
{
UINT i;
UCHAR size;
TOKEN_LIST *token;
BUF *b;
// Validate arguments
if (hostname == NULL)
{
return NULL;
}
// Split the host name into tokens
token = ParseToken(hostname, ".");
if (token == NULL)
{
return NULL;
}
b = NewBuf();
// Add a host string
for (i = 0;i < token->NumTokens;i++)
{
size = (UCHAR)StrLen(token->Token[i]);
WriteBuf(b, &size, 1);
WriteBuf(b, token->Token[i], size);
}
// NULL character
size = 0;
WriteBuf(b, &size, 1);
SeekBuf(b, 0, 0);
FreeToken(token);
return b;
}
// Process the NAT DNS entry
void PollingNatDns(VH *v, NAT_ENTRY *n)
{
// Validate arguments
if (v == NULL || n == NULL)
{
return;
}
if (n->DnsFinished)
{
if (n->DnsPollingFlag == false)
{
n->DnsPollingFlag = true;
// Process has been completed
SendNatDnsResponse(v, n);
// Terminating
n->DisconnectNow = true;
}
}
}
// Create a NAT DNS entry
NAT_ENTRY *CreateNatDns(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port,
UINT transaction_id, bool dns_get_ip_from_host, char *dns_target_host_name)
{
NAT_ENTRY *n;
HUB_OPTION *o;
// Validate arguments
if (v == NULL || dns_target_host_name == NULL)
{
return NULL;
}
if (CanCreateNewNatEntry(v) == false)
{
return NULL;
}
o = NatGetHubOption(v);
if (o != NULL && o->SecureNAT_MaxDnsSessionsPerIp != 0)
{
if (GetNumNatEntriesPerIp(v, src_ip, NAT_DNS, false) >= o->SecureNAT_MaxDnsSessionsPerIp)
{
NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_DNS);
if (oldest != NULL)
{
DisconnectNatEntryNow(v, oldest);
}
}
}
n = ZeroMalloc(sizeof(NAT_ENTRY));
n->Id = Inc(v->Counter);
n->v = v;
n->lock = NewLock();
n->Protocol = NAT_DNS;
n->SrcIp = src_ip;
n->SrcPort = src_port;
n->DestIp = dest_ip;
n->DestPort = dest_port;
n->DnsTransactionId = transaction_id;
n->CreatedTime = n->LastCommTime = v->Now;
n->DisconnectNow = false;
n->DnsGetIpFromHost = false;
n->DnsTargetHostName = CopyStr(dns_target_host_name);
Add(v->NatTable, n);
#if 1
{
IP ip1, ip2;
char s1[MAX_SIZE], s2[MAX_SIZE];
UINTToIP(&ip1, src_ip);
UINTToIP(&ip2, dest_ip);
IPToStr(s1, 0, &ip1);
IPToStr(s2, 0, &ip2);
Debug("NAT_ENTRY: CreateNatDns %s %u -> %s %u\n", s1, src_port, s2, dest_port);
}
#endif
return n;
}
// Set the VGS host name
void SetDnsProxyVgsHostname(char *hostname)
{
// Validate arguments
if (hostname == NULL)
{
return;
}
StrCpy(v_vgs_hostname, sizeof(v_vgs_hostname), hostname);
}
// Operate as a DNS proxy
void DnsProxy(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
{
// Validate arguments
if (v == NULL || data == NULL || size == 0)
{
return;
}
if (dest_port == SPECIAL_UDP_PORT_LLMNR)
{
// Process by analyzing the DNS query in the case of LLMNR
ParseDnsPacket(v, src_ip, src_port, dest_ip, dest_port, data, size);
}
else
{
// Forward the packet as it is in the case of a normal DNS packet
if (IsEmptyStr(v_vgs_hostname) == false)
{
// Response by proxy in the case of trying to get the IP of the VGS
DNS_PARSED_PACKET p;
Zero(&p, sizeof(p));
if (ParseDnsPacketEx(v, src_ip, src_port, dest_ip, dest_port, data, size, &p))
{
if (StrCmpi(p.Hostname, "254.254.211.10.in-addr.arpa") == 0)
{
NAT_ENTRY n;
Zero(&n, sizeof(n));
n.DnsTargetHostName = p.Hostname;
n.DnsGetIpFromHost = true;
n.DnsResponseHostName = v_vgs_hostname;
n.DnsTransactionId = p.TransactionId;
n.DnsOk = true;
n.DestIp = dest_ip;
n.SrcIp = src_ip;
n.DestPort = dest_port;
n.SrcPort = src_port;
SendNatDnsResponse(v, &n);
return;
}
}
}
UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, data, size, true);
}
}
// Process the LLMNR query
void UdpRecvLlmnr(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
{
// Validate arguments
if (data == NULL || v == NULL)
{
return;
}
if (dest_port == SPECIAL_UDP_PORT_LLMNR)
{
// DNS proxy start
DnsProxy(v, src_ip, src_port, dest_ip, dest_port, data, size);
}
}
// Process the UDP packet to the virtual host
void UdpRecvForMe(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
{
// Validate arguments
if (data == NULL || v == NULL)
{
return;
}
if (dest_port == NAT_DNS_PROXY_PORT)
{
// DNS proxy start
DnsProxy(v, src_ip, src_port, dest_ip, dest_port, data, size);
}
}
// Process the UDP broadcast packet
void UdpRecvForBroadcast(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)
{
// Validate arguments
if (data == NULL || v == NULL)
{
return;
}
}
// An UDP packet has been received
void VirtualUdpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, bool mac_broadcast, bool is_localmac, UINT max_l3_size)
{
UDP_HEADER *udp;
UINT packet_length;
void *buf;
UINT buf_size;
UINT src_port, dest_port;
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
// Check the header
udp = (UDP_HEADER *)data;
if (size < UDP_HEADER_SIZE)
{
return;
}
packet_length = Endian16(udp->PacketLength);
if (packet_length != size)
{
return;
}
buf = ((UCHAR *)data) + UDP_HEADER_SIZE;
buf_size = size - UDP_HEADER_SIZE;
src_port = Endian16(udp->SrcPort);
dest_port = Endian16(udp->DstPort);
// Check the port number
if (dest_port == 0)
{
// Port number is invalid
return;
}
// Determine whether it's broadcast packet or packet addressed to myself
if (dest_ip == v->HostIP)
{
// IP packet addressed to myself has arrived
UdpRecvForMe(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);
}
else if ((mac_broadcast || dest_ip == Endian32(0xE00000FC)) && dest_port == SPECIAL_UDP_PORT_LLMNR)
{
if (is_localmac == false)
{
// Packet addressed to 224.0.0.252 (LLMNR) arrives
UdpRecvLlmnr(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);
}
}
else if (mac_broadcast && (dest_port == SPECIAL_UDP_PORT_WSD || dest_port == SPECIAL_UDP_PORT_SSDP))
{
if (is_localmac == false)
{
// WS-Discovery packet arrives
UdpRecvForInternet(v, src_ip, src_port, 0xFFFFFFFF, dest_port, buf, buf_size, false);
}
}
else if (mac_broadcast && (dest_port == SPECIAL_UDP_PORT_NBTDGM || dest_port == SPECIAL_UDP_PORT_NBTNS))
{
if (is_localmac == false)
{
// NetBIOS Broadcast packet arrived
UdpRecvForNetBiosBroadcast(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size, false, false);
}
}
else if (mac_broadcast || dest_ip == 0xffffffff || dest_ip == GetBroadcastAddress(v->HostIP, v->HostMask))
{
if (is_localmac == false)
{
// Broadcast packet arrived
UdpRecvForBroadcast(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);
}
}
else if (IsInNetwork(dest_ip, v->HostIP, v->HostMask) == false)
{
// Packets to other than local address (that is on the Internet) has been received
if (NnIsActive(v) == false)
{
if (v->HubOption != NULL && v->HubOption->DisableUserModeSecureNAT)
{
// User-mode NAT is disabled
return;
}
// User-mode NAT
UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size, false);
}
else
{
// Kernel-mode NAT
NnUdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size, max_l3_size);
}
}
else
{
// Local address has arrived. Ignore it
}
}
// Determine the network address of the subnet to which the specified IP address belongs
UINT GetNetworkAddress(UINT addr, UINT mask)
{
return (addr & mask);
}
// Determine the broadcast address of the subnet to which the specified IP address belongs
UINT GetBroadcastAddress(UINT addr, UINT mask)
{
return ((addr & mask) | (~mask));
}
void GetBroadcastAddress4(IP *dst, IP *addr, IP *mask)
{
// Validate arguments
if (dst == NULL || IsIP4(addr) == false || IsIP4(mask) == false)
{
Zero(dst, sizeof(IP));
return;
}
UINTToIP(dst, GetBroadcastAddress(IPToUINT(addr), IPToUINT(mask)));
}
// Determine whether the specified IP address belongs to the sub-network that is
// represented by a another specified network address and a subnet mask
bool IsInNetwork(UINT uni_addr, UINT network_addr, UINT mask)
{
if (GetNetworkAddress(uni_addr, mask) == GetNetworkAddress(network_addr, mask))
{
return true;
}
return false;
}
// Send an UDP packet
void SendUdp(VH *v, UINT dest_ip, UINT dest_port, UINT src_ip, UINT src_port, void *data, UINT size)
{
UDPV4_PSEUDO_HEADER *vh;
UDP_HEADER *udp;
UINT udp_packet_length = UDP_HEADER_SIZE + size;
USHORT checksum;
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
if (udp_packet_length > 65536)
{
return;
}
// Generate a virtual header
vh = Malloc(sizeof(UDPV4_PSEUDO_HEADER) + size);
udp = (UDP_HEADER *)(((UCHAR *)vh) + 12);
vh->SrcIP = src_ip;
vh->DstIP = dest_ip;
vh->Reserved = 0;
vh->Protocol = IP_PROTO_UDP;
vh->PacketLength1 = Endian16((USHORT)udp_packet_length);
udp->SrcPort = Endian16((USHORT)src_port);
udp->DstPort = Endian16((USHORT)dest_port);
udp->PacketLength = Endian16((USHORT)udp_packet_length);
udp->Checksum = 0;
// Copy data
Copy(((UCHAR *)udp) + UDP_HEADER_SIZE, data, size);
// Calculate the checksum
checksum = IpChecksum(vh, udp_packet_length + 12);
if (checksum == 0x0000)
{
checksum = 0xffff;
}
udp->Checksum = checksum;
// Send a packet
SendIp(v, dest_ip, src_ip, IP_PROTO_UDP, udp, udp_packet_length);
// Release the memory
Free(vh);
}
// Poll the IP combining object
void PollingIpCombine(VH *v)
{
LIST *o;
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
// Discard the old combining object
o = NULL;
for (i = 0;i < LIST_NUM(v->IpCombine);i++)
{
IP_COMBINE *c = LIST_DATA(v->IpCombine, i);
if (c->Expire < v->Now)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, c);
}
}
if (o != NULL)
{
for (i = 0;i < LIST_NUM(o);i++)
{
IP_COMBINE *c = LIST_DATA(o, i);
// Remove from the list
Delete(v->IpCombine, c);
// Release the memory
FreeIpCombine(v, c);
}
ReleaseList(o);
}
}
// Send an ICMP packet
void VirtualIcmpSend(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)
{
ICMP_HEADER *icmp;
void *data_buf;
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
// Build the header
icmp = ZeroMalloc(sizeof(ICMP_HEADER) + size);
// Data copy
data_buf = ((UCHAR *)icmp) + sizeof(ICMP_HEADER);
Copy(data_buf, data, size);
// Other
icmp->Checksum = 0;
icmp->Code = 0;
icmp->Type = ICMP_TYPE_ECHO_RESPONSE;
// Checksum
icmp->Checksum = IpChecksum(icmp, sizeof(ICMP_HEADER) + size);
// IP packet transmission
SendIp(v, dst_ip, src_ip, IP_PROTO_ICMPV4, icmp, sizeof(ICMP_HEADER) + size);
// Release the memory
Free(icmp);
}
// Send the ICMP Echo Response packet
void VirtualIcmpEchoSendResponse(VH *v, UINT src_ip, UINT dst_ip, USHORT id, USHORT seq_no, void *data, UINT size)
{
ICMP_ECHO *e;
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
// Build the header
e = ZeroMalloc(sizeof(ICMP_ECHO) + size);
e->Identifier = Endian16(id);
e->SeqNo = Endian16(seq_no);
// Data copy
Copy(((UCHAR *)e) + sizeof(ICMP_ECHO), data, size);
// Send an ICMP
VirtualIcmpSend(v, src_ip, dst_ip, e, sizeof(ICMP_ECHO) + size);
// Release the memory
Free(e);
}
// Treat the ICMP Echo Request packet with a Raw Socket
void VirtualIcmpEchoRequestReceivedRaw(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size, UCHAR ttl, void *icmp_data, UINT icmp_size, UCHAR *ip_header, UINT ip_header_size)
{
ICMP_ECHO *echo;
UINT data_size;
void *data_buf;
USHORT id, seq_no;
void *buf;
BLOCK *block;
// Validate arguments
if (v == NULL || data == NULL || icmp_data == NULL || ip_header == NULL)
{
return;
}
if (ttl == 0)
{
ttl = 1;
}
echo = (ICMP_ECHO *)data;
// Echo size check
if (size < sizeof(ICMP_ECHO))
{
// Insufficient data
return;
}
id = Endian16(echo->Identifier);
seq_no = Endian16(echo->SeqNo);
// Data size
data_size = size - sizeof(ICMP_ECHO);
// Data body
data_buf = ((UCHAR *)data) + sizeof(ICMP_ECHO);
if (dst_ip == v->HostIP)
{
// Respond because it is addressed to me
VirtualIcmpEchoSendResponse(v, v->HostIP, src_ip, id, seq_no, data_buf, data_size);
}
else if (IsInNetwork(dst_ip, v->HostIP, v->HostMask) == false)
{
NAT_ENTRY *n = NULL, t;
// Process by creating a NAT entry because it is addressed to the Internet
if (ttl <= 1)
{
// Reply the Time Exceeded immediately for the packet whose TTL is 1
UINT reply_size = sizeof(ICMP_HEADER) + 4 + ip_header_size + 8;
UCHAR *reply_data = ZeroMalloc(reply_size);
ICMP_HEADER *icmp = (ICMP_HEADER *)reply_data;
icmp->Type = ICMP_TYPE_TIME_EXCEEDED;
icmp->Code = ICMP_CODE_TTL_EXCEEDED_IN_TRANSIT;
Copy(reply_data + sizeof(ICMP_HEADER) + 4, ip_header, ip_header_size);
Copy(reply_data + sizeof(ICMP_HEADER) + 4 + ip_header_size, icmp_data, MIN(icmp_size, 8));
icmp->Checksum = IpChecksum(icmp, reply_size);
SendIp(v, src_ip, v->HostIP, IP_PROTO_ICMPV4, reply_data, reply_size);
Free(reply_data);
}
else
{
SetNat(&t, NAT_ICMP, src_ip, id, dst_ip, id, 0, 0);
if (v->IcmpRawSocketOk)
{
// Examine whether a NAT entry for this packet has already been created
n = SearchNat(v, &t);
}
if (n == NULL)
{
// Create a NAT entry because it is the first packet
n = CreateNatIcmp(v, src_ip, id, dst_ip, id, (UCHAR *)ip_header, ip_header_size + 8);
if (n == NULL)
{
// Entry creation failed
return;
}
}
// Set the event by inserting the packet into the queue
buf = Malloc(icmp_size);
Copy(buf, icmp_data, icmp_size);
block = NewBlock(buf, icmp_size, 0);
block->Ttl = MAKESURE(ttl - 1, 1, 255);
InsertQueue(n->UdpSendQueue, block);
SetSockEvent(v->SockEvent);
}
}
}
// Receive an ICMP Echo Request packet
void VirtualIcmpEchoRequestReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size, UCHAR ttl, void *icmp_data, UINT icmp_size, UCHAR *ip_header, UINT ip_header_size, UINT max_l3_size)
{
ICMP_ECHO *echo;
UINT data_size;
void *data_buf;
USHORT id, seq_no;
// Validate arguments
if (v == NULL || data == NULL || icmp_data == NULL)
{
return;
}
//Debug("ICMP: %u\n", size);
if (NnIsActive(v))
{
// Process by the Native NAT
NnIcmpEchoRecvForInternet(v, src_ip, dst_ip, data, size, ttl, icmp_data, icmp_size,
ip_header, ip_header_size, max_l3_size);
return;
}
if (v->HubOption != NULL && v->HubOption->DisableUserModeSecureNAT)
{
// User-mode NAT is disabled
return;
}
if (v->IcmpRawSocketOk || v->IcmpApiOk)
{
// Process in the Raw Socket
VirtualIcmpEchoRequestReceivedRaw(v, src_ip, dst_ip, data, size, ttl, icmp_data, icmp_size,
ip_header, ip_header_size);
return;
}
// Returns the fake ICMP forcibly if any of Native NAT or Raw Socket can not be used
echo = (ICMP_ECHO *)data;
// Echo size check
if (size < sizeof(ICMP_ECHO))
{
// Insufficient data
return;
}
id = Endian16(echo->Identifier);
seq_no = Endian16(echo->SeqNo);
// Data size
data_size = size - sizeof(ICMP_ECHO);
// Data body
data_buf = ((UCHAR *)data) + sizeof(ICMP_ECHO);
// Return the ICMP Echo Response
VirtualIcmpEchoSendResponse(v, dst_ip, src_ip, id, seq_no, data_buf, data_size);
}
// An ICMP packet has been received
void VirtualIcmpReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size, UCHAR ttl, UCHAR *ip_header, UINT ip_header_size, UINT max_l3_size)
{
ICMP_HEADER *icmp;
UINT msg_size;
USHORT checksum_calc, checksum_original;
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
// Size check
if (size < sizeof(ICMP_HEADER))
{
return;
}
// ICMP header
icmp = (ICMP_HEADER *)data;
// Get the ICMP message size
msg_size = size - sizeof(ICMP_HEADER);
// Check the checksum of the ICMP header
checksum_original = icmp->Checksum;
icmp->Checksum = 0;
checksum_calc = IpChecksum(data, size);
icmp->Checksum = checksum_original;
if (checksum_calc != checksum_original)
{
// Checksum is invalid
Debug("ICMP CheckSum Failed.\n");
return;
}
// Identified by the opcode
switch (icmp->Type)
{
case ICMP_TYPE_ECHO_REQUEST: // ICMP Echo request
VirtualIcmpEchoRequestReceived(v, src_ip, dst_ip, ((UCHAR *)data) + sizeof(ICMP_HEADER), msg_size, ttl,
icmp, size, ip_header, ip_header_size, max_l3_size);
break;
case ICMP_TYPE_ECHO_RESPONSE: // ICMP Echo response
// Do Nothing
break;
}
}
// Received an IP packet
void IpReceived(VH *v, UINT src_ip, UINT dest_ip, UINT protocol, void *data, UINT size, bool mac_broadcast, UCHAR ttl, UCHAR *ip_header, UINT ip_header_size, bool is_local_mac, UINT max_l3_size)
{
// Validate arguments
if (v == NULL || data == NULL)
{
return;
}
// Deliver the data to the supported high-level protocol
switch (protocol)
{
case IP_PROTO_ICMPV4: // ICMPv4
if (mac_broadcast == false)
{
VirtualIcmpReceived(v, src_ip, dest_ip, data, size, ttl, ip_header, ip_header_size, max_l3_size);
}
break;
case IP_PROTO_TCP: // TCP
if (mac_broadcast == false)
{
VirtualTcpReceived(v, src_ip, dest_ip, data, size, max_l3_size);
}
break;
case IP_PROTO_UDP: // UDP
VirtualUdpReceived(v, src_ip, dest_ip, data, size, mac_broadcast, is_local_mac, max_l3_size);
break;
}
}
// Combine the IP packet received to the IP combining object
void CombineIp(VH *v, IP_COMBINE *c, UINT offset, void *data, UINT size, bool last_packet, UCHAR *head_ip_header_data, UINT head_ip_header_size)
{
UINT i;
IP_PART *p;
UINT need_size;
UINT data_size_delta;
// Validate arguments
if (c == NULL || data == NULL)
{
return;
}
// Check the size and offset
if ((offset + size) > 65535)
{
// Do not process packet larger than 64Kbytes
return;
}
if (last_packet == false && c->Size != 0)
{
if ((offset + size) > c->Size)
{
// Do not process the packet larger than the packet size
return;
}
}
if (head_ip_header_data != NULL && head_ip_header_size >= sizeof(IPV4_HEADER))
{
if (c->HeadIpHeaderData == NULL)
{
c->HeadIpHeaderData = Clone(head_ip_header_data, head_ip_header_size);
c->HeadIpHeaderDataSize = head_ip_header_size;
}
}
need_size = offset + size;
data_size_delta = c->DataReserved;
// Ensure sufficient if the buffer is insufficient
while (c->DataReserved < need_size)
{
c->DataReserved = c->DataReserved * 4;
c->Data = ReAlloc(c->Data, c->DataReserved);
}
data_size_delta = c->DataReserved - data_size_delta;
v->CurrentIpQuota += data_size_delta;
// Overwrite the data into the buffer
Copy(((UCHAR *)c->Data) + offset, data, size);
if (last_packet)
{
// If No More Fragment packet arrives, the size of this datagram is finalized
c->Size = offset + size;
}
// Check the overlap between the region which is represented by the offset and size of the
// existing received list and the region which is represented by the offset and size
for (i = 0;i < LIST_NUM(c->IpParts);i++)
{
UINT moving_size;
IP_PART *p = LIST_DATA(c->IpParts, i);
// Check the overlapping between the existing area and head area
if ((p->Offset <= offset) && ((p->Offset + p->Size) > offset))
{
// Compress behind the offset of this packet since a duplication is
// found in the first part with the existing packet and this packet
if ((offset + size) <= (p->Offset + p->Size))
{
// This packet is buried in the existing packet
size = 0;
}
else
{
// Retral region is not overlapped
moving_size = p->Offset + p->Size - offset;
offset += moving_size;
size -= moving_size;
}
}
if ((p->Offset < (offset + size)) && ((p->Offset + p->Size) >= (offset + size)))
{
// Compress the size of this packet forward because a duplication is
// found between the posterior portion the existing packet and this packet
moving_size = p->Offset + p->Size - offset - size;
size -= moving_size;
}
if ((p->Offset >= offset) && ((p->Offset + p->Size) <= (offset + size)))
{
// This packet was overwritten to completely cover an existing packet
p->Size = 0;
}
}
if (size != 0)
{
// Register this packet
p = ZeroMalloc(sizeof(IP_PART));
p->Offset = offset;
p->Size = size;
Add(c->IpParts, p);
}
if (c->Size != 0)
{
// Get the total size of the data portion list already received
UINT total_size = 0;
UINT i;
for (i = 0;i < LIST_NUM(c->IpParts);i++)
{
IP_PART *p = LIST_DATA(c->IpParts, i);
total_size += p->Size;
}
if (total_size == c->Size)
{
// Received all of the IP packet
IpReceived(v, c->SrcIP, c->DestIP, c->Protocol, c->Data, c->Size, c->MacBroadcast, c->Ttl,
c->HeadIpHeaderData, c->HeadIpHeaderDataSize, c->SrcIsLocalMacAddr, c->MaxL3Size);
// Release the combining object
FreeIpCombine(v, c);
// Remove from the combining object list
Delete(v->IpCombine, c);
}
}
}
// Release the IP combining object
void FreeIpCombine(VH *v, IP_COMBINE *c)
{
UINT i;
// Validate arguments
if (c == NULL)
{
return;
}
// Release the data
v->CurrentIpQuota -= c->DataReserved;
Free(c->Data);
// Release the partial list
for (i = 0;i < LIST_NUM(c->IpParts);i++)
{
IP_PART *p = LIST_DATA(c->IpParts, i);
Free(p);
}
Free(c->HeadIpHeaderData);
ReleaseList(c->IpParts);
Free(c);
}
// Search the IP combining list
IP_COMBINE *SearchIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol)
{
IP_COMBINE *c, t;
// Validate arguments
if (v == NULL)
{
return NULL;
}
t.DestIP = dest_ip;
t.SrcIP = src_ip;
t.Id = id;
t.Protocol = protocol;
c = Search(v->IpCombine, &t);
return c;
}
// Insert by creating a new object to the IP combining list
IP_COMBINE *InsertIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol, bool mac_broadcast, UCHAR ttl, bool src_is_localmac)
{
IP_COMBINE *c;
// Validate arguments
if (v == NULL)
{
return NULL;
}
// Examine the quota
if ((v->CurrentIpQuota + IP_COMBINE_INITIAL_BUF_SIZE) > IP_COMBINE_WAIT_QUEUE_SIZE_QUOTA)
{
// IP packet can not be stored any more
return NULL;
}
c = ZeroMalloc(sizeof(IP_COMBINE));
c->SrcIsLocalMacAddr = src_is_localmac;
c->DestIP = dest_ip;
c->SrcIP = src_ip;
c->Id = id;
c->Expire = v->Now + (UINT64)IP_COMBINE_TIMEOUT;
c->Size = 0;
c->IpParts = NewList(NULL);
c->Protocol = protocol;
c->MacBroadcast = mac_broadcast;
c->Ttl = ttl;
// Secure the memory
c->DataReserved = IP_COMBINE_INITIAL_BUF_SIZE;
c->Data = Malloc(c->DataReserved);
v->CurrentIpQuota += c->DataReserved;
Insert(v->IpCombine, c);
return c;
}
// Initialize the IP combining list
void InitIpCombineList(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
v->IpCombine = NewList(CompareIpCombine);
}
// Release the IP combining list
void FreeIpCombineList(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(v->IpCombine);i++)
{
IP_COMBINE *c = LIST_DATA(v->IpCombine, i);
FreeIpCombine(v, c);
}
ReleaseList(v->IpCombine);
}
// Comparison of IP combining list entry
int CompareIpCombine(void *p1, void *p2)
{
IP_COMBINE *c1, *c2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
c1 = *(IP_COMBINE **)p1;
c2 = *(IP_COMBINE **)p2;
if (c1 == NULL || c2 == NULL)
{
return 0;
}
if (c1->Id > c2->Id)
{
return 1;
}
else if (c1->Id < c2->Id)
{
return -1;
}
else if (c1->DestIP > c2->DestIP)
{
return 1;
}
else if (c1->DestIP < c2->DestIP)
{
return -1;
}
else if (c1->SrcIP > c2->SrcIP)
{
return 1;
}
else if (c1->SrcIP < c2->SrcIP)
{
return -1;
}
else if (c1->Protocol > c2->Protocol)
{
return 1;
}
else if (c1->Protocol < c2->Protocol)
{
return -1;
}
return 0;
}
// Received an IP packet
void VirtualIpReceived(VH *v, PKT *packet)
{
IPV4_HEADER *ip;
void *data;
UINT data_size_recved;
UINT size;
UINT ipv4_header_size;
bool last_packet;
UCHAR *head_ip_header_data = NULL;
UINT head_ip_header_size = 0;
bool is_local_mac = false;
UINT ip_l3_size;
// Validate arguments
if (v == NULL || packet == NULL)
{
return;
}
ip = packet->L3.IPv4Header;
if (packet->BroadcastPacket)
{
is_local_mac = IsMacAddressLocalFast(packet->MacAddressSrc);
}
// Get the size of the IPv4 header
ipv4_header_size = IPV4_GET_HEADER_LEN(packet->L3.IPv4Header) * 4;
head_ip_header_size = ipv4_header_size;
// Calculate the checksum of the IPv4 header
if (IpCheckChecksum(ip) == false)
{
return;
}
// Get a pointer to the data
data = ((UCHAR *)packet->L3.PointerL3) + ipv4_header_size;
// Register to the ARP table
ArpIpWasKnown(v, packet->L3.IPv4Header->SrcIP, packet->MacAddressSrc);
// Get the data size
size = ip_l3_size = Endian16(ip->TotalLength);
if (size <= ipv4_header_size)
{
// There is no data
return;
}
size -= ipv4_header_size;
// Get the size of data actually received
data_size_recved = packet->PacketSize - (ipv4_header_size + MAC_HEADER_SIZE);
if (data_size_recved < size)
{
// Data insufficient (It may be missing on the way)
return;
}
if (IPV4_GET_OFFSET(ip) == 0 && (IPV4_GET_FLAGS(ip) & 0x01) == 0)
{
// Because this packet has not been fragmented, it can be delivered to the upper layer immediately
head_ip_header_data = (UCHAR *)packet->L3.IPv4Header;
IpReceived(v, ip->SrcIP, ip->DstIP, ip->Protocol, data, size, packet->BroadcastPacket, ip->TimeToLive,
head_ip_header_data, head_ip_header_size, is_local_mac, ip_l3_size);
}
else
{
// This packet is necessary to combine because it is fragmented
UINT offset = IPV4_GET_OFFSET(ip) * 8;
IP_COMBINE *c = SearchIpCombine(v, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol);
if (offset == 0)
{
head_ip_header_data = (UCHAR *)packet->L3.IPv4Header;
}
last_packet = ((IPV4_GET_FLAGS(ip) & 0x01) == 0 ? true : false);
if (c != NULL)
{
// It is the second or subsequent packet
c->MaxL3Size = MAX(c->MaxL3Size, ip_l3_size);
CombineIp(v, c, offset, data, size, last_packet, head_ip_header_data, head_ip_header_size);
}
else
{
// Create a combining object because it is the first packet
c = InsertIpCombine(
v, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol, packet->BroadcastPacket,
ip->TimeToLive, is_local_mac);
if (c != NULL)
{
c->MaxL3Size = ip_l3_size;
CombineIp(v, c, offset, data, size, last_packet, head_ip_header_data, head_ip_header_size);
}
}
}
}
// Send the waiting IP packets from the specified IP address
void SendWaitingIp(VH *v, UCHAR *mac, UINT dest_ip)
{
UINT i;
LIST *o = NULL;
// Validate arguments
if (v == NULL || mac == NULL)
{
return;
}
// Get a target list
for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)
{
IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);
if (w->DestIP == dest_ip)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, w);
}
}
// Send the target packets at once
if (o != NULL)
{
for (i = 0;i < LIST_NUM(o);i++)
{
IP_WAIT *w = LIST_DATA(o, i);
// Transmission processing
VirtualIpSend(v, mac, w->Data, w->Size);
// Remove from the list
Delete(v->IpWaitTable, w);
// Release the memory
Free(w->Data);
Free(w);
}
ReleaseList(o);
}
}
// Remove the old IP waiting table entries
void DeleteOldIpWaitTable(VH *v)
{
UINT i;
LIST *o = NULL;
// Validate arguments
if (v == NULL)
{
return;
}
// Get the deleting list
for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)
{
IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);
if (w->Expire < v->Now)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, w);
}
}
// Delete all at once
if (o != NULL)
{
for (i = 0;i < LIST_NUM(o);i++)
{
IP_WAIT *w = LIST_DATA(o, i);
// Remove from the list
Delete(v->IpWaitTable, w);
// Release the memory
Free(w->Data);
Free(w);
}
ReleaseList(o);
}
}
// Poll the IP waiting table
void PollingIpWaitTable(VH *v)
{
// Delete the old table entries
DeleteOldIpWaitTable(v);
}
// Insert the IP packet to the IP waiting table
void InsertIpWaitTable(VH *v, UINT dest_ip, UINT src_ip, void *data, UINT size)
{
IP_WAIT *w;
// Validate arguments
if (v == NULL || data == NULL || size == 0)
{
return;
}
w = ZeroMalloc(sizeof(IP_WAIT));
w->Data = data;
w->Size = size;
w->SrcIP = src_ip;
w->DestIP = dest_ip;
w->Expire = v->Now + (UINT64)IP_WAIT_FOR_ARP_TIMEOUT;
Add(v->IpWaitTable, w);
}
// Initialize the IP waiting table
void InitIpWaitTable(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
v->IpWaitTable = NewList(NULL);
}
// Release the IP waiting table
void FreeIpWaitTable(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)
{
IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);
Free(w->Data);
Free(w);
}
ReleaseList(v->IpWaitTable);
}
// MAC address for the IP address is found because something such as an ARP Response arrives
void ArpIpWasKnown(VH *v, UINT ip, UCHAR *mac)
{
// Validate arguments
if (v == NULL || mac == NULL)
{
return;
}
// If there is a query for this IP address in the ARP queue, delete it
DeleteArpWaitTable(v, ip);
// Update or register in the ARP table
InsertArpTable(v, mac, ip);
// Send the IP packets waiting in the IP waiting list
SendWaitingIp(v, mac, ip);
}
// Re-issue ARPs by checking the ARP waiting list
void PollingArpWaitTable(VH *v)
{
UINT i;
LIST *o;
// Validate arguments
if (v == NULL)
{
return;
}
// Initialize the deletion list
o = NULL;
// Scan whole ARP waiting list
for (i = 0;i < LIST_NUM(v->ArpWaitTable);i++)
{
ARP_WAIT *w = LIST_DATA(v->ArpWaitTable, i);
if (w->GiveupTime < v->Now || (w->GiveupTime - 100 * 1000) > v->Now)
{
// Give up the sending of ARP
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, w);
}
else
{
if (w->TimeoutTime < v->Now)
{
// Send an ARP again
VirtualArpSendRequest(v, w->IpAddress);
// Set the next timeout time
w->TimeoutTime = v->Now + (UINT64)w->NextTimeoutTimeValue;
// Increase the ARP transmission interval of the second and subsequent
w->NextTimeoutTimeValue = w->NextTimeoutTimeValue + ARP_REQUEST_TIMEOUT;
}
}
}
// Remove if there is a ARP waiting record to be deleted
if (o != NULL)
{
for (i = 0;i < LIST_NUM(o);i++)
{
ARP_WAIT *w = LIST_DATA(o, i);
DeleteArpWaitTable(v, w->IpAddress);
}
ReleaseList(o);
}
}
// Issue an ARP
void SendArp(VH *v, UINT ip)
{
ARP_WAIT *w;
// Validate arguments
if (v == NULL)
{
return;
}
// Examine whether the destination IP address has been registered in the ARP waiting list first
w = SearchArpWaitTable(v, ip);
if (w != NULL)
{
// Do not do anything because it is already registered
return;
}
// Send an ARP packet first
VirtualArpSendRequest(v, ip);
// Register in the ARP waiting list
w = ZeroMalloc(sizeof(ARP_WAIT));
w->GiveupTime = v->Now + (UINT64)ARP_REQUEST_GIVEUP;
w->TimeoutTime = v->Now + (UINT64)ARP_REQUEST_TIMEOUT;
w->NextTimeoutTimeValue = ARP_REQUEST_TIMEOUT;
w->IpAddress = ip;
InsertArpWaitTable(v, w);
}
// Delete the ARP waiting table
void DeleteArpWaitTable(VH *v, UINT ip)
{
ARP_WAIT *w;
// Validate arguments
if (v == NULL)
{
return;
}
w = SearchArpWaitTable(v, ip);
if (w == NULL)
{
return;
}
Delete(v->ArpWaitTable, w);
Free(w);
}
// Search the ARP waiting table
ARP_WAIT *SearchArpWaitTable(VH *v, UINT ip)
{
ARP_WAIT *w, t;
// Validate arguments
if (v == NULL)
{
return NULL;
}
t.IpAddress = ip;
w = Search(v->ArpWaitTable, &t);
return w;
}
// Register in the ARP waiting table
void InsertArpWaitTable(VH *v, ARP_WAIT *w)
{
// Validate arguments
if (v == NULL || w == NULL)
{
return;
}
Add(v->ArpWaitTable, w);
}
// Initialize the ARP waiting table
void InitArpWaitTable(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
v->ArpWaitTable = NewList(CompareArpWaitTable);
}
// Release the ARP waiting table
void FreeArpWaitTable(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
for (i = 0;i < LIST_NUM(v->ArpWaitTable);i++)
{
ARP_WAIT *w = LIST_DATA(v->ArpWaitTable, i);
Free(w);
}
ReleaseList(v->ArpWaitTable);
}
// Check whether the MAC address is valid
bool IsMacInvalid(UCHAR *mac)
{
UINT i;
// Validate arguments
if (mac == NULL)
{
return false;
}
for (i = 0;i < 6;i++)
{
if (mac[i] != 0x00)
{
return false;
}
}
return true;
}
// Check whether the MAC address is a broadcast address
bool IsMacBroadcast(UCHAR *mac)
{
UINT i;
// Validate arguments
if (mac == NULL)
{
return false;
}
for (i = 0;i < 6;i++)
{
if (mac[i] != 0xff)
{
return false;
}
}
return true;
}
// Insert an entry in the ARP table
void InsertArpTable(VH *v, UCHAR *mac, UINT ip)
{
ARP_ENTRY *e, t;
// Validate arguments
if (v == NULL || mac == NULL || ip == 0 || ip == 0xffffffff || IsMacBroadcast(mac) || IsMacInvalid(mac))
{
return;
}
// Check whether the same IP address is not already registered
t.IpAddress = ip;
e = Search(v->ArpTable, &t);
if (e != NULL)
{
// Override this simply because it was registered
if (Cmp(e->MacAddress, mac, 6) != 0)
{
e->Created = v->Now;
Copy(e->MacAddress, mac, 6);
}
e->Expire = v->Now + (UINT64)ARP_ENTRY_EXPIRES;
}
else
{
// Create a new entry
e = ZeroMalloc(sizeof(ARP_ENTRY));
e->Created = v->Now;
e->Expire = v->Now + (UINT64)ARP_ENTRY_EXPIRES;
Copy(e->MacAddress, mac, 6);
e->IpAddress = ip;
Add(v->ArpTable, e);
}
}
// Poll the ARP table
void PollingArpTable(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
if (v->Now > v->NextArpTablePolling)
{
v->NextArpTablePolling = v->Now + (UINT64)ARP_ENTRY_POLLING_TIME;
RefreshArpTable(v);
}
}
// Remove the old ARP entries
void RefreshArpTable(VH *v)
{
UINT i;
LIST *o;
// Validate arguments
if (v == NULL)
{
return;
}
o = NewListFast(NULL);
for (i = 0;i < LIST_NUM(v->ArpTable);i++)
{
ARP_ENTRY *e = LIST_DATA(v->ArpTable, i);
// Check for expired
if (e->Expire < v->Now)
{
// Expired
Add(o, e);
}
}
// Remove expired entries at once
for (i = 0;i < LIST_NUM(o);i++)
{
ARP_ENTRY *e = LIST_DATA(o, i);
Delete(v->ArpTable, e);
Free(e);
}
ReleaseList(o);
}
// Search the ARP table
ARP_ENTRY *SearchArpTable(VH *v, UINT ip)
{
ARP_ENTRY *e, t;
// Validate arguments
if (v == NULL)
{
return NULL;
}
t.IpAddress = ip;
e = Search(v->ArpTable, &t);
return e;
}
// Initialize the ARP table
void InitArpTable(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
v->ArpTable = NewList(CompareArpTable);
}
// Release the ARP table
void FreeArpTable(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
// Delete all entries
for (i = 0;i < LIST_NUM(v->ArpTable);i++)
{
ARP_ENTRY *e = LIST_DATA(v->ArpTable, i);
Free(e);
}
ReleaseList(v->ArpTable);
}
// Comparison of the ARP waiting table entry
int CompareArpWaitTable(void *p1, void *p2)
{
ARP_WAIT *e1, *e2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
e1 = *(ARP_WAIT **)p1;
e2 = *(ARP_WAIT **)p2;
if (e1 == NULL || e2 == NULL)
{
return 0;
}
if (e1->IpAddress > e2->IpAddress)
{
return 1;
}
else if (e1->IpAddress < e2->IpAddress)
{
return -1;
}
return 0;
}
// Comparison of the ARP table entry
int CompareArpTable(void *p1, void *p2)
{
ARP_ENTRY *e1, *e2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
e1 = *(ARP_ENTRY **)p1;
e2 = *(ARP_ENTRY **)p2;
if (e1 == NULL || e2 == NULL)
{
return 0;
}
if (e1->IpAddress > e2->IpAddress)
{
return 1;
}
else if (e1->IpAddress < e2->IpAddress)
{
return -1;
}
return 0;
}
// Initialize the virtual host
bool VirtualInit(VH *v)
{
// Initialize the log
v->Logger = NULL;
LockVirtual(v);
{
// Initialize
v->Cancel = NewCancel();
v->SendQueue = NewQueue();
}
UnlockVirtual(v);
// Counter reset
v->Counter->c = 0;
v->DhcpId = 0;
// Initialize the ARP table
InitArpTable(v);
// Initialize the ARP waiting table
InitArpWaitTable(v);
// Initialize the IP waiting table
InitIpWaitTable(v);
// Initialize the IP combining list
InitIpCombineList(v);
// Initialize the NAT
InitNat(v);
// Initialize the DHCP server
InitDhcpServer(v);
// Other initialization
v->flag1 = false;
v->NextArpTablePolling = Tick64() + (UINT64)ARP_ENTRY_POLLING_TIME;
v->CurrentIpQuota = 0;
v->Active = true;
return true;
}
bool VirtualPaInit(SESSION *s)
{
VH *v;
// Validate arguments
if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
{
return false;
}
return VirtualInit(v);
}
// Get the cancel object of the virtual host
CANCEL *VirtualPaGetCancel(SESSION *s)
{
VH *v;
// Validate arguments
if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
{
return NULL;
}
AddRef(v->Cancel->ref);
return v->Cancel;
}
// Get the next packet from the virtual host
UINT VirtualGetNextPacket(VH *v, void **data)
{
UINT ret = 0;
START:
// Examine the transmission queue
LockQueue(v->SendQueue);
{
BLOCK *block = GetNext(v->SendQueue);
if (block != NULL)
{
// There is a packet
ret = block->Size;
*data = block->Buf;
// Discard the structure
Free(block);
}
}
UnlockQueue(v->SendQueue);
if (ret == 0)
{
LockVirtual(v);
{
v->Now = Tick64();
// Polling process
VirtualPolling(v);
}
UnlockVirtual(v);
if (v->SendQueue->num_item != 0)
{
goto START;
}
}
return ret;
}
UINT VirtualPaGetNextPacket(SESSION *s, void **data)
{
VH *v;
// Validate arguments
if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
{
return INFINITE;
}
return VirtualGetNextPacket(v, data);
}
// Polling process (Always called once in a SessionMain loop)
void VirtualPolling(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
// DHCP polling
PollingDhcpServer(v);
// NAT polling
PoolingNat(v);
// Clear the old ARP table entries
PollingArpTable(v);
// Poll the ARP waiting list
PollingArpWaitTable(v);
// Poll the IP waiting list
PollingIpWaitTable(v);
// Poll the IP combining list
PollingIpCombine(v);
// Beacon transmission procedure
PollingBeacon(v);
}
// Beacon transmission procedure
void PollingBeacon(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
if (v->LastSendBeacon == 0 ||
((v->LastSendBeacon + BEACON_SEND_INTERVAL) <= Tick64()))
{
v->LastSendBeacon = Tick64();
SendBeacon(v);
}
}
// Send a Layer-2 packet
void VirtualLayer2Send(VH *v, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size)
{
MAC_HEADER *mac_header;
UCHAR *buf;
BLOCK *block;
// Validate arguments
if (v == NULL || dest_mac == NULL || src_mac == NULL || data == NULL || size > (MAX_PACKET_SIZE - sizeof(MAC_HEADER)))
{
return;
}
// Create buffer
buf = Malloc(MAC_HEADER_SIZE + size);
// MAC header
mac_header = (MAC_HEADER *)&buf[0];
Copy(mac_header->DestAddress, dest_mac, 6);
Copy(mac_header->SrcAddress, src_mac, 6);
mac_header->Protocol = Endian16(protocol);
// Copy data
Copy(&buf[sizeof(MAC_HEADER)], data, size);
// Size
size += sizeof(MAC_HEADER);
// Generate the packet
block = NewBlock(buf, size, 0);
// Insert into the queue
LockQueue(v->SendQueue);
{
InsertQueue(v->SendQueue, block);
}
UnlockQueue(v->SendQueue);
// Cancel
Cancel(v->Cancel);
}
// Send an IP packet (with automatic fragmentation)
void SendIp(VH *v, UINT dest_ip, UINT src_ip, UCHAR protocol, void *data, UINT size)
{
SendIpEx(v, dest_ip, src_ip, protocol, data, size, 0);
}
void SendIpEx(VH *v, UINT dest_ip, UINT src_ip, UCHAR protocol, void *data, UINT size, UCHAR ttl)
{
UINT mss;
UCHAR *buf;
USHORT offset;
USHORT id;
USHORT total_size;
UINT size_of_this_packet;
// Validate arguments
if (v == NULL || data == NULL || size == 0 || size > MAX_IP_DATA_SIZE_TOTAL)
{
return;
}
// Maximum segment size
mss = v->IpMss;
// Buffer
buf = (UCHAR *)data;
// ID
id = (v->NextId++);
// Total size
total_size = (USHORT)size;
// Start to split
offset = 0;
while (true)
{
bool last_packet = false;
// Gets the size of this packet
size_of_this_packet = MIN((USHORT)mss, (total_size - offset));
if ((offset + (USHORT)size_of_this_packet) == total_size)
{
last_packet = true;
}
// Transmit the fragmented packet
SendFragmentedIp(v, dest_ip, src_ip, id,
total_size, offset, protocol, buf + offset, size_of_this_packet, NULL, ttl);
if (last_packet)
{
break;
}
offset += (USHORT)size_of_this_packet;
}
}
// Reserve to send the fragmented IP packet
void SendFragmentedIp(VH *v, UINT dest_ip, UINT src_ip, USHORT id, USHORT total_size, USHORT offset, UCHAR protocol, void *data, UINT size, UCHAR *dest_mac, UCHAR ttl)
{
UCHAR *buf;
IPV4_HEADER *ip;
ARP_ENTRY *arp;
// Validate arguments
if (v == NULL || data == NULL || size == 0)
{
return;
}
// Memory allocation
buf = Malloc(size + IP_HEADER_SIZE);
ip = (IPV4_HEADER *)&buf[0];
// IP header construction
ip->VersionAndHeaderLength = 0;
IPV4_SET_VERSION(ip, 4);
IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4));
ip->TypeOfService = DEFAULT_IP_TOS;
ip->TotalLength = Endian16((USHORT)(size + IP_HEADER_SIZE));
ip->Identification = Endian16(id);
ip->FlagsAndFragmentOffset[0] = ip->FlagsAndFragmentOffset[1] = 0;
IPV4_SET_OFFSET(ip, (offset / 8));
if ((offset + size) >= total_size)
{
IPV4_SET_FLAGS(ip, 0x00);
}
else
{
IPV4_SET_FLAGS(ip, 0x01);
}
ip->TimeToLive = (ttl == 0 ? DEFAULT_IP_TTL : ttl);
ip->Protocol = protocol;
ip->Checksum = 0;
ip->SrcIP = src_ip;
ip->DstIP = dest_ip;
// Checksum calculation
ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE);
// Data copy
Copy(buf + IP_HEADER_SIZE, data, size);
if (dest_mac == NULL)
{
if (ip->DstIP == 0xffffffff ||
(IsInNetwork(ip->DstIP, v->HostIP, v->HostMask) && (ip->DstIP & (~v->HostMask)) == (~v->HostMask)))
{
// Broadcast address
dest_mac = broadcast;
}
else
{
// Send an ARP query if the destination MAC address is unknown
arp = SearchArpTable(v, dest_ip);
if (arp != NULL)
{
dest_mac = arp->MacAddress;
}
}
}
if (dest_mac != NULL)
{
// Send the packet immediately
VirtualIpSend(v, dest_mac, buf, size + IP_HEADER_SIZE);
// Packet data may be released
Free(buf);
}
else
{
// Because this packet still can not be transferred, add it to the IP waiting table
InsertIpWaitTable(v, dest_ip, src_ip, buf, size + IP_HEADER_SIZE);
// Issue an ARP
SendArp(v, dest_ip);
}
}
// Send an IP packet (fragmented)
void VirtualIpSend(VH *v, UCHAR *dest_mac, void *data, UINT size)
{
// Validate arguments
if (v == NULL || dest_mac == NULL || data == NULL || size == 0)
{
return;
}
// Transmission
VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_IPV4, data, size);
}
// Send an ARP request packet
void VirtualArpSendRequest(VH *v, UINT dest_ip)
{
ARPV4_HEADER arp;
// Validate arguments
if (v == NULL)
{
return;
}
// Build the ARP header
arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
arp.HardwareSize = 6;
arp.ProtocolSize = 4;
arp.Operation = Endian16(ARP_OPERATION_REQUEST);
Copy(arp.SrcAddress, v->MacAddress, 6);
arp.SrcIP = v->HostIP;
Zero(&arp.TargetAddress, 6);
arp.TargetIP = dest_ip;
// Transmission
VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
}
// Send an ARP response packet
void VirtualArpSendResponse(VH *v, UCHAR *dest_mac, UINT dest_ip, UINT src_ip)
{
ARPV4_HEADER arp;
// Validate arguments
if (v == NULL || dest_mac == NULL)
{
return;
}
// Build the ARP header
arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
arp.HardwareSize = 6;
arp.ProtocolSize = 4;
arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
Copy(arp.SrcAddress, v->MacAddress, 6);
Copy(arp.TargetAddress, dest_mac, 6);
arp.SrcIP = src_ip;
arp.TargetIP = dest_ip;
// Transmission
VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(ARPV4_HEADER));
}
// An ARP request packet was received
void VirtualArpResponseRequest(VH *v, PKT *packet)
{
ARPV4_HEADER *arp;
// Validate arguments
if (v == NULL || packet == NULL)
{
return;
}
arp = packet->L3.ARPv4Header;
// Memory the information of the host IP address and the MAC address of the other party
ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress);
// Search whether it matches with the IP address of this host
if (v->HostIP == arp->TargetIP)
{
// Respond since the match
VirtualArpSendResponse(v, arp->SrcAddress, arp->SrcIP, v->HostIP);
return;
}
// Do nothing if it doesn't match
}
// An ARP response packet is received
void VirtualArpResponseReceived(VH *v, PKT *packet)
{
ARPV4_HEADER *arp;
// Validate arguments
if (v == NULL || packet == NULL)
{
return;
}
arp = packet->L3.ARPv4Header;
// Regard this information as known information
ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress);
}
// Received an ARP packet
void VirtualArpReceived(VH *v, PKT *packet)
{
ARPV4_HEADER *arp;
// Validate arguments
if (v == NULL || packet == NULL)
{
return;
}
arp = packet->L3.ARPv4Header;
if (Endian16(arp->HardwareType) != ARP_HARDWARE_TYPE_ETHERNET)
{
// Ignore if hardware type is other than Ethernet
return;
}
if (Endian16(arp->ProtocolType) != MAC_PROTO_IPV4)
{
// Ignore if the protocol type is a non-IPv4
return;
}
if (arp->HardwareSize != 6 || arp->ProtocolSize != 4)
{
// Ignore because the size of protocol address or hardware address is invalid
return;
}
// Check the source MAC address
if (Cmp(arp->SrcAddress, packet->MacAddressSrc, 6) != 0)
{
// MAC address in the MAC header and the MAC address of the ARP packet are different
return;
}
switch (Endian16(arp->Operation))
{
case ARP_OPERATION_REQUEST: // ARP request
VirtualArpResponseRequest(v, packet);
break;
case ARP_OPERATION_RESPONSE: // ARP response
VirtualArpResponseReceived(v, packet);
break;
}
}
// Release the DHCP server
void FreeDhcpServer(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
// Remove the all lease entries
for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
{
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
FreeDhcpLease(d);
}
ReleaseList(v->DhcpLeaseList);
v->DhcpLeaseList = NULL;
}
// Initialize the DHCP server
void InitDhcpServer(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
// Create a list
v->DhcpLeaseList = NewList(CompareDhcpLeaseList);
}
// Search for a DHCP lease item by the IP address
DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return NULL;
}
for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
{
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
if (d->IpAddress == ip)
{
return d;
}
}
return NULL;
}
// Search for a DHCP lease item by the MAC address
DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac)
{
DHCP_LEASE *d, t;
// Validate arguments
if (v == NULL || mac == NULL)
{
return NULL;
}
Copy(&t.MacAddress, mac, 6);
d = Search(v->DhcpLeaseList, &t);
return d;
}
// Release the DHCP lease item
void FreeDhcpLease(DHCP_LEASE *d)
{
// Validate arguments
if (d == NULL)
{
return;
}
Free(d->Hostname);
Free(d);
}
// Create a DHCP lease item
DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname)
{
DHCP_LEASE *d;
// Validate arguments
if (mac_address == NULL || hostname == NULL)
{
return NULL;
}
d = ZeroMalloc(sizeof(DHCP_LEASE));
d->LeasedTime = (UINT64)Tick64();
if (expire == INFINITE)
{
d->ExpireTime = INFINITE;
}
else
{
d->ExpireTime = d->LeasedTime + (UINT64)expire;
}
d->IpAddress = ip;
d->Mask = mask;
d->Hostname = CopyStr(hostname);
Copy(d->MacAddress, mac_address, 6);
return d;
}
// Comparison of the items in the DHCP list
int CompareDhcpLeaseList(void *p1, void *p2)
{
DHCP_LEASE *d1, *d2;
// Validate arguments
if (p1 == NULL || p2 == NULL)
{
return 0;
}
d1 = *(DHCP_LEASE **)p1;
d2 = *(DHCP_LEASE **)p2;
if (d1 == NULL || d2 == NULL)
{
return 0;
}
return Cmp(d1->MacAddress, d2->MacAddress, 6);
}
// Poll the DHCP server
void PollingDhcpServer(VH *v)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return;
}
if (v->LastDhcpPolling != 0)
{
if ((v->LastDhcpPolling + (UINT64)DHCP_POLLING_INTERVAL) > v->Now &&
v->LastDhcpPolling < v->Now)
{
return;
}
}
v->LastDhcpPolling = v->Now;
// Remove expired entries
FIRST_LIST:
for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
{
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
if (d->ExpireTime < v->Now)
{
FreeDhcpLease(d);
Delete(v->DhcpLeaseList, d);
goto FIRST_LIST;
}
}
}
// Correspond to the DHCP REQUEST
UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip)
{
UINT ret;
// Validate arguments
if (v == NULL || mac == NULL)
{
return 0;
}
ret = ServeDhcpDiscover(v, mac, request_ip);
if (ret != request_ip)
{
if (request_ip != 0)
{
// Raise an error if the requested IP address cannot to be assigned
return 0;
}
}
return ret;
}
// Correspond to the DHCP DISCOVER
UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip)
{
UINT ret = 0;
// Validate arguments
if (v == NULL || mac == NULL)
{
return 0;
}
if (request_ip != 0)
{
// IP address is specified
DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip);
if (d != NULL)
{
// If an entry for the same IP address already exists,
// check whether it is a request from the same MAC address
if (Cmp(mac, d->MacAddress, 6) == 0)
{
// Examine whether the specified IP address is within the range of assignment
if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) &&
Endian32(request_ip) <= Endian32(v->DhcpIpEnd))
{
// Accept if within the range
ret = request_ip;
}
}
}
else
{
// Examine whether the specified IP address is within the range of assignment
if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) &&
Endian32(request_ip) <= Endian32(v->DhcpIpEnd))
{
// Accept if within the range
ret = request_ip;
}
else
{
// Propose an IP in the range since it's a Discover although It is out of range
}
}
}
if (ret == 0)
{
// If there is any entry with the same MAC address
// that are already registered, use it with priority
DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac);
if (d != NULL)
{
// Examine whether the found IP address is in the allocation region
if (Endian32(v->DhcpIpStart) <= Endian32(d->IpAddress) &&
Endian32(d->IpAddress) <= Endian32(v->DhcpIpEnd))
{
// Use the IP address if it's found within the range
ret = d->IpAddress;
}
}
}
if (ret == 0)
{
// Take an appropriate IP addresses that can be assigned newly
HUB_OPTION *opt = NatGetHubOption(v);
if (opt != NULL && opt->SecureNAT_RandomizeAssignIp)
{
ret = GetFreeDhcpIpAddressByRandom(v, mac);
}
else
{
ret = GetFreeDhcpIpAddress(v);
}
}
return ret;
}
// Take an appropriate IP addresses that can be assigned newly
UINT GetFreeDhcpIpAddress(VH *v)
{
UINT ip_start, ip_end;
UINT i;
// Validate arguments
if (v == NULL)
{
return 0;
}
ip_start = Endian32(v->DhcpIpStart);
ip_end = Endian32(v->DhcpIpEnd);
for (i = ip_start; i <= ip_end;i++)
{
UINT ip = Endian32(i);
if (SearchDhcpLeaseByIp(v, ip) == NULL)
{
// A free IP address is found
return ip;
}
}
// There is no free address
return 0;
}
// Take an appropriate IP addresses that can be assigned newly (random)
UINT GetFreeDhcpIpAddressByRandom(VH *v, UCHAR *mac)
{
UINT ip_start, ip_end;
UINT i;
UINT num_retry;
// Validate arguments
if (v == NULL || mac == NULL)
{
return 0;
}
ip_start = Endian32(v->DhcpIpStart);
ip_end = Endian32(v->DhcpIpEnd);
if (ip_start > ip_end)
{
return 0;
}
num_retry = (ip_end - ip_start + 1) * 2;
num_retry = MIN(num_retry, 65536 * 2);
for (i = 0;i < num_retry;i++)
{
UCHAR rand_seed[sizeof(UINT) + 6];
UCHAR hash[16];
UINT rand_int;
UINT new_ip;
WRITE_UINT(&rand_seed[0], i);
Copy(rand_seed + sizeof(UINT), mac, 6);
Hash(hash, rand_seed, sizeof(rand_seed), false);
rand_int = READ_UINT(hash);
new_ip = Endian32(ip_start + (rand_int % (ip_end - ip_start + 1)));
if (SearchDhcpLeaseByIp(v, new_ip) == NULL)
{
// A free IP address is found
return new_ip;
}
}
// There is no free address
return 0;
}
// Virtual DHCP Server
void VirtualDhcpServer(VH *v, PKT *p)
{
DHCPV4_HEADER *dhcp;
UCHAR *data;
UINT size;
UINT dhcp_header_size;
UINT dhcp_data_offset;
UINT tran_id;
UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE);
bool ok;
DHCP_OPTION_LIST *opt;
// Validate arguments
if (v == NULL || p == NULL)
{
return;
}
if (v->NativeNat != NULL)
{
if (Cmp(p->MacAddressSrc, v->NativeNat->CurrentMacAddress, 6) == 0)
{
// DHCP server is kept from responding for the native NAT interface
// ** Not be needed to return yet **
//return;
}
}
dhcp = p->L7.DHCPv4Header;
tran_id = Endian32(dhcp->TransactionId);
// Get the DHCP data and size
dhcp_header_size = sizeof(DHCPV4_HEADER);
dhcp_data_offset = (UINT)(((UCHAR *)p->L7.DHCPv4Header) - ((UCHAR *)p->MacHeader) + dhcp_header_size);
data = ((UCHAR *)dhcp) + dhcp_header_size;
size = p->PacketSize - dhcp_data_offset;
if (dhcp_header_size < 5)
{
// Data size is invalid
return;
}
// Search for Magic Cookie
ok = false;
while (size >= 5)
{
if (Cmp(data, &magic_cookie, sizeof(magic_cookie)) == 0)
{
// Found
data += 4;
size -= 4;
ok = true;
break;
}
data++;
size--;
}
if (ok == false)
{
// The packet is invalid
return;
}
// Parse DHCP options list
opt = ParseDhcpOptionList(data, size);
if (opt == NULL)
{
// The packet is invalid
return;
}
if (StartWith(opt->Hostname, NN_HOSTNAME_STARTWITH) || StartWith(opt->Hostname, NN_HOSTNAME_STARTWITH2))
{
Free(opt);
return;
}
if (dhcp->OpCode == 1 && (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST || opt->Opcode == DHCP_INFORM))
{
// Operate as the server
UINT ip = 0;
if (opt->RequestedIp == 0)
{
opt->RequestedIp = p->L3.IPv4Header->SrcIP;
}
if (opt->Opcode == DHCP_DISCOVER)
{
// Return an IP address that can be used
ip = ServeDhcpDiscover(v, p->MacAddressSrc, opt->RequestedIp);
}
else if (opt->Opcode == DHCP_REQUEST)
{
// Determine the IP address
ip = ServeDhcpRequest(v, p->MacAddressSrc, opt->RequestedIp);
}
if (ip != 0 || opt->Opcode == DHCP_INFORM)
{
// Respond if there is providable IP address
if (opt->Opcode == DHCP_REQUEST)
{
DHCP_LEASE *d;
char mac[MAX_SIZE];
char str[MAX_SIZE];
// Remove old records with the same IP address
d = SearchDhcpLeaseByIp(v, ip);
if (d != NULL)
{
FreeDhcpLease(d);
Delete(v->DhcpLeaseList, d);
}
// Create a new entry
d = NewDhcpLease(v->DhcpExpire, p->MacAddressSrc,
ip, v->DhcpMask,
opt->Hostname);
d->Id = ++v->DhcpId;
Add(v->DhcpLeaseList, d);
MacToStr(mac, sizeof(mac), d->MacAddress);
IPToStr32(str, sizeof(str), d->IpAddress);
NLog(v, "LH_NAT_DHCP_CREATED", d->Id, mac, str, d->Hostname, v->DhcpExpire / 1000);
}
// Respond
if (true)
{
DHCP_OPTION_LIST ret;
LIST *o;
Zero(&ret, sizeof(ret));
ret.Opcode = (opt->Opcode == DHCP_DISCOVER ? DHCP_OFFER : DHCP_ACK);
ret.ServerAddress = v->HostIP;
if (v->DhcpExpire == INFINITE)
{
ret.LeaseTime = INFINITE;
}
else
{
ret.LeaseTime = Endian32(v->DhcpExpire / 1000);
}
if (opt->Opcode == DHCP_INFORM)
{
ret.LeaseTime = 0;
}
StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain);
ret.SubnetMask = v->DhcpMask;
ret.DnsServer = v->DhcpDns;
ret.DnsServer2 = v->DhcpDns2;
ret.Gateway = v->DhcpGateway;
if (GetGlobalServerFlag(GSF_DISABLE_PUSH_ROUTE) == 0)
{
Copy(&ret.ClasslessRoute, &v->PushRoute, sizeof(DHCP_CLASSLESS_ROUTE_TABLE));
if (IsIpcMacAddress(p->MacAddressSrc))
{
if (ret.Gateway == 0)
{
// If the default gateway is not specified, add the static routing table
// entry for the local IP subnet
// (for PPP clients)
IP dhcp_ip;
IP dhcp_mask;
IP dhcp_network;
UINTToIP(&dhcp_ip, ip);
if (ip == 0)
{
UINTToIP(&dhcp_ip, p->L3.IPv4Header->SrcIP);
}
UINTToIP(&dhcp_mask, v->DhcpMask);
IPAnd4(&dhcp_network, &dhcp_ip, &dhcp_mask);
if (GetBestClasslessRoute(&ret.ClasslessRoute, &dhcp_ip) == NULL)
{
if (ret.ClasslessRoute.NumExistingRoutes < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES)
{
DHCP_CLASSLESS_ROUTE *cr = &ret.ClasslessRoute.Entries[ret.ClasslessRoute.NumExistingRoutes];
cr->Exists = true;
UINTToIP(&cr->Gateway, v->HostIP);
if (v->UseNat == false && ret.ClasslessRoute.NumExistingRoutes >= 1)
{
Copy(&cr->Gateway, &ret.ClasslessRoute.Entries[0].Gateway, sizeof(IP));
}
Copy(&cr->Network, &dhcp_network, sizeof(IP));
Copy(&cr->SubnetMask, &dhcp_mask, sizeof(IP));
cr->SubnetMaskLen = SubnetMaskToInt(&dhcp_mask);
ret.ClasslessRoute.NumExistingRoutes++;
}
}
}
}
}
if (opt->Opcode != DHCP_INFORM)
{
char client_mac[MAX_SIZE];
char client_ip[64];
IP ips;
BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6);
UINTToIP(&ips, ip);
IPToStr(client_ip, sizeof(client_ip), &ips);
Debug("DHCP %s : %s given %s\n",
ret.Opcode == DHCP_OFFER ? "DHCP_OFFER" : "DHCP_ACK",
client_mac, client_ip);
}
// Build a DHCP option
o = BuildDhcpOption(&ret);
if (o != NULL)
{
BUF *b = BuildDhcpOptionsBuf(o);
if (b != NULL)
{
UINT dest_ip = p->L3.IPv4Header->SrcIP;
if (dest_ip == 0)
{
dest_ip = 0xffffffff;
}
// Transmission
VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort),
ip, dhcp->ClientMacAddress, b, dhcp->HardwareType, dhcp->HardwareAddressSize);
// Release the memory
FreeBuf(b);
}
FreeDhcpOptions(o);
}
}
}
else
{
// There is no IP address that can be provided
DHCP_OPTION_LIST ret;
LIST *o;
Zero(&ret, sizeof(ret));
ret.Opcode = DHCP_NACK;
ret.ServerAddress = v->HostIP;
StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain);
ret.SubnetMask = v->DhcpMask;
// Build the DHCP option
o = BuildDhcpOption(&ret);
if (o != NULL)
{
BUF *b = BuildDhcpOptionsBuf(o);
if (b != NULL)
{
UINT dest_ip = p->L3.IPv4Header->SrcIP;
if (dest_ip == 0)
{
dest_ip = 0xffffffff;
}
// Transmission
VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort),
ip, dhcp->ClientMacAddress, b, dhcp->HardwareType, dhcp->HardwareAddressSize);
// Release the memory
FreeBuf(b);
}
FreeDhcpOptions(o);
}
}
}
// Release the memory
Free(opt);
}
// Submit the DHCP response packet
void VirtualDhcpSend(VH *v, UINT tran_id, UINT dest_ip, UINT dest_port,
UINT new_ip, UCHAR *client_mac, BUF *b, UINT hw_type, UINT hw_addr_size)
{
UINT blank_size = 128 + 64;
UINT dhcp_packet_size;
UINT magic = Endian32(DHCP_MAGIC_COOKIE);
DHCPV4_HEADER *dhcp;
void *magic_cookie_addr;
void *buffer_addr;
// Validate arguments
if (v == NULL || b == NULL)
{
return;
}
// Calculate the DHCP packet size
dhcp_packet_size = blank_size + sizeof(DHCPV4_HEADER) + sizeof(magic) + b->Size;
if (dhcp_packet_size < DHCP_MIN_SIZE)
{
// Padding
dhcp_packet_size = DHCP_MIN_SIZE;
}
// Create a header
dhcp = ZeroMalloc(dhcp_packet_size);
dhcp->OpCode = 2;
dhcp->HardwareType = hw_type;
dhcp->HardwareAddressSize = hw_addr_size;
dhcp->Hops = 0;
dhcp->TransactionId = Endian32(tran_id);
dhcp->Seconds = 0;
dhcp->Flags = 0;
dhcp->YourIP = new_ip;
dhcp->ServerIP = v->HostIP;
Copy(dhcp->ClientMacAddress, client_mac, 6);
// Calculate the address
magic_cookie_addr = (((UCHAR *)dhcp) + sizeof(DHCPV4_HEADER) + blank_size);
buffer_addr = ((UCHAR *)magic_cookie_addr) + sizeof(magic);
// Magic Cookie
Copy(magic_cookie_addr, &magic, sizeof(magic));
// Buffer
Copy(buffer_addr, b->Buf, b->Size);
// Transmission
SendUdp(v, dest_ip, dest_port, v->HostIP, NAT_DHCP_SERVER_PORT, dhcp, dhcp_packet_size);
Free(dhcp);
}
// Virtual host: Process the Layer2
void VirtualLayer2(VH *v, PKT *packet)
{
bool ok;
// Validate arguments
if (packet == NULL || v == NULL)
{
return;
}
// Packet filter
if (VirtualLayer2Filter(v, packet) == false)
{
// Packet was ignored
return;
}
ok = false;
if (packet->TypeL3 == L3_IPV4 && packet->TypeL4 == L4_UDP && packet->TypeL7 == L7_DHCPV4)
{
if (v->UseDhcp)
{
// A special treatment on the DHCP packet
if (packet->BroadcastPacket || Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0)
{
// Virtual DHCP server processing
VirtualDhcpServer(v, packet);
ok = true;
}
}
}
if (ok == false)
{
// The process for each supported protocol
switch (packet->TypeL3)
{
case L3_ARPV4: // ARPv4
VirtualArpReceived(v, packet);
break;
case L3_IPV4: // IPv4
VirtualIpReceived(v, packet);
break;
}
}
}
// Packet filter (Blocking packets to other than me)
bool VirtualLayer2Filter(VH *v, PKT *packet)
{
// Validate arguments
if (v == NULL || packet == NULL)
{
return false;
}
// Pass through if broadcast packet
if (packet->BroadcastPacket)
{
return true;
}
// Ignore if the sender of the packet is myself
if (Cmp(packet->MacAddressSrc, v->MacAddress, 6) == 0)
{
return false;
}
// Pass through in the case of a packet addressed to me
if (Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0)
{
return true;
}
// Discard if the other packets
return false;
}
// The virtual host is made to receive a packet
bool VirtualPutPacket(VH *v, void *data, UINT size)
{
if (data == NULL)
{
// Flush
v->flag1 = false;
if (v->NativeNat != NULL)
{
if (v->NativeNat->SendStateChanged)
{
TUBE *halt_tube = NULL;
Lock(v->NativeNat->Lock);
{
if (v->NativeNat->HaltTube != NULL)
{
halt_tube = v->NativeNat->HaltTube;
AddRef(halt_tube->Ref);
}
}
Unlock(v->NativeNat->Lock);
if (halt_tube != NULL)
{
TubeFlushEx(halt_tube, true);
v->NativeNat->SendStateChanged = false;
ReleaseTube(halt_tube);
}
}
}
}
else
{
// Interpret the received packet
PKT *packet = ParsePacket(data, size);
if (v->flag1 == false)
{
v->flag1 = true;
v->Now = Tick64();
}
// Lock the entire virtual machine in here
LockVirtual(v);
{
if (packet != NULL)
{
// Process the Layer-2
VirtualLayer2(v, packet);
// Release the packet structure
FreePacket(packet);
}
}
UnlockVirtual(v);
Free(data);
}
return true;
}
bool VirtualPaPutPacket(SESSION *s, void *data, UINT size)
{
VH *v;
// Validate arguments
if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
{
return false;
}
return VirtualPutPacket(v, data, size);
}
// Get the options for the virtual host
void GetVirtualHostOption(VH *v, VH_OPTION *o)
{
// Validate arguments
if (v == NULL)
{
return;
}
LockVirtual(v);
{
Zero(o, sizeof(VH_OPTION));
// MAC address
Copy(o->MacAddress, v->MacAddress, 6);
// Host information
UINTToIP(&o->Ip, v->HostIP);
UINTToIP(&o->Mask, v->HostMask);
o->Mtu = v->Mtu;
// NAT timeout information
o->NatTcpTimeout = v->NatTcpTimeout / 1000;
o->NatUdpTimeout = v->NatUdpTimeout / 1000;
// NAT using flag
o->UseNat = v->UseNat;
// DHCP using flag
o->UseDhcp = v->UseDhcp;
// IP address range for DHCP distribution
UINTToIP(&o->DhcpLeaseIPStart, v->DhcpIpStart);
UINTToIP(&o->DhcpLeaseIPEnd, v->DhcpIpEnd);
// Subnet mask
UINTToIP(&o->DhcpSubnetMask, v->DhcpMask);
// Expiration date
if (v->DhcpExpire != INFINITE)
{
o->DhcpExpireTimeSpan = v->DhcpExpire / 1000;
}
else
{
o->DhcpExpireTimeSpan = INFINITE;
}
// Gateway address
UINTToIP(&o->DhcpGatewayAddress, v->DhcpGateway);
// DNS server address
UINTToIP(&o->DhcpDnsServerAddress, v->DhcpDns);
UINTToIP(&o->DhcpDnsServerAddress2, v->DhcpDns2);
// Domain name
StrCpy(o->DhcpDomainName, sizeof(o->DhcpDomainName), v->DhcpDomain);
// Save a log
o->SaveLog = v->SaveLog;
// Pushing route option
BuildClasslessRouteTableStr(o->DhcpPushRoutes, sizeof(o->DhcpPushRoutes), &v->PushRoute);
o->ApplyDhcpPushRoutes = true;
}
UnlockVirtual(v);
}
// Set the option to the virtual host
void SetVirtualHostOption(VH *v, VH_OPTION *vo)
{
UINT i;
// Validate arguments
if (v == NULL || vo == NULL)
{
return;
}
LockVirtual(v);
{
// Set the MAC address
for (i = 0;i < 6;i++)
{
if (vo->MacAddress[i] != 0)
{
Copy(v->MacAddress, vo->MacAddress, 6);
break;
}
}
// Set the host information list
v->HostIP = IPToUINT(&vo->Ip);
v->HostMask = IPToUINT(&vo->Mask);
// Set the MTU, MMS
v->Mtu = MIN(vo->Mtu, MAX_L3_DATA_SIZE);
if (v->Mtu == 0)
{
v->Mtu = MAX_L3_DATA_SIZE;
}
v->Mtu = MAX(v->Mtu, TCP_HEADER_SIZE + IP_HEADER_SIZE + MAC_HEADER_SIZE + 8);
v->IpMss = ((v->Mtu - IP_HEADER_SIZE) / 8) * 8;
v->TcpMss = ((v->IpMss - TCP_HEADER_SIZE) / 8) * 8;
v->UdpMss = ((v->IpMss - UDP_HEADER_SIZE) / 8) * 8;
if (vo->NatTcpTimeout != 0)
{
v->NatTcpTimeout = MIN(vo->NatTcpTimeout, 4000000) * 1000;
}
if (vo->NatUdpTimeout != 0)
{
v->NatUdpTimeout = MIN(vo->NatUdpTimeout, 4000000) * 1000;
}
v->NatTcpTimeout = MAKESURE(v->NatTcpTimeout, NAT_TCP_MIN_TIMEOUT, NAT_TCP_MAX_TIMEOUT);
v->NatUdpTimeout = MAKESURE(v->NatUdpTimeout, NAT_UDP_MIN_TIMEOUT, NAT_UDP_MAX_TIMEOUT);
Debug("Timeout: %d , %d\n", v->NatTcpTimeout, v->NatUdpTimeout);
// NAT using flag
v->UseNat = vo->UseNat;
// DHCP using flag
v->UseDhcp = vo->UseDhcp;
// Expiration date
if (vo->DhcpExpireTimeSpan == 0 || vo->DhcpExpireTimeSpan == INFINITE)
{
v->DhcpExpire = INFINITE;
}
else
{
v->DhcpExpire = MAKESURE(DHCP_MIN_EXPIRE_TIMESPAN,
MIN(vo->DhcpExpireTimeSpan * 1000, 2000000000),
INFINITE);
}
// Address range to be distributed
v->DhcpIpStart = IPToUINT(&vo->DhcpLeaseIPStart);
v->DhcpIpEnd = IPToUINT(&vo->DhcpLeaseIPEnd);
if (Endian32(v->DhcpIpEnd) < Endian32(v->DhcpIpStart))
{
v->DhcpIpEnd = v->DhcpIpStart;
}
// Subnet mask
v->DhcpMask = IPToUINT(&vo->DhcpSubnetMask);
// Gateway address
v->DhcpGateway = IPToUINT(&vo->DhcpGatewayAddress);
// DNS server address
v->DhcpDns = IPToUINT(&vo->DhcpDnsServerAddress);
v->DhcpDns2 = IPToUINT(&vo->DhcpDnsServerAddress2);
// Domain name
StrCpy(v->DhcpDomain, sizeof(v->DhcpDomain), vo->DhcpDomainName);
// Save a log
v->SaveLog = vo->SaveLog;
// DHCP routing table pushing setting
if (vo->ApplyDhcpPushRoutes)
{
DHCP_CLASSLESS_ROUTE_TABLE rt;
Zero(&rt, sizeof(rt));
if (ParseClasslessRouteTableStr(&rt, vo->DhcpPushRoutes))
{
Copy(&v->PushRoute, &rt, sizeof(DHCP_CLASSLESS_ROUTE_TABLE));
}
}
}
UnlockVirtual(v);
}
// Release the virtual host
void Virtual_Free(VH *v)
{
// Release the DHCP server
FreeDhcpServer(v);
// NAT release
FreeNat(v);
LockVirtual(v);
{
// Release the IP combining list
FreeIpCombineList(v);
// Release the IP waiting table
FreeIpWaitTable(v);
// Release the ARP waiting table
FreeArpWaitTable(v);
// Release the ARP table
FreeArpTable(v);
// Release the transmission queue
LockQueue(v->SendQueue);
{
BLOCK *block;
// Release all queues
while (block = GetNext(v->SendQueue))
{
FreeBlock(block);
}
}
UnlockQueue(v->SendQueue);
ReleaseQueue(v->SendQueue);
v->SendQueue = NULL;
// Release the cancel object
ReleaseCancel(v->Cancel);
v->Active = false;
}
UnlockVirtual(v);
// Release the logger
FreeLog(v->Logger);
}
void VirtualPaFree(SESSION *s)
{
VH *v;
// Validate arguments
if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)
{
return;
}
Virtual_Free(v);
}
// Release the virtual host
void ReleaseVirtual(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
if (Release(v->ref) == 0)
{
CleanupVirtual(v);
}
}
// Lock the virtual host
void LockVirtual(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
Lock(v->lock);
}
// Unlock the virtual host
void UnlockVirtual(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
Unlock(v->lock);
}
// Cleanup the virtual host
void CleanupVirtual(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
if (v->Session != NULL)
{
ReleaseSession(v->Session);
}
DeleteCounter(v->Counter);
DeleteLock(v->lock);
Free(v);
}
// Stop the virtual host
void StopVirtualHost(VH *v)
{
SESSION *s;
// Validate arguments
if (v == NULL)
{
return;
}
// Get the session corresponding to the virtual host
LockVirtual(v);
{
s = v->Session;
if (s != NULL)
{
AddRef(s->ref);
}
}
UnlockVirtual(v);
if (s == NULL)
{
// This session is already stopped
return;
}
// Stop Session
StopSession(s);
ReleaseSession(s);
}
// Create a new virtual host
VH *NewVirtualHost(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option)
{
return NewVirtualHostEx(cedar, option, auth, vh_option, NULL);
}
VH *NewVirtualHostEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option, NAT *nat)
{
VH *v;
SOCK *s;
// Validate arguments
if (vh_option == NULL)
{
return NULL;
}
// Create a VH
v = ZeroMalloc(sizeof(VH));
v->ref = NewRef();
v->lock = NewLock();
v->Counter = NewCounter();
v->nat = nat;
// Examine whether ICMP Raw Socket can be created
s = NewUDP4(MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4), NULL);
if (s != NULL)
{
if (s->IsTtlSupported)
{
v->IcmpRawSocketOk = true;
}
ReleaseSock(s);
}
if (v->IcmpRawSocketOk == false)
{
if (IsIcmpApiSupported())
{
v->IcmpApiOk = true;
}
}
// Set the options
SetVirtualHostOption(v, vh_option);
return v;
}
// Generate a random MAC address
void GenMacAddress(UCHAR *mac)
{
UCHAR rand_data[32];
UINT64 now;
BUF *b;
UCHAR hash[SHA1_SIZE];
// Validate arguments
if (mac == NULL)
{
return;
}
// Get the current time
now = SystemTime64();
// Generate a random number
Rand(rand_data, sizeof(rand_data));
// Add to the buffer
b = NewBuf();
WriteBuf(b, &now, sizeof(now));
WriteBuf(b, rand_data, sizeof(rand_data));
// Hash
Hash(hash, b->Buf, b->Size, true);
// Generate a MAC address
mac[0] = 0x5E;
mac[1] = hash[0];
mac[2] = hash[1];
mac[3] = hash[2];
mac[4] = hash[3];
mac[5] = hash[4];
FreeBuf(b);
}
// Get a packet of virtual host adapter
PACKET_ADAPTER *VirtualGetPacketAdapter()
{
return NewPacketAdapter(VirtualPaInit, VirtualPaGetCancel,
VirtualPaGetNextPacket, VirtualPaPutPacket, VirtualPaFree);
}