Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/SoftEtherVPN/SoftEtherVPN_Stable.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Cedar/Interop_OpenVPN.c')
-rw-r--r--src/Cedar/Interop_OpenVPN.c2942
1 files changed, 2942 insertions, 0 deletions
diff --git a/src/Cedar/Interop_OpenVPN.c b/src/Cedar/Interop_OpenVPN.c
new file mode 100644
index 00000000..48045333
--- /dev/null
+++ b/src/Cedar/Interop_OpenVPN.c
@@ -0,0 +1,2942 @@
+// 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 IT IN OTHER COUNTRIES. IMPORTING THIS
+// SOFTWARE INTO OTHER COUNTRIES IS AT YOUR OWN RISK. SOME COUNTRIES
+// PROHIBIT ENCRYPTED COMMUNICATIONS. USING THIS SOFTWARE IN OTHER
+// COUNTRIES MIGHT BE RESTRICTED.
+//
+//
+// 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.
+
+
+// Interop_OpenVPN.c
+// OpenVPN protocol stack
+
+#include "CedarPch.h"
+
+
+static bool g_no_openvpn_tcp = false;
+static bool g_no_openvpn_udp = false;
+
+// Ping signature of the OpenVPN protocol
+static UCHAR ping_signature[] =
+{
+ 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb,
+ 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48
+};
+
+// Set the OpenVPN over TCP disabling flag
+void OvsSetNoOpenVpnTcp(bool b)
+{
+ g_no_openvpn_tcp = b;
+}
+
+// Get the OpenVPN over TCP disabling flag
+bool OvsGetNoOpenVpnTcp()
+{
+ return g_no_openvpn_tcp;
+}
+
+// Set the OpenVPN over UDP disabling flag
+void OvsSetNoOpenVpnUdp(bool b)
+{
+ g_no_openvpn_udp = b;
+}
+
+// Get the OpenVPN over UDP disabling flag
+bool OvsGetNoOpenVpnUdp()
+{
+ return g_no_openvpn_udp;
+}
+
+
+// Write the OpenVPN log
+void OvsLog(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, char *name, ...)
+{
+ wchar_t prefix[MAX_SIZE * 2];
+ wchar_t buf2[MAX_SIZE * 2];
+ va_list args;
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+ if (se == NULL)
+ {
+ UniStrCpy(prefix, sizeof(prefix), _UU("LO_PREFIX_RAW"));
+ }
+ else
+ {
+ if (c == NULL)
+ {
+ UniFormat(prefix, sizeof(prefix), _UU("LO_PREFIX_SESSION"),
+ se->Id, &se->ClientIp, se->ClientPort, &se->ServerIp, se->ServerPort);
+ }
+ else
+ {
+ UniFormat(prefix, sizeof(prefix), _UU("LO_PREFIX_CHANNEL"),
+ se->Id, &se->ClientIp, se->ClientPort, &se->ServerIp, se->ServerPort,
+ c->KeyId);
+ }
+ }
+ va_start(args, name);
+ UniFormatArgs(buf2, sizeof(buf2), _UU(name), args);
+ va_end(args);
+
+ UniStrCat(prefix, sizeof(prefix), buf2);
+
+ WriteServerLog(s->Cedar, prefix);
+}
+
+// Process the received packet
+void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
+{
+ OPENVPN_SESSION *se;
+ OPENVPN_PACKET *recv_packet;
+ // Validate arguments
+ if (s == NULL || p == NULL)
+ {
+ return;
+ }
+
+
+ // Search for the session
+ se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, protocol);
+ if (se == NULL)
+ {
+ return;
+ }
+
+ // Parse the packet
+ recv_packet = OvsParsePacket(p->Data, p->Size);
+
+ if (recv_packet != NULL)
+ {
+ OPENVPN_CHANNEL *c = NULL;
+ if (recv_packet->OpCode != OPENVPN_P_DATA_V1 && recv_packet->MySessionId != 0)
+ {
+ Debug("RECV PACKET: %u %I64u\n", recv_packet->KeyId, recv_packet->MySessionId);
+ }
+ if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
+ {
+ Debug(" PKT %u %u\n", recv_packet->OpCode, recv_packet->KeyId);
+ }
+
+ if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
+ {
+ // Control packet
+ if (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2 ||
+ recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
+ {
+ // Connection request packet
+ if (se->Channels[recv_packet->KeyId] != NULL)
+ {
+ // Release when there is a channel data already
+ OvsFreeChannel(se->Channels[recv_packet->KeyId]);
+ se->Channels[recv_packet->KeyId] = NULL;
+ }
+
+ // Create a new channel
+ c = OvsNewChannel(se, recv_packet->KeyId);
+ if (se->ClientSessionId == 0)
+ {
+ se->ClientSessionId = recv_packet->MySessionId;
+ }
+ se->Channels[recv_packet->KeyId] = c;
+ Debug("OpenVPN New Channel :%u\n", recv_packet->KeyId);
+ OvsLog(s, se, c, "LO_NEW_CHANNEL");
+ }
+/* else if (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
+ {
+ // Response to soft reset request packet
+ OPENVPN_PACKET *p;
+
+ p = OvsNewControlPacket(OPENVPN_P_CONTROL_SOFT_RESET_V1, recv_packet->KeyId, se->ServerSessionId,
+ 0, NULL, 0, 0, 0, NULL);
+
+ OvsSendPacketNow(s, se, p);
+
+ OvsFreePacket(p);
+ }*/
+ else
+ {
+ // Packet other than the connection request
+ if (se->Channels[recv_packet->KeyId] != NULL)
+ {
+ c = se->Channels[recv_packet->KeyId];
+ }
+ }
+
+ if (c != NULL)
+ {
+ // Delete the send packet list by looking the packet ID in the ACK list of arrived packet
+ OvsDeleteFromSendingControlPacketList(c, recv_packet->NumAck, recv_packet->AckPacketId);
+
+ if (recv_packet->OpCode != OPENVPN_P_ACK_V1)
+ {
+ // Add the Packet ID of arrived packet to the list
+ InsertIntDistinct(c->AckReplyList, recv_packet->PacketId);
+ Debug("Recv Packet ID (c=%u): %u\n", c->KeyId, recv_packet->PacketId);
+
+ if ((recv_packet->PacketId > c->MaxRecvPacketId)
+ || (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2)
+ || (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1))
+ {
+ c->MaxRecvPacketId = recv_packet->PacketId;
+
+ // Process the received control packet
+ OvsProcessRecvControlPacket(s, se, c, recv_packet);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Data packet
+ if (se->Channels[recv_packet->KeyId] != NULL)
+ {
+ OPENVPN_CHANNEL *c = se->Channels[recv_packet->KeyId];
+ if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
+ {
+ UCHAR *data;
+ UINT size;
+
+ data = recv_packet->Data;
+ size = recv_packet->DataSize;
+
+ if (size >= (c->MdRecv->Size + c->CipherDecrypt->IvSize + sizeof(UINT)))
+ {
+ UCHAR *hmac;
+ UCHAR *iv;
+ UCHAR hmac_test[128];
+
+ // HMAC
+ hmac = data;
+ data += c->MdRecv->Size;
+ size -= c->MdRecv->Size;
+
+ // Confirmation of HMAC
+ MdProcess(c->MdRecv, hmac_test, data, size);
+ if (Cmp(hmac_test, hmac, c->MdRecv->Size) == 0)
+ {
+ // Update of last communication time
+ se->LastCommTick = s->Now;
+
+ // IV
+ iv = data;
+ data += c->CipherDecrypt->IvSize;
+ size -= c->CipherDecrypt->IvSize;
+
+ // Payload
+ if (size >= 1 && (c->CipherDecrypt->BlockSize == 0 || (size % c->CipherDecrypt->BlockSize) == 0))
+ {
+ UINT data_packet_id;
+
+ // Decryption
+ size = CipherProcess(c->CipherDecrypt, iv, s->TmpBuf, data, size);
+
+ data = s->TmpBuf;
+
+ if (size >= sizeof(UINT))
+ {
+ data_packet_id = READ_UINT(data);
+
+ data += sizeof(UINT);
+ size -= sizeof(UINT);
+
+ if (size >= sizeof(ping_signature) &&
+ Cmp(data, ping_signature, sizeof(ping_signature)) == 0)
+ {
+ // Ignore since a ping packet has been received
+ DoNothing();
+ }
+ else
+ {
+ // Receive a packet!!
+ if (se->Ipc != NULL)
+ {
+ switch (se->Mode)
+ {
+ case OPENVPN_MODE_L2: // Send an Ethernet packet to a session
+ IPCSendL2(se->Ipc, data, size);
+ break;
+
+ case OPENVPN_MODE_L3: // Send an IPv4 packet to a session
+ IPCSendIPv4(se->Ipc, data, size);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+// Debug("HMAC Failed (c=%u)\n", c->KeyId);
+ }
+ }
+ }
+ }
+ }
+
+ OvsFreePacket(recv_packet);
+ }
+}
+
+// Remove a packet which the opponent has received from the transmission list
+void OvsDeleteFromSendingControlPacketList(OPENVPN_CHANNEL *c, UINT num_acks, UINT *acks)
+{
+ LIST *o;
+ UINT i;
+ // Validate arguments
+ if (c == NULL || num_acks == 0)
+ {
+ return;
+ }
+
+ o = NewListFast(NULL);
+ for (i = 0;i < num_acks;i++)
+ {
+ UINT ack = acks[i];
+ UINT j;
+
+ for (j = 0;j < LIST_NUM(c->SendControlPacketList);j++)
+ {
+ OPENVPN_CONTROL_PACKET *p = LIST_DATA(c->SendControlPacketList, j);
+
+ if (p->PacketId == ack)
+ {
+ AddDistinct(o, p);
+ }
+ }
+ }
+
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ OPENVPN_CONTROL_PACKET *p = LIST_DATA(o, i);
+
+ Delete(c->SendControlPacketList, p);
+
+ OvsFreeControlPacket(p);
+ }
+
+ ReleaseList(o);
+}
+
+// Process the received control packet
+void OvsProcessRecvControlPacket(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, OPENVPN_PACKET *p)
+{
+ FIFO *recv_fifo = NULL;
+ FIFO *send_fifo = NULL;
+ // Validate arguments
+ if (s == NULL || se == NULL || c == NULL || p == NULL)
+ {
+ return;
+ }
+
+ if (p->OpCode == OPENVPN_P_CONTROL_V1)
+ {
+ Debug("SSL (c=%u): %u\n", c->KeyId, p->DataSize);
+
+ if (c->SslPipe == NULL)
+ {
+ // Create an SSL pipe
+ Lock(s->Cedar->lock);
+ {
+ c->SslPipe = NewSslPipe(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh);
+ }
+ Unlock(s->Cedar->lock);
+
+ Debug("SSL Pipe Created (c=%u).\n", c->KeyId);
+ }
+
+ if (c->SslPipe->IsDisconnected == false)
+ {
+ // Pour the physically received data into SSL pipe
+ if (FifoSize(c->SslPipe->RawIn->SendFifo) < OPENVPN_MAX_SSL_RECV_BUF_SIZE)
+ {
+ Debug("SSL_Write: %u\n", p->DataSize);
+ WriteFifo(c->SslPipe->RawIn->SendFifo, p->Data, p->DataSize);
+ }
+ SyncSslPipe(c->SslPipe);
+ }
+ }
+
+ if (c->SslPipe != NULL && c->SslPipe->IsDisconnected == false)
+ {
+ recv_fifo = c->SslPipe->SslInOut->RecvFifo;
+ send_fifo = c->SslPipe->SslInOut->SendFifo;
+ }
+
+ Debug("SIZE: recv_fifo = %u, send_fifo = %u\n", FifoSize(recv_fifo), FifoSize(send_fifo));
+
+ switch (c->Status)
+ {
+ case OPENVPN_CHANNEL_STATUS_INIT:
+ switch (p->OpCode)
+ {
+ case OPENVPN_P_CONTROL_SOFT_RESET_V1:
+ // Key update (soft reset)
+ if (se->Established)
+ {
+ if (c->IsInitiatorServer == false)
+ {
+ OvsSendControlPacket(c, OPENVPN_P_CONTROL_SOFT_RESET_V1, NULL, 0);
+ }
+
+ c->Status = OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_KEY;
+ c->IsRekeyChannel = true;
+ }
+ break;
+
+ case OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2:
+ // New connection (hard reset)
+ OvsSendControlPacket(c, OPENVPN_P_CONTROL_HARD_RESET_SERVER_V2, NULL, 0);
+
+ c->Status = OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_KEY;
+ break;
+ }
+ break;
+
+ case OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_KEY:
+ if (FifoSize(recv_fifo) >= 1)
+ {
+ OPENVPN_KEY_METHOD_2 data;
+ UCHAR *ptr = FifoPtr(recv_fifo);
+
+ // Parse OPENVPN_KEY_METHOD_2
+ UINT read_size = OvsParseKeyMethod2(&data, ptr, FifoSize(recv_fifo), true);
+ if (read_size != 0)
+ {
+ BUF *b;
+
+ // Success in parsing key information
+ ReadFifo(recv_fifo, NULL, read_size);
+
+ // Set session parameters
+ OvsSetupSessionParameters(s, se, c, &data);
+
+ // Build OPENVPN_KEY_METHOD_2 to respond
+ b = OvsBuildKeyMethod2(&c->ServerKey);
+
+ // Transmission of the response data
+ if (b != NULL)
+ {
+ WriteFifo(send_fifo, b->Buf, b->Size);
+
+ FreeBuf(b);
+ }
+
+ // State transition
+ c->Status = OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_PUSH_REQUEST;
+ if (c->IsRekeyChannel)
+ {
+ c->Status = OPENVPN_CHANNEL_STATUS_ESTABLISHED;
+ c->EstablishedTick = s->Now;
+ Debug("OpenVPN Channel %u Established (re-key).\n", c->KeyId);
+ OvsLog(s, se, c, "LO_CHANNEL_ESTABLISHED_NEWKEY");
+ }
+ }
+ }
+ break;
+
+ case OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_PUSH_REQUEST:
+ if (FifoSize(recv_fifo) >= 1)
+ {
+ char tmp[MAX_SIZE];
+ UINT read_size = OvsPeekStringFromFifo(recv_fifo, tmp, sizeof(tmp));
+
+ if (read_size >= 1)
+ {
+ Debug("Client->Server (c=%u): %s\n", c->KeyId, tmp);
+
+ ReadFifo(recv_fifo, NULL, read_size);
+
+ if (StartWith(tmp, "PUSH_REQUEST"))
+ {
+ // Since connection requested, start VPN connection
+ // When the IPC VPN connection has not been started yet, start it
+ OvsBeginIPCAsyncConnectionIfEmpty(s, se, c);
+
+ // State transition
+ c->Status = OPENVPN_CHANNEL_STATUS_TLS_VPN_CONNECTING;
+ }
+ }
+ }
+ break;
+
+ case OPENVPN_CHANNEL_STATUS_TLS_VPN_CONNECTING:
+ case OPENVPN_CHANNEL_STATUS_ESTABLISHED:
+ if (FifoSize(recv_fifo) >= 1)
+ {
+ char tmp[MAX_SIZE];
+ UINT read_size = OvsPeekStringFromFifo(recv_fifo, tmp, sizeof(tmp));
+
+ if (read_size >= 1)
+ {
+ Debug("Client->Server (c=%u): %s\n", c->KeyId, tmp);
+
+ ReadFifo(recv_fifo, NULL, read_size);
+
+ if (StartWith(tmp, "PUSH_REQUEST"))
+ {
+ WriteFifo(send_fifo, se->PushReplyStr, StrLen(se->PushReplyStr));
+ }
+ }
+ }
+ break;
+ }
+}
+
+// Calculate the proper MSS
+UINT OvsCalcTcpMss(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c)
+{
+ UINT ret = MTU_FOR_PPPOE;
+ // Validate arguments
+ if (s == NULL || se == NULL || c == NULL)
+ {
+ return 0;
+ }
+
+ if (c->MdSend == NULL || c->CipherEncrypt == NULL)
+ {
+ return 0;
+ }
+
+ if (se->Protocol == OPENVPN_PROTOCOL_TCP)
+ {
+ // Calculation is not required for TCP mode
+ return 0;
+ }
+
+ // IPv4 / IPv6
+ if (IsIP4(&se->ClientIp))
+ {
+ ret -= 20;
+ }
+ else
+ {
+ ret -= 40;
+ }
+
+ // UDP
+ ret -= 8;
+
+ // opcode
+ ret -= 1;
+
+ // HMAC
+ ret -= c->MdSend->Size;
+
+ // IV
+ ret -= c->CipherEncrypt->IvSize;
+
+ // Packet ID
+ ret -= 4;
+
+ if (c->CipherEncrypt->IsNullCipher == false)
+ {
+ // block
+ ret -= c->CipherEncrypt->BlockSize;
+ }
+
+ if (se->Mode == OPENVPN_MODE_L2)
+ {
+ // Inner Ethernet Header
+ ret -= 14;
+ }
+
+ // Inner IPv4
+ ret -= 20;
+
+ // Inner TCP
+ ret -= 20;
+
+ return ret;
+}
+
+// When the IPC VPN connection has not been started yet, start it
+void OvsBeginIPCAsyncConnectionIfEmpty(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c)
+{
+ // Validate arguments
+ if (s == NULL || se == NULL || c == NULL)
+ {
+ return;
+ }
+
+ if (IsIPCConnected(se->Ipc) == false)
+ {
+ FreeIPC(se->Ipc);
+
+ se->Ipc = NULL;
+ }
+
+ if (se->IpcAsync == NULL)
+ {
+ IPC_PARAM p;
+ ETHERIP_ID id;
+
+ Zero(&p, sizeof(p));
+ Zero(&id, sizeof(id));
+
+ // Parse the user name
+ PPPParseUsername(s->Cedar, c->ClientKey.Username, &id);
+
+
+ // Build IPC connection parameters
+ StrCpy(p.ClientName, sizeof(p.ClientName), OPENVPN_IPC_CLIENT_NAME);
+ StrCpy(p.Postfix, sizeof(p.Postfix), (se->Mode == OPENVPN_MODE_L3 ? OPENVPN_IPC_POSTFIX_L3 : OPENVPN_IPC_POSTFIX_L2));
+
+ StrCpy(p.UserName, sizeof(p.UserName), id.UserName);
+ StrCpy(p.HubName, sizeof(p.HubName), id.HubName);
+ StrCpy(p.Password, sizeof(p.Password), c->ClientKey.Password);
+
+ Copy(&p.ClientIp, &se->ClientIp, sizeof(IP));
+ p.ClientPort = se->ClientPort;
+
+ Copy(&p.ServerIp, &se->ServerIp, sizeof(IP));
+ p.ServerPort = se->ServerPort;
+
+ if (c->CipherEncrypt->IsNullCipher == false)
+ {
+ StrCpy(p.CryptName, sizeof(p.CryptName), c->CipherEncrypt->Name);
+ }
+
+ if (se->Mode == OPENVPN_MODE_L3)
+ {
+ // L3 Mode
+ p.IsL3Mode = true;
+ }
+ else
+ {
+ // L2 Mode
+ p.BridgeMode = true;
+ }
+
+ p.IsOpenVPN = true;
+
+ // Calculate the MSS
+ p.Mss = OvsCalcTcpMss(s, se, c);
+ Debug("MSS=%u\n", p.Mss);
+
+ // Start an IPC connection
+ se->IpcAsync = NewIPCAsync(s->Cedar, &p, s->SockEvent);
+ }
+}
+
+// Peek a NULL-terminated string from the FIFO
+UINT OvsPeekStringFromFifo(FIFO *f, char *str, UINT str_size)
+{
+ UINT i;
+ bool ok = false;
+ // Validate arguments
+ if (f == NULL || str == NULL || str_size == 0)
+ {
+ return 0;
+ }
+
+ StrCpy(str, str_size, "");
+
+ for (i = 0;i < MIN(str_size, FifoSize(f));i++)
+ {
+ char c = *(((char *)FifoPtr(f)) + i);
+
+ if (c != 0)
+ {
+ str[i] = c;
+ }
+ else
+ {
+ str[i] = 0;
+ i++;
+ ok = true;
+ break;
+ }
+ }
+
+ if (ok == false)
+ {
+ return 0;
+ }
+
+ return i;
+}
+
+// Set session parameters
+void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, OPENVPN_KEY_METHOD_2 *data)
+{
+ LIST *o;
+ BUF *b;
+ // Validate arguments
+ if (s == NULL || se == NULL || c == NULL || data == NULL)
+ {
+ return;
+ }
+
+ Copy(&c->ClientKey, data, sizeof(OPENVPN_KEY_METHOD_2));
+
+ // Parse the parameter string
+ Debug("Parsing Option Str: %s\n", data->OptionString);
+
+ OvsLog(s, se, c, "LO_OPTION_STR_RECV", data->OptionString);
+
+ o = OvsParseOptions(data->OptionString);
+
+ if (se->Mode == OPENVPN_MODE_UNKNOWN)
+ {
+ UINT mtu;
+ // Layer
+ if (StrCmpi(IniStrValue(o, "dev-type"), "tun") == 0)
+ {
+ // L3
+ se->Mode = OPENVPN_MODE_L3;
+ }
+ else
+ {
+ // L2
+ se->Mode = OPENVPN_MODE_L2;
+ }
+
+ // Link MTU
+ mtu = IniIntValue(o, "link-mtu");
+ if (mtu == 0)
+ {
+ mtu = OPENVPN_MTU_LINK;
+ }
+ se->LinkMtu = mtu;
+
+ // Tun MTU
+ mtu = IniIntValue(o, "tun-mtu");
+ if (mtu == 0)
+ {
+ mtu = OPENVPN_MTU_TUN;
+ }
+ se->TunMtu = mtu;
+ }
+
+ // Protocol
+ if (se->Protocol == OPENVPN_PROTOCOL_TCP)
+ {
+ // TCP
+ // UDP
+ if (IsIP6(&se->ClientIp) == false)
+ {
+ StrCpy(c->Proto, sizeof(c->Proto), "TCPv4_SERVER");
+ }
+ else
+ {
+ StrCpy(c->Proto, sizeof(c->Proto), "TCPv6_SERVER");
+ }
+ }
+ else
+ {
+ // UDP
+ if (IsIP6(&se->ClientIp) == false)
+ {
+ StrCpy(c->Proto, sizeof(c->Proto), "UDPv4");
+ }
+ else
+ {
+ StrCpy(c->Proto, sizeof(c->Proto), "UDPv6");
+ }
+ }
+
+ // Encryption algorithm
+ c->CipherEncrypt = OvsGetCipher(IniStrValue(o, "cipher"));
+ c->CipherDecrypt = NewCipher(c->CipherEncrypt->Name);
+
+ // Hash algorithm
+ c->MdSend = OvsGetMd(IniStrValue(o, "auth"));
+ c->MdRecv = NewMd(c->MdSend->Name);
+
+ // Random number generation
+ Rand(c->ServerKey.Random1, sizeof(c->ServerKey.Random1));
+ Rand(c->ServerKey.Random2, sizeof(c->ServerKey.Random2));
+
+ // Generate the Master Secret
+ b = NewBuf();
+ WriteBuf(b, OPENVPN_PREMASTER_LABEL, StrLen(OPENVPN_PREMASTER_LABEL));
+ WriteBuf(b, c->ClientKey.Random1, sizeof(c->ClientKey.Random1));
+ WriteBuf(b, c->ServerKey.Random1, sizeof(c->ServerKey.Random1));
+ Enc_tls1_PRF(b->Buf, b->Size,
+ c->ClientKey.PreMasterSecret, sizeof(c->ClientKey.PreMasterSecret),
+ c->MasterSecret, sizeof(c->MasterSecret));
+ FreeBuf(b);
+
+ // Generate an Expansion Key
+ b = NewBuf();
+ WriteBuf(b, OPENVPN_EXPANSION_LABEL, StrLen(OPENVPN_EXPANSION_LABEL));
+ WriteBuf(b, c->ClientKey.Random2, sizeof(c->ClientKey.Random2));
+ WriteBuf(b, c->ServerKey.Random2, sizeof(c->ServerKey.Random2));
+ WriteBufInt64(b, se->ClientSessionId);
+ WriteBufInt64(b, se->ServerSessionId);
+ Enc_tls1_PRF(b->Buf, b->Size, c->MasterSecret, sizeof(c->MasterSecret),
+ c->ExpansionKey, sizeof(c->ExpansionKey));
+ FreeBuf(b);
+
+ // Set the key
+ SetCipherKey(c->CipherDecrypt, c->ExpansionKey + 0, false);
+ SetCipherKey(c->CipherEncrypt, c->ExpansionKey + 128, true);
+ SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size);
+ SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size);
+
+ OvsFreeOptions(o);
+
+ // Generate the response option string
+ Format(c->ServerKey.OptionString, sizeof(c->ServerKey.OptionString),
+ "V4,dev-type %s,link-mtu %u,tun-mtu %u,proto %s,"
+ "cipher %s,auth %s,keysize %u,key-method 2,tls-server",
+ (se->Mode == OPENVPN_MODE_L2 ? "tap" : "tun"),
+ se->LinkMtu,
+ se->TunMtu,
+ c->Proto,
+ c->CipherEncrypt->Name, c->MdSend->Name, c->CipherEncrypt->KeySize * 8);
+ Debug("Building OptionStr: %s\n", c->ServerKey.OptionString);
+
+ OvsLog(s, se, c, "LO_OPTION_STR_SEND", c->ServerKey.OptionString);
+}
+
+// Get the encryption algorithm
+CIPHER *OvsGetCipher(char *name)
+{
+ CIPHER *c = NULL;
+
+ if (IsEmptyStr(name) == false && IsStrInStrTokenList(OPENVPN_CIPHER_LIST, name, NULL, false))
+ {
+ c = NewCipher(name);
+ }
+
+ if (c == NULL)
+ {
+ c = NewCipher(OPENVPN_DEFAULT_CIPHER);
+ }
+
+ return c;
+}
+
+// Get the hash algorithm
+MD *OvsGetMd(char *name)
+{
+ MD *m = NULL;
+
+ if (IsEmptyStr(name) == false && IsStrInStrTokenList(OPENVPN_MD_LIST, name, NULL, false))
+ {
+ m = NewMd(name);
+ }
+
+ if (m == NULL)
+ {
+ m = NewMd(OPENVPN_DEFAULT_MD);
+ }
+
+ return m;
+}
+
+// Parse the option string
+LIST *OvsParseOptions(char *str)
+{
+ LIST *o = NewListFast(NULL);
+ TOKEN_LIST *t;
+
+ t = ParseTokenWithoutNullStr(str, ",");
+ if (t != NULL)
+ {
+ UINT i;
+
+ for (i = 0;i < t->NumTokens;i++)
+ {
+ char key[MAX_SIZE];
+ char value[MAX_SIZE];
+ char *line = t->Token[i];
+ Trim(line);
+
+ if (GetKeyAndValue(line, key, sizeof(key), value, sizeof(value), " \t"))
+ {
+ INI_ENTRY *e = ZeroMalloc(sizeof(INI_ENTRY));
+
+ e->Key = CopyStr(key);
+ e->Value = CopyStr(value);
+
+ Add(o, e);
+ }
+ }
+
+ FreeToken(t);
+ }
+
+ return o;
+}
+
+// Release the option list
+void OvsFreeOptions(LIST *o)
+{
+ // Validate arguments
+ if (o == NULL)
+ {
+ return;
+ }
+
+ FreeIni(o);
+}
+
+// Create an Option List
+LIST *OvsNewOptions()
+{
+ return NewListFast(NULL);
+}
+
+// Add a value to the option list
+void OvsAddOption(LIST *o, char *key, char *value)
+{
+ INI_ENTRY *e;
+ // Validate arguments
+ if (o == NULL)
+ {
+ return;
+ }
+
+ e = GetIniEntry(o, key);
+ if (e != NULL)
+ {
+ // Overwrite existing keys
+ Free(e->Key);
+ e->Key = CopyStr(key);
+
+ Free(e->Value);
+ e->Value = CopyStr(value);
+ }
+ else
+ {
+ // Create a new key
+ e = ZeroMalloc(sizeof(INI_ENTRY));
+
+ e->Key = CopyStr(key);
+ e->Value = CopyStr(value);
+
+ Add(o, e);
+ }
+}
+
+// Confirm whether there is specified option key string
+bool OvsHasOption(LIST *o, char *key)
+{
+ // Validate arguments
+ if (o == NULL || key == NULL)
+ {
+ return false;
+ }
+
+ if (GetIniEntry(o, key) != NULL)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// Build the data from KEY_METHOD2
+BUF *OvsBuildKeyMethod2(OPENVPN_KEY_METHOD_2 *d)
+{
+ BUF *b;
+ UCHAR uc;
+ // Validate arguments
+ if (d == NULL)
+ {
+ return NULL;
+ }
+
+ b = NewBuf();
+
+ // Reserved
+ WriteBufInt(b, 0);
+
+ // Method
+ uc = 2;
+ WriteBuf(b, &uc, sizeof(UCHAR));
+
+ // Random1
+ WriteBuf(b, d->Random1, sizeof(d->Random1));
+
+ // Random2
+ WriteBuf(b, d->Random2, sizeof(d->Random2));
+
+ // Option String
+ OvsWriteStringToBuf(b, d->OptionString, sizeof(d->OptionString));
+
+ // Username
+ OvsWriteStringToBuf(b, d->Username, sizeof(d->Username));
+
+ // Password
+ OvsWriteStringToBuf(b, d->Password, sizeof(d->Password));
+
+ // PeerInfo
+ OvsWriteStringToBuf(b, d->PeerInfo, sizeof(d->PeerInfo));
+
+ return b;
+}
+
+// Append a string to buf
+void OvsWriteStringToBuf(BUF *b, char *str, UINT max_size)
+{
+ USHORT us;
+ UINT i;
+ char *tmp;
+ // Validate arguments
+ if (b == NULL)
+ {
+ return;
+ }
+ if (str == NULL)
+ {
+ str = "";
+ }
+
+ if (StrLen(str) == 0)
+ {
+ us = 0;
+ WriteBuf(b, &us, sizeof(USHORT));
+ return;
+ }
+
+ i = StrSize(str);
+ i = MIN(i, max_size);
+ us = Endian16((USHORT)i);
+ WriteBuf(b, &us, sizeof(USHORT));
+
+ tmp = Malloc(i);
+ Copy(tmp, str, i);
+ tmp[i - 1] = 0;
+ WriteBuf(b, tmp, i);
+
+ Free(tmp);
+}
+
+// Parse the KEY_METHOD2
+UINT OvsParseKeyMethod2(OPENVPN_KEY_METHOD_2 *ret, UCHAR *data, UINT size, bool client_mode)
+{
+ BUF *b;
+ UINT read_size = 0;
+ UINT ui;
+ UCHAR uc;
+ // Validate arguments
+ Zero(ret, sizeof(OPENVPN_KEY_METHOD_2));
+ if (ret == NULL || data == NULL || size == 0)
+ {
+ return 0;
+ }
+
+ b = NewBuf();
+ WriteBuf(b, data, size);
+ SeekBuf(b, 0, 0);
+
+ // Reserved
+ if (ReadBuf(b, &ui, sizeof(UINT)) == sizeof(UINT))
+ {
+ // Method
+ if (ReadBuf(b, &uc, sizeof(UCHAR)) == sizeof(UCHAR) && uc == 2)
+ {
+ // Pre Master Secret
+ if (client_mode == false || ReadBuf(b, ret->PreMasterSecret, sizeof(ret->PreMasterSecret)) == sizeof(ret->PreMasterSecret))
+ {
+ // Random1
+ if (ReadBuf(b, ret->Random1, sizeof(ret->Random1)) == sizeof(ret->Random1))
+ {
+ // Random2
+ if (ReadBuf(b, ret->Random2, sizeof(ret->Random2)) == sizeof(ret->Random2))
+ {
+ // String
+ if (OvsReadStringFromBuf(b, ret->OptionString, sizeof(ret->OptionString)) &&
+ OvsReadStringFromBuf(b, ret->Username, sizeof(ret->Username)) &&
+ OvsReadStringFromBuf(b, ret->Password, sizeof(ret->Password)) &&
+ OvsReadStringFromBuf(b, ret->PeerInfo, sizeof(ret->PeerInfo)))
+ {
+ read_size = b->Current;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ FreeBuf(b);
+
+ return read_size;
+}
+
+// Read a string from BUF
+bool OvsReadStringFromBuf(BUF *b, char *str, UINT str_size)
+{
+ USHORT us;
+ // Validate arguments
+ if (b == NULL || str == NULL)
+ {
+ return false;
+ }
+
+ if (ReadBuf(b, &us, sizeof(USHORT)) != sizeof(USHORT))
+ {
+ return false;
+ }
+
+ us = Endian16(us);
+
+ if (us == 0)
+ {
+ StrCpy(str, str_size, "");
+ return true;
+ }
+
+ if (us > str_size)
+ {
+ return false;
+ }
+
+ if (ReadBuf(b, str, us) != us)
+ {
+ return false;
+ }
+
+ if (str[us - 1] != 0)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Transmission of control packet (Automatic segmentation with the maximum size)
+void OvsSendControlPacketWithAutoSplit(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT data_size)
+{
+ BUF *b;
+ // Validate arguments
+ if (c == NULL || (data_size != 0 && data == NULL))
+ {
+ return;
+ }
+
+ b = NewBuf();
+ WriteBuf(b, data, data_size);
+ SeekBuf(b, 0, 0);
+
+ while (true)
+ {
+ UCHAR tmp[OPENVPN_CONTROL_PACKET_MAX_DATASIZE];
+ UINT size = ReadBuf(b, tmp, sizeof(tmp));
+
+ if (size == 0)
+ {
+ break;
+ }
+
+ OvsSendControlPacket(c, opcode, tmp, size);
+ //Debug(" *** CNT SEND %u\n", size);
+ }
+
+ FreeBuf(b);
+}
+
+// Send the control packet
+void OvsSendControlPacket(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT data_size)
+{
+ OPENVPN_CONTROL_PACKET *p;
+ // Validate arguments
+ if (c == NULL || (data_size != 0 && data == NULL))
+ {
+ return;
+ }
+
+ p = ZeroMalloc(sizeof(OPENVPN_CONTROL_PACKET));
+
+ p->OpCode = opcode;
+ p->PacketId = c->NextSendPacketId++;
+
+ if (data != NULL)
+ {
+ p->Data = Clone(data, data_size);
+ p->DataSize = data_size;
+ }
+
+ p->NextSendTime = 0;
+
+ Add(c->SendControlPacketList, p);
+}
+
+// Release the control packet being transmitted
+void OvsFreeControlPacket(OPENVPN_CONTROL_PACKET *p)
+{
+ // Validate arguments
+ if (p == NULL)
+ {
+ return;
+ }
+
+ if (p->Data != NULL)
+ {
+ Free(p->Data);
+ }
+
+ Free(p);
+}
+
+// Get a list of packet ID to be responded
+UINT OvsGetAckReplyList(OPENVPN_CHANNEL *c, UINT *ret)
+{
+ UINT i;
+ LIST *o = NULL;
+ UINT num;
+ // Validate arguments
+ if (c == NULL || ret == NULL)
+ {
+ return 0;
+ }
+
+ num = MIN(LIST_NUM(c->AckReplyList), OPENVPN_MAX_NUMACK);
+
+ for (i = 0;i < num;i++)
+ {
+ UINT *v = LIST_DATA(c->AckReplyList, i);
+
+ if (o == NULL)
+ {
+ o = NewListFast(NULL);
+ }
+
+ Add(o, v);
+
+ ret[i] = *v;
+ }
+
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ UINT *v = LIST_DATA(o, i);
+
+ Delete(c->AckReplyList, v);
+
+ Free(v);
+ }
+
+ ReleaseList(o);
+
+ return num;
+}
+
+// Release the channel
+void OvsFreeChannel(OPENVPN_CHANNEL *c)
+{
+ UINT i;
+ // Validate arguments
+ if (c == NULL)
+ {
+ return;
+ }
+
+ if (c->SslPipe != NULL)
+ {
+ FreeSslPipe(c->SslPipe);
+ }
+
+ ReleaseIntList(c->AckReplyList);
+
+ for (i = 0;i < LIST_NUM(c->SendControlPacketList);i++)
+ {
+ OPENVPN_CONTROL_PACKET *p = LIST_DATA(c->SendControlPacketList, i);
+
+ OvsFreeControlPacket(p);
+ }
+
+ ReleaseList(c->SendControlPacketList);
+
+ FreeCipher(c->CipherDecrypt);
+ FreeCipher(c->CipherEncrypt);
+
+ FreeMd(c->MdRecv);
+ FreeMd(c->MdSend);
+
+ Free(c);
+}
+
+// Create a new channel
+OPENVPN_CHANNEL *OvsNewChannel(OPENVPN_SESSION *se, UCHAR key_id)
+{
+ OPENVPN_CHANNEL *c;
+ // Validate arguments
+ if (se == NULL)
+ {
+ return NULL;
+ }
+
+ c = ZeroMalloc(sizeof(OPENVPN_CHANNEL));
+
+ c->Session = se;
+ c->Server = se->Server;
+
+ c->Status = OPENVPN_CHANNEL_STATUS_INIT;
+
+ c->AckReplyList = NewIntList(true);
+
+ c->SendControlPacketList = NewListFast(NULL);
+
+ c->KeyId = key_id;
+
+ Rand(c->NextIv, sizeof(c->NextIv));
+
+ //c->NextRekey = se->Server->Now + (UINT64)5000;
+
+ se->LastCreatedChannelIndex = key_id;
+
+ return c;
+}
+
+// Create a new server-side channel ID
+UINT64 OvsNewServerSessionId(OPENVPN_SERVER *s)
+{
+ // Validate arguments
+ if (s == NULL)
+ {
+ return 0;
+ }
+
+ while (true)
+ {
+ UINT64 id = Rand64();
+ UINT i;
+ bool exists = false;
+
+ if (id == 0 || id == (UINT64)(0xFFFFFFFFFFFFFFFFULL))
+ {
+ continue;
+ }
+
+ for (i = 0;i < LIST_NUM(s->SessionList);i++)
+ {
+ OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
+ if (se->ServerSessionId == id)
+ {
+ exists = true;
+ }
+ }
+
+ if (exists == false)
+ {
+ return id;
+ }
+ }
+}
+
+// Build and submit the OpenVPN data packet
+void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, void *data, UINT data_size)
+{
+ UCHAR uc;
+ UCHAR *encrypted_data;
+ UINT encrypted_size;
+ UCHAR *dest_data;
+ UINT dest_size;
+ UINT r;
+
+ // Validate arguments
+ if (c == NULL || data == NULL || data_size == 0)
+ {
+ return;
+ }
+
+ uc = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07);
+
+ // Generate the data to be encrypted
+
+ encrypted_size = sizeof(UINT) + data_size;
+ encrypted_data = ZeroMalloc(encrypted_size);
+
+ WRITE_UINT(encrypted_data, data_packet_id);
+ Copy(encrypted_data + sizeof(UINT), data, data_size);
+
+ // Prepare a buffer to store the results
+ dest_data = Malloc(sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256);
+
+ // Encrypt
+ r = CipherProcess(c->CipherEncrypt, c->NextIv, dest_data + sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize,
+ encrypted_data, encrypted_size);
+ dest_size = sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + r;
+
+ // Copy the IV
+ Copy(dest_data + sizeof(UCHAR) + c->MdSend->Size, c->NextIv, c->CipherEncrypt->IvSize);
+
+ // Calculate the HMAC
+ MdProcess(c->MdSend, dest_data + sizeof(UCHAR), dest_data + sizeof(UCHAR) + c->MdSend->Size,
+ dest_size - sizeof(UCHAR) - c->MdSend->Size);
+
+ // Update the NextIV
+ Copy(c->NextIv, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize);
+
+ // Op-code
+ dest_data[0] = uc;
+
+ OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size);
+
+ Free(encrypted_data);
+}
+
+// Build an OpenVPN control packet
+BUF *OvsBuildPacket(OPENVPN_PACKET *p)
+{
+ BUF *b;
+ UCHAR uc;
+ UINT num_ack;
+ // Validate arguments
+ if (p == NULL)
+ {
+ return NULL;
+ }
+
+ b = NewBuf();
+
+ // OpCode + KeyID
+ uc = ((p->OpCode << 3) & 0xF8) | (p->KeyId & 0x07);
+ WriteBufChar(b, uc);
+
+ if (p->OpCode == OPENVPN_P_DATA_V1)
+ {
+ // Data Packet
+ WriteBuf(b, p->Data, p->DataSize);
+ SeekBuf(b, 0, 0);
+ return b;
+ }
+
+ // Sender Channel ID
+ WriteBufInt64(b, p->MySessionId);
+
+ // NumAck
+ num_ack = MIN(p->NumAck, OPENVPN_MAX_NUMACK);
+ WriteBufChar(b, (UCHAR)num_ack);
+
+ if (p->NumAck >= 1)
+ {
+ UINT i;
+
+ for (i = 0;i < num_ack;i++)
+ {
+ WriteBufInt(b, (UCHAR)p->AckPacketId[i]);
+ }
+
+ // Received Channel ID
+ WriteBufInt64(b, p->YourSessionId);
+ }
+
+ if (p->OpCode != OPENVPN_P_ACK_V1)
+ {
+ // Packet ID
+ WriteBufInt(b, p->PacketId);
+
+ // Payload
+ if (p->DataSize >= 1 && p->Data != NULL)
+ {
+ WriteBuf(b, p->Data, p->DataSize);
+ }
+ }
+
+ SeekBuf(b, 0, 0);
+
+ return b;
+}
+
+// Parse the OpenVPN packet
+OPENVPN_PACKET *OvsParsePacket(UCHAR *data, UINT size)
+{
+ UCHAR uc;
+ OPENVPN_PACKET *ret = NULL;
+ // Validate arguments
+ if (data == NULL || size == 0)
+ {
+ return NULL;
+ }
+
+ ret = ZeroMalloc(sizeof(OPENVPN_PACKET));
+
+ // OpCode + KeyID
+ if (size < 1)
+ {
+ goto LABEL_ERROR;
+ }
+ uc = *((UCHAR *)data);
+ data++;
+ size--;
+
+ ret->OpCode = ((uc & 0xF8) >> 3) & 0x1F;
+ ret->KeyId = uc & 0x07;
+
+ if (ret->OpCode == OPENVPN_P_DATA_V1)
+ {
+ // Data packet
+ ret->DataSize = size;
+ ret->Data = Clone(data, size);
+ return ret;
+ }
+
+ // Sender Channel ID
+ if (size < sizeof(UINT64))
+ {
+ goto LABEL_ERROR;
+ }
+ ret->MySessionId = READ_UINT64(data);
+ data += sizeof(UINT64);
+ size -= sizeof(UINT64);
+
+ // ACK
+ if (size < 1)
+ {
+ goto LABEL_ERROR;
+ }
+ uc = *((UCHAR *)data);
+ data++;
+ size--;
+
+ ret->NumAck = uc;
+
+ if (ret->NumAck > 4)
+ {
+ goto LABEL_ERROR;
+ }
+
+ if (ret->NumAck >= 1)
+ {
+ UINT i;
+
+ if (size < (sizeof(UINT) * (UINT)ret->NumAck + sizeof(UINT64)))
+ {
+ goto LABEL_ERROR;
+ }
+
+ for (i = 0;i < ret->NumAck;i++)
+ {
+ UINT ui;
+
+ ui = READ_UINT(data);
+
+ ret->AckPacketId[i] = ui;
+
+ data += sizeof(UINT);
+ size -= sizeof(UINT);
+ }
+
+ ret->YourSessionId = READ_UINT64(data);
+ data += sizeof(UINT64);
+ size -= sizeof(UINT64);
+ }
+
+ if (ret->OpCode != OPENVPN_P_ACK_V1)
+ {
+ // Read the Packet ID Because in the case of other than ACK
+ if (size < sizeof(UINT))
+ {
+ goto LABEL_ERROR;
+ }
+
+ ret->PacketId = READ_UINT(data);
+ data += sizeof(UINT);
+ size -= sizeof(UINT);
+
+ // Payload
+ ret->DataSize = size;
+ if (size >= 1)
+ {
+ ret->Data = Clone(data, size);
+ }
+ }
+
+ return ret;
+
+LABEL_ERROR:
+ Debug("OvsParsePacket Error.\n");
+ if (ret != NULL)
+ {
+ OvsFreePacket(ret);
+ }
+ return NULL;
+}
+
+// Release the OpenVPN packet
+void OvsFreePacket(OPENVPN_PACKET *p)
+{
+ // Validate arguments
+ if (p == NULL)
+ {
+ return;
+ }
+
+ if (p->Data != NULL)
+ {
+ Free(p->Data);
+ }
+
+ Free(p);
+}
+
+// If the session does not exist, create a session
+OPENVPN_SESSION *OvsFindOrCreateSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol)
+{
+ OPENVPN_SESSION *se;
+ // Validate arguments
+ if (s == NULL || server_ip == NULL || server_port == 0 || client_ip == NULL || client_port == 0)
+ {
+ return NULL;
+ }
+
+ se = OvsSearchSession(s, server_ip, server_port, client_ip, client_port, protocol);
+ if (se == NULL)
+ {
+ se = OvsNewSession(s, server_ip, server_port, client_ip, client_port, protocol);
+
+ if (se != NULL)
+ {
+ Insert(s->SessionList, se);
+ }
+ }
+
+ return se;
+}
+
+// Get the number of sessions currently connected from the IP address of the client
+UINT OvsGetNumSessionByClientIp(OPENVPN_SERVER *s, IP *ip)
+{
+ UINT i;
+ UINT ret = 0;
+ // Validate arguments
+ if (s == NULL || ip == NULL)
+ {
+ return 0;
+ }
+
+ for (i = 0;i < LIST_NUM(s->SessionList);i++)
+ {
+ OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
+
+ if (CmpIpAddr(&se->ClientIp, ip) == 0)
+ {
+ ret++;
+ }
+ }
+
+ return ret;
+}
+
+// Create a new session
+OPENVPN_SESSION *OvsNewSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol)
+{
+ OPENVPN_SESSION *se;
+ char server_ip_str[MAX_SIZE];
+ char client_ip_str[MAX_SIZE];
+ // Validate arguments
+ if (s == NULL || server_ip == NULL || server_port == 0 || client_ip == NULL || client_port == 0)
+ {
+ return NULL;
+ }
+
+
+ if (OvsGetNumSessionByClientIp(s, client_ip) > OPENVPN_QUOTA_MAX_NUM_SESSIONS_PER_IP)
+ {
+ // Number of sessions from the same IP address too many
+ return NULL;
+ }
+
+ if (LIST_NUM(s->SessionList) > OPENVPN_QUOTA_MAX_NUM_SESSIONS)
+ {
+ // Too many OpenVPN sessions
+ return NULL;
+ }
+
+ se = ZeroMalloc(sizeof(OPENVPN_SESSION));
+
+ se->Server = s;
+
+ Copy(&se->ClientIp, client_ip, sizeof(IP));
+ se->ClientPort = client_port;
+
+ Copy(&se->ServerIp, server_ip, sizeof(IP));
+ se->ServerPort = server_port;
+
+ se->LastCommTick = s->Now;
+
+ se->Protocol = protocol;
+
+ se->ServerSessionId = OvsNewServerSessionId(se->Server);
+
+ se->CreatedTick = s->Now;
+
+ se->Id = s->NextSessionId;
+ s->NextSessionId++;
+
+ IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);
+ IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
+ Debug("OpenVPN New Session: %s:%u -> %s:%u Proto=%u\n", server_ip_str, server_port,
+ client_ip_str, client_port, protocol);
+
+ OvsLog(s, se, NULL, "LO_NEW_SESSION", (protocol == OPENVPN_PROTOCOL_UDP ? "UDP" : "TCP"));
+
+ return se;
+}
+
+// Release the session
+void OvsFreeSession(OPENVPN_SESSION *se)
+{
+ UINT i;
+ // Validate arguments
+ if (se == NULL)
+ {
+ return;
+ }
+
+ // If there is IP addresses which is got from a DHCP server in the session, release it
+ if (se->Ipc != NULL)
+ {
+ if (se->Mode == OPENVPN_MODE_L3)
+ {
+ if (se->IpcAsync != NULL)
+ {
+ IP dhcp_ip;
+
+ UINTToIP(&dhcp_ip, se->IpcAsync->L3ClientAddressOption.ServerAddress);
+
+ IPCDhcpFreeIP(se->Ipc, &dhcp_ip);
+ IPCProcessL3Events(se->Ipc);
+ }
+ }
+ }
+
+ // Release the channel
+ for (i = 0;i < OPENVPN_NUM_CHANNELS;i++)
+ {
+ OPENVPN_CHANNEL *c = se->Channels[i];
+
+ if (c != NULL)
+ {
+ OvsFreeChannel(c);
+ }
+ }
+
+ // Release the IPC
+ if (se->Ipc != NULL)
+ {
+ FreeIPC(se->Ipc);
+ }
+
+ if (se->IpcAsync != NULL)
+ {
+ FreeIPCAsync(se->IpcAsync);
+ }
+
+ Free(se);
+}
+
+// Search the session from the endpoint information
+OPENVPN_SESSION *OvsSearchSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol)
+{
+ OPENVPN_SESSION *se;
+ OPENVPN_SESSION t;
+ // Validate arguments
+ if (s == NULL || server_ip == NULL || server_port == 0 || client_ip == NULL || client_port == 0)
+ {
+ return NULL;
+ }
+
+ Copy(&t.ClientIp, client_ip, sizeof(IP));
+ t.ClientPort = client_port;
+ Copy(&t.ServerIp, server_ip, sizeof(IP));
+ t.ServerPort = server_port;
+ t.Protocol = protocol;
+
+ se = Search(s->SessionList, &t);
+
+ return se;
+}
+
+// Receive packets in the OpenVPN server
+void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol)
+{
+ UINT i, j;
+ LIST *delete_session_list = NULL;
+ // Validate arguments
+ if (s == NULL || recv_packet_list == NULL)
+ {
+ return;
+ }
+
+ s->Now = Tick64();
+
+ // Process for all sessions
+ for (i = 0;i < LIST_NUM(s->SessionList);i++)
+ {
+ OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
+
+ if (se->Ipc != NULL)
+ {
+ if (se->Mode == OPENVPN_MODE_L3)
+ {
+ // Flush the ARP table of the IPC
+ IPCFlushArpTableEx(se->Ipc, s->Now);
+ }
+ }
+ }
+
+ // Process received packets
+ for (i = 0;i < LIST_NUM(recv_packet_list);i++)
+ {
+ UDPPACKET *p = LIST_DATA(recv_packet_list, i);
+
+ OvsProceccRecvPacket(s, p, protocol);
+ }
+
+ // Treat for all sessions and all channels
+ for (i = 0;i < LIST_NUM(s->SessionList);i++)
+ {
+ OPENVPN_CHANNEL *latest_channel = NULL;
+ UINT64 max_tick = 0;
+ OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
+ bool is_disconnected = false;
+
+ if (se->Ipc != NULL)
+ {
+ if (se->Mode == OPENVPN_MODE_L3)
+ {
+ IPCProcessL3Events(se->Ipc);
+ }
+ }
+
+ for (j = 0;j < OPENVPN_NUM_CHANNELS;j++)
+ {
+ OPENVPN_CHANNEL *c = se->Channels[j];
+
+ if (c != NULL)
+ {
+ if (c->RekeyInitiated == false && ((c->NextRekey <= s->Now && c->NextRekey != 0) || (c->LastDataPacketId >= OPENVPN_MAX_PACKET_ID_FOR_TRIGGER_REKEY)))
+ {
+ OPENVPN_CHANNEL *c2;
+ // Send a soft reset by creating a new channel
+ UINT next_channel_id = se->LastCreatedChannelIndex + 1;
+ if (next_channel_id >= OPENVPN_NUM_CHANNELS)
+ {
+ next_channel_id = 1;
+ }
+ if (se->Channels[next_channel_id] != NULL)
+ {
+ // Release when there is a channel data already
+ OvsFreeChannel(se->Channels[next_channel_id]);
+ se->Channels[next_channel_id] = NULL;
+ }
+
+ // Create a new channel
+ c2 = OvsNewChannel(se, (UCHAR)next_channel_id);
+ c2->IsInitiatorServer = true;
+ se->Channels[next_channel_id] = c2;
+ Debug("OpenVPN New Channel for Re-Keying :%u\n", next_channel_id);
+ OvsLog(s, se, c, "LO_INITIATE_REKEY");
+
+ // Send a soft reset
+ OvsSendControlPacket(c2, OPENVPN_P_CONTROL_SOFT_RESET_V1, NULL, 0);
+
+ c->RekeyInitiated = true;
+ }
+ }
+
+ if (c != NULL)
+ {
+ switch (c->Status)
+ {
+ case OPENVPN_CHANNEL_STATUS_TLS_VPN_CONNECTING:
+ // Check whether the connection process completed if there is a channel running a VPN connection process
+ if (se->IpcAsync != NULL)
+ {
+ if (se->IpcAsync->Done)
+ {
+ if (se->IpcAsync->Ipc != NULL)
+ {
+ char option_str[MAX_SIZE];
+ char l3_options[MAX_SIZE];
+
+ // Successful in VPN connection
+ Debug("OpenVPN Channel %u Established (new key).\n", j);
+ OvsLog(s, se, c, "LO_CHANNEL_ESTABLISHED");
+
+ // Return the PUSH_REPLY
+ Format(option_str, sizeof(option_str),
+ "PUSH_REPLY,ping %u,ping-restart %u",
+ (OPENVPN_PING_SEND_INTERVAL / 1000),
+ (OPENVPN_RECV_TIMEOUT / 1000));
+
+ if (se->Mode == OPENVPN_MODE_L3)
+ {
+ // Add such as the IP address that was acquired from the DHCP server
+ // if the L3 mode to the option character string
+ DHCP_OPTION_LIST *cao = &se->IpcAsync->L3ClientAddressOption;
+ char ip_client[64];
+ char ip_tunnel_endpoint[64];
+ UINT ip_tunnel_endpoint_32;
+ char ip_network[64];
+ char ip_subnet_mask[64];
+ char ip_dns1[64];
+ char ip_dns2[64];
+ char ip_wins1[64];
+ char ip_wins2[64];
+ char ip_defgw[64];
+
+ ClearStr(ip_dns1, sizeof(ip_dns1));
+ ClearStr(ip_dns2, sizeof(ip_dns2));
+ ClearStr(ip_wins1, sizeof(ip_wins1));
+ ClearStr(ip_wins2, sizeof(ip_wins2));
+ ClearStr(ip_defgw, sizeof(ip_defgw));
+
+ IPToStr32(ip_client, sizeof(ip_client),
+ cao->ClientAddress);
+
+ // Generate a virtual gateway address to be passed to the OpenVPN
+ ip_tunnel_endpoint_32 = Endian32(cao->ClientAddress);
+ ip_tunnel_endpoint_32++;
+ ip_tunnel_endpoint_32 = Endian32(ip_tunnel_endpoint_32);
+ IPToStr32(ip_tunnel_endpoint, sizeof(ip_tunnel_endpoint), ip_tunnel_endpoint_32);
+
+ // Create a subnet information for the LAN
+ IPToStr32(ip_network, sizeof(ip_network),
+ GetNetworkAddress(cao->ClientAddress,
+ cao->SubnetMask));
+
+ IPToStr32(ip_subnet_mask, sizeof(ip_subnet_mask),
+ cao->SubnetMask);
+
+ Format(l3_options, sizeof(l3_options),
+ ",ifconfig %s %s",
+// ",ifconfig %s %s,route %s %s %s 1",
+ ip_client, ip_tunnel_endpoint, ip_network, ip_subnet_mask,
+ ip_tunnel_endpoint);
+ StrCat(option_str, sizeof(option_str), l3_options);
+
+ // Domain name
+ if (IsEmptyStr(cao->DomainName) == false)
+ {
+ Format(l3_options, sizeof(l3_options),
+ ",dhcp-option DOMAIN %s", cao->DomainName);
+ StrCat(option_str, sizeof(option_str), l3_options);
+ }
+
+ // DNS server address 1
+ if (cao->DnsServer != 0)
+ {
+ char ip_str[64];
+ IPToStr32(ip_str, sizeof(ip_str), cao->DnsServer);
+ Format(l3_options, sizeof(l3_options),
+ ",dhcp-option DNS %s", ip_str);
+ StrCat(option_str, sizeof(option_str), l3_options);
+
+ StrCpy(ip_dns1, sizeof(ip_dns1), ip_str);
+ }
+
+ // DNS server address 2
+ if (cao->DnsServer2 != 0)
+ {
+ char ip_str[64];
+ IPToStr32(ip_str, sizeof(ip_str), cao->DnsServer2);
+ Format(l3_options, sizeof(l3_options),
+ ",dhcp-option DNS %s", ip_str);
+ StrCat(option_str, sizeof(option_str), l3_options);
+
+ StrCpy(ip_dns2, sizeof(ip_dns2), ip_str);
+ }
+
+ // WINS address 1
+ if (cao->WinsServer != 0)
+ {
+ char ip_str[64];
+ IPToStr32(ip_str, sizeof(ip_str), cao->WinsServer);
+ Format(l3_options, sizeof(l3_options),
+ ",dhcp-option WINS %s", ip_str);
+ StrCat(option_str, sizeof(option_str), l3_options);
+
+ StrCpy(ip_wins1, sizeof(ip_wins1), ip_str);
+ }
+
+ // WINS address 2
+ if (cao->WinsServer2 != 0)
+ {
+ char ip_str[64];
+ IPToStr32(ip_str, sizeof(ip_str), cao->WinsServer2);
+ Format(l3_options, sizeof(l3_options),
+ ",dhcp-option WINS %s", ip_str);
+ StrCat(option_str, sizeof(option_str), l3_options);
+
+ StrCpy(ip_wins2, sizeof(ip_wins2), ip_str);
+ }
+
+ // Default gateway
+ if (cao->Gateway != 0)
+ {
+ Format(l3_options, sizeof(l3_options),
+ ",route-gateway %s,redirect-gateway def1", ip_tunnel_endpoint);
+ StrCat(option_str, sizeof(option_str), l3_options);
+
+ IPToStr32(ip_defgw, sizeof(ip_defgw), cao->Gateway);
+ }
+
+ OvsLog(s, se, c, "LP_SET_IPV4_PARAM",
+ ip_client, ip_subnet_mask, ip_defgw, ip_dns1, ip_dns2, ip_wins1, ip_wins2);
+ }
+
+ WriteFifo(c->SslPipe->SslInOut->SendFifo, option_str, StrSize(option_str));
+
+ Debug("Push Str: %s\n", option_str);
+ OvsLog(s, se, c, "LO_PUSH_REPLY", option_str);
+
+ StrCpy(se->PushReplyStr, sizeof(se->PushReplyStr), option_str);
+
+ se->Ipc = se->IpcAsync->Ipc;
+ se->IpcAsync->Ipc = NULL;
+
+ s->SessionEstablishedCount++;
+
+ // Set a Sock Event of IPC to Sock Event of the UDP Listener
+ IPCSetSockEventWhenRecvL2Packet(se->Ipc, s->SockEvent);
+
+ // State transition
+ c->Status = OPENVPN_CHANNEL_STATUS_ESTABLISHED;
+ c->EstablishedTick = s->Now;
+ se->Established = true;
+ se->LastCommTick = Tick64();
+ }
+ else
+ {
+ char *str;
+
+ if (se->IpcAsync->DhcpAllocFailed)
+ {
+ OvsLog(s, se, c, "LP_DHCP_REQUEST_NG");
+ }
+
+ // Failed to connect VPN
+ Debug("OpenVPN Channel %u Failed.\n", j);
+ OvsLog(s, se, c, "LO_CHANNEL_FAILED");
+
+ // Return the AUTH_FAILED
+ str = "AUTH_FAILED";
+ WriteFifo(c->SslPipe->SslInOut->SendFifo, str, StrSize(str));
+
+ s->SessionEstablishedCount++;
+
+ // State transition
+ c->Status = OPENVPN_CHANNEL_STATUS_DISCONNECTED;
+
+ FreeIPCAsync(se->IpcAsync);
+ se->IpcAsync = NULL;
+ }
+ }
+ }
+ break;
+
+ case OPENVPN_CHANNEL_STATUS_ESTABLISHED:
+ // Monitor the IPC whether not disconnected when there is a VPN connection completed channel
+ if (IsIPCConnected(se->Ipc) == false)
+ {
+ // Send the RESTART since IPC is disconnected
+ char *str = "RESTART";
+ Debug("OpenVPN Channel %u Disconnected by HUB.\n", j);
+
+ OvsLog(s, se, c, "LO_CHANNEL_DISCONNECTED_BY_HUB");
+
+ WriteFifo(c->SslPipe->SslInOut->SendFifo, str, StrSize(str));
+
+ // State transition
+ c->Status = OPENVPN_CHANNEL_STATUS_DISCONNECTED;
+
+ // Set the session to disconnected state
+ se->Established = false;
+ se->LastCommTick = s->Now;
+ }
+ break;
+ }
+ }
+
+ if (c != NULL)
+ {
+ // If there is a packet to be transmitted physically in SSL, send it
+ if (c->SslPipe != NULL && SyncSslPipe(c->SslPipe))
+ {
+ if (FifoSize(c->SslPipe->RawOut->RecvFifo) >= 1)
+ {
+ Debug("RawOut Fifo Size (c=%u): %u\n", c->KeyId, FifoSize(c->SslPipe->RawOut->RecvFifo));
+
+ OvsSendControlPacketWithAutoSplit(c, OPENVPN_P_CONTROL_V1,
+ FifoPtr(c->SslPipe->RawOut->RecvFifo),
+ FifoSize(c->SslPipe->RawOut->RecvFifo));
+
+ ReadFifo(c->SslPipe->RawOut->RecvFifo, NULL, FifoSize(c->SslPipe->RawOut->RecvFifo));
+ }
+ }
+ }
+
+ if (c != NULL)
+ {
+ UINT num;
+ UINT acks[OPENVPN_MAX_NUMACK];
+ UINT k;
+
+ // Packet transmission
+ for (k = 0;k < LIST_NUM(c->SendControlPacketList);k++)
+ {
+ OPENVPN_CONTROL_PACKET *cp = LIST_DATA(c->SendControlPacketList, k);
+
+ if (cp->NextSendTime <= s->Now)
+ {
+ OPENVPN_PACKET *p;
+
+ num = OvsGetAckReplyList(c, acks);
+
+ p = OvsNewControlPacket(cp->OpCode, j, se->ServerSessionId, num, acks,
+ se->ClientSessionId, cp->PacketId, cp->DataSize, cp->Data);
+
+ OvsSendPacketNow(s, se, p);
+
+ OvsFreePacket(p);
+
+ cp->NextSendTime = s->Now + (UINT64)OPENVPN_CONTROL_PACKET_RESEND_INTERVAL;
+
+ AddInterrupt(s->Interrupt, cp->NextSendTime);
+ }
+ }
+
+ // If the response with an ACK-only packet is required, respond such that
+ num = OvsGetAckReplyList(c, acks);
+
+ if (num >= 1)
+ {
+ OPENVPN_PACKET *p = OvsNewControlPacket(OPENVPN_P_ACK_V1, j, se->ServerSessionId,
+ num, acks, se->ClientSessionId, 0, 0, NULL);
+
+ OvsSendPacketNow(s, se, p);
+
+ OvsFreePacket(p);
+ }
+ }
+ }
+
+ if (se->Ipc != NULL)
+ {
+ if (se->Mode == OPENVPN_MODE_L3)
+ {
+ if (se->IpcAsync != NULL)
+ {
+ // Update DHCP address
+ if (se->IpcAsync->L3NextDhcpRenewTick <= s->Now)
+ {
+ IP ip;
+
+ se->IpcAsync->L3NextDhcpRenewTick = s->Now + se->IpcAsync->L3DhcpRenewInterval;
+
+ UINTToIP(&ip, se->IpcAsync->L3ClientAddressOption.ServerAddress);
+
+ IPCDhcpRenewIP(se->Ipc, &ip);
+ }
+ }
+
+ IPCProcessL3Events(se->Ipc);
+ }
+
+ IPCProcessInterrupts(se->Ipc);
+ }
+
+ // Choose the latest channel in all established channels
+ for (j = 0;j < OPENVPN_NUM_CHANNELS;j++)
+ {
+ OPENVPN_CHANNEL *c = se->Channels[j];
+
+ if (c != NULL)
+ {
+ if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
+ {
+ if (max_tick <= c->EstablishedTick)
+ {
+ max_tick = c->EstablishedTick;
+ latest_channel = c;
+ }
+ }
+ }
+ }
+
+ if (se->Established == false)
+ {
+ latest_channel = NULL;
+ }
+
+ // Send the data using the latest channel (when there is no transmission channel, suck out the queue simply)
+ if (se->Mode == OPENVPN_MODE_L2)
+ {
+ // Get an Ethernet frame from IPC
+ while (true)
+ {
+ BLOCK *b = IPCRecvL2(se->Ipc);
+ if (b == NULL)
+ {
+ break;
+ }
+
+ if (latest_channel != NULL && s->SupressSendPacket == false)
+ {
+ OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId, b->Buf, b->Size);
+ }
+
+ FreeBlock(b);
+ }
+ }
+ else
+ {
+ // Get an IPv4 packet from IPC
+ while (true)
+ {
+ BLOCK *b = IPCRecvIPv4(se->Ipc);
+ if (b == NULL)
+ {
+ break;
+ }
+
+ if (latest_channel != NULL && s->SupressSendPacket == false)
+ {
+ OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId, b->Buf, b->Size);
+ }
+
+ FreeBlock(b);
+ }
+ }
+
+ // Send a Ping
+ if (latest_channel != NULL)
+ {
+ if ((se->NextPingSendTick == 0) || (se->NextPingSendTick <= s->Now))
+ {
+ se->NextPingSendTick = s->Now + (UINT64)(OPENVPN_PING_SEND_INTERVAL);
+
+ OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId,
+ ping_signature, sizeof(ping_signature));
+ //Debug(".");
+
+ AddInterrupt(s->Interrupt, se->NextPingSendTick);
+ }
+ }
+
+ if ((se->Established == false) && (s->Now >= (se->CreatedTick + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT)))
+ {
+ is_disconnected = true;
+ }
+
+ if (se->Established && (s->Now >= (se->LastCommTick + (UINT64)OPENVPN_RECV_TIMEOUT)))
+ {
+ is_disconnected = true;
+ }
+
+ if (is_disconnected)
+ {
+ if (delete_session_list == NULL)
+ {
+ delete_session_list = NewListFast(NULL);
+ }
+
+ Add(delete_session_list, se);
+ }
+ }
+
+ if (delete_session_list != NULL)
+ {
+ UINT i;
+
+ for (i = 0;i < LIST_NUM(delete_session_list);i++)
+ {
+ OPENVPN_SESSION *se = LIST_DATA(delete_session_list, i);
+
+ Debug("Deleting Session %p\n", se);
+ OvsLog(s, se, NULL, "LO_DELETE_SESSION");
+
+ OvsFreeSession(se);
+
+ s->DisconnectCount++;
+
+ Delete(s->SessionList, se);
+ }
+
+ ReleaseList(delete_session_list);
+ }
+}
+
+// Send the packet now
+void OvsSendPacketNow(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_PACKET *p)
+{
+ BUF *b;
+ UINT i;
+ // Validate arguments
+ if (s == NULL || se == NULL || p == NULL)
+ {
+ return;
+ }
+
+ Debug("Sending Opcode=%u ", p->OpCode);
+ if (p->NumAck >= 1)
+ {
+ Debug("Sending ACK Packet IDs (c=%u): ", p->KeyId);
+ for (i = 0;i < p->NumAck;i++)
+ {
+ Debug("%u ", p->AckPacketId[i]);
+ }
+ }
+ Debug("\n");
+
+ b = OvsBuildPacket(p);
+
+ OvsSendPacketRawNow(s, se, b->Buf, b->Size);
+
+ Free(b);
+}
+void OvsSendPacketRawNow(OPENVPN_SERVER *s, OPENVPN_SESSION *se, void *data, UINT size)
+{
+ UDPPACKET *u;
+
+ // Validate arguments
+ if (s == NULL || se == NULL || data == NULL || size == 0)
+ {
+ Free(data);
+ return;
+ }
+
+ u = NewUdpPacket(&se->ServerIp, se->ServerPort, &se->ClientIp, se->ClientPort,
+ data, size);
+
+ Add(s->SendPacketList, u);
+}
+// Create a new OpenVPN control packet
+OPENVPN_PACKET *OvsNewControlPacket(UCHAR opcode, UCHAR key_id, UINT64 my_channel_id, UINT num_ack,
+ UINT *ack_packet_ids, UINT64 your_channel_id, UINT packet_id,
+ UINT data_size, UCHAR *data)
+{
+ OPENVPN_PACKET *p = ZeroMalloc(sizeof(OPENVPN_PACKET));
+ UINT i;
+
+ p->OpCode = opcode;
+ p->KeyId = key_id;
+ p->MySessionId = my_channel_id;
+ p->NumAck = num_ack;
+
+ for (i = 0;i < MIN(num_ack, OPENVPN_MAX_NUMACK);i++)
+ {
+ p->AckPacketId[i] = ack_packet_ids[i];
+ }
+
+ p->YourSessionId = your_channel_id;
+ p->PacketId = packet_id;
+
+ if (data_size != 0 && data != NULL)
+ {
+ p->Data = Clone(data, data_size);
+ p->DataSize = data_size;
+ }
+
+ return p;
+}
+
+// Comparison function of the entries in the session list
+int OvsCompareSessionList(void *p1, void *p2)
+{
+ OPENVPN_SESSION *s1, *s2;
+ int i;
+ // Validate arguments
+ if (p1 == NULL || p2 == NULL)
+ {
+ return 0;
+ }
+ s1 = *(OPENVPN_SESSION **)p1;
+ s2 = *(OPENVPN_SESSION **)p2;
+ if (s1 == NULL || s2 == NULL)
+ {
+ return 0;
+ }
+
+ i = CmpIpAddr(&s1->Protocol, &s2->Protocol);
+ if (i != 0)
+ {
+ return i;
+ }
+
+ i = CmpIpAddr(&s1->ClientIp, &s2->ClientIp);
+ if (i != 0)
+ {
+ return i;
+ }
+
+ i = COMPARE_RET(s1->ClientPort, s2->ClientPort);
+ if (i != 0)
+ {
+ return i;
+ }
+
+ i = CmpIpAddr(&s1->ServerIp, &s2->ServerIp);
+ if (i != 0)
+ {
+ return i;
+ }
+
+ i = COMPARE_RET(s1->ServerPort, s2->ServerPort);
+ if (i != 0)
+ {
+ return i;
+ }
+
+ return 0;
+}
+
+// Identify whether the IP address is compatible to the tun device of OpenVPN
+bool OvsIsCompatibleL3IP(UINT ip)
+{
+ IP p;
+
+ UINTToIP(&p, ip);
+ if ((p.addr[3] % 4) == 1)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// Get an IP address that is compatible to tun device of the OpenVPN after the specified IP address
+UINT OvsGetCompatibleL3IPNext(UINT ip)
+{
+ ip = Endian32(ip);
+
+ while (true)
+ {
+ if (OvsIsCompatibleL3IP(Endian32(ip)))
+ {
+ return Endian32(ip);
+ }
+
+ ip++;
+ }
+}
+
+// Create a new OpenVPN server
+OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOCK_EVENT *sock_event)
+{
+ OPENVPN_SERVER *s;
+ // Validate arguments
+ if (cedar == NULL)
+ {
+ return NULL;
+ }
+
+ s = ZeroMalloc(sizeof(OPENVPN_SERVER));
+
+ s->Cedar = cedar;
+
+ AddRef(s->Cedar->ref);
+
+ s->Interrupt = interrupt;
+
+ s->SessionList = NewList(OvsCompareSessionList);
+ s->SendPacketList = NewListFast(NULL);
+
+ s->Now = Tick64();
+
+ s->NextSessionId = 1;
+
+ if (sock_event != NULL)
+ {
+ s->SockEvent = sock_event;
+ AddRef(s->SockEvent->ref);
+ }
+
+ OvsLog(s, NULL, NULL, "LO_START");
+
+ s->Dh = DhNewGroup2();
+
+ return s;
+}
+
+// Release the OpenVPN server
+void FreeOpenVpnServer(OPENVPN_SERVER *s)
+{
+ UINT i;
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ OvsLog(s, NULL, NULL, "LO_STOP");
+
+ // Release the session list
+ for (i = 0;i < LIST_NUM(s->SessionList);i++)
+ {
+ OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
+
+ OvsFreeSession(se);
+ }
+
+ ReleaseList(s->SessionList);
+
+ // Release the packet which is attempting to send
+ for (i = 0;i < LIST_NUM(s->SendPacketList);i++)
+ {
+ UDPPACKET *p = LIST_DATA(s->SendPacketList, i);
+
+ FreeUdpPacket(p);
+ }
+
+ ReleaseList(s->SendPacketList);
+
+ ReleaseCedar(s->Cedar);
+
+ if (s->SockEvent != NULL)
+ {
+ ReleaseSockEvent(s->SockEvent);
+ }
+
+ DhFree(s->Dh);
+
+ Free(s);
+}
+
+// UDP reception procedure
+void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list)
+{
+ OPENVPN_SERVER_UDP *us;
+ UINT64 now = Tick64();
+ // Validate arguments
+ if (u == NULL || packet_list == NULL)
+ {
+ return;
+ }
+
+ us = (OPENVPN_SERVER_UDP *)u->Param;
+
+ if (OvsGetNoOpenVpnUdp())
+ {
+ // OpenVPN over UDP is disabled
+ return;
+ }
+
+ if (us->OpenVpnServer != NULL)
+ {
+ {
+ u->PollMyIpAndPort = false;
+
+ ClearStr(us->Cedar->OpenVPNPublicPorts, sizeof(us->Cedar->OpenVPNPublicPorts));
+ }
+
+ OvsRecvPacket(us->OpenVpnServer, packet_list, OPENVPN_PROTOCOL_UDP);
+
+ UdpListenerSendPackets(u, us->OpenVpnServer->SendPacketList);
+ DeleteAll(us->OpenVpnServer->SendPacketList);
+ }
+}
+
+// Create an OpenVPN server (UDP mode)
+OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar)
+{
+ OPENVPN_SERVER_UDP *u;
+ // Validate arguments
+ if (cedar == NULL)
+ {
+ return NULL;
+ }
+
+ u = ZeroMalloc(sizeof(OPENVPN_SERVER_UDP));
+
+ u->Cedar = cedar;
+
+ AddRef(u->Cedar->ref);
+
+ // Create a UDP listener
+ u->UdpListener = NewUdpListener(OpenVpnServerUdpListenerProc, u);
+
+ // Create an OpenVPN server
+ u->OpenVpnServer = NewOpenVpnServer(cedar, u->UdpListener->Interrupts, u->UdpListener->Event);
+
+ return u;
+}
+
+// Apply the port list to the OpenVPN server
+void OvsApplyUdpPortList(OPENVPN_SERVER_UDP *u, char *port_list)
+{
+ LIST *o;
+ UINT i;
+ // Validate arguments
+ if (u == NULL)
+ {
+ return;
+ }
+
+ DeleteAllPortFromUdpListener(u->UdpListener);
+
+ o = StrToIntList(port_list, true);
+
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ UINT port = *((UINT *)LIST_DATA(o, i));
+
+ if (port >= 1 && port <= 65535)
+ {
+ AddPortToUdpListener(u->UdpListener, port);
+ }
+ }
+
+ ReleaseIntList(o);
+}
+
+// Release the OpenVPN server (UDP mode)
+void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u)
+{
+ // Validate arguments
+ if (u == NULL)
+ {
+ return;
+ }
+
+ // Stop the UDP listener
+ FreeUdpListener(u->UdpListener);
+
+ // Release the OpenVPN server
+ FreeOpenVpnServer(u->OpenVpnServer);
+
+ ReleaseCedar(u->Cedar);
+
+ Free(u);
+}
+
+// Check whether it's OpenSSL protocol by looking the first receive buffer of the TCP
+bool OvsCheckTcpRecvBufIfOpenVPNProtocol(UCHAR *buf, UINT size)
+{
+ if (buf == NULL || size != 2)
+ {
+ return false;
+ }
+
+ if (buf[0] == 0x00 && buf[1] == 0x0E)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// Run the OpenVPN server in TCP mode
+bool OvsPerformTcpServer(CEDAR *cedar, SOCK *sock)
+{
+ OPENVPN_SERVER *s;
+ INTERRUPT_MANAGER *im;
+ SOCK_EVENT *se;
+ FIFO *tcp_recv_fifo;
+ FIFO *tcp_send_fifo;
+ UINT buf_size = (128 * 1024);
+ UCHAR *buf;
+ UINT64 giveup_time = Tick64() + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT;
+ LIST *ovs_recv_packet;
+ UINT i;
+ bool ret = false;
+ // Validate arguments
+ if (cedar == NULL || sock == NULL)
+ {
+ return false;
+ }
+
+ // Initialize
+ buf = Malloc(buf_size);
+ im = NewInterruptManager();
+ se = NewSockEvent();
+ SetTimeout(sock, TIMEOUT_INFINITE);
+ JoinSockToSockEvent(sock, se);
+
+ tcp_recv_fifo = NewFifoFast();
+ tcp_send_fifo = NewFifoFast();
+
+ ovs_recv_packet = NewListFast(NULL);
+
+ // Create an OpenVPN server
+ s = NewOpenVpnServer(cedar, im, se);
+
+ // Main loop
+ Debug("Entering OpenVPN TCP Server Main Loop.\n");
+ while (true)
+ {
+ UINT next_interval;
+ bool disconnected = false;
+ UINT64 now = Tick64();
+
+ // Receive data from a TCP socket
+ while (true)
+ {
+ UINT r = Recv(sock, buf, buf_size, false);
+ if (r == SOCK_LATER)
+ {
+ // Can not read any more
+ break;
+ }
+ else if (r == 0)
+ {
+ // Disconnected
+ disconnected = true;
+ break;
+ }
+ else
+ {
+ // Read
+ WriteFifo(tcp_recv_fifo, buf, r);
+ }
+ }
+
+ // Separate to a list of datagrams by interpreting the data received from the TCP socket
+ while (true)
+ {
+ UINT r = FifoSize(tcp_recv_fifo);
+ if (r >= sizeof(USHORT))
+ {
+ void *ptr = FifoPtr(tcp_recv_fifo);
+ USHORT packet_size = READ_USHORT(ptr);
+ if (packet_size <= OPENVPN_TCP_MAX_PACKET_SIZE)
+ {
+ UINT total_len = (UINT)packet_size + sizeof(USHORT);
+ if (r >= total_len)
+ {
+ if (ReadFifo(tcp_recv_fifo, buf, total_len) != total_len)
+ {
+ // Mismatch
+ disconnected = true;
+ break;
+ }
+ else
+ {
+ // Read one packet
+ UINT payload_len = packet_size;
+ UCHAR *payload_ptr = buf + sizeof(USHORT);
+
+ // Pass the packet to the OpenVPN server
+ Add(ovs_recv_packet, NewUdpPacket(&sock->RemoteIP, sock->RemotePort,
+ &sock->LocalIP, sock->LocalPort,
+ Clone(payload_ptr, payload_len), payload_len));
+ }
+ }
+ else
+ {
+ // Non-arrival
+ break;
+ }
+ }
+ else
+ {
+ // Invalid packet size
+ disconnected = true;
+ break;
+ }
+ }
+ else
+ {
+ // Non-arrival
+ break;
+ }
+ }
+
+ // Pass a list of received datagrams to the OpenVPN server
+ OvsRecvPacket(s, ovs_recv_packet, OPENVPN_PROTOCOL_TCP);
+
+ // Release the received packet list
+ for (i = 0;i < LIST_NUM(ovs_recv_packet);i++)
+ {
+ UDPPACKET *p = LIST_DATA(ovs_recv_packet, i);
+
+ FreeUdpPacket(p);
+ }
+
+ DeleteAll(ovs_recv_packet);
+
+ // Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server
+ for (i = 0;i < LIST_NUM(s->SendPacketList);i++)
+ {
+ UDPPACKET *p = LIST_DATA(s->SendPacketList, i);
+ // Store the size to the TCP send queue first
+ USHORT us = (USHORT)p->Size;
+ //Debug(" *** TCP SEND %u\n", us);
+ us = Endian16(us);
+ WriteFifo(tcp_send_fifo, &us, sizeof(USHORT));
+
+ // Write the data body
+ WriteFifo(tcp_send_fifo, p->Data, p->Size);
+
+ // Packet release
+ FreeUdpPacket(p);
+ }
+ DeleteAll(s->SendPacketList);
+
+ // Send data to the TCP socket
+ while (FifoSize(tcp_send_fifo) >= 1)
+ {
+ UINT r = Send(sock, FifoPtr(tcp_send_fifo), FifoSize(tcp_send_fifo), false);
+
+ if (r == SOCK_LATER)
+ {
+ // Can not write any more
+ break;
+ }
+ else if (r == 0)
+ {
+ // Disconnected
+ disconnected = true;
+ break;
+ }
+ else
+ {
+ // Wrote out
+ ReadFifo(tcp_send_fifo, NULL, r);
+ }
+ }
+
+ if (FifoSize(tcp_send_fifo) > MAX_BUFFERING_PACKET_SIZE)
+ {
+ s->SupressSendPacket = true;
+ }
+ else
+ {
+ s->SupressSendPacket = false;
+ }
+
+ if (s->DisconnectCount >= 1)
+ {
+ // Session disconnection has occurred on OpenVPN server-side
+ disconnected = true;
+ }
+
+ if (giveup_time <= now)
+ {
+ UINT i;
+ UINT num_established_sessions = 0;
+ for (i = 0;i < LIST_NUM(s->SessionList);i++)
+ {
+ OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
+
+ if (se->Established)
+ {
+ num_established_sessions++;
+ }
+ }
+
+ if (num_established_sessions == 0)
+ {
+ // If the number of sessions is 0 even if wait a certain period of time after the start of server, abort
+ disconnected = true;
+ }
+ }
+
+ if (disconnected)
+ {
+ // Error or disconnect occurs
+ Debug("Breaking OpenVPN TCP Server Main Loop.\n");
+ break;
+ }
+
+ // Wait until the next event occurs
+ next_interval = GetNextIntervalForInterrupt(im);
+ next_interval = MIN(next_interval, UDPLISTENER_WAIT_INTERVAL);
+ WaitSockEvent(se, next_interval);
+ }
+
+ if (s != NULL && s->SessionEstablishedCount != 0)
+ {
+ ret = true;
+ }
+
+ // Release the OpenVPN server
+ FreeOpenVpnServer(s);
+
+ // Release object
+ FreeInterruptManager(im);
+ ReleaseSockEvent(se);
+ ReleaseFifo(tcp_recv_fifo);
+ ReleaseFifo(tcp_send_fifo);
+ Free(buf);
+
+ // Release the received packet list
+ for (i = 0;i < LIST_NUM(ovs_recv_packet);i++)
+ {
+ UDPPACKET *p = LIST_DATA(ovs_recv_packet, i);
+
+ FreeUdpPacket(p);
+ }
+
+ ReleaseList(ovs_recv_packet);
+
+ return ret;
+}
+
+
+
+
+// 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/