// SoftEther VPN Source Code // Cedar Communication Module // // SoftEther VPN Server, Client and Bridge are free software under GPLv2. // // Copyright (c) 2012-2014 Daiyuu Nobori. // Copyright (c) 2012-2014 SoftEther VPN Project, University of Tsukuba, Japan. // Copyright (c) 2012-2014 SoftEther Corporation. // // All Rights Reserved. // // http://www.softether.org/ // // Author: Daiyuu Nobori // Comments: Tetsuo Sugiyama, Ph.D. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License version 2 // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE // AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE. // // // THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, // UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, // MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS // SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS // SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER // CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL // DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING, // MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR // SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND // CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO // EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, // JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION // AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN // THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE. // // USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS // YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY // CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS // SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE // SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO // COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING // PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR // CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE // NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR // INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ // COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE // WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY // COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE // COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE // SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR // COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO // RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL // RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT // JUST A STATEMENT FOR WARNING AND DISCLAIMER. // // // SOURCE CODE CONTRIBUTION // ------------------------ // // Your contribution to SoftEther VPN Project is much appreciated. // Please send patches to us through GitHub. // Read the SoftEther VPN Patch Acceptance Policy in advance: // http://www.softether.org/5-download/src/9.patch // // // DEAR SECURITY EXPERTS // --------------------- // // If you find a bug or a security vulnerability please kindly inform us // about the problem immediately so that we can fix the security problem // to protect a lot of users around the world as soon as possible. // // Our e-mail address for security reports is: // softether-vpn-security [at] softether.org // // Please note that the above e-mail address is not a technical support // inquiry address. If you need technical assistance, please visit // http://www.softether.org/ and ask your question on the users forum. // // Thank you for your cooperation. // // // NO MEMORY OR RESOURCE LEAKS // --------------------------- // // The memory-leaks and resource-leaks verification under the stress // test has been passed before release this source code. // Virtual.c // User-mode virtual host program #include "CedarPch.h" static UCHAR broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static char v_vgs_hostname[256] = {0}; static char secure_nat_target_hostname[MAX_SIZE] = {0}; // Specify the destination host name to be used for connectivity testing in SecureNAT void NnSetSecureNatTargetHostname(char *name) { // Validate arguments if (name == NULL) { return; } StrCpy(secure_nat_target_hostname, sizeof(secure_nat_target_hostname), name); } // Delete the oldest NAT session if necessary void NnDeleteOldestNatSessionIfNecessary(NATIVE_NAT *t, UINT ip, UINT protocol) { UINT current_num; UINT max_sessions = 0; // Validate arguments if (t == NULL) { return; } if (t->v->HubOption != NULL) { HUB_OPTION *o = t->v->HubOption; switch (protocol) { case NAT_TCP: max_sessions = o->SecureNAT_MaxTcpSessionsPerIp; break; case NAT_UDP: max_sessions = o->SecureNAT_MaxUdpSessionsPerIp; break; case NAT_ICMP: max_sessions = o->SecureNAT_MaxIcmpSessionsPerIp; break; } } if (max_sessions == 0) { return; } current_num = NnGetNumNatEntriesPerIp(t, ip, protocol); if (current_num >= max_sessions) { NnDeleteOldestNatSession(t, ip, protocol); } } // Delete the oldest NAT session void NnDeleteOldestNatSession(NATIVE_NAT *t, UINT ip, UINT protocol) { NATIVE_NAT_ENTRY *e; // Validate arguments if (t == NULL) { return; } e = NnGetOldestNatEntryOfIp(t, ip, protocol); if (e != NULL) { NnDeleteSession(t, e); } } // Get the oldest NAT session NATIVE_NAT_ENTRY *NnGetOldestNatEntryOfIp(NATIVE_NAT *t, UINT ip, UINT protocol) { UINT i; NATIVE_NAT_ENTRY *oldest = NULL; UINT64 oldest_tick = 0xFFFFFFFFFFFFFFFFULL; // Validate arguments if (t == NULL) { return NULL; } for (i = 0;i < LIST_NUM(t->NatTableForRecv->AllList);i++) { NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForRecv->AllList, i); if (e->SrcIp == ip) { if (e->Protocol == protocol) { if (e->LastCommTime <= oldest_tick) { oldest_tick = e->LastCommTime; oldest = e; } } } } return oldest; } // Get the number of NAT sessions per IP address UINT NnGetNumNatEntriesPerIp(NATIVE_NAT *t, UINT src_ip, UINT protocol) { UINT ret = 0; UINT i; // Validate arguments if (t == NULL) { return 0; } for (i = 0;i < LIST_NUM(t->NatTableForRecv->AllList);i++) { NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForRecv->AllList, i); if (e->SrcIp == src_ip) { if (e->Protocol == protocol) { ret++; } } } return ret; } // Delete the old NAT sessions void NnDeleteOldSessions(NATIVE_NAT *t) { UINT i; LIST *o; UINT64 now; // Validate arguments if (t == NULL) { return; } o = NULL; now = t->v->Now; for (i = 0;i < LIST_NUM(t->NatTableForSend->AllList);i++) { NATIVE_NAT_ENTRY *e = LIST_DATA(t->NatTableForSend->AllList, i); UINT64 timeout; if (e->Status == NAT_TCP_CONNECTED || e->Status == NAT_TCP_ESTABLISHED) { timeout = e->LastCommTime + (UINT64)(e->Protocol == NAT_TCP ? t->v->NatTcpTimeout : t->v->NatUdpTimeout); } else { timeout = e->LastCommTime + (UINT64)NN_TIMEOUT_FOR_UNESTBALISHED_TCP; } if (timeout < now) { // Time-out occurs if (o == NULL) { o = NewListFast(NULL); } Add(o, e); } } if (o != NULL) { for (i = 0;i < LIST_NUM(o);i++) { NATIVE_NAT_ENTRY *e = LIST_DATA(o, i); NnDeleteSession(t, e); } ReleaseList(o); } } // Delete the NAT entry void NnDeleteSession(NATIVE_NAT *t, NATIVE_NAT_ENTRY *e) { // Validate arguments if (t == NULL || e == NULL) { return; } switch (e->Protocol) { case NAT_TCP: // Send a RST to the client side SendTcp(t->v, e->DestIp, e->DestPort, e->SrcIp, e->SrcPort, e->LastAck, e->LastSeq + (e->Status == NAT_TCP_CONNECTING ? 1 : 0), TCP_RST | TCP_ACK, 0, 0, NULL, 0); NLog(t->v, "LH_NAT_TCP_DELETED", e->Id); break; case NAT_UDP: NLog(t->v, "LH_NAT_UDP_DELETED", e->Id); break; case NAT_ICMP: Debug("NAT ICMP %u Deleted.", 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 Flagment 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 NnIpReceived(t, c->SrcIP, c->DestIP, c->Protocol, c->Data, c->Size, c->Ttl, c->HeadIpHeaderData, c->HeadIpHeaderDataSize, c->MaxL3Size); // Release the combining object NnFreeIpCombine(t, c); // Remove from the combining object list Delete(t->IpCombine, c); } } } // Release the IP combining object void NnFreeIpCombine(NATIVE_NAT *t, IP_COMBINE *c) { UINT i; // Validate arguments if (c == NULL) { return; } // Release the data t->CurrentIpQuota -= c->DataReserved; Free(c->Data); // Release the partial list for (i = 0;i < LIST_NUM(c->IpParts);i++) { IP_PART *p = LIST_DATA(c->IpParts, i); Free(p); } Free(c->HeadIpHeaderData); ReleaseList(c->IpParts); Free(c); } // Search the IP combining list IP_COMBINE *NnSearchIpCombine(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol) { IP_COMBINE *c, tt; // Validate arguments if (t == NULL) { return NULL; } tt.DestIP = dest_ip; tt.SrcIP = src_ip; tt.Id = id; tt.Protocol = protocol; c = Search(t->IpCombine, &tt); return c; } // Insert by creating a new object to the IP combining list IP_COMBINE *NnInsertIpCombine(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol, bool mac_broadcast, UCHAR ttl, bool src_is_localmac) { IP_COMBINE *c; // Validate arguments if (t == NULL) { return NULL; } // Examine the quota if ((t->CurrentIpQuota + IP_COMBINE_INITIAL_BUF_SIZE) > IP_COMBINE_WAIT_QUEUE_SIZE_QUOTA) { // IP packet can not be stored any more return NULL; } c = ZeroMalloc(sizeof(IP_COMBINE)); c->SrcIsLocalMacAddr = src_is_localmac; c->DestIP = dest_ip; c->SrcIP = src_ip; c->Id = id; c->Expire = t->v->Now + (UINT64)IP_COMBINE_TIMEOUT; c->Size = 0; c->IpParts = NewList(NULL); c->Protocol = protocol; c->MacBroadcast = mac_broadcast; c->Ttl = ttl; // Secure the memory c->DataReserved = IP_COMBINE_INITIAL_BUF_SIZE; c->Data = Malloc(c->DataReserved); t->CurrentIpQuota += c->DataReserved; Insert(t->IpCombine, c); return c; } // Initialize the IP combining list void NnInitIpCombineList(NATIVE_NAT *t) { // Validate arguments if (t == NULL) { return; } t->IpCombine = NewList(CompareIpCombine); } // Release the IP combining list void NnFreeIpCombineList(NATIVE_NAT *t) { UINT i; // Validate arguments if (t == NULL) { return; } for (i = 0;i < LIST_NUM(t->IpCombine);i++) { IP_COMBINE *c = LIST_DATA(t->IpCombine, i); NnFreeIpCombine(t, c); } ReleaseList(t->IpCombine); } // A TCP packet is received void NnTcpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, UINT max_l3_size) { TCP_HEADER *tcp; UCHAR *payload; UINT payload_size; UINT tcp_header_size; // Validate arguments if (t == NULL || data == NULL) { return; } // TCP header if (size < sizeof(TCP_HEADER)) { return; } tcp = (TCP_HEADER *)data; // Get the TCP header size tcp_header_size = TCP_GET_HEADER_SIZE(tcp) * 4; if (size < tcp_header_size || tcp_header_size < sizeof(TCP_HEADER)) { return; } // Payload payload = ((UCHAR *)data) + tcp_header_size; payload_size = size - tcp_header_size; // Search the port from the NAT table if (true) { NATIVE_NAT_ENTRY tt; NATIVE_NAT_ENTRY *e; NnSetNat(&tt, NAT_TCP, 0, 0, src_ip, Endian16(tcp->SrcPort), dest_ip, Endian16(tcp->DstPort)); e = SearchHash(t->NatTableForRecv, &tt); if (e != NULL) { // Last communication time e->LastCommTime = t->v->Now; e->TotalRecv += (UINT64)size; // Rewrite the TCP header tcp->Checksum = 0; tcp->DstPort = Endian16(e->SrcPort); if (tcp->Flag & TCP_FIN || tcp->Flag & TCP_RST) { // Disconnect e->Status = NAT_TCP_WAIT_DISCONNECT; } if (tcp->Flag & TCP_SYN && tcp->Flag & TCP_ACK) { // Connection complete if (e->Status != NAT_TCP_WAIT_DISCONNECT) { e->Status = NAT_TCP_ESTABLISHED; } } e->LastSeq = Endian32(tcp->AckNumber); e->LastAck = Endian32(tcp->SeqNumber); // Checksum recalculation tcp->Checksum = CalcChecksumForIPv4(src_ip, e->SrcIp, IP_PROTO_TCP, tcp, size, 0); // IP transmission SendIp(t->v, e->SrcIp, src_ip, IP_PROTO_TCP, tcp, size); } } } // An ICMP packet has been received void NnIcmpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, UINT max_l3_size) { ICMP_HEADER *icmp; // Validate arguments if (t == NULL || data == NULL) { return; } if (ttl == 0) { ttl = 1; } // ICMP header if (size < sizeof(ICMP_HEADER)) { return; } icmp = (ICMP_HEADER *)data; if (icmp->Type == ICMP_TYPE_ECHO_RESPONSE) { UCHAR *payload; UINT payload_size; ICMP_ECHO *echo; NATIVE_NAT_ENTRY tt, *e; // Echo Response echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER)); if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO))) { return; } payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO); payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); // Search the NAT NnSetNat(&tt, NAT_ICMP, 0, 0, 0, 0, dest_ip, Endian16(echo->Identifier)); e = SearchHash(t->NatTableForRecv, &tt); if (e != NULL) { // Rewrite the header icmp->Checksum = 0; echo->Identifier = Endian16(e->SrcPort); icmp->Checksum = IpChecksum(icmp, size); e->LastCommTime = t->v->Now; e->TotalRecv += (UINT64)size; // Transmission SendIpEx(t->v, e->SrcIp, src_ip, IP_PROTO_ICMPV4, icmp, size, MAX(ttl - 1, 1)); } } else if (icmp->Type == ICMP_TYPE_ECHO_REQUEST) { UCHAR *payload; UINT payload_size; ICMP_ECHO *echo; // Echo Response echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER)); if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO))) { return; } payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO); payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); if (dest_ip == t->PublicIP) { // Respond as soon as the Echo Request is received at the public side interface ICMP_HEADER *ret_icmp; ICMP_ECHO *ret_echo; UINT ret_size = sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + payload_size; ret_icmp = ZeroMalloc(ret_size); ret_echo = (ICMP_ECHO *)(((UCHAR *)ret_icmp) + sizeof(ICMP_HEADER)); ret_icmp->Type = ICMP_TYPE_ECHO_RESPONSE; ret_icmp->Code = icmp->Code; ret_echo->Identifier = echo->Identifier; ret_echo->SeqNo = echo->SeqNo; Copy((UCHAR *)ret_icmp + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO), payload, payload_size); ret_icmp->Checksum = IpChecksum(ret_icmp, ret_size); NnIpSendForInternet(t, IP_PROTO_ICMPV4, 0, dest_ip, src_ip, ret_icmp, ret_size, max_l3_size); Free(ret_icmp); } } else { if (icmp->Type == ICMP_TYPE_DESTINATION_UNREACHABLE || icmp->Type == ICMP_TYPE_TIME_EXCEEDED) { // Rewrite the Src IP of the IPv4 header of the ICMP response packet if (size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + sizeof(IPV4_HEADER))) { IPV4_HEADER *orig_ipv4 = (IPV4_HEADER *)(((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); UINT orig_ipv4_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); UINT orig_ipv4_header_size = GetIpHeaderSize((UCHAR *)orig_ipv4, orig_ipv4_size); if (orig_ipv4_header_size >= sizeof(IPV4_HEADER) && orig_ipv4_size >= orig_ipv4_header_size) { if (orig_ipv4->Protocol == IP_PROTO_ICMPV4) { // Search the inner ICMP header UINT inner_icmp_size = orig_ipv4_size - orig_ipv4_header_size; if (inner_icmp_size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO))) { ICMP_HEADER *inner_icmp = (ICMP_HEADER *)(((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + orig_ipv4_header_size); if (inner_icmp->Type == ICMP_TYPE_ECHO_REQUEST) { ICMP_ECHO *inner_echo = (ICMP_ECHO *)(((UCHAR *)inner_icmp) + sizeof(ICMP_HEADER)); NATIVE_NAT_ENTRY tt, *e; // Search for the existing NAT table entry NnSetNat(&tt, NAT_ICMP, 0, 0, 0, 0, orig_ipv4->SrcIP, Endian16(inner_echo->Identifier)); e = SearchHash(t->NatTableForRecv, &tt); if (e != NULL) { e->LastCommTime = t->v->Now; // Rewrite the inner IP packet and the ICMP header according to the NAT table inner_echo->Identifier = Endian16(e->SrcPort); inner_icmp->Checksum = 0; orig_ipv4->SrcIP = e->SrcIp; orig_ipv4->Checksum = 0; orig_ipv4->Checksum = IpChecksum(orig_ipv4, orig_ipv4_header_size); // Rewrite the outer ICMP header if (true) { UCHAR *payload; UINT payload_size; ICMP_ECHO *echo; // Echo Response echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER)); if (size < (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO))) { return; } payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO); payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); // Rewrite the header icmp->Checksum = 0; echo->Identifier = Endian16(e->SrcPort); icmp->Checksum = IpChecksum(icmp, size); // Transmission SendIpEx(t->v, e->SrcIp, src_ip, IP_PROTO_ICMPV4, icmp, size, MAX(ttl - 1, 1)); } } } } } } } } } } // An UDP packet has been received void NnUdpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, void *data, UINT size, UCHAR ttl, UINT max_l3_size) { UDP_HEADER *udp; UCHAR *payload; UINT payload_size; // Validate arguments if (t == NULL || data == NULL) { return; } // UDP header if (size <= sizeof(UDP_HEADER)) { return; } udp = (UDP_HEADER *)data; // Payload payload = ((UCHAR *)data) + sizeof(UDP_HEADER); payload_size = size - sizeof(UDP_HEADER); // Inspect the payload size if (payload_size < (Endian16(udp->PacketLength) - sizeof(UDP_HEADER))) { return; } // Truncate the payload payload_size = Endian16(udp->PacketLength) - sizeof(UDP_HEADER); // Search the port number from the NAT table if (true) { NATIVE_NAT_ENTRY tt; NATIVE_NAT_ENTRY *e; NnSetNat(&tt, NAT_UDP, 0, 0, 0, 0, dest_ip, Endian16(udp->DstPort)); e = SearchHash(t->NatTableForRecv, &tt); if (e != NULL) { // Last communication time e->LastCommTime = t->v->Now; e->TotalRecv += (UINT64)payload_size; // Deliver to the client by rewriting the port number SendUdp(t->v, e->SrcIp, e->SrcPort, src_ip, Endian16(udp->SrcPort), payload, payload_size); } } } // A combined IP packet is received void NnIpReceived(NATIVE_NAT *t, UINT src_ip, UINT dest_ip, UINT protocol, void *data, UINT size, UCHAR ttl, UCHAR *ip_header, UINT ip_header_size, UINT max_l3_size) { // Validate arguments if (t == NULL || data == NULL) { return; } if (dest_ip != t->PublicIP) { // Destination IP is not a unicast return; } switch (protocol) { case IP_PROTO_UDP: // UDP NnUdpReceived(t, src_ip, dest_ip, data, size, ttl, max_l3_size); break; case IP_PROTO_TCP: // TCP NnTcpReceived(t, src_ip, dest_ip, data, size, ttl, max_l3_size); break; case IP_PROTO_ICMPV4: // ICMP NnIcmpReceived(t, src_ip, dest_ip, data, size, ttl, max_l3_size); break; } } // Received an IP packet void NnFragmentedIpReceived(NATIVE_NAT *t, PKT *packet) { IPV4_HEADER *ip; void *data; UINT data_size_recved; UINT size; UINT ipv4_header_size; bool last_packet = false; UINT l3_size = 0; UCHAR *head_ip_header_data = NULL; UINT head_ip_header_size = 0; // Validate arguments if (t == NULL || packet == NULL) { return; } ip = packet->L3.IPv4Header; // Get the size of the IPv4 header ipv4_header_size = IPV4_GET_HEADER_LEN(packet->L3.IPv4Header) * 4; head_ip_header_size = ipv4_header_size; // Get the pointer to the data data = ((UCHAR *)packet->L3.PointerL3) + ipv4_header_size; // Get the data size size = l3_size = Endian16(ip->TotalLength); if (size <= ipv4_header_size) { // There is no data return; } size -= ipv4_header_size; // Get the size of data actually received data_size_recved = packet->PacketSize - (ipv4_header_size + MAC_HEADER_SIZE); if (data_size_recved < size) { // Data insufficient (It may be missing on the way) return; } if (IPV4_GET_OFFSET(ip) == 0 && (IPV4_GET_FLAGS(ip) & 0x01) == 0) { // Because this packet has not been fragmented, it can be passed to the upper layer immediately head_ip_header_data = (UCHAR *)packet->L3.IPv4Header; NnIpReceived(t, ip->SrcIP, ip->DstIP, ip->Protocol, data, size, ip->TimeToLive, head_ip_header_data, head_ip_header_size, l3_size); } else { // This packet is necessary to combine because it is fragmented UINT offset = IPV4_GET_OFFSET(ip) * 8; IP_COMBINE *c = NnSearchIpCombine(t, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol); if (offset == 0) { head_ip_header_data = (UCHAR *)packet->L3.IPv4Header; } last_packet = ((IPV4_GET_FLAGS(ip) & 0x01) == 0 ? true : false); if (c != NULL) { // It is the second or subsequent packet c->MaxL3Size = MAX(c->MaxL3Size, l3_size); NnCombineIp(t, c, offset, data, size, last_packet, head_ip_header_data, head_ip_header_size); } else { // Create a combining object because it is the first packet c = NnInsertIpCombine( t, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol, packet->BroadcastPacket, ip->TimeToLive, false); c->MaxL3Size = MAX(c->MaxL3Size, l3_size); if (c != NULL) { NnCombineIp(t, c, offset, data, size, last_packet, head_ip_header_data, head_ip_header_size); } } } } // Layer 2 packet processing void NnLayer2(NATIVE_NAT *t, PKT *packet) { // Validate arguments if (t == NULL || packet == NULL) { return; } if (packet->TypeL3 == L3_IPV4) { // IPv4 NnFragmentedIpReceived(t, packet); } } // Extract the received packets of native NAT, and deliver it to the VPN client void NnPoll(NATIVE_NAT *t) { // Validate arguments if (t == NULL) { return; } LockQueue(t->RecvQueue); { while (true) { PKT *pkt = GetNext(t->RecvQueue); if (pkt == NULL) { break; } NnLayer2(t, pkt); FreePacketWithData(pkt); } } UnlockQueue(t->RecvQueue); if (t->SendStateChanged) { TUBE *halt_tube = NULL; Lock(t->Lock); { if (t->HaltTube != NULL) { halt_tube = t->HaltTube; AddRef(halt_tube->Ref); } } Unlock(t->Lock); if (halt_tube != NULL) { TubeFlushEx(halt_tube, true); t->SendStateChanged = false; ReleaseTube(halt_tube); } } NnPollingIpCombine(t); NnDeleteOldSessions(t); } // Send a fragmented IP packet to the Internet void NnIpSendFragmentedForInternet(NATIVE_NAT *t, UCHAR ip_protocol, UINT src_ip, UINT dest_ip, USHORT id, USHORT total_size, USHORT offset, void *data, UINT size, UCHAR ttl) { UCHAR *buf; IPV4_HEADER *ip; BLOCK *b; // Validate arguments if (t == NULL || data == NULL) { return; } // Memory allocation buf = Malloc(size + IP_HEADER_SIZE); ip = (IPV4_HEADER *)&buf[0]; // IP header construction ip->VersionAndHeaderLength = 0; IPV4_SET_VERSION(ip, 4); IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4)); ip->TypeOfService = DEFAULT_IP_TOS; ip->TotalLength = Endian16((USHORT)(size + IP_HEADER_SIZE)); ip->Identification = Endian16(id); ip->FlagsAndFlagmentOffset[0] = ip->FlagsAndFlagmentOffset[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; // Validate arguments if (t == NULL) { return 0; } base_port = Rand32() % (65500 - 1025) + 1025; for (i = 0;i < (65500 - 1025);i++) { UINT port; NATIVE_NAT_ENTRY tt; NATIVE_NAT *e; port = base_port + i; if (port > 65500) { port = port - 65500 + 1025; } // 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) { // Validate arguments if (v == NULL) { return false; } if (v->NativeNat == NULL) { return false; } if (v->NativeNat->PublicIP == 0) { return false; } 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; 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; AddInterrupt(interrupt, next_dhcp_renew_tick); while (t->Halt == false && t->v->UseNat && ((t->v->HubOption == NULL) || (t->v->HubOption->DisableKernelModeSecureNAT == false))) { UINT64 now = Tick64(); bool call_cancel = false; bool state_changed = false; UINT wait_interval; 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(); 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; 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(); // Generate a TCP connection attempt packet tcp_seq = Rand32(); tcp_query = NnBuildIpPacket(NnBuildTcpPacket(NewBuf(), IPToUINT(&ipc->ClientIPAddress), tcp_src_port, IPToUINT(&yahoo_ip), 80, tcp_seq, 0, TCP_SYN, 8192, 1414), IPToUINT(&ipc->ClientIPAddress), IPToUINT(&yahoo_ip), IP_PROTO_TCP, 0); IPCSendIPv4(ipc, tcp_query->Buf, tcp_query->Size); FreeBuf(tcp_query); wait_for_dns = false; } } } } } if (pkt->TypeL3 == L3_IPV4 && pkt->TypeL4 == L4_TCP && pkt->L3.IPv4Header->SrcIP == IPToUINT(&yahoo_ip) && pkt->L3.IPv4Header->DstIP == IPToUINT(&ipc->ClientIPAddress) && pkt->L4.TCPHeader->SrcPort == Endian16(80) && pkt->L4.TCPHeader->DstPort == Endian16(tcp_src_port)) { TCP_HEADER *tcp_header = (TCP_HEADER *)pkt->L4.TCPHeader; if ((tcp_header->Flag & TCP_SYN) && (tcp_header->Flag & TCP_ACK)) { // There was a TCP response BUF *tcp_query; UINT recv_seq = Endian32(tcp_header->SeqNumber) + 1; no_store = true; // Send a RST tcp_query = NnBuildIpPacket(NnBuildTcpPacket(NewBuf(), IPToUINT(&ipc->ClientIPAddress), tcp_src_port, IPToUINT(&yahoo_ip), 80, tcp_seq + 1, recv_seq, TCP_RST | TCP_ACK, 8192, 0), IPToUINT(&ipc->ClientIPAddress), IPToUINT(&yahoo_ip), IP_PROTO_TCP, 0); IPCSendIPv4(ipc, tcp_query->Buf, tcp_query->Size); FreeBuf(tcp_query); tcp_last_recv_tick = now; } } if (t->RecvQueue->num_item > NN_MAX_QUEUE_LENGTH) { no_store = true; } if (no_store == false) { // Put in the queue InsertQueue(t->RecvQueue, pkt); call_cancel = true; state_changed = true; } else { // Release the packet FreePacketWithData(pkt); } } } } UnlockQueue(t->RecvQueue); if (state_changed) { goto LABEL_RESTART; } if (call_cancel) { CANCEL *c = NULL; Lock(t->CancelLock); { c = t->Cancel; AddRef(c->ref); } Unlock(t->CancelLock); if (c != NULL) { Cancel(c); ReleaseCancel(c); } } if (IsTubeConnected(ipc->Sock->RecvTube) == false || IsTubeConnected(ipc->Sock->SendTube) == false) { // Disconnected break; } if ((tcp_last_recv_tick + (UINT64)NN_POLL_CONNECTIVITY_TIMEOUT) < now) { // Connectivity test has timed out because a certain period of time has elapsed Debug("NN_POLL_CONNECTIVITY_TIMEOUT\n"); break; } wait_interval = GetNextIntervalForInterrupt(interrupt); wait_interval = MIN(wait_interval, 1234); if (wait_interval != 0) { WaitForTubes(tubes, num_tubes, wait_interval); } } FreeInterruptManager(interrupt); } // Build an IP packet BUF *NnBuildIpPacket(BUF *payload, UINT src_ip, UINT dst_ip, UCHAR protocol, UCHAR ttl) { BUF *ret = NewBuf(); IPV4_HEADER h; if (ttl == 0) { ttl = 127; } // IP header Zero(&h, sizeof(h)); IPV4_SET_VERSION(&h, 4); IPV4_SET_HEADER_LEN(&h, sizeof(IPV4_HEADER) / 4); h.TotalLength = Endian16((USHORT)sizeof(IPV4_HEADER) + payload->Size); h.Identification = Rand16(); h.TimeToLive = ttl; h.Protocol = protocol; h.SrcIP = src_ip; h.DstIP = dst_ip; h.Checksum = IpChecksum(&h, sizeof(h)); WriteBuf(ret, &h, sizeof(h)); WriteBufBuf(ret, payload); SeekBufToBegin(ret); FreeBuf(payload); return ret; } // Build an UDP packet BUF *NnBuildUdpPacket(BUF *payload, UINT src_ip, USHORT src_port, UINT dst_ip, USHORT dst_port) { BUF *ret = NewBuf(); BUF *phbuf = NewBuf(); UDPV4_PSEUDO_HEADER ph; UDP_HEADER h; // UDP pseudo header Zero(&ph, sizeof(ph)); ph.SrcIP = src_ip; ph.DstIP = dst_ip; ph.SrcPort = Endian16(src_port); ph.DstPort = Endian16(dst_port); ph.Protocol = IP_PROTO_UDP; ph.PacketLength1 = ph.PacketLength2 = Endian16(payload->Size + (USHORT)sizeof(UDP_HEADER)); WriteBuf(phbuf, &ph, sizeof(ph)); WriteBufBuf(phbuf, payload); // UDP header Zero(&h, sizeof(h)); h.SrcPort = Endian16(src_port); h.DstPort = Endian16(dst_port); h.PacketLength = Endian16(payload->Size + (USHORT)sizeof(UDP_HEADER)); h.Checksum = IpChecksum(phbuf->Buf, phbuf->Size); WriteBuf(ret, &h, sizeof(h)); WriteBuf(ret, payload->Buf, payload->Size); SeekBufToBegin(ret); FreeBuf(payload); FreeBuf(phbuf); return ret; } // Build a TCP packet BUF *NnBuildTcpPacket(BUF *payload, UINT src_ip, USHORT src_port, UINT dst_ip, USHORT dst_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss) { BUF *ret; IPV4_PSEUDO_HEADER *vh; TCP_HEADER *tcp; static UCHAR tcp_mss_option[] = {0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}; UINT header_size = TCP_HEADER_SIZE; UINT total_size; // Memory allocation vh = Malloc(sizeof(IPV4_PSEUDO_HEADER) + TCP_HEADER_SIZE + payload->Size + 32); tcp = (TCP_HEADER *)(((UCHAR *)vh) + sizeof(IPV4_PSEUDO_HEADER)); if (mss != 0) { USHORT *mss_size; mss_size = (USHORT *)(&tcp_mss_option[2]); *mss_size = Endian16((USHORT)mss); header_size += sizeof(tcp_mss_option); } total_size = header_size + payload->Size; // Pseudo header generation vh->SrcIP = src_ip; vh->DstIP = dst_ip; vh->Reserved = 0; vh->Protocol = IP_PROTO_TCP; vh->PacketLength = Endian16((USHORT)total_size); // TCP header generation tcp->SrcPort = Endian16((USHORT)src_port); tcp->DstPort = Endian16((USHORT)dst_port); tcp->SeqNumber = Endian32(seq); tcp->AckNumber = Endian32(ack); tcp->HeaderSizeAndReserved = 0; TCP_SET_HEADER_SIZE(tcp, (UCHAR)(header_size / 4)); tcp->Flag = (UCHAR)flag; tcp->WindowSize = Endian16((USHORT)window_size); tcp->Checksum = 0; tcp->UrgentPointer = 0; // Copy the option values if (mss != 0) { Copy(((UCHAR *)tcp) + TCP_HEADER_SIZE, tcp_mss_option, sizeof(tcp_mss_option)); } // Data copy Copy(((UCHAR *)tcp) + header_size, payload->Buf, payload->Size); // Checksum calculation tcp->Checksum = IpChecksum(vh, total_size + 12); ret = NewBufFromMemory(tcp, total_size); Free(vh); FreeBuf(payload); return ret; } // Build a DNS query packet BUF *NnBuildDnsQueryPacket(char *hostname, USHORT tran_id) { BUF *buf = NewBuf(); DNSV4_HEADER header; Zero(&header, sizeof(header)); header.TransactionId = Endian16(tran_id); header.Flag1 = 0x01; header.Flag2 = 0x00; header.NumQuery = Endian16(1); WriteBuf(buf, &header, sizeof(header)); BuildDnsQueryPacket(buf, hostname, false); SeekBufToBegin(buf); return buf; } // Read a DNS record BUF *NnReadDnsRecord(BUF *buf, bool answer, USHORT *ret_type, USHORT *ret_class) { USHORT type; USHORT clas; UINT ttl; BUF *ret = NULL; // Validate arguments if (buf == NULL) { return NULL; } // Read the DNS label if (NnReadDnsLabel(buf) == false) { return false; } // Type and Class if (ReadBuf(buf, &type, sizeof(USHORT)) != sizeof(USHORT)) { return false; } if (ret_type != NULL) { *ret_type = Endian16(type); } if (ReadBuf(buf, &clas, sizeof(USHORT)) != sizeof(USHORT)) { return false; } if (ret_class != NULL) { *ret_class = Endian16(clas); } if (answer) { USHORT data_len; UCHAR *data; // TTL if (ReadBuf(buf, &ttl, sizeof(UINT)) != sizeof(UINT)) { return false; } // data_len if (ReadBuf(buf, &data_len, sizeof(USHORT)) != sizeof(USHORT)) { return false; } data_len = Endian16(data_len); // data data = Malloc(data_len); if (ReadBuf(buf, data, data_len) != data_len) { 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; bool ok = false; USHORT dns_tran_id = Rand16(); UINT64 next_send_tick = 0; UINT64 giveup_time; IPC *ipc; UINT src_port = NnGenSrcPort(); INTERRUPT_MANAGER *interrupt; TUBE *tubes[3]; UINT num_tubes = 0; IP yahoo_ip; // Validate arguments if (a == NULL) { return false; } 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); 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); IPCSendIPv4(ipc, dns_query->Buf, dns_query->Size); } // 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->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)) { 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); 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); return ok; } // Generate source port number by a random number UINT NnGenSrcPort() { return 1025 + Rand32() % (65500 - 1025); } // 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 = GetEthList(); 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); 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); } // 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)) { 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); 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->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; UINT dest_port = n->DestPort; IP dest_ip; UINT num_ignore_errors = 0; // Validate arguments if (v == NULL || n == NULL) { return true; } 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; UINT dest_port = n->DestPort; IP dest_ip; UINT num_ignore_errors; // Validate arguments if (v == NULL || n == NULL) { return true; } if (n->DisconnectNow) { goto DISCONNECT; } if (n->UdpSocketCreated == false) { // Create a UDP socket n->Sock = NewUDP(0); if (n->Sock == NULL) { // Socket creation failure goto DISCONNECT; } else { n->PublicIp = IPToUINT(&n->Sock->LocalIP); n->PublicPort = n->Sock->LocalPort; JoinSockToSockEvent(n->Sock, v->SockEvent); n->UdpSocketCreated = true; } } buf = v->TmpBuf; if (n->ProxyDns == false) { UINTToIP(&dest_ip, n->DestIp); } else { UINTToIP(&dest_ip, n->DestIpProxy); } num_ignore_errors = 0; // Try to receive data from the UDP socket while (true) { IP src_ip; UINT src_port; recv_size = RecvFrom(n->Sock, &src_ip, &src_port, buf, 65536); if (recv_size == SOCK_LATER) { // Packet has not arrived break; } else if (recv_size == 0) { // Error? if (n->Sock->IgnoreRecvErr == false) { // A fatal error occurred goto DISCONNECT; } else { if ((num_ignore_errors++) > MAX_NUM_IGNORE_ERRORS) { goto DISCONNECT; } } } else { // Packet arrives. Check the source IP if (IPToUINT(&src_ip) == n->DestIp || n->DestIp == 0xFFFFFFFF || (IPToUINT(&src_ip) == n->DestIpProxy && n->ProxyDns) && src_port == n->DestPort) { // Insert to the queue void *data = Malloc(recv_size); Copy(data, buf, recv_size); block = NewBlock(data, recv_size, 0); if (block != NULL) { if (src_port == SPECIAL_UDP_PORT_WSD || src_port == SPECIAL_UDP_PORT_SSDP) { // Make believe there is a response from the host really in the case of WSD packet block->Param1 = IPToUINT(&src_ip); } } InsertQueue(n->UdpRecvQueue, block); v->NatDoCancelFlag = true; n->LastCommTime = v->Now; } } } // Try to send data to the UDP socket while (block = GetNext(n->UdpSendQueue)) { UINT send_size; bool is_nbtdgm = false; LIST *local_ip_list = NULL; if (dest_port == SPECIAL_UDP_PORT_NBTDGM) { // Determine whether NetBIOS Datagram packet NBTDG_HEADER *nh = (NBTDG_HEADER *)block->Buf; if (nh != NULL && block->Size >= sizeof(NBTDG_HEADER)) { if (nh->SrcIP == n->SrcIp && Endian16(nh->SrcPort) == n->SrcPort) { local_ip_list = GetHostIPAddressList(); if (local_ip_list != NULL) { is_nbtdgm = true; } } } } if (is_nbtdgm == false) { // Normal UDP packet send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size); } else { // IP address and port number is embedded in the NetBIOS Datagram Packet. // Transfer by rewriting it properly UINT i; for (i = 0;i < LIST_NUM(local_ip_list);i++) { IP *my_ip = LIST_DATA(local_ip_list, i); if (IsIP4(my_ip) && IsZeroIp(my_ip) == false && IsLocalHostIP(my_ip) == false) { NBTDG_HEADER *nh = (NBTDG_HEADER *)block->Buf; nh->SrcIP = IPToUINT(my_ip); nh->SrcPort = Endian16(n->PublicPort); send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size); } } } if (local_ip_list != NULL) { FreeHostIPAddressList(local_ip_list); } FreeBlock(block); if (send_size == 0) { // Determining whether a fatal error if (n->Sock->IgnoreSendErr == false) { // A fatal error occurred goto DISCONNECT; } } else { n->LastCommTime = v->Now; } } // Examine whether this session timed-out if ((n->LastCommTime + (UINT64)v->NatUdpTimeout) < v->Now || n->LastCommTime > v->Now) { // Time-out goto DISCONNECT; } return true; DISCONNECT: // Disconnect this session if (n->UdpSocketCreated) { // Close the socket Disconnect(n->Sock); ReleaseSock(n->Sock); n->Sock = NULL; } // Delete the entry DeleteNatUdp(v, n); return false; } // Thread to make a connection to the TCP host void NatTcpConnectThread(THREAD *t, void *p) { NAT_ENTRY *n = (NAT_ENTRY *)p; IP ip; char hostname[MAX_SIZE]; UINT port_number; SOCK *sock; SOCK_EVENT *e; // Validate arguments if (n == NULL || t == NULL) { return; } UINTToIP(&ip, n->DestIp); IPToStr(hostname, sizeof(hostname), &ip); port_number = n->DestPort; e = n->v->SockEvent; AddRef(e->ref); // Notify the initialization completion NoticeThreadInit(t); // Attempt to connect to the TCP host Debug("NatTcpConnect Connecting to %s:%u\n", hostname, port_number); sock = ConnectEx3(hostname, port_number, 0, &n->NatTcpCancelFlag, NULL, NULL, false, false, true); if (sock == NULL) { // Connection failure n->TcpMakeConnectionFailed = true; } else { // Successful connection n->TcpMakeConnectionSucceed = true; } n->Sock = sock; JoinSockToSockEvent(sock, e); SetSockEvent(e); ReleaseSockEvent(e); } // Create a thread for trying to connect to the TCP host void CreateNatTcpConnectThread(VH *v, NAT_ENTRY *n) { // Validate arguments if (v == NULL || n == NULL) { return; } // Create a thread n->NatTcpConnectThread = NewThread(NatTcpConnectThread, (void *)n); // Wait for a thread initialization completion WaitThreadInit(n->NatTcpConnectThread); } // Handle the TCP entry bool NatTransactTcp(VH *v, NAT_ENTRY *n) { char str[MAX_SIZE]; bool timeouted = false; // Validate arguments if (v == NULL || n == NULL) { return false; } if (n->DisconnectNow) { goto DISCONNECT; } // Process by state of the TCP switch (n->TcpStatus) { case NAT_TCP_CONNECTING: // Waiting for connection if (n->NatTcpConnectThread == NULL) { // Start a connection by creating a connection thread CreateNatTcpConnectThread(v, n); } else { // Wait for the result of the connection thread that has already started if (n->TcpMakeConnectionFailed || n->TcpMakeConnectionSucceed) { // Use the results because operation thread has already finished WaitThread(n->NatTcpConnectThread, INFINITE); ReleaseThread(n->NatTcpConnectThread); n->NatTcpConnectThread = NULL; if (n->TcpMakeConnectionSucceed) { // Connection is successful, and a Sock was created n->TcpStatus = NAT_TCP_CONNECTED; IPToStr32(str, sizeof(str), n->DestIp); NLog(v, "LH_NAT_TCP_SUCCEED", n->Id, n->Sock->RemoteHostname, str, n->DestPort); } else { // Failed to connect n->TcpStatus = NAT_TCP_SEND_RESET; IPToStr32(str, sizeof(str), n->DestIp); NLog(v, "LH_NAT_TCP_FAILED", n->Id, str, n->DestPort); } v->NatDoCancelFlag = true; } } break; case NAT_TCP_CONNECTED: // TCP socket connection completed. Negotiating with the client host break; case NAT_TCP_SEND_RESET: // TCP communication disconnection: Send a RST to the client host break; case NAT_TCP_ESTABLISHED: // TCP connection established { UINT old_send_fifo_size = 0; // Transmit to the socket if there is data in the receive buffer while (n->RecvFifo->size > 0) { UINT sent_size = Send(n->Sock, ((UCHAR *)n->RecvFifo->p) + n->RecvFifo->pos, n->RecvFifo->size, false); if (sent_size == 0) { // Communication has been disconnected n->TcpFinished = true; v->NatDoCancelFlag = true; break; } else if (sent_size == SOCK_LATER) { // Blocking break; } else { // Successful transmission ReadFifo(n->RecvFifo, NULL, sent_size); n->SendAckNext = true; if (false) { IP ip; n->test_TotalSent += sent_size; UINTToIP(&ip, n->DestIp); Debug("TCP %u: %r:%u %u\n", n->Id, &ip, n->DestPort, (UINT)n->test_TotalSent); } } } old_send_fifo_size = FifoSize(n->SendFifo); // Write to the transmission buffer by obtaining data from the socket while (true) { void *buf = (void *)v->TmpBuf; UINT want_to_recv_size = 0; UINT recv_size; // Calculate the size of wanting to receive if (n->SendFifo->size < NAT_SEND_BUF_SIZE) { // Still can receive want_to_recv_size = MIN(NAT_SEND_BUF_SIZE - n->SendFifo->size, NAT_TMPBUF_SIZE); } if (want_to_recv_size == 0) { SetNoNeedToRead(n->Sock); break; } recv_size = Recv(n->Sock, buf, want_to_recv_size, false); if (recv_size == 0) { // Communication has been disconnected n->TcpFinished = true; v->NatDoCancelFlag = true; if (n->TcpDisconnected == false) { Disconnect(n->Sock); n->TcpDisconnected = true; } break; } else if (recv_size == SOCK_LATER) { // Blocking break; } else { // Successful reception WriteFifo(n->SendFifo, buf, recv_size); v->NatDoCancelFlag = true; } } if (old_send_fifo_size == 0 && FifoSize(n->SendFifo) != 0) { // Reset the time data for timeout when the data is newly queued // in the empty transmission buffer in the transmission process n->TcpLastRecvAckTime = v->Now; } // Raise a transmission time-out if a certain period of time elapsed // after receiving the last ACK, and the transmission buffer is not // empty, and the reception window size of other party is not 0 if ((n->TcpLastRecvAckTime + (UINT64)VIRTUAL_TCP_SEND_TIMEOUT) < v->Now) { if (FifoSize(n->SendFifo) != 0 && n->TcpSendWindowSize != 0) { timeouted = true; } } } break; } // Timeout Detection if ((n->LastCommTime + (UINT64)v->NatTcpTimeout) < v->Now || n->LastCommTime > v->Now) { timeouted = true; } if (timeouted) { // Time-out occurs, the session close n->TcpStatus = NAT_TCP_SEND_RESET; v->NatDoCancelFlag = true; } return true; DISCONNECT: // Disconnect and session disposal DeleteNatTcp(v, n); return false; } // Delete the entry of TCP NAT void DeleteNatTcp(VH *v, NAT_ENTRY *n) { // Validate arguments if (v == NULL || n == NULL) { return; } NLog(v, "LH_NAT_TCP_DELETED", n->Id); // Shutdown of connection thread if (n->NatTcpConnectThread != NULL) { n->NatTcpCancelFlag = true; WaitThread(n->NatTcpConnectThread, INFINITE); ReleaseThread(n->NatTcpConnectThread); n->NatTcpConnectThread = NULL; } if (n->Sock != NULL) { // Disconnect the socket Disconnect(n->Sock); ReleaseSock(n->Sock); n->Sock = NULL; } // Release the window memory if (n->TcpRecvWindow != NULL) { ReleaseFifo(n->TcpRecvWindow); n->TcpRecvWindow = NULL; } // Release the window reception list if (n->TcpRecvList != NULL) { UINT i; for (i = 0;i < LIST_NUM(n->TcpRecvList);i++) { IP_PART *p = LIST_DATA(n->TcpRecvList, i); Free(p); } ReleaseList(n->TcpRecvList); n->TcpRecvList = NULL; } // FIFO release ReleaseFifo(n->SendFifo); ReleaseFifo(n->RecvFifo); // Delete from the NAT entry Delete(v->NatTable, n); DeleteLock(n->lock); // Release the memory Free(n); Debug("NAT_ENTRY: DeleteNatTcp\n"); } // NAT processing thread void NatThread(THREAD *t, void *param) { // Validate arguments if (t == NULL || param == NULL) { return; } // Notify the initialization completion NoticeThreadInit(t); NatThreadMain((VH *)param); } // Send a beacon packet void SendBeacon(VH *v) { UINT dest_ip; ARPV4_HEADER arp; static char beacon_str[] = "SecureNAT Virtual TCP/IP Stack Beacon"; // Validate arguments if (v == NULL) { return; } // Send an UDP dest_ip = (v->HostIP & v->HostMask) | (~v->HostMask); SendUdp(v, dest_ip, 7, v->HostIP, 7, beacon_str, sizeof(beacon_str)); // Build the ARP header arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET); arp.ProtocolType = Endian16(MAC_PROTO_IPV4); arp.HardwareSize = 6; arp.ProtocolSize = 4; arp.Operation = Endian16(ARP_OPERATION_RESPONSE); Copy(arp.SrcAddress, v->MacAddress, 6); arp.SrcIP = v->HostIP; arp.TargetAddress[0] = arp.TargetAddress[1] = arp.TargetAddress[2] = arp.TargetAddress[3] = arp.TargetAddress[4] = arp.TargetAddress[5] = 0xff; arp.TargetIP = dest_ip; // Transmission VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp)); } // Send a TCP packet void SendTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss, void *data, UINT size) { static UCHAR tcp_mss_option[] = {0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}; IPV4_PSEUDO_HEADER *vh; TCP_HEADER *tcp; UINT header_size = TCP_HEADER_SIZE; UINT total_size; // Validate arguments if (v == NULL || (size != 0 && data == NULL)) { return; } // Memory allocation vh = Malloc(sizeof(IPV4_PSEUDO_HEADER) + TCP_HEADER_SIZE + size + 32); tcp = (TCP_HEADER *)(((UCHAR *)vh) + sizeof(IPV4_PSEUDO_HEADER)); if (mss != 0) { USHORT *mss_size; mss_size = (USHORT *)(&tcp_mss_option[2]); *mss_size = Endian16((USHORT)mss); header_size += sizeof(tcp_mss_option); } total_size = header_size + size; if (total_size > 65536) { // Packet is too long Free(vh); return; } // Pseudo header generation vh->SrcIP = src_ip; vh->DstIP = dest_ip; vh->Reserved = 0; vh->Protocol = IP_PROTO_TCP; vh->PacketLength = Endian16((USHORT)total_size); // TCP header generation tcp->SrcPort = Endian16((USHORT)src_port); tcp->DstPort = Endian16((USHORT)dest_port); tcp->SeqNumber = Endian32(seq); tcp->AckNumber = Endian32(ack); tcp->HeaderSizeAndReserved = 0; TCP_SET_HEADER_SIZE(tcp, (UCHAR)(header_size / 4)); tcp->Flag = (UCHAR)flag; tcp->WindowSize = Endian16((USHORT)window_size); tcp->Checksum = 0; tcp->UrgentPointer = 0; // Copy the option values if (mss != 0) { Copy(((UCHAR *)tcp) + TCP_HEADER_SIZE, tcp_mss_option, sizeof(tcp_mss_option)); } // Data copy Copy(((UCHAR *)tcp) + header_size, data, size); // Checksum calculation tcp->Checksum = IpChecksum(vh, total_size + 12); // Submit as an IP packet SendIp(v, dest_ip, src_ip, IP_PROTO_TCP, tcp, total_size); // Release the memory Free(vh); } // Polling process of TCP void PollingNatTcp(VH *v, NAT_ENTRY *n) { // Validate arguments if (v == NULL || n == NULL) { return; } switch (n->TcpStatus) { case NAT_TCP_CONNECTING: // Socket connecting: nothing to do break; case NAT_TCP_CONNECTED: // The socket connected: process SYN + ACK, ACK if ((n->LastSynAckSentTime > v->Now) || n->LastSynAckSentTime == 0 || ((n->LastSynAckSentTime + (UINT64)(NAT_TCP_SYNACK_SEND_TIMEOUT * (UINT64)(n->SynAckSentCount + 1)) <= v->Now))) { n->LastSynAckSentTime = v->Now; // Send a SYN + ACK SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeqInit + n->SendSeq), (UINT)(n->RecvSeqInit + n->RecvSeq), TCP_SYN | TCP_ACK, n->TcpRecvWindowSize, v->TcpMss, NULL, 0); n->SynAckSentCount++; } break; case NAT_TCP_SEND_RESET: // Reset the connection // Send a RST if (n->TcpFinished == false || n->TcpForceReset) { SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeq + n->SendSeqInit), (UINT)(n->SendSeq + n->SendSeqInit), TCP_RST, 0, 0, NULL, 0); // Disconnect n->TcpStatus = NAT_TCP_WAIT_DISCONNECT; n->DisconnectNow = true; } else { // Send FINs for NAT_FIN_SEND_MAX_COUNT times if (n->FinSentTime == 0 || (n->FinSentTime > v->Now) || (n->FinSentTime + NAT_FIN_SEND_INTERVAL * (n->FinSentCount + 1)) < v->Now) { SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeq + n->SendSeqInit), (UINT)(n->RecvSeq + n->RecvSeqInit), TCP_ACK | TCP_FIN, 0, 0, NULL, 0); n->FinSentTime = v->Now; n->FinSentSeq = (UINT)(n->SendSeq + n->SendSeqInit); n->FinSentCount++; if (n->FinSentCount >= NAT_FIN_SEND_MAX_COUNT) { n->TcpFinished = false; } } } break; case NAT_TCP_ESTABLISHED: // Connection established { UINT send_data_size; UINT current_pointer; UINT notice_window_size_value = 0; UINT buf_free_bytes = 0; // Determine the value of the window size to be notified if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE) { buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo); } notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes); if (n->LastSentKeepAliveTime == 0 || (n->LastSentKeepAliveTime + (UINT64)NAT_ACK_KEEPALIVE_SPAN) < v->Now || (n->LastSentKeepAliveTime > v->Now)) { if (n->LastSentKeepAliveTime != 0) { // Send an ACK packet for Keep-Alive SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeqInit + n->SendSeq), (UINT)(n->RecvSeqInit + n->RecvSeq) - 1, TCP_ACK, notice_window_size_value, 0, NULL, 0); } n->LastSentKeepAliveTime = v->Now; } if (n->TcpLastSentTime == 0 || (n->TcpLastSentTime > v->Now) || ((n->TcpLastSentTime + (UINT64)n->TcpSendTimeoutSpan) < v->Now) || n->SendAckNext) { // If there is data to send, send the data // Calculate the segment size to be transmitted send_data_size = n->TcpSendWindowSize; if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize)) { // Apply the cwnd value send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize; } if (send_data_size > n->SendFifo->size) { // Can not be sent over the data that is currently held send_data_size = n->SendFifo->size; } if (send_data_size >= 1) { // Transmit the fragmented segments current_pointer = 0; while (send_data_size > 0) { UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size); void *send_segment = (void *)(((UCHAR *)n->SendFifo->p) + n->SendFifo->pos + current_pointer); SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer), (UINT)(n->RecvSeqInit + n->RecvSeq), TCP_ACK | TCP_PSH, notice_window_size_value, 0, send_segment, send_segment_size); current_pointer += send_segment_size; send_data_size -= send_segment_size; } // Record the transmission time n->TcpLastSentTime = v->Now; // Record the stream size to be transmitted this time n->SendMissionSize = current_pointer; n->CurrentSendingMission = true; // RTT measurement if (n->CalcRTTStartTime == 0) { n->CalcRTTStartTime = v->Now; n->CalcRTTStartValue = n->SendSeq + current_pointer - 1; } if (n->RetransmissionUsedFlag == false) { n->RetransmissionUsedFlag = true; } else { // Congestion is detected if (n->TcpSendCWnd > 2) { n->TcpSendCWnd--; } } } else if (n->SendAckNext) { // Send only an ACK SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeqInit + n->SendSeq), (UINT)(n->RecvSeqInit + n->RecvSeq), TCP_ACK, notice_window_size_value, 0, NULL, 0); } n->SendAckNext = false; } if (n->TcpFinished) { // Disconnect if all data transmission has completed if (n->SendFifo->size == 0) { n->TcpStatus = NAT_TCP_SEND_RESET; } } } break; } } // Reception of TCP packets addressed to the Internet void TcpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, TCP_HEADER *tcp, void *data, UINT size, UINT max_l3_size) { NAT_ENTRY *n, t; UINT seq, ack; UINT64 seq64 = 0, ack64 = 0; // Validate arguments if (v == NULL || tcp == NULL || data == NULL) { return; } if (NnIsActive(v)) { NnTcpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, tcp, data, size, max_l3_size); return; } seq = Endian32(tcp->SeqNumber); ack = Endian32(tcp->AckNumber); if (v->HubOption != NULL && v->HubOption->DisableUserModeSecureNAT) { // Disable User-mode NAT SendTcp(v, dest_ip, dest_port, src_ip, src_port, 0, seq + 1, TCP_RST | TCP_ACK, 0, 0, NULL, 0); return; } // Search for a session for this packet from the NAT table SetNat(&t, NAT_TCP, src_ip, src_port, dest_ip, dest_port, 0, 0); n = SearchNat(v, &t); if (n == NULL) { // There is no existing session // Allow through only SYN packet if ((tcp->Flag & TCP_SYN) && ((tcp->Flag & TCP_ACK) == false)) { TCP_OPTION o; // Create a new session n = CreateNatTcp(v, src_ip, src_port, dest_ip, dest_port); if (n == NULL) { // Return the RST if it was not possible to create SendTcp(v, dest_ip, dest_port, src_ip, src_port, 0, seq + 1, TCP_RST | TCP_ACK, 0, 0, NULL, 0); return; } // Get the options ParseTcpOption(&o, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE); if (o.MaxSegmentSize == 0) { o.MaxSegmentSize = v->TcpMss; } Debug("TCP SYN: MSS=%u, WS=%u\n", o.MaxSegmentSize, o.WindowScaling); // Initial sequence number n->RecvSeqInit = (UINT64)Endian32(tcp->SeqNumber); n->RecvSeq = 1; n->TcpSendMaxSegmentSize = o.MaxSegmentSize; n->TcpRecvWindowSize = NAT_TCP_RECV_WINDOW_SIZE; n->TcpSendWindowSize = (UINT)Endian16(tcp->WindowSize); if (o.WindowScaling != 0) { if (o.WindowScaling > 14) { o.WindowScaling = 14; } n->TcpSendWindowSize = (n->TcpSendWindowSize << o.WindowScaling); } } } if (n == NULL) { // Return a RST since a packet which is not registered in the NAT entry arrived SendTcp(v, dest_ip, dest_port, src_ip, src_port, ack, ack, TCP_RST, 0, 0, NULL, 0); return; } n->TcpLastRecvAckTime = v->Now; switch (n->TcpStatus) { case NAT_TCP_SEND_RESET: // Disconnect the connection by sending a RST if ((tcp->Flag & TCP_ACK) && ((tcp->Flag & TCP_SYN) == false)) { if (n->FinSentCount >= 1) { if (ack == (n->FinSentSeq + 1)) { n->TcpForceReset = true; } } } break; case NAT_TCP_CONNECTED: // Socket connection completion: SYN + ACK, ACK processing if ((tcp->Flag & TCP_ACK) && ((tcp->Flag & TCP_SYN) == false)) { if (seq == (UINT)(n->RecvSeqInit + n->RecvSeq) && ack == (UINT)(n->SendSeqInit + n->SendSeq + 1)) { // Handshake complete since the ACK packet came back n->SendSeq++; // SYN packet consumes the seq by 1 Debug("TCP Connection Established.\n"); n->TcpStatus = NAT_TCP_ESTABLISHED; // Initialize the congestion window size n->TcpSendCWnd = 1; n->LastCommTime = v->Now; } else { goto TCP_RESET; } } else if (tcp->Flag & TCP_RST) { TCP_RESET: // Receive a RST Debug("TCP Connection Reseted.\n"); n->TcpStatus = NAT_TCP_SEND_RESET; } break; case NAT_TCP_ESTABLISHED: // Connection established if (tcp->Flag & TCP_FIN) { // Complete the connection n->TcpFinished = true; } if (tcp->Flag & TCP_RST) { // Receive a RST goto TCP_RESET; } else if (tcp->Flag & TCP_ACK) { TCP_OPTION opt; n->LastCommTime = v->Now; // Get the options, such as window size n->TcpSendWindowSize = Endian16(tcp->WindowSize); ParseTcpOption(&opt, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE); if (opt.WindowScaling != 0) { if (opt.WindowScaling > 14) { opt.WindowScaling = 14; } n->TcpSendWindowSize = (n->TcpSendWindowSize << opt.WindowScaling); } // First, process the received ACK // Store the end position of the stream that has received the acknowledgment to ack64 ack64 = n->SendSeq + (UINT64)ack - (n->SendSeqInit + n->SendSeq) % X32; if ((n->SendSeqInit + n->SendSeq) % X32 > ack) { if (((n->SendSeqInit + n->SendSeq) % X32 - ack) >= 0x80000000) { ack64 = n->SendSeq + (UINT64)ack + X32 - (n->SendSeqInit + n->SendSeq) % X32; } } if (ack64 > n->SendSeq) { // Reception of 1 byte or more seems to have been completed by the client UINT slide_offset = (UINT)(ack64 - n->SendSeq); // Sliding size of the window if (slide_offset == 0 || slide_offset > n->TcpSendWindowSize || slide_offset > n->SendFifo->size) { // Ignore because the offset value of acknowledgment is // larger than the size that should have been sent so far } else { // RTT measurement if (n->CalcRTTStartTime != 0) { if (n->CalcRTTStartValue < ack64) { UINT time_span; if (v->Now > n->CalcRTTStartTime) { time_span = (UINT)(v->Now - n->CalcRTTStartTime); } else { time_span = 100; } n->CalcRTTStartTime = 0; // Smoothing n->CurrentRTT = (UINT) ( ((UINT64)n->CurrentRTT * (UINT64)9 + (UINT64)time_span * (UINT64)1) / (UINT64)10 ); n->TcpSendTimeoutSpan = n->CurrentRTT * 2; } } // Reduce the transmission size n->SendMissionSize -= slide_offset; if (n->SendMissionSize == 0) { // Try to increase the transmission segment size because // all segments to be sent this time have been sent if (n->TcpSendCWnd < 65536) { n->TcpSendCWnd++; } n->CurrentSendingMission = false; n->TcpLastSentTime = 0; n->RetransmissionUsedFlag = false; } // Slide the buffer n->SendSeq += slide_offset; ReadFifo(n->SendFifo, NULL, slide_offset); // Send further by the size of confirmed transmission completion by the ACK this time if (n->SendMissionSize != 0 && false) { UINT notice_window_size_value = 0; UINT send_data_size; UINT buf_free_bytes; UINT send_offset = n->SendMissionSize; // Determine the value of the window size to be notified if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE) { buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo); } notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes); // Calculate the segment size to be transmitted send_data_size = n->TcpSendWindowSize; if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize)) { // Apply the cwnd value send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize; } if (n->SendFifo->size > send_offset) { send_data_size = MIN(send_data_size, n->SendFifo->size - send_offset); send_data_size = MIN(send_data_size, slide_offset); } else { send_data_size = 0; } if (send_data_size >= 1) { // Transmit the fragmented segments UINT current_pointer = 0; while (send_data_size > 0) { UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size); void *send_segment = (void *)(( (UCHAR *)n->SendFifo->p) + n->SendFifo->pos + current_pointer + send_offset); SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort, (UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer + (UINT)send_offset), (UINT)(n->RecvSeqInit + n->RecvSeq), TCP_ACK | TCP_PSH, notice_window_size_value, 0, send_segment, send_segment_size); current_pointer += send_segment_size; send_data_size -= send_segment_size; } n->SendMissionSize += current_pointer; n->CurrentSendingMission = true; n->TcpLastSentTime = v->Now; // RTT measurement if (n->CalcRTTStartTime == 0) { n->CalcRTTStartTime = v->Now; n->CalcRTTStartValue = n->SendSeq + current_pointer - 1; } } } // Event occurs SetSockEvent(v->SockEvent); } } // Next, receive the data seq64 = n->RecvSeq + (UINT64)seq - (n->RecvSeqInit + n->RecvSeq) % X32; if ((n->RecvSeqInit + n->RecvSeq) % X32 > seq) { if (((n->RecvSeqInit + n->RecvSeq) % X32 - ack) >= 0x80000000) { seq64 = n->RecvSeq + (UINT64)seq + X32 - (n->RecvSeqInit + n->RecvSeq) % X32; } } // Position of the starting point of the data from the client is in the seq64 at this time if (seq64 >= n->RecvSeq && (seq64 + size) <= (n->RecvSeq + n->TcpRecvWindowSize)) { if (size >= 1) { // One or more bytes of data has been received within the receive window UINT offset = (UINT)(seq64 - n->RecvSeq); UINT i; IP_PART *me; if (n->TcpRecvWindow == NULL) { n->TcpRecvWindow = NewFifo(); } if (n->TcpRecvList == NULL) { n->TcpRecvList = NewListFast(NULL); } // Add to the list by overwriting arriving packets to the buffer if (FifoSize(n->TcpRecvWindow) < (offset + size)) { // Buffer size expansion WriteFifo(n->TcpRecvWindow, NULL, offset + size - FifoSize(n->TcpRecvWindow)); } Copy(((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos + offset, data, size); me = ZeroMalloc(sizeof(IP_PART)); me->Offset = offset; me->Size = size; for (i = 0;i < LIST_NUM(n->TcpRecvList);i++) { IP_PART *p = LIST_DATA(n->TcpRecvList, i); // If there are overlapped region, remove these if (p->Size != 0) { if (me->Offset <= p->Offset && (me->Offset + me->Size) >= (p->Offset + p->Size)) { // This packet completely overwrite the existing packet p->Size = 0; } else if (me->Offset >= p->Offset && (me->Offset + me->Size) <= (p->Offset + p->Size)) { // Existing packet completely override this packet me->Size = 0; } else if (me->Offset > p->Offset && me->Offset < (p->Offset + p->Size) && (me->Offset + me->Size) > (p->Offset + p->Size)) { // Partially overlapped p->Size -= p->Offset + p->Size - me->Offset; } else if (me->Offset < p->Offset && (me->Offset + size) > p->Offset && (me->Offset + size) < (p->Offset + p->Size)) { // Partially overlapped me->Size -= me->Offset + me->Size - p->Offset; } } } if (me->Size == 0) { Free(me); } else { Add(n->TcpRecvList, me); } KILL_NULL_FIRST: // Remove all blank items from reception list for (i = 0;i < LIST_NUM(n->TcpRecvList);i++) { IP_PART *p = LIST_DATA(n->TcpRecvList, i); if (p->Size == 0) { Delete(n->TcpRecvList, p); Free(p); goto KILL_NULL_FIRST; } } SCAN_FIRST: // Extract if there is something starting at offset 0 in the received list for (i = 0;i < LIST_NUM(n->TcpRecvList);i++) { IP_PART *p = LIST_DATA(n->TcpRecvList, i); UINT sz; if (p->Offset == 0) { // Since a data block starts with 0 is found, // slide it left by that amount and write the buffer // for extracting data to the FIFO sz = p->Size; WriteFifo(n->RecvFifo, ((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos, sz); // Release from the list Delete(n->TcpRecvList, p); Free(p); ReadFifo(n->TcpRecvWindow, NULL, sz); // Slide all the items to the left for (i = 0;i < LIST_NUM(n->TcpRecvList);i++) { p = LIST_DATA(n->TcpRecvList, i); p->Offset -= sz; } // Update the parameters of the TCB n->RecvSeq += (UINT64)sz; SetSockEvent(v->SockEvent); n->SendAckNext = true; // Re-scan from the beginning goto SCAN_FIRST; } } } } } break; } SetSockEvent(v->SockEvent); } // Parse the TCP options void ParseTcpOption(TCP_OPTION *o, void *data, UINT size) { UCHAR *buf = (UCHAR *)data; UINT i; UINT value_size = 0; UINT value_id = 0; UCHAR value[128]; // Validate arguments if (o == NULL || data == NULL) { return; } Zero(o, sizeof(TCP_OPTION)); for (i = 0;i < size;i++) { if (buf[i] == 0) { return; } if (buf[i] != 1) { 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 = Endian16(*wss); } break; } } } } // Create a new NAT TCP session NAT_ENTRY *CreateNatTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port) { NAT_ENTRY *n; HUB_OPTION *o; // Validate arguments if (v == NULL) { return NULL; } if (CanCreateNewNatEntry(v) == false) { return NULL; } o = NatGetHubOption(v); // Fail immediately if the connection with SYN_SENT are too many if (o != NULL && o->SecureNAT_MaxTcpSynSentPerIp != 0) { if (GetNumNatEntriesPerIp(v, src_ip, NAT_TCP, true) >= o->SecureNAT_MaxTcpSynSentPerIp) { return NULL; } } // If the connections other than SYN_SENT are too many, delete old ones if (o != NULL && o->SecureNAT_MaxTcpSessionsPerIp != 0) { if (GetNumNatEntriesPerIp(v, src_ip, NAT_TCP, false) >= o->SecureNAT_MaxTcpSessionsPerIp) { NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_TCP); if (oldest != NULL) { DisconnectNatEntryNow(v, oldest); } } } // Create a NAT entry n = ZeroMalloc(sizeof(NAT_ENTRY)); n->Id = Inc(v->Counter); n->v = v; n->lock = NewLock(); n->Protocol = NAT_TCP; n->SrcIp = src_ip; n->SrcPort = src_port; n->DestIp = dest_ip; n->DestPort = dest_port; n->CreatedTime = n->LastCommTime = v->Now; n->TcpLastRecvAckTime = v->Now; n->Sock = NULL; n->DisconnectNow = false; n->TcpSendMaxSegmentSize = n->TcpRecvMaxSegmentSize = v->TcpMss; n->SendFifo = NewFifo(); n->RecvFifo = NewFifo(); n->TcpStatus = NAT_TCP_CONNECTING; n->SendSeqInit = Rand32(); n->CurrentRTT = NAT_INITIAL_RTT_VALUE; n->TcpSendTimeoutSpan = n->CurrentRTT * 2; // Add to the NAT table Add(v->NatTable, n); #if 1 { IP ip1, ip2; char s1[MAX_SIZE], s2[MAX_SIZE]; UINTToIP(&ip1, src_ip); UINTToIP(&ip2, dest_ip); IPToStr(s1, 0, &ip1); IPToStr(s2, 0, &ip2); Debug("NAT_ENTRY: CreateNatTcp %s %u -> %s %u\n", s1, src_port, s2, dest_port); NLog(v, "LH_NAT_TCP_CREATED", n->Id, s1, src_port, s2, dest_port); } #endif return n; } // Received TCP packets from the virtual network void VirtualTcpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, UINT max_l3_size) { TCP_HEADER *tcp; UINT src_port, dest_port; UINT header_size, buf_size; void *buf; IP ip1, ip2; // Validate arguments if (v == NULL || data == NULL) { return; } // Get the header if (size < TCP_HEADER_SIZE) { // Size is too small return; } tcp = (TCP_HEADER *)data; src_port = Endian16(tcp->SrcPort); dest_port = Endian16(tcp->DstPort); if (src_port == 0 || dest_port == 0) { // Port number is invalid return; } if (src_ip == dest_ip || src_ip == 0 || src_ip == 0xffffffff || dest_ip == 0 || dest_ip == 0xffffffff) { // IP address is invalid return; } UINTToIP(&ip1, src_ip); UINTToIP(&ip2, dest_ip); if (ip1.addr[0] == 127 || ip2.addr[0] == 127) { // Loopback IP address can not be specified return; } if (IsInNetwork(dest_ip, v->HostIP, v->HostMask)) { // Ignore the packets toward the network of the virtual LAN side return; } // Get the header size header_size = TCP_GET_HEADER_SIZE(tcp) * 4; if (size < header_size) { // Header size is invalid return; } // Get the address and size of the buffer buf_size = size - header_size; buf = (void *)(((UCHAR *)data) + header_size); TcpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, tcp, buf, buf_size, max_l3_size); } // NAT ICMP polling void PollingNatIcmp(VH *v, NAT_ENTRY *n) { // Validate arguments if (v == NULL || n == NULL) { return; } // Process if there are any packets in the receive queue if (n->UdpRecvQueue->num_item != 0) { BLOCK *block; // Send all ICMP packets to the virtual network while (block = GetNext(n->UdpRecvQueue)) { // Rewrite the destination IP address of the returned packet to the IP address of the client UCHAR *data; UINT size; data = (UCHAR *)block->Buf; size = block->Size; if (size >= sizeof(IPV4_HEADER)) { IPV4_HEADER *ipv4 = (IPV4_HEADER *)data; UINT ipv4_header_size = GetIpHeaderSize((UCHAR *)ipv4, size); if (ipv4_header_size >= sizeof(IPV4_HEADER) && (Endian16(ipv4->TotalLength) >= ipv4_header_size)) { UCHAR *ipv4_payload = data + ipv4_header_size; UINT ipv4_payload_size = Endian16(ipv4->TotalLength) - ipv4_header_size; if (ipv4_payload_size >= sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)) { ICMP_HEADER *icmp = (ICMP_HEADER *)(data + ipv4_header_size); UINT icmp_size = ipv4_payload_size; if (icmp->Type == ICMP_TYPE_DESTINATION_UNREACHABLE || icmp->Type == ICMP_TYPE_TIME_EXCEEDED) { // Rewrite the Src IP of the IPv4 header of the ICMP response packet if (icmp_size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + sizeof(IPV4_HEADER))) { IPV4_HEADER *orig_ipv4 = (IPV4_HEADER *)(data + ipv4_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); UINT orig_ipv4_size = icmp_size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)); UINT orig_ipv4_header_size = GetIpHeaderSize((UCHAR *)orig_ipv4, orig_ipv4_size); if (orig_ipv4_header_size >= sizeof(IPV4_HEADER)) { orig_ipv4->SrcIP = n->SrcIp; orig_ipv4->Checksum = 0; orig_ipv4->Checksum = IpChecksum(orig_ipv4, orig_ipv4_header_size); } } } // Recalculate the checksum of ICMP icmp->Checksum = IpChecksum(icmp, icmp_size); SendIpEx(v, n->SrcIp, ipv4->SrcIP, ipv4->Protocol, ipv4_payload, ipv4_payload_size, MAX(ipv4->TimeToLive - 1, 1)); } } } FreeBlock(block); } if (v->IcmpRawSocketOk == false) { // Release the NAT entry as soon as the results is received in the case of using ICMP API n->DisconnectNow = true; } } } // NAT UDP polling void PoolingNatUdp(VH *v, NAT_ENTRY *n) { // Validate arguments if (v == NULL || n == NULL) { return; } // Process if there are any packets in the receive queue if (n->UdpRecvQueue->num_item != 0) { BLOCK *block; // Send all UDP packets to the virtual network while (block = GetNext(n->UdpRecvQueue)) { UINT src_ip = n->DestIp; if (src_ip == 0xFFFFFFFF) { src_ip = v->HostIP; } if (block->Param1 != 0) { src_ip = block->Param1; } SendUdp(v, n->SrcIp, n->SrcPort, src_ip, n->DestPort, block->Buf, block->Size); FreeBlock(block); } } } // NAT polling void PoolingNat(VH *v) { UINT i; // Validate arguments if (v == NULL) { return; } if (NnIsActive(v)) { // Poll whether the packet comes from native NAT NnPoll(v->NativeNat); } // Process by scanning the all NAT entries for (i = 0;i < LIST_NUM(v->NatTable);i++) { NAT_ENTRY *n = LIST_DATA(v->NatTable, i); switch (n->Protocol) { case NAT_TCP: PollingNatTcp(v, n); break; case NAT_UDP: PoolingNatUdp(v, n); break; case NAT_ICMP: PollingNatIcmp(v, n); break; case NAT_DNS: PollingNatDns(v, n); break; } } } // Comparison function of the NAT table entry int CompareNat(void *p1, void *p2) { NAT_ENTRY *n1, *n2; if (p1 == NULL || p2 == NULL) { return 0; } n1 = *(NAT_ENTRY **)p1; n2 = *(NAT_ENTRY **)p2; if (n1 == n2) { return 0; } if (n1->SrcIp > n2->SrcIp) return 1; else if (n1->SrcIp < n2->SrcIp) return -1; else if (n1->DestIp > n2->DestIp) return 1; else if (n1->DestIp < n2->DestIp) return -1; else if (n1->SrcPort > n2->SrcPort) return 1; else if (n1->SrcPort < n2->SrcPort) return -1; else if (n1->DestPort > n2->DestPort) return 1; else if (n1->DestPort < n2->DestPort) return -1; else if (n1->Protocol > n2->Protocol) return 1; else if (n1->Protocol < n2->Protocol) return -1; else return 0; } // Configure the NAT structure void SetNat(NAT_ENTRY *n, UINT protocol, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT public_ip, UINT public_port) { // Validate arguments if (n == NULL) { return; } n->Protocol = protocol; n->SrcIp = src_ip; n->SrcPort = src_port; n->DestIp = dest_ip; n->DestPort = dest_port; n->PublicIp = public_ip; n->PublicPort = public_port; } // Initialize the NAT void InitNat(VH *v) { // Validate arguments if (v == NULL) { return; } // Create a NAT table v->NatTable = NewList(CompareNat); // Create a socket event v->SockEvent = NewSockEvent(); // Create the NAT thread v->HaltNat = false; v->NatThread = NewThread(NatThread, (void *)v); WaitThreadInit(v->NatThread); if (IsEthSupported()) { // Start a native NAT if access to the layer 2 Ethernet is supported v->NativeNat = NewNativeNat(v); } } // Release the NAT void FreeNat(VH *v) { // Validate arguments if (v == NULL) { return; } // Stop the native NAT if (v->NativeNat != NULL) { FreeNativeNat(v->NativeNat); v->NativeNat = NULL; } // Stop the NAT thread v->HaltNat = true; SetSockEvent(v->SockEvent); WaitThread(v->NatThread, INFINITE); ReleaseThread(v->NatThread); v->NatThread = NULL; ReleaseSockEvent(v->SockEvent); v->SockEvent = NULL; // Release the NAT table ReleaseList(v->NatTable); } // Search the NAT table NAT_ENTRY *SearchNat(VH *v, NAT_ENTRY *target) { NAT_ENTRY *n; // Validate arguments if (v == NULL || target == NULL) { return NULL; } // Binary search n = (NAT_ENTRY *)Search(v->NatTable, target); return n; } // Delete the UDP NAT entry void DeleteNatUdp(VH *v, NAT_ENTRY *n) { BLOCK *block; // Validate arguments if (v == NULL || n == NULL) { return; } NLog(v, "LH_NAT_UDP_DELETED", n->Id); // Release all queues while (block = GetNext(n->UdpRecvQueue)) { FreeBlock(block); } ReleaseQueue(n->UdpRecvQueue); while (block = GetNext(n->UdpSendQueue)) { FreeBlock(block); } ReleaseQueue(n->UdpSendQueue); // Release the socket if (n->Sock != NULL) { Disconnect(n->Sock); ReleaseSock(n->Sock); n->Sock = NULL; } DeleteLock(n->lock); // Remove from the table Delete(v->NatTable, n); // Release the memory Free(n); Debug("NAT: DeleteNatUdp\n"); } // Delete the ICMP NAT entry void DeleteNatIcmp(VH *v, NAT_ENTRY *n) { BLOCK *block; // Validate arguments if (v == NULL || n == NULL) { return; } //NLog(v, "LH_NAT_ICMP_DELETED", n->Id); // Release all queues while (block = GetNext(n->UdpRecvQueue)) { FreeBlock(block); } ReleaseQueue(n->UdpRecvQueue); while (block = GetNext(n->UdpSendQueue)) { FreeBlock(block); } ReleaseQueue(n->UdpSendQueue); if (n->IcmpQueryBlock != NULL) { FreeBlock(n->IcmpQueryBlock); } if (n->IcmpResponseBlock != NULL) { FreeBlock(n->IcmpResponseBlock); } if (n->IcmpOriginalCopy != NULL) { Free(n->IcmpOriginalCopy); } // Release the socket if (n->Sock != NULL) { Disconnect(n->Sock); ReleaseSock(n->Sock); n->Sock = NULL; } DeleteLock(n->lock); // Remove from the table Delete(v->NatTable, n); // Release the memory Free(n); Debug("NAT: DeleteNatIcmp\n"); } // Create a NAT ICMP entry NAT_ENTRY *CreateNatIcmp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UCHAR *original_copy, UINT original_copy_size) { NAT_ENTRY *n; HUB_OPTION *o; // Validate arguments if (v == NULL || original_copy == NULL || original_copy_size == 0) { return NULL; } if (CanCreateNewNatEntry(v) == false) { return NULL; } o = NatGetHubOption(v); if (o != NULL && o->SecureNAT_MaxIcmpSessionsPerIp != 0) { if (GetNumNatEntriesPerIp(v, src_ip, NAT_ICMP, false) >= o->SecureNAT_MaxIcmpSessionsPerIp) { NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_ICMP); if (oldest != NULL) { DisconnectNatEntryNow(v, oldest); } } } n = ZeroMalloc(sizeof(NAT_ENTRY)); n->Id = Inc(v->Counter); n->v = v; n->lock = NewLock(); n->Protocol = NAT_ICMP; n->SrcIp = src_ip; n->SrcPort = src_port; n->DestIp = dest_ip; n->DestPort = dest_port; n->CreatedTime = n->LastCommTime = v->Now; n->UdpSendQueue = NewQueue(); n->UdpRecvQueue = NewQueue(); n->UdpSocketCreated = false; n->IcmpOriginalCopy = Clone(original_copy, original_copy_size); n->IcmpOriginalCopySize = original_copy_size; SetSockEvent(v->SockEvent); #if 1 { IP ip1, ip2; char s1[MAX_SIZE], s2[MAX_SIZE]; UINTToIP(&ip1, src_ip); UINTToIP(&ip2, dest_ip); IPToStr(s1, 0, &ip1); IPToStr(s2, 0, &ip2); Debug("NAT_ENTRY: CreateNatIcmp %s %u -> %s %u\n", s1, src_port, s2, dest_port); //NLog(v, "LH_NAT_ICMP_CREATED", n->Id, s1, s2, src_port); } #endif Add(v->NatTable, n); return n; } // Create a NAT UDP entry NAT_ENTRY *CreateNatUdp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT dns_proxy_ip) { NAT_ENTRY *n; HUB_OPTION *o; // Validate arguments if (v == NULL) { return NULL; } if (CanCreateNewNatEntry(v) == false) { return NULL; } o = NatGetHubOption(v); if (o != NULL && o->SecureNAT_MaxTcpSessionsPerIp != 0) { if (GetNumNatEntriesPerIp(v, src_ip, NAT_UDP, false) >= o->SecureNAT_MaxUdpSessionsPerIp) { NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_UDP); if (oldest != NULL) { DisconnectNatEntryNow(v, oldest); } } } n = ZeroMalloc(sizeof(NAT_ENTRY)); n->Id = Inc(v->Counter); n->v = v; n->lock = NewLock(); n->Protocol = NAT_UDP; n->SrcIp = src_ip; n->SrcPort = src_port; n->DestIp = dest_ip; n->DestPort = dest_port; if (dns_proxy_ip != 0) { n->ProxyDns = true; n->DestIpProxy = dns_proxy_ip; } n->CreatedTime = n->LastCommTime = v->Now; n->UdpSendQueue = NewQueue(); n->UdpRecvQueue = NewQueue(); n->UdpSocketCreated = false; SetSockEvent(v->SockEvent); #if 1 { IP ip1, ip2; char s1[MAX_SIZE], s2[MAX_SIZE]; UINTToIP(&ip1, src_ip); UINTToIP(&ip2, dest_ip); IPToStr(s1, 0, &ip1); IPToStr(s2, 0, &ip2); Debug("NAT_ENTRY: CreateNatUdp %s %u -> %s %u\n", s1, src_port, s2, dest_port); NLog(v, "LH_NAT_UDP_CREATED", n->Id, s1, src_port, s2, dest_port); } #endif Add(v->NatTable, n); return n; } // Ignore for NetBIOS name registration packet bool IsNetbiosRegistrationPacket(UCHAR *buf, UINT size) { // Validate arguments if (buf == NULL || size == 0) { return false; } if (size >= 4) { USHORT us = *((USHORT *)(buf + 2)); us = Endian16(us); if (((us & 0x7800) >> 11) == 5) { return true; } } return false; } // Generate the encoded NetBIOS name void EncodeNetBiosName(UCHAR *dst, char *src) { char tmp[17]; UINT i; UINT copy_len; UINT wp; // Validate arguments if (dst == NULL || src == NULL) { return; } for (i = 0;i < 16;i++) { tmp[i] = ' '; } tmp[16] = 0; copy_len = StrLen(src); if (copy_len > 16) { copy_len = 16; } Copy(tmp, src, StrLen(src)); wp = 0; tmp[15] = 0; for (i = 0;i < 16;i++) { char c = tmp[i]; char *s = CharToNetBiosStr(c); dst[wp++] = s[0]; dst[wp++] = s[1]; } } // Convert the string to NetBIOS characters char *CharToNetBiosStr(char c) { c = ToUpper(c); switch (c) { case '\0': return "AA"; case 'A': return "EB"; case 'B': return "EC"; case 'C': return "ED"; case 'D': return "EE"; case 'E': return "EF"; case 'F': return "EG"; case 'G': return "EH"; case 'H': return "EI"; case 'I': return "EJ"; case 'J': return "EK"; case 'K': return "EL"; case 'L': return "EM"; case 'M': return "EN"; case 'N': return "EO"; case 'O': return "EP"; case 'P': return "FA"; case 'Q': return "FB"; case 'R': return "FC"; case 'S': return "FD"; case 'T': return "FE"; case 'U': return "FF"; case 'V': return "FG"; case 'W': return "FH"; case 'X': return "FI"; case 'Y': return "FJ"; case 'Z': return "FK"; case '0': return "DA"; case '1': return "DB"; case '2': return "DC"; case '3': return "DD"; case '4': return "DE"; case '5': return "DF"; case '6': return "DG"; case '7': return "DH"; case '8': return "DI"; case '9': return "DJ"; case ' ': return "CA"; case '!': return "CB"; case '\"': return "CC"; case '#': return "CD"; case '$': return "CE"; case '%': return "CF"; case '&': return "CG"; case '\'': return "CH"; case '(': return "CI"; case ')': return "CJ"; case '*': return "CK"; case '+': return "CL"; case ',': return "CM"; case '-': return "CN"; case '.': return "CO"; case '=': return "DN"; case ':': return "DK"; case ';': return "DL"; case '@': return "EA"; case '^': return "FO"; case '_': return "FP"; case '{': return "HL"; case '}': return "HN"; case '~': return "HO"; } return "CA"; } // Process if a NetBIOS name resolution packet for the my host name bool ProcessNetBiosNameQueryPacketForMyself(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size) { BUF *rb; USHORT tran_id; USHORT flags; USHORT num_query; USHORT zero1, zero2, zero3; UCHAR name_size; UCHAR encoded_name[32]; UCHAR node_type; USHORT type, classid; UCHAR my_pc_encoded_name[32]; bool ret = false; // Validate arguments if (v == NULL || data == NULL) { return false; } rb = NewBufFromMemory(data, size); ReadBuf(rb, &tran_id, sizeof(USHORT)); ReadBuf(rb, &flags, sizeof(USHORT)); flags = Endian16(flags); ReadBuf(rb, &num_query, sizeof(USHORT)); num_query = Endian16(num_query); ReadBuf(rb, &zero1, sizeof(USHORT)); ReadBuf(rb, &zero2, sizeof(USHORT)); ReadBuf(rb, &zero3, sizeof(USHORT)); ReadBuf(rb, &name_size, 1); ReadBuf(rb, encoded_name, 32); ReadBuf(rb, &node_type, 1); ReadBuf(rb, &type, sizeof(USHORT)); type = Endian16(type); if (ReadBuf(rb, &classid, sizeof(USHORT)) == sizeof(USHORT)) { classid = Endian16(classid); if (((flags >> 11) & 0x0F) == 0 && num_query == 1 && name_size == 0x20 && zero1 == 0 && zero2 == 0 && zero3 == 0 && node_type == 0 && type == 0x0020 && classid == 0x0001) { char my_pcname[MAX_SIZE]; // Get the encoded name of this PC Zero(my_pcname, sizeof(my_pcname)); GetMachineHostName(my_pcname, sizeof(my_pcname)); EncodeNetBiosName(my_pc_encoded_name, my_pcname); if (Cmp(my_pc_encoded_name, encoded_name, 30) == 0) { // Assemble the response packet since the name resolution packet which targets this PC name received BUF *sb = NewBuf(); USHORT us; UINT ui; LIST *ip_list; BUF *ip_list_buf; bool found = false; WriteBuf(sb, &tran_id, sizeof(USHORT)); flags = Endian16(0x8500); WriteBuf(sb, &flags, sizeof(USHORT)); num_query = 0; WriteBuf(sb, &num_query, sizeof(USHORT)); us = Endian16(1); WriteBuf(sb, &us, sizeof(USHORT)); us = 0; WriteBuf(sb, &us, sizeof(USHORT)); WriteBuf(sb, &us, sizeof(USHORT)); name_size = 0x20; WriteBuf(sb, &name_size, 1); WriteBuf(sb, encoded_name, 32); node_type = 0; WriteBuf(sb, &node_type, 1); type = Endian16(type); classid = Endian16(classid); WriteBuf(sb, &type, sizeof(USHORT)); WriteBuf(sb, &classid, sizeof(USHORT)); ui = Endian32((UINT)(Tick64() / 1000ULL)); WriteBuf(sb, &ui, sizeof(UINT)); ip_list_buf = NewBuf(); ip_list = GetHostIPAddressList(); if (ip_list != NULL) { UINT i; // Return only private IP if there is a private IP for (i = 0;i < LIST_NUM(ip_list);i++) { IP *ip = LIST_DATA(ip_list, i); if (IsIP4(ip) && IsLocalHostIP4(ip) == false && IsZeroIp(ip) == false) { if (IsIPPrivate(ip)) { USHORT flags = Endian16(0x4000); UINT ip_uint = IPToUINT(ip); WriteBuf(ip_list_buf, &flags, sizeof(USHORT)); WriteBuf(ip_list_buf, &ip_uint, sizeof(UINT)); found = true; } } } if (found == false) { // Return all IP if no private IP are found for (i = 0;i < LIST_NUM(ip_list);i++) { IP *ip = LIST_DATA(ip_list, i); if (IsIP4(ip) && IsLocalHostIP4(ip) == false && IsZeroIp(ip) == false) { USHORT flags = Endian16(0x4000); UINT ip_uint = IPToUINT(ip); WriteBuf(ip_list_buf, &flags, sizeof(USHORT)); WriteBuf(ip_list_buf, &ip_uint, sizeof(UINT)); found = true; } } } FreeHostIPAddressList(ip_list); } us = Endian16(ip_list_buf->Size); WriteBuf(sb, &us, sizeof(USHORT)); WriteBufBuf(sb, ip_list_buf); SendUdp(v, src_ip, src_port, v->HostIP, dest_port, sb->Buf, sb->Size); FreeBuf(ip_list_buf); FreeBuf(sb); WHERE; } } } FreeBuf(rb); return ret; } // Process the NetBIOS broadcast packet void UdpRecvForNetBiosBroadcast(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy, bool unicast) { // Validate arguments if (data == NULL || v == NULL) { return; } // Ignore for NetBIOS name registration packet if (IsNetbiosRegistrationPacket(data, size) == false) { if (unicast == false) { dest_ip = 0xFFFFFFFF; } if (ProcessNetBiosNameQueryPacketForMyself(v, src_ip, src_port, dest_ip, dest_port, data, size) == false) { UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, data, size, false); } } } // Process the UDP packet to the Internet void UdpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy) { NAT_ENTRY *n, t; BLOCK *block; void *buf; UINT dns_ip = 0; // Validate arguments if (data == NULL || v == NULL) { return; } if (dns_proxy) { // Get the DNS server of the proxy to connect to IP ip; char tmp[MAX_SIZE]; if (GetDefaultDns(&ip) == false) { // Failure Debug("Failed to GetDefaultDns()\n"); return; } dns_ip = IPToUINT(&ip); IPToStr(tmp, sizeof(tmp), &ip); Debug("Redirect to DNS Server %s\n", tmp); } // Examine whether the NAT entry for this packet has already been created SetNat(&t, NAT_UDP, src_ip, src_port, dest_ip, dest_port, 0, 0); n = SearchNat(v, &t); if (n == NULL) { // Create a NAT entry because it is the first packet n = CreateNatUdp(v, src_ip, src_port, dest_ip, dest_port, dns_proxy ? dns_ip : 0); if (n == NULL) { // Entry creation failed return; } if (dns_proxy) { n->ProxyDns = true; n->DestIpProxy = dns_ip; } } // Set the event by inserting the packet into the queue buf = Malloc(size); Copy(buf, data, size); block = NewBlock(buf, size, 0); InsertQueue(n->UdpSendQueue, block); SetSockEvent(v->SockEvent); } // Attempt to interpret the DNS packet bool ParseDnsPacket(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size) { return ParseDnsPacketEx(v, src_ip, src_port, dest_ip, dest_port, data, size, NULL); } bool ParseDnsPacketEx(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, DNS_PARSED_PACKET *parsed_result) { DNSV4_HEADER *dns; NAT_ENTRY *nat; UINT transaction_id; void *query_data; UINT query_data_size; char hostname[256]; // Validate arguments if (v == NULL || data == NULL || size == 0) { return false; } // Check the header size if (size < sizeof(DNSV4_HEADER)) { // Undersize return false; } // DNS header acquisition dns = (DNSV4_HEADER *)data; transaction_id = Endian16(dns->TransactionId); if ((dns->Flag1 & 78) != 0 || (dns->Flag1 & 0x80) != 0) { // Illegal opcode return false; } if (Endian16(dns->NumQuery) != 1) { // Number of queries is invalid return false; } query_data = ((UCHAR *)dns) + sizeof(DNSV4_HEADER); query_data_size = size - sizeof(DNSV4_HEADER); // Interpret the query if (ParseDnsQuery(hostname, sizeof(hostname), query_data, query_data_size) == false) { // Interpretation fails return false; } if (parsed_result != NULL) { // Only analyse without processing Zero(parsed_result, sizeof(DNS_PARSED_PACKET)); StrCpy(parsed_result->Hostname, sizeof(parsed_result->Hostname), hostname); parsed_result->TransactionId = transaction_id; return true; } // Create a DNS entry nat = CreateNatDns(v, src_ip, src_port, dest_ip, dest_port, transaction_id, false, hostname); if (nat == false) { return false; } return true; } // Send the NAT DNS response packet void SendNatDnsResponse(VH *v, NAT_ENTRY *n) { BUF *b; UINT dns_header_size; DNSV4_HEADER *dns; UINT src_ip; // Validate arguments if (n == NULL || v == NULL) { return; } // Generate the data b = NewBuf(); // Add a Query if (n->DnsGetIpFromHost == false) { BuildDnsQueryPacket(b, n->DnsTargetHostName, false); } else { BuildDnsQueryPacket(b, n->DnsTargetHostName, true); } // Add a Response if (n->DnsOk) { if (n->DnsGetIpFromHost == false) { BuildDnsResponsePacketA(b, &n->DnsResponseIp); } else { BuildDnsResponsePacketPtr(b, n->DnsResponseHostName); } } // Generate a DNS header dns_header_size = sizeof(DNSV4_HEADER) + b->Size; dns = ZeroMalloc(dns_header_size); dns->TransactionId = Endian16((USHORT)n->DnsTransactionId); // Generate a response flag if (n->DnsOk) { dns->Flag1 = 0x85; dns->Flag2 = 0x80; } else { dns->Flag1 = 0x85; dns->Flag2 = 0x83; } dns->NumQuery = Endian16(1); dns->AnswerRRs = Endian16(n->DnsOk != false ? 1 : 0); dns->AuthorityRRs = 0; dns->AdditionalRRs = 0; // Settings, such as the source IP address src_ip = n->DestIp; if (src_ip == Endian32(SPECIAL_IPV4_ADDR_LLMNR_DEST) && n->DestPort == SPECIAL_UDP_PORT_LLMNR) { // Make a unicast response in the case of LLMNR packet src_ip = v->HostIP; dns->Flag1 = 0x84; dns->Flag2 = 0x00; } // Copy data Copy(((UCHAR *)dns) + sizeof(DNSV4_HEADER), b->Buf, b->Size); // Send this packet SendUdp(v, n->SrcIp, n->SrcPort, src_ip, n->DestPort, dns, dns_header_size); // Release the memory Free(dns); FreeBuf(b); } // Generate a DNS response packet (host name) void BuildDnsResponsePacketPtr(BUF *b, char *hostname) { USHORT magic; USHORT type, clas; UINT ttl; USHORT len; BUF *c; // Validate arguments if (b == NULL || hostname == NULL) { return; } magic = Endian16(0xc00c); type = Endian16(0x000c); clas = Endian16(0x0001); ttl = Endian32(NAT_DNS_RESPONSE_TTL); c = BuildDnsHostName(hostname); if (c == NULL) { return; } len = Endian16((USHORT)c->Size); WriteBuf(b, &magic, 2); WriteBuf(b, &type, 2); WriteBuf(b, &clas, 2); WriteBuf(b, &ttl, 4); WriteBuf(b, &len, 2); WriteBuf(b, c->Buf, c->Size); FreeBuf(c); } // Generate a DNS response packet (host IP address) void BuildDnsResponsePacketA(BUF *b, IP *ip) { UINT ip_addr; USHORT magic; USHORT type, clas; UINT ttl; USHORT len; // Validate arguments if (b == NULL || ip == NULL) { return; } ip_addr = IPToUINT(ip); magic = Endian16(0xc00c); type = Endian16(0x0001); clas = Endian16(0x0001); ttl = Endian32(NAT_DNS_RESPONSE_TTL); len = Endian16((USHORT)sizeof(ttl)); WriteBuf(b, &magic, sizeof(magic)); WriteBuf(b, &type, sizeof(type)); WriteBuf(b, &clas, sizeof(clas)); WriteBuf(b, &ttl, sizeof(ttl)); WriteBuf(b, &len, sizeof(len)); WriteBuf(b, &ip_addr, sizeof(ip_addr)); } // Generate a DNS query data packet void BuildDnsQueryPacket(BUF *b, char *hostname, bool ptr) { USHORT val; BUF *c; // Validate arguments if (b == NULL || hostname == NULL) { return; } // Convert the host name to a buffer c = BuildDnsHostName(hostname); if (c == NULL) { return; } WriteBuf(b, c->Buf, c->Size); FreeBuf(c); // Type and class if (ptr == false) { val = Endian16(0x0001); } else { val = Endian16(0x000c); } WriteBuf(b, &val, 2); val = Endian16(0x0001); WriteBuf(b, &val, 2); } // Generate a DNS host name buffer BUF *BuildDnsHostName(char *hostname) { UINT i; UCHAR size; TOKEN_LIST *token; BUF *b; // Validate arguments if (hostname == NULL) { return NULL; } // Split the host name into tokens token = ParseToken(hostname, "."); if (token == NULL) { return NULL; } b = NewBuf(); // Add a host string for (i = 0;i < token->NumTokens;i++) { size = (UCHAR)StrLen(token->Token[i]); WriteBuf(b, &size, 1); WriteBuf(b, token->Token[i], size); } // NULL character size = 0; WriteBuf(b, &size, 1); SeekBuf(b, 0, 0); FreeToken(token); return b; } // Process the NAT DNS entry void PollingNatDns(VH *v, NAT_ENTRY *n) { // Validate arguments if (v == NULL || n == NULL) { return; } if (n->DnsFinished) { if (n->DnsPollingFlag == false) { n->DnsPollingFlag = true; // Process has been completed SendNatDnsResponse(v, n); // Terminating n->DisconnectNow = true; } } } // Create a NAT DNS entry NAT_ENTRY *CreateNatDns(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT transaction_id, bool dns_get_ip_from_host, char *dns_target_host_name) { NAT_ENTRY *n; HUB_OPTION *o; // Validate arguments if (v == NULL || dns_target_host_name == NULL) { return NULL; } if (CanCreateNewNatEntry(v) == false) { return NULL; } o = NatGetHubOption(v); if (o != NULL && o->SecureNAT_MaxDnsSessionsPerIp != 0) { if (GetNumNatEntriesPerIp(v, src_ip, NAT_DNS, false) >= o->SecureNAT_MaxDnsSessionsPerIp) { NAT_ENTRY *oldest = GetOldestNatEntryOfIp(v, src_ip, NAT_DNS); if (oldest != NULL) { DisconnectNatEntryNow(v, oldest); } } } n = ZeroMalloc(sizeof(NAT_ENTRY)); n->Id = Inc(v->Counter); n->v = v; n->lock = NewLock(); n->Protocol = NAT_DNS; n->SrcIp = src_ip; n->SrcPort = src_port; n->DestIp = dest_ip; n->DestPort = dest_port; n->DnsTransactionId = transaction_id; n->CreatedTime = n->LastCommTime = v->Now; n->DisconnectNow = false; n->DnsGetIpFromHost = false; n->DnsTargetHostName = CopyStr(dns_target_host_name); Add(v->NatTable, n); #if 1 { IP ip1, ip2; char s1[MAX_SIZE], s2[MAX_SIZE]; UINTToIP(&ip1, src_ip); UINTToIP(&ip2, dest_ip); IPToStr(s1, 0, &ip1); IPToStr(s2, 0, &ip2); Debug("NAT_ENTRY: CreateNatDns %s %u -> %s %u\n", s1, src_port, s2, dest_port); } #endif return n; } // Get the next byte UCHAR GetNextByte(BUF *b) { UCHAR c = 0; // Validate arguments if (b == NULL) { return 0; } if (ReadBuf(b, &c, 1) != 1) { return 0; } return c; } // Interpret the DNS query bool ParseDnsQuery(char *name, UINT name_size, void *data, UINT data_size) { BUF *b; char tmp[257]; bool ok = true; USHORT val; // Validate arguments if (name == NULL || data == NULL || data_size == 0) { return false; } StrCpy(name, name_size, ""); b = NewBuf(); WriteBuf(b, data, data_size); SeekBuf(b, 0, 0); while (true) { UINT next_len = (UINT)GetNextByte(b); if (next_len > 0) { // Read only the specified length Zero(tmp, sizeof(tmp)); if (ReadBuf(b, tmp, next_len) != next_len) { ok = false; break; } // Append if (StrLen(name) != 0) { StrCat(name, name_size, "."); } StrCat(name, name_size, tmp); } else { // Read all break; } } if (ReadBuf(b, &val, sizeof(val)) != sizeof(val)) { ok = false; } else { if (Endian16(val) != 0x01 && Endian16(val) != 0x0c) { ok = false; } } if (ReadBuf(b, &val, sizeof(val)) != sizeof(val)) { ok = false; } else { if (Endian16(val) != 0x01) { ok = false; } } FreeBuf(b); if (ok == false || StrLen(name) == 0) { return false; } else { return true; } } // 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; } 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 Flagment 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->FlagsAndFlagmentOffset[0] = ip->FlagsAndFlagmentOffset[1] = 0; IPV4_SET_OFFSET(ip, (offset / 8)); if ((offset + size) >= total_size) { IPV4_SET_FLAGS(ip, 0x00); } else { IPV4_SET_FLAGS(ip, 0x01); } ip->TimeToLive = (ttl == 0 ? DEFAULT_IP_TTL : ttl); ip->Protocol = protocol; ip->Checksum = 0; ip->SrcIP = src_ip; ip->DstIP = dest_ip; // Checksum calculation ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE); // Data copy Copy(buf + IP_HEADER_SIZE, data, size); if (dest_mac == NULL) { if (ip->DstIP == 0xffffffff || (IsInNetwork(ip->DstIP, v->HostIP, v->HostMask) && (ip->DstIP & (~v->HostMask)) == (~v->HostMask))) { // Broadcast address dest_mac = broadcast; } else { // Send an ARP query if the destination MAC address is unknown arp = SearchArpTable(v, dest_ip); if (arp != NULL) { dest_mac = arp->MacAddress; } } } if (dest_mac != NULL) { // Send the packet immediately VirtualIpSend(v, dest_mac, buf, size + IP_HEADER_SIZE); // Packet data may be released Free(buf); } else { // Because this packet still can not be transferred, add it to the IP waiting table InsertIpWaitTable(v, dest_ip, src_ip, buf, size + IP_HEADER_SIZE); // Issue an ARP SendArp(v, dest_ip); } } // Send an IP packet (fragmented) void VirtualIpSend(VH *v, UCHAR *dest_mac, void *data, UINT size) { // Validate arguments if (v == NULL || dest_mac == NULL || data == NULL || size == 0) { return; } // Transmission VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_IPV4, data, size); } // Send an ARP request packet void VirtualArpSendRequest(VH *v, UINT dest_ip) { ARPV4_HEADER arp; // Validate arguments if (v == NULL) { return; } // Build the ARP header arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET); arp.ProtocolType = Endian16(MAC_PROTO_IPV4); arp.HardwareSize = 6; arp.ProtocolSize = 4; arp.Operation = Endian16(ARP_OPERATION_REQUEST); Copy(arp.SrcAddress, v->MacAddress, 6); arp.SrcIP = v->HostIP; Zero(&arp.TargetAddress, 6); arp.TargetIP = dest_ip; // Transmission VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp)); } // Send an ARP response packet void VirtualArpSendResponse(VH *v, UCHAR *dest_mac, UINT dest_ip, UINT src_ip) { ARPV4_HEADER arp; // Validate arguments if (v == NULL || dest_mac == NULL) { return; } // Build the ARP header arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET); arp.ProtocolType = Endian16(MAC_PROTO_IPV4); arp.HardwareSize = 6; arp.ProtocolSize = 4; arp.Operation = Endian16(ARP_OPERATION_RESPONSE); Copy(arp.SrcAddress, v->MacAddress, 6); Copy(arp.TargetAddress, dest_mac, 6); arp.SrcIP = src_ip; arp.TargetIP = dest_ip; // Transmission VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(ARPV4_HEADER)); } // An ARP request packet was received void VirtualArpResponseRequest(VH *v, PKT *packet) { ARPV4_HEADER *arp; // Validate arguments if (v == NULL || packet == NULL) { return; } arp = packet->L3.ARPv4Header; // Memory the information of the host IP address and the MAC address of the other party ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress); // Search whether it matches with the IP address of this host if (v->HostIP == arp->TargetIP) { // Respond since the match VirtualArpSendResponse(v, arp->SrcAddress, arp->SrcIP, v->HostIP); return; } // Do nothing if it doesn't match } // An ARP response packet is received void VirtualArpResponseReceived(VH *v, PKT *packet) { ARPV4_HEADER *arp; // Validate arguments if (v == NULL || packet == NULL) { return; } arp = packet->L3.ARPv4Header; // Regard this information as known information ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress); } // Received an ARP packet void VirtualArpReceived(VH *v, PKT *packet) { ARPV4_HEADER *arp; // Validate arguments if (v == NULL || packet == NULL) { return; } arp = packet->L3.ARPv4Header; if (Endian16(arp->HardwareType) != ARP_HARDWARE_TYPE_ETHERNET) { // Ignore if hardware type is other than Ethernet return; } if (Endian16(arp->ProtocolType) != MAC_PROTO_IPV4) { // Ignore if the protocol type is a non-IPv4 return; } if (arp->HardwareSize != 6 || arp->ProtocolSize != 4) { // Ignore because the size of protocol address or hardware address is invalid return; } // Check the source MAC address if (Cmp(arp->SrcAddress, packet->MacAddressSrc, 6) != 0) { // MAC address in the MAC header and the MAC address of the ARP packet are different return; } switch (Endian16(arp->Operation)) { case ARP_OPERATION_REQUEST: // ARP request VirtualArpResponseRequest(v, packet); break; case ARP_OPERATION_RESPONSE: // ARP response VirtualArpResponseReceived(v, packet); break; } } // Release the DHCP server void FreeDhcpServer(VH *v) { UINT i; // Validate arguments if (v == NULL) { return; } // Remove the all lease entries for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++) { DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i); FreeDhcpLease(d); } ReleaseList(v->DhcpLeaseList); v->DhcpLeaseList = NULL; } // Initialize the DHCP server void InitDhcpServer(VH *v) { // Validate arguments if (v == NULL) { return; } // Create a list v->DhcpLeaseList = NewList(CompareDhcpLeaseList); } // Search for a DHCP lease item by the IP address DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip) { UINT i; // Validate arguments if (v == NULL) { return NULL; } for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++) { DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i); if (d->IpAddress == ip) { return d; } } return NULL; } // Search for a DHCP lease item by the MAC address DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac) { DHCP_LEASE *d, t; // Validate arguments if (v == NULL || mac == NULL) { return NULL; } Copy(&t.MacAddress, mac, 6); d = Search(v->DhcpLeaseList, &t); return d; } // Release the DHCP lease item void FreeDhcpLease(DHCP_LEASE *d) { // Validate arguments if (d == NULL) { return; } Free(d->Hostname); Free(d); } // Create a DHCP lease item DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname) { DHCP_LEASE *d; // Validate arguments if (mac_address == NULL || hostname == NULL) { return NULL; } d = ZeroMalloc(sizeof(DHCP_LEASE)); d->LeasedTime = (UINT64)Tick64(); if (expire == INFINITE) { d->ExpireTime = INFINITE; } else { d->ExpireTime = d->LeasedTime + (UINT64)expire; } d->IpAddress = ip; d->Mask = mask; d->Hostname = CopyStr(hostname); Copy(d->MacAddress, mac_address, 6); return d; } // Comparison of the items in the DHCP list int CompareDhcpLeaseList(void *p1, void *p2) { DHCP_LEASE *d1, *d2; // Validate arguments if (p1 == NULL || p2 == NULL) { return 0; } d1 = *(DHCP_LEASE **)p1; d2 = *(DHCP_LEASE **)p2; if (d1 == NULL || d2 == NULL) { return 0; } return Cmp(d1->MacAddress, d2->MacAddress, 6); } // Poll the DHCP server void PollingDhcpServer(VH *v) { UINT i; // Validate arguments if (v == NULL) { return; } if (v->LastDhcpPolling != 0) { if ((v->LastDhcpPolling + (UINT64)DHCP_POLLING_INTERVAL) > v->Now && v->LastDhcpPolling < v->Now) { return; } } v->LastDhcpPolling = v->Now; // Remove expired entries FIRST_LIST: for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++) { DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i); if (d->ExpireTime < v->Now) { FreeDhcpLease(d); Delete(v->DhcpLeaseList, d); goto FIRST_LIST; } } } // Correspond to the DHCP REQUEST UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip) { UINT ret; // Validate arguments if (v == NULL || mac == NULL) { return 0; } ret = ServeDhcpDiscover(v, mac, request_ip); if (ret != request_ip) { if (request_ip != 0) { // Raise an error if the requested IP address cannot to be assigned return 0; } } return ret; } // Correspond to the DHCP DISCOVER UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip) { UINT ret = 0; // Validate arguments if (v == NULL || mac == NULL) { return 0; } if (request_ip != 0) { // IP address is specified DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip); if (d != NULL) { // If an entry for the same IP address already exists, // check whether it is a request from the same MAC address if (Cmp(mac, d->MacAddress, 6) == 0) { // Examine whether the specified IP address is within the range of assignment if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) && Endian32(request_ip) <= Endian32(v->DhcpIpEnd)) { // Accept if within the range ret = request_ip; } } } else { // Examine whether the specified IP address is within the range of assignment if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) && Endian32(request_ip) <= Endian32(v->DhcpIpEnd)) { // Accept if within the range ret = request_ip; } else { // Propose an IP in the range since it's a Discover although It is out of range } } } if (ret == 0) { // If there is any entry with the same MAC address // that are already registered, use it with priority DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac); if (d != NULL) { // Examine whether the found IP address is in the allocation region if (Endian32(v->DhcpIpStart) <= Endian32(d->IpAddress) && Endian32(d->IpAddress) <= Endian32(v->DhcpIpEnd)) { // Use the IP address if it's found within the range ret = d->IpAddress; } } } if (ret == 0) { // Take an appropriate IP addresses that can be assigned newly HUB_OPTION *opt = NatGetHubOption(v); if (opt != NULL && opt->SecureNAT_RandomizeAssignIp) { ret = GetFreeDhcpIpAddressByRandom(v, mac); } else { ret = GetFreeDhcpIpAddress(v); } } return ret; } // Take an appropriate IP addresses that can be assigned newly UINT GetFreeDhcpIpAddress(VH *v) { UINT ip_start, ip_end; UINT i; // Validate arguments if (v == NULL) { return 0; } ip_start = Endian32(v->DhcpIpStart); ip_end = Endian32(v->DhcpIpEnd); for (i = ip_start; i <= ip_end;i++) { UINT ip = Endian32(i); if (SearchDhcpLeaseByIp(v, ip) == NULL) { // A free IP address is found return ip; } } // There is no free address return 0; } // Take an appropriate IP addresses that can be assigned newly (random) UINT GetFreeDhcpIpAddressByRandom(VH *v, UCHAR *mac) { UINT ip_start, ip_end; UINT i; UINT num_retry; // Validate arguments if (v == NULL || mac == NULL) { return 0; } ip_start = Endian32(v->DhcpIpStart); ip_end = Endian32(v->DhcpIpEnd); if (ip_start > ip_end) { return 0; } num_retry = (ip_end - ip_start + 1) * 2; num_retry = MIN(num_retry, 65536 * 2); for (i = 0;i < num_retry;i++) { UCHAR rand_seed[sizeof(UINT) + 6]; UCHAR hash[16]; UINT rand_int; UINT new_ip; WRITE_UINT(&rand_seed[0], i); Copy(rand_seed + sizeof(UINT), mac, 6); Hash(hash, rand_seed, sizeof(rand_seed), false); rand_int = READ_UINT(hash); new_ip = Endian32(ip_start + (rand_int % (ip_end - ip_start + 1))); if (SearchDhcpLeaseByIp(v, new_ip) == NULL) { // A free IP address is found return new_ip; } } // There is no free address return 0; } // Virtual DHCP Server void VirtualDhcpServer(VH *v, PKT *p) { DHCPV4_HEADER *dhcp; UCHAR *data; UINT size; UINT dhcp_header_size; UINT dhcp_data_offset; UINT tran_id; UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE); bool ok; DHCP_OPTION_LIST *opt; // Validate arguments if (v == NULL || p == NULL) { return; } if (v->NativeNat != NULL) { if (Cmp(p->MacAddressSrc, v->NativeNat->CurrentMacAddress, 6) == 0) { // DHCP server is kept from responding for the native NAT interface // ** Not be needed to return yet ** //return; } } dhcp = p->L7.DHCPv4Header; tran_id = Endian32(dhcp->TransactionId); // Get the DHCP data and size dhcp_header_size = sizeof(DHCPV4_HEADER); dhcp_data_offset = (UINT)(((UCHAR *)p->L7.DHCPv4Header) - ((UCHAR *)p->MacHeader) + dhcp_header_size); data = ((UCHAR *)dhcp) + dhcp_header_size; size = p->PacketSize - dhcp_data_offset; if (dhcp_header_size < 5) { // Data size is invalid return; } // Search for Magic Cookie ok = false; while (size >= 5) { if (Cmp(data, &magic_cookie, sizeof(magic_cookie)) == 0) { // Found data += 4; size -= 4; ok = true; break; } data++; size--; } if (ok == false) { // The packet is invalid return; } // Parse DHCP options list opt = ParseDhcpOptionList(data, size); if (opt == NULL) { // The packet is invalid return; } if (StartWith(opt->Hostname, NN_HOSTNAME_STARTWITH) || StartWith(opt->Hostname, NN_HOSTNAME_STARTWITH2)) { Free(opt); return; } if (dhcp->OpCode == 1 && (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST || opt->Opcode == DHCP_INFORM)) { // Operate as the server UINT ip = 0; if (opt->RequestedIp == 0) { opt->RequestedIp = p->L3.IPv4Header->SrcIP; } if (opt->Opcode == DHCP_DISCOVER) { // Return an IP address that can be used ip = ServeDhcpDiscover(v, p->MacAddressSrc, opt->RequestedIp); } else if (opt->Opcode == DHCP_REQUEST) { // Determine the IP address ip = ServeDhcpRequest(v, p->MacAddressSrc, opt->RequestedIp); } if (ip != 0 || opt->Opcode == DHCP_INFORM) { // Respond if there is providable IP address if (opt->Opcode == DHCP_REQUEST) { DHCP_LEASE *d; char mac[MAX_SIZE]; char str[MAX_SIZE]; // Remove old records with the same IP address d = SearchDhcpLeaseByIp(v, ip); if (d != NULL) { FreeDhcpLease(d); Delete(v->DhcpLeaseList, d); } // Create a new entry d = NewDhcpLease(v->DhcpExpire, p->MacAddressSrc, ip, v->DhcpMask, opt->Hostname); d->Id = ++v->DhcpId; Add(v->DhcpLeaseList, d); MacToStr(mac, sizeof(mac), d->MacAddress); IPToStr32(str, sizeof(str), d->IpAddress); NLog(v, "LH_NAT_DHCP_CREATED", d->Id, mac, str, d->Hostname, v->DhcpExpire / 1000); } // Respond if (true) { DHCP_OPTION_LIST ret; LIST *o; Zero(&ret, sizeof(ret)); ret.Opcode = (opt->Opcode == DHCP_DISCOVER ? DHCP_OFFER : DHCP_ACK); ret.ServerAddress = v->HostIP; if (v->DhcpExpire == INFINITE) { ret.LeaseTime = INFINITE; } else { ret.LeaseTime = Endian32(v->DhcpExpire / 1000); } if (opt->Opcode == DHCP_INFORM) { ret.LeaseTime = 0; } StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain); ret.SubnetMask = v->DhcpMask; ret.DnsServer = v->DhcpDns; ret.DnsServer2 = v->DhcpDns2; ret.Gateway = v->DhcpGateway; if (GetGlobalServerFlag(GSF_DISABLE_PUSH_ROUTE) == 0) { Copy(&ret.ClasslessRoute, &v->PushRoute, sizeof(DHCP_CLASSLESS_ROUTE_TABLE)); if (IsIpcMacAddress(p->MacAddressSrc)) { if (ret.Gateway == 0) { // If the default gateway is not specified, add the static routing table // entry for the local IP subnet // (for PPP clients) IP dhcp_ip; IP dhcp_mask; IP dhcp_network; UINTToIP(&dhcp_ip, ip); if (ip == 0) { UINTToIP(&dhcp_ip, p->L3.IPv4Header->SrcIP); } UINTToIP(&dhcp_mask, v->DhcpMask); IPAnd4(&dhcp_network, &dhcp_ip, &dhcp_mask); if (GetBestClasslessRoute(&ret.ClasslessRoute, &dhcp_ip) == NULL) { if (ret.ClasslessRoute.NumExistingRoutes < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES) { DHCP_CLASSLESS_ROUTE *cr = &ret.ClasslessRoute.Entries[ret.ClasslessRoute.NumExistingRoutes]; cr->Exists = true; UINTToIP(&cr->Gateway, v->HostIP); if (v->UseNat == false && ret.ClasslessRoute.NumExistingRoutes >= 1) { Copy(&cr->Gateway, &ret.ClasslessRoute.Entries[0].Gateway, sizeof(IP)); } Copy(&cr->Network, &dhcp_network, sizeof(IP)); Copy(&cr->SubnetMask, &dhcp_mask, sizeof(IP)); cr->SubnetMaskLen = SubnetMaskToInt(&dhcp_mask); ret.ClasslessRoute.NumExistingRoutes++; } } } } } if (opt->Opcode != DHCP_INFORM) { char client_mac[MAX_SIZE]; char client_ip[64]; IP ips; BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6); UINTToIP(&ips, ip); IPToStr(client_ip, sizeof(client_ip), &ips); Debug("DHCP %s : %s given %s\n", ret.Opcode == DHCP_OFFER ? "DHCP_OFFER" : "DHCP_ACK", client_mac, client_ip); } // Build a DHCP option o = BuildDhcpOption(&ret); if (o != NULL) { BUF *b = BuildDhcpOptionsBuf(o); if (b != NULL) { UINT dest_ip = p->L3.IPv4Header->SrcIP; if (dest_ip == 0) { dest_ip = 0xffffffff; } // Transmission VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort), ip, dhcp->ClientMacAddress, b, dhcp->HardwareType, dhcp->HardwareAddressSize); // Release the memory FreeBuf(b); } FreeDhcpOptions(o); } } } else { // There is no IP address that can be provided DHCP_OPTION_LIST ret; LIST *o; Zero(&ret, sizeof(ret)); ret.Opcode = DHCP_NACK; ret.ServerAddress = v->HostIP; StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain); ret.SubnetMask = v->DhcpMask; // Build the DHCP option o = BuildDhcpOption(&ret); if (o != NULL) { BUF *b = BuildDhcpOptionsBuf(o); if (b != NULL) { UINT dest_ip = p->L3.IPv4Header->SrcIP; if (dest_ip == 0) { dest_ip = 0xffffffff; } // Transmission VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort), ip, dhcp->ClientMacAddress, b, dhcp->HardwareType, dhcp->HardwareAddressSize); // Release the memory FreeBuf(b); } FreeDhcpOptions(o); } } } // Release the memory Free(opt); } // Submit the DHCP response packet void VirtualDhcpSend(VH *v, UINT tran_id, UINT dest_ip, UINT dest_port, UINT new_ip, UCHAR *client_mac, BUF *b, UINT hw_type, UINT hw_addr_size) { UINT blank_size = 128 + 64; UINT dhcp_packet_size; UINT magic = Endian32(DHCP_MAGIC_COOKIE); DHCPV4_HEADER *dhcp; void *magic_cookie_addr; void *buffer_addr; // Validate arguments if (v == NULL || b == NULL) { return; } // Calculate the DHCP packet size dhcp_packet_size = blank_size + sizeof(DHCPV4_HEADER) + sizeof(magic) + b->Size; if (dhcp_packet_size < DHCP_MIN_SIZE) { // Padding dhcp_packet_size = DHCP_MIN_SIZE; } // Create a header dhcp = ZeroMalloc(dhcp_packet_size); dhcp->OpCode = 2; dhcp->HardwareType = hw_type; dhcp->HardwareAddressSize = hw_addr_size; dhcp->Hops = 0; dhcp->TransactionId = Endian32(tran_id); dhcp->Seconds = 0; dhcp->Flags = 0; dhcp->YourIP = new_ip; dhcp->ServerIP = v->HostIP; Copy(dhcp->ClientMacAddress, client_mac, 6); // Calculate the address magic_cookie_addr = (((UCHAR *)dhcp) + sizeof(DHCPV4_HEADER) + blank_size); buffer_addr = ((UCHAR *)magic_cookie_addr) + sizeof(magic); // Magic Cookie Copy(magic_cookie_addr, &magic, sizeof(magic)); // Buffer Copy(buffer_addr, b->Buf, b->Size); // Transmission SendUdp(v, dest_ip, dest_port, v->HostIP, NAT_DHCP_SERVER_PORT, dhcp, dhcp_packet_size); Free(dhcp); } // Virtual host: Process the Layer2 void VirtualLayer2(VH *v, PKT *packet) { bool ok; // Validate arguments if (packet == NULL || v == NULL) { return; } // Packet filter if (VirtualLayer2Filter(v, packet) == false) { // Packet was ignored return; } ok = false; if (packet->TypeL3 == L3_IPV4 && packet->TypeL4 == L4_UDP && packet->TypeL7 == L7_DHCPV4) { if (v->UseDhcp) { // A special treatment on the DHCP packet if (packet->BroadcastPacket || Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0) { // Virtual DHCP server processing VirtualDhcpServer(v, packet); ok = true; } } } if (ok == false) { // The process for each supported protocol switch (packet->TypeL3) { case L3_ARPV4: // ARPv4 VirtualArpReceived(v, packet); break; case L3_IPV4: // IPv4 VirtualIpReceived(v, packet); break; } } } // Packet filter (Blocking packets to other than me) bool VirtualLayer2Filter(VH *v, PKT *packet) { // Validate arguments if (v == NULL || packet == NULL) { return false; } // Pass through if broadcast packet if (packet->BroadcastPacket) { return true; } // Ignore if the sender of the packet is myself if (Cmp(packet->MacAddressSrc, v->MacAddress, 6) == 0) { return false; } // Pass through in the case of a packet addressed to me if (Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0) { return true; } // Discard if the other packets return false; } // The virtual host is made to receive a packet bool VirtualPutPacket(VH *v, void *data, UINT size) { if (data == NULL) { // Flush v->flag1 = false; if (v->NativeNat != NULL) { if (v->NativeNat->SendStateChanged) { TUBE *halt_tube = NULL; Lock(v->NativeNat->Lock); { if (v->NativeNat->HaltTube != NULL) { halt_tube = v->NativeNat->HaltTube; AddRef(halt_tube->Ref); } } Unlock(v->NativeNat->Lock); if (halt_tube != NULL) { TubeFlushEx(halt_tube, true); v->NativeNat->SendStateChanged = false; ReleaseTube(halt_tube); } } } } else { // Interpret the received packet PKT *packet = ParsePacket(data, size); if (v->flag1 == false) { v->flag1 = true; v->Now = Tick64(); } // Lock the entire virtual machine in here LockVirtual(v); { if (packet != NULL) { // Process the Layer-2 VirtualLayer2(v, packet); // Release the packet structure FreePacket(packet); } } UnlockVirtual(v); Free(data); } return true; } bool VirtualPaPutPacket(SESSION *s, void *data, UINT size) { VH *v; // Validate arguments if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL) { return false; } return VirtualPutPacket(v, data, size); } // Get the options for the virtual host void GetVirtualHostOption(VH *v, VH_OPTION *o) { // Validate arguments if (v == NULL) { return; } LockVirtual(v); { Zero(o, sizeof(VH_OPTION)); // MAC address Copy(o->MacAddress, v->MacAddress, 6); // Host information UINTToIP(&o->Ip, v->HostIP); UINTToIP(&o->Mask, v->HostMask); o->Mtu = v->Mtu; // NAT timeout information o->NatTcpTimeout = v->NatTcpTimeout / 1000; o->NatUdpTimeout = v->NatUdpTimeout / 1000; // NAT using flag o->UseNat = v->UseNat; // DHCP using flag o->UseDhcp = v->UseDhcp; // IP address range for DHCP distribution UINTToIP(&o->DhcpLeaseIPStart, v->DhcpIpStart); UINTToIP(&o->DhcpLeaseIPEnd, v->DhcpIpEnd); // Subnet mask UINTToIP(&o->DhcpSubnetMask, v->DhcpMask); // Expiration date if (v->DhcpExpire != INFINITE) { o->DhcpExpireTimeSpan = v->DhcpExpire / 1000; } else { o->DhcpExpireTimeSpan = INFINITE; } // Gateway address UINTToIP(&o->DhcpGatewayAddress, v->DhcpGateway); // DNS server address UINTToIP(&o->DhcpDnsServerAddress, v->DhcpDns); UINTToIP(&o->DhcpDnsServerAddress2, v->DhcpDns2); // Domain name StrCpy(o->DhcpDomainName, sizeof(o->DhcpDomainName), v->DhcpDomain); // Save a log o->SaveLog = v->SaveLog; // Pushing route option BuildClasslessRouteTableStr(o->DhcpPushRoutes, sizeof(o->DhcpPushRoutes), &v->PushRoute); o->ApplyDhcpPushRoutes = true; } UnlockVirtual(v); } // Set the option to the virtual host void SetVirtualHostOption(VH *v, VH_OPTION *vo) { UINT i; // Validate arguments if (v == NULL || vo == NULL) { return; } LockVirtual(v); { // Set the MAC address for (i = 0;i < 6;i++) { if (vo->MacAddress[i] != 0) { Copy(v->MacAddress, vo->MacAddress, 6); break; } } // Set the host information list v->HostIP = IPToUINT(&vo->Ip); v->HostMask = IPToUINT(&vo->Mask); // Set the MTU, MMS v->Mtu = MIN(vo->Mtu, MAX_L3_DATA_SIZE); if (v->Mtu == 0) { v->Mtu = MAX_L3_DATA_SIZE; } v->Mtu = MAX(v->Mtu, TCP_HEADER_SIZE + IP_HEADER_SIZE + MAC_HEADER_SIZE + 8); v->IpMss = ((v->Mtu - IP_HEADER_SIZE) / 8) * 8; v->TcpMss = ((v->IpMss - TCP_HEADER_SIZE) / 8) * 8; v->UdpMss = ((v->IpMss - UDP_HEADER_SIZE) / 8) * 8; if (vo->NatTcpTimeout != 0) { v->NatTcpTimeout = MIN(vo->NatTcpTimeout, 4000000) * 1000; } if (vo->NatUdpTimeout != 0) { v->NatUdpTimeout = MIN(vo->NatUdpTimeout, 4000000) * 1000; } v->NatTcpTimeout = MAKESURE(v->NatTcpTimeout, NAT_TCP_MIN_TIMEOUT, NAT_TCP_MAX_TIMEOUT); v->NatUdpTimeout = MAKESURE(v->NatUdpTimeout, NAT_UDP_MIN_TIMEOUT, NAT_UDP_MAX_TIMEOUT); Debug("Timeout: %d , %d\n", v->NatTcpTimeout, v->NatUdpTimeout); // NAT using flag v->UseNat = vo->UseNat; // DHCP using flag v->UseDhcp = vo->UseDhcp; // Expiration date if (vo->DhcpExpireTimeSpan == 0 || vo->DhcpExpireTimeSpan == INFINITE) { v->DhcpExpire = INFINITE; } else { v->DhcpExpire = MAKESURE(DHCP_MIN_EXPIRE_TIMESPAN, MIN(vo->DhcpExpireTimeSpan * 1000, 2000000000), INFINITE); } // Address range to be distributed v->DhcpIpStart = IPToUINT(&vo->DhcpLeaseIPStart); v->DhcpIpEnd = IPToUINT(&vo->DhcpLeaseIPEnd); if (Endian32(v->DhcpIpEnd) < Endian32(v->DhcpIpStart)) { v->DhcpIpEnd = v->DhcpIpStart; } // Subnet mask v->DhcpMask = IPToUINT(&vo->DhcpSubnetMask); // Gateway address v->DhcpGateway = IPToUINT(&vo->DhcpGatewayAddress); // DNS server address v->DhcpDns = IPToUINT(&vo->DhcpDnsServerAddress); v->DhcpDns2 = IPToUINT(&vo->DhcpDnsServerAddress2); // Domain name StrCpy(v->DhcpDomain, sizeof(v->DhcpDomain), vo->DhcpDomainName); // Save a log v->SaveLog = vo->SaveLog; // DHCP routing table pushing setting if (vo->ApplyDhcpPushRoutes) { DHCP_CLASSLESS_ROUTE_TABLE rt; Zero(&rt, sizeof(rt)); if (ParseClasslessRouteTableStr(&rt, vo->DhcpPushRoutes)) { Copy(&v->PushRoute, &rt, sizeof(DHCP_CLASSLESS_ROUTE_TABLE)); } } } UnlockVirtual(v); } // Release the virtual host void Virtual_Free(VH *v) { // Release the DHCP server FreeDhcpServer(v); // NAT release FreeNat(v); LockVirtual(v); { // Release the IP combining list FreeIpCombineList(v); // Release the IP waiting table FreeIpWaitTable(v); // Release the ARP waiting table FreeArpWaitTable(v); // Release the ARP table FreeArpTable(v); // Release the transmission queue LockQueue(v->SendQueue); { BLOCK *block; // Release all queues while (block = GetNext(v->SendQueue)) { FreeBlock(block); } } UnlockQueue(v->SendQueue); ReleaseQueue(v->SendQueue); v->SendQueue = NULL; // Release the cancel object ReleaseCancel(v->Cancel); v->Active = false; } UnlockVirtual(v); // Release the logger FreeLog(v->Logger); } void VirtualPaFree(SESSION *s) { VH *v; // Validate arguments if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL) { return; } Virtual_Free(v); } // Release the virtual host void ReleaseVirtual(VH *v) { // Validate arguments if (v == NULL) { return; } if (Release(v->ref) == 0) { CleanupVirtual(v); } } // Lock the virtual host void LockVirtual(VH *v) { // Validate arguments if (v == NULL) { return; } Lock(v->lock); } // Unlock the virtual host void UnlockVirtual(VH *v) { // Validate arguments if (v == NULL) { return; } Unlock(v->lock); } // Cleanup the virtual host void CleanupVirtual(VH *v) { // Validate arguments if (v == NULL) { return; } if (v->Session != NULL) { ReleaseSession(v->Session); } DeleteCounter(v->Counter); DeleteLock(v->lock); Free(v); } // Stop the virtual host void StopVirtualHost(VH *v) { SESSION *s; // Validate arguments if (v == NULL) { return; } // Get the session corresponding to the virtual host LockVirtual(v); { s = v->Session; if (s != NULL) { AddRef(s->ref); } } UnlockVirtual(v); if (s == NULL) { // This session is already stopped return; } // Stop Session StopSession(s); ReleaseSession(s); } // Create a new virtual host VH *NewVirtualHost(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option) { return NewVirtualHostEx(cedar, option, auth, vh_option, NULL); } VH *NewVirtualHostEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option, NAT *nat) { VH *v; SOCK *s; // Validate arguments if (vh_option == NULL) { return NULL; } // Create a VH v = ZeroMalloc(sizeof(VH)); v->ref = NewRef(); v->lock = NewLock(); v->Counter = NewCounter(); v->nat = nat; // Examine whether ICMP Raw Socket can be created s = NewUDP4(MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4), NULL); if (s != NULL) { if (s->IsTtlSupported) { v->IcmpRawSocketOk = true; } ReleaseSock(s); } if (v->IcmpRawSocketOk == false) { if (IsIcmpApiSupported()) { v->IcmpApiOk = true; } } // Set the options SetVirtualHostOption(v, vh_option); return v; } // Generate a random MAC address void GenMacAddress(UCHAR *mac) { UCHAR rand_data[32]; UINT64 now; BUF *b; UCHAR hash[SHA1_SIZE]; // Validate arguments if (mac == NULL) { return; } // Get the current time now = SystemTime64(); // Generate a random number Rand(rand_data, sizeof(rand_data)); // Add to the buffer b = NewBuf(); WriteBuf(b, &now, sizeof(now)); WriteBuf(b, rand_data, sizeof(rand_data)); // Hash Hash(hash, b->Buf, b->Size, true); // Generate a MAC address mac[0] = 0x00; mac[1] = 0xAC; // AC hurray mac[2] = hash[0]; mac[3] = hash[1]; mac[4] = hash[2]; mac[5] = hash[3]; FreeBuf(b); } // Get a packet of virtual host adapter PACKET_ADAPTER *VirtualGetPacketAdapter() { return NewPacketAdapter(VirtualPaInit, VirtualPaGetCancel, VirtualPaGetNextPacket, VirtualPaPutPacket, VirtualPaFree); } // Developed by SoftEther VPN Project at University of Tsukuba in Japan. // Department of Computer Science has dozens of overly-enthusiastic geeks. // Join us: http://www.tsukuba.ac.jp/english/admission/