1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-25 10:59:53 +03:00
SoftEtherVPN/src/Cedar/Virtual.c
stffabi 850a5faa0d Fix buffer overflow during NETBIOS name resolution
If SecureNAT is enabled and the hostname of the server
is longer than 16characters, every NETBIOS name resolution
query triggers the buffer overflow. If the server was built
with stack protection, the process will be killed.
2019-09-04 14:32:19 +02:00

10308 lines
223 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module
// 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);
if (c != NULL)
{
c->MaxL3Size = MAX(c->MaxL3Size, l3_size);
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);
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, 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->RecvFifo->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 - seq) >= 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, copy_len);
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;
}
// Empty the leases lists
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;
for (i = 0; i < LIST_NUM(v->DhcpPendingLeaseList); ++i)
{
DHCP_LEASE *d = LIST_DATA(v->DhcpPendingLeaseList, i);
FreeDhcpLease(d);
}
ReleaseList(v->DhcpPendingLeaseList);
v->DhcpPendingLeaseList = NULL;
}
// Initialize the DHCP server
void InitDhcpServer(VH *v)
{
// Validate arguments
if (v == NULL)
{
return;
}
// Create a list
v->DhcpLeaseList = NewList(CompareDhcpLeaseList);
v->DhcpPendingLeaseList = NewList(CompareDhcpLeaseList);
}
// Search for a pending DHCP lease item by the IP address
DHCP_LEASE *SearchDhcpPendingLeaseByIp(VH *v, UINT ip)
{
UINT i;
// Validate arguments
if (v == NULL)
{
return NULL;
}
for (i = 0; i < LIST_NUM(v->DhcpPendingLeaseList); ++i)
{
DHCP_LEASE *d = LIST_DATA(v->DhcpPendingLeaseList, i);
if (d->IpAddress == ip)
{
return d;
}
}
return NULL;
}
// 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 pending DHCP lease item by the MAC address
DHCP_LEASE *SearchDhcpPendingLeaseByMac(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->DhcpPendingLeaseList, &t);
return d;
}
// 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;
LIST_CLEANUP:
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 LIST_CLEANUP;
}
}
PENDING_LIST_CLEANUP:
// Remove expired entries
for (i = 0; i < LIST_NUM(v->DhcpPendingLeaseList); ++i)
{
DHCP_LEASE *d = LIST_DATA(v->DhcpPendingLeaseList, i);
if (d->ExpireTime < v->Now)
{
FreeDhcpLease(d);
Delete(v->DhcpPendingLeaseList, d);
goto PENDING_LIST_CLEANUP;
}
}
}
// 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)
{
d = SearchDhcpPendingLeaseByIp(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)
{
d = SearchDhcpPendingLeaseByMac(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 && SearchDhcpPendingLeaseByIp(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);
Md5(hash, rand_seed, sizeof(rand_seed));
rand_int = READ_UINT(hash);
new_ip = Endian32(ip_start + (rand_int % (ip_end - ip_start + 1)));
if (SearchDhcpLeaseByIp(v, new_ip) == NULL && SearchDhcpPendingLeaseByIp(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 client_mac[MAX_SIZE];
char client_ip[MAX_SIZE];
// Remove old records with the same IP address
d = SearchDhcpLeaseByIp(v, ip);
if (d != NULL)
{
FreeDhcpLease(d);
Delete(v->DhcpLeaseList, d);
}
d = SearchDhcpPendingLeaseByIp(v, ip);
if (d != NULL)
{
FreeDhcpLease(d);
Delete(v->DhcpPendingLeaseList, 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(client_mac, sizeof(client_mac), d->MacAddress);
IPToStr32(client_ip, sizeof(client_ip), d->IpAddress);
NLog(v, "LH_NAT_DHCP_CREATED", d->Id, client_mac, client_ip, 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);
if (ret.Opcode == DHCP_OFFER)
{
// DHCP_OFFER
DHCP_LEASE *d = NewDhcpLease(5000, p->MacAddressSrc, ip, v->DhcpMask, opt->Hostname);
d->Id = LIST_NUM(v->DhcpPendingLeaseList);
Add(v->DhcpPendingLeaseList, d);
Debug("VirtualDhcpServer(): %s has been marked as pending for %s\n", client_ip, client_mac);
}
else
{
// DHCP_ACK
Debug("VirtualDhcpServer(): %s has been assigned to %s\n", client_ip, client_mac);
}
}
// 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
Sha0(hash, b->Buf, b->Size);
// 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);
}