1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-14 05:30:41 +03:00
SoftEtherVPN/src/Cedar/Proto_OpenVPN.c

2940 lines
68 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module
//
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
//
// Copyright (c) Daiyuu Nobori.
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) SoftEther Corporation.
//
// All Rights Reserved.
//
// http://www.softether.org/
//
// Author: Daiyuu Nobori, Ph.D.
// Comments: Tetsuo Sugiyama, Ph.D.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
//
//
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
//
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
//
//
// SOURCE CODE CONTRIBUTION
// ------------------------
//
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
//
//
// DEAR SECURITY EXPERTS
// ---------------------
//
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
//
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
//
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
//
// Thank you for your cooperation.
//
//
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
//
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.
// Proto_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
};
// Get the OpenVPN over TCP disabling flag
bool OvsGetNoOpenVpnTcp()
{
return g_no_openvpn_tcp;
}
// 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)
{
// 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);
{
bool cert_verify = true;
c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, cert_verify, &c->ClientCert);
}
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)
{
LIST *pi;
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);
}
// OpenVPN sends the default gateway's MAC address,
// if the option --push-peer-info is enabled.
// It also sends all of the client's environment
// variables whose names start with "UV_".
pi = NewEntryList(c->ClientKey.PeerInfo, "\n", "=\t");
// Check presence of custom hostname
if (EntryListHasKey(pi, "UV_HOSTNAME"))
{
StrCpy(p.ClientHostname, sizeof(p.ClientHostname), EntryListStrValue(pi, "UV_HOSTNAME"));
}
else // Use the default gateway's MAC address
{
StrCpy(p.ClientHostname, sizeof(p.ClientHostname), EntryListStrValue(pi, "IV_HWADDR"));
}
FreeEntryList(pi);
if (se->Mode == OPENVPN_MODE_L3)
{
// L3 Mode
p.IsL3Mode = true;
}
else
{
// L2 Mode
p.BridgeMode = true;
}
if (c->ClientCert.X != NULL)
{
p.ClientCertificate = c->ClientCert.X;
}
// 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;
char opt_str[MAX_SIZE];
char *cipher_name;
// 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);
if (c->ClientCert.X != NULL)
{
if (c->ClientCert.X->subject_name != NULL)
{
OvsLog(s, se, c, "LO_CLIENT_CERT", c->ClientCert.X->subject_name->CommonName);
}
else
{
OvsLog(s, se, c, "LO_CLIENT_CERT", "(unknown CN)");
}
}
else if (!c->ClientCert.PreverifyErr)
{
OvsLog(s, se, c, "LO_CLIENT_NO_CERT");
}
else
{
OvsLog(s, se, c, "LO_CLIENT_UNVERIFIED_CERT", c->ClientCert.PreverifyErrMessage);
}
Zero(opt_str, sizeof(opt_str));
StrCpy(opt_str, sizeof(opt_str), data->OptionString);
if (s->Cedar != NULL && (IsEmptyStr(opt_str) || StartWith(opt_str, "V0 UNDEF") || InStr(opt_str, ",") == false))
{
StrCpy(opt_str, sizeof(opt_str), s->Cedar->OpenVPNDefaultClientOption);
}
o = NewEntryList(opt_str, ",", " \t");
if (se->Mode == OPENVPN_MODE_UNKNOWN)
{
UINT mtu;
// Layer
if (StrCmpi(EntryListStrValue(o, "dev-type"), "tun") == 0)
{
// L3
se->Mode = OPENVPN_MODE_L3;
}
else
{
// L2
se->Mode = OPENVPN_MODE_L2;
}
// Link MTU
mtu = EntryListIntValue(o, "link-mtu");
if (mtu == 0)
{
mtu = OPENVPN_MTU_LINK;
}
se->LinkMtu = mtu;
// Tun MTU
mtu = EntryListIntValue(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
cipher_name = EntryListStrValue(o, "cipher");
c->CipherEncrypt = OvsGetCipher(cipher_name);
c->CipherDecrypt = OvsGetCipher(cipher_name);
// Hash algorithm
c->MdSend = OvsGetMd(EntryListStrValue(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);
FreeEntryList(o);
// We pass the cipher name sent from the OpenVPN client, unless it's a different cipher, to prevent a message such as:
// WARNING: 'cipher' is used inconsistently, local='cipher AES-128-GCM', remote='cipher aes-128-gcm'
// It happens because OpenVPN uses "strcmp()" to compare the local and remote parameters:
// https://github.com/OpenVPN/openvpn/blob/a6fd48ba36ede465b0905a95568c3ec0d425ca71/src/openvpn/options.c#L3819-L3831
if (StrCmpi(cipher_name, c->CipherEncrypt->Name) != 0)
{
cipher_name = c->CipherEncrypt->Name;
}
// 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,
cipher_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;
// OpenVPN sends the cipher name in uppercase, even if it's not standard,
// thus we have to convert it to lowercase for EVP_get_cipherbyname().
char lowercase_name[MAX_SIZE];
StrCpy(lowercase_name, sizeof(lowercase_name), name);
StrLower(lowercase_name);
if (IsEmptyStr(lowercase_name) == false)
{
c = NewCipher(lowercase_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)
{
m = NewMd(name);
}
if (m == NULL)
{
m = NewMd(OPENVPN_DEFAULT_MD);
}
return m;
}
// 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)))
{
if (!OvsReadStringFromBuf(b, ret->PeerInfo, sizeof(ret->PeerInfo)))
{
Zero(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);
if (c->ClientCert.X != NULL)
{
FreeX(c->ClientCert.X);
}
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));
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");
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[4096];
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_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);
IPToStr32(ip_subnet_mask, sizeof(ip_subnet_mask),
cao->SubnetMask);
Format(l3_options, sizeof(l3_options),
",topology subnet");
StrCat(option_str, sizeof(option_str), l3_options);
Format(l3_options, sizeof(l3_options),
",ifconfig %s %s",
ip_client,
ip_subnet_mask);
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)
{
char ip_str[64];
IPToStr32(ip_str, sizeof(ip_str), cao->Gateway);
Format(l3_options, sizeof(l3_options),
",route-gateway %s,redirect-gateway def1", ip_str);
StrCat(option_str, sizeof(option_str), l3_options);
StrCpy(ip_defgw, sizeof(ip_defgw), ip_str);
}
else
{
#if 0 // Currently disabled
// If the default gateway is not specified, add the static routing table
// entry for the local IP subnet
IP local_network;
IP client_ip;
IP subnet_mask;
UINTToIP(&client_ip, cao->ClientAddress);
UINTToIP(&subnet_mask, cao->SubnetMask);
Zero(&local_network, sizeof(IP));
IPAnd4(&local_network, &client_ip, &subnet_mask);
Format(l3_options, sizeof(l3_options),
",route %r %r vpn_gateway",
&local_network,
&cao->SubnetMask);
StrCat(option_str, sizeof(option_str), l3_options);
#endif
}
// Classless routing table
if (cao->ClasslessRoute.NumExistingRoutes >= 1)
{
UINT i;
for (i = 0;i < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES;i++)
{
DHCP_CLASSLESS_ROUTE *r = &cao->ClasslessRoute.Entries[i];
if (r->Exists)
{
Format(l3_options, sizeof(l3_options),
",route %r %r vpn_gateway",
&r->Network, &r->SubnetMask);
StrCat(option_str, sizeof(option_str), l3_options);
}
}
}
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;
}
// 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 = DhNewFromBits(DH_PARAM_BITS_DEFAULT);
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;
// 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, &cedar->Server->ListenIP);
// Create an OpenVPN server
u->OpenVpnServer = NewOpenVpnServer(cedar, u->UdpListener->Interrupts, u->UdpListener->Event);
return u;
}
void OpenVpnServerUdpSetDhParam(OPENVPN_SERVER_UDP *u, DH_CTX *dh)
{
// Validate arguments
if (u == NULL) {
return;
}
if (u->OpenVpnServer->Dh)
{
DhFree(u->OpenVpnServer->Dh);
}
u->OpenVpnServer->Dh = dh;
}
// Apply the port list to the OpenVPN server
void OvsApplyUdpPortList(OPENVPN_SERVER_UDP *u, char *port_list, IP *listen_ip)
{
LIST *o;
UINT i;
// Validate arguments
if (u == NULL)
{
return;
}
DeleteAllPortFromUdpListener(u->UdpListener);
if (u->UdpListener != NULL && listen_ip != NULL)
{
Copy(&u->UdpListener->ListenIP, listen_ip, sizeof(IP));
}
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 != 0 && 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;
}