mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-04-03 18:00:08 +03:00
Buffer overread in ParseL2TPPacket() Memory corruption in IcmpParseResult Missing bounds check in ParseUDP() can lead to invalid memory access Out-of-bounds read in IPsec_PPP.c (unterminated string buffer) Overlapping parameters to memcpy() via StrToIp6() PACK ReadValue() crash vulnerability Potential use of uninitialized memory via IPToInAddr6() 4 memory leaks. While the amount of leakage is very small per time, these bugs can finally cause process crash by out of memory. So these bugs must be fixed. Memory leak in NnReadDnsRecord Memory leak in RadiusLogin() Memory leak via ParsePacketIPv4WithDummyMacHeader Remote memory leak in OpenVPN server code 1 coding improvement. This is not a bug, however, I fixed the code to avoid furture misunderstanding. RecvAll can return success on failure (leading to use of uninitialized memory) Contributors for this bugfix: - Max Planck Institute for Molecular Genetics - Guido Vranken
4490 lines
94 KiB
C
4490 lines
94 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Mayaqua Kernel
|
|
//
|
|
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
|
|
//
|
|
// Copyright (c) Daiyuu Nobori.
|
|
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
|
|
// Copyright (c) SoftEther Corporation.
|
|
//
|
|
// All Rights Reserved.
|
|
//
|
|
// http://www.softether.org/
|
|
//
|
|
// Author: Daiyuu Nobori, Ph.D.
|
|
// Comments: Tetsuo Sugiyama, Ph.D.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// version 2 as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License version 2
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
|
|
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
|
|
//
|
|
//
|
|
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
|
|
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
|
|
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
|
|
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
|
|
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
|
|
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
|
|
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
|
|
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
|
|
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
|
|
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
|
|
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
|
|
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
|
|
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
|
|
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
|
|
//
|
|
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
|
|
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
|
|
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
|
|
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
|
|
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
|
|
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
|
|
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
|
|
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
|
|
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
|
|
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
|
|
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
|
|
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
|
|
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
|
|
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
|
|
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
|
|
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
|
|
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
|
|
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
|
|
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
|
|
//
|
|
//
|
|
// SOURCE CODE CONTRIBUTION
|
|
// ------------------------
|
|
//
|
|
// Your contribution to SoftEther VPN Project is much appreciated.
|
|
// Please send patches to us through GitHub.
|
|
// Read the SoftEther VPN Patch Acceptance Policy in advance:
|
|
// http://www.softether.org/5-download/src/9.patch
|
|
//
|
|
//
|
|
// DEAR SECURITY EXPERTS
|
|
// ---------------------
|
|
//
|
|
// If you find a bug or a security vulnerability please kindly inform us
|
|
// about the problem immediately so that we can fix the security problem
|
|
// to protect a lot of users around the world as soon as possible.
|
|
//
|
|
// Our e-mail address for security reports is:
|
|
// softether-vpn-security [at] softether.org
|
|
//
|
|
// Please note that the above e-mail address is not a technical support
|
|
// inquiry address. If you need technical assistance, please visit
|
|
// http://www.softether.org/ and ask your question on the users forum.
|
|
//
|
|
// Thank you for your cooperation.
|
|
//
|
|
//
|
|
// NO MEMORY OR RESOURCE LEAKS
|
|
// ---------------------------
|
|
//
|
|
// The memory-leaks and resource-leaks verification under the stress
|
|
// test has been passed before release this source code.
|
|
|
|
|
|
// TcpIp.c
|
|
// Utility module for TCP/IP packet processing
|
|
|
|
#include <GlobalConst.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <Mayaqua/Mayaqua.h>
|
|
|
|
|
|
// Send an ICMP Echo
|
|
ICMP_RESULT *IcmpEchoSend(IP *dest_ip, UCHAR ttl, UCHAR *data, UINT size, UINT timeout)
|
|
{
|
|
// Validate arguments
|
|
if (dest_ip == NULL || IsIP4(dest_ip) == false || (size != 0 && data == NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
if (ttl == 0)
|
|
{
|
|
ttl = 127;
|
|
}
|
|
|
|
if (IsIcmpApiSupported())
|
|
{
|
|
return IcmpApiEchoSend(dest_ip, ttl, data, size, timeout);
|
|
}
|
|
else
|
|
{
|
|
return IcmpEchoSendBySocket(dest_ip, ttl, data, size, timeout);
|
|
}
|
|
}
|
|
|
|
// Release the memory for the ICMP response
|
|
void IcmpFreeResult(ICMP_RESULT *r)
|
|
{
|
|
// Validate arguments
|
|
if (r == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IcmpApiFreeResult(r);
|
|
}
|
|
|
|
// Parse the ICMP reply packet received from the socket
|
|
ICMP_RESULT *IcmpParseResult(IP *dest_ip, USHORT src_id, USHORT src_seqno, UCHAR *recv_buffer, UINT recv_buffer_size)
|
|
{
|
|
ICMP_RESULT *ret = NULL;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (dest_ip == NULL || IsIP4(dest_ip) == false || recv_buffer == NULL || recv_buffer_size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
i = recv_buffer_size;
|
|
|
|
if (true)
|
|
{
|
|
UINT ip_header_size = GetIpHeaderSize(recv_buffer, i);
|
|
if (ip_header_size >= sizeof(IPV4_HEADER) && (ip_header_size <= i))
|
|
{
|
|
IPV4_HEADER *ipv4 = (IPV4_HEADER *)recv_buffer;
|
|
if ((IPV4_GET_VERSION(ipv4) == 4) && (ipv4->Protocol == IP_PROTO_ICMPV4))
|
|
{
|
|
UINT ip_total_len = (UINT)Endian16(ipv4->TotalLength);
|
|
|
|
if ((ip_total_len >= sizeof(IPV4_HEADER)) && (ip_total_len <= i) && (ip_total_len >= ip_header_size))
|
|
{
|
|
UINT icmp_packet_size = ip_total_len - ip_header_size;
|
|
ICMP_HEADER *icmp = (ICMP_HEADER *)(recv_buffer + ip_header_size);
|
|
|
|
if (icmp_packet_size >= sizeof(ICMP_HEADER))
|
|
{
|
|
USHORT chksum = icmp->Checksum;
|
|
USHORT chksum2;
|
|
icmp->Checksum = 0;
|
|
|
|
chksum2 = IpChecksum(icmp, icmp_packet_size);
|
|
|
|
if (chksum2 == chksum)
|
|
{
|
|
if (icmp->Type == ICMP_TYPE_ECHO_RESPONSE)
|
|
{
|
|
ICMP_ECHO *echo = (ICMP_ECHO *)(recv_buffer + ip_header_size + sizeof(ICMP_HEADER));
|
|
if (icmp_packet_size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
|
|
{
|
|
if (Endian16(echo->Identifier) == src_id && (src_seqno == 0 || Endian16(echo->SeqNo) == src_seqno))
|
|
{
|
|
IP ip;
|
|
|
|
UINTToIP(&ip, ipv4->SrcIP);
|
|
|
|
// Received the correct Echo response
|
|
ret = ZeroMalloc(sizeof(ICMP_RESULT));
|
|
|
|
ret->Ok = true;
|
|
ret->Ttl = ipv4->TimeToLive;
|
|
ret->DataSize = icmp_packet_size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
|
|
ret->Data = Clone(recv_buffer + ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO),
|
|
ret->DataSize);
|
|
Copy(&ret->IpAddress, &ip, sizeof(IP));
|
|
}
|
|
}
|
|
}
|
|
else if (icmp->Type == ICMP_TYPE_ECHO_REQUEST)
|
|
{
|
|
// Ignore because an Echo request should not arrive
|
|
}
|
|
else
|
|
{
|
|
// If an error is returned, compare to the copy of
|
|
// the ICMP packet last sent
|
|
IPV4_HEADER *orig_ipv4 = (IPV4_HEADER *)(recv_buffer + ip_header_size + 4 + sizeof(ICMP_HEADER));
|
|
if (icmp_packet_size >= (sizeof(ICMP_HEADER) + 4 + sizeof(IPV4_HEADER)))
|
|
{
|
|
UINT orig_ipv4_header_size = GetIpHeaderSize((UCHAR *)orig_ipv4, icmp_packet_size - 4 - sizeof(ICMP_HEADER));
|
|
if (orig_ipv4_header_size >= sizeof(IPV4_HEADER))
|
|
{
|
|
if ((IPV4_GET_VERSION(orig_ipv4) == 4) && (orig_ipv4->Protocol == IP_PROTO_ICMPV4))
|
|
{
|
|
if (icmp_packet_size >= (sizeof(ICMP_HEADER) + 4 + orig_ipv4_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
|
|
{
|
|
ICMP_HEADER *orig_icmp = (ICMP_HEADER *)(recv_buffer + ip_header_size + sizeof(ICMP_HEADER) + 4 + orig_ipv4_header_size);
|
|
ICMP_ECHO *orig_echo = (ICMP_ECHO *)(recv_buffer + ip_header_size + sizeof(ICMP_HEADER) + 4 + orig_ipv4_header_size + sizeof(ICMP_HEADER));
|
|
|
|
if (orig_icmp->Type == ICMP_TYPE_ECHO_REQUEST && orig_echo->Identifier == Endian16(src_id) && (src_seqno == 0 || orig_echo->SeqNo == Endian16(src_seqno)))
|
|
{
|
|
IP ip;
|
|
|
|
UINTToIP(&ip, ipv4->SrcIP);
|
|
|
|
ret = ZeroMalloc(sizeof(ICMP_RESULT));
|
|
|
|
ret->Type = icmp->Type;
|
|
ret->Code = icmp->Code;
|
|
ret->Ttl = ipv4->TimeToLive;
|
|
ret->DataSize = icmp_packet_size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
|
|
ret->Data = Clone(recv_buffer + ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO),
|
|
ret->DataSize);
|
|
Copy(&ret->IpAddress, &ip, sizeof(IP));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Send the ICMP Echo (by a socket)
|
|
ICMP_RESULT *IcmpEchoSendBySocket(IP *dest_ip, UCHAR ttl, UCHAR *data, UINT size, UINT timeout)
|
|
{
|
|
SOCK *s;
|
|
ICMP_RESULT *ret = NULL;
|
|
USHORT id;
|
|
USHORT seq;
|
|
UINT64 sent_tick;
|
|
UINT64 recv_tick;
|
|
// Validate arguments
|
|
if (dest_ip == NULL || IsIP4(dest_ip) == false || (size != 0 && data == NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
if (ttl == 0)
|
|
{
|
|
ttl = 127;
|
|
}
|
|
|
|
s = NewUDP4(MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4), NULL);
|
|
if (s != NULL)
|
|
{
|
|
// Construction of the ICMP packet
|
|
UCHAR *send_buffer;
|
|
UINT send_buffer_size = sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + size;
|
|
ICMP_HEADER *send_icmp_header;
|
|
ICMP_ECHO *send_icmp_echo;
|
|
UINT i;
|
|
|
|
id = Rand16();
|
|
if (id == 0)
|
|
{
|
|
id = 1;
|
|
}
|
|
|
|
seq = Rand16();
|
|
if (seq == 0)
|
|
{
|
|
seq = 1;
|
|
}
|
|
|
|
send_buffer = ZeroMalloc(send_buffer_size);
|
|
|
|
send_icmp_header = (ICMP_HEADER *)send_buffer;
|
|
send_icmp_header->Type = ICMP_TYPE_ECHO_REQUEST;
|
|
|
|
send_icmp_echo = (ICMP_ECHO *)(send_buffer + sizeof(ICMP_HEADER));
|
|
send_icmp_echo->Identifier = Endian16(id);
|
|
send_icmp_echo->SeqNo = Endian16(seq);
|
|
|
|
Copy(send_buffer + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO), data, size);
|
|
|
|
send_icmp_header->Checksum = IpChecksum(send_buffer, send_buffer_size);
|
|
|
|
// Send an ICMP
|
|
SetTtl(s, ttl);
|
|
sent_tick = TickHighres64();
|
|
i = SendTo(s, dest_ip, MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4), send_buffer, send_buffer_size);
|
|
|
|
if (i != 0 && i != INFINITE)
|
|
{
|
|
// ICMP response received
|
|
INTERRUPT_MANAGER *interrupt = NewInterruptManager();
|
|
UINT64 giveup_time = Tick64() + (UINT64)timeout;
|
|
UINT recv_buffer_size = (sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + size + 64) * 2;
|
|
UCHAR *recv_buffer = Malloc(recv_buffer_size);
|
|
|
|
AddInterrupt(interrupt, giveup_time);
|
|
|
|
while (true)
|
|
{
|
|
UINT interval = GetNextIntervalForInterrupt(interrupt);
|
|
IP src_ip;
|
|
UINT src_port;
|
|
SOCKSET set;
|
|
|
|
InitSockSet(&set);
|
|
AddSockSet(&set, s);
|
|
|
|
Select(&set, interval, NULL, NULL);
|
|
|
|
while (true)
|
|
{
|
|
Zero(recv_buffer, recv_buffer_size);
|
|
i = RecvFrom(s, &src_ip, &src_port, recv_buffer, recv_buffer_size);
|
|
recv_tick = TickHighres64();
|
|
|
|
if (i != 0 && i != SOCK_LATER)
|
|
{
|
|
ret = IcmpParseResult(dest_ip, id, seq, recv_buffer, i);
|
|
|
|
if (ret != NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (interval == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ret != NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreeInterruptManager(interrupt);
|
|
|
|
Free(recv_buffer);
|
|
|
|
if (ret == NULL)
|
|
{
|
|
ret = ZeroMalloc(sizeof(ICMP_RESULT));
|
|
|
|
ret->Timeout = true;
|
|
}
|
|
}
|
|
|
|
Free(send_buffer);
|
|
ReleaseSock(s);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Get whether the packet is a DHCP packet associated with the specified MAC address
|
|
bool IsDhcpPacketForSpecificMac(UCHAR *data, UINT size, UCHAR *mac_address)
|
|
{
|
|
USHORT *us;
|
|
IPV4_HEADER *ip;
|
|
UDP_HEADER *udp;
|
|
UINT ip_header_size;
|
|
bool is_send = false, is_recv = false;
|
|
// Validate arguments
|
|
if (data == NULL || mac_address == NULL || IsZero(mac_address, 6))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Whether the src or the dest matches
|
|
if (size < 14)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Destination MAC address
|
|
if (Cmp(data, mac_address, 6) == 0)
|
|
{
|
|
is_recv = true;
|
|
}
|
|
size -= 6;
|
|
data += 6;
|
|
|
|
// Source MAC address
|
|
if (Cmp(data, mac_address, 6) == 0)
|
|
{
|
|
is_send = true;
|
|
}
|
|
size -= 6;
|
|
data += 6;
|
|
|
|
if (is_send == false && is_recv == false)
|
|
{
|
|
return false;
|
|
}
|
|
if (is_send && is_recv)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TPID
|
|
us = (USHORT *)data;
|
|
size -= 2;
|
|
data += 2;
|
|
|
|
if (READ_USHORT(us) != MAC_PROTO_IPV4)
|
|
{
|
|
// Other than IPv4
|
|
return false;
|
|
}
|
|
|
|
// IP header
|
|
ip_header_size = GetIpHeaderSize(data, size);
|
|
if (ip_header_size == 0)
|
|
{
|
|
// IPv4 header analysis failure
|
|
return false;
|
|
}
|
|
|
|
ip = (IPV4_HEADER *)data;
|
|
data += ip_header_size;
|
|
size -= ip_header_size;
|
|
|
|
if (ip->Protocol != IP_PROTO_UDP)
|
|
{
|
|
// Not an UDP packet
|
|
return false;
|
|
}
|
|
|
|
// UDP header
|
|
if (size < sizeof(UDP_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
udp = (UDP_HEADER *)data;
|
|
data += sizeof(UDP_HEADER);
|
|
size -= sizeof(UDP_HEADER);
|
|
|
|
if (is_send)
|
|
{
|
|
// Detect whether it's a DHCP Request packet
|
|
if (Endian16(udp->DstPort) == 67)
|
|
{
|
|
Debug("IsDhcpPacketForSpecificMac: DHCP Request Packet is Detected.\n");
|
|
return true;
|
|
}
|
|
}
|
|
else if (is_recv)
|
|
{
|
|
// Detect whether it's a DHCP Response packet
|
|
if (Endian16(udp->SrcPort) == 67)
|
|
{
|
|
Debug("IsDhcpPacketForSpecificMac: DHCP Response Packet is Detected.\n");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Adjust the MSS of the TCP in the IP packet (L2)
|
|
bool AdjustTcpMssL2(UCHAR *src, UINT src_size, UINT mss, USHORT tag_vlan_tpid)
|
|
{
|
|
MAC_HEADER *mac;
|
|
USHORT proto;
|
|
// Validate arguments
|
|
if (src == NULL || src_size == 0 || mss == 0)
|
|
{
|
|
return false;
|
|
}
|
|
if (tag_vlan_tpid == 0)
|
|
{
|
|
tag_vlan_tpid = MAC_PROTO_TAGVLAN;
|
|
}
|
|
|
|
if (src_size < sizeof(MAC_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
mac = (MAC_HEADER *)src;
|
|
|
|
src += sizeof(MAC_HEADER);
|
|
src_size -= sizeof(MAC_HEADER);
|
|
|
|
proto = Endian16(mac->Protocol);
|
|
|
|
if (proto == MAC_PROTO_IPV4 || proto == MAC_PROTO_IPV6)
|
|
{
|
|
// Ordinary IPv4 / IPv6 packet
|
|
return AdjustTcpMssL3(src, src_size, mss);
|
|
}
|
|
else if (proto == tag_vlan_tpid)
|
|
{
|
|
// IPv4 / IPv6 packets in the VLAN tag
|
|
if (src_size < 4)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
src += 2;
|
|
src_size -= 2;
|
|
|
|
proto = READ_USHORT(src);
|
|
|
|
if (proto == MAC_PROTO_IPV4 || proto == MAC_PROTO_IPV6)
|
|
{
|
|
if (mss >= 5)
|
|
{
|
|
mss -= 4;
|
|
|
|
src += 2;
|
|
src_size -= 2;
|
|
|
|
return AdjustTcpMssL3(src, src_size, mss);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Get an IP header size
|
|
UINT GetIpHeaderSize(UCHAR *src, UINT src_size)
|
|
{
|
|
UCHAR ip_ver;
|
|
TCP_HEADER *tcp = NULL;
|
|
UINT tcp_size = 0;
|
|
IPV4_HEADER *ip = NULL;
|
|
IPV6_HEADER *ip6 = NULL;
|
|
// Validate arguments
|
|
if (src == NULL || src_size == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Get the IP version number
|
|
ip_ver = (src[0] >> 4) & 0x0f;
|
|
|
|
if (ip_ver == 4)
|
|
{
|
|
// IPv4
|
|
UINT ip_header_size;
|
|
if (src_size < sizeof(IPV4_HEADER))
|
|
{
|
|
// No IPv4 header
|
|
return 0;
|
|
}
|
|
|
|
ip = (IPV4_HEADER *)src;
|
|
|
|
ip_header_size = IPV4_GET_HEADER_LEN(ip) * 4;
|
|
if (ip_header_size < sizeof(IPV4_HEADER))
|
|
{
|
|
// Header size is invalid
|
|
return 0;
|
|
}
|
|
|
|
if (src_size < ip_header_size)
|
|
{
|
|
// No IPv4 header
|
|
return 0;
|
|
}
|
|
|
|
return ip_header_size;
|
|
}
|
|
else if (ip_ver == 6)
|
|
{
|
|
// IPv6
|
|
IPV6_HEADER_PACKET_INFO v6;
|
|
|
|
if (ParsePacketIPv6Header(&v6, src, src_size) == false)
|
|
{
|
|
// IPv6 analysis failure
|
|
return 0;
|
|
}
|
|
|
|
ip6 = v6.IPv6Header;
|
|
if (ip6 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (src_size < v6.TotalHeaderSize)
|
|
{
|
|
// No header data
|
|
return 0;
|
|
}
|
|
|
|
return v6.TotalHeaderSize;
|
|
}
|
|
else
|
|
{
|
|
// Invalid
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Adjust the MSS of TCP in the IP packet (L3)
|
|
bool AdjustTcpMssL3(UCHAR *src, UINT src_size, UINT mss)
|
|
{
|
|
UCHAR ip_ver;
|
|
TCP_HEADER *tcp = NULL;
|
|
UINT tcp_size = 0;
|
|
UINT tcp_header_size;
|
|
UCHAR *options;
|
|
UINT options_size;
|
|
IPV4_HEADER *ip = NULL;
|
|
IPV6_HEADER *ip6 = NULL;
|
|
// Validate arguments
|
|
if (src == NULL || src_size == 0 || mss == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the IP version number
|
|
ip_ver = (src[0] >> 4) & 0x0f;
|
|
|
|
if (ip_ver == 4)
|
|
{
|
|
UINT ip_header_size;
|
|
UINT ip_total_length;
|
|
// IPv4
|
|
if (src_size < sizeof(IPV4_HEADER))
|
|
{
|
|
// No IPv4 header
|
|
return false;
|
|
}
|
|
|
|
ip = (IPV4_HEADER *)src;
|
|
|
|
if (ip->Protocol != IP_PROTO_TCP)
|
|
{
|
|
// Non-TCP
|
|
return false;
|
|
}
|
|
|
|
if (IPV4_GET_OFFSET(ip) != 0)
|
|
{
|
|
// It is the second or later packet of fragmented packet
|
|
return false;
|
|
}
|
|
|
|
if (IPV4_GET_FLAGS(ip) & 0x01)
|
|
{
|
|
// Fragmented packet
|
|
return false;
|
|
}
|
|
|
|
ip_header_size = IPV4_GET_HEADER_LEN(ip) * 4;
|
|
if (ip_header_size < sizeof(IPV4_HEADER))
|
|
{
|
|
// Header size is invalid
|
|
return false;
|
|
}
|
|
|
|
if (src_size < ip_header_size)
|
|
{
|
|
// No IPv4 header
|
|
return false;
|
|
}
|
|
|
|
ip_total_length = READ_USHORT(&ip->TotalLength);
|
|
|
|
if (ip_total_length < ip_header_size)
|
|
{
|
|
// Invalid total length
|
|
return false;
|
|
}
|
|
|
|
if (src_size < ip_total_length)
|
|
{
|
|
// No total length
|
|
return false;
|
|
}
|
|
|
|
src += ip_header_size;
|
|
src_size = ip_total_length - ip_header_size;
|
|
|
|
if (src_size < sizeof(TCP_HEADER))
|
|
{
|
|
// No TCP header
|
|
return false;
|
|
}
|
|
|
|
tcp = (TCP_HEADER *)src;
|
|
tcp_size = src_size;
|
|
}
|
|
else if (ip_ver == 6)
|
|
{
|
|
// IPv6
|
|
IPV6_HEADER_PACKET_INFO v6;
|
|
|
|
if (ParsePacketIPv6Header(&v6, src, src_size) == false)
|
|
{
|
|
// IPv6 analysis failure
|
|
return false;
|
|
}
|
|
|
|
ip6 = v6.IPv6Header;
|
|
if (ip6 == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (v6.Protocol != IP_PROTO_TCP)
|
|
{
|
|
// Non-TCP
|
|
return false;
|
|
}
|
|
|
|
if (v6.IsFragment)
|
|
{
|
|
// It is the second or later packet of fragmented packet
|
|
return false;
|
|
}
|
|
|
|
if (v6.FragmentHeader != NULL)
|
|
{
|
|
if (IPV6_GET_FLAGS(v6.FragmentHeader) & IPV6_FRAGMENT_HEADER_FLAG_MORE_FRAGMENTS)
|
|
{
|
|
// Fragmented packet
|
|
return false;
|
|
}
|
|
}
|
|
|
|
tcp = (TCP_HEADER *)v6.Payload;
|
|
tcp_size = v6.PayloadSize;
|
|
}
|
|
else
|
|
{
|
|
// This isn't either IPv4, IPv6
|
|
return false;
|
|
}
|
|
|
|
// Processing of the TCP header
|
|
if (tcp == NULL || tcp_size < sizeof(TCP_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tcp_header_size = TCP_GET_HEADER_SIZE(tcp) * 4;
|
|
if (tcp_header_size < sizeof(TCP_HEADER))
|
|
{
|
|
// TCP header size is invalid
|
|
return false;
|
|
}
|
|
|
|
if (tcp_size < tcp_header_size)
|
|
{
|
|
// Packet length shortage
|
|
return false;
|
|
}
|
|
|
|
if (((tcp->Flag & TCP_SYN) == false) ||
|
|
((tcp->Flag & TCP_RST) ||
|
|
(tcp->Flag & TCP_PSH) ||
|
|
(tcp->Flag & TCP_URG)))
|
|
{
|
|
// Not a SYN packet
|
|
return false;
|
|
}
|
|
|
|
// Get the option field
|
|
options = ((UCHAR *)tcp) + sizeof(TCP_HEADER);
|
|
options_size = tcp_header_size - sizeof(TCP_HEADER);
|
|
|
|
if (ip6 != NULL)
|
|
{
|
|
// Reduce MSS by 20 since an IP header for IPv6 is 20 bytes larger than IPv4
|
|
if (mss >= 20)
|
|
{
|
|
mss -= 20;
|
|
}
|
|
}
|
|
|
|
// MSS should be at least 64
|
|
mss = MAX(mss, 64);
|
|
|
|
if (options_size >= 4 && options[0] == 0x02 && options[1] == 0x04)
|
|
{
|
|
// MSS option of TCP is added
|
|
USHORT current_mss = READ_USHORT(((UCHAR *)options) + 2);
|
|
|
|
if (current_mss <= mss)
|
|
{
|
|
// if the value of the MSS is smaller than the specified size
|
|
// from the beginning, it doesn't need to be rewritten
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
WRITE_USHORT(((UCHAR *)options) + 2, mss);
|
|
|
|
// Clear the checksum
|
|
tcp->Checksum = 0;
|
|
|
|
if (ip != NULL)
|
|
{
|
|
// Calculate the TCPv4 checksum
|
|
tcp->Checksum = CalcChecksumForIPv4(ip->SrcIP, ip->DstIP, IP_PROTO_TCP, tcp, tcp_size, 0);
|
|
}
|
|
else
|
|
{
|
|
// Calculate the TCPv6 checksum
|
|
tcp->Checksum = CalcChecksumForIPv6(&ip6->SrcAddress, &ip6->DestAddress,
|
|
IP_PROTO_TCP, tcp, tcp_size, 0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// MSS option of TCP is not added
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// Parse the DHCPv4 packet
|
|
DHCPV4_DATA *ParseDHCPv4Data(PKT *pkt)
|
|
{
|
|
DHCPV4_DATA *d;
|
|
UCHAR *data;
|
|
UINT size;
|
|
UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE);
|
|
bool ok = false;
|
|
DHCP_OPTION *o;
|
|
// Validate arguments
|
|
if (pkt == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (pkt->TypeL3 != L3_IPV4 || pkt->TypeL4 != L4_UDP || pkt->TypeL7 != L7_DHCPV4)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
d = ZeroMalloc(sizeof(DHCPV4_DATA));
|
|
d->Size = (UINT)(pkt->PacketSize - (((UCHAR *)pkt->L7.PointerL7) - ((UCHAR *)pkt->PacketData)));
|
|
d->Data = Clone(pkt->L7.PointerL7, d->Size);
|
|
|
|
if (d->Size < sizeof(DHCPV4_HEADER))
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
// Header
|
|
d->Header = (DHCPV4_HEADER *)d->Data;
|
|
|
|
data = d->Data;
|
|
size = d->Size;
|
|
|
|
// Search for the Magic Cookie
|
|
ok = false;
|
|
while (size >= 5)
|
|
{
|
|
if (Cmp(data, &magic_cookie, 4) == 0)
|
|
{
|
|
// Found
|
|
data += 4;
|
|
size -= 4;
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
data++;
|
|
size--;
|
|
}
|
|
|
|
if (ok == false)
|
|
{
|
|
// Magic Cookie not found
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
// Parse the DHCP Options
|
|
d->OptionData = data;
|
|
d->OptionSize = size;
|
|
|
|
d->OptionList = ParseDhcpOptions(data, size);
|
|
if (d->OptionList == NULL)
|
|
{
|
|
// Parsing failure
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
UINTToIP(&d->SrcIP, pkt->L3.IPv4Header->SrcIP);
|
|
UINTToIP(&d->DestIP, pkt->L3.IPv4Header->DstIP);
|
|
|
|
d->SrcPort = Endian16(pkt->L4.UDPHeader->SrcPort);
|
|
d->DestPort = Endian16(pkt->L4.UDPHeader->DstPort);
|
|
|
|
o = GetDhcpOption(d->OptionList, DHCP_ID_MESSAGE_TYPE);
|
|
if (o == NULL || o->Size != 1)
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
d->OpCode = *((UCHAR *)o->Data);
|
|
|
|
d->ParsedOptionList = ParseDhcpOptionList(d->OptionData, d->OptionSize);
|
|
|
|
if (d->ParsedOptionList == NULL)
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
if (d->ParsedOptionList->ServerAddress == 0)
|
|
{
|
|
d->ParsedOptionList->ServerAddress = d->Header->ServerIP;
|
|
}
|
|
|
|
d->ParsedOptionList->ClientAddress = d->Header->YourIP;
|
|
|
|
return d;
|
|
|
|
LABEL_ERROR:
|
|
FreeDHCPv4Data(d);
|
|
return NULL;
|
|
}
|
|
|
|
// Release the DHCPv4 packet
|
|
void FreeDHCPv4Data(DHCPV4_DATA *d)
|
|
{
|
|
// Validate arguments
|
|
if (d == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreeDhcpOptions(d->OptionList);
|
|
Free(d->Data);
|
|
|
|
Free(d->ParsedOptionList);
|
|
|
|
Free(d);
|
|
}
|
|
|
|
// Embed a VLAN tag to the packet
|
|
void VLanInsertTag(void **packet_data, UINT *packet_size, UINT vlan_id, UINT vlan_tpid)
|
|
{
|
|
UINT dest_size;
|
|
UCHAR *dest_data;
|
|
UINT src_size;
|
|
UCHAR *src_data;
|
|
USHORT vlan_ushort = Endian16(((USHORT)vlan_id) & 0xFFF);
|
|
USHORT vlan_tpid_ushort;
|
|
// Validate arguments
|
|
if (packet_data == NULL || *packet_data == NULL || packet_size == NULL ||
|
|
*packet_size < 14 || vlan_id == 0)
|
|
{
|
|
return;
|
|
}
|
|
if (vlan_tpid == 0)
|
|
{
|
|
vlan_tpid = MAC_PROTO_TAGVLAN;
|
|
}
|
|
|
|
vlan_tpid_ushort = Endian16((USHORT)vlan_tpid);
|
|
|
|
src_size = *packet_size;
|
|
src_data = (UCHAR *)(*packet_data);
|
|
|
|
dest_size = src_size + 4;
|
|
dest_data = Malloc(dest_size);
|
|
|
|
Copy(&dest_data[12], &vlan_tpid_ushort, sizeof(USHORT));
|
|
Copy(&dest_data[14], &vlan_ushort, sizeof(USHORT));
|
|
|
|
Copy(&dest_data[0], &src_data[0], 12);
|
|
Copy(&dest_data[16], &src_data[12], src_size - 12);
|
|
|
|
*packet_size = dest_size;
|
|
*packet_data = dest_data;
|
|
|
|
Free(src_data);
|
|
}
|
|
|
|
// Remove the VLAN tag from the packet
|
|
bool VLanRemoveTag(void **packet_data, UINT *packet_size, UINT vlan_id, UINT vlan_tpid)
|
|
{
|
|
bool has_vlan_tag = false;
|
|
UCHAR *src_data;
|
|
UINT src_size;
|
|
USHORT vlan_tpid_ushort;
|
|
UCHAR *vlan_tpid_uchar;
|
|
// Validate arguments
|
|
if (packet_data == NULL || *packet_data == NULL || packet_size == NULL ||
|
|
*packet_size < 14)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (vlan_tpid == 0)
|
|
{
|
|
vlan_tpid = MAC_PROTO_TAGVLAN;
|
|
}
|
|
|
|
vlan_tpid_ushort = Endian16((USHORT)vlan_tpid);
|
|
vlan_tpid_uchar = (UCHAR *)(&vlan_tpid_ushort);
|
|
|
|
src_data = (UCHAR *)(*packet_data);
|
|
src_size = *packet_size;
|
|
|
|
if (src_data[12] == vlan_tpid_uchar[0] && src_data[13] == vlan_tpid_uchar[1])
|
|
{
|
|
if (src_size >= 18)
|
|
{
|
|
USHORT vlan_ushort;
|
|
|
|
vlan_ushort = READ_USHORT(&src_data[14]);
|
|
vlan_ushort = vlan_ushort & 0xFFF;
|
|
|
|
if (vlan_id == 0 || (vlan_ushort == vlan_id))
|
|
{
|
|
UINT dest_size = src_size - 4;
|
|
UINT i;
|
|
|
|
for (i = 12;i < dest_size;i++)
|
|
{
|
|
src_data[i] = src_data[i + 4];
|
|
}
|
|
|
|
*packet_size = dest_size;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Sending of an ICMPv6 packet
|
|
BUF *BuildICMPv6(IPV6_ADDR *src_ip, IPV6_ADDR *dest_ip, UCHAR hop_limit, UCHAR type, UCHAR code, void *data, UINT size, UINT id)
|
|
{
|
|
ICMP_HEADER *icmp;
|
|
void *data_buf;
|
|
BUF *ret;
|
|
// Validate arguments
|
|
if (src_ip == NULL || dest_ip == NULL || data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Assembe the header
|
|
icmp = ZeroMalloc(sizeof(ICMP_HEADER) + size);
|
|
data_buf = ((UCHAR *)icmp) + sizeof(ICMP_HEADER);
|
|
Copy(data_buf, data, size);
|
|
|
|
icmp->Type = type;
|
|
icmp->Code = code;
|
|
icmp->Checksum = CalcChecksumForIPv6(src_ip, dest_ip, IP_PROTO_ICMPV6, icmp,
|
|
sizeof(ICMP_HEADER) + size, 0);
|
|
|
|
ret = BuildIPv6(dest_ip, src_ip, id, IP_PROTO_ICMPV6, hop_limit, icmp,
|
|
sizeof(ICMP_HEADER) + size);
|
|
|
|
Free(icmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Build an ICMPv6 Neighbor Solicitation packet
|
|
BUF *BuildICMPv6NeighborSoliciation(IPV6_ADDR *src_ip, IPV6_ADDR *target_ip, UCHAR *my_mac_address, UINT id)
|
|
{
|
|
ICMPV6_OPTION_LIST opt;
|
|
ICMPV6_OPTION_LINK_LAYER link;
|
|
ICMPV6_NEIGHBOR_SOLICIATION_HEADER header;
|
|
BUF *b;
|
|
BUF *b2;
|
|
BUF *ret;
|
|
// Validate arguments
|
|
if (src_ip == NULL || target_ip == NULL || my_mac_address == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Zero(&link, sizeof(link));
|
|
Copy(link.Address, my_mac_address, 6);
|
|
|
|
Zero(&opt, sizeof(opt));
|
|
opt.SourceLinkLayer = &link;
|
|
|
|
b = BuildICMPv6Options(&opt);
|
|
|
|
Zero(&header, sizeof(header));
|
|
Copy(&header.TargetAddress, target_ip, sizeof(IPV6_ADDR));
|
|
|
|
b2 = NewBuf();
|
|
|
|
WriteBuf(b2, &header, sizeof(header));
|
|
WriteBufBuf(b2, b);
|
|
|
|
ret = BuildICMPv6(src_ip, target_ip, 255,
|
|
ICMPV6_TYPE_NEIGHBOR_SOLICIATION, 0, b2->Buf, b2->Size, id);
|
|
|
|
FreeBuf(b);
|
|
FreeBuf(b2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Get the next header number from the queue
|
|
UCHAR IPv6GetNextHeaderFromQueue(QUEUE *q)
|
|
{
|
|
UINT *p;
|
|
UCHAR v;
|
|
// Validate arguments
|
|
if (q == NULL)
|
|
{
|
|
return IPV6_HEADER_NONE;
|
|
}
|
|
|
|
p = (UINT *)GetNext(q);
|
|
v = (UCHAR)(*p);
|
|
Free(p);
|
|
|
|
return v;
|
|
}
|
|
|
|
// Add an IPv6 extension header option (variable length)
|
|
void BuildAndAddIPv6PacketOptionHeader(BUF *b, IPV6_OPTION_HEADER *opt, UCHAR next_header, UINT size)
|
|
{
|
|
IPV6_OPTION_HEADER *h;
|
|
UINT total_size;
|
|
// Validate arguments
|
|
if (b == NULL || opt == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
total_size = size;
|
|
if ((total_size % 8) != 0)
|
|
{
|
|
total_size = ((total_size / 8) + 1) * 8;
|
|
}
|
|
|
|
h = ZeroMalloc(total_size);
|
|
Copy(h, opt, size);
|
|
h->Size = (total_size / 8) - 1;
|
|
h->NextHeader = next_header;
|
|
|
|
WriteBuf(b, h, total_size);
|
|
|
|
Free(h);
|
|
}
|
|
|
|
// Build an IPv6 packet
|
|
BUF *BuildIPv6(IPV6_ADDR *dest_ip, IPV6_ADDR *src_ip, UINT id, UCHAR protocol, UCHAR hop_limit, void *data,
|
|
UINT size)
|
|
{
|
|
IPV6_HEADER_PACKET_INFO info;
|
|
IPV6_HEADER ip_header;
|
|
BUF *buf;
|
|
UINT size_for_headers;
|
|
// Validate arguments
|
|
if (dest_ip == NULL || src_ip == NULL || data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (hop_limit == 0)
|
|
{
|
|
hop_limit = 255;
|
|
}
|
|
|
|
// IPv6 header
|
|
Zero(&ip_header, sizeof(ip_header));
|
|
IPV6_SET_VERSION(&ip_header, 6);
|
|
ip_header.HopLimit = hop_limit;
|
|
Copy(&ip_header.SrcAddress, src_ip, sizeof(IPV6_ADDR));
|
|
Copy(&ip_header.DestAddress, dest_ip, sizeof(IPV6_ADDR));
|
|
|
|
// Arrangement of the packet header information
|
|
Zero(&info, sizeof(info));
|
|
info.IPv6Header = &ip_header;
|
|
info.Protocol = protocol;
|
|
info.Payload = data;
|
|
info.PayloadSize = size;
|
|
|
|
buf = BuildIPv6PacketHeader(&info, &size_for_headers);
|
|
if (buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
// Build the IPv6 packet header section
|
|
BUF *BuildIPv6PacketHeader(IPV6_HEADER_PACKET_INFO *info, UINT *bytes_before_payload)
|
|
{
|
|
BUF *b;
|
|
QUEUE *q;
|
|
UINT bbp = 0;
|
|
// Validate arguments
|
|
if (info == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
q = NewQueueFast();
|
|
|
|
// Create the list of options headers
|
|
if (info->HopHeader != NULL)
|
|
{
|
|
InsertQueueInt(q, IPV6_HEADER_HOP);
|
|
}
|
|
if (info->EndPointHeader != NULL)
|
|
{
|
|
InsertQueueInt(q, IPV6_HEADER_ENDPOINT);
|
|
}
|
|
if (info->RoutingHeader != NULL)
|
|
{
|
|
InsertQueueInt(q, IPV6_HEADER_ROUTING);
|
|
}
|
|
if (info->FragmentHeader != NULL)
|
|
{
|
|
InsertQueueInt(q, IPV6_HEADER_FRAGMENT);
|
|
}
|
|
InsertQueueInt(q, info->Protocol);
|
|
|
|
// IPv6 header
|
|
info->IPv6Header->NextHeader = IPv6GetNextHeaderFromQueue(q);
|
|
WriteBuf(b, info->IPv6Header, sizeof(IPV6_HEADER));
|
|
|
|
// Hop-by-hop option header
|
|
if (info->HopHeader != NULL)
|
|
{
|
|
BuildAndAddIPv6PacketOptionHeader(b, info->HopHeader,
|
|
IPv6GetNextHeaderFromQueue(q), info->HopHeaderSize);
|
|
}
|
|
|
|
// End point option header
|
|
if (info->EndPointHeader != NULL)
|
|
{
|
|
BuildAndAddIPv6PacketOptionHeader(b, info->EndPointHeader,
|
|
IPv6GetNextHeaderFromQueue(q), info->EndPointHeaderSize);
|
|
}
|
|
|
|
// Routing header
|
|
if (info->RoutingHeader != NULL)
|
|
{
|
|
BuildAndAddIPv6PacketOptionHeader(b, info->RoutingHeader,
|
|
IPv6GetNextHeaderFromQueue(q), info->RoutingHeaderSize);
|
|
}
|
|
|
|
// Fragment header
|
|
if (info->FragmentHeader != NULL)
|
|
{
|
|
info->FragmentHeader->NextHeader = IPv6GetNextHeaderFromQueue(q);
|
|
WriteBuf(b, info->FragmentHeader, sizeof(IPV6_FRAGMENT_HEADER));
|
|
}
|
|
|
|
bbp = b->Size;
|
|
if (info->FragmentHeader == NULL)
|
|
{
|
|
bbp += sizeof(IPV6_FRAGMENT_HEADER);
|
|
}
|
|
|
|
// Payload
|
|
if (info->Protocol != IPV6_HEADER_NONE)
|
|
{
|
|
WriteBuf(b, info->Payload, info->PayloadSize);
|
|
}
|
|
|
|
ReleaseQueue(q);
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
// Payload length
|
|
((IPV6_HEADER *)b->Buf)->PayloadLength = Endian16(b->Size - (USHORT)sizeof(IPV6_HEADER));
|
|
|
|
if (bytes_before_payload != NULL)
|
|
{
|
|
// Calculate the length just before the payload
|
|
// (by assuming fragment header is always included)
|
|
*bytes_before_payload = bbp;
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
// Build the option values of an ICMPv6 packet
|
|
void BuildICMPv6OptionValue(BUF *b, UCHAR type, void *header_pointer, UINT total_size)
|
|
{
|
|
UINT packet_size;
|
|
UCHAR *packet;
|
|
ICMPV6_OPTION *opt;
|
|
// Validate arguments
|
|
if (b == NULL || header_pointer == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
packet_size = ((total_size + 7) / 8) * 8;
|
|
packet = ZeroMalloc(packet_size);
|
|
|
|
Copy(packet, header_pointer, total_size);
|
|
opt = (ICMPV6_OPTION *)packet;
|
|
opt->Length = (UCHAR)(packet_size / 8);
|
|
opt->Type = type;
|
|
|
|
WriteBuf(b, packet, packet_size);
|
|
|
|
Free(packet);
|
|
}
|
|
|
|
// Build the options of the ICMPv6 packet
|
|
BUF *BuildICMPv6Options(ICMPV6_OPTION_LIST *o)
|
|
{
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
if (o->SourceLinkLayer != NULL)
|
|
{
|
|
BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER, o->SourceLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
|
|
}
|
|
if (o->TargetLinkLayer != NULL)
|
|
{
|
|
BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_TARGET_LINK_LAYER, o->TargetLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
|
|
}
|
|
if (o->Prefix != NULL)
|
|
{
|
|
BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_PREFIX, o->Prefix, sizeof(ICMPV6_OPTION_PREFIX));
|
|
}
|
|
if (o->Mtu != NULL)
|
|
{
|
|
BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_MTU, o->Mtu, sizeof(ICMPV6_OPTION_MTU));
|
|
}
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Checksum calculation (IPv4)
|
|
USHORT CalcChecksumForIPv4(UINT src_ip, UINT dst_ip, UCHAR protocol, void *data, UINT size, UINT real_size)
|
|
{
|
|
UCHAR *tmp;
|
|
UINT tmp_size;
|
|
IPV4_PSEUDO_HEADER *ph;
|
|
USHORT ret;
|
|
bool use_free = false;
|
|
UCHAR tmp_buffer[1600];
|
|
// Validate arguments
|
|
if (data == NULL && size != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (real_size == 0)
|
|
{
|
|
real_size = size;
|
|
}
|
|
|
|
if (real_size == INFINITE)
|
|
{
|
|
real_size = 0;
|
|
}
|
|
|
|
tmp_size = size + sizeof(IPV4_PSEUDO_HEADER);
|
|
|
|
if (tmp_size > sizeof(tmp_buffer))
|
|
{
|
|
tmp = Malloc(tmp_size);
|
|
|
|
use_free = true;
|
|
}
|
|
else
|
|
{
|
|
tmp = tmp_buffer;
|
|
}
|
|
|
|
ph = (IPV4_PSEUDO_HEADER *)tmp;
|
|
ph->SrcIP = src_ip;
|
|
ph->DstIP = dst_ip;
|
|
ph->PacketLength = Endian16(real_size);
|
|
ph->Protocol = protocol;
|
|
ph->Reserved = 0;
|
|
|
|
if (size >= 1)
|
|
{
|
|
Copy(((UCHAR *)tmp) + sizeof(IPV4_PSEUDO_HEADER), data, size);
|
|
}
|
|
|
|
ret = IpChecksum(tmp, tmp_size);
|
|
|
|
if (use_free)
|
|
{
|
|
Free(tmp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Checksum calculation (IPv6)
|
|
USHORT CalcChecksumForIPv6(IPV6_ADDR *src_ip, IPV6_ADDR *dest_ip, UCHAR protocol, void *data, UINT size, UINT real_size)
|
|
{
|
|
UCHAR *tmp;
|
|
UINT tmp_size;
|
|
IPV6_PSEUDO_HEADER *ph;
|
|
USHORT ret;
|
|
bool use_free = false;
|
|
UCHAR tmp_buffer[256];
|
|
// Validate arguments
|
|
if (data == NULL && size != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (real_size == 0)
|
|
{
|
|
real_size = size;
|
|
}
|
|
|
|
if (real_size == INFINITE)
|
|
{
|
|
real_size = 0;
|
|
}
|
|
|
|
tmp_size = size + sizeof(IPV6_PSEUDO_HEADER);
|
|
|
|
if (tmp_size > sizeof(tmp_buffer))
|
|
{
|
|
tmp = Malloc(tmp_size);
|
|
|
|
use_free = true;
|
|
}
|
|
else
|
|
{
|
|
tmp = tmp_buffer;
|
|
}
|
|
|
|
ph = (IPV6_PSEUDO_HEADER *)tmp;
|
|
Zero(ph, sizeof(IPV6_PSEUDO_HEADER));
|
|
Copy(&ph->SrcAddress, src_ip, sizeof(IPV6_ADDR));
|
|
Copy(&ph->DestAddress, dest_ip, sizeof(IPV6_ADDR));
|
|
ph->UpperLayerPacketSize = Endian32(real_size);
|
|
ph->NextHeader = protocol;
|
|
|
|
Copy(((UCHAR *)tmp) + sizeof(IPV6_PSEUDO_HEADER), data, size);
|
|
|
|
ret = IpChecksum(tmp, tmp_size);
|
|
|
|
if (use_free)
|
|
{
|
|
Free(tmp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Release the cloned packet
|
|
void FreeClonePacket(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Free(p->IPv6HeaderPacketInfo.IPv6Header);
|
|
Free(p->IPv6HeaderPacketInfo.HopHeader);
|
|
Free(p->IPv6HeaderPacketInfo.EndPointHeader);
|
|
Free(p->IPv6HeaderPacketInfo.RoutingHeader);
|
|
Free(p->IPv6HeaderPacketInfo.FragmentHeader);
|
|
Free(p->IPv6HeaderPacketInfo.Payload);
|
|
Free(p->ICMPv6HeaderPacketInfo.Data);
|
|
Free(p->ICMPv6HeaderPacketInfo.EchoData);
|
|
Free(p->ICMPv6HeaderPacketInfo.Headers.HeaderPointer);
|
|
FreeCloneICMPv6Options(&p->ICMPv6HeaderPacketInfo.OptionList);
|
|
Free(p->L3.PointerL3);
|
|
Free(p->L4.PointerL4);
|
|
Free(p->L7.PointerL7);
|
|
Free(p->PacketData);
|
|
Free(p->MacHeader);
|
|
Free(p->HttpLog);
|
|
Free(p);
|
|
}
|
|
|
|
// Copy the packet header
|
|
PKT *ClonePacket(PKT *p, bool copy_data)
|
|
{
|
|
PKT *ret;
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMallocFast(sizeof(PKT));
|
|
ret->PacketSize = p->PacketSize;
|
|
|
|
// Copy of the MAC header
|
|
ret->MacHeader = MallocFast(sizeof(MAC_HEADER));
|
|
Copy(ret->MacHeader, p->MacHeader, sizeof(MAC_HEADER));
|
|
|
|
// Copy of the MAC flag
|
|
ret->BroadcastPacket = p->BroadcastPacket;
|
|
ret->InvalidSourcePacket = p->InvalidSourcePacket;
|
|
|
|
// Copy of the IPv6 related structure
|
|
Copy(&ret->IPv6HeaderPacketInfo, &p->IPv6HeaderPacketInfo, sizeof(IPV6_HEADER_PACKET_INFO));
|
|
Copy(&ret->ICMPv6HeaderPacketInfo, &p->ICMPv6HeaderPacketInfo, sizeof(ICMPV6_HEADER_INFO));
|
|
|
|
// Layer 3
|
|
ret->TypeL3 = p->TypeL3;
|
|
switch (ret->TypeL3)
|
|
{
|
|
case L3_ARPV4:
|
|
// ARP packet
|
|
ret->L3.ARPv4Header = MallocFast(sizeof(ARPV4_HEADER));
|
|
Copy(ret->L3.ARPv4Header, p->L3.ARPv4Header, sizeof(ARPV4_HEADER));
|
|
break;
|
|
|
|
case L3_IPV4:
|
|
// IPv4 packet
|
|
ret->L3.IPv4Header = MallocFast(sizeof(IPV4_HEADER));
|
|
Copy(ret->L3.IPv4Header, p->L3.IPv4Header, sizeof(IPV4_HEADER));
|
|
break;
|
|
|
|
case L3_IPV6:
|
|
// IPv6 packet
|
|
ret->L3.IPv6Header = MallocFast(sizeof(IPV6_HEADER));
|
|
Copy(ret->L3.IPv6Header, p->L3.IPv6Header, sizeof(IPV6_HEADER));
|
|
|
|
ret->IPv6HeaderPacketInfo.IPv6Header = Clone(p->IPv6HeaderPacketInfo.IPv6Header,
|
|
sizeof(IPV6_HEADER));
|
|
|
|
ret->IPv6HeaderPacketInfo.HopHeader = Clone(p->IPv6HeaderPacketInfo.HopHeader,
|
|
sizeof(IPV6_OPTION_HEADER));
|
|
|
|
ret->IPv6HeaderPacketInfo.EndPointHeader = Clone(p->IPv6HeaderPacketInfo.EndPointHeader,
|
|
sizeof(IPV6_OPTION_HEADER));
|
|
|
|
ret->IPv6HeaderPacketInfo.RoutingHeader = Clone(p->IPv6HeaderPacketInfo.RoutingHeader,
|
|
sizeof(IPV6_OPTION_HEADER));
|
|
|
|
ret->IPv6HeaderPacketInfo.FragmentHeader = Clone(p->IPv6HeaderPacketInfo.FragmentHeader,
|
|
sizeof(IPV6_FRAGMENT_HEADER));
|
|
|
|
ret->IPv6HeaderPacketInfo.Payload = Clone(p->IPv6HeaderPacketInfo.Payload,
|
|
p->IPv6HeaderPacketInfo.PayloadSize);
|
|
break;
|
|
}
|
|
|
|
// Layer 4
|
|
ret->TypeL4 = p->TypeL4;
|
|
switch (ret->TypeL4)
|
|
{
|
|
case L4_ICMPV4:
|
|
// ICMPv4 packet
|
|
ret->L4.ICMPHeader = MallocFast(sizeof(ICMP_HEADER));
|
|
Copy(ret->L4.ICMPHeader, p->L4.ICMPHeader, sizeof(ICMP_HEADER));
|
|
break;
|
|
|
|
case L4_ICMPV6:
|
|
// ICMPv6 packet
|
|
ret->L4.ICMPHeader = MallocFast(sizeof(ICMP_HEADER));
|
|
Copy(ret->L4.ICMPHeader, p->L4.ICMPHeader, sizeof(ICMP_HEADER));
|
|
|
|
ret->ICMPv6HeaderPacketInfo.Data = Clone(p->ICMPv6HeaderPacketInfo.Data,
|
|
p->ICMPv6HeaderPacketInfo.DataSize);
|
|
|
|
ret->ICMPv6HeaderPacketInfo.EchoData = Clone(p->ICMPv6HeaderPacketInfo.EchoData,
|
|
p->ICMPv6HeaderPacketInfo.EchoDataSize);
|
|
|
|
switch (ret->ICMPv6HeaderPacketInfo.Type)
|
|
{
|
|
case ICMPV6_TYPE_ECHO_REQUEST:
|
|
case ICMPV6_TYPE_ECHO_RESPONSE:
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ROUTER_SOLICIATION:
|
|
ret->ICMPv6HeaderPacketInfo.Headers.RouterSoliciationHeader =
|
|
Clone(p->ICMPv6HeaderPacketInfo.Headers.RouterSoliciationHeader,
|
|
sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER));
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
|
ret->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader =
|
|
Clone(p->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader,
|
|
sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER));
|
|
break;
|
|
|
|
case ICMPV6_TYPE_NEIGHBOR_SOLICIATION:
|
|
ret->ICMPv6HeaderPacketInfo.Headers.NeighborSoliciationHeader =
|
|
Clone(p->ICMPv6HeaderPacketInfo.Headers.NeighborSoliciationHeader,
|
|
sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER));
|
|
break;
|
|
|
|
case ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT:
|
|
ret->ICMPv6HeaderPacketInfo.Headers.NeighborAdvertisementHeader =
|
|
Clone(p->ICMPv6HeaderPacketInfo.Headers.NeighborAdvertisementHeader,
|
|
sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER));
|
|
break;
|
|
}
|
|
|
|
CloneICMPv6Options(&ret->ICMPv6HeaderPacketInfo.OptionList,
|
|
&p->ICMPv6HeaderPacketInfo.OptionList);
|
|
break;
|
|
|
|
case L4_TCP:
|
|
// TCP packet
|
|
ret->L4.TCPHeader = MallocFast(sizeof(TCP_HEADER));
|
|
Copy(ret->L4.TCPHeader, p->L4.TCPHeader, sizeof(TCP_HEADER));
|
|
break;
|
|
|
|
case L4_UDP:
|
|
// UDP packet
|
|
ret->L4.UDPHeader = MallocFast(sizeof(UDP_HEADER));
|
|
Copy(ret->L4.UDPHeader, p->L4.UDPHeader, sizeof(UDP_HEADER));
|
|
break;
|
|
}
|
|
|
|
// Layer 7
|
|
ret->TypeL7 = p->TypeL7;
|
|
switch (ret->TypeL7)
|
|
{
|
|
case L7_DHCPV4:
|
|
// DHCP packet
|
|
ret->L7.DHCPv4Header = MallocFast(sizeof(DHCPV4_HEADER));
|
|
Copy(ret->L7.DHCPv4Header, p->L7.DHCPv4Header, sizeof(DHCPV4_HEADER));
|
|
break;
|
|
|
|
case L7_IKECONN:
|
|
// IKE packet
|
|
ret->L7.IkeHeader = MallocFast(sizeof(IKE_HEADER));
|
|
Copy(ret->L7.IkeHeader, p->L7.IkeHeader, sizeof(IKE_HEADER));
|
|
break;
|
|
|
|
case L7_DNS:
|
|
StrCpy(ret->DnsQueryHost, sizeof(ret->DnsQueryHost), p->DnsQueryHost);
|
|
break;
|
|
}
|
|
|
|
// Address data
|
|
ret->MacAddressSrc = ret->MacHeader->SrcAddress;
|
|
ret->MacAddressDest = ret->MacHeader->DestAddress;
|
|
|
|
if (copy_data)
|
|
{
|
|
// Copy also the packet body
|
|
ret->PacketData = MallocFast(p->PacketSize);
|
|
Copy(ret->PacketData, p->PacketData, p->PacketSize);
|
|
}
|
|
|
|
if (p->HttpLog != NULL)
|
|
{
|
|
ret->HttpLog = Clone(p->HttpLog, sizeof(HTTPLOG));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse the contents of the packet
|
|
PKT *ParsePacket(UCHAR *buf, UINT size)
|
|
{
|
|
return ParsePacketEx(buf, size, false);
|
|
}
|
|
PKT *ParsePacketEx(UCHAR *buf, UINT size, bool no_l3)
|
|
{
|
|
return ParsePacketEx2(buf, size, no_l3, 0);
|
|
}
|
|
PKT *ParsePacketEx2(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id)
|
|
{
|
|
return ParsePacketEx3(buf, size, no_l3, vlan_type_id, true);
|
|
}
|
|
PKT *ParsePacketEx3(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id, bool bridge_id_as_mac_address)
|
|
{
|
|
return ParsePacketEx4(buf, size, no_l3, vlan_type_id, bridge_id_as_mac_address, false, false);
|
|
}
|
|
PKT *ParsePacketEx4(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id, bool bridge_id_as_mac_address, bool no_http, bool correct_checksum)
|
|
{
|
|
PKT *p;
|
|
USHORT vlan_type_id_16;
|
|
// Validate arguments
|
|
if (buf == NULL || size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (vlan_type_id == 0)
|
|
{
|
|
vlan_type_id = MAC_PROTO_TAGVLAN;
|
|
}
|
|
|
|
vlan_type_id_16 = Endian16((USHORT)vlan_type_id);
|
|
|
|
p = ZeroMallocFast(sizeof(PKT));
|
|
|
|
p->VlanTypeID = vlan_type_id;
|
|
|
|
// If there is garbage after the payload in IPv4 and IPv6 packets, eliminate it
|
|
if (size >= 24)
|
|
{
|
|
if (buf[12] == 0x08 && buf[13] == 0x00)
|
|
{
|
|
USHORT ip_total_size2 = READ_USHORT(&buf[16]);
|
|
UINT mac_packet_size;
|
|
|
|
if (ip_total_size2 >= 1)
|
|
{
|
|
mac_packet_size = (UINT)ip_total_size2 + 14;
|
|
|
|
if (size > mac_packet_size)
|
|
{
|
|
size = mac_packet_size;
|
|
}
|
|
}
|
|
}
|
|
else if (buf[12] == 0x86 && buf[13] == 0xdd)
|
|
{
|
|
USHORT ip_payload_size_2 = READ_USHORT(&buf[18]);
|
|
UINT mac_packet_size;
|
|
|
|
if (ip_payload_size_2 >= 1)
|
|
{
|
|
mac_packet_size = (UINT)ip_payload_size_2 + 14 + 40;
|
|
|
|
if (size > mac_packet_size)
|
|
{
|
|
size = mac_packet_size;
|
|
}
|
|
}
|
|
}
|
|
else if (buf[12] == ((UCHAR *)&vlan_type_id_16)[0] && buf[13] == ((UCHAR *)&vlan_type_id_16)[1])
|
|
{
|
|
if (buf[16] == 0x08 && buf[17] == 0x00)
|
|
{
|
|
USHORT ip_total_size2 = READ_USHORT(&buf[20]);
|
|
UINT mac_packet_size;
|
|
|
|
if (ip_total_size2 >= 1)
|
|
{
|
|
mac_packet_size = (UINT)ip_total_size2 + 14 + 4;
|
|
|
|
if (size > mac_packet_size)
|
|
{
|
|
size = mac_packet_size;
|
|
}
|
|
}
|
|
}
|
|
else if (buf[16] == 0x86 && buf[17] == 0xdd)
|
|
{
|
|
USHORT ip_payload_size_2 = READ_USHORT(&buf[22]);
|
|
UINT mac_packet_size;
|
|
|
|
if (ip_payload_size_2 >= 1)
|
|
{
|
|
mac_packet_size = (UINT)ip_payload_size_2 + 14 + 40 + 4;
|
|
|
|
if (size > mac_packet_size)
|
|
{
|
|
size = mac_packet_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do parse
|
|
if (ParsePacketL2Ex(p, buf, size, no_l3) == false)
|
|
{
|
|
// Parsing failure
|
|
FreePacket(p);
|
|
return NULL;
|
|
}
|
|
|
|
p->PacketData = buf;
|
|
p->PacketSize = size;
|
|
|
|
p->MacAddressSrc = p->MacHeader->SrcAddress;
|
|
p->MacAddressDest = p->MacHeader->DestAddress;
|
|
|
|
if (bridge_id_as_mac_address)
|
|
{
|
|
if (p->TypeL3 == L3_BPDU)
|
|
{
|
|
if (p->L3.BpduHeader != NULL)
|
|
{
|
|
p->MacAddressSrc = p->L3.BpduHeader->BridgeMacAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (no_http == false)
|
|
{
|
|
USHORT port_raw = Endian16(80);
|
|
USHORT port_raw2 = Endian16(8080);
|
|
USHORT port_raw3 = Endian16(443);
|
|
USHORT port_raw4 = Endian16(3128);
|
|
|
|
// Analyze if the packet is a part of HTTP
|
|
if ((p->TypeL3 == L3_IPV4 || p->TypeL3 == L3_IPV6) && p->TypeL4 == L4_TCP)
|
|
{
|
|
TCP_HEADER *tcp = p->L4.TCPHeader;
|
|
if (tcp != NULL && (tcp->DstPort == port_raw || tcp->DstPort == port_raw2 || tcp->DstPort == port_raw4) &&
|
|
(!((tcp->Flag & TCP_SYN) || (tcp->Flag & TCP_RST) || (tcp->Flag & TCP_FIN))))
|
|
{
|
|
if (p->PayloadSize >= 1)
|
|
{
|
|
p->HttpLog = ParseHttpAccessLog(p);
|
|
}
|
|
}
|
|
if (tcp != NULL && tcp->DstPort == port_raw3 &&
|
|
(!((tcp->Flag & TCP_SYN) || (tcp->Flag & TCP_RST) || (tcp->Flag & TCP_FIN))))
|
|
{
|
|
if (p->PayloadSize >= 1)
|
|
{
|
|
p->HttpLog = ParseHttpsAccessLog(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->TypeL3 == L3_IPV4 && p->TypeL4 == L4_UDP && p->TypeL7 == L7_DHCPV4)
|
|
{
|
|
// Get the DHCP opcode
|
|
DHCPV4_DATA *d = ParseDHCPv4Data(p);
|
|
|
|
if (d != NULL)
|
|
{
|
|
p->DhcpOpCode = d->OpCode;
|
|
|
|
FreeDHCPv4Data(d);
|
|
}
|
|
}
|
|
|
|
if (correct_checksum)
|
|
{
|
|
// Correct the checksum of the UDP, IP and TCP
|
|
CorrectChecksum(p);
|
|
}
|
|
|
|
// Parsing success
|
|
return p;
|
|
}
|
|
|
|
// Correct the checksum (store the correct value in the header by recalculating the checksum which is by off-load processing)
|
|
void CorrectChecksum(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->TypeL3 == L3_IPV4)
|
|
{
|
|
IPV4_HEADER *v4 = p->L3.IPv4Header;
|
|
|
|
if (v4 != NULL)
|
|
{
|
|
if (v4->Checksum == 0x0000)
|
|
{
|
|
v4->Checksum = IpChecksum(v4, IPV4_GET_HEADER_LEN(v4) * 4);
|
|
}
|
|
|
|
if (p->TypeL4 == L4_TCP)
|
|
{
|
|
// Recalculate the TCP checksum
|
|
if (IPV4_GET_OFFSET(v4) == 0 && (IPV4_GET_FLAGS(v4) & 0x01) == 0)
|
|
{
|
|
// TCP checksuming doesn't target fragmented IP packets
|
|
TCP_HEADER *tcp = p->L4.TCPHeader;
|
|
|
|
if (tcp != NULL)
|
|
{
|
|
USHORT tcp_offloading_checksum1 = CalcChecksumForIPv4(v4->SrcIP, v4->DstIP, IP_PROTO_TCP, NULL, 0, p->IPv4PayloadSize);
|
|
USHORT tcp_offloading_checksum2 = ~tcp_offloading_checksum1;
|
|
|
|
if (tcp->Checksum == 0 || tcp->Checksum == tcp_offloading_checksum1 || tcp->Checksum == tcp_offloading_checksum2)
|
|
{
|
|
tcp->Checksum = 0;
|
|
tcp->Checksum = CalcChecksumForIPv4(v4->SrcIP, v4->DstIP, IP_PROTO_TCP, tcp, p->IPv4PayloadSize, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->TypeL4 == L4_UDP)
|
|
{
|
|
// Recalculation of the UDP checksum
|
|
if (IPV4_GET_OFFSET(v4) == 0 || (IPV4_GET_FLAGS(v4) & 0x01) == 0)
|
|
{
|
|
// If it is not divided, or it is divided but it is the first fragment of the UDP packet
|
|
UDP_HEADER *udp = p->L4.UDPHeader;
|
|
|
|
if (udp != NULL && udp->Checksum != 0)
|
|
{
|
|
USHORT udp_len = Endian16(udp->PacketLength);
|
|
USHORT udp_offloading_checksum1 = CalcChecksumForIPv4(v4->SrcIP, v4->DstIP, IP_PROTO_UDP, NULL, 0, udp_len);
|
|
USHORT udp_offloading_checksum2 = ~udp_offloading_checksum1;
|
|
|
|
if (udp->Checksum == udp_offloading_checksum1 || udp->Checksum == udp_offloading_checksum2)
|
|
{
|
|
udp->Checksum = 0;
|
|
|
|
if ((IPV4_GET_FLAGS(v4) & 0x01) == 0 && (p->IPv4PayloadSize >= udp_len))
|
|
{
|
|
// Calculate the checksum correctly based on the data in case of a non-fragmented packet
|
|
udp->Checksum = CalcChecksumForIPv4(v4->SrcIP, v4->DstIP, IP_PROTO_UDP, udp, udp_len, 0);
|
|
}
|
|
else
|
|
{
|
|
// In case of the first fragment of the packet, set the checksum to 0
|
|
// because there isn't entire data of the packet
|
|
udp->Checksum = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (p->TypeL3 == L3_IPV6)
|
|
{
|
|
IPV6_HEADER *v6 = p->L3.IPv6Header;
|
|
IPV6_HEADER_PACKET_INFO *v6info = &p->IPv6HeaderPacketInfo;
|
|
|
|
if (v6 != NULL)
|
|
{
|
|
if (p->TypeL4 == L4_TCP)
|
|
{
|
|
// Recalculate the TCP checksum
|
|
if (v6info->IsFragment == false)
|
|
{
|
|
if (v6info->FragmentHeader == NULL || ((IPV6_GET_FLAGS(v6info->FragmentHeader) & IPV6_FRAGMENT_HEADER_FLAG_MORE_FRAGMENTS) == 0))
|
|
{
|
|
// TCP checksuming doesn't target fragmented packets
|
|
TCP_HEADER *tcp = p->L4.TCPHeader;
|
|
|
|
if (tcp != NULL)
|
|
{
|
|
UINT tcp_header_size = TCP_GET_HEADER_SIZE(tcp) * 4;
|
|
USHORT tcp_offloading_checksum1 = CalcChecksumForIPv6(&v6->SrcAddress, &v6->DestAddress, IP_PROTO_TCP, NULL, 0, v6info->PayloadSize);
|
|
USHORT tcp_offloading_checksum2 = ~tcp_offloading_checksum1;
|
|
|
|
if (tcp->Checksum == 0 || tcp->Checksum == tcp_offloading_checksum1 || tcp->Checksum == tcp_offloading_checksum2)
|
|
{
|
|
tcp->Checksum = 0;
|
|
tcp->Checksum = CalcChecksumForIPv6(&v6->SrcAddress, &v6->DestAddress, IP_PROTO_TCP, tcp, v6info->PayloadSize, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (p->TypeL4 == L4_UDP)
|
|
{
|
|
// Recalculation of the UDP checksum
|
|
if (v6info->IsFragment == false)
|
|
{
|
|
UDP_HEADER *udp = p->L4.UDPHeader;
|
|
|
|
if (udp != NULL && udp->Checksum != 0)
|
|
{
|
|
USHORT udp_len = Endian16(udp->PacketLength);
|
|
USHORT udp_offloading_checksum1 = CalcChecksumForIPv6(&v6->SrcAddress, &v6->DestAddress, IP_PROTO_UDP, NULL, 0, udp_len);
|
|
USHORT udp_offloading_checksum2 = ~udp_offloading_checksum1;
|
|
|
|
if (udp->Checksum == 0 || udp->Checksum == udp_offloading_checksum1 || udp->Checksum == udp_offloading_checksum2)
|
|
{
|
|
udp->Checksum = 0;
|
|
|
|
if ((v6info->FragmentHeader == NULL || ((IPV6_GET_FLAGS(v6info->FragmentHeader) & IPV6_FRAGMENT_HEADER_FLAG_MORE_FRAGMENTS) == 0)) && (v6info->PayloadSize >= udp_len))
|
|
{
|
|
// If the packet is not fragmented, recalculate the checksum
|
|
udp->Checksum = CalcChecksumForIPv6(&v6->SrcAddress, &v6->DestAddress, IP_PROTO_UDP, udp, udp_len, 0);
|
|
}
|
|
else
|
|
{
|
|
// Don't do (can't do) anything in the case of fragmented packet
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Parse the HTTPS access log
|
|
HTTPLOG *ParseHttpsAccessLog(PKT *pkt)
|
|
{
|
|
HTTPLOG h;
|
|
char sni[MAX_PATH];
|
|
// Validate arguments
|
|
if (pkt == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (GetSniNameFromSslPacket(pkt->Payload, pkt->PayloadSize, sni, sizeof(sni)) == false)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Zero(&h, sizeof(h));
|
|
|
|
StrCpy(h.Method, sizeof(h.Method), "SSL_Connect");
|
|
StrCpy(h.Hostname, sizeof(h.Hostname), sni);
|
|
h.Port = Endian16(pkt->L4.TCPHeader->DstPort);
|
|
StrCpy(h.Path, sizeof(h.Path), "/");
|
|
h.IsSsl = true;
|
|
|
|
return Clone(&h, sizeof(h));
|
|
}
|
|
|
|
// Parse the HTTP access log
|
|
HTTPLOG *ParseHttpAccessLog(PKT *pkt)
|
|
{
|
|
HTTPLOG h;
|
|
UCHAR *buf;
|
|
UINT size;
|
|
BUF *b;
|
|
char *line1;
|
|
bool ok = false;
|
|
// Validate arguments
|
|
if (pkt == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
buf = pkt->Payload;
|
|
size = pkt->PayloadSize;
|
|
|
|
if (size <= 5)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Check whether it starts with the HTTP-specific string
|
|
if (CmpCaseIgnore(buf, "GET ", 4) != 0 &&
|
|
CmpCaseIgnore(buf, "HEAD ", 5) != 0 &&
|
|
CmpCaseIgnore(buf, "POST ", 5) != 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Zero(&h, sizeof(h));
|
|
|
|
h.Port = Endian16(pkt->L4.TCPHeader->DstPort);
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, buf, size);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
line1 = CfgReadNextLine(b);
|
|
|
|
if (line1 != NULL)
|
|
{
|
|
TOKEN_LIST *tokens = ParseToken(line1, " \t");
|
|
if (tokens != NULL)
|
|
{
|
|
if (tokens->NumTokens == 3)
|
|
{
|
|
StrCpy(h.Method, sizeof(h.Hostname), tokens->Token[0]);
|
|
Trim(h.Method);
|
|
|
|
StrCpy(h.Path, sizeof(h.Path), tokens->Token[1]);
|
|
Trim(h.Path);
|
|
|
|
StrCpy(h.Protocol, sizeof(h.Protocol), tokens->Token[2]);
|
|
Trim(h.Protocol);
|
|
|
|
StrUpper(h.Method);
|
|
|
|
while (true)
|
|
{
|
|
char *line = CfgReadNextLine(b);
|
|
UINT i;
|
|
|
|
if (line == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
i = SearchStr(line, ":", 0);
|
|
if (i != INFINITE && i < (MAX_SIZE / 2))
|
|
{
|
|
char name[MAX_SIZE];
|
|
char value[MAX_SIZE];
|
|
|
|
StrCpy(name, sizeof(name), line);
|
|
name[i] = 0;
|
|
Trim(name);
|
|
|
|
StrCpy(value, sizeof(value), line + i + 1);
|
|
Trim(value);
|
|
|
|
if (StrCmpi(name, "host") == 0)
|
|
{
|
|
StrCpy(h.Hostname, sizeof(h.Hostname), value);
|
|
}
|
|
else if (StrCmpi(name, "referer") == 0)
|
|
{
|
|
StrCpy(h.Referer, sizeof(h.Referer), value);
|
|
}
|
|
else if (StrCmpi(name, "user-agent") == 0)
|
|
{
|
|
StrCpy(h.UserAgent, sizeof(h.UserAgent), value);
|
|
}
|
|
}
|
|
|
|
Free(line);
|
|
}
|
|
|
|
if (IsEmptyStr(h.Hostname) == false)
|
|
{
|
|
ok = true;
|
|
}
|
|
}
|
|
FreeToken(tokens);
|
|
}
|
|
}
|
|
|
|
Free(line1);
|
|
FreeBuf(b);
|
|
|
|
if (ok)
|
|
{
|
|
return Clone(&h, sizeof(h));
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
// Layer-2 parsing
|
|
bool ParsePacketL2(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
return ParsePacketL2Ex(p, buf, size, false);
|
|
}
|
|
bool ParsePacketL2Ex(PKT *p, UCHAR *buf, UINT size, bool no_l3)
|
|
{
|
|
UINT i;
|
|
bool b1, b2;
|
|
USHORT type_id_16;
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(MAC_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// MAC header
|
|
p->MacHeader = (MAC_HEADER *)buf;
|
|
|
|
buf += sizeof(MAC_HEADER);
|
|
size -= sizeof(MAC_HEADER);
|
|
|
|
// Analysis of the MAC header
|
|
p->BroadcastPacket = true;
|
|
b1 = true;
|
|
b2 = true;
|
|
for (i = 0;i < 6;i++)
|
|
{
|
|
if (p->MacHeader->DestAddress[i] != 0xff)
|
|
{
|
|
p->BroadcastPacket = false;
|
|
}
|
|
if (p->MacHeader->SrcAddress[i] != 0xff)
|
|
{
|
|
b1 = false;
|
|
}
|
|
if (p->MacHeader->SrcAddress[i] != 0x00)
|
|
{
|
|
b2 = false;
|
|
}
|
|
}
|
|
if (b1 || b2 || (memcmp(p->MacHeader->SrcAddress, p->MacHeader->DestAddress, 6) == 0))
|
|
{
|
|
p->InvalidSourcePacket = true;
|
|
}
|
|
else
|
|
{
|
|
p->InvalidSourcePacket = false;
|
|
}
|
|
|
|
if (p->MacHeader->DestAddress[0] & 0x01)
|
|
{
|
|
p->BroadcastPacket = true;
|
|
}
|
|
|
|
// Parse L3 packet
|
|
type_id_16 = Endian16(p->MacHeader->Protocol);
|
|
|
|
if (type_id_16 > 1500)
|
|
{
|
|
// Ordinary Ethernet frame
|
|
switch (type_id_16)
|
|
{
|
|
case MAC_PROTO_ARPV4: // ARPv4
|
|
if (no_l3)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return ParsePacketARPv4(p, buf, size);
|
|
|
|
case MAC_PROTO_IPV4: // IPv4
|
|
if (no_l3)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return ParsePacketIPv4(p, buf, size);
|
|
|
|
case MAC_PROTO_IPV6: // IPv6
|
|
if (no_l3)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return ParsePacketIPv6(p, buf, size);
|
|
|
|
default: // Unknown
|
|
if (type_id_16 == p->VlanTypeID)
|
|
{
|
|
// VLAN
|
|
return ParsePacketTAGVLAN(p, buf, size);
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Old IEEE 802.3 frame (payload length of the packet is written in the header)
|
|
// (It has been used in the BPDU, etc.)
|
|
UINT length = (UINT)type_id_16;
|
|
LLC_HEADER *llc;
|
|
|
|
// Check whether the length is remaining
|
|
if (size < length || size < sizeof(LLC_HEADER))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Read an LLC header
|
|
llc = (LLC_HEADER *)buf;
|
|
buf += sizeof(LLC_HEADER);
|
|
size -= sizeof(LLC_HEADER);
|
|
|
|
// Determine the protocol by the value of DSAP and SSAP
|
|
if (llc->Dsap == LLC_DSAP_BPDU && llc->Ssap == LLC_SSAP_BPDU)
|
|
{
|
|
// This is a BPDU (Spanning Tree)
|
|
return ParsePacketBPDU(p, buf, size);
|
|
}
|
|
else
|
|
{
|
|
// Unknown protocol
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TAG VLAN parsing
|
|
bool ParsePacketTAGVLAN(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
USHORT vlan_ushort;
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(TAGVLAN_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TAG VLAN header
|
|
p->L3.TagVlanHeader = (TAGVLAN_HEADER *)buf;
|
|
p->TypeL3 = L3_TAGVLAN;
|
|
|
|
buf += sizeof(TAGVLAN_HEADER);
|
|
size -= sizeof(TAGVLAN_HEADER);
|
|
|
|
vlan_ushort = READ_USHORT(p->L3.TagVlanHeader->Data);
|
|
vlan_ushort = vlan_ushort & 0xFFF;
|
|
|
|
p->VlanId = vlan_ushort;
|
|
|
|
return true;
|
|
}
|
|
|
|
// BPDU Parsing
|
|
bool ParsePacketBPDU(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(BPDU_HEADER))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// BPDU header
|
|
p->L3.BpduHeader = (BPDU_HEADER *)buf;
|
|
p->TypeL3 = L3_BPDU;
|
|
|
|
buf += sizeof(BPDU_HEADER);
|
|
size -= sizeof(BPDU_HEADER);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ARPv4 Parsing
|
|
bool ParsePacketARPv4(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(ARPV4_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// ARPv4 header
|
|
p->L3.ARPv4Header = (ARPV4_HEADER *)buf;
|
|
p->TypeL3 = L3_ARPV4;
|
|
|
|
buf += sizeof(ARPV4_HEADER);
|
|
size -= sizeof(ARPV4_HEADER);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Analysis of the IPv6 extension header
|
|
bool ParseIPv6ExtHeader(IPV6_HEADER_PACKET_INFO *info, UCHAR next_header, UCHAR *buf, UINT size)
|
|
{
|
|
bool ret = false;
|
|
IPV6_OPTION_HEADER *option_header;
|
|
UINT option_header_size;
|
|
UCHAR next_header_2 = IPV6_HEADER_NONE;
|
|
// Validate arguments
|
|
if (info == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
info->IsFragment = false;
|
|
|
|
while (true)
|
|
{
|
|
if (size > 8)
|
|
{
|
|
next_header_2 = *((UCHAR *)buf);
|
|
}
|
|
|
|
switch (next_header)
|
|
{
|
|
case IPV6_HEADER_HOP:
|
|
case IPV6_HEADER_ENDPOINT:
|
|
case IPV6_HEADER_ROUTING:
|
|
// Variable-length header
|
|
if (size < 8)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
option_header = (IPV6_OPTION_HEADER *)buf;
|
|
option_header_size = (option_header->Size + 1) * 8;
|
|
if (size < option_header_size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (next_header)
|
|
{
|
|
case IPV6_HEADER_HOP:
|
|
info->HopHeader = (IPV6_OPTION_HEADER *)buf;
|
|
info->HopHeaderSize = option_header_size;
|
|
break;
|
|
|
|
case IPV6_HEADER_ENDPOINT:
|
|
info->EndPointHeader = (IPV6_OPTION_HEADER *)buf;
|
|
info->EndPointHeaderSize = option_header_size;
|
|
break;
|
|
|
|
case IPV6_HEADER_ROUTING:
|
|
info->RoutingHeader = (IPV6_OPTION_HEADER *)buf;
|
|
info->RoutingHeaderSize = option_header_size;
|
|
break;
|
|
}
|
|
|
|
buf += option_header_size;
|
|
size -= option_header_size;
|
|
break;
|
|
|
|
case IPV6_HEADER_FRAGMENT:
|
|
// Fragment header (fixed length)
|
|
if (size < sizeof(IPV6_FRAGMENT_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
info->FragmentHeader = (IPV6_FRAGMENT_HEADER *)buf;
|
|
|
|
if (IPV6_GET_FRAGMENT_OFFSET(info->FragmentHeader) != 0)
|
|
{
|
|
info->IsFragment = true;
|
|
}
|
|
|
|
buf += sizeof(IPV6_FRAGMENT_HEADER);
|
|
size -= sizeof(IPV6_FRAGMENT_HEADER);
|
|
break;
|
|
|
|
default:
|
|
// Considered that the payload follows
|
|
if (next_header != IPV6_HEADER_NONE)
|
|
{
|
|
info->Payload = buf;
|
|
info->PayloadSize = size;
|
|
}
|
|
else
|
|
{
|
|
info->Payload = NULL;
|
|
info->PayloadSize = 0;
|
|
}
|
|
info->Protocol = next_header;
|
|
return true;
|
|
}
|
|
|
|
next_header = next_header_2;
|
|
}
|
|
}
|
|
|
|
// Analysis of the IPv6 header
|
|
bool ParsePacketIPv6Header(IPV6_HEADER_PACKET_INFO *info, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (info == NULL || buf == NULL)
|
|
{
|
|
Zero(info, sizeof(IPV6_HEADER_PACKET_INFO));
|
|
return false;
|
|
}
|
|
|
|
Zero(info, sizeof(IPV6_HEADER_PACKET_INFO));
|
|
|
|
// IPv6 header
|
|
if (size < sizeof(IPV6_HEADER))
|
|
{
|
|
// Invalid size
|
|
return false;
|
|
}
|
|
|
|
info->IPv6Header = (IPV6_HEADER *)buf;
|
|
buf += sizeof(IPV6_HEADER);
|
|
size -= sizeof(IPV6_HEADER);
|
|
|
|
if (IPV6_GET_VERSION(info->IPv6Header) != 6)
|
|
{
|
|
// Invalid version
|
|
return false;
|
|
}
|
|
|
|
// Analysis of the extension header
|
|
if (ParseIPv6ExtHeader(info, info->IPv6Header->NextHeader, buf, size) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Record the header size
|
|
if (info->Payload != NULL)
|
|
{
|
|
info->TotalHeaderSize = (UINT)((UINT64)(info->Payload) - (UINT64)(info->IPv6Header));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Analyse the options of ICMPv6 packet
|
|
bool ParseICMPv6Options(ICMPV6_OPTION_LIST *o, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(o, sizeof(ICMPV6_OPTION_LIST));
|
|
|
|
// Read the header part
|
|
while (true)
|
|
{
|
|
ICMPV6_OPTION *option_header;
|
|
UINT header_total_size;
|
|
UCHAR *header_pointer;
|
|
if (size < sizeof(ICMPV6_OPTION))
|
|
{
|
|
// Size shortage
|
|
return true;
|
|
}
|
|
|
|
option_header = (ICMPV6_OPTION *)buf;
|
|
// Calculate the entire header size
|
|
header_total_size = option_header->Length * 8;
|
|
if (header_total_size == 0)
|
|
{
|
|
// The size is zero
|
|
return true;
|
|
}
|
|
if (size < header_total_size)
|
|
{
|
|
// Size shortage
|
|
return true;
|
|
}
|
|
|
|
header_pointer = buf;
|
|
buf += header_total_size;
|
|
size -= header_total_size;
|
|
|
|
switch (option_header->Type)
|
|
{
|
|
case ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER:
|
|
case ICMPV6_OPTION_TYPE_TARGET_LINK_LAYER:
|
|
// Source or target link-layer option
|
|
if (header_total_size >= sizeof(ICMPV6_OPTION_LINK_LAYER))
|
|
{
|
|
if (option_header->Type == ICMPV6_OPTION_TYPE_SOURCE_LINK_LAYER)
|
|
{
|
|
o->SourceLinkLayer = (ICMPV6_OPTION_LINK_LAYER *)header_pointer;
|
|
}
|
|
else
|
|
{
|
|
o->TargetLinkLayer = (ICMPV6_OPTION_LINK_LAYER *)header_pointer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ICMPv6 packet corruption?
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case ICMPV6_OPTION_TYPE_PREFIX:
|
|
// Prefix Information
|
|
if (header_total_size >= sizeof(ICMPV6_OPTION_PREFIX))
|
|
{
|
|
o->Prefix = (ICMPV6_OPTION_PREFIX *)header_pointer;
|
|
}
|
|
else
|
|
{
|
|
// ICMPv6 packet corruption?
|
|
}
|
|
break;
|
|
|
|
case ICMPV6_OPTION_TYPE_MTU:
|
|
// MTU
|
|
if (header_total_size >= sizeof(ICMPV6_OPTION_MTU))
|
|
{
|
|
o->Mtu = (ICMPV6_OPTION_MTU *)header_pointer;
|
|
}
|
|
else
|
|
{
|
|
// ICMPv6 packet corruption?
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ICMPv6 parsing
|
|
bool ParseICMPv6(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
ICMPV6_HEADER_INFO icmp_info;
|
|
ICMP_HEADER *icmp;
|
|
ICMP_ECHO *echo;
|
|
UINT msg_size;
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(&icmp_info, sizeof(icmp_info));
|
|
|
|
if (size < sizeof(ICMP_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
icmp = (ICMP_HEADER *)buf;
|
|
p->L4.ICMPHeader = icmp;
|
|
|
|
msg_size = size - sizeof(ICMP_HEADER);
|
|
|
|
icmp_info.Type = icmp->Type;
|
|
icmp_info.Code = icmp->Code;
|
|
icmp_info.Data = ((UCHAR *)buf) + sizeof(ICMP_HEADER);
|
|
icmp_info.DataSize = msg_size;
|
|
|
|
switch (icmp_info.Type)
|
|
{
|
|
case ICMPV6_TYPE_ECHO_REQUEST:
|
|
case ICMPV6_TYPE_ECHO_RESPONSE:
|
|
// ICMP Echo Request / Response
|
|
if (icmp_info.DataSize < sizeof(ICMP_ECHO))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
echo = (ICMP_ECHO *)icmp_info.Data;
|
|
|
|
icmp_info.EchoHeader.Identifier = Endian16(echo->Identifier);
|
|
icmp_info.EchoHeader.SeqNo = Endian16(echo->SeqNo);
|
|
icmp_info.EchoData = (UCHAR *)echo + sizeof(ICMP_ECHO);
|
|
icmp_info.EchoDataSize = icmp_info.DataSize - sizeof(ICMP_ECHO);
|
|
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ROUTER_SOLICIATION:
|
|
// Router Solicitation
|
|
if (icmp_info.DataSize < sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
icmp_info.Headers.RouterSoliciationHeader =
|
|
(ICMPV6_ROUTER_SOLICIATION_HEADER *)(((UCHAR *)icmp_info.Data));
|
|
|
|
if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER),
|
|
icmp_info.DataSize - sizeof(ICMPV6_ROUTER_SOLICIATION_HEADER)) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
|
// Router Advertisement
|
|
if (icmp_info.DataSize < sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
icmp_info.Headers.RouterAdvertisementHeader =
|
|
(ICMPV6_ROUTER_ADVERTISEMENT_HEADER *)(((UCHAR *)icmp_info.Data));
|
|
|
|
if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER),
|
|
icmp_info.DataSize - sizeof(ICMPV6_ROUTER_ADVERTISEMENT_HEADER)) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
case ICMPV6_TYPE_NEIGHBOR_SOLICIATION:
|
|
// Neighbor Solicitation
|
|
if (icmp_info.DataSize < sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
icmp_info.Headers.NeighborSoliciationHeader =
|
|
(ICMPV6_NEIGHBOR_SOLICIATION_HEADER *)(((UCHAR *)icmp_info.Data));
|
|
|
|
if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER),
|
|
icmp_info.DataSize - sizeof(ICMPV6_NEIGHBOR_SOLICIATION_HEADER)) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
case ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT:
|
|
// Neighbor Advertisement
|
|
if (icmp_info.DataSize < sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
icmp_info.Headers.NeighborAdvertisementHeader =
|
|
(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER *)(((UCHAR *)icmp_info.Data));
|
|
|
|
if (ParseICMPv6Options(&icmp_info.OptionList, ((UCHAR *)icmp_info.Headers.HeaderPointer) + sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER),
|
|
icmp_info.DataSize - sizeof(ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER)) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
p->TypeL4 = L4_ICMPV6;
|
|
Copy(&p->ICMPv6HeaderPacketInfo, &icmp_info, sizeof(ICMPV6_HEADER_INFO));
|
|
|
|
return true;
|
|
}
|
|
|
|
// Release of the ICMPv6 options
|
|
void FreeCloneICMPv6Options(ICMPV6_OPTION_LIST *o)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Free(o->SourceLinkLayer);
|
|
Free(o->TargetLinkLayer);
|
|
Free(o->Prefix);
|
|
Free(o->Mtu);
|
|
}
|
|
|
|
// Clone of the ICMPv6 options
|
|
void CloneICMPv6Options(ICMPV6_OPTION_LIST *dst, ICMPV6_OPTION_LIST *src)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Zero(dst, sizeof(ICMPV6_OPTION_LIST));
|
|
|
|
dst->SourceLinkLayer = Clone(src->SourceLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
|
|
dst->TargetLinkLayer = Clone(src->TargetLinkLayer, sizeof(ICMPV6_OPTION_LINK_LAYER));
|
|
dst->Prefix = Clone(src->Prefix, sizeof(ICMPV6_OPTION_PREFIX));
|
|
dst->Mtu = Clone(src->Mtu, sizeof(ICMPV6_OPTION_MTU));
|
|
}
|
|
|
|
// IPv6 parsing
|
|
bool ParsePacketIPv6(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ParsePacketIPv6Header(&p->IPv6HeaderPacketInfo, buf, size) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
p->TypeL3 = L3_IPV6;
|
|
p->L3.IPv6Header = p->IPv6HeaderPacketInfo.IPv6Header;
|
|
|
|
if (p->IPv6HeaderPacketInfo.Payload == NULL)
|
|
{
|
|
// No payload
|
|
return true;
|
|
}
|
|
|
|
buf = p->IPv6HeaderPacketInfo.Payload;
|
|
size = p->IPv6HeaderPacketInfo.PayloadSize;
|
|
|
|
if (p->IPv6HeaderPacketInfo.IsFragment)
|
|
{
|
|
// This is a fragmented packet. Quit interpreting
|
|
p->TypeL4 = L4_FRAGMENT;
|
|
return true;
|
|
}
|
|
|
|
// Parse a L4 packet
|
|
switch (p->IPv6HeaderPacketInfo.Protocol)
|
|
{
|
|
case IP_PROTO_ICMPV6: // ICMPv6
|
|
if (ParseICMPv6(p, buf, size) == false)
|
|
{
|
|
// Returns true also if it fails to parse ICMPv6
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
|
|
case IP_PROTO_TCP: // TCP
|
|
return ParseTCP(p, buf, size);
|
|
|
|
case IP_PROTO_UDP: // UDP
|
|
return ParseUDP(p, buf, size);
|
|
|
|
default: // Unknown
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Parse the IPv4 by adding a dummy MAC header
|
|
PKT *ParsePacketIPv4WithDummyMacHeader(UCHAR *buf, UINT size)
|
|
{
|
|
UCHAR *tmp;
|
|
UINT tmp_size;
|
|
PKT *ret;
|
|
// Validate arguments
|
|
if (buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
tmp_size = size + 14;
|
|
tmp = Malloc(tmp_size);
|
|
Zero(tmp, 12);
|
|
WRITE_USHORT(tmp + 12, MAC_PROTO_IPV4);
|
|
Copy(tmp + 14, buf, size);
|
|
|
|
ret = ParsePacket(tmp, tmp_size);
|
|
|
|
if (ret == NULL)
|
|
{
|
|
Free(tmp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// IPv4 parsing
|
|
bool ParsePacketIPv4(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
UINT header_size;
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(IPV4_HEADER))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// IPv4 header
|
|
p->L3.IPv4Header = (IPV4_HEADER *)buf;
|
|
p->TypeL3 = L3_IPV4;
|
|
|
|
// Check the header
|
|
header_size = IPV4_GET_HEADER_LEN(p->L3.IPv4Header) * 4;
|
|
if (header_size < sizeof(IPV4_HEADER) || size < header_size)
|
|
{
|
|
// Header size is invalid
|
|
p->L3.IPv4Header = NULL;
|
|
p->TypeL3= L3_UNKNOWN;
|
|
return true;
|
|
}
|
|
|
|
buf += header_size;
|
|
size -= header_size;
|
|
|
|
p->IPv4PayloadSize = MIN(size, Endian16(p->L3.IPv4Header->TotalLength) - header_size);
|
|
if (Endian16(p->L3.IPv4Header->TotalLength) < header_size)
|
|
{
|
|
p->IPv4PayloadSize = 0;
|
|
}
|
|
|
|
p->IPv4PayloadData = buf;
|
|
|
|
if (IPV4_GET_OFFSET(p->L3.IPv4Header) != 0)
|
|
{
|
|
// Quit analysing since this is fragmented
|
|
p->TypeL4 = L4_FRAGMENT;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Parse a L4 packet
|
|
switch (p->L3.IPv4Header->Protocol)
|
|
{
|
|
case IP_PROTO_ICMPV4: // ICMPv4
|
|
return ParseICMPv4(p, buf, size);
|
|
|
|
case IP_PROTO_UDP: // UDP
|
|
return ParseUDP(p, buf, size);
|
|
|
|
case IP_PROTO_TCP: // TCP
|
|
return ParseTCP(p, buf, size);
|
|
|
|
default: // Unknown
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// ICMPv4 parsing
|
|
bool ParseICMPv4(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(ICMP_HEADER))
|
|
{
|
|
// Size is invalid
|
|
return false;
|
|
}
|
|
|
|
// ICMPv4 header
|
|
p->L4.ICMPHeader = (ICMP_HEADER *)buf;
|
|
p->TypeL4 = L4_ICMPV4;
|
|
|
|
buf += sizeof(ICMP_HEADER);
|
|
size -= sizeof(ICMP_HEADER);
|
|
|
|
return true;
|
|
}
|
|
|
|
// TCP parsing
|
|
bool ParseTCP(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
UINT header_size;
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(TCP_HEADER))
|
|
{
|
|
// Size is invalid
|
|
return false;
|
|
}
|
|
|
|
// TCP header
|
|
p->L4.TCPHeader = (TCP_HEADER *)buf;
|
|
p->TypeL4 = L4_TCP;
|
|
|
|
// Check the header size
|
|
header_size = TCP_GET_HEADER_SIZE(p->L4.TCPHeader) * 4;
|
|
if (header_size < sizeof(TCP_HEADER) || size < header_size)
|
|
{
|
|
// Header size is invalid
|
|
p->L4.TCPHeader = NULL;
|
|
p->TypeL4 = L4_UNKNOWN;
|
|
return true;
|
|
}
|
|
|
|
buf += header_size;
|
|
size -= header_size;
|
|
|
|
p->Payload = buf;
|
|
p->PayloadSize = size;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Get the next byte
|
|
UCHAR GetNextByte(BUF *b)
|
|
{
|
|
UCHAR c = 0;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ReadBuf(b, &c, 1) != 1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
// Interpret the DNS query
|
|
bool ParseDnsQuery(char *name, UINT name_size, void *data, UINT data_size)
|
|
{
|
|
BUF *b;
|
|
char tmp[257];
|
|
bool ok = true;
|
|
USHORT val;
|
|
// Validate arguments
|
|
if (name == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return false;
|
|
}
|
|
StrCpy(name, name_size, "");
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, data, data_size);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
while (true)
|
|
{
|
|
UINT next_len = (UINT)GetNextByte(b);
|
|
if (next_len > 0)
|
|
{
|
|
// Read only the specified length
|
|
Zero(tmp, sizeof(tmp));
|
|
if (ReadBuf(b, tmp, next_len) != next_len)
|
|
{
|
|
ok = false;
|
|
break;
|
|
}
|
|
// Append
|
|
if (StrLen(name) != 0)
|
|
{
|
|
StrCat(name, name_size, ".");
|
|
}
|
|
StrCat(name, name_size, tmp);
|
|
}
|
|
else
|
|
{
|
|
// Read all
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ReadBuf(b, &val, sizeof(val)) != sizeof(val))
|
|
{
|
|
ok = false;
|
|
}
|
|
else
|
|
{
|
|
if (Endian16(val) != 0x01 && Endian16(val) != 0x0c)
|
|
{
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (ReadBuf(b, &val, sizeof(val)) != sizeof(val))
|
|
{
|
|
ok = false;
|
|
}
|
|
else
|
|
{
|
|
if (Endian16(val) != 0x01)
|
|
{
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
FreeBuf(b);
|
|
|
|
if (ok == false || StrLen(name) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// DNS parsing
|
|
void ParseDNS(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
UCHAR *query_data;
|
|
UINT query_data_size;
|
|
DNSV4_HEADER *dns;
|
|
char hostname[MAX_SIZE];
|
|
if (p == NULL|| buf == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (size < sizeof(DNSV4_HEADER))
|
|
{
|
|
return;
|
|
}
|
|
|
|
dns = (DNSV4_HEADER *)buf;
|
|
|
|
if ((dns->Flag1 & 78) != 0 || (dns->Flag1 & 0x80) != 0)
|
|
{
|
|
// Illegal opcode
|
|
return;
|
|
}
|
|
if (Endian16(dns->NumQuery) != 1)
|
|
{
|
|
// Number of queries is invalid
|
|
return;
|
|
}
|
|
|
|
query_data = ((UCHAR *)dns) + sizeof(DNSV4_HEADER);
|
|
query_data_size = size - sizeof(DNSV4_HEADER);
|
|
|
|
// Interpret the query
|
|
if (ParseDnsQuery(hostname, sizeof(hostname), query_data, query_data_size) == false)
|
|
{
|
|
// Interpretation fails
|
|
return;
|
|
}
|
|
|
|
StrCpy(p->DnsQueryHost, sizeof(p->DnsQueryHost), hostname);
|
|
p->TypeL7 = L7_DNS;
|
|
}
|
|
|
|
// UDP parsing
|
|
bool ParseUDP(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
USHORT src_port, dst_port;
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(UDP_HEADER))
|
|
{
|
|
// Size is invalid
|
|
return false;
|
|
}
|
|
|
|
// UDP header
|
|
p->L4.UDPHeader = (UDP_HEADER *)buf;
|
|
p->TypeL4 = L4_UDP;
|
|
|
|
buf += sizeof(UDP_HEADER);
|
|
size -= sizeof(UDP_HEADER);
|
|
|
|
p->Payload = buf;
|
|
p->PayloadSize = size;
|
|
|
|
// Check the port number
|
|
src_port = Endian16(p->L4.UDPHeader->SrcPort);
|
|
dst_port = Endian16(p->L4.UDPHeader->DstPort);
|
|
|
|
if ((src_port == 67 && dst_port == 68) ||
|
|
(src_port == 68 && dst_port == 67))
|
|
{
|
|
if (p->TypeL3 == L3_IPV4)
|
|
{
|
|
// A DHCP packet is found
|
|
ParseDHCPv4(p, buf, size);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (dst_port == 53)
|
|
{
|
|
ParseDNS(p, buf, size);
|
|
return true;
|
|
}
|
|
|
|
|
|
if (src_port == 500 || dst_port == 500 || src_port == 4500 || dst_port == 4500)
|
|
{
|
|
if (p->PayloadSize >= sizeof(IKE_HEADER))
|
|
{
|
|
IKE_HEADER *ike_header = (IKE_HEADER *)p->Payload;
|
|
|
|
if (ike_header->InitiatorCookie != 0 && ike_header->ResponderCookie == 0 &&
|
|
(ike_header->ExchangeType == IKE_EXCHANGE_TYPE_MAIN ||
|
|
ike_header->ExchangeType == IKE_EXCHANGE_TYPE_AGGRESSIVE))
|
|
{
|
|
// the IKE connection request packet is found
|
|
p->TypeL7 = L7_IKECONN;
|
|
p->L7.IkeHeader = ike_header;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine whether it's an OpenVPN UDP packet
|
|
if (size == 14)
|
|
{
|
|
if (buf[0] == 0x38)
|
|
{
|
|
if (IsZero(buf + 9, 5))
|
|
{
|
|
if (IsZero(buf + 1, 8) == false)
|
|
{
|
|
// An OpenVPN connection request packet is found
|
|
p->TypeL7 = L7_OPENVPNCONN;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// DHCPv4 parsing
|
|
void ParseDHCPv4(PKT *p, UCHAR *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL || buf == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check the size
|
|
if (size < sizeof(DHCPV4_HEADER))
|
|
{
|
|
// Size is invalid
|
|
return;
|
|
}
|
|
|
|
// DHCPv4 header
|
|
p->L7.DHCPv4Header = (DHCPV4_HEADER *)buf;
|
|
p->TypeL7 = L7_DHCPV4;
|
|
}
|
|
|
|
// Release the memory of the packet
|
|
void FreePacket(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->MacHeader != NULL)
|
|
{
|
|
switch (p->TypeL3)
|
|
{
|
|
case L3_IPV4:
|
|
FreePacketIPv4(p);
|
|
break;
|
|
|
|
case L3_ARPV4:
|
|
FreePacketARPv4(p);
|
|
break;
|
|
|
|
case L3_TAGVLAN:
|
|
FreePacketTagVlan(p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p->HttpLog != NULL)
|
|
{
|
|
Free(p->HttpLog);
|
|
}
|
|
|
|
Free(p);
|
|
}
|
|
|
|
// Release the memory of the packet with data
|
|
void FreePacketWithData(PKT *p)
|
|
{
|
|
void *data;
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
data = p->PacketData;
|
|
|
|
FreePacket(p);
|
|
|
|
Free(data);
|
|
}
|
|
|
|
// Release the memory for the IPv4 packet
|
|
void FreePacketIPv4(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (p->TypeL4)
|
|
{
|
|
case L4_ICMPV4:
|
|
FreePacketICMPv4(p);
|
|
break;
|
|
|
|
case L4_TCP:
|
|
FreePacketTCPv4(p);
|
|
break;
|
|
|
|
case L4_UDP:
|
|
FreePacketUDPv4(p);
|
|
break;
|
|
}
|
|
|
|
p->L3.IPv4Header = NULL;
|
|
p->TypeL3 = L3_UNKNOWN;
|
|
}
|
|
|
|
// Release the memory for the tagged VLAN packet
|
|
void FreePacketTagVlan(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->L3.TagVlanHeader = NULL;
|
|
p->TypeL3 = L3_UNKNOWN;
|
|
}
|
|
|
|
// Release the memory for the ARPv4 packet
|
|
void FreePacketARPv4(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->L3.ARPv4Header = NULL;
|
|
p->TypeL3 = L3_UNKNOWN;
|
|
}
|
|
|
|
// Release the memory of the UDPv4 packet
|
|
void FreePacketUDPv4(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (p->TypeL7)
|
|
{
|
|
case L7_DHCPV4:
|
|
FreePacketDHCPv4(p);
|
|
break;
|
|
}
|
|
|
|
p->L4.UDPHeader = NULL;
|
|
p->TypeL4 = L4_UNKNOWN;
|
|
}
|
|
|
|
// Release the memory for the TCPv4 packet
|
|
void FreePacketTCPv4(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->L4.TCPHeader = NULL;
|
|
p->TypeL4 = L4_UNKNOWN;
|
|
}
|
|
|
|
// Release the memory for the ICMPv4 packet
|
|
void FreePacketICMPv4(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->L4.ICMPHeader = NULL;
|
|
p->TypeL4 = L4_UNKNOWN;
|
|
}
|
|
|
|
// Release the memory for the DHCPv4 packet
|
|
void FreePacketDHCPv4(PKT *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->L7.DHCPv4Header = NULL;
|
|
p->TypeL7 = L7_UNKNOWN;
|
|
}
|
|
|
|
|
|
// Confirm the checksum of the IP header
|
|
bool IpCheckChecksum(IPV4_HEADER *ip)
|
|
{
|
|
UINT header_size;
|
|
USHORT checksum_original, checksum_calc;
|
|
// Validate arguments
|
|
if (ip == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
header_size = IPV4_GET_HEADER_LEN(ip) * 4;
|
|
checksum_original = ip->Checksum;
|
|
ip->Checksum = 0;
|
|
checksum_calc = IpChecksum(ip, header_size);
|
|
ip->Checksum = checksum_original;
|
|
|
|
if (checksum_original == checksum_calc)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Calculate the checksum
|
|
USHORT IpChecksum(void *buf, UINT size)
|
|
{
|
|
int sum = 0;
|
|
USHORT *addr = (USHORT *)buf;
|
|
int len = (int)size;
|
|
USHORT *w = addr;
|
|
int nleft = len;
|
|
USHORT answer = 0;
|
|
|
|
while (nleft > 1)
|
|
{
|
|
USHORT ww = 0;
|
|
Copy(&ww, w++, sizeof(USHORT));
|
|
sum += ww;
|
|
nleft -= 2;
|
|
}
|
|
|
|
if (nleft == 1)
|
|
{
|
|
*(UCHAR *)(&answer) = *(UCHAR *)w;
|
|
sum += answer;
|
|
}
|
|
|
|
sum = (sum >> 16) + (sum & 0xffff);
|
|
sum += (sum >> 16);
|
|
|
|
answer = ~sum;
|
|
|
|
return answer;
|
|
}
|
|
|
|
// Convert a DHCP option list into a buffer
|
|
BUF *BuildDhcpOptionsBuf(LIST *o)
|
|
{
|
|
BUF *b;
|
|
UINT i;
|
|
UCHAR id;
|
|
UCHAR sz;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
DHCP_OPTION *d = LIST_DATA(o, i);
|
|
UINT current_size = d->Size;
|
|
UINT current_pos = 0;
|
|
|
|
id = (UCHAR)d->Id;
|
|
if (d->Size <= 255)
|
|
{
|
|
sz = (UCHAR)d->Size;
|
|
}
|
|
else
|
|
{
|
|
sz = 0xFF;
|
|
}
|
|
WriteBuf(b, &id, 1);
|
|
WriteBuf(b, &sz, 1);
|
|
WriteBuf(b, d->Data, sz);
|
|
|
|
current_size -= sz;
|
|
current_pos += sz;
|
|
|
|
while (current_size != 0)
|
|
{
|
|
id = DHCP_ID_PRIVATE;
|
|
if (current_size <= 255)
|
|
{
|
|
sz = (UCHAR)current_size;
|
|
}
|
|
else
|
|
{
|
|
sz = 0xFF;
|
|
}
|
|
WriteBuf(b, &id, 1);
|
|
WriteBuf(b, &sz, 1);
|
|
WriteBuf(b, ((UCHAR *)d->Data) + current_pos, sz);
|
|
|
|
current_size -= sz;
|
|
current_pos += sz;
|
|
}
|
|
|
|
}
|
|
|
|
id = 0xff;
|
|
WriteBuf(b, &id, 1);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Convert a DHCP option list to the DHCP option
|
|
LIST *BuildDhcpOption(DHCP_OPTION_LIST *opt)
|
|
{
|
|
LIST *o;
|
|
UCHAR opcode;
|
|
BUF *dns_buf;
|
|
// Validate arguments
|
|
if (opt == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
o = NewListFast(NULL);
|
|
|
|
// Op-code
|
|
opcode = (UCHAR)opt->Opcode;
|
|
Add(o, NewDhcpOption(DHCP_ID_MESSAGE_TYPE, &opcode, sizeof(opcode)));
|
|
Add(o, NewDhcpOption(DHCP_ID_SERVER_ADDRESS, &opt->ServerAddress, sizeof(opt->ServerAddress)));
|
|
|
|
if (opt->LeaseTime != 0)
|
|
{
|
|
Add(o, NewDhcpOption(DHCP_ID_LEASE_TIME, &opt->LeaseTime, sizeof(opt->LeaseTime)));
|
|
}
|
|
|
|
if (StrLen(opt->DomainName) != 0 && opt->DnsServer != 0)
|
|
{
|
|
Add(o, NewDhcpOption(DHCP_ID_DOMAIN_NAME, opt->DomainName, StrLen(opt->DomainName)));
|
|
}
|
|
if (opt->SubnetMask != 0)
|
|
{
|
|
Add(o, NewDhcpOption(DHCP_ID_SUBNET_MASK, &opt->SubnetMask, sizeof(opt->SubnetMask)));
|
|
}
|
|
if (opt->Gateway != 0)
|
|
{
|
|
Add(o, NewDhcpOption(DHCP_ID_GATEWAY_ADDR, &opt->Gateway, sizeof(opt->Gateway)));
|
|
}
|
|
|
|
dns_buf = NewBuf();
|
|
|
|
if (opt->DnsServer != 0)
|
|
{
|
|
WriteBuf(dns_buf, &opt->DnsServer, sizeof(opt->DnsServer));
|
|
}
|
|
if (opt->DnsServer2 != 0)
|
|
{
|
|
WriteBuf(dns_buf, &opt->DnsServer2, sizeof(opt->DnsServer2));
|
|
}
|
|
|
|
if (dns_buf->Size >= 1)
|
|
{
|
|
Add(o, NewDhcpOption(DHCP_ID_DNS_ADDR, dns_buf->Buf, dns_buf->Size));
|
|
}
|
|
|
|
FreeBuf(dns_buf);
|
|
|
|
if (opt->ClasslessRoute.NumExistingRoutes >= 1)
|
|
{
|
|
BUF *b = DhcpBuildClasslessRouteData(&opt->ClasslessRoute);
|
|
|
|
if (b != NULL)
|
|
{
|
|
Add(o, NewDhcpOption(DHCP_ID_CLASSLESS_ROUTE, b->Buf, b->Size));
|
|
Add(o, NewDhcpOption(DHCP_ID_MS_CLASSLESS_ROUTE, b->Buf, b->Size));
|
|
|
|
FreeBuf(b);
|
|
}
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
// Create a new DHCP option item
|
|
DHCP_OPTION *NewDhcpOption(UINT id, void *data, UINT size)
|
|
{
|
|
DHCP_OPTION *ret;
|
|
if (size != 0 && data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(DHCP_OPTION));
|
|
ret->Data = ZeroMalloc(size);
|
|
Copy(ret->Data, data, size);
|
|
ret->Size = (UCHAR)size;
|
|
ret->Id = (UCHAR)id;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse a DHCP options list
|
|
DHCP_OPTION_LIST *ParseDhcpOptionList(void *data, UINT size)
|
|
{
|
|
DHCP_OPTION_LIST *ret;
|
|
LIST *o;
|
|
DHCP_OPTION *a;
|
|
// Validate arguments
|
|
if (data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Parse the list
|
|
o = ParseDhcpOptions(data, size);
|
|
if (o == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(DHCP_OPTION_LIST));
|
|
|
|
// Get the opcode
|
|
a = GetDhcpOption(o, DHCP_ID_MESSAGE_TYPE);
|
|
if (a != NULL)
|
|
{
|
|
if (a->Size == 1)
|
|
{
|
|
ret->Opcode = *((UCHAR *)a->Data);
|
|
}
|
|
}
|
|
|
|
switch (ret->Opcode)
|
|
{
|
|
case DHCP_DISCOVER:
|
|
case DHCP_REQUEST:
|
|
// Parse this more finely because this is client requests
|
|
// Requested IP address
|
|
a = GetDhcpOption(o, DHCP_ID_REQUEST_IP_ADDRESS);
|
|
if (a != NULL && a->Size == 4)
|
|
{
|
|
Copy(&ret->RequestedIp, a->Data, 4);
|
|
}
|
|
// Host name
|
|
a = GetDhcpOption(o, DHCP_ID_HOST_NAME);
|
|
if (a != NULL)
|
|
{
|
|
if (a->Size > 1)
|
|
{
|
|
Copy(ret->Hostname, a->Data, MIN(a->Size, sizeof(ret->Hostname) - 1));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DHCP_OFFER:
|
|
case DHCP_ACK:
|
|
// Subnet mask
|
|
a = GetDhcpOption(o, DHCP_ID_SUBNET_MASK);
|
|
if (a != NULL && a->Size >= 4)
|
|
{
|
|
Copy(&ret->SubnetMask, a->Data, 4);
|
|
}
|
|
|
|
// Lease time
|
|
a = GetDhcpOption(o, DHCP_ID_LEASE_TIME);
|
|
if (a != NULL && a->Size == 4)
|
|
{
|
|
ret->LeaseTime = READ_UINT(a->Data);
|
|
}
|
|
|
|
// Server IP address
|
|
a = GetDhcpOption(o, DHCP_ID_SERVER_ADDRESS);
|
|
if (a != NULL && a->Size >= 4)
|
|
{
|
|
Copy(&ret->ServerAddress, a->Data, 4);
|
|
}
|
|
|
|
// Domain name
|
|
a = GetDhcpOption(o, DHCP_ID_DOMAIN_NAME);
|
|
if (a != NULL && a->Size >= 1)
|
|
{
|
|
Zero(ret->DomainName, sizeof(ret->DomainName));
|
|
Copy(ret->DomainName, a->Data, MIN(a->Size, sizeof(ret->DomainName) - 1));
|
|
}
|
|
|
|
// Gateway
|
|
a = GetDhcpOption(o, DHCP_ID_GATEWAY_ADDR);
|
|
if (a != NULL && a->Size >= 4)
|
|
{
|
|
Copy(&ret->Gateway, a->Data, 4);
|
|
}
|
|
|
|
// DNS server
|
|
a = GetDhcpOption(o, DHCP_ID_DNS_ADDR);
|
|
if (a != NULL && a->Size >= 4)
|
|
{
|
|
Copy(&ret->DnsServer, a->Data, 4);
|
|
|
|
if (a->Size >= 8)
|
|
{
|
|
Copy(&ret->DnsServer2, ((UCHAR *)a->Data) + 4, 4);
|
|
}
|
|
}
|
|
|
|
// WINS server
|
|
a = GetDhcpOption(o, DHCP_ID_WINS_ADDR);
|
|
if (a != NULL && a->Size >= 4)
|
|
{
|
|
Copy(&ret->WinsServer, a->Data, 4);
|
|
|
|
if (a->Size >= 8)
|
|
{
|
|
Copy(&ret->WinsServer2, ((UCHAR *)a->Data) + 4, 4);
|
|
}
|
|
}
|
|
|
|
// Classless static routing table entries
|
|
// RFC 3442
|
|
a = GetDhcpOption(o, DHCP_ID_CLASSLESS_ROUTE);
|
|
if (a != NULL)
|
|
{
|
|
DhcpParseClasslessRouteData(&ret->ClasslessRoute, a->Data, a->Size);
|
|
}
|
|
// Microsoft Extension
|
|
a = GetDhcpOption(o, DHCP_ID_MS_CLASSLESS_ROUTE);
|
|
if (a != NULL)
|
|
{
|
|
DhcpParseClasslessRouteData(&ret->ClasslessRoute, a->Data, a->Size);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Release the list
|
|
FreeDhcpOptions(o);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Normalize the classless routing table string
|
|
bool NormalizeClasslessRouteTableStr(char *dst, UINT dst_size, char *src)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE_TABLE t;
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(&t, sizeof(t));
|
|
if (ParseClasslessRouteTableStr(&t, src))
|
|
{
|
|
BuildClasslessRouteTableStr(dst, dst_size, &t);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Build the string from the classless routing table
|
|
void BuildClasslessRouteTableStr(char *str, UINT str_size, DHCP_CLASSLESS_ROUTE_TABLE *t)
|
|
{
|
|
UINT i;
|
|
UINT num = 0;
|
|
ClearStr(str, str_size);
|
|
// Validate arguments
|
|
if (str == NULL || t == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES;i++)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE *r = &t->Entries[i];
|
|
|
|
if (r->Exists)
|
|
{
|
|
char tmp[128];
|
|
|
|
Zero(tmp, sizeof(tmp));
|
|
BuildClasslessRouteStr(tmp, sizeof(tmp), r);
|
|
|
|
if (IsEmptyStr(tmp) == false)
|
|
{
|
|
if (num >= 1)
|
|
{
|
|
StrCat(str, str_size, ", ");
|
|
}
|
|
|
|
StrCat(str, str_size, tmp);
|
|
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build the string from the classless routing table entry
|
|
void BuildClasslessRouteStr(char *str, UINT str_size, DHCP_CLASSLESS_ROUTE *r)
|
|
{
|
|
ClearStr(str, str_size);
|
|
// Validate arguments
|
|
if (str == NULL || r == NULL || r->Exists == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Format(str, str_size, "%r/%r/%r", &r->Network, &r->SubnetMask, &r->Gateway);
|
|
}
|
|
|
|
// Check the classless routing table string
|
|
bool CheckClasslessRouteTableStr(char *str)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE_TABLE d;
|
|
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ParseClasslessRouteTableStr(&d, str);
|
|
}
|
|
|
|
// Parse the classless routing table string
|
|
bool ParseClasslessRouteTableStr(DHCP_CLASSLESS_ROUTE_TABLE *d, char *str)
|
|
{
|
|
bool ret = true;
|
|
TOKEN_LIST *t;
|
|
// Validate arguments
|
|
if (d == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(d, sizeof(DHCP_CLASSLESS_ROUTE_TABLE));
|
|
|
|
t = ParseTokenWithoutNullStr(str, NULL);
|
|
|
|
if (t != NULL)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0;i < t->NumTokens;i++)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE r;
|
|
|
|
Zero(&r, sizeof(r));
|
|
if (ParseClasslessRouteStr(&r, t->Token[i]))
|
|
{
|
|
if (d->NumExistingRoutes < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES)
|
|
{
|
|
Copy(&d->Entries[d->NumExistingRoutes], &r, sizeof(DHCP_CLASSLESS_ROUTE));
|
|
d->NumExistingRoutes++;
|
|
}
|
|
else
|
|
{
|
|
// Overflow
|
|
ret = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Parse error
|
|
ret = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeToken(t);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse the classless routing table entry string
|
|
bool ParseClasslessRouteStr(DHCP_CLASSLESS_ROUTE *r, char *str)
|
|
{
|
|
TOKEN_LIST *t;
|
|
bool ret = false;
|
|
char tmp[MAX_PATH];
|
|
// Validate arguments
|
|
if (r == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), str);
|
|
Trim(tmp);
|
|
|
|
t = ParseTokenWithoutNullStr(str, "/");
|
|
if (t == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (t->NumTokens == 3)
|
|
{
|
|
char ip_and_mask[MAX_PATH];
|
|
char gateway[MAX_PATH];
|
|
|
|
Zero(r, sizeof(DHCP_CLASSLESS_ROUTE));
|
|
|
|
Format(ip_and_mask, sizeof(ip_and_mask), "%s/%s", t->Token[0], t->Token[1]);
|
|
StrCpy(gateway, sizeof(gateway), t->Token[2]);
|
|
|
|
if (ParseIpAndSubnetMask46(ip_and_mask, &r->Network, &r->SubnetMask))
|
|
{
|
|
r->SubnetMaskLen = SubnetMaskToInt4(&r->SubnetMask);
|
|
|
|
if (StrToIP(&r->Gateway, gateway))
|
|
{
|
|
if (IsIP4(&r->Gateway) && IsIP4(&r->Network) && IsIP4(&r->SubnetMask))
|
|
{
|
|
r->Exists = true;
|
|
|
|
IPAnd4(&r->Network, &r->Network, &r->SubnetMask);
|
|
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeToken(t);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Build the classless static routing table data for a DHCP message
|
|
BUF *DhcpBuildClasslessRouteData(DHCP_CLASSLESS_ROUTE_TABLE *t)
|
|
{
|
|
BUF *b;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (t == NULL || t->NumExistingRoutes == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
for (i = 0;i < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES;i++)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE *r = &t->Entries[i];
|
|
|
|
if (r->Exists && r->SubnetMaskLen <= 32)
|
|
{
|
|
UCHAR c;
|
|
UINT data_len;
|
|
UINT ip32;
|
|
UCHAR tmp[4];
|
|
|
|
// Width of subnet mask
|
|
c = (UCHAR)r->SubnetMaskLen;
|
|
WriteBuf(b, &c, 1);
|
|
|
|
// Number of significant octets
|
|
data_len = (r->SubnetMaskLen + 7) / 8;
|
|
Zero(tmp, sizeof(tmp));
|
|
Copy(tmp, &r->Network, data_len);
|
|
WriteBuf(b, tmp, data_len);
|
|
|
|
// Gateway
|
|
ip32 = IPToUINT(&r->Gateway);
|
|
WriteBuf(b, &ip32, sizeof(UINT));
|
|
}
|
|
}
|
|
|
|
SeekBufToBegin(b);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Parse a classless static routing table entries from the DHCP message
|
|
void DhcpParseClasslessRouteData(DHCP_CLASSLESS_ROUTE_TABLE *t, void *data, UINT size)
|
|
{
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (t == NULL || data == NULL || size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
b = MemToBuf(data, size);
|
|
|
|
while (b->Current < b->Size)
|
|
{
|
|
UCHAR c;
|
|
UINT subnet_mask_len;
|
|
UINT data_len;
|
|
UCHAR tmp[4];
|
|
IP ip;
|
|
IP mask;
|
|
IP gateway;
|
|
DHCP_CLASSLESS_ROUTE r;
|
|
UINT ip32;
|
|
bool exists = false;
|
|
UINT i;
|
|
|
|
// Subnet mask length
|
|
c = ReadBufChar(b);
|
|
subnet_mask_len = c;
|
|
if (subnet_mask_len > 32)
|
|
{
|
|
// Invalid data
|
|
break;
|
|
}
|
|
|
|
data_len = (subnet_mask_len + 7) / 8;
|
|
if (data_len > 4)
|
|
{
|
|
// Invalid data
|
|
break;
|
|
}
|
|
|
|
Zero(tmp, sizeof(tmp));
|
|
if (ReadBuf(b, tmp, data_len) != data_len)
|
|
{
|
|
// Invalid data
|
|
break;
|
|
}
|
|
|
|
// IP address body
|
|
Zero(&ip, sizeof(IP));
|
|
Copy(ip.addr, tmp, data_len);
|
|
|
|
Zero(&mask, sizeof(mask));
|
|
IntToSubnetMask4(&mask, subnet_mask_len);
|
|
|
|
// Gateway address
|
|
Zero(&gateway, sizeof(gateway));
|
|
if (ReadBuf(b, &ip32, sizeof(UINT)) != sizeof(UINT))
|
|
{
|
|
// Invalid data
|
|
break;
|
|
}
|
|
UINTToIP(&gateway, ip32);
|
|
|
|
Zero(&r, sizeof(r));
|
|
r.Exists = true;
|
|
Copy(&r.Gateway, &gateway, sizeof(IP));
|
|
Copy(&r.Network, &ip, sizeof(IP));
|
|
Copy(&r.SubnetMask, &mask, sizeof(IP));
|
|
r.SubnetMaskLen = subnet_mask_len;
|
|
|
|
for (i = 0;i < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES;i++)
|
|
{
|
|
if (Cmp(&t->Entries[i], &r, sizeof(DHCP_CLASSLESS_ROUTE)) == 0)
|
|
{
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (exists == false)
|
|
{
|
|
if (t->NumExistingRoutes >= MAX_DHCP_CLASSLESS_ROUTE_ENTRIES)
|
|
{
|
|
// Overflow
|
|
break;
|
|
}
|
|
|
|
Copy(&t->Entries[t->NumExistingRoutes], &r, sizeof(DHCP_CLASSLESS_ROUTE));
|
|
t->NumExistingRoutes++;
|
|
}
|
|
}
|
|
|
|
FreeBuf(b);
|
|
}
|
|
|
|
// Finding a DHCP option
|
|
DHCP_OPTION *GetDhcpOption(LIST *o, UINT id)
|
|
{
|
|
UINT i;
|
|
DHCP_OPTION *ret = NULL;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
DHCP_OPTION *opt = LIST_DATA(o, i);
|
|
if (opt->Id == id)
|
|
{
|
|
ret = opt;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Get the best classless routing table entry from the routing table
|
|
DHCP_CLASSLESS_ROUTE *GetBestClasslessRoute(DHCP_CLASSLESS_ROUTE_TABLE *t, IP *ip)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE *ret = NULL;
|
|
UINT i;
|
|
UINT max_mask = 0;
|
|
// Validate arguments
|
|
if (t == NULL || ip == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (t->NumExistingRoutes == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES;i++)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE *e = &t->Entries[i];
|
|
|
|
if (e->Exists)
|
|
{
|
|
if (IsInSameNetwork4(ip, &e->Network, &e->SubnetMask))
|
|
{
|
|
if (max_mask <= e->SubnetMaskLen)
|
|
{
|
|
max_mask = e->SubnetMaskLen;
|
|
ret = e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Release the DHCP option
|
|
void FreeDhcpOptions(LIST *o)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
DHCP_OPTION *opt = LIST_DATA(o, i);
|
|
Free(opt->Data);
|
|
Free(opt);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
// Parse the DHCP Options
|
|
LIST *ParseDhcpOptions(void *data, UINT size)
|
|
{
|
|
BUF *b;
|
|
LIST *o;
|
|
DHCP_OPTION *last_opt;
|
|
// Validate arguments
|
|
if (data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, data, size);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
o = NewListFast(NULL);
|
|
|
|
last_opt = NULL;
|
|
|
|
while (true)
|
|
{
|
|
UCHAR c = 0;
|
|
UCHAR sz = 0;
|
|
DHCP_OPTION *opt;
|
|
if (ReadBuf(b, &c, 1) != 1)
|
|
{
|
|
break;
|
|
}
|
|
if (c == 0xff)
|
|
{
|
|
break;
|
|
}
|
|
if (ReadBuf(b, &sz, 1) != 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (c == DHCP_ID_PRIVATE && last_opt != NULL)
|
|
{
|
|
UINT new_size = last_opt->Size + (UINT)sz;
|
|
UCHAR *new_buf = ZeroMalloc(new_size);
|
|
Copy(new_buf, last_opt->Data, last_opt->Size);
|
|
ReadBuf(b, new_buf + last_opt->Size, sz);
|
|
Free(last_opt->Data);
|
|
last_opt->Data = new_buf;
|
|
last_opt->Size = new_size;
|
|
}
|
|
else
|
|
{
|
|
opt = ZeroMalloc(sizeof(DHCP_OPTION));
|
|
opt->Id = (UINT)c;
|
|
opt->Size = (UINT)sz;
|
|
opt->Data = ZeroMalloc((UINT)sz);
|
|
ReadBuf(b, opt->Data, sz);
|
|
Add(o, opt);
|
|
|
|
last_opt = opt;
|
|
}
|
|
}
|
|
|
|
FreeBuf(b);
|
|
|
|
return o;
|
|
}
|
|
|
|
// Rewrite the DHCP message data in the requested IPv4 packet appropriately
|
|
BUF *DhcpModifyIPv4(DHCP_MODIFY_OPTION *m, void *data, UINT size)
|
|
{
|
|
PKT *p;
|
|
BUF *ret = NULL;
|
|
// Validate arguments
|
|
if (m == NULL || data == NULL || size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
p = ParsePacketEx4(data, size, false, 0, false, false, false);
|
|
|
|
if (p != NULL && p->TypeL3 == L3_IPV4 && p->TypeL4 == L4_UDP && p->TypeL7 == L7_DHCPV4)
|
|
{
|
|
BUF *new_buf = DhcpModify(m, p->Payload, p->PayloadSize);
|
|
|
|
if (new_buf != NULL)
|
|
{
|
|
ret = NewBuf();
|
|
|
|
WriteBuf(ret, p->PacketData, p->PacketSize - p->PayloadSize);
|
|
WriteBuf(ret, new_buf->Buf, new_buf->Size);
|
|
|
|
FreeBuf(new_buf);
|
|
}
|
|
}
|
|
|
|
FreePacket(p);
|
|
|
|
if (ret != NULL)
|
|
{
|
|
PKT *p = ParsePacketEx4(ret->Buf, ret->Size, false, 0, false, false, false);
|
|
|
|
if (p != NULL)
|
|
{
|
|
// Recalculation of the UDP checksum
|
|
if (p->TypeL3 == L3_IPV4 && p->TypeL4 == L4_UDP)
|
|
{
|
|
UDP_HEADER *udp = p->L4.UDPHeader;
|
|
|
|
udp->Checksum = 0;
|
|
udp->Checksum = CalcChecksumForIPv4(p->L3.IPv4Header->SrcIP,
|
|
p->L3.IPv4Header->DstIP,
|
|
IP_PROTO_UDP,
|
|
udp,
|
|
p->PacketSize - (UINT)(((UCHAR *)udp) - ((UCHAR *)p->PacketData)), 0);
|
|
}
|
|
|
|
FreePacket(p);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Rewrite the DHCP packet appropriately
|
|
BUF *DhcpModify(DHCP_MODIFY_OPTION *m, void *data, UINT size)
|
|
{
|
|
DHCPV4_HEADER *dhcp_header;
|
|
UCHAR *data_ptr;
|
|
bool ret_ok = false;
|
|
BUF *ret = NULL;
|
|
BUF *opt_buf = NULL;
|
|
UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE);
|
|
bool ok = false;
|
|
DHCP_OPTION_LIST *opt = NULL;
|
|
LIST *opt_list = NULL;
|
|
LIST *opt_list2 = NULL;
|
|
UINT src_size = size;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (m == NULL || data == NULL || size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
data_ptr = (UCHAR *)data;
|
|
|
|
if (size < sizeof(DHCPV4_HEADER))
|
|
{
|
|
goto LABEL_CLEANUP;
|
|
}
|
|
|
|
dhcp_header = (DHCPV4_HEADER *)data_ptr;
|
|
data_ptr += sizeof(DHCPV4_HEADER);
|
|
|
|
// Search for a Magic Cookie
|
|
while (size >= 5)
|
|
{
|
|
if (Cmp(data_ptr, &magic_cookie, sizeof(UINT)) == 0)
|
|
{
|
|
// Found
|
|
data_ptr += sizeof(UINT);
|
|
size -= sizeof(UINT);
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
data_ptr++;
|
|
size--;
|
|
}
|
|
|
|
if (ok == false)
|
|
{
|
|
// The packet is invalid
|
|
goto LABEL_CLEANUP;
|
|
}
|
|
|
|
ret = NewBuf();
|
|
WriteBuf(ret, data, (UINT)(data_ptr - ((UCHAR *)data)));
|
|
|
|
// Parse the DHCP options list
|
|
opt = ParseDhcpOptionList(data_ptr, size);
|
|
if (opt == NULL)
|
|
{
|
|
// The packet is invalid
|
|
goto LABEL_CLEANUP;
|
|
}
|
|
|
|
opt_list = ParseDhcpOptions(data_ptr, size);
|
|
if (opt_list == NULL)
|
|
{
|
|
// The packet is invalid
|
|
goto LABEL_CLEANUP;
|
|
}
|
|
|
|
// Rebuilding the options list
|
|
opt_list2 = NewListFast(NULL);
|
|
|
|
for (i = 0;i < LIST_NUM(opt_list);i++)
|
|
{
|
|
DHCP_OPTION *o = LIST_DATA(opt_list, i);
|
|
DHCP_OPTION *o2 = NULL;
|
|
bool ok = true;
|
|
|
|
if (m->RemoveDefaultGatewayOnReply)
|
|
{
|
|
if (opt->Opcode == DHCP_OFFER || opt->Opcode == DHCP_ACK)
|
|
{
|
|
// Remove the default gateway from the DHCP Reply
|
|
if (o->Id == DHCP_ID_GATEWAY_ADDR)
|
|
{
|
|
ok = false;
|
|
}
|
|
if (o->Id == DHCP_ID_DNS_ADDR || o->Id == DHCP_ID_WINS_ADDR || o->Id == DHCP_ID_DOMAIN_NAME)
|
|
{
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok && o2 == NULL)
|
|
{
|
|
o2 = NewDhcpOption(o->Id, o->Data, o->Size);
|
|
}
|
|
|
|
if (o2 != NULL)
|
|
{
|
|
Add(opt_list2, o2);
|
|
}
|
|
}
|
|
|
|
opt_buf = BuildDhcpOptionsBuf(opt_list2);
|
|
|
|
WriteBuf(ret, opt_buf->Buf, opt_buf->Size);
|
|
|
|
if (src_size != ret->Size || Cmp(data, ret->Buf, ret->Size) != 0)
|
|
{
|
|
// Rewrite if anything changes. Do not rewrite if there is no change
|
|
ret_ok = true;
|
|
|
|
if (ret->Size < DHCP_MIN_SIZE)
|
|
{
|
|
// Padding
|
|
UCHAR *pad_buf;
|
|
UINT pad_size = DHCP_MIN_SIZE - ret->Size;
|
|
|
|
pad_buf = ZeroMalloc(pad_size);
|
|
|
|
WriteBuf(ret, pad_buf, pad_size);
|
|
|
|
Free(pad_buf);
|
|
}
|
|
}
|
|
|
|
LABEL_CLEANUP:
|
|
// Memory release
|
|
if (opt_buf != NULL)
|
|
{
|
|
FreeBuf(opt_buf);
|
|
}
|
|
|
|
if (opt != NULL)
|
|
{
|
|
Free(opt);
|
|
}
|
|
|
|
if (opt_list != NULL)
|
|
{
|
|
FreeDhcpOptions(opt_list);
|
|
}
|
|
|
|
if (opt_list2 != NULL)
|
|
{
|
|
FreeDhcpOptions(opt_list2);
|
|
}
|
|
|
|
// Return a value
|
|
if (ret_ok)
|
|
{
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
FreeBuf(ret);
|
|
return NULL;
|
|
}
|
|
}
|