// SoftEther VPN Source Code - Developer Edition Master Branch // Mayaqua Kernel // TcpIp.c // Utility module for TCP/IP packet processing #include "TcpIp.h" #include "Cfg.h" #include "Memory.h" #include "Str.h" // 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; } // 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; 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) { 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; } // Assemble 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; } BUF *BuildICMPv6RouterSoliciation(IPV6_ADDR *src_ip, IPV6_ADDR *target_ip, UCHAR *my_mac_address, UINT id) { ICMPV6_OPTION_LIST opt; ICMPV6_OPTION_LINK_LAYER link; ICMPV6_ROUTER_SOLICIATION_HEADER header; BUF *b; BUF *b2; BUF *ret; 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)); b2 = NewBuf(); WriteBuf(b2, &header, sizeof(header)); WriteBufBuf(b2, b); ret = BuildICMPv6(src_ip, target_ip, 255, ICMPV6_TYPE_ROUTER_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 = 0; // Validate arguments if (q == NULL) { return IPV6_HEADER_NONE; } p = (UINT *)GetNext(q); if (p != NULL) { 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; UINT i; // 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)); } for (i = 0; i < ICMPV6_OPTION_PREFIXES_MAX_COUNT; i++) { if (o->Prefix[i] != NULL) { BuildICMPv6OptionValue(b, ICMPV6_OPTION_TYPE_PREFIX, o->Prefix[i], sizeof(ICMPV6_OPTION_PREFIX)); } else { break; } } 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 packet but without data layer except for ICMP PKT *ParsePacketUpToICMPv6(UCHAR *buf, UINT size) { return ParsePacketEx5(buf, size, false, 0, true, true, false, true); } // 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) { return ParsePacketEx5(buf, size, no_l3, vlan_type_id, bridge_id_as_mac_address, no_http, correct_checksum, false); } PKT *ParsePacketEx5(UCHAR *buf, UINT size, bool no_l3, UINT vlan_type_id, bool bridge_id_as_mac_address, bool no_http, bool correct_checksum, bool no_l3_l4_except_icmpv6) { 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, no_l3_l4_except_icmpv6) == 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) { 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 ParsePacketL2Ex(PKT *p, UCHAR *buf, UINT size, bool no_l3, bool no_l3_l4_except_icmpv6) { 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 || (Cmp(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 || no_l3_l4_except_icmpv6) { return true; } return ParsePacketARPv4(p, buf, size); case MAC_PROTO_IPV4: // IPv4 if (no_l3 || no_l3_l4_except_icmpv6) { return true; } return ParsePacketIPv4(p, buf, size); case MAC_PROTO_IPV6: // IPv6 if (no_l3) { return true; } return ParsePacketIPv6(p, buf, size, no_l3_l4_except_icmpv6); 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)) { UINT i; for (i = 0; i < ICMPV6_OPTION_PREFIXES_MAX_COUNT; i++) { if (o->Prefix[i] == NULL) { o->Prefix[i] = (ICMPV6_OPTION_PREFIX *)header_pointer; break; } } } 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) { UINT i; // Validate arguments if (o == NULL) { return; } Free(o->SourceLinkLayer); Free(o->TargetLinkLayer); for (i = 0; i < ICMPV6_OPTION_PREFIXES_MAX_COUNT; i++) { Free(o->Prefix[i]); o->Prefix[i] = NULL; } Free(o->Mtu); } // Clone of the ICMPv6 options void CloneICMPv6Options(ICMPV6_OPTION_LIST *dst, ICMPV6_OPTION_LIST *src) { UINT i; // 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)); for (i = 0; i < ICMPV6_OPTION_PREFIXES_MAX_COUNT; i++) { if (src->Prefix[i] != NULL) { dst->Prefix[i] = Clone(src->Prefix[i], sizeof(ICMPV6_OPTION_PREFIX)); } else { break; } } dst->Mtu = Clone(src->Mtu, sizeof(ICMPV6_OPTION_MTU)); } // IPv6 parsing bool ParsePacketIPv6(PKT *p, UCHAR *buf, UINT size, bool no_l3_l4_except_icmpv6) { // 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 if (no_l3_l4_except_icmpv6) { return true; } return ParseTCP(p, buf, size); case IP_PROTO_UDP: // UDP if (no_l3_l4_except_icmpv6) { return true; } 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 = size; ret->Id = 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; 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; } }