// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module


// Proto_IKE.c
// IKE (ISAKMP) and ESP protocol stack

#include "Proto_IKE.h"

#include "Cedar.h"
#include "Connection.h"
#include "Logging.h"
#include "Proto_EtherIP.h"
#include "Proto_IPsec.h"
#include "Proto_L2TP.h"
#include "Server.h"

#include "Mayaqua/Memory.h"
#include "Mayaqua/Object.h"
#include "Mayaqua/Str.h"
#include "Mayaqua/Table.h"
#include "Mayaqua/TcpIp.h"
#include "Mayaqua/Tick64.h"

//#define	RAW_DEBUG

// Processing of IKE received packet
void ProcIKEPacketRecv(IKE_SERVER *ike, UDPPACKET *p)
{
	// Validate arguments
	if (ike == NULL || p == NULL)
	{
		return;
	}

	if (p->Type == IKE_UDP_TYPE_ISAKMP)
	{
		// ISAKMP (IKE) packet
		IKE_PACKET *header;

		header = ParseIKEPacketHeader(p);
		if (header == NULL)
		{
			return;
		}

		//Debug("InitiatorCookie: %I64u, ResponderCookie: %I64u\n", header->InitiatorCookie, header->ResponderCookie);

		switch (header->ExchangeType)
		{
		case IKE_EXCHANGE_TYPE_MAIN:	// Main mode
			ProcIkeMainModePacketRecv(ike, p, header);
			break;

		case IKE_EXCHANGE_TYPE_AGGRESSIVE:	// Aggressive mode
			if (ike->Cedar->Server->DisableIPsecAggressiveMode == false)
			{
				ProcIkeAggressiveModePacketRecv(ike, p, header);
			}
			break;

		case IKE_EXCHANGE_TYPE_QUICK:	// Quick mode
			ProcIkeQuickModePacketRecv(ike, p, header);
			break;

		case IKE_EXCHANGE_TYPE_INFORMATION:	// Information exchange
			ProcIkeInformationalExchangePacketRecv(ike, p, header);
			break;
		}

		IkeFree(header);
	}
	else if (p->Type == IKE_UDP_TYPE_ESP)
	{
		// ESP packet
		ProcIPsecEspPacketRecv(ike, p);
	}
}

// Send a packet via IPsec
void IPsecSendPacketByIPsecSa(IKE_SERVER *ike, IPSECSA *sa, UCHAR *data, UINT data_size, UCHAR protocol_id)
{
	bool is_tunnel_mode;
	IKE_CLIENT *c;
	// Validate arguments
	if (ike == NULL || sa == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	is_tunnel_mode = IsIPsecSaTunnelMode(sa);

	c = sa->IkeClient;

	if (c == NULL)
	{
		return;
	}

	if (is_tunnel_mode)
	{
		// Add an IPv4 / IPv6 header in the case of tunnel mode
		if (IsZeroIP(&c->TunnelModeClientIP) == false || IsZeroIP(&c->TunnelModeServerIP) == false)
		{
			BUF *b;
			UCHAR esp_proto_id;

			b = NewBuf();

			if (IsIP4(&c->TunnelModeClientIP))
			{
				// IPv4 header
				IPV4_HEADER h;

				h.VersionAndHeaderLength = 0;
				h.TypeOfService = 0;
				IPV4_SET_VERSION(&h, 4);
				IPV4_SET_HEADER_LEN(&h, sizeof(IPV4_HEADER) / 4);
				h.TotalLength = Endian16((USHORT)(data_size + sizeof(IPV4_HEADER)));
				h.Identification = Endian16(c->TunnelSendIpId++);
				h.FlagsAndFragmentOffset[0] = h.FlagsAndFragmentOffset[1] = 0;
				h.TimeToLive = DEFAULT_IP_TTL;
				h.Protocol = protocol_id;
				h.SrcIP = IPToUINT(&c->TunnelModeServerIP);
				h.DstIP = IPToUINT(&c->TunnelModeClientIP);
				h.Checksum = 0;
				h.Checksum = IpChecksum(&h, sizeof(IPV4_HEADER));

				WriteBuf(b, &h, sizeof(IPV4_HEADER));

				esp_proto_id = IKE_PROTOCOL_ID_IPV4;
			}
			else
			{
				// IPv6 header
				IPV6_HEADER h;

				Zero(&h, sizeof(h));
				h.VersionAndTrafficClass1 = 0;
				IPV6_SET_VERSION(&h, 6);
				h.TrafficClass2AndFlowLabel1 = 0;
				h.FlowLabel2 = h.FlowLabel3 = 0;
				h.PayloadLength = Endian16(data_size);
				h.NextHeader = protocol_id;
				h.HopLimit = 64;
				Copy(h.SrcAddress.Value, c->TunnelModeServerIP.address, sizeof(h.SrcAddress.Value));
				Copy(h.DestAddress.Value, c->TunnelModeClientIP.address, sizeof(h.DestAddress.Value));

				WriteBuf(b, &h, sizeof(IPV6_HEADER));

				esp_proto_id = IKE_PROTOCOL_ID_IPV6;
			}

			WriteBuf(b, data, data_size);

			IPsecSendPacketByIPsecSaInner(ike, sa, b->Buf, b->Size, esp_proto_id);

			FreeBuf(b);
		}
	}
	else
	{
		// Send as it is in the case of transport mode
		IPsecSendPacketByIPsecSaInner(ike, sa, data, data_size, protocol_id);
	}
}
void IPsecSendPacketByIPsecSaInner(IKE_SERVER *ike, IPSECSA *sa, UCHAR *data, UINT data_size, UCHAR protocol_id)
{
	UINT esp_size;
	UINT encrypted_payload_size;
	UCHAR *esp;
	UINT i;
	UINT size_of_padding;
	IKE_CRYPTO_PARAM cp;
	BUF *enc;
	IKE_CLIENT *c;
	// Validate arguments
	if (ike == NULL || sa == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	c = sa->IkeClient;
	if (c == NULL)
	{
		return;
	}

	// Calculate the payload size after encryption
	encrypted_payload_size = data_size + 2;
	if ((encrypted_payload_size % sa->TransformSetting.Crypto->BlockSize) != 0)
	{
		encrypted_payload_size = ((encrypted_payload_size / sa->TransformSetting.Crypto->BlockSize) + 1) * sa->TransformSetting.Crypto->BlockSize;
	}
	size_of_padding = encrypted_payload_size - data_size - 2;

	// Calculate the size of the ESP packet
	esp_size = sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size + IKE_ESP_HASH_SIZE;

	// Build the ESP packet
	esp = Malloc(esp_size + IKE_MAX_HASH_SIZE);

	// SPI
	WRITE_UINT(esp, sa->Spi);

	// Sequence number
	sa->CurrentSeqNo++;
	WRITE_UINT(esp + sizeof(UINT), sa->CurrentSeqNo);

	// IV
	Copy(esp + sizeof(UINT) * 2, sa->EspIv, sa->TransformSetting.Crypto->BlockSize);

	// Payload data
	Copy(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize, data, data_size);

	// Padding
	for (i = 0;i < size_of_padding;i++)
	{
		esp[sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + data_size + i] = (UCHAR)(i + 1);
	}

	// Padding length
	esp[sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + data_size + size_of_padding] = (UCHAR)size_of_padding;

	// Next header number
	esp[sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + data_size + size_of_padding + 1] = protocol_id;

	// Encryption
	Copy(cp.Iv, sa->EspIv, sa->TransformSetting.Crypto->BlockSize);
	cp.Key = sa->CryptoKey;

	enc = IkeEncrypt(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize, encrypted_payload_size, &cp);
	if (enc != NULL)
	{
		bool start_qm = false;
		UINT server_port = c->ServerPort;
		UINT client_port = c->ClientPort;

		// Overwrite the encrypted result
		Copy(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize, enc->Buf, encrypted_payload_size);

		FreeBuf(enc);

		// Calculate the HMAC
		IkeHMac(sa->TransformSetting.Hash,
			esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size,
			sa->HashKey,
			sa->TransformSetting.Hash->HashSize,
			esp,
			sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size);

		//*(UCHAR *)(esp + sizeof(UINT) * 2 + sa->TransformSetting.Crypto->BlockSize + encrypted_payload_size) = 0xff;

		if (sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TRANSPORT ||
			sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TUNNEL)
		{
			server_port = client_port = IPSEC_PORT_IPSEC_ESP_RAW;
		}

		// Add the completed packet to the transmission list
		IkeSendUdpPacket(ike, IKE_UDP_TYPE_ESP, &c->ServerIP, server_port, &c->ClientIP, client_port,
			esp, esp_size);

		// Feedback the IV
		Copy(sa->EspIv, cp.NextIv, sa->TransformSetting.Crypto->BlockSize);

		sa->TotalSize += esp_size;

		if (sa->CurrentSeqNo >= 0xf0000000)
		{
			start_qm = true;
		}

		if (sa->TransformSetting.LifeKilobytes != 0)
		{
			UINT64 hard_size = (UINT64)sa->TransformSetting.LifeKilobytes * (UINT64)1000;
			UINT64 soft_size = hard_size * (UINT64)2 / (UINT64)3;

			if (sa->TotalSize >= soft_size)
			{
				start_qm = true;
			}
		}

		if (start_qm)
		{
			if (sa->StartQM_FlagSet == false)
			{
				sa->StartQM_FlagSet = true;
				c->StartQuickModeAsSoon = true;
			}
		}
	}
	else
	{
		// Encryption failure
		Free(esp);
	}
}
void IPsecSendPacketByIkeClient(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size, UCHAR protocol_id)
{
	// Validate arguments
	if (ike == NULL || c == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	if (c->CurrentIpSecSaSend == NULL)
	{
		return;
	}

	IPsecSendPacketByIPsecSa(ike, c->CurrentIpSecSaSend, data, data_size, protocol_id);
}

// Send an UDP packet via IPsec
void IPsecSendUdpPacket(IKE_SERVER *ike, IKE_CLIENT *c, UINT src_port, UINT dst_port, UCHAR *data, UINT data_size)
{
	UCHAR *udp;
	UINT udp_size;
	UDP_HEADER *u;
	UCHAR tmp1600[1600];
	bool no_free = false;
	// Validate arguments
	if (ike == NULL || c == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	// Build an UDP packet
	udp_size = sizeof(UDP_HEADER) + data_size;

	if (udp_size > sizeof(tmp1600))
	{
		udp = Malloc(udp_size);
	}
	else
	{
		udp = tmp1600;
		no_free = true;
	}

	// UDP header
	u = (UDP_HEADER *)udp;
	u->SrcPort = Endian16(src_port);
	u->DstPort = Endian16(dst_port);
	u->PacketLength = Endian16(udp_size);
	u->Checksum = 0;

	//Debug("IPsec UDP Send: %u -> %u %u\n", src_port, dst_port, data_size);
#ifdef	RAW_DEBUG
	IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, data, data_size);
#endif	// RAW_DEBUG

	// Payload
	Copy(udp + sizeof(UDP_HEADER), data, data_size);

	if (IsIP6(&c->ClientIP))
	{
		if (IsIPsecSaTunnelMode(c->CurrentIpSecSaSend) == false)
		{
			u->Checksum = CalcChecksumForIPv6((IPV6_ADDR *)c->TransportModeServerIP.address,
				(IPV6_ADDR *)c->TransportModeClientIP.address,
				IP_PROTO_UDP,
				u,
				udp_size, 0);
		}
		else
		{
			u->Checksum = CalcChecksumForIPv6((IPV6_ADDR *)c->TunnelModeServerIP.address,
				(IPV6_ADDR *)c->TunnelModeClientIP.address,
				IP_PROTO_UDP,
				u,
				udp_size, 0);
		}
	}

	IPsecSendPacketByIkeClient(ike, c, udp, udp_size, IP_PROTO_UDP);

	if (no_free == false)
	{
		Free(udp);
	}
}

// Get whether the specified IPsec SA is in tunnel mode
bool IsIPsecSaTunnelMode(IPSECSA *sa)
{
	// Validate arguments
	if (sa == NULL)
	{
		return false;
	}

	if (sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TUNNEL ||
		sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_1 ||
		sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_2)
	{
		return true;
	}

	return false;
}

// Reception process of ESP packet
void ProcIPsecEspPacketRecv(IKE_SERVER *ike, UDPPACKET *p)
{
	UCHAR *src;
	UINT src_size;
	UINT spi;
	UINT seq;
	IPSECSA *ipsec_sa;
	IKE_CLIENT *c;
	UINT block_size;
	UINT hash_size;
	bool update_status = false;
	UCHAR *iv;
	UCHAR *hash;
	UCHAR *encrypted_payload_data;
	UINT size_of_payload_data;
	IKE_CRYPTO_PARAM cp;
	BUF *dec;
	UCHAR calced_hash[IKE_MAX_HASH_SIZE];
	bool is_tunnel_mode = false;
	// Validate arguments
	if (ike == NULL || p == NULL)
	{
		return;
	}

	src = (UCHAR *)p->Data;
	src_size = p->Size;

	if (p->DestPort == IPSEC_PORT_IPSEC_ESP_RAW)
	{
		if (IsIP4(&p->DstIP))
		{
			// Skip the IP header when received in Raw mode (only in the case of IPv4)
			UINT ip_header_size = GetIpHeaderSize(src, src_size);

			src += ip_header_size;
			src_size -= ip_header_size;
		}
	}

	// Get the SPI
	if (src_size < sizeof(UINT))
	{
		return;
	}

	spi = READ_UINT(src + 0);
	if (spi == 0)
	{
		return;
	}

	// Get the sequence number
	if (src_size < (sizeof(UINT) * 2))
	{
		return;
	}
	seq = READ_UINT(src + sizeof(UINT));

	// Search and retrieve the IPsec SA from SPI

	// thank to @phillibert report, responding to bad SA might lead to amplification
	// according to RFC4303 we should drop such packets

	ipsec_sa = SearchClientToServerIPsecSaBySpi(ike, spi);
	if (ipsec_sa == NULL)
	{
		return;
	}

	is_tunnel_mode = IsIPsecSaTunnelMode(ipsec_sa);

	c = ipsec_sa->IkeClient;
	if (c == NULL)
	{
		return;
	}

	block_size = ipsec_sa->TransformSetting.Crypto->BlockSize;
	hash_size = IKE_ESP_HASH_SIZE;

	// Get the IV
	if (src_size < (sizeof(UINT) * 2 + block_size + hash_size + block_size))
	{
		return;
	}
	iv = src + sizeof(UINT) * 2;

	// Get the hash
	hash = src + src_size - hash_size;

	// Inspect the HMAC
	IkeHMac(ipsec_sa->TransformSetting.Hash, calced_hash, ipsec_sa->HashKey,
		ipsec_sa->TransformSetting.Hash->HashSize, src, src_size - hash_size);

	if (Cmp(calced_hash, hash, hash_size) != 0)
	{
		//Debug("IPsec SA 0x%X: Invalid HMAC Value.\n", ipsec_sa->Spi);
		return;
	}

	// Get the payload data
	encrypted_payload_data = src + sizeof(UINT) * 2 + block_size;
	size_of_payload_data = src_size - hash_size - block_size - sizeof(UINT) * 2;
	if (size_of_payload_data == 0 || (size_of_payload_data % block_size) != 0)
	{
		// Payload data don't exist or is not a multiple of block size
		return;
	}

	// Decrypt the payload data
	cp.Key = ipsec_sa->CryptoKey;
	Copy(&cp.Iv, iv, block_size);

	dec = IkeDecrypt(encrypted_payload_data, size_of_payload_data, &cp);
	if (dec != NULL)
	{
		UCHAR *dec_data = dec->Buf;
		UINT dec_size = dec->Size;
		UCHAR size_of_padding = dec_data[dec_size - 2];
		UCHAR next_header = dec_data[dec_size - 1];
		if ((dec_size - 2) >= size_of_padding)
		{
			UINT orig_size = dec_size - 2 - size_of_padding;

			ipsec_sa->TotalSize += dec_size;

			if (is_tunnel_mode)
			{
				// Tunnel Mode
				if (next_header == IKE_PROTOCOL_ID_IPV4 || next_header == IKE_PROTOCOL_ID_IPV6)
				{
					// Check the contents by parsing the IPv4 / IPv6 header in the case of tunnel mode
					BUF *b = NewBuf();
					static UCHAR src_mac_dummy[6] = {0, 0, 0, 0, 0, 0, };
					static UCHAR dst_mac_dummy[6] = {0, 0, 0, 0, 0, 0, };
					USHORT tpid = Endian16(next_header == IKE_PROTOCOL_ID_IPV4 ? MAC_PROTO_IPV4 : MAC_PROTO_IPV6);
					PKT *pkt;

					WriteBuf(b, src_mac_dummy, sizeof(src_mac_dummy));
					WriteBuf(b, dst_mac_dummy, sizeof(dst_mac_dummy));
					WriteBuf(b, &tpid, sizeof(tpid));

					WriteBuf(b, dec_data, dec_size);

					// Parse
					pkt = ParsePacket(b->Buf, b->Size);

#ifdef	RAW_DEBUG
					IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, b->Buf, b->Size);
#endif	// RAW_DEBUG

					if (pkt == NULL)
					{
						// Parsing failure
						dec_data = NULL;
						dec_size = 0;
					}
					else
					{
						// Parsing success
						switch (pkt->TypeL3)
						{
						case L3_IPV4:
							// Save the internal IP address information
							UINTToIP(&c->TunnelModeServerIP, pkt->L3.IPv4Header->DstIP);
							UINTToIP(&c->TunnelModeClientIP, pkt->L3.IPv4Header->SrcIP);

							if (IPV4_GET_OFFSET(pkt->L3.IPv4Header) == 0)
							{
								if ((IPV4_GET_FLAGS(pkt->L3.IPv4Header) & 0x01) == 0)
								{
									if (pkt->L3.IPv4Header->Protocol == IPSEC_IP_PROTO_ETHERIP)
									{
										// EtherIP
										if (ike->IPsec->Services.EtherIP_IPsec)
										{
											// An EtherIP packet has been received
											ProcIPsecEtherIPPacketRecv(ike, c, pkt->IPv4PayloadData, pkt->IPv4PayloadSize, true);
										}
									}
									else if (pkt->L3.IPv4Header->Protocol == IPSEC_IP_PROTO_L2TPV3)
									{
										// L2TPv3
										if (ike->IPsec->Services.EtherIP_IPsec)
										{
											// A L2TPv3 packet has been received
											ProcL2TPv3PacketRecv(ike, c, pkt->IPv4PayloadData, pkt->IPv4PayloadSize, true);
										}
									}
								}
							}
							break;

						case L3_IPV6:
							// Save the internal IP address information
							SetIP6(&c->TunnelModeServerIP, pkt->IPv6HeaderPacketInfo.IPv6Header->DestAddress.Value);
							SetIP6(&c->TunnelModeClientIP, pkt->IPv6HeaderPacketInfo.IPv6Header->SrcAddress.Value);

							if (pkt->IPv6HeaderPacketInfo.IsFragment == false)
							{
								if (pkt->IPv6HeaderPacketInfo.FragmentHeader == NULL || (IPV6_GET_FLAGS(pkt->IPv6HeaderPacketInfo.FragmentHeader) & IPV6_FRAGMENT_HEADER_FLAG_MORE_FRAGMENTS) == 0)
								{
									if (pkt->IPv6HeaderPacketInfo.Protocol == IPSEC_IP_PROTO_ETHERIP)
									{
										// EtherIP
										if (ike->IPsec->Services.EtherIP_IPsec)
										{
											// An EtherIP packet has been received
											ProcIPsecEtherIPPacketRecv(ike, c, pkt->IPv6HeaderPacketInfo.Payload, pkt->IPv6HeaderPacketInfo.PayloadSize, true);
										}
									}
									else if (pkt->IPv6HeaderPacketInfo.Protocol == IPSEC_IP_PROTO_L2TPV3)
									{
										// L2TPv3
										if (ike->IPsec->Services.EtherIP_IPsec)
										{
											// A L2TPv3 packet has been received
											ProcL2TPv3PacketRecv(ike, c, pkt->IPv6HeaderPacketInfo.Payload, pkt->IPv6HeaderPacketInfo.PayloadSize, true);
										}
									}
								}
							}
							break;
						}

						FreePacket(pkt);
					}

					FreeBuf(b);
				}
			}
			else
			{
				// Transport mode
				if (next_header == IP_PROTO_UDP)
				{
					if (ike->IPsec->Services.L2TP_IPsec || ike->IPsec->Services.EtherIP_IPsec)
					{
						// An UDP packet has been received
						ProcIPsecUdpPacketRecv(ike, c, dec_data, dec_size);
					}
				}
				else if (next_header == IPSEC_IP_PROTO_ETHERIP)
				{
					if (ike->IPsec->Services.EtherIP_IPsec)
					{
						// An EtherIP packet has been received
						ProcIPsecEtherIPPacketRecv(ike, c, dec_data, dec_size, false);
					}
				}
				else if (next_header == IPSEC_IP_PROTO_L2TPV3)
				{
					if (ike->IPsec->Services.EtherIP_IPsec)
					{
						// A L2TPv3 packet has been received
						ProcL2TPv3PacketRecv(ike, c, dec_data, dec_size, false);
					}
				}
			}

			update_status = true;
		}

		FreeBuf(dec);
	}

	if (update_status)
	{
		bool start_qm = false;
		// Update the status of the client
		c->CurrentIpSecSaRecv = ipsec_sa;
		if (ipsec_sa->PairIPsecSa != NULL)
		{
			c->CurrentIpSecSaSend = ipsec_sa->PairIPsecSa;

			if (p->DestPort == IPSEC_PORT_IPSEC_ESP_UDP)
			{
				IPSECSA *send_sa = c->CurrentIpSecSaSend;
				if (send_sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TUNNEL)
				{
					send_sa->TransformSetting.CapsuleMode = IKE_P2_CAPSULE_NAT_TUNNEL_1;
				}
				else if (send_sa->TransformSetting.CapsuleMode == IKE_P2_CAPSULE_TRANSPORT)
				{
					send_sa->TransformSetting.CapsuleMode = IKE_P2_CAPSULE_NAT_TRANSPORT_1;
				}
			}
		}
		c->LastCommTick = ike->Now;
		ipsec_sa->LastCommTick = ike->Now;
		if (ipsec_sa->PairIPsecSa != NULL)
		{
			ipsec_sa->PairIPsecSa->LastCommTick = ike->Now;
		}

		SetIkeClientEndpoint(ike, c, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort);

		if (seq >= 0xf0000000)
		{
			// Execute a QuickMode forcibly since sequence number is going to exhaust
			start_qm = true;
		}

		if (ipsec_sa->TransformSetting.LifeKilobytes != 0)
		{
			UINT64 hard_size = (UINT64)ipsec_sa->TransformSetting.LifeKilobytes * (UINT64)1000;
			UINT64 soft_size = hard_size * (UINT64)2 / (UINT64)3;

			if (ipsec_sa->TotalSize >= soft_size)
			{
				// Execute a QuickMode forcibly because the capacity limit is going to exceed
				start_qm = true;
			}
		}

		if (start_qm)
		{
			if (ipsec_sa->StartQM_FlagSet == false)
			{
				c->StartQuickModeAsSoon = true;
				ipsec_sa->StartQM_FlagSet = true;
			}
		}
	}
}

// Received the L2TPv3 packet via the IPsec tunnel
void ProcL2TPv3PacketRecv(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size, bool is_tunnel_mode)
{
	UDPPACKET p;
	// Validate arguments
	if (ike == NULL || c == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	c->IsL2TPOnIPsecTunnelMode = is_tunnel_mode;

	IPsecIkeClientManageL2TPServer(ike, c);

	// Pass the received packet to the L2TP server
	p.Type = 0;
	p.Data = data;
	p.DestPort = IPSEC_PORT_L2TPV3_VIRTUAL;
	p.Size = data_size;

	if (is_tunnel_mode)
	{
		Copy(&p.DstIP, &c->TunnelModeServerIP, sizeof(IP));
		Copy(&p.SrcIP, &c->TunnelModeClientIP, sizeof(IP));
	}
	else
	{
		Copy(&p.DstIP, &c->L2TPServerIP, sizeof(IP));
		Copy(&p.SrcIP, &c->L2TPClientIP, sizeof(IP));
	}
	p.SrcPort = IPSEC_PORT_L2TPV3_VIRTUAL;

#ifdef	RAW_DEBUG
	IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, ((UCHAR *)p.Data) + 4, p.Size - 4);
#endif	// RAW_DEBUG

	ProcL2TPPacketRecv(c->L2TP, &p);
}

// An EtherIP packet has been received via an IPsec tunnel
void ProcIPsecEtherIPPacketRecv(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size, bool is_tunnel_mode)
{
	BLOCK *b;
	// Validate arguments
	if (ike == NULL || c == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	c->IsEtherIPOnIPsecTunnelMode = is_tunnel_mode;

	IPsecIkeClientManageEtherIPServer(ike, c);

	b = NewBlock(data, data_size, 0);

	EtherIPProcRecvPackets(c->EtherIP, b);

	Free(b);
}

// An UDP packet has been received via the IPsec tunnel
void ProcIPsecUdpPacketRecv(IKE_SERVER *ike, IKE_CLIENT *c, UCHAR *data, UINT data_size)
{
	UDP_HEADER *u;
	UINT payload_size;
	UINT src_port, dst_port;
	UINT packet_length;
	// Validate arguments
	if (ike == NULL || c == NULL || data == NULL || data_size == 0)
	{
		return;
	}

	if (data_size <= sizeof(UDP_HEADER))
	{
		// There is no UDP header or the data is 0 bytes
		return;
	}

	// UDP header
	u = (UDP_HEADER *)data;

	packet_length = Endian16(u->PacketLength);

	if (packet_length <= sizeof(UDP_HEADER))
	{
		return;
	}

	payload_size = packet_length - sizeof(UDP_HEADER);

	if (payload_size == 0)
	{
		// No data
		return;
	}

	if (data_size < (sizeof(UDP_HEADER) + payload_size))
	{
		// Data is not followed
		return;
	}

	src_port = Endian16(u->SrcPort);
	dst_port = Endian16(u->DstPort);

	if (dst_port == IPSEC_PORT_L2TP)
	{
		UDPPACKET p;
		// A L2TP packet has been received
		IPsecIkeClientManageL2TPServer(ike, c);

		// Update Port number
		c->L2TPClientPort = src_port;

		// Pass the received packet to the L2TP server
		p.Type = 0;
		p.Data = data + sizeof(UDP_HEADER);
		p.DestPort = IPSEC_PORT_L2TP;
		Copy(&p.DstIP, &c->L2TPServerIP, sizeof(IP));
		p.Size = payload_size;
		Copy(&p.SrcIP, &c->L2TPClientIP, sizeof(IP));
		p.SrcPort = IPSEC_PORT_L2TP;

		ProcL2TPPacketRecv(c->L2TP, &p);

		//Debug("IPsec UDP Recv: %u <= %u %u\n", dst_port, src_port, p.Size);

#ifdef	RAW_DEBUG
		IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, p.Data, p.Size);
#endif	// RAW_DEBUG
	}
}

// Send a raw packet for debugging
void IPsecIkeSendUdpForDebug(UINT dst_port, UINT dst_ip, void *data, UINT size)
{
	SOCK *s = NewUDP(0);
	IP d;

	SetIP(&d, dst_ip, dst_ip, dst_ip, dst_ip);

	SendTo(s, &d, dst_port, data, size);

	ReleaseSock(s);
}

// L2TP packet transmission (via IPsec SA tunnel)
void IPsecIkeClientSendL2TPPackets(IKE_SERVER *ike, IKE_CLIENT *c, L2TP_SERVER *l2tp)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || c == NULL || l2tp == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(l2tp->SendPacketList);i++)
	{
		UDPPACKET *u = LIST_DATA(l2tp->SendPacketList, i);

		if (u->SrcPort != IPSEC_PORT_L2TPV3_VIRTUAL)
		{
			// L2TP UDP packet transmission
			IPsecSendUdpPacket(ike, c, IPSEC_PORT_L2TP, c->L2TPClientPort,
				u->Data, u->Size);
		}
		else
		{
			// L2TPv3 special IP packet transmission
			IPsecSendPacketByIkeClient(ike, c, u->Data, u->Size, IPSEC_IP_PROTO_L2TPV3);

#ifdef	RAW_DEBUG
			IPsecIkeSendUdpForDebug(IPSEC_PORT_L2TP, 1, ((UCHAR *)u->Data) + 4, u->Size - 4);
#endif	// RAW_DEBUG
		}

		FreeUdpPacket(u);
	}

	DeleteAll(l2tp->SendPacketList);
}

// Manage the L2TP server that is associated with the IKE_CLIENT
void IPsecIkeClientManageL2TPServer(IKE_SERVER *ike, IKE_CLIENT *c)
{
	L2TP_SERVER *l2tp;
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return;
	}

	if (c->L2TP == NULL)
	{
		UINT crypt_block_size = IKE_MAX_BLOCK_SIZE;

		if (c->CurrentIpSecSaRecv != NULL)
		{
			crypt_block_size = c->CurrentIpSecSaRecv->TransformSetting.Crypto->BlockSize;
		}

		c->L2TP = NewL2TPServerEx(ike->Cedar, ike, IsIP6(&c->ClientIP), crypt_block_size);
		c->L2TP->IkeClient = c;

		Copy(&c->L2TPServerIP, &c->ServerIP, sizeof(IP));
		Copy(&c->L2TPClientIP, &c->ClientIP, sizeof(IP));

		if (c->CurrentIpSecSaRecv != NULL)
		{
			Format(c->L2TP->CryptName, sizeof(c->L2TP->CryptName),
				"IPsec - %s (%u bits)",
				c->CurrentIpSecSaRecv->TransformSetting.Crypto->Name,
				c->CurrentIpSecSaRecv->TransformSetting.CryptoKeySize * 8);
		}

		Debug("IKE_CLIENT 0x%X: L2TP Server Started.\n", c);

		IPsecLog(ike, c, NULL, NULL, "LI_L2TP_SERVER_STARTED");
	}

	l2tp = c->L2TP;

	if (l2tp->Interrupts == NULL)
	{
		l2tp->Interrupts = ike->Interrupts;
	}

	if (l2tp->SockEvent == NULL)
	{
		SetL2TPServerSockEvent(l2tp, ike->SockEvent);
	}

	l2tp->Now = ike->Now;
}

// Manage the EtherIP server that is associated with the IKE_CLIENT
void IPsecIkeClientManageEtherIPServer(IKE_SERVER *ike, IKE_CLIENT *c)
{
	ETHERIP_SERVER *s;
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return;
	}

	if (c->EtherIP == NULL)
	{
		char crypt_name[MAX_SIZE];
		UINT crypt_block_size = IKE_MAX_BLOCK_SIZE;

		Zero(crypt_name, sizeof(crypt_name));

		if (c->CurrentIpSecSaRecv != NULL)
		{
			Format(crypt_name, sizeof(crypt_name),
				"IPsec - %s (%u bits)",
				c->CurrentIpSecSaRecv->TransformSetting.Crypto->Name,
				c->CurrentIpSecSaRecv->TransformSetting.CryptoKeySize * 8);

			crypt_block_size = c->CurrentIpSecSaRecv->TransformSetting.Crypto->BlockSize;
		}

		c->EtherIP = NewEtherIPServer(ike->Cedar, ike->IPsec, ike,
			&c->ClientIP, c->ClientPort,
			&c->ServerIP, c->ServerPort, crypt_name,
			c->IsEtherIPOnIPsecTunnelMode, crypt_block_size, c->ClientId,
			++ike->CurrentEtherId);

		Debug("IKE_CLIENT 0x%X: EtherIP Server Started.\n", c);

		IPsecLog(ike, c, NULL, NULL, NULL, "LI_ETHERIP_SERVER_STARTED", ike->CurrentEtherId);
	}
	else
	{
		StrCpy(c->EtherIP->ClientId, sizeof(c->EtherIP->ClientId), c->ClientId);
	}

	s = c->EtherIP;

	if (s->Interrupts == NULL)
	{
		s->Interrupts = ike->Interrupts;
	}

	if (s->SockEvent == NULL)
	{
		SetEtherIPServerSockEvent(s, ike->SockEvent);
	}

	s->Now = ike->Now;
}

// EtherIP packet transmission (via IPsec SA tunnel)
void IPsecIkeClientSendEtherIPPackets(IKE_SERVER *ike, IKE_CLIENT *c, ETHERIP_SERVER *s)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || c == NULL || s == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(s->SendPacketList);i++)
	{
		BLOCK *b = LIST_DATA(s->SendPacketList, i);

		// Packet transmission
		IPsecSendPacketByIkeClient(ike, c, b->Buf, b->Size, IPSEC_IP_PROTO_ETHERIP);

		FreeBlock(b);
	}

	DeleteAll(s->SendPacketList);
}

// Handle the deletion payload
void ProcDeletePayload(IKE_SERVER *ike, IKE_CLIENT *c, IKE_PACKET_DELETE_PAYLOAD *d)
{
	// Validate arguments
	if (ike == NULL || c == NULL || d == NULL)
	{
		return;
	}

	if (d->ProtocolId == IKE_PROTOCOL_ID_IPSEC_ESP)
	{
		UINT i;
		// Remove the IPsec SA
		for (i = 0;i < LIST_NUM(d->SpiList);i++)
		{
			BUF *b = LIST_DATA(d->SpiList, i);

			if (b->Size == 4)
			{
				UINT spi = READ_UINT(b->Buf);
				MarkIPsecSaAsDeleted(ike, SearchIPsecSaBySpi(ike, c, spi));
			}
		}
	}
	else if (d->ProtocolId == IKE_PROTOCOL_ID_IKE)
	{
		UINT i;
		// Remove the IKE SA
		for (i = 0;i < LIST_NUM(d->SpiList);i++)
		{
			BUF *b = LIST_DATA(d->SpiList, i);

			if (b->Size == 16)
			{
				UINT64 v1 = READ_UINT64(((UCHAR *)b->Buf) + 0);
				UINT64 v2 = READ_UINT64(((UCHAR *)b->Buf) + 8);

				IKE_SA *sa = FindIkeSaByResponderCookie(ike, v2);

				if (sa != NULL && sa->IkeClient == c)
				{
					MarkIkeSaAsDeleted(ike, sa);
				}
			}
		}
	}
}

// Mark the IKE_CLIENT for deletion
void MarkIkeClientAsDeleted(IKE_SERVER *ike, IKE_CLIENT *c)
{
	char client_ip_str[MAX_SIZE];
	char server_ip_str[MAX_SIZE];
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return;
	}

	if (c->Deleting)
	{
		return;
	}

	ike->StateHasChanged = true;

	c->Deleting = true;

	IPToStr(client_ip_str, sizeof(client_ip_str), &c->ClientIP);
	IPToStr(server_ip_str, sizeof(server_ip_str), &c->ServerIP);

	Debug("Deleting IKE_CLIENT: %p: %s:%u -> %s:%u\n", c, client_ip_str, c->ClientPort, server_ip_str, c->ServerPort);

	IPsecLog(ike, c, NULL, NULL, "LI_DELETE_IKE_CLIENT");
}

// Mark the IKE SA for deletion
void MarkIkeSaAsDeleted(IKE_SERVER *ike, IKE_SA *sa)
{
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	if (sa->Deleting)
	{
		return;
	}

	ike->StateHasChanged = true;

	sa->Deleting = true;

	Debug("IKE SA %I64u - %I64u has been marked as being deleted.\n", sa->InitiatorCookie, sa->ResponderCookie);

	SendDeleteIkeSaPacket(ike, sa->IkeClient, sa->InitiatorCookie, sa->ResponderCookie);

	IPsecLog(ike, NULL, sa, NULL, "LI_DELETE_IKE_SA");
}

// Mark the IPsec SA for deletion
void MarkIPsecSaAsDeleted(IKE_SERVER *ike, IPSECSA *sa)
{
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	if (sa->Deleting)
	{
		return;
	}

	ike->StateHasChanged = true;

	sa->Deleting = true;

	Debug("IPsec SA 0x%X has been marked as being deleted.\n", sa->Spi);

	SendDeleteIPsecSaPacket(ike, sa->IkeClient, sa->Spi);

	IPsecLog(ike, NULL, NULL, sa, "LI_DELETE_IPSEC_SA");
}

// IPsec SA Deletion packet transmission process
void SendDeleteIPsecSaPacket(IKE_SERVER *ike, IKE_CLIENT *c, UINT spi)
{
	IKE_PACKET_PAYLOAD *payload;
	BUF *buf;
	// Validate arguments
	if (ike == NULL || c == NULL || spi == 0)
	{
		return;
	}

	buf = NewBuf();
	WriteBufInt(buf, spi);

	payload = IkeNewDeletePayload(IKE_PROTOCOL_ID_IPSEC_ESP, NewListSingle(buf));

	SendInformationalExchangePacket(ike, c, payload);
}

// IKE SA deletion packet transmission process
void SendDeleteIkeSaPacket(IKE_SERVER *ike, IKE_CLIENT *c, UINT64 init_cookie, UINT64 resp_cookie)
{
	IKE_PACKET_PAYLOAD *payload;
	BUF *buf;
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return;
	}

	buf = NewBuf();
	WriteBufInt64(buf, init_cookie);
	WriteBufInt64(buf, resp_cookie);

	payload = IkeNewDeletePayload(IKE_PROTOCOL_ID_IKE, NewListSingle(buf));

	SendInformationalExchangePacket(ike, c, payload);
}

// Information exchange packet transmission process
void SendInformationalExchangePacket(IKE_SERVER *ike, IKE_CLIENT *c, IKE_PACKET_PAYLOAD *payload)
{
	SendInformationalExchangePacketEx(ike, c, payload, false, 0, 0);
}
void SendInformationalExchangePacketEx(IKE_SERVER *ike, IKE_CLIENT *c, IKE_PACKET_PAYLOAD *payload, bool force_plain, UINT64 init_cookie, UINT64 resp_cookie)
{
	IKE_SA *sa;
	IKE_PACKET *ps;
	LIST *payload_list;
	UCHAR dummy_hash_data[IKE_MAX_HASH_SIZE];
	IKE_PACKET_PAYLOAD *hash_payload;
	BUF *ps_buf;
	UINT after_hash_offset, after_hash_size;
	BUF *ps_buf_after_hash;
	BUF *tmp_buf;
	UCHAR hash[IKE_MAX_HASH_SIZE];
	IKE_CRYPTO_PARAM cp;
	bool plain = false;
	// Validate arguments
	if (ike == NULL || c == NULL || payload == NULL)
	{
		IkeFreePayload(payload);
		return;
	}

	sa = c->CurrentIkeSa;
	if (sa == NULL)
	{
		plain = true;
	}

	if (force_plain)
	{
		plain = true;
	}

	if (plain && (init_cookie == 0 && resp_cookie == 0))
	{
		init_cookie = Rand64();
		resp_cookie = 0;
	}

	payload_list = NewListFast(NULL);

	Zero(dummy_hash_data, sizeof(dummy_hash_data));

	// Hash payload
	if (plain == false)
	{
		hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, dummy_hash_data, sa->HashSize);
		Add(payload_list, hash_payload);
	}

	// Body
	Add(payload_list, payload);

	// Packet creation
	ps = IkeNew((plain ? init_cookie : sa->InitiatorCookie), (plain ? resp_cookie : sa->ResponderCookie),
		IKE_EXCHANGE_TYPE_INFORMATION, false, false, false,
		GenerateNewMessageId(ike), payload_list);

	if (plain == false)
	{
		// Build a temporary packet
		ps_buf = IkeBuild(ps, NULL);

		// Get the payload after the hash part
		after_hash_offset = sizeof(IKE_HEADER) + hash_payload->BitArray->Size + sizeof(IKE_COMMON_HEADER);
		after_hash_size = ((ps_buf->Size > after_hash_offset) ? (ps_buf->Size - after_hash_offset) : 0);

		ps_buf_after_hash = MemToBuf(((UCHAR *)ps_buf->Buf) + after_hash_offset, after_hash_size);
		FreeBuf(ps_buf);

		// Calculate the hash
		tmp_buf = NewBuf();
		WriteBufInt(tmp_buf, ps->MessageId);
		WriteBufBuf(tmp_buf, ps_buf_after_hash);
		IkeHMac(sa->TransformSetting.Hash, hash, sa->SKEYID_a, sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
		FreeBuf(tmp_buf);

		// Overwrite the hash
		Copy(hash_payload->Payload.Hash.Data->Buf, hash, sa->HashSize);

		ps->FlagEncrypted = true;
		FreeBuf(ps_buf_after_hash);
	}

	// Packet reply
	Zero(&cp, sizeof(cp));

	if (plain == false)
	{
		cp.Key = sa->CryptoKey;
		IkeCalcPhase2InitialIv(cp.Iv, sa, ps->MessageId);
	}

	ps_buf = IkeBuild(ps, &cp);

	IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &c->ServerIP, c->ServerPort,
		&c->ClientIP, c->ClientPort,
		ps_buf->Buf, ps_buf->Size);

#ifdef	RAW_DEBUG
	IkeDebugUdpSendRawPacket(ps);
#endif	// RAW_DEBUG

	Free(ps_buf);

	IkeFree(ps);
}

// Information exchange packet reception process
void ProcIkeInformationalExchangePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
{
	IKE_CLIENT *c;
	IKE_SA *ike_sa;
	// Validate arguments
	if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0 || header->ResponderCookie == 0
		|| header->MessageId == 0 || header->FlagEncrypted == false)
	{
		return;
	}

	c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);

	if (c == NULL)
	{
		return;
	}

	ike_sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);

	if (ike_sa != NULL && ike_sa->Established)
	{
		IKE_PACKET *pr;
		IKE_CRYPTO_PARAM cp;

		// Packet decoding
		Zero(&cp, sizeof(cp));
		cp.Key = ike_sa->CryptoKey;
		IkeCalcPhase2InitialIv(cp.Iv, ike_sa, header->MessageId);

		pr = IkeParse(p->Data, p->Size, &cp);
#ifdef	RAW_DEBUG
		IkeDebugUdpSendRawPacket(pr);
#endif	// RAW_DEBUG
		if (pr != NULL)
		{
			// Get the hash payload
			IKE_PACKET_PAYLOAD *hash_payload;

			hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
			if (hash_payload != NULL)
			{
				// Get the payload after the hash
				UINT header_and_hash_size = sizeof(IKE_COMMON_HEADER) + hash_payload->BitArray->Size;
				void *after_hash_data = ((UCHAR *)pr->DecryptedPayload->Buf) + header_and_hash_size;
				if (pr->DecryptedPayload->Size > header_and_hash_size)
				{
					UINT after_hash_size = pr->DecryptedPayload->Size - header_and_hash_size;
					UCHAR hash1[IKE_MAX_HASH_SIZE];
					BUF *hash1_buf;

					hash1_buf = NewBuf();
					WriteBufInt(hash1_buf, header->MessageId);
					WriteBuf(hash1_buf, after_hash_data, after_hash_size);

					IkeHMac(ike_sa->TransformSetting.Hash, hash1, ike_sa->SKEYID_a, ike_sa->HashSize,
						hash1_buf->Buf, hash1_buf->Size);

					// Compare the hash value
					if (IkeCompareHash(hash_payload, hash1, ike_sa->HashSize))
					{
						UINT i, num;
						// Handle the deletion payload
						num = IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_DELETE);
						for (i = 0;i < num;i++)
						{
							IKE_PACKET_PAYLOAD *payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_DELETE, i);
							IKE_PACKET_DELETE_PAYLOAD *del = &payload->Payload.Delete;

							ProcDeletePayload(ike, c, del);
						}
						num = IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_NOTICE);
						// Handle the notification payload
						for (i = 0;i < num;i++)
						{
							IKE_PACKET_PAYLOAD *payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NOTICE, i);
							IKE_PACKET_NOTICE_PAYLOAD *n = &payload->Payload.Notice;

							if (n->MessageType == IKE_NOTICE_DPD_REQUEST || n->MessageType == IKE_NOTICE_DPD_RESPONSE)
							{
								if (n->MessageData != NULL && n->MessageData->Size == sizeof(UINT))
								{
									UINT seq_no = READ_UINT(n->MessageData->Buf);

									if (n->Spi->Size == (sizeof(UINT64) * 2))
									{
										UINT64 init_cookie = READ_UINT64(((UCHAR *)n->Spi->Buf));
										UINT64 resp_cookie = READ_UINT64(((UCHAR *)n->Spi->Buf) + sizeof(UINT64));

										if (init_cookie != 0 && resp_cookie != 0)
										{
											IKE_SA *found_ike_sa = SearchIkeSaByCookie(ike, init_cookie, resp_cookie);

											if (found_ike_sa != NULL && found_ike_sa->IkeClient == c)
											{
												if (n->MessageType == IKE_NOTICE_DPD_REQUEST)
												{
													// Return the DPD Response (ACK) for the DPD Request
													SendInformationalExchangePacket(ike, c,
														IkeNewNoticeDpdPayload(true, init_cookie, resp_cookie,
														seq_no));
												}

												// Update the status of the IKE SA
												found_ike_sa->LastCommTick = ike->Now;
												ike_sa->LastCommTick = ike->Now;
												found_ike_sa->IkeClient->LastCommTick = ike->Now;
												ike_sa->IkeClient->LastCommTick = ike->Now;
												ike_sa->IkeClient->CurrentIkeSa = ike_sa;
											}
										}
									}
								}
							}
						}
					}

					FreeBuf(hash1_buf);
				}
			}

			IkeFree(pr);
		}
	}
}

// Create a new message ID
UINT GenerateNewMessageId(IKE_SERVER *ike)
{
	UINT ret;
	// Validate arguments
	if (ike == NULL)
	{
		return 0;
	}

	while (true)
	{
		ret = Rand32();

		if (ret != 0 && ret != 0xffffffff)
		{
			UINT i;
			bool ok = true;

			for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
			{
				IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

				if (sa->MessageId == ret)
				{
					ok = false;
					break;
				}
			}

			if (ok)
			{
				return ret;
			}
		}
	}
}

// Start the quick mode
void StartQuickMode(IKE_SERVER *ike, IKE_CLIENT *c)
{
	IPSEC_SA_TRANSFORM_SETTING setting;
	IKE_SA *ike_sa;
	UINT message_id;
	UCHAR iv[IKE_MAX_BLOCK_SIZE];
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return;
	}

	if (IsZero(&c->CachedTransformSetting, sizeof(IPSEC_SA_TRANSFORM_SETTING)))
	{
		// Cached transform setting does not exist
		Debug("Error: c->CachedTransformSetting is not existing.\n");
		return;
	}

	ike_sa = c->CurrentIkeSa;
	if (ike_sa == NULL)
	{
		return;
	}

	IPsecLog(ike, NULL, ike_sa, NULL, "LI_START_QM_FROM_SERVER");

	Copy(&setting, &c->CachedTransformSetting, sizeof(IPSEC_SA_TRANSFORM_SETTING));

	message_id = GenerateNewMessageId(ike);

	IkeCalcPhase2InitialIv(iv, ike_sa, message_id);

#ifdef	FORCE_LIFETIME_QM
	setting.LifeSeconds = FORCE_LIFETIME_QM;
#endif	// FORCE_LIFETIME_QM

	if (true)
	{
		IKE_PACKET *ps;
		LIST *payload_list;
		IKE_PACKET_PAYLOAD *send_hash_payload;
		IKE_PACKET_PAYLOAD *send_sa_payload;
		IKE_PACKET_PAYLOAD *send_proposal_payload;
		IKE_PACKET_PAYLOAD *send_transform_payload;
		IKE_PACKET_PAYLOAD *send_rand_payload;
		IKE_PACKET_PAYLOAD *send_key_payload = NULL;
		IKE_PACKET_PAYLOAD *send_id_1 = NULL, *send_id_2 = NULL;
		UINT shared_key_size = 0;
		UCHAR *shared_key = NULL;
		BUF *initiator_rand;
		IPSECSA *ipsec_sa_s_c, *ipsec_sa_c_s;
		BUF *ps_buf;
		UINT after_hash_offset, after_hash_size;
		BUF *ps_buf_after_hash;
		BUF *tmp_buf;
		UINT spi;
		UINT spi_be;
		UCHAR hash1[IKE_MAX_HASH_SIZE];
		DH_CTX *dh = NULL;
		UCHAR dummy_hash_data[IKE_MAX_HASH_SIZE];

		initiator_rand = RandBuf(IKE_SA_RAND_SIZE);

		if (setting.Dh != NULL)
		{
			// Generate DH
			dh = IkeDhNewCtx(setting.Dh);

			if (dh != NULL)
			{
				send_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE,
					dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
			}
		}

		Zero(dummy_hash_data, sizeof(dummy_hash_data));

		// Dummy hash value
		payload_list = NewListFast(NULL);
		send_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, dummy_hash_data, ike_sa->HashSize);
		Add(payload_list, send_hash_payload);

		// Determine the SPI
		spi = GenerateNewIPsecSaSpi(ike, 0);
		spi_be = Endian32(spi);

		// SA
		send_transform_payload = TransformSettingToTransformPayloadForIPsec(ike, &setting);
		send_proposal_payload = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IPSEC_ESP, &spi_be, sizeof(spi_be),
			NewListSingle(send_transform_payload));
		send_sa_payload = IkeNewSaPayload(NewListSingle(send_proposal_payload));
		Add(payload_list, send_sa_payload);

		// Random number
		send_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, initiator_rand->Buf, initiator_rand->Size);
		Add(payload_list, send_rand_payload);

		// Key exchange
		if (send_key_payload != NULL)
		{
			Add(payload_list, send_key_payload);
		}

		if (c->SendID1andID2)
		{
			// Add the ID payload
			if (setting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_1 || setting.CapsuleMode == IKE_P2_CAPSULE_NAT_TUNNEL_2)
			{
				UCHAR zero[32];

				Zero(zero, sizeof(zero));

				// Tunnel Mode
				send_id_1 = IkeNewIdPayload((IsIP4(&c->ServerIP) ? IKE_ID_IPV4_ADDR_SUBNET : IKE_ID_IPV6_ADDR_SUBNET),
					0, 0,
					zero, (IsIP4(&c->ServerIP) ? 8 : 32));

				send_id_2 = IkeNewIdPayload(c->SendID1_Type,
					c->SendID1_Protocol, c->SendID1_Port,
					c->SendID1_Buf->Buf, c->SendID1_Buf->Size);
			}
			else
			{
				// Transport mode
				// Specify in the reverse order in which the client has been specified
				send_id_2 = IkeNewIdPayload(c->SendID1_Type,
					c->SendID1_Protocol, c->SendID1_Port,
					c->SendID1_Buf->Buf, c->SendID1_Buf->Size);

				send_id_1 = IkeNewIdPayload(c->SendID2_Type,
					c->SendID2_Protocol, c->SendID2_Port,
					c->SendID2_Buf->Buf, c->SendID2_Buf->Size);
			}

			Add(payload_list, send_id_1);
			Add(payload_list, send_id_2);
		}

		if (true)
		{
			// NAT-OA payload
			if (c->SendNatOaDraft1)
			{
				Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT, &c->ServerIP));
			}

			if (c->SendNatOaDraft2)
			{
				Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT_2, &c->ServerIP));
			}

			if (c->SendNatOaRfc)
			{
				Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ClientIP));
				Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ServerIP));
			}
		}

		// Build a packet
		ps = IkeNew(ike_sa->InitiatorCookie, ike_sa->ResponderCookie, IKE_EXCHANGE_TYPE_QUICK,
			false, false, false, message_id, payload_list);

		// Build a temporary packet
		ps_buf = IkeBuild(ps, NULL);

		// Get the payload after the hash part
		after_hash_offset = sizeof(IKE_HEADER) + send_hash_payload->BitArray->Size + sizeof(IKE_COMMON_HEADER);
		after_hash_size = ((ps_buf->Size > after_hash_offset) ? (ps_buf->Size - after_hash_offset) : 0);

		ps_buf_after_hash = MemToBuf(((UCHAR *)ps_buf->Buf) + after_hash_offset, after_hash_size);
		FreeBuf(ps_buf);

		// Calculate the hash #1
		tmp_buf = NewBuf();
		WriteBufInt(tmp_buf, message_id);
		WriteBufBuf(tmp_buf, ps_buf_after_hash);
		IkeHMac(ike_sa->TransformSetting.Hash, hash1, ike_sa->SKEYID_a, ike_sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
		FreeBuf(tmp_buf);

		// Overwrite hash #1
		Copy(send_hash_payload->Payload.Hash.Data->Buf, hash1, ike_sa->HashSize);

		// Create an IPsec SA
		ipsec_sa_c_s = NewIPsecSa(ike, c, ike_sa, true, message_id, false, iv, spi,
			initiator_rand->Buf, initiator_rand->Size, NULL, 0,
			&setting, shared_key, shared_key_size);

		ipsec_sa_s_c = NewIPsecSa(ike, c, ike_sa, true, message_id, true, iv, 0,
			initiator_rand->Buf, initiator_rand->Size, NULL, 0,
			&setting, shared_key, shared_key_size);

		ipsec_sa_c_s->PairIPsecSa = ipsec_sa_s_c;
		ipsec_sa_s_c->PairIPsecSa = ipsec_sa_c_s;

		ipsec_sa_s_c->Dh = dh;

		Insert(ike->IPsecSaList, ipsec_sa_c_s);
		Insert(ike->IPsecSaList, ipsec_sa_s_c);

		// Packet transmission
		ps->FlagEncrypted = true;
		IPsecSaSendPacket(ike, ipsec_sa_s_c, ps);
		ipsec_sa_s_c->NumResends = 3;
#ifdef	RAW_DEBUG
		IkeDebugUdpSendRawPacket(ps);
#endif	// RAW_DEBUG

		IkeFree(ps);
		Free(shared_key);
		FreeBuf(ps_buf_after_hash);
		FreeBuf(initiator_rand);
	}
}

// Process the quick mode received packet
void ProcIkeQuickModePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
{
	IKE_CLIENT *c;
	IKE_SA *ike_sa;
	// Validate arguments
	if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0 || header->ResponderCookie == 0
		|| header->MessageId == 0 || header->FlagEncrypted == false)
	{
		return;
	}

	c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);

	if (c == NULL)
	{
		return;
	}

	ike_sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);

	if (ike_sa == NULL)
	{
		// IKE SA does not exist
		SendInformationalExchangePacketEx(ike, c, IkeNewNoticeErrorInvalidCookiePayload(header->InitiatorCookie,
			header->ResponderCookie), true, header->InitiatorCookie, header->ResponderCookie);
	}

	if (ike_sa != NULL && ike_sa->Established)
	{
		// Update the status of the IKE SA
		ike_sa->LastCommTick = ike->Now;
		ike_sa->IkeClient->LastCommTick = ike->Now;
		ike_sa->IkeClient->CurrentIkeSa = ike_sa;

		// Search whether the Message ID is already in the database
		if (SearchIPsecSaByMessageId(ike, c, header->MessageId) == NULL)
		{
			IKE_PACKET *pr;
			IKE_CRYPTO_PARAM cp;

			// Message ID does not exist. Start a new Quick Mode session
			Zero(&cp, sizeof(cp));
			cp.Key = ike_sa->CryptoKey;
			IkeCalcPhase2InitialIv(cp.Iv, ike_sa, header->MessageId);

			pr = IkeParse(p->Data, p->Size, &cp);
#ifdef	RAW_DEBUG
			IkeDebugUdpSendRawPacket(pr);
#endif	// RAW_DEBUG
			if (pr != NULL)
			{
				// Get the hash payload
				IKE_PACKET_PAYLOAD *hash_payload;

				hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
				if (hash_payload != NULL)
				{
					// Get the payload after the hash
					UINT header_and_hash_size = sizeof(IKE_COMMON_HEADER) + hash_payload->BitArray->Size;
					void *after_hash_data = ((UCHAR *)pr->DecryptedPayload->Buf) + header_and_hash_size;
					if (pr->DecryptedPayload->Size > header_and_hash_size)
					{
						UINT after_hash_size = pr->DecryptedPayload->Size - header_and_hash_size;
						UCHAR hash1[IKE_MAX_HASH_SIZE];
						BUF *hash1_buf;

						hash1_buf = NewBuf();
						WriteBufInt(hash1_buf, header->MessageId);
						WriteBuf(hash1_buf, after_hash_data, after_hash_size);

						IkeHMac(ike_sa->TransformSetting.Hash, hash1, ike_sa->SKEYID_a, ike_sa->HashSize,
							hash1_buf->Buf, hash1_buf->Size);

						// Compare the hash value
						if (IkeCompareHash(hash_payload, hash1, ike_sa->HashSize))
						{
							IKE_PACKET_PAYLOAD *sa_payload, *rand_payload, *key_payload, *id_payload_1, *id_payload_2;

							// Get the payload of other
							sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
							rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
							key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
							id_payload_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
							id_payload_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 1);

							if (sa_payload != NULL && rand_payload != NULL)
							{
								IPSEC_SA_TRANSFORM_SETTING setting;

								Zero(&setting, sizeof(setting));

								// Interpret the SA payload
								if (GetBestTransformSettingForIPsecSa(ike, pr, &setting, &p->DstIP) && (GetNumberOfIPsecSaOfIkeClient(ike, c) <= IKE_QUOTA_MAX_SA_PER_CLIENT))
								{
									// Appropriate transform setting is selected
									Debug("P2 Transform: %s %s %s(%u) %u %u\n",
										(setting.Dh == NULL ? NULL : setting.Dh->Name), setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
										setting.LifeKilobytes, setting.LifeSeconds);

#ifdef	FORCE_LIFETIME_QM
									setting.LifeSeconds = FORCE_LIFETIME_QM;
#endif	// FORCE_LIFETIME_QM

									// Cache the transform attribute value
									Copy(&c->CachedTransformSetting, &setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));

									// Check the key exchange payload if the PFS is specified
									if (setting.Dh == NULL || (setting.Dh != NULL && key_payload != NULL &&
										key_payload->Payload.KeyExchange.Data->Size <= setting.Dh->KeySize))
									{
										// Create a payload for response
										IKE_PACKET *ps;
										LIST *payload_list;
										IKE_PACKET_PAYLOAD *send_hash_payload;
										IKE_PACKET_PAYLOAD *send_sa_payload;
										IKE_PACKET_PAYLOAD *send_proposal_payload;
										IKE_PACKET_PAYLOAD *send_transform_payload;
										IKE_PACKET_PAYLOAD *send_rand_payload;
										IKE_PACKET_PAYLOAD *send_key_payload = NULL;
										IKE_PACKET_PAYLOAD *send_id_1 = NULL, *send_id_2 = NULL;
										UCHAR dummy_hash_data[IKE_MAX_HASH_SIZE];
										DH_CTX *dh = NULL;
										UINT shared_key_size = 0;
										UCHAR *shared_key = NULL;
										BUF *initiator_rand, *responder_rand;
										IPSECSA *ipsec_sa_s_c, *ipsec_sa_c_s;
										BUF *ps_buf;
										UINT after_hash_offset, after_hash_size;
										BUF *ps_buf_after_hash;
										BUF *tmp_buf;
										UINT spi;
										UINT spi_be;
										UCHAR hash2[IKE_MAX_HASH_SIZE];
										UCHAR hash3[IKE_MAX_HASH_SIZE];
										UCHAR zero = 0;

										IPsecLog(ike, NULL, ike_sa, NULL, "LI_START_QM_FROM_CLIENT");

										initiator_rand = CloneBuf(rand_payload->Payload.Rand.Data);
										responder_rand = RandBuf(IKE_SA_RAND_SIZE);

										if (setting.Dh != NULL)
										{
											// Calculate DH
											dh = IkeDhNewCtx(setting.Dh);
											shared_key_size = (dh == NULL ? 0 : dh->Size);
											shared_key = ZeroMalloc(shared_key_size);

											if (DhCompute(dh, shared_key, key_payload->Payload.KeyExchange.Data->Buf, key_payload->Payload.KeyExchange.Data->Size))
											{
												// DH calculation success
												Debug("P2 DH Ok.\n");

												send_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE,
													dh->MyPublicKey->Buf, dh->MyPublicKey->Size);

												IkeDhFreeCtx(dh);
											}
											else
											{
												// DH calculation failure
												Debug("P2 DhCompute failed.\n");

												shared_key = NULL;
												Free(shared_key);
												shared_key_size = 0;

												IPsecLog(ike, NULL, ike_sa, NULL, "LI_QM_DH_ERROR");
											}
										}

										Zero(dummy_hash_data, sizeof(dummy_hash_data));

										// Dummy hash value
										payload_list = NewListFast(NULL);
										send_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, dummy_hash_data, ike_sa->HashSize);
										Add(payload_list, send_hash_payload);

										// Determine the SPI
										spi = GenerateNewIPsecSaSpi(ike, setting.SpiServerToClient);
										spi_be = Endian32(spi);

										// SA
										send_transform_payload = TransformSettingToTransformPayloadForIPsec(ike, &setting);
										send_proposal_payload = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IPSEC_ESP, &spi_be, sizeof(spi_be),
											NewListSingle(send_transform_payload));
										send_sa_payload = IkeNewSaPayload(NewListSingle(send_proposal_payload));
										Add(payload_list, send_sa_payload);

										// Random number
										send_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, responder_rand->Buf, responder_rand->Size);
										Add(payload_list, send_rand_payload);

										// Key exchange
										if (send_key_payload != NULL)
										{
											Add(payload_list, send_key_payload);
										}

										// ID
										if (id_payload_1 != NULL && id_payload_2 != NULL)
										{
											send_id_1 = IkeNewIdPayload(id_payload_1->Payload.Id.Type,
												id_payload_1->Payload.Id.ProtocolId, id_payload_1->Payload.Id.Port,
												id_payload_1->Payload.Id.IdData->Buf, id_payload_1->Payload.Id.IdData->Size);

											send_id_2 = IkeNewIdPayload(id_payload_2->Payload.Id.Type,
												id_payload_2->Payload.Id.ProtocolId, id_payload_2->Payload.Id.Port,
												id_payload_2->Payload.Id.IdData->Buf, id_payload_2->Payload.Id.IdData->Size);

											Add(payload_list, send_id_1);
											Add(payload_list, send_id_2);

											if (c->SendID1_Buf != NULL)
											{
												FreeBuf(c->SendID1_Buf);
											}

											if (c->SendID2_Buf != NULL)
											{
												FreeBuf(c->SendID2_Buf);
											}

											c->SendID1_Type = id_payload_1->Payload.Id.Type;
											c->SendID1_Protocol = id_payload_1->Payload.Id.ProtocolId;
											c->SendID1_Port = id_payload_1->Payload.Id.Port;
											c->SendID1_Buf = CloneBuf(id_payload_1->Payload.Id.IdData);

											c->SendID2_Type = id_payload_2->Payload.Id.Type;
											c->SendID2_Protocol = id_payload_2->Payload.Id.ProtocolId;
											c->SendID2_Port = id_payload_2->Payload.Id.Port;
											c->SendID2_Buf = CloneBuf(id_payload_2->Payload.Id.IdData);

											c->SendID1andID2 = true;
										}
										else
										{
											c->SendID1andID2 = false;
										}

										if (true)
										{
											// Reply if NAT-OA payload is presented by the client
											IKE_PACKET_PAYLOAD *nat_oa_draft1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA_DRAFT, 0);
											IKE_PACKET_PAYLOAD *nat_oa_draft2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA_DRAFT_2, 0);
											IKE_PACKET_PAYLOAD *nat_oa_rfc_0 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA, 0);
											IKE_PACKET_PAYLOAD *nat_oa_rfc_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_OA, 1);

											c->SendNatOaDraft1 = c->SendNatOaDraft2 = c->SendNatOaRfc = false;

											c->ShouldCalcChecksumForUDP = false;

											if (nat_oa_draft1 != NULL)
											{
												Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT, &c->ServerIP));
												c->SendNatOaDraft1 = true;

												if (IsIP4(&nat_oa_draft1->Payload.NatOa.IpAddress) == IsIP4(&c->ServerIP))
												{
													Copy(&c->TransportModeClientIP, &nat_oa_draft1->Payload.NatOa.IpAddress, sizeof(IP));
													Copy(&c->TransportModeServerIP, &c->ServerIP, sizeof(IP));

													c->ShouldCalcChecksumForUDP = true;
												}
											}

											if (nat_oa_draft2 != NULL)
											{
												Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA_DRAFT_2, &c->ServerIP));
												c->SendNatOaDraft2 = true;

												if (IsIP4(&nat_oa_draft2->Payload.NatOa.IpAddress) == IsIP4(&c->ServerIP))
												{
													Copy(&c->TransportModeClientIP, &nat_oa_draft2->Payload.NatOa.IpAddress, sizeof(IP));
													Copy(&c->TransportModeServerIP, &c->ServerIP, sizeof(IP));

													c->ShouldCalcChecksumForUDP = true;
												}
											}

											if (nat_oa_rfc_0 != NULL && nat_oa_rfc_1 != NULL)
											{
												Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ClientIP));
												Add(payload_list, IkeNewNatOaPayload(IKE_PAYLOAD_NAT_OA, &c->ServerIP));
												c->SendNatOaRfc = true;

												if (IsIP4(&nat_oa_rfc_0->Payload.NatOa.IpAddress) == IsIP4(&c->ServerIP))
												{
													Copy(&c->TransportModeClientIP, &nat_oa_rfc_0->Payload.NatOa.IpAddress, sizeof(IP));
													Copy(&c->TransportModeServerIP, &c->ServerIP, sizeof(IP));

													c->ShouldCalcChecksumForUDP = true;
												}
											}
										}

										// Build a packet
										ps = IkeNew(ike_sa->InitiatorCookie, ike_sa->ResponderCookie, IKE_EXCHANGE_TYPE_QUICK,
											false, false, false, header->MessageId, payload_list);

										// Build a temporary packet
										ps_buf = IkeBuild(ps, NULL);

										// Get the payload after the hash part
										after_hash_offset = sizeof(IKE_HEADER) + send_hash_payload->BitArray->Size + sizeof(IKE_COMMON_HEADER);
										after_hash_size = ((ps_buf->Size > after_hash_offset) ? (ps_buf->Size - after_hash_offset) : 0);

										ps_buf_after_hash = MemToBuf(((UCHAR *)ps_buf->Buf) + after_hash_offset, after_hash_size);
										FreeBuf(ps_buf);

										// Calculate the hash #2
										tmp_buf = NewBuf();
										WriteBufInt(tmp_buf, header->MessageId);
										WriteBufBuf(tmp_buf, initiator_rand);
										WriteBufBuf(tmp_buf, ps_buf_after_hash);
										IkeHMac(ike_sa->TransformSetting.Hash, hash2, ike_sa->SKEYID_a, ike_sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
										FreeBuf(tmp_buf);

										// Calculate the hash #3
										tmp_buf = NewBuf();
										WriteBuf(tmp_buf, &zero, 1);
										WriteBufInt(tmp_buf, header->MessageId);
										WriteBufBuf(tmp_buf, initiator_rand);
										WriteBufBuf(tmp_buf, responder_rand);
										IkeHMac(ike_sa->TransformSetting.Hash, hash3, ike_sa->SKEYID_a, ike_sa->HashSize, tmp_buf->Buf, tmp_buf->Size);
										FreeBuf(tmp_buf);

										// Create an IPsec SA
										ipsec_sa_c_s = NewIPsecSa(ike, c, ike_sa, false, header->MessageId, false, cp.NextIv, spi,
											initiator_rand->Buf, initiator_rand->Size, responder_rand->Buf, responder_rand->Size,
											&setting, shared_key, shared_key_size);
										ipsec_sa_s_c = NewIPsecSa(ike, c, ike_sa, false, header->MessageId, true, cp.NextIv, setting.SpiServerToClient,
											initiator_rand->Buf, initiator_rand->Size, responder_rand->Buf, responder_rand->Size,
											&setting, shared_key, shared_key_size);

										ipsec_sa_c_s->PairIPsecSa = ipsec_sa_s_c;
										ipsec_sa_s_c->PairIPsecSa = ipsec_sa_c_s;

										Insert(ike->IPsecSaList, ipsec_sa_c_s);
										Insert(ike->IPsecSaList, ipsec_sa_s_c);

										Copy(ipsec_sa_c_s->Hash3, hash3, ike_sa->HashSize);

										// Overwrite hash #2
										Copy(send_hash_payload->Payload.Hash.Data->Buf, hash2, ike_sa->HashSize);

										// Packet reply
										ps->FlagEncrypted = true;
										IPsecSaSendPacket(ike, ipsec_sa_s_c, ps);
										IkeSaSendPacket(ike, ike_sa, NULL);

#ifdef	RAW_DEBUG
										IkeDebugUdpSendRawPacket(ps);
#endif	// RAW_DEBUG

										IkeFree(ps);
										Free(shared_key);
										FreeBuf(ps_buf_after_hash);
										FreeBuf(initiator_rand);
										FreeBuf(responder_rand);
									}
								}
								else
								{
									// No appropriate transform setting
									Debug("No Appropriate Transform was Found.\n");

									IPsecLog(ike, NULL, ike_sa, NULL, "LI_IPSEC_NO_TRANSFORM");

									SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(true, header->InitiatorCookie, header->ResponderCookie));
								}
							}
						}
						else
						{
							Debug("QM-1: Hash 1 is invalid.\n");
						}

						FreeBuf(hash1_buf);
					}
				}

				IkeFree(pr);
			}
		}
		else
		{
			// Get the IPsec SA
			IPSECSA *ipsec_sa_cs = SearchIPsecSaByMessageId(ike, c, header->MessageId);
			if (ipsec_sa_cs != NULL)
			{
				IPSECSA *ipsec_sa_sc = ipsec_sa_cs->PairIPsecSa;
				if (ipsec_sa_sc != NULL)
				{
					if (ipsec_sa_sc->Established == false && ipsec_sa_cs->Established == false)
					{
						IKE_PACKET *pr = IPsecSaRecvPacket(ike, ipsec_sa_cs, p->Data, p->Size);

#ifdef	RAW_DEBUG
						IkeDebugUdpSendRawPacket(pr);
#endif	// RAW_DEBUG

						if (pr != NULL)
						{
							if (ipsec_sa_cs->Initiated == false)
							{
								// Initiator is client-side
								// Check hash3 payload
								IKE_PACKET_PAYLOAD *hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);

								if (hash_payload != NULL)
								{
									BUF *hash_buf = hash_payload->Payload.Hash.Data;
									if (hash_buf != NULL)
									{
										if (hash_buf->Size == ipsec_sa_cs->IkeSa->HashSize)
										{
											if (Cmp(hash_buf->Buf, ipsec_sa_cs->Hash3, hash_buf->Size) == 0)
											{
												ipsec_sa_cs->Established = ipsec_sa_sc->Established = true;
												ipsec_sa_cs->EstablishedTick = ipsec_sa_sc->EstablishedTick = ike->Now;
												ipsec_sa_cs->LastCommTick = ipsec_sa_sc->LastCommTick = ike->Now;

												c->CurrentIpSecSaRecv = ipsec_sa_cs;
												c->CurrentIpSecSaSend = ipsec_sa_sc;

												Debug("IPsec SA 0x%X & 0x%X Established.\n",
													ipsec_sa_cs->Spi,
													ipsec_sa_sc->Spi);

												IPsecLog(ike, NULL, NULL, ipsec_sa_sc, "LI_IPSEC_SA_ESTABLISHED");

												IPsecSaSendPacket(ike, ipsec_sa_sc, NULL);
											}
											else
											{
												Debug("QM-3: Hash 3 is invalid.\n");
											}
										}
									}
								}
							}
							else
							{
								// Initiator is server-side
								// Get hash payload
								IKE_PACKET_PAYLOAD *hash_payload;

								hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);
								if (hash_payload != NULL && ipsec_sa_sc->InitiatorRand != NULL)
								{
									// Get the payload after the hash
									UINT header_and_hash_size = sizeof(IKE_COMMON_HEADER) + hash_payload->BitArray->Size;
									void *after_hash_data = ((UCHAR *)pr->DecryptedPayload->Buf) + header_and_hash_size;
									if (pr->DecryptedPayload->Size > header_and_hash_size)
									{
										UINT after_hash_size = pr->DecryptedPayload->Size - header_and_hash_size;
										UCHAR hash2[IKE_MAX_HASH_SIZE];
										BUF *hash2_buf;

										hash2_buf = NewBuf();
										WriteBufInt(hash2_buf, header->MessageId);
										WriteBufBuf(hash2_buf, ipsec_sa_sc->InitiatorRand);
										WriteBuf(hash2_buf, after_hash_data, after_hash_size);

										IkeHMac(ipsec_sa_sc->SKEYID_Hash, hash2, ipsec_sa_sc->SKEYID_a, ipsec_sa_sc->SKEYID_Hash->HashSize,
											hash2_buf->Buf, hash2_buf->Size);

										FreeBuf(hash2_buf);

										// Compare the hash value
										if (IkeCompareHash(hash_payload, hash2, ike_sa->HashSize))
										{
											IKE_PACKET_PAYLOAD *sa_payload, *rand_payload, *key_payload, *id_payload_1, *id_payload_2;

											// Get the payload of other
											sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
											rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
											key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
											id_payload_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
											id_payload_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 1);

											if (sa_payload != NULL && rand_payload != NULL)
											{
												IPSEC_SA_TRANSFORM_SETTING setting;

												// Interpret the SA payload
												if (GetBestTransformSettingForIPsecSa(ike, pr, &setting, &p->DstIP))
												{
													// Appropriate transform setting is selected
													Debug("P2 Transform: %s %s %s(%u) %u %u\n",
														(setting.Dh == NULL ? NULL : setting.Dh->Name), setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
														setting.LifeKilobytes, setting.LifeSeconds);

#ifdef	FORCE_LIFETIME_QM
													setting.LifeSeconds = FORCE_LIFETIME_QM;
#endif	// FORCE_LIFETIME_QM

													// Check the key exchange payload if the PFS is specified
													if (setting.Dh == NULL || (setting.Dh != NULL && key_payload != NULL && ipsec_sa_sc->Dh != NULL &&
														key_payload->Payload.KeyExchange.Data->Size <= setting.Dh->KeySize))
													{
														IKE_PACKET *ps;
														LIST *payload_list;
														IKE_PACKET_PAYLOAD *send_hash_payload;
														IKE_PACKET_PAYLOAD *send_key_payload = NULL;
														IKE_PACKET_PAYLOAD *send_id_1 = NULL, *send_id_2 = NULL;
														DH_CTX *dh = NULL;
														UINT shared_key_size = 0;
														UCHAR *shared_key = NULL;
														BUF *initiator_rand, *responder_rand;
														BUF *tmp_buf;
														UCHAR hash3[IKE_MAX_HASH_SIZE];
														char tmp[MAX_SIZE];
														UCHAR zero = 0;

														initiator_rand = ipsec_sa_sc->InitiatorRand;
														responder_rand = CloneBuf(rand_payload->Payload.Rand.Data);

														if (setting.Dh != NULL)
														{
															// Calculate DH
															DH_CTX *dh = ipsec_sa_sc->Dh;

															shared_key_size = (dh == NULL ? 0 : dh->Size);
															shared_key = ZeroMalloc(shared_key_size);

															if (DhCompute(dh, shared_key, key_payload->Payload.KeyExchange.Data->Buf, key_payload->Payload.KeyExchange.Data->Size))
															{
																// DH calculation success
																Debug("P2 DH Ok.\n");
															}
															else
															{
																// DH calculation failure
																Debug("P2 DhCompute failed.\n");

																shared_key = NULL;
																Free(shared_key);
																shared_key_size = 0;

																IPsecLog(ike, NULL, ike_sa, NULL, "LI_QM_DH_ERROR");
															}
														}

														// Update the information of IPsec SA
														if (shared_key != NULL)
														{
															ipsec_sa_sc->SharedKey = NewBuf(shared_key, shared_key_size);
															ipsec_sa_cs->SharedKey = NewBuf(shared_key, shared_key_size);
														}

														ipsec_sa_sc->Spi = setting.SpiServerToClient;
														IPsecLog(ike, NULL, NULL, ipsec_sa_sc, "LI_IPSEC_SA_SPI_SET", ipsec_sa_sc->Spi);
														ike->IPsecSaList->sorted = false;

														ipsec_sa_sc->ResponderRand = CloneBuf(responder_rand);
														ipsec_sa_cs->ResponderRand = CloneBuf(responder_rand);

														Copy(&ipsec_sa_sc->TransformSetting, &setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));
														Copy(&ipsec_sa_cs->TransformSetting, &setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));

														ipsec_sa_sc->Established = true;
														ipsec_sa_cs->Established = true;

														IPsecLog(ike, NULL, NULL, ipsec_sa_sc, "LI_IPSEC_SA_ESTABLISHED");

														ipsec_sa_sc->LastCommTick = ike->Now;
														ipsec_sa_cs->LastCommTick = ike->Now;

														c->CurrentIpSecSaRecv = ipsec_sa_cs;
														c->CurrentIpSecSaSend = ipsec_sa_sc;

														// Calculate the KEYMAT
														IPsecCalcKeymat(ike, ipsec_sa_sc->SKEYID_Hash, ipsec_sa_sc->KeyMat, sizeof(ipsec_sa_sc->KeyMat),
															ipsec_sa_sc->SKEYID_d, ipsec_sa_sc->SKEYID_Hash->HashSize, IKE_PROTOCOL_ID_IPSEC_ESP,
															ipsec_sa_sc->Spi, initiator_rand->Buf, initiator_rand->Size,
															responder_rand->Buf, responder_rand->Size,
															shared_key, shared_key_size);

														IPsecCalcKeymat(ike, ipsec_sa_cs->SKEYID_Hash, ipsec_sa_cs->KeyMat, sizeof(ipsec_sa_cs->KeyMat),
															ipsec_sa_cs->SKEYID_d, ipsec_sa_cs->SKEYID_Hash->HashSize, IKE_PROTOCOL_ID_IPSEC_ESP,
															ipsec_sa_cs->Spi, initiator_rand->Buf, initiator_rand->Size,
															responder_rand->Buf, responder_rand->Size,
															shared_key, shared_key_size);

														IkeFreeKey(ipsec_sa_sc->CryptoKey);
														IkeFreeKey(ipsec_sa_cs->CryptoKey);

														ipsec_sa_sc->CryptoKey = IkeNewKey(setting.Crypto, ipsec_sa_sc->KeyMat, setting.CryptoKeySize);
														ipsec_sa_cs->CryptoKey = IkeNewKey(setting.Crypto, ipsec_sa_cs->KeyMat, setting.CryptoKeySize);

														Copy(ipsec_sa_sc->HashKey, ipsec_sa_sc->KeyMat + setting.CryptoKeySize, setting.Hash->HashSize);
														Copy(ipsec_sa_cs->HashKey, ipsec_sa_cs->KeyMat + setting.CryptoKeySize, setting.Hash->HashSize);

														BinToStrEx(tmp, sizeof(tmp), ipsec_sa_sc->KeyMat, ipsec_sa_sc->TransformSetting.CryptoKeySize);
														Debug("  KEYMAT (SC): %s\n", tmp);

														BinToStrEx(tmp, sizeof(tmp), ipsec_sa_cs->KeyMat, ipsec_sa_cs->TransformSetting.CryptoKeySize);
														Debug("  KEYMAT (CS): %s\n", tmp);

														Debug("IPsec SA 0x%X & 0x%X Established (Server is Initiator).\n",
															ipsec_sa_cs->Spi,
															ipsec_sa_sc->Spi);

														// Calculate the hash #3
														tmp_buf = NewBuf();
														WriteBuf(tmp_buf, &zero, 1);
														WriteBufInt(tmp_buf, header->MessageId);
														WriteBufBuf(tmp_buf, initiator_rand);
														WriteBufBuf(tmp_buf, responder_rand);
														IkeHMac(ipsec_sa_cs->SKEYID_Hash, hash3, ipsec_sa_cs->SKEYID_a, ipsec_sa_cs->SKEYID_Hash->HashSize, tmp_buf->Buf, tmp_buf->Size);
														FreeBuf(tmp_buf);

														// Return the hash #3
														send_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, hash3, ipsec_sa_cs->SKEYID_Hash->HashSize);

														payload_list = NewListSingle(send_hash_payload);
														ps = IkeNew(ike_sa->InitiatorCookie, ike_sa->ResponderCookie,
															IKE_EXCHANGE_TYPE_QUICK, true, false, false, header->MessageId, payload_list);

														IPsecSaSendPacket(ike, ipsec_sa_sc, ps);
#ifdef	RAW_DEBUG
														IkeDebugUdpSendRawPacket(ps);
#endif	// RAW_DEBUG
														ipsec_sa_sc->NumResends = 3;

														if (false)
														{
															UINT i;

															for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
															{
																IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

																if (sa != ipsec_sa_sc && sa != ipsec_sa_cs)
																{
																	MarkIPsecSaAsDeleted(ike, sa);
																}
															}
														}

														IkeFree(ps);

														// Release the memory
														FreeBuf(responder_rand);
													}
												}
												else
												{
													// No appropriate transform setting
													Debug("No Appropriate Transform was Found.\n");

													IPsecLog(ike, NULL, ike_sa, NULL, "LI_IPSEC_NO_TRANSFORM");

													SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(true, header->InitiatorCookie, header->ResponderCookie));
												}
											}
										}
									}
								}
							}
							IkeFree(pr);
						}
					}
				}
			}
		}
	}
}

// Calculate the KEYMAT
void IPsecCalcKeymat(IKE_SERVER *ike, IKE_HASH *h, void *dst, UINT dst_size, void *skeyid_d_data, UINT skeyid_d_size, UCHAR protocol, UINT spi, void *rand_init_data, UINT rand_init_size,
					 void *rand_resp_data, UINT rand_resp_size, void *df_key_data, UINT df_key_size)
{
	BUF *k;
	BUF *ret;
	// Validate arguments
	if (ike == NULL || dst == NULL || h == NULL || rand_init_data == NULL || rand_resp_data == NULL||
		(df_key_size != 0 && df_key_data == NULL))
	{
		return;
	}

	ret = NewBuf();

	k = NULL;

	while (true)
	{
		BUF *tmp = NewBuf();
		UCHAR hash[IKE_MAX_HASH_SIZE];

		if (k != NULL)
		{
			WriteBufBuf(tmp, k);
		}

		if (df_key_data != NULL)
		{
			WriteBuf(tmp, df_key_data, df_key_size);
		}

		WriteBuf(tmp, &protocol, 1);

		WriteBufInt(tmp, spi);

		WriteBuf(tmp, rand_init_data, rand_init_size);
		WriteBuf(tmp, rand_resp_data, rand_resp_size);

		if (k != NULL)
		{
			FreeBuf(k);
		}

		IkeHMac(h, hash, skeyid_d_data, skeyid_d_size, tmp->Buf, tmp->Size);

		FreeBuf(tmp);

		k = MemToBuf(hash, h->HashSize);

		WriteBufBuf(ret, k);

		if (ret->Size >= dst_size)
		{
			break;
		}
	}

	Copy(dst, ret->Buf, dst_size);

	FreeBuf(ret);
	FreeBuf(k);
}

// Search for IPsec SA from Message ID
IPSECSA *SearchIPsecSaByMessageId(IKE_SERVER *ike, IKE_CLIENT *c, UINT message_id)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || c == NULL || message_id == 0)
	{
		return NULL;
	}

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

		if (sa->IkeClient == c)
		{
			if (sa->MessageId == message_id)
			{
				if (sa->ServerToClient == false)
				{
					if (sa->Established == false)
					{
						return sa;
					}
				}
			}
		}
	}

	return NULL;
}

// Search for IPsec SA from SPI value
IPSECSA *SearchClientToServerIPsecSaBySpi(IKE_SERVER *ike, UINT spi)
{
	IPSECSA t;
	// Validate arguments
	if (ike == NULL || spi == 0)
	{
		return NULL;
	}

	t.ServerToClient = false;
	t.Spi = spi;

	return Search(ike->IPsecSaList, &t);
}
IPSECSA *SearchIPsecSaBySpi(IKE_SERVER *ike, IKE_CLIENT *c, UINT spi)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || c == NULL || spi == 0)
	{
		return NULL;
	}

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

		if (sa->Spi == spi)
		{
			if (sa->IkeClient == c)
			{
				return sa;
			}
		}
	}

	return NULL;
}

// Search an IKE SA from the value of the Cookie
IKE_SA *SearchIkeSaByCookie(IKE_SERVER *ike, UINT64 init_cookie, UINT64 resp_cookie)
{
	UINT i;
	// Validate arguments
	if (ike == NULL)
	{
		return NULL;
	}

	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

		if (sa->InitiatorCookie == init_cookie && sa->ResponderCookie == resp_cookie)
		{
			return sa;
		}
	}

	return NULL;
}

// Generate the SPI value of new IPsec SA
UINT GenerateNewIPsecSaSpi(IKE_SERVER *ike, UINT counterpart_spi)
{
	UINT ret;
	// Validate arguments
	if (ike == NULL)
	{
		return 0;
	}

	while (true)
	{
		ret = Rand32();

		if (ret != counterpart_spi)
		{
			if (ret >= 4096 && ret != INFINITE)
			{
				if (SearchClientToServerIPsecSaBySpi(ike, ret) == NULL)
				{
					return ret;
				}
			}
		}
	}
}

// Calculate the initial IV for Phase 2
void IkeCalcPhase2InitialIv(void *iv, IKE_SA *sa, UINT message_id)
{
	BUF *b;
	UCHAR hash[IKE_MAX_HASH_SIZE];
	// Validate arguments
	if (iv == NULL || sa == NULL)
	{
		return;
	}

	message_id = Endian32(message_id);

	b = NewBuf();
	WriteBuf(b, sa->Iv, sa->BlockSize);
	WriteBuf(b, &message_id, sizeof(UINT));

	IkeHash(sa->TransformSetting.Hash, hash, b->Buf, b->Size);

	Copy(iv, hash, sa->TransformSetting.Crypto->BlockSize);

	FreeBuf(b);
}

// Create a new IPsec SA
IPSECSA *NewIPsecSa(IKE_SERVER *ike, IKE_CLIENT *c, IKE_SA *ike_sa, bool initiate, UINT message_id, bool server_to_client, void *iv, UINT spi, void *init_rand_data, UINT init_rand_size, void *res_rand_data, UINT res_rand_size, IPSEC_SA_TRANSFORM_SETTING *setting, void *shared_key_data, UINT shared_key_size)
{
	IPSECSA *sa;
	char tmp[MAX_SIZE];
	UINT total_key_size;
	// Validate arguments
	if (ike == NULL || c == NULL || ike_sa == NULL || message_id == 0 || iv == NULL || setting == NULL ||
		(shared_key_data == NULL && shared_key_size != 0))
	{
		return NULL;
	}

	sa = ZeroMalloc(sizeof(IPSECSA));

	if (server_to_client == false)
	{
		ike->CurrentIPsecSaId++;
	}
	sa->Id = ike->CurrentIPsecSaId;

	sa->IkeClient = c;
	sa->IkeSa = ike_sa;

	sa->MessageId = message_id;
	sa->FirstCommTick = ike->Now;
	sa->LastCommTick = ike->Now;
	sa->Initiated = initiate;

	sa->ServerToClient = server_to_client;

	sa->Spi = spi;

	sa->SKEYID_Hash = ike_sa->TransformSetting.Hash;
	Copy(sa->SKEYID_a, ike_sa->SKEYID_a, sa->SKEYID_Hash->HashSize);
	Copy(sa->SKEYID_d, ike_sa->SKEYID_d, sa->SKEYID_Hash->HashSize);

	sa->InitiatorRand = MemToBuf(init_rand_data, init_rand_size);

	if (initiate == false)
	{
		sa->ResponderRand = MemToBuf(res_rand_data, res_rand_size);
	}

	Copy(sa->Iv, iv, ike_sa->BlockSize);

	Copy(&sa->TransformSetting, setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));

	if (shared_key_data != NULL)
	{
		sa->SharedKey = MemToBuf(shared_key_data, shared_key_size);
	}

	total_key_size = sa->TransformSetting.CryptoKeySize + sa->TransformSetting.Hash->HashSize;

	if (initiate == false)
	{
		IPsecCalcKeymat(ike, ike_sa->TransformSetting.Hash, sa->KeyMat, total_key_size,
			ike_sa->SKEYID_d, ike_sa->HashSize, IKE_PROTOCOL_ID_IPSEC_ESP, spi, sa->InitiatorRand->Buf,
			sa->InitiatorRand->Size, sa->ResponderRand->Buf, sa->ResponderRand->Size,
			shared_key_data, shared_key_size);

		sa->CryptoKey = IkeNewKey(sa->TransformSetting.Crypto, sa->KeyMat, sa->TransformSetting.CryptoKeySize);

		Copy(sa->HashKey, sa->KeyMat + sa->TransformSetting.CryptoKeySize, sa->TransformSetting.Hash->HashSize);
	}

	Debug("New IPsec SA (StoC = %u): 0x%X 0x%X (%s %s %s(%u) %u %u)\n",
		sa->ServerToClient,
		sa->MessageId,
		sa->Spi,
		(setting->Dh == NULL ? NULL : setting->Dh->Name), setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize,
		setting->LifeKilobytes, setting->LifeSeconds);

	IPsecLog(ike, c, NULL, sa, "LI_NEW_IPSEC_SA",
		(sa->ServerToClient ? _UU("LI_TAG_SERVER_TO_CLIENT") : _UU("LI_TAG_CLIENT_TO_SERVER")),
		sa->Spi,
		(setting->Dh == NULL ? NULL : setting->Dh->Name), setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize * 8,
		setting->LifeKilobytes, setting->LifeSeconds);

	Rand(sa->EspIv, sizeof(sa->EspIv));

	if (initiate == false)
	{
		BinToStrEx(tmp, sizeof(tmp), sa->KeyMat, sa->TransformSetting.CryptoKeySize);
		Debug("  KEYMAT: %s\n", tmp);
	}

	// Set the expiration time
	if (setting->LifeSeconds != 0)
	{
		const UINT64 span = (UINT64)((UINT64)setting->LifeSeconds * (UINT64)1000) + (UINT64)IKE_SOFT_EXPIRES_MARGIN;
		sa->ExpiresHardTick = ike->Now + span;
		sa->ExpiresSoftTick = ike->Now + span;
		//sa->ExpiresSoftTick = ike->Now + (UINT64)5000;

		AddInterrupt(ike->Interrupts, sa->ExpiresSoftTick);
	}

	return sa;
}

// Treat aggressive mode packet reception
void ProcIkeAggressiveModePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
{
	IKE_CLIENT *c;
	char tmp[MAX_SIZE];
	// Validate arguments
	if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0)
	{
		return;
	}

	c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);

	if (c == NULL)
	{
		return;
	}

	if (header->ResponderCookie == 0)
	{
		// Start process of the state 1
		IKE_CAPS caps;
		IKE_SA *sa;
		IKE_PACKET *pr = IkeParse(p->Data, p->Size, NULL);

		if (pr != NULL)
		{
			// Determine the CAPS
			IkeCheckCaps(&caps, pr);
			if (caps.MS_L2TPIPSecVPNClient || caps.MS_NT5_ISAKMP_OAKLEY || caps.MS_Vid_InitialContact)
			{
				c->IsMicrosoft = true;
			}

			if ((caps.NatTraversalDraftIetf || caps.NatTraversalRfc3947) || (IsUdpPortOpened(ike->IPsec->UdpListener, &p->DstIP, IPSEC_PORT_IPSEC_ESP_RAW)))
			{
				sa = FindIkeSaByEndPointAndInitiatorCookie(ike, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, header->InitiatorCookie, IKE_SA_AGGRESSIVE_MODE);

				if (sa == NULL)
				{
					// Check whether there is acceptable SA parameters by analyzing proposed parameters
					IKE_SA_TRANSFORM_SETTING setting;

					if (GetBestTransformSettingForIkeSa(ike, pr, &setting) && (GetNumberOfIkeSaOfIkeClient(ike, c) <= IKE_QUOTA_MAX_SA_PER_CLIENT))
					{
						IKE_PACKET_PAYLOAD *tp;
						IKE_PACKET_PAYLOAD *pp;
						IKE_PACKET_PAYLOAD *sap;
						IKE_PACKET_PAYLOAD *client_sa_payload;
						IKE_PACKET_PAYLOAD *your_key_payload;
						IKE_PACKET_PAYLOAD *your_rand_payload;
						IKE_PACKET_PAYLOAD *your_id_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);

						// Appropriate transform setting is selected
						Debug("P1 Transform: %s %s %s(%u) %u %u\n",
							setting.Dh->Name, setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
							setting.LifeKilobytes, setting.LifeSeconds);

						// Receive a key exchange packet
						your_key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
						your_rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);
						if (your_key_payload != NULL && your_rand_payload != NULL && your_id_payload != NULL)
						{
							// Check the key payload
							BUF *your_key_buf = your_key_payload->Payload.KeyExchange.Data;
							BUF *your_rand_buf = your_rand_payload->Payload.Rand.Data;

							// DH generation
							DH_CTX *dh = IkeDhNewCtx(setting.Dh);
							UINT shared_key_size = (dh == NULL ? 0 : dh->Size);
							UCHAR *shared_key = ZeroMalloc(shared_key_size);

							// DH calculation
							if (DhCompute(dh, shared_key, your_key_buf->Buf, your_key_buf->Size))
							{
								IKE_PACKET *ps;
								LIST *payload_list;
								IKE_PACKET_PAYLOAD *my_key_payload;
								IKE_PACKET_PAYLOAD *my_rand_payload;
								BUF *nat_buf1, *nat_buf2;
								BUF *iv_buf;
								UCHAR iv_hashed_data[IKE_MAX_HASH_SIZE];
								UCHAR initiator_hash[IKE_MAX_HASH_SIZE];
								BUF *b;
								IKE_PACKET_PAYLOAD *my_id_payload, *my_hash_payload;
								UCHAR responder_hash[IKE_MAX_HASH_SIZE];
								BUF *idir_b;
								IKE_PACKET_PAYLOAD *your_nat_d_1 = NULL;
								IKE_PACKET_PAYLOAD *your_nat_d_2 = NULL;

								// Create an IKE SA
								sa = NewIkeSa(ike, c, header->InitiatorCookie, IKE_SA_AGGRESSIVE_MODE, &setting);
								Copy(&sa->Caps, &caps, sizeof(IKE_CAPS));
								sa->State= IKE_SA_AM_STATE_1_SA;
								Insert(ike->IkeSaList, sa);

								sa->HashSize = sa->TransformSetting.Hash->HashSize;
								sa->KeySize = sa->TransformSetting.CryptoKeySize;
								sa->BlockSize = sa->TransformSetting.Crypto->BlockSize;

								// Get the Caps additionally
								if (sa->Caps.NatTraversalRfc3947)
								{
									sa->Caps.UsingNatTraversalRfc3947 = true;

									your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 0);
									your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 1);
								}
								else if (sa->Caps.NatTraversalDraftIetf)
								{
									sa->Caps.UsingNatTraversalDraftIetf = true;

									your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 0);
									your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 1);
								}

								// Calculation success
								sa->DhSharedKey = MemToBuf(shared_key, shared_key_size);
								sa->InitiatorRand = RandBuf(IKE_SA_RAND_SIZE);
								sa->ResponderRand = CloneBuf(your_rand_buf);

								// Save a bit array of SA payload presented by the client
								client_sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
								sa->SAi_b = CloneBuf(client_sa_payload->BitArray);

								// Save the ID payload presented by the client
								sa->YourIDPayloadForAM = CloneBuf(your_id_payload->BitArray);

								//// Assemble the SA payload
								// Construct transform payload
								tp = TransformSettingToTransformPayloadForIke(ike, &setting);

								// Build a proposal payload
								pp = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IKE, NULL, 0, NewListSingle(tp));

								// Build the SA payload
								sap = IkeNewSaPayload(NewListSingle(pp));

								payload_list = NewListSingle(sap);

								// Send a key exchange packet
								my_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
								my_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, sa->InitiatorRand->Buf, sa->InitiatorRand->Size);

								Add(payload_list, my_key_payload);
								Add(payload_list, my_rand_payload);

								// NAT-D Packet
								// Address of the opponent. Randomize in order to be forced to use NAT
								nat_buf1 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash, Rand64(), Rand64(), &c->ClientIP, Rand16());

								// My address
								if (c->IsMicrosoft == false || (your_nat_d_1 == NULL || your_nat_d_2 == NULL || your_nat_d_1->BitArray == NULL))
								{
									// Calculate exactly
									nat_buf2 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash,
										sa->InitiatorCookie, sa->ResponderCookie, &c->ServerIP, c->ServerPort);
								}
								else
								{
									// Parrot the NAT_D payload indicating myself I got from
									// the other if it has connected from a Microsoft VPN Client
									nat_buf2 = CloneBuf(your_nat_d_1->BitArray);
								}

								// Save DH information
								sa->GXi = CloneBuf(your_key_buf);
								sa->GXr = CloneBuf(dh->MyPublicKey);

								// Calculate the key set
								IkeCalcSaKeySet(ike, sa, NULL);

								// Calculate the initiator side hash value
								b = NewBuf();
								WriteBufBuf(b, sa->GXi);
								WriteBufBuf(b, sa->GXr);
								WriteBufInt64(b, sa->InitiatorCookie);
								WriteBufInt64(b, sa->ResponderCookie);
								WriteBufBuf(b, sa->SAi_b);
								WriteBufBuf(b, sa->YourIDPayloadForAM);

								IkeHMac(sa->TransformSetting.Hash, initiator_hash, sa->SKEYID, sa->HashSize,
									b->Buf, b->Size);

								FreeBuf(b);

								Copy(sa->InitiatorHashForAM, initiator_hash, sa->HashSize);

								// Prepare the response ID payload
								// Generate the ID payload
								if (IsIP6(&sa->IkeClient->ServerIP))
								{
									// IPv6 address
									my_id_payload = IkeNewIdPayload(IKE_ID_IPV6_ADDR, 0, 0, sa->IkeClient->ServerIP.address, 16);
								}
								else
								{
									// IPv4 address
									my_id_payload = IkeNewIdPayload(IKE_ID_IPV4_ADDR, 0, 0, IPV4(sa->IkeClient->ServerIP.address), IPV4_SIZE);
								}

								// Build the ID payload tentatively
								idir_b = IkeBuildIdPayload(&my_id_payload->Payload.Id);

								b = NewBuf();
								WriteBufBuf(b, sa->GXr);
								WriteBufBuf(b, sa->GXi);
								WriteBufInt64(b, sa->ResponderCookie);
								WriteBufInt64(b, sa->InitiatorCookie);
								WriteBufBuf(b, sa->SAi_b);
								WriteBufBuf(b, idir_b);

								IkeHMac(sa->TransformSetting.Hash, responder_hash, sa->SKEYID, sa->HashSize,
									b->Buf, b->Size);

								FreeBuf(b);
								FreeBuf(idir_b);

								my_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, responder_hash, sa->HashSize);

								Add(payload_list, my_id_payload);
								Add(payload_list, my_hash_payload);

								ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_AGGRESSIVE,
									false, false, false, 0, payload_list);

								// Add the vendor ID
								IkeAddVendorIdPayloads(ps);

								// NAT-D related
								if (sa->Caps.UsingNatTraversalRfc3947)
								{
									// RFC-compliant
									Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf1->Buf, nat_buf1->Size));
									Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf2->Buf, nat_buf2->Size));
								}

								if (sa->Caps.UsingNatTraversalDraftIetf)
								{
									// Draft compliant
									Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf1->Buf, nat_buf1->Size));
									Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf2->Buf, nat_buf2->Size));
								}

								FreeBuf(nat_buf1);
								FreeBuf(nat_buf2);

								StrCpy(c->ClientId, sizeof(c->ClientId), your_id_payload->Payload.Id.StrData);
								Debug("Client ID = %s\n", c->ClientId);

								IPsecLog(ike, c, NULL, NULL, NULL, "LI_SET_CLIENT_ID", c->ClientId);

								// Initial IV setting
								iv_buf = NewBuf();
								WriteBuf(iv_buf, your_key_buf->Buf, your_key_buf->Size);
								WriteBuf(iv_buf, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
								IkeHash(sa->TransformSetting.Hash, iv_hashed_data, iv_buf->Buf, iv_buf->Size);

								BinToStrEx(tmp, sizeof(tmp), iv_hashed_data, sa->BlockSize);
								Debug("Initial IV: %s\n", tmp);

								IkeSaUpdateIv(sa, iv_hashed_data, sa->HashSize);

								FreeBuf(iv_buf);

								// Transmission
								IkeSaSendPacket(ike, sa, ps);

								IkeFree(ps);
							}
							else
							{
								// DH calculation failure
								Debug("DhCompute failed.\n");
							}

							Free(shared_key);
							DhFree(dh);
						}
					}
					else
					{
						// No appropriate transform setting
						Debug("No Appropriate Transform was Found.\n");

						IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_TRANSFORM");

						SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(false, header->InitiatorCookie, header->ResponderCookie));
					}
				}
			}
			else
			{
				// Client does not support NAT Traversal
				Debug("Client doesn't support NAT-T.\n");

				IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_NAT_T");
			}

			IkeFree(pr);
		}
	}
	else
	{
		// Process of state 2
		IKE_SA *sa;

		sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);

		if (sa == NULL)
		{
			SendInformationalExchangePacketEx(ike, c, IkeNewNoticeErrorInvalidCookiePayload(header->InitiatorCookie,
				header->ResponderCookie), true, header->InitiatorCookie, header->ResponderCookie);
		}

		if (sa != NULL && sa->Mode == IKE_SA_AGGRESSIVE_MODE)
		{
			IKE_PACKET *pr = NULL;

			sa->LastCommTick = ike->Now;

			switch (sa->State)
			{
			case IKE_SA_AM_STATE_1_SA:
				pr = IkeSaRecvPacket(ike, sa, p->Data, p->Size);
				if (pr != NULL)
				{
					IKE_PACKET_PAYLOAD *your_hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);

					if (your_hash_payload != NULL)
					{
						// Compare the hash
						if (IkeCompareHash(your_hash_payload, sa->InitiatorHashForAM, sa->HashSize))
						{
							// Transit to the established state
							Debug("IKE SA 0x%X Established.\n", sa);
							sa->State = IKE_SA_AM_STATE_2_ESTABLISHED;
							sa->EstablishedTick = ike->Now;
							sa->Established = true;
							c->CurrentIkeSa = sa;
							c->NextDpdSendTick = ike->Now + (UINT64)IKE_INTERVAL_DPD_KEEPALIVE;
							StrCpy(c->Secret, sizeof(c->Secret), sa->Secret);

							IPsecLog(ike, NULL, sa, NULL, "LI_IKE_SA_ESTABLISHED");

							IkeSaSendPacket(ike, sa, NULL);
						}
						else
						{
							Debug("IKE SA 0x%X Invalid Hash.\n", sa);
						}
					}
				}
				break;
			}

			if (pr != NULL)
			{
				IkeFree(pr);
			}
		}
	}
}

// Process of the main mode packet reception
void ProcIkeMainModePacketRecv(IKE_SERVER *ike, UDPPACKET *p, IKE_PACKET *header)
{
	IKE_CLIENT *c;
	char tmp[MAX_SIZE];
	// Validate arguments
	if (ike == NULL || p == NULL || header == NULL || header->InitiatorCookie == 0)
	{
		return;
	}

	c = SearchOrCreateNewIkeClientForIkePacket(ike, &p->SrcIP, p->SrcPort, &p->DstIP, p->DestPort, header);

	if (c == NULL)
	{
		return;
	}

	if (header->ResponderCookie == 0)
	{
		// Start process of the state 1
		IKE_CAPS caps;
		IKE_SA *sa;
		IKE_PACKET *pr = IkeParse(p->Data, p->Size, NULL);

		if (pr != NULL)
		{
			// Determine the CAPS
			IkeCheckCaps(&caps, pr);
			if (caps.MS_L2TPIPSecVPNClient || caps.MS_NT5_ISAKMP_OAKLEY || caps.MS_Vid_InitialContact)
			{
				c->IsMicrosoft = true;
			}

			if ((caps.NatTraversalDraftIetf || caps.NatTraversalRfc3947) || (IsUdpPortOpened(ike->IPsec->UdpListener, &p->DstIP, IPSEC_PORT_IPSEC_ESP_RAW)))
			{
				sa = FindIkeSaByEndPointAndInitiatorCookie(ike, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, header->InitiatorCookie, IKE_SA_MAIN_MODE);

				if (sa == NULL)
				{
					// Check whether there is acceptable SA parameters by analyzing proposed parameters
					IKE_SA_TRANSFORM_SETTING setting;

					if (GetBestTransformSettingForIkeSa(ike, pr, &setting) && (GetNumberOfIkeSaOfIkeClient(ike, c) <= IKE_QUOTA_MAX_SA_PER_CLIENT))
					{
						IKE_PACKET *ps;
						IKE_PACKET_PAYLOAD *tp;
						IKE_PACKET_PAYLOAD *pp;
						IKE_PACKET_PAYLOAD *sap;
						LIST *payload_list;
						IKE_PACKET_PAYLOAD *client_sa_payload;

						// Appropriate transform setting is selected
						Debug("P1 Transform: %s %s %s(%u) %u %u\n",
							setting.Dh->Name, setting.Hash->Name, setting.Crypto->Name, setting.CryptoKeySize,
							setting.LifeKilobytes, setting.LifeSeconds);

#ifdef	FORCE_LIFETIME_MM
						setting.LifeSeconds = FORCE_LIFETIME_MM;
#endif	// FORCE_LIFETIME_MM

						// Create an IKE SA
						sa = NewIkeSa(ike, c, header->InitiatorCookie, IKE_SA_MAIN_MODE, &setting);

						Copy(&sa->Caps, &caps, sizeof(IKE_CAPS));

						Insert(ike->IkeSaList, sa);

						// Answer the SA parameter selection results
						sa->State = IKE_SA_MM_STATE_1_SA;

						// Save a bit array of SA payload presented by the client
						client_sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
						sa->SAi_b = CloneBuf(client_sa_payload->BitArray);

						//// Assemble the SA payload
						// Construct a transform payload
						tp = TransformSettingToTransformPayloadForIke(ike, &setting);

						// Build a proposal payload
						pp = IkeNewProposalPayload(1, IKE_PROTOCOL_ID_IKE, NULL, 0, NewListSingle(tp));

						// Build a SA payload
						sap = IkeNewSaPayload(NewListSingle(pp));

						payload_list = NewListSingle(sap);

						ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_MAIN,
							false, false, false, 0, payload_list);

						// Add the vendor ID payload
						IkeAddVendorIdPayloads(ps);

						IkeSaSendPacket(ike, sa, ps);

						sa->HashSize = sa->TransformSetting.Hash->HashSize;
						sa->KeySize = sa->TransformSetting.CryptoKeySize;
						sa->BlockSize = sa->TransformSetting.Crypto->BlockSize;

						IkeFree(ps);
					}
					else
					{
						// No appropriate transform setting
						Debug("No Appropriate Transform was Found.\n");

						IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_TRANSFORM");

						SendInformationalExchangePacket(ike, c, IkeNewNoticeErrorNoProposalChosenPayload(false, header->InitiatorCookie, header->ResponderCookie));
					}
				}
				else
				{
					// Ignore for IKE SA which already exists (Because it's likely to be a re-transmission)
				}
			}
			else
			{
				// It does not support NAT Traversal
				Debug("Client doesn't support NAT-T.\n");

				IPsecLog(ike, c, NULL, NULL, "LI_IKE_NO_NAT_T");
			}
			IkeFree(pr);
		}
	}
	else
	{
		// Process of state 2 or later
		IKE_SA *sa;

		sa = FindIkeSaByResponderCookieAndClient(ike, header->ResponderCookie, c);

		if (sa == NULL)
		{
			SendInformationalExchangePacketEx(ike, c, IkeNewNoticeErrorInvalidCookiePayload(header->InitiatorCookie,
				header->ResponderCookie), true, header->InitiatorCookie, header->ResponderCookie);
		}

		if (sa != NULL && sa->Mode == IKE_SA_MAIN_MODE)
		{
			IKE_PACKET *pr = NULL;

			sa->LastCommTick = ike->Now;

			switch (sa->State)
			{
			case IKE_SA_MM_STATE_1_SA:
				pr = IkeSaRecvPacket(ike, sa, p->Data, p->Size);
				if (pr != NULL)
				{
					// Receive a key exchange packet
					IKE_PACKET_PAYLOAD *your_key_payload;
					IKE_PACKET_PAYLOAD *your_rand_payload;
					IKE_PACKET_PAYLOAD *your_nat_d_1 = NULL;
					IKE_PACKET_PAYLOAD *your_nat_d_2 = NULL;

					your_key_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_KEY_EXCHANGE, 0);
					your_rand_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_RAND, 0);

					if (IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_NAT_D) != 0)
					{
						sa->Caps.UsingNatTraversalRfc3947 = true;

						your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 0);
						your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D, 1);
					}

					if (IkeGetPayloadNum(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT) != 0)
					{
						sa->Caps.UsingNatTraversalDraftIetf = true;

						your_nat_d_1 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 0);
						your_nat_d_2 = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_NAT_D_DRAFT, 1);
					}

					if (your_key_payload != NULL && your_rand_payload != NULL)
					{
						// Check the key payload
						BUF *your_key_buf = your_key_payload->Payload.KeyExchange.Data;
						BUF *your_rand_buf = your_rand_payload->Payload.Rand.Data;

						// DH generation
						DH_CTX *dh = IkeDhNewCtx(sa->TransformSetting.Dh);
						UINT shared_key_size = (dh == NULL ? 0 : dh->Size);
						UCHAR *shared_key = ZeroMalloc(shared_key_size);

						// DH calculation
						if (DhCompute(dh, shared_key, your_key_buf->Buf, your_key_buf->Size))
						{
							IKE_PACKET *ps;
							LIST *payload_list;
							IKE_PACKET_PAYLOAD *my_key_payload;
							IKE_PACKET_PAYLOAD *my_rand_payload;
							BUF *nat_buf1, *nat_buf2;
							BUF *iv_buf;
							UCHAR iv_hashed_data[IKE_MAX_HASH_SIZE];

							// Calculation success
							sa->DhSharedKey = MemToBuf(shared_key, shared_key_size);
							sa->InitiatorRand = RandBuf(IKE_SA_RAND_SIZE);
							sa->ResponderRand = CloneBuf(your_rand_buf);

							// Send a key exchange packet
							my_key_payload = IkeNewDataPayload(IKE_PAYLOAD_KEY_EXCHANGE, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
							my_rand_payload = IkeNewDataPayload(IKE_PAYLOAD_RAND, sa->InitiatorRand->Buf, sa->InitiatorRand->Size);

							payload_list = NewListSingle(my_key_payload);
							Add(payload_list, my_rand_payload);

							// NAT-D packet
							// Address of the opponent. Randomize in order to be forced to use NAT
							nat_buf1 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash, Rand64(), Rand64(), &c->ClientIP, Rand16());
							//nat_buf1 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash, sa->InitiatorCookie, sa->ResponderCookie, &c->ClientIP, c->ClientPort);
							// My address

							if (c->IsMicrosoft == false || (your_nat_d_1 == NULL || your_nat_d_2 == NULL || your_nat_d_1->BitArray == NULL))
							{
								// Calculate exactly
								nat_buf2 = IkeCalcNatDetectHash(ike, sa->TransformSetting.Hash,
									sa->InitiatorCookie, sa->ResponderCookie, &c->ServerIP, c->ServerPort);
							}
							else
							{
								// Parrot the NAT_D payload indicating myself I got from
								// the other if it has connected from a Microsoft VPN Client
								nat_buf2 = CloneBuf(your_nat_d_1->BitArray);
							}

							if (sa->Caps.UsingNatTraversalRfc3947)
							{
								// RFC-compliant
								Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf1->Buf, nat_buf1->Size));
								Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D, nat_buf2->Buf, nat_buf2->Size));
							}

							if (sa->Caps.UsingNatTraversalDraftIetf)
							{
								// Draft compliant
								Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf1->Buf, nat_buf1->Size));
								Add(payload_list, IkeNewDataPayload(IKE_PAYLOAD_NAT_D_DRAFT, nat_buf2->Buf, nat_buf2->Size));
							}

							FreeBuf(nat_buf1);
							FreeBuf(nat_buf2);

							ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_MAIN,
								false, false, false, 0, payload_list);

							// Initial IV setting
							iv_buf = NewBuf();
							WriteBuf(iv_buf, your_key_buf->Buf, your_key_buf->Size);
							WriteBuf(iv_buf, dh->MyPublicKey->Buf, dh->MyPublicKey->Size);
							IkeHash(sa->TransformSetting.Hash, iv_hashed_data, iv_buf->Buf, iv_buf->Size);

							BinToStrEx(tmp, sizeof(tmp), iv_hashed_data, sa->BlockSize);
							Debug("Initial IV: %s\n", tmp);

							IkeSaUpdateIv(sa, iv_hashed_data, sa->HashSize);

							FreeBuf(iv_buf);

							// Save the DH information
							sa->GXi = CloneBuf(your_key_buf);
							sa->GXr = CloneBuf(dh->MyPublicKey);

							// Transmission
							IkeSaSendPacket(ike, sa, ps);

							IkeFree(ps);

							// Calculate the key set
							IkeCalcSaKeySet(ike, sa, NULL);

							sa->State = IKE_SA_MM_STATE_2_KEY;
						}
						else
						{
							// DH calculation failure
							Debug("DhCompute failed.\n");
						}

						Free(shared_key);
						DhFree(dh);
					}
				}
				break;

			case IKE_SA_MM_STATE_2_KEY:
				pr = IkeSaRecvPacket(ike, sa, p->Data, p->Size);
				if (pr != NULL && pr->FlagEncrypted)
				{
					// Receive an ID exchange packet
					IKE_PACKET_PAYLOAD *your_id_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_ID, 0);
					IKE_PACKET_PAYLOAD *your_hash_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_HASH, 0);

					if (your_id_payload && your_hash_payload)
					{
						UCHAR initiator_hash[IKE_MAX_HASH_SIZE];
						BUF *b;

						// Calculate the initiator side hash value
						b = NewBuf();
						WriteBufBuf(b, sa->GXi);
						WriteBufBuf(b, sa->GXr);
						WriteBufInt64(b, sa->InitiatorCookie);
						WriteBufInt64(b, sa->ResponderCookie);
						WriteBufBuf(b, sa->SAi_b);
						WriteBufBuf(b, your_id_payload->BitArray);

						StrCpy(c->ClientId, sizeof(c->ClientId), your_id_payload->Payload.Id.StrData);
						Debug("Client ID = %s\n", c->ClientId);
						IPsecLog(ike, c, NULL, NULL, NULL, "LI_SET_CLIENT_ID", c->ClientId);

						IkeHMac(sa->TransformSetting.Hash, initiator_hash, sa->SKEYID, sa->HashSize,
							b->Buf, b->Size);

						FreeBuf(b);

						// Hash comparison
						if (IkeCompareHash(your_hash_payload, initiator_hash, sa->HashSize))
						{
							// Generate a response packet
							IKE_PACKET *ps;
							LIST *payload_list = NewListFast(NULL);
							IKE_PACKET_PAYLOAD *my_id_payload, *my_hash_payload;
							UCHAR responder_hash[IKE_MAX_HASH_SIZE];
							BUF *idir_b;

							// Generate an ID payload
							if (IsIP6(&sa->IkeClient->ServerIP))
							{
								// IPv6 address
								my_id_payload = IkeNewIdPayload(IKE_ID_IPV6_ADDR, 0, 0, sa->IkeClient->ServerIP.address, 16);
							}
							else
							{
								// IPv4 address
								my_id_payload = IkeNewIdPayload(IKE_ID_IPV4_ADDR, 0, 0, IPV4(sa->IkeClient->ServerIP.address), IPV4_SIZE);
							}

							// Build the ID payload tentatively
							idir_b = IkeBuildIdPayload(&my_id_payload->Payload.Id);

							// Generate the hash payload
							b = NewBuf();
							WriteBufBuf(b, sa->GXr);
							WriteBufBuf(b, sa->GXi);
							WriteBufInt64(b, sa->ResponderCookie);
							WriteBufInt64(b, sa->InitiatorCookie);
							WriteBufBuf(b, sa->SAi_b);
							WriteBufBuf(b, idir_b);

							IkeHMac(sa->TransformSetting.Hash, responder_hash, sa->SKEYID, sa->HashSize,
								b->Buf, b->Size);

							FreeBuf(b);
							FreeBuf(idir_b);

							my_hash_payload = IkeNewDataPayload(IKE_PAYLOAD_HASH, responder_hash, sa->HashSize);

							Add(payload_list, my_id_payload);
							Add(payload_list, my_hash_payload);

							ps = IkeNew(sa->InitiatorCookie, sa->ResponderCookie, IKE_EXCHANGE_TYPE_MAIN, true, false,
								false, 0, payload_list);

							// Transmission
							IkeSaSendPacket(ike, sa, ps);
							sa->NumResends = 3;

							IkeFree(ps);

							StrCpy(c->ClientId, sizeof(c->ClientId), your_id_payload->Payload.Id.StrData);

							// Transit to the established state
							Debug("IKE SA 0x%X Established. Client ID=%s\n", sa, c->ClientId);
							sa->State = IKE_SA_MM_STATE_3_ESTABLISHED;
							sa->EstablishedTick = ike->Now;
							c->CurrentIkeSa = sa;
							c->NextDpdSendTick = ike->Now + (UINT64)IKE_INTERVAL_DPD_KEEPALIVE;
							StrCpy(c->Secret, sizeof(c->Secret), sa->Secret);
							sa->Established = true;

							IPsecLog(ike, NULL, sa, NULL, "LI_IKE_SA_ESTABLISHED");
						}
						else
						{
							Debug("IKE SA 0x%X Invalid Hash.\n", sa);
						}
					}
				}
				break;
			}

			if (pr != NULL)
			{
				IkeFree(pr);
			}
		}
	}
}

// Update the IV of IPsec SA
void IPsecSaUpdateIv(IPSECSA *sa, void *iv, UINT iv_size)
{
	// Validate arguments
	if (sa == NULL || iv == NULL)
	{
		return;
	}

	Copy(sa->Iv, iv, MIN(sa->IkeSa->BlockSize, iv_size));

	if (iv_size < sa->IkeSa->BlockSize)
	{
		Zero(sa->Iv + sa->IkeSa->BlockSize, sa->IkeSa->BlockSize - iv_size);
	}

	sa->IsIvExisting = true;
}

// Update the IV of the IKE SA
void IkeSaUpdateIv(IKE_SA *sa, void *iv, UINT iv_size)
{
	// Validate arguments
	if (sa == NULL || iv == NULL)
	{
		return;
	}

	Copy(sa->Iv, iv, MIN(sa->BlockSize, iv_size));

	if (iv_size < sa->BlockSize)
	{
		Zero(sa->Iv + sa->BlockSize, sa->BlockSize - iv_size);
	}

	sa->IsIvExisting = true;
}

// Calculate the key set of the IKE SA
void IkeCalcSaKeySet(IKE_SERVER *ike, IKE_SA *sa, char *secret)
{
	BUF *secret_buf;
	BUF *rand_buf;
	BUF *d_buf, *a_buf, *e_buf;
	UCHAR u;
	IKE_HASH *h;
	char tmp[MAX_SIZE];
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	h = sa->TransformSetting.Hash;

	// Calculation of SKEYID
	StrCpy(sa->Secret, sizeof(sa->Secret), secret == NULL ? ike->Secret : secret);
	secret_buf = IkeStrToPassword(sa->Secret);
	rand_buf = CloneBuf(sa->ResponderRand);
	SeekBufToEnd(rand_buf);
	BinToStrEx(tmp, sizeof(tmp), rand_buf->Buf, rand_buf->Size);
	Debug("ResponderRand: %s\n", tmp);
	BinToStrEx(tmp, sizeof(tmp), sa->InitiatorRand->Buf, sa->InitiatorRand->Size);
	Debug("InitiatorRand: %s\n", tmp);

	WriteBufBuf(rand_buf, sa->InitiatorRand);

	IkeHMacBuf(h, sa->SKEYID, secret_buf, rand_buf);

	BinToStrEx(tmp, sizeof(tmp), sa->SKEYID, sa->HashSize);
	Debug("SKEYID: %s\n", tmp);

	// SKEYID_d
	d_buf = CloneBuf(sa->DhSharedKey);
	SeekBufToEnd(d_buf);
	WriteBufInt64(d_buf, sa->InitiatorCookie);
	WriteBufInt64(d_buf, sa->ResponderCookie);
	u = 0;
	WriteBuf(d_buf, &u, 1);
	IkeHMac(h, sa->SKEYID_d, sa->SKEYID, sa->HashSize, d_buf->Buf, d_buf->Size);

	BinToStrEx(tmp, sizeof(tmp), sa->SKEYID_d, sa->HashSize);
	Debug("SKEYID_d: %s\n", tmp);

	// SKEYID_a
	a_buf = MemToBuf(sa->SKEYID_d, sa->HashSize);
	SeekBufToEnd(a_buf);
	WriteBufBuf(a_buf, sa->DhSharedKey);
	WriteBufInt64(a_buf, sa->InitiatorCookie);
	WriteBufInt64(a_buf, sa->ResponderCookie);
	u = 1;
	WriteBuf(a_buf, &u, 1);
	IkeHMac(h, sa->SKEYID_a, sa->SKEYID, sa->HashSize, a_buf->Buf, a_buf->Size);

	BinToStrEx(tmp, sizeof(tmp), sa->SKEYID_a, sa->HashSize);
	Debug("SKEYID_a: %s\n", tmp);

	// SKEYID_e
	e_buf = MemToBuf(sa->SKEYID_a, sa->HashSize);
	SeekBufToEnd(e_buf);
	WriteBufBuf(e_buf, sa->DhSharedKey);
	WriteBufInt64(e_buf, sa->InitiatorCookie);
	WriteBufInt64(e_buf, sa->ResponderCookie);
	u = 2;
	WriteBuf(e_buf, &u, 1);
	IkeHMac(h, sa->SKEYID_e, sa->SKEYID, sa->HashSize, e_buf->Buf, e_buf->Size);

	BinToStrEx(tmp, sizeof(tmp), sa->SKEYID_e, sa->HashSize);
	Debug("SKEYID_e: %s\n", tmp);

	if (sa->CryptoKey != NULL)
	{
		IkeFreeKey(sa->CryptoKey);
	}

	sa->CryptoKey = IkeNewCryptoKeyFromK(ike, sa->SKEYID_e, sa->HashSize, sa->TransformSetting.Hash,
		sa->TransformSetting.Crypto, sa->TransformSetting.CryptoKeySize);

	// Release the memory
	FreeBuf(secret_buf);
	FreeBuf(rand_buf);
	FreeBuf(d_buf);
	FreeBuf(a_buf);
	FreeBuf(e_buf);
}

// Extend the key size
BUF *IkeExpandKeySize(IKE_HASH *h, void *k, UINT k_size, UINT target_size)
{
	BUF *b1, *b2;
	UCHAR tmp[IKE_MAX_HASH_SIZE];
	UINT tmp_size;
	// Validate arguments
	if (h == NULL || k == NULL || k_size == 0)
	{
		return NULL;
	}

	if (k_size >= target_size)
	{
		return MemToBuf(k, target_size);
	}

	tmp[0] = 0;
	tmp_size = 1;
	b1 = NewBuf();

	do
	{
		IkeHMac(h, tmp, k, k_size, tmp, tmp_size);
		WriteBuf(b1, tmp, h->HashSize);

		tmp_size = h->HashSize;
	}
	while (b1->Size < target_size);

	b2 = MemToBuf(b1->Buf, target_size);

	FreeBuf(b1);

	return b2;
}

// Generate a key from K
IKE_CRYPTO_KEY *IkeNewCryptoKeyFromK(IKE_SERVER *ike, void *k, UINT k_size, IKE_HASH *h, IKE_CRYPTO *c, UINT crypto_key_size)
{
	BUF *key_buf;
	IKE_CRYPTO_KEY *ret;
	// Validate arguments
	if (ike == NULL || k == NULL || k_size == 0 || h == NULL || c == NULL || crypto_key_size == 0)
	{
		return NULL;
	}

	key_buf = IkeExpandKeySize(h, k, k_size, crypto_key_size);
	if (key_buf == NULL)
	{
		return NULL;
	}

	ret = IkeNewKey(c, key_buf->Buf, key_buf->Size);

	FreeBuf(key_buf);

	return ret;
}

// Generate a hash for NAT detection
BUF *IkeCalcNatDetectHash(IKE_SERVER *ike, IKE_HASH *hash, UINT64 initiator_cookie, UINT64 responder_cookie, IP *ip, UINT port)
{
	BUF *b;
	USHORT us;
	USHORT hash_data[IKE_MAX_HASH_SIZE];
	// Validate arguments
	if (ike == NULL || ip == NULL || hash == NULL)
	{
		return NewBuf();
	}

	b = NewBuf();

	WriteBufInt64(b, initiator_cookie);
	WriteBufInt64(b, responder_cookie);

	if (IsIP6(ip))
	{
		WriteBuf(b, ip->address, sizeof(ip->address));
	}
	else
	{
		WriteBuf(b, IPV4(ip->address), IPV4_SIZE);
	}

	us = Endian16((USHORT)port);

	WriteBuf(b, &us, sizeof(USHORT));

	IkeHash(hash, hash_data, b->Buf, b->Size);

	FreeBuf(b);

	return MemToBuf(hash_data, hash->HashSize);
}

// Check the capacity of the opposite IPsec client
void IkeCheckCaps(IKE_CAPS *caps, IKE_PACKET *p)
{
	// Validate arguments
	if (caps == NULL || p == NULL)
	{
		Zero(caps, sizeof(IKE_CAPS));
		return;
	}

	Zero(caps, sizeof(IKE_CAPS));

	caps->NatTraversalRfc3947 = IkeIsVendorIdExists(p, IKE_VENDOR_ID_RFC3947_NAT_T);

	caps->NatTraversalDraftIetf = IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_03) ||
		IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02) ||
		IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02_2) ||
		IkeIsVendorIdExists(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_00);

	caps->DpdRfc3706 = IkeIsVendorIdExists(p, IKE_VENDOR_ID_RFC3706_DPD);

	caps->MS_L2TPIPSecVPNClient = IkeIsVendorIdExists(p, IKE_VENDOR_ID_MICROSOFT_L2TP);
	caps->MS_NT5_ISAKMP_OAKLEY = IkeIsVendorIdExists(p, IKE_VENDOR_ID_MS_NT5_ISAKMPOAKLEY);
	caps->MS_Vid_InitialContact = IkeIsVendorIdExists(p, IKE_VENDOR_ID_MS_VID_INITIALCONTACT);
}

// Check whether the specified vendor ID is contained in the packet
bool IkeIsVendorIdExists(IKE_PACKET *p, char *str)
{
	BUF *buf;
	UINT i, num;
	bool ok = false;
	// Validate arguments
	if (p == NULL || str == NULL)
	{
		return false;
	}

	buf = IkeStrToVendorId(str);
	if (buf == NULL)
	{
		return false;
	}

	num = IkeGetPayloadNum(p->PayloadList, IKE_PAYLOAD_VENDOR_ID);
	for (i = 0;i < num;i++)
	{
		IKE_PACKET_PAYLOAD *payload = IkeGetPayload(p->PayloadList, IKE_PAYLOAD_VENDOR_ID, i);
		if (payload == NULL)
		{
			break;
		}

		if (CompareBuf(payload->Payload.VendorId.Data, buf))
		{
			ok = true;
		}
		else
		{
			if (payload->Payload.VendorId.Data != NULL)
			{
				if (payload->Payload.VendorId.Data->Size >= buf->Size)
				{
					if (Cmp(payload->Payload.VendorId.Data->Buf, buf->Buf, buf->Size) == 0)
					{
						ok = true;
					}
				}
			}
		}
	}

	FreeBuf(buf);

	return ok;
}

// Add the vendor ID payload list
void IkeAddVendorIdPayloads(IKE_PACKET *p)
{
	// Validate arguments
	if (p == NULL)
	{
		return;
	}

	IkeAddVendorId(p, IKE_VENDOR_ID_RFC3947_NAT_T);
	IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_03);
	IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02);
	IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_02_2);
	IkeAddVendorId(p, IKE_VENDOR_ID_IPSEC_NAT_T_IKE_00);
	IkeAddVendorId(p, IKE_VENDOR_ID_RFC3706_DPD);
}

// Add the vendor ID payload
void IkeAddVendorId(IKE_PACKET *p, char *str)
{
	BUF *buf;
	IKE_PACKET_PAYLOAD *payload;
	// Validate arguments
	if (p == NULL || str == NULL)
	{
		return;
	}

	buf = IkeStrToVendorId(str);
	if (buf == NULL)
	{
		return;
	}

	payload = IkeNewDataPayload(IKE_PAYLOAD_VENDOR_ID, buf->Buf, buf->Size);

	Add(p->PayloadList, payload);

	FreeBuf(buf);
}

// Convert string to the vendor ID
BUF *IkeStrToVendorId(char *str)
{
	// Validate arguments
	if (IsEmptyStr(str))
	{
		return NULL;
	}

	if (StartWith(str, "0x"))
	{
		BUF *buf = StrToBin(str + 2);

		if (buf == NULL || buf->Size == 0)
		{
			FreeBuf(buf);
			return NULL;
		}

		return buf;
	}
	else
	{
		BUF *buf;
		UCHAR hash[MD5_SIZE];

		Md5(hash, str, StrLen(str));

		buf = MemToBuf(hash, sizeof(hash));

		return buf;
	}
}

// Receive a packet using the IKE SA
IKE_PACKET *IkeSaRecvPacket(IKE_SERVER *ike, IKE_SA *sa, void *data, UINT size)
{
	IKE_PACKET *ret;
	// Validate arguments
	if (ike == NULL || sa == NULL || (size != 0 && data == NULL))
	{
		return NULL;
	}

	if (sa->IsIvExisting == false || sa->CryptoKey == NULL)
	{
		ret = IkeParse(data, size, NULL);
	}
	else
	{
		IKE_CRYPTO_PARAM cp;

		Copy(&cp.Iv, sa->Iv, sa->BlockSize);
		cp.Key = sa->CryptoKey;

		ret = IkeParse(data, size, &cp);

		if (ret->FlagEncrypted)
		{
			IkeSaUpdateIv(sa, cp.NextIv, sa->BlockSize);
		}
	}

	return ret;
}

// Receive a packet using IPsec SA (Quick Mode received)
IKE_PACKET *IPsecSaRecvPacket(IKE_SERVER *ike, IPSECSA *sa, void *data, UINT size)
{
	IKE_PACKET *ret;
	// Validate arguments
	if (ike == NULL || sa == NULL || (size != 0 && data == NULL))
	{
		return NULL;
	}

	if (sa->IsIvExisting == false || sa->IkeSa->CryptoKey == NULL)
	{
		ret = IkeParse(data, size, NULL);
	}
	else
	{
		IKE_CRYPTO_PARAM cp;

		Copy(&cp.Iv, sa->Iv, sa->IkeSa->BlockSize);
		cp.Key = sa->IkeSa->CryptoKey;

		ret = IkeParse(data, size, &cp);

		if (ret->FlagEncrypted)
		{
			IPsecSaUpdateIv(sa, cp.NextIv, sa->IkeSa->BlockSize);
			IPsecSaUpdateIv(sa->PairIPsecSa, cp.NextIv, sa->IkeSa->BlockSize);
		}
	}

	return ret;
}

// Send a packet using IPsec SA (Quick Mode transmission)
void IPsecSaSendPacket(IKE_SERVER *ike, IPSECSA *sa, IKE_PACKET *p)
{
	BUF *buf;
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	if (p == NULL)
	{
		FreeBuf(sa->SendBuffer);
		sa->SendBuffer = NULL;
		sa->NextSendTick = 0;
		return;
	}

	// Build a packet
	if (p->FlagEncrypted == false)
	{
		buf = IkeBuild(p, NULL);
	}
	else
	{
		IKE_CRYPTO_PARAM cp;

		Copy(cp.Iv, sa->Iv, sa->IkeSa->BlockSize);
		cp.Key = sa->IkeSa->CryptoKey;

		buf = IkeBuild(p, &cp);

		IPsecSaUpdateIv(sa, cp.NextIv, sa->IkeSa->BlockSize);
		IPsecSaUpdateIv(sa->PairIPsecSa, cp.NextIv, sa->IkeSa->BlockSize);
	}

	if (buf == NULL)
	{
		return;
	}

	// Register the last packet to re-transmit
	if (sa->SendBuffer != NULL)
	{
		FreeBuf(sa->SendBuffer);
	}

	sa->SendBuffer = CloneBuf(buf);
	sa->NextSendTick = ike->Now + (UINT64)(IKE_SA_RESEND_INTERVAL);
	AddInterrupt(ike->Interrupts, sa->NextSendTick);

	IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &sa->IkeClient->ServerIP, sa->IkeClient->ServerPort,
		&sa->IkeClient->ClientIP, sa->IkeClient->ClientPort,
		buf->Buf, buf->Size);

	Free(buf);
}

// Send a packet using the IKE SA
void IkeSaSendPacket(IKE_SERVER *ike, IKE_SA *sa, IKE_PACKET *p)
{
	BUF *buf;
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	if (p == NULL)
	{
		FreeBuf(sa->SendBuffer);
		sa->SendBuffer = NULL;
		sa->NextSendTick = 0;
		return;
	}

	// Build a packet
	if (p->FlagEncrypted == false)
	{
		buf = IkeBuild(p, NULL);
	}
	else
	{
		IKE_CRYPTO_PARAM cp;

		Copy(cp.Iv, sa->Iv, sa->BlockSize);
		cp.Key = sa->CryptoKey;

		buf = IkeBuild(p, &cp);

		IkeSaUpdateIv(sa, cp.NextIv, sa->BlockSize);
	}

	if (buf == NULL)
	{
		return;
	}

	if (p->ExchangeType != IKE_EXCHANGE_TYPE_INFORMATION)
	{
		// Register the last packet to re-transmit
		if (sa->SendBuffer != NULL)
		{
			FreeBuf(sa->SendBuffer);
		}

		sa->SendBuffer = CloneBuf(buf);
		sa->NextSendTick = ike->Now + (UINT64)(IKE_SA_RESEND_INTERVAL);
		AddInterrupt(ike->Interrupts, sa->NextSendTick);
	}

	IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &sa->IkeClient->ServerIP, sa->IkeClient->ServerPort,
		&sa->IkeClient->ClientIP, sa->IkeClient->ClientPort,
		buf->Buf, buf->Size);

	Free(buf);
}

// Send an UDP packet
void IkeSendUdpPacket(IKE_SERVER *ike, UINT type, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, void *data, UINT size)
{
	UDPPACKET *p;
	// Validate arguments
	if (ike == NULL || server_ip == NULL || client_ip == NULL || server_port == 0 || client_port == 0 || data == NULL || size == 0)
	{
		return;
	}

	p = NewUdpPacket(server_ip, server_port, client_ip, client_port, data, size);

	p->Type = type;

	Add(ike->SendPacketList, p);
}

// Create an IKE SA
IKE_SA *NewIkeSa(IKE_SERVER *ike, IKE_CLIENT *c, UINT64 init_cookie, UINT mode, IKE_SA_TRANSFORM_SETTING *setting)
{
	IKE_SA *sa;
	// Validate arguments
	if (ike == NULL || c == NULL || init_cookie == 0 || setting == NULL)
	{
		return NULL;
	}

	sa = ZeroMalloc(sizeof(IKE_SA));

	sa->Id = ++ike->CurrentIkeSaId;

	sa->IkeClient = c;
	sa->InitiatorCookie = init_cookie;
	sa->ResponderCookie = GenerateNewResponserCookie(ike);
	sa->Mode = mode;
	sa->FirstCommTick = sa->LastCommTick = ike->Now;
	Copy(&sa->TransformSetting, setting, sizeof(IKE_SA_TRANSFORM_SETTING));

	Debug("New IKE SA (Mode = %u): %I64u <--> %I64u (%s %s %s(%u) %u %u)\n",
		mode,
		sa->InitiatorCookie,
		sa->ResponderCookie,
		setting->Dh->Name, setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize,
		setting->LifeKilobytes, setting->LifeSeconds);

	IPsecLog(ike, NULL, sa, NULL, "LI_NEW_IKE_SA",
		(mode == IKE_SA_MAIN_MODE ? _UU("LI_TAG_MAINMODE") : _UU("LI_TAG_AGGRESSIVE")),
		sa->InitiatorCookie, sa->ResponderCookie,
		setting->Dh->Name, setting->Hash->Name, setting->Crypto->Name, setting->CryptoKeySize * 8,
		setting->LifeKilobytes, setting->LifeSeconds);

	return sa;
}

// Search an IKE SA from the Responder Cookie
IKE_SA *FindIkeSaByResponderCookie(IKE_SERVER *ike, UINT64 responder_cookie)
{
	IKE_SA t;
	// Validate arguments
	if (ike == NULL || responder_cookie == 0)
	{
		return NULL;
	}

	t.ResponderCookie = responder_cookie;

	return Search(ike->IkeSaList, &t);
}

// Search an IKE SA from the Responder Cookie and the IKE_CLIENT
IKE_SA *FindIkeSaByResponderCookieAndClient(IKE_SERVER *ike, UINT64 responder_cookie, IKE_CLIENT *c)
{
	IKE_SA *sa;
	// Validate arguments
	if (ike == NULL || responder_cookie == 0 || c == NULL)
	{
		return NULL;
	}

	sa = FindIkeSaByResponderCookie(ike, responder_cookie);
	if (sa == NULL)
	{
		return NULL;
	}

	if (sa->IkeClient != c)
	{
		return NULL;
	}

	return sa;
}

// Search an IKE SA from the endpoint and the Initiator Cookie
IKE_SA *FindIkeSaByEndPointAndInitiatorCookie(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, UINT64 init_cookie, UINT mode)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0 || init_cookie == 0)
	{
		return NULL;
	}

	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
		IKE_CLIENT *c;

		c = sa->IkeClient;

		if (CmpIpAddr(&c->ClientIP, client_ip) == 0 &&
			CmpIpAddr(&c->ServerIP, server_ip) == 0 &&
			c->ClientPort == client_port &&
			c->ServerPort == server_port &&
			sa->InitiatorCookie == init_cookie &&
			sa->Mode == mode)
		{
			return sa;
		}
	}

	return NULL;
}

// Get the number of IPsec SA that is associated with the IKE_CLIENT
UINT GetNumberOfIPsecSaOfIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
{
	UINT num = 0, i;
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return 0;
	}

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

		if (sa->IkeClient == c)
		{
			num++;
		}
	}

	return num;
}

// Get the number of IKE SA that is associated with the IKE_CLIENT
UINT GetNumberOfIkeSaOfIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
{
	UINT num = 0, i;
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return 0;
	}

	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

		if (sa->IkeClient == c)
		{
			num++;
		}
	}

	return num;
}

// Get the number of clients that are connected from the specified IP address
UINT GetNumberOfIkeClientsFromIP(IKE_SERVER *ike, IP *client_ip)
{
	UINT i, num;
	// Validate arguments
	if (ike == NULL || client_ip == NULL)
	{
		return 0;
	}

	num = 0;

	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);

		if (CmpIpAddr(&c->ClientIP, client_ip) == 0)
		{
			num++;
		}
	}

	return num;
}

// Find the appropriate IKE client. Create if it is absent
IKE_CLIENT *SearchOrCreateNewIkeClientForIkePacket(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, IKE_PACKET *pr)
{
	IKE_CLIENT *c;
	// Validate arguments
	if (ike == NULL || pr == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0)
	{
		return NULL;
	}

	c = SearchIkeClientForIkePacket(ike, client_ip, client_port, server_ip, server_port, pr);
	if (c == NULL)
	{
		if (GetNumberOfIkeClientsFromIP(ike, client_ip) > IKE_QUOTA_MAX_NUM_CLIENTS_PER_IP ||
			LIST_NUM(ike->ClientList) > IKE_QUOTA_MAX_NUM_CLIENTS)
		{
			return NULL;
		}


		c = NewIkeClient(ike, client_ip, client_port, server_ip, server_port);

		Insert(ike->ClientList, c);
	}

	return SetIkeClientEndpoint(ike, c, client_ip, client_port, server_ip, server_port);
}

// Create an IKE client
IKE_CLIENT *NewIkeClient(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port)
{
	IKE_CLIENT *c;
	char client_ip_str[MAX_SIZE];
	char server_ip_str[MAX_SIZE];
	// Validate arguments
	if (ike == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0)
	{
		return NULL;
	}

	c = ZeroMalloc(sizeof(IKE_CLIENT));

	c->Id = ++ike->CurrentIkeClientId;

	Copy(&c->ClientIP, client_ip, sizeof(IP));
	c->ClientPort = client_port;

	Copy(&c->ServerIP, server_ip, sizeof(IP));
	Copy(&c->TransportModeServerIP, server_ip, sizeof(IP));
	Copy(&c->TransportModeClientIP, client_ip, sizeof(IP));
	c->ServerPort = server_port;

	c->LastCommTick = ike->Now;
	c->FirstCommTick = ike->Now;

	IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
	IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);

	Debug("New IKE_CLIENT: %p: %s:%u -> %s:%u\n", c, client_ip_str, client_port, server_ip_str, server_port);

	IPsecLog(ike, c, NULL, NULL, "LI_NEW_IKE_CLIENT");

	return c;
}

// Search for the best associated IKE client when an IKE packet has been received
IKE_CLIENT *SearchIkeClientForIkePacket(IKE_SERVER *ike, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port, IKE_PACKET *pr)
{
	IKE_CLIENT t;
	IKE_CLIENT *c = NULL;
	// Validate arguments
	if (ike == NULL || pr == NULL || client_ip == NULL || server_ip == NULL || client_port == 0 || server_port == 0)
	{
		return NULL;
	}

	if (true)
	{
		UINT i;

		if (pr->InitiatorCookie != 0 && pr->ResponderCookie != 0)
		{
			for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
			{
				IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

				// Extract what Cookie matches exactly
				if (sa->InitiatorCookie == pr->InitiatorCookie && sa->ResponderCookie == pr->ResponderCookie)
				{
					IKE_CLIENT *cc = sa->IkeClient;

					if (CmpIpAddr(&cc->ServerIP, server_ip) == 0 &&
						CmpIpAddr(&cc->ClientIP, client_ip) == 0)
					{
						c = cc;
						break;
					}
				}
			}
		}
	}

	if (c == NULL)
	{
		// Search by a pair of IP address and port number
		Copy(&t.ClientIP, client_ip, sizeof(IP));
		t.ClientPort = client_port;
		Copy(&t.ServerIP, server_ip, sizeof(IP));
		t.ServerPort = server_port;

		c = Search(ike->ClientList, &t);

		if (c != NULL)// && server_port == IPSEC_PORT_IPSEC_ISAKMP)
		{
			// Search that the IKE_SA that points to this IKE_CLIENT exists and match the Cookie
			bool ok = false;
			UINT i;

			if (server_port == IPSEC_PORT_IPSEC_ESP_UDP)
			{
				// Regard as OK if the port number exactly match in the case of connecting to a server-side 4500
				ok = true;
			}
			else
			{
				if (c->CurrentIkeSa != NULL &&
					c->CurrentIkeSa->InitiatorCookie == pr->InitiatorCookie &&
					c->CurrentIkeSa->ResponderCookie == pr->ResponderCookie)
				{
					ok = true;
				}
				else 
				{
					for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
					{
						IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

						if (sa->IkeClient == c)
						{
							if (sa->InitiatorCookie == pr->InitiatorCookie &&
								sa->ResponderCookie == pr->ResponderCookie)
							{
								ok = true;
								break;
							}
						}
					}
				}
			}

			if (ok == false)
			{
				// Not found
				c = NULL;
			}
		}
	}

	return c;
}

// Comparison of IPsec SA
int CmpIPsecSa(void *p1, void *p2)
{
	IPSECSA *sa1, *sa2;
	int r;
	// Validate arguments
	if (p1 == NULL || p2 == NULL)
	{
		return 0;
	}
	sa1 = *(IPSECSA **)p1;
	sa2 = *(IPSECSA **)p2;
	if (sa1 == NULL || sa2 == NULL)
	{
		return 0;
	}

	r = COMPARE_RET(sa1->ServerToClient, sa2->ServerToClient);
	if (r != 0)
	{
		return r;
	}

	r = COMPARE_RET(sa1->Spi, sa2->Spi);

	return r;
}

// Comparison of IKE_SA
int CmpIkeSa(void *p1, void *p2)
{
	IKE_SA *sa1, *sa2;
	int r;
	// Validate arguments
	if (p1 == NULL || p2 == NULL)
	{
		return 0;
	}
	sa1 = *(IKE_SA **)p1;
	sa2 = *(IKE_SA **)p2;
	if (sa1 == NULL || sa2 == NULL)
	{
		return 0;
	}

	r = COMPARE_RET(sa1->ResponderCookie, sa2->ResponderCookie);

	return r;
}

// Comparison of IKE_CLIENT
int CmpIkeClient(void *p1, void *p2)
{
	IKE_CLIENT *c1, *c2;
	int r;
	// Validate arguments
	if (p1 == NULL || p2 == NULL)
	{
		return 0;
	}
	c1 = *(IKE_CLIENT **)p1;
	c2 = *(IKE_CLIENT **)p2;
	if (c1 == NULL || c2 == NULL)
	{
		return 0;
	}

	r = CmpIpAddr(&c1->ClientIP, &c2->ClientIP);
	if (r != 0)
	{
		return r;
	}

	r = CmpIpAddr(&c1->ServerIP, &c2->ServerIP);
	if (r != 0)
	{
		return r;
	}

	r = COMPARE_RET(c1->ClientPort, c2->ClientPort);
	if (r != 0)
	{
		return r;
	}

	r = COMPARE_RET(c1->ServerPort, c2->ServerPort);
	if (r != 0)
	{
		return r;
	}

	return 0;
}

// Update the endpoint information of IKE_CLIENT
IKE_CLIENT *SetIkeClientEndpoint(IKE_SERVER *ike, IKE_CLIENT *c, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port)
{
	char client_ip_str[MAX_SIZE];
	char server_ip_str[MAX_SIZE];
	IKE_CLIENT *ret = c;
	IKE_CLIENT *cc;
	IKE_CLIENT t;
	// Validate arguments
	if (ike == NULL || c == NULL || client_ip == NULL || client_port == 0 || server_ip == NULL || server_port == 0)
	{
		return NULL;
	}

	if (CmpIpAddr(&c->ClientIP, client_ip) == 0 &&
		CmpIpAddr(&c->ServerIP, server_ip) == 0 &&
		c->ClientPort == client_port &&
		c->ServerPort == server_port)
	{
		// No change
		return ret;
	}

	if (IS_SPECIAL_PORT(client_port) || IS_SPECIAL_PORT(server_port))
	{
		// Don't change in the case of Raw socket
		return ret;
	}

	// Search for an existing IKE_CLIENT which exactly matches to combination of the new IP address and the port number
	Copy(&t.ClientIP, client_ip, sizeof(IP));
	t.ClientPort = client_port;
	Copy(&t.ServerIP, server_ip, sizeof(IP));
	t.ServerPort = server_port;

	cc = Search(ike->ClientList, &t);
	if (cc != NULL && c != cc && cc->Deleting == false && c->L2TP == NULL)
	{
		UINT i;
		// Merge into this existing IKE_CLIENT since it found
		for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
		{
			IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

			if (sa->IkeClient == c)
			{
				sa->IkeClient = cc;
			}
		}
		for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
		{
			IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

			if (sa->IkeClient == c)
			{
				sa->IkeClient = cc;
			}
		}

		if (cc->LastCommTick < c->LastCommTick)
		{
			StrCpy(cc->ClientId, sizeof(cc->ClientId), c->ClientId);
		}

		cc->FirstCommTick = MIN(cc->FirstCommTick, c->FirstCommTick);
		cc->LastCommTick = MAX(cc->LastCommTick, c->LastCommTick);

		ret = cc;

		IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
		IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);

		Debug("Merge IKE_CLIENT: %p->%p: %s:%u -> %s:%u\n", c, cc, client_ip_str, client_port, server_ip_str, server_port);

		IPsecLog(ike, c, NULL, NULL, "LI_CLIENT_MERGE", c->Id, cc->Id, cc->Id);

		// Remove old IKE_CLIENT from the list and free
		Delete(ike->ClientList, c);
		FreeIkeClient(ike, c);
	}
	else
	{
		// Rewrite the end point information of this IKE_CLIENT because not found
		Copy(&c->ClientIP, client_ip, sizeof(IP));
		Copy(&c->ServerIP, server_ip, sizeof(IP));
		c->ClientPort = client_port;
		c->ServerPort = server_port;

		IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
		IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);

		Debug("Update IKE_CLIENT: %p: %s:%u -> %s:%u\n", c, client_ip_str, client_port, server_ip_str, server_port);

		IPsecLog(ike, c, NULL, NULL, "LI_CLIENT_UPDATE");

		ike->ClientList->sorted = false;
	}

	return ret;
}

// Select the optimal transform setting for IPsec SA
bool GetBestTransformSettingForIPsecSa(IKE_SERVER *ike, IKE_PACKET *pr, IPSEC_SA_TRANSFORM_SETTING *setting, IP *server_ip)
{
	IKE_PACKET_PAYLOAD *sa_payload;
	IKE_PACKET_SA_PAYLOAD *sa;
	UINT i, num;
	bool ocmii_flag = false;
	// Validate arguments
	if (ike == NULL || pr == NULL || setting == NULL || server_ip == NULL)
	{
		return false;
	}

	Zero(setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));

	// Get the SA payload
	sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
	if (sa_payload == NULL)
	{
		return false;
	}

	sa = &sa_payload->Payload.Sa;

	// Scan all proposal payloads
	num = IkeGetPayloadNum(sa->PayloadList, IKE_PAYLOAD_PROPOSAL);
	for (i = 0;i < num;i++)
	{
		IKE_PACKET_PAYLOAD *proposal_payload = IkeGetPayload(sa->PayloadList, IKE_PAYLOAD_PROPOSAL, i);

		if (proposal_payload != NULL)
		{
			IKE_PACKET_PROPOSAL_PAYLOAD *proposal = &proposal_payload->Payload.Proposal;

			// Examine the contents of the proposal payload
			if (proposal->ProtocolId == IKE_PROTOCOL_ID_IPSEC_ESP && proposal->Spi->Size == 4)
			{
				// Scan all transform payloads
				UINT j, num2;

				num2 = IkeGetPayloadNum(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM);
				for (j = 0;j < num2;j++)
				{
					IKE_PACKET_PAYLOAD *transform_payload = IkeGetPayload(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM, j);
					if (transform_payload != NULL)
					{
						IKE_PACKET_TRANSFORM_PAYLOAD *transform = &transform_payload->Payload.Transform;
						IPSEC_SA_TRANSFORM_SETTING set;

						Zero(&set, sizeof(set));

						if (TransformPayloadToTransformSettingForIPsecSa(ike, transform, &set, server_ip))
						{
							Copy(setting, &set, sizeof(IPSEC_SA_TRANSFORM_SETTING));

							setting->SpiServerToClient = READ_UINT(proposal->Spi->Buf);

							return true;
						}
						else
						{
							if (set.OnlyCapsuleModeIsInvalid)
							{
								if (ocmii_flag == false)
								{
									Copy(setting, &set, sizeof(IPSEC_SA_TRANSFORM_SETTING));
									ocmii_flag = true;
								}
							}
						}
					}
				}
			}
		}
	}

	return false;
}

// Select the optimal transform settings for the IKE SA
bool GetBestTransformSettingForIkeSa(IKE_SERVER *ike, IKE_PACKET *pr, IKE_SA_TRANSFORM_SETTING *setting)
{
	IKE_PACKET_PAYLOAD *sa_payload;
	IKE_PACKET_SA_PAYLOAD *sa;
	UINT i, num;
	// Validate arguments
	if (ike == NULL || pr == NULL || setting == NULL)
	{
		return false;
	}

	// Get the SA payload
	sa_payload = IkeGetPayload(pr->PayloadList, IKE_PAYLOAD_SA, 0);
	if (sa_payload == NULL)
	{
		return false;
	}

	sa = &sa_payload->Payload.Sa;

	// Scan all proposal payloads
	num = IkeGetPayloadNum(sa->PayloadList, IKE_PAYLOAD_PROPOSAL);
	for (i = 0;i < num;i++)
	{
		IKE_PACKET_PAYLOAD *proposal_payload = IkeGetPayload(sa->PayloadList, IKE_PAYLOAD_PROPOSAL, i);

		if (proposal_payload != NULL)
		{
			IKE_PACKET_PROPOSAL_PAYLOAD *proposal = &proposal_payload->Payload.Proposal;

			// Examine the contents of the proposal payload
			if (proposal->ProtocolId == IKE_PROTOCOL_ID_IKE)
			{
				// Scan all transform payloads
				UINT j, num2;

				num2 = IkeGetPayloadNum(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM);
				for (j = 0;j < num2;j++)
				{
					IKE_PACKET_PAYLOAD *transform_payload = IkeGetPayload(proposal->PayloadList, IKE_PAYLOAD_TRANSFORM, j);
					if (transform_payload != NULL)
					{
						IKE_PACKET_TRANSFORM_PAYLOAD *transform = &transform_payload->Payload.Transform;

						if (transform->TransformId == IKE_TRANSFORM_ID_P1_KEY_IKE)
						{
							IKE_SA_TRANSFORM_SETTING set;

							if (TransformPayloadToTransformSettingForIkeSa(ike, transform, &set))
							{
								Copy(setting, &set, sizeof(IKE_SA_TRANSFORM_SETTING));
								return true;
							}
						}
					}
				}
			}
		}
	}

	return false;
}

// Convert a structure to the transform payload (for IPsec SA)
IKE_PACKET_PAYLOAD *TransformSettingToTransformPayloadForIPsec(IKE_SERVER *ike, IPSEC_SA_TRANSFORM_SETTING *setting)
{
	LIST *value_list;
	// Validate arguments
	if (ike == NULL || setting == NULL)
	{
		return NULL;
	}

	value_list = NewListFast(NULL);

	Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_HMAC, setting->HashId));

	if (setting->Dh != NULL)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_DH_GROUP, setting->DhId));
	}

	if (setting->LifeSeconds != INFINITE)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_TYPE, IKE_P2_LIFE_TYPE_SECONDS));
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, setting->LifeSeconds));
	}

	if (setting->LifeKilobytes != INFINITE)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_TYPE, IKE_P2_LIFE_TYPE_KILOBYTES));
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, setting->LifeKilobytes));
	}

	if (setting->Crypto->VariableKeySize)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_KEY_SIZE, setting->CryptoKeySize * 8));
	}

	Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P2_CAPSULE, setting->CapsuleMode));

	return IkeNewTransformPayload(1, setting->CryptoId, value_list);
}

// Convert a structure to the transform payload (for IKE SA)
IKE_PACKET_PAYLOAD *TransformSettingToTransformPayloadForIke(IKE_SERVER *ike, IKE_SA_TRANSFORM_SETTING *setting)
{
	LIST *value_list;
	// Validate arguments
	if (ike == NULL || setting == NULL)
	{
		return NULL;
	}

	value_list = NewListFast(NULL);

	Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_CRYPTO, setting->CryptoId));
	Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_HASH, setting->HashId));
	Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_AUTH_METHOD, IKE_P1_AUTH_METHOD_PRESHAREDKEY));
	Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_DH_GROUP, setting->DhId));

	if (setting->LifeSeconds != INFINITE)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_TYPE, IKE_P1_LIFE_TYPE_SECONDS));
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, setting->LifeSeconds));
	}

	if (setting->LifeKilobytes != INFINITE)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_TYPE, IKE_P1_LIFE_TYPE_KILOBYTES));
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, setting->LifeKilobytes));
	}

	if (setting->Crypto->VariableKeySize)
	{
		Add(value_list, IkeNewTransformValue(IKE_TRANSFORM_VALUE_P1_KET_SIZE, setting->CryptoKeySize * 8));
	}

	return IkeNewTransformPayload(1, IKE_TRANSFORM_ID_P1_KEY_IKE, value_list);
}

// Convert a transform payload to a structure (for IPsec SA)
bool TransformPayloadToTransformSettingForIPsecSa(IKE_SERVER *ike, IKE_PACKET_TRANSFORM_PAYLOAD *transform, IPSEC_SA_TRANSFORM_SETTING *setting, IP *server_ip)
{
	UINT i;
	UINT capsule_mode;
	bool is_esp_supported;
	// Validate arguments
	if (ike == NULL || transform == NULL || setting == NULL || server_ip == NULL)
	{
		return false;
	}

	is_esp_supported = IsUdpPortOpened(ike->IPsec->UdpListener, server_ip, IPSEC_PORT_IPSEC_ESP_RAW);

	Zero(setting, sizeof(IPSEC_SA_TRANSFORM_SETTING));

	setting->CryptoId = transform->TransformId;
	setting->HashId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_HMAC, 0);

	setting->DhId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_DH_GROUP, 0);

	setting->LifeKilobytes = INFINITE;
	setting->LifeSeconds = INFINITE;

	for (i = 0;i < IkeGetTransformValueNum(transform, IKE_TRANSFORM_VALUE_P2_LIFE_TYPE);i++)
	{
		UINT life_type = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_LIFE_TYPE, i);

		switch (life_type)
		{
		case IKE_P2_LIFE_TYPE_SECONDS:		// Number of seconds
			setting->LifeSeconds = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, i);
			break;

		case IKE_P2_LIFE_TYPE_KILOBYTES:	// Kilobytes
			setting->LifeKilobytes = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_LIFE_VALUE, i);
			break;

		default:
			// Unsupported expiration type
			return false;
		}
	}

	setting->Crypto = GetIkeCrypto(ike->Engine, true, setting->CryptoId);
	setting->Hash = GetIkeHash(ike->Engine, true, setting->HashId);
	setting->Dh = GetIkeDh(ike->Engine, true, setting->DhId);

	if (setting->Crypto == NULL || setting->Hash == NULL)
	{
		// Unsupported algorithm
		return false;
	}

	if (setting->Crypto->VariableKeySize)
	{
		// Get the actual key size in the case of variable key size
		setting->CryptoKeySize = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_KEY_SIZE, 0);

		// bits -> bytes
		setting->CryptoKeySize = setting->CryptoKeySize / 8;

		if (setting->CryptoKeySize == 0 || IkeCheckKeySize(setting->Crypto, setting->CryptoKeySize) == false)
		{
			// The key size is not specified or inappropriate
			return false;
		}
	}
	else
	{
		// Get a fixed key length for fixed key size
		setting->CryptoKeySize = setting->Crypto->KeySizes[0];
	}

	capsule_mode = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P2_CAPSULE, 0);
	if (capsule_mode != IKE_P2_CAPSULE_NAT_TUNNEL_1 && capsule_mode != IKE_P2_CAPSULE_NAT_TUNNEL_2 &&
		capsule_mode != IKE_P2_CAPSULE_NAT_TRANSPORT_1 && capsule_mode != IKE_P2_CAPSULE_NAT_TRANSPORT_2)
	{
		// No support for UDP encapsulation mode except for the NAT-Traversal
		if (capsule_mode == IKE_P2_CAPSULE_TRANSPORT || capsule_mode == IKE_P2_CAPSULE_TUNNEL)
		{
			if (is_esp_supported == false)
			{
				setting->OnlyCapsuleModeIsInvalid = true;
				return false;
			}
			else
			{
				// It is an environment that can send and receive ESP packets
			}
		}
		else
		{
			return false;
		}
	}

	setting->CapsuleMode = capsule_mode;

	return true;
}

// Convert a transform payload to a structure (for IKE SA)
bool TransformPayloadToTransformSettingForIkeSa(IKE_SERVER *ike, IKE_PACKET_TRANSFORM_PAYLOAD *transform, IKE_SA_TRANSFORM_SETTING *setting)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || transform == NULL || setting == NULL)
	{
		return false;
	}

	Zero(setting, sizeof(IKE_SA_TRANSFORM_SETTING));

	setting->CryptoId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_CRYPTO, 0);
	setting->HashId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_HASH, 0);

	if (IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_AUTH_METHOD, 0) != IKE_P1_AUTH_METHOD_PRESHAREDKEY)
	{
		// Only PSK authentication method is supported
		return false;
	}

	setting->DhId = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_DH_GROUP, 0);

	setting->LifeKilobytes = INFINITE;
	setting->LifeSeconds = INFINITE;

	for (i = 0;i < IkeGetTransformValueNum(transform, IKE_TRANSFORM_VALUE_P1_LIFE_TYPE);i++)
	{
		UINT life_type = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_LIFE_TYPE, i);

		switch (life_type)
		{
		case IKE_P1_LIFE_TYPE_SECONDS:		// Number of seconds
			setting->LifeSeconds = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, i);
			break;

		case IKE_P1_LIFE_TYPE_KILOBYTES:	// Kilobytes
			setting->LifeKilobytes = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_LIFE_VALUE, i);
			break;

		default:
			// Unsupported expiration type
			return false;
		}
	}

	setting->Crypto = GetIkeCrypto(ike->Engine, false, setting->CryptoId);
	setting->Hash = GetIkeHash(ike->Engine, false, setting->HashId);
	setting->Dh = GetIkeDh(ike->Engine, false, setting->DhId);

	if (setting->Crypto == NULL || setting->Hash == NULL || setting->Dh == NULL)
	{
		// Unsupported algorithm
		return false;
	}

	if (setting->Crypto->VariableKeySize)
	{
		// Get the actual key size in the case of variable key size
		setting->CryptoKeySize = IkeGetTransformValue(transform, IKE_TRANSFORM_VALUE_P1_KET_SIZE, 0);

		// bits -> bytes
		setting->CryptoKeySize = setting->CryptoKeySize / 8;

		if (setting->CryptoKeySize == 0 || IkeCheckKeySize(setting->Crypto, setting->CryptoKeySize) == false)
		{
			// The key size is not specified or inappropriate
			return false;
		}
	}
	else
	{
		// Get a fixed key length for fixed key size
		setting->CryptoKeySize = setting->Crypto->KeySizes[0];
	}

	return true;
}

// Creating a new Responder Cookie
UINT64 GenerateNewResponserCookie(IKE_SERVER *ike)
{
	UINT64 c;
	// Validate arguments
	if (ike == NULL)
	{
		return 0;
	}

	while (true)
	{
		bool b = false;
		UINT i;

		c = Rand64();

		for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
		{
			IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

			if (sa->ResponderCookie == c)
			{
				b = true;
				break;
			}
		}

		if (b == false)
		{
			return c;
		}
	}
}

// Parse the IKE packet header
IKE_PACKET *ParseIKEPacketHeader(UDPPACKET *p)
{
	// Validate arguments
	if (p == NULL)
	{
		return NULL;
	}

	return IkeParseHeader(p->Data, p->Size, NULL);
}

// Search for another IPsec SA belonging to the IKE_CLIENT which have same conditions to the specified IPsec SA
IPSECSA *GetOtherLatestIPsecSa(IKE_SERVER *ike, IPSECSA *sa)
{
	UINT i;
	UINT64 min_value = 0;
	IPSECSA *max_sa = NULL;
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return NULL;
	}

	if (sa->IkeClient == NULL)
	{
		return NULL;
	}

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa2 = LIST_DATA(ike->IPsecSaList, i);

		if (sa2 != sa)
		{
			if (sa2->IkeClient == sa->IkeClient)
			{
				if (sa2->ServerToClient == sa->ServerToClient)
				{
					if (sa2->Deleting == false)
					{
						if (sa2->Established)
						{
							UINT64 last_comm_tick = sa2->LastCommTick;

							if (sa2->ServerToClient)
							{
								if (sa2->PairIPsecSa != NULL)
								{
									last_comm_tick = sa2->PairIPsecSa->LastCommTick;
								}
							}

							if (min_value < last_comm_tick)
							{
								min_value = last_comm_tick;

								max_sa = sa2;
							}
						}
					}
				}
			}
		}
	}

	return max_sa;
}

// Search for another IKE_SA belonging to the IKE_CLIENT which have same conditions to the specified IKE_SA
IKE_SA *GetOtherLatestIkeSa(IKE_SERVER *ike, IKE_SA *sa)
{
	UINT i;
	UINT64 min_value = 0;
	IKE_SA *max_sa = NULL;
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return NULL;
	}

	if (sa->IkeClient == NULL)
	{
		return NULL;
	}

	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa2 = LIST_DATA(ike->IkeSaList, i);

		if (sa2 != sa)
		{
			if (sa2->IkeClient == sa->IkeClient)
			{
				if (sa2->Deleting == false)
				{
					if (sa2->Established)
					{
						if (min_value < sa2->LastCommTick)
						{
							min_value = sa2->LastCommTick;

							max_sa = sa2;
						}
					}
				}
			}
		}
	}

	return max_sa;
}

// Purge the IPsec SA
void PurgeIPsecSa(IKE_SERVER *ike, IPSECSA *sa)
{
	UINT i;
	IPSECSA *other_sa;
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	other_sa = GetOtherLatestIPsecSa(ike, sa);

	// Rewrite the pairing partner by looking for IPsec SA that are paired
	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa2 = LIST_DATA(ike->IPsecSaList, i);

		if (sa2->PairIPsecSa == sa)
		{
			sa2->PairIPsecSa = other_sa;
		}
	}

	// Rewrite the IKE_CLIENT using this IPsec SA to use alternate
	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);

		if (c->CurrentIpSecSaRecv == sa)
		{
			c->CurrentIpSecSaRecv = other_sa;
		}

		if (c->CurrentIpSecSaSend == sa)
		{
			c->CurrentIpSecSaSend = other_sa;
		}
	}

	Delete(ike->IPsecSaList, sa);
	FreeIPsecSa(sa);
}

// Remove the IKE SA
void PurgeIkeSa(IKE_SERVER *ike, IKE_SA *sa)
{
	IKE_SA *other_sa;
	UINT i;
	// Validate arguments
	if (ike == NULL || sa == NULL)
	{
		return;
	}

	Debug("Purging IKE SA %I64u-%I64u\n", sa->InitiatorCookie, sa->ResponderCookie);

	// Rewrite to alternative IKE_SA of all IPsec SA that are using this IKE_SA
	other_sa = GetOtherLatestIkeSa(ike, sa);

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *ipsec_sa = LIST_DATA(ike->IPsecSaList, i);

		if (ipsec_sa->IkeSa == sa)
		{
			if (other_sa == NULL)
			{
				// Remove this IPsec SA because there is no alternative IKE_SA
				Debug("  Deleting IPsec SA 0x%X of this IKE SA (no alternatives)\n", ipsec_sa->Spi);
				MarkIPsecSaAsDeleted(ike, ipsec_sa);
				ipsec_sa->IkeSa = NULL;
			}
			else
			{
				// Replace to the alternative IKE_SA
				Debug("  Replacing IKE SA of IPsec SA 0x%X from %I64u-%I64u to %I64u-%I64u\n", ipsec_sa->Spi,
					sa->InitiatorCookie, sa->ResponderCookie,
					other_sa->InitiatorCookie, other_sa->ResponderCookie);
				ipsec_sa->IkeSa = other_sa;
			}
		}
	}

	// Substitute the IKE_SA of all IKE_CLIENT that are using this IKE_SA with alternative
	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);

		if (c->CurrentIkeSa == sa)
		{
			c->CurrentIkeSa = other_sa;
		}
	}

	Delete(ike->IkeSaList, sa);
	FreeIkeSa(sa);
}

// Purge the IKE_CLIENT
void PurgeIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
{
	UINT i;
	// Validate arguments
	if (ike == NULL || c == NULL)
	{
		return;
	}

	// Delete all of IPsec SA and IKE SA that belong to this IKE Client
	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

		if (sa->IkeClient == c)
		{
			MarkIkeSaAsDeleted(ike, sa);
		}
	}
	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

		if (sa->IkeClient == c)
		{
			MarkIPsecSaAsDeleted(ike, sa);
		}
	}

	Delete(ike->ClientList, c);
	FreeIkeClient(ike, c);
}

// Remove the SA that has been marked to delete
void PurgeDeletingSAsAndClients(IKE_SERVER *ike)
{
	UINT i;
	LIST *o = NULL;
	// Validate arguments
	if (ike == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);
		if (sa->Deleting)
		{
			if (o == NULL)
			{
				o = NewListFast(NULL);
			}

			Add(o, sa);
		}
	}

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_SA *sa = LIST_DATA(o, i);

		PurgeIkeSa(ike, sa);
	}

	ReleaseList(o);

	o = NULL;

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
		if (sa->Deleting)
		{
			if (o == NULL)
			{
				o = NewListFast(NULL);
			}

			Add(o, sa);
		}
	}

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IPSECSA *sa = LIST_DATA(o, i);

		PurgeIPsecSa(ike, sa);
	}

	ReleaseList(o);

	o = NULL;

	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
		if (c->Deleting)
		{
			if (o == NULL)
			{
				o = NewListFast(NULL);
			}

			Add(o, c);
		}
	}

	for (i = 0;i < LIST_NUM(o);i++)
	{
		IKE_CLIENT *c = LIST_DATA(o, i);

		PurgeIkeClient(ike, c);
	}

	ReleaseList(o);
}

// IKE interrupt process
void ProcessIKEInterrupts(IKE_SERVER *ike)
{
	UINT i;
	// Validate arguments
	if (ike == NULL)
	{
		return;
	}

	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);

		c->CurrentExpiresSoftTick_CtoS = 0;
		c->CurrentExpiresSoftTick_StoC = 0;
		c->CurrentNumEstablishedIPsecSA_CtoS = 0;
		c->CurrentNumEstablishedIPsecSA_StoC = 0;
		c->CurrentNumHealtyIPsecSA_CtoS = 0;
		c->CurrentNumHealtyIPsecSA_StoC = 0;
	}

	// Packet retransmission by scanning all IKE SA
	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

		if (sa->SendBuffer != NULL)
		{
			if (ike->Now >= sa->NextSendTick)
			{
				IKE_CLIENT *c = sa->IkeClient;

				IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &c->ServerIP, c->ServerPort, &c->ClientIP, c->ClientPort,
					Clone(sa->SendBuffer->Buf, sa->SendBuffer->Size), sa->SendBuffer->Size);

				sa->NextSendTick += (UINT64)(IKE_SA_RESEND_INTERVAL);

				AddInterrupt(ike->Interrupts, sa->NextSendTick);

				if (sa->NumResends != 0)
				{
					sa->NumResends--;
					if (sa->NumResends == 0)
					{
						sa->NextSendTick = 0;
						FreeBuf(sa->SendBuffer);
						sa->SendBuffer = NULL;
					}
				}
			}
		}

		// Remove those of non-communication
		if (sa->IkeClient == NULL || (sa->IkeClient->CurrentIkeSa != sa))
		{
			// When the IKE_CLIENT don't point this
			if (sa->Established == false)
			{
				// Make time-out in a short time when it is not established
				if ((sa->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT_FOR_NOT_ESTABLISHED) <= ike->Now)
				{
					WHERE;
					MarkIkeSaAsDeleted(ike, sa);
				}
			}
			else
			{
				// Timeout in a long time in the case of established
				if ((sa->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT) <= ike->Now)
				{
					WHERE;
					MarkIkeSaAsDeleted(ike, sa);
				}
			}
		}
	}

	// Packet retransmission by scanning all IPsec SA
	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);
		IKE_CLIENT *c = sa->IkeClient;

		if (sa->SendBuffer != NULL)
		{
			if (ike->Now >= sa->NextSendTick)
			{
				IKE_CLIENT *c = sa->IkeClient;

				IkeSendUdpPacket(ike, IKE_UDP_TYPE_ISAKMP, &c->ServerIP, c->ServerPort, &c->ClientIP, c->ClientPort,
					Clone(sa->SendBuffer->Buf, sa->SendBuffer->Size), sa->SendBuffer->Size);

				sa->NextSendTick += (UINT64)(IKE_SA_RESEND_INTERVAL);

				AddInterrupt(ike->Interrupts, sa->NextSendTick);

				if (sa->NumResends != 0)
				{
					sa->NumResends--;

					if (sa->NumResends == 0)
					{
						sa->NextSendTick = 0;
						FreeBuf(sa->SendBuffer);
						sa->SendBuffer = NULL;
					}
				}
			}
		}

		if (sa->Established && sa->Deleting == false && c != NULL)
		{
			// Get the flexible expiration date of SA for each IKE_CLIENT
			if (sa->ServerToClient)
			{
				c->CurrentExpiresSoftTick_StoC = MAX(c->CurrentExpiresSoftTick_StoC, sa->ExpiresSoftTick);
				c->CurrentNumEstablishedIPsecSA_StoC++;

				if (sa->ExpiresSoftTick == 0 || sa->ExpiresSoftTick > ike->Now)
				{
					c->CurrentNumHealtyIPsecSA_StoC++;
				}
			}
			else
			{
				c->CurrentExpiresSoftTick_CtoS = MAX(c->CurrentExpiresSoftTick_CtoS, sa->ExpiresSoftTick);
				c->CurrentNumEstablishedIPsecSA_CtoS++;

				if (sa->ExpiresSoftTick == 0 || sa->ExpiresSoftTick > ike->Now)
				{
					c->CurrentNumHealtyIPsecSA_CtoS++;
				}
			}
		}

		// Remove those of non-communication
		if (sa->IkeClient == NULL || (sa->IkeClient->CurrentIpSecSaRecv != sa && sa->IkeClient->CurrentIpSecSaSend != sa))
		{
			// When the IKE_CLIENT don't point this
			UINT64 last_comm_tick = sa->LastCommTick;

			if (sa->ServerToClient && sa->PairIPsecSa != NULL)
			{
				last_comm_tick = sa->PairIPsecSa->LastCommTick;
			}

			if (sa->Established == false)
			{
				// Make time-out in a short time when it is not established
				if ((last_comm_tick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT_FOR_NOT_ESTABLISHED) <= ike->Now)
				{
					WHERE;
					MarkIPsecSaAsDeleted(ike, sa);
				}
			}
			else
			{
				// Timeout in a long time in the case of established
				if ((last_comm_tick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT) <= ike->Now)
				{
					WHERE;
					MarkIPsecSaAsDeleted(ike, sa);
				}
			}
		}
	}

	// IKE_CLIENT scanning process
	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);
		UINT64 tick;
		UCHAR data[1];
		bool need_qm = false;
		bool need_qm_hard = false;
		UINT64 qm_soft_tick = 0;

		// Determine whether it is necessary to start a new Quick Mode
		if (c->CurrentExpiresSoftTick_StoC != 0 && ike->Now >= c->CurrentExpiresSoftTick_StoC)
		{
			need_qm = true;
			qm_soft_tick = MAX(qm_soft_tick, c->CurrentExpiresSoftTick_StoC);
		}

		if (c->CurrentExpiresSoftTick_CtoS != 0 && ike->Now >= c->CurrentExpiresSoftTick_CtoS)
		{
			need_qm = true;
			qm_soft_tick = MAX(qm_soft_tick, c->CurrentExpiresSoftTick_StoC);
		}

		if (c->CurrentNumHealtyIPsecSA_CtoS == 0 || c->CurrentNumHealtyIPsecSA_StoC == 0)
		{
			need_qm = true;
			need_qm_hard = true;
		}

		if (c->StartQuickModeAsSoon)
		{
			need_qm = true;
			need_qm_hard = true;
		}

		if (c->Deleting || c->CurrentIkeSa == NULL || c->CurrentIkeSa->Deleting)
		{
			need_qm = false;
			need_qm_hard = true;
		}

		if (need_qm)
		{
			if (c->StartQuickModeAsSoon || ((c->LastQuickModeStartTick + (UINT64)IKE_QUICKMODE_START_INTERVAL) <= ike->Now))
			{
				// Start the Quick Mode
				Debug("IKE_CLIENT 0x%X: Begin QuickMode\n", c);
				c->StartQuickModeAsSoon = false;
				c->LastQuickModeStartTick = ike->Now;

				AddInterrupt(ike->Interrupts, c->LastQuickModeStartTick + (UINT64)IKE_QUICKMODE_START_INTERVAL);

				StartQuickMode(ike, c);
			}
		}

		if (need_qm_hard)
		{
			if (c->NeedQmBeginTick == 0)
			{
				c->NeedQmBeginTick = ike->Now;
			}
		}
		else
		{
			c->NeedQmBeginTick = 0;
		}

		if (((c->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT) <= ike->Now) ||
			((c->CurrentIkeSa == NULL && c->CurrentIpSecSaRecv == NULL && c->CurrentIpSecSaSend == NULL) && (c->LastCommTick + (UINT64)IKE_TIMEOUT_FOR_IKE_CLIENT_FOR_NOT_ESTABLISHED) <= ike->Now) ||
			(c->NeedQmBeginTick != 0 && ((c->NeedQmBeginTick + (UINT64)IKE_QUICKMODE_FAILED_TIMEOUT) <= ike->Now)))
		{
			// Remove IKE_CLIENT not communicating for a certain period of time
			WHERE;
			MarkIkeClientAsDeleted(ike, c);
		}

		// L2TP processing
		if (c->L2TP != NULL)
		{
			IPsecIkeClientManageL2TPServer(ike, c);

			// Interrupt processing occurs
			L2TPProcessInterrupts(c->L2TP);

			// Packet transmission
			IPsecIkeClientSendL2TPPackets(ike, c, c->L2TP);
		}

		// EtherIP processing
		if (c->EtherIP != NULL)
		{
			IPsecIkeClientManageEtherIPServer(ike, c);

			// Interrupt processing occurs
			EtherIPProcInterrupts(c->EtherIP);

			// Packet transmission
			IPsecIkeClientSendEtherIPPackets(ike, c, c->EtherIP);
		}

		// KeepAlive transmission
		tick = MAX(c->LastCommTick + (UINT64)IKE_INTERVAL_UDP_KEEPALIVE, c->NextKeepAliveSendTick);

		if (tick <= ike->Now && c->ServerPort == IPSEC_PORT_IPSEC_ESP_UDP)
		{
			c->NextKeepAliveSendTick = ike->Now + (UINT64)IKE_INTERVAL_UDP_KEEPALIVE;

			AddInterrupt(ike->Interrupts, c->NextKeepAliveSendTick);

			Zero(data, sizeof(data));
			data[0] = 0xff;

			IkeSendUdpPacket(ike, IKE_UDP_KEEPALIVE, &c->ServerIP, c->ServerPort, &c->ClientIP, c->ClientPort, Clone(data, sizeof(data)), sizeof(data));
		}

		// DPD transmission
		if (c->NextDpdSendTick == 0 || c->NextDpdSendTick <= ike->Now)
		{
			if (c->CurrentIkeSa != NULL && c->CurrentIkeSa->Established)
			{
				if (c->CurrentIkeSa->Caps.DpdRfc3706)
				{
					c->NextDpdSendTick = ike->Now + (UINT64)IKE_INTERVAL_DPD_KEEPALIVE;

					AddInterrupt(ike->Interrupts, c->NextDpdSendTick);

					SendInformationalExchangePacket(ike, c,
						IkeNewNoticeDpdPayload(false, c->CurrentIkeSa->InitiatorCookie, c->CurrentIkeSa->ResponderCookie,
						c->DpdSeqNo++));
				}
			}
		}
	}

	do
	{
		ike->StateHasChanged = false;

		// Deletion process
		PurgeDeletingSAsAndClients(ike);
	}
	while (ike->StateHasChanged);

	// Maintenance of the thread list
	MaintainThreadList(ike->ThreadList);
	/*Debug("ike->ThreadList: %u\n", LIST_NUM(ike->ThreadList));
	{
		UINT i;
		for (i = 0;i < LIST_NUM(ike->ThreadList);i++)
		{
			THREAD *t = LIST_DATA(ike->ThreadList, i);

			Debug("  Thread %u: 0x%p ID: %u Stop: %u Ref: %u\n", i, t, t->ThreadId, t->Stopped, t->ref->c->c);
		}
	}*/
}

// Stop the IKE server
void StopIKEServer(IKE_SERVER *ike)
{
	// Validate arguments
	if (ike == NULL)
	{
		return;
	}
}

// Set the socket events in IKE server
void SetIKEServerSockEvent(IKE_SERVER *ike, SOCK_EVENT *e)
{
	// Validate arguments
	if (ike == NULL)
	{
		return;
	}

	if (e != NULL)
	{
		AddRef(e->ref);
	}

	if (ike->SockEvent != NULL)
	{
		ReleaseSockEvent(ike->SockEvent);
	}

	ike->SockEvent = e;
}

// Release the IKE client
void FreeIkeClient(IKE_SERVER *ike, IKE_CLIENT *c)
{
	// Validate arguments
	if (c == NULL || ike == NULL)
	{
		return;
	}

	if (c->L2TP != NULL)
	{
		StopL2TPServer(c->L2TP, true);
		FreeL2TPServer(c->L2TP);
	}

	if (c->EtherIP != NULL)
	{
		ReleaseEtherIPServer(c->EtherIP);
	}

	FreeBuf(c->SendID1_Buf);
	FreeBuf(c->SendID2_Buf);

	Free(c);
}

// Release the IPsec SA
void FreeIPsecSa(IPSECSA *sa)
{
	// Validate arguments
	if (sa == NULL)
	{
		return;
	}

	IkeFreeKey(sa->CryptoKey);

	FreeBuf(sa->SendBuffer);

	FreeBuf(sa->InitiatorRand);
	FreeBuf(sa->ResponderRand);

	FreeBuf(sa->SharedKey);

	IkeDhFreeCtx(sa->Dh);

	Free(sa);
}

// Release the IKE SA
void FreeIkeSa(IKE_SA *sa)
{
	// Validate arguments
	if (sa == NULL)
	{
		return;
	}

	FreeBuf(sa->SendBuffer);

	FreeBuf(sa->InitiatorRand);
	FreeBuf(sa->ResponderRand);
	FreeBuf(sa->DhSharedKey);
	FreeBuf(sa->YourIDPayloadForAM);

	FreeBuf(sa->GXi);
	FreeBuf(sa->GXr);

	FreeBuf(sa->SAi_b);

	IkeFreeKey(sa->CryptoKey);

	Free(sa);
}

// Release the IKE server
void FreeIKEServer(IKE_SERVER *ike)
{
	UINT i;
	// Validate arguments
	if (ike == NULL)
	{
		return;
	}

	IPsecLog(ike, NULL, NULL, NULL, "LI_STOPPING");

	for (i = 0;i < LIST_NUM(ike->SendPacketList);i++)
	{
		UDPPACKET *udp = LIST_DATA(ike->SendPacketList, i);

		FreeUdpPacket(udp);
	}

	ReleaseList(ike->SendPacketList);

	Debug("Num of IPsec SAs: %u\n", LIST_NUM(ike->IPsecSaList));
	IPsecLog(ike, NULL, NULL, NULL, "LI_NUM_IPSEC_SA", LIST_NUM(ike->IPsecSaList));

	for (i = 0;i < LIST_NUM(ike->IPsecSaList);i++)
	{
		IPSECSA *sa = LIST_DATA(ike->IPsecSaList, i);

		FreeIPsecSa(sa);
	}

	ReleaseList(ike->IPsecSaList);

	Debug("Num of IKE SAs: %u\n", LIST_NUM(ike->IkeSaList));
	IPsecLog(ike, NULL, NULL, NULL, "LI_NUM_IKE_SA", LIST_NUM(ike->IkeSaList));

	for (i = 0;i < LIST_NUM(ike->IkeSaList);i++)
	{
		IKE_SA *sa = LIST_DATA(ike->IkeSaList, i);

		FreeIkeSa(sa);
	}

	ReleaseList(ike->IkeSaList);

	Debug("Num of IKE_CLIENTs: %u\n", LIST_NUM(ike->ClientList));
	IPsecLog(ike, NULL, NULL, NULL, "LI_NUM_IKE_CLIENTS", LIST_NUM(ike->ClientList));

	for (i = 0;i < LIST_NUM(ike->ClientList);i++)
	{
		IKE_CLIENT *c = LIST_DATA(ike->ClientList, i);

		FreeIkeClient(ike, c);
	}

	ReleaseList(ike->ClientList);

	ReleaseSockEvent(ike->SockEvent);

	IPsecLog(ike, NULL, NULL, NULL, "LI_STOP");

	ReleaseCedar(ike->Cedar);

	FreeIkeEngine(ike->Engine);

	Debug("FreeThreadList()...\n");
	FreeThreadList(ike->ThreadList);
	Debug("FreeThreadList() Done.\n");

	Free(ike);
}

// Create a new IKE server
IKE_SERVER *NewIKEServer(CEDAR *cedar, IPSEC_SERVER *ipsec)
{
	IKE_SERVER *ike;
	// Validate arguments
	if (cedar == NULL)
	{
		return NULL;
	}

	ike = ZeroMalloc(sizeof(IKE_SERVER));

	ike->Cedar = cedar;
	AddRef(cedar->ref);

	ike->IPsec = ipsec;

	ike->Now = Tick64();

	ike->SendPacketList = NewList(NULL);

	ike->IkeSaList = NewList(CmpIkeSa);

	ike->IPsecSaList = NewList(CmpIPsecSa);

	ike->ClientList = NewList(CmpIkeClient);

	ike->Engine = NewIkeEngine();

	ike->ThreadList = NewThreadList();

	IPsecLog(ike, NULL, NULL, NULL, "LI_START");

	return ike;
}



