reactos/drivers/network/dd/netkvm/Common/sw-offload.c
2021-06-11 15:33:08 +03:00

619 lines
23 KiB
C

/*
* This file contains SW Implementation of checksum computation for IP,TCP,UDP
*
* Copyright (c) 2008-2017 Red Hat, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of their contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ndis56common.h"
#ifdef WPP_EVENT_TRACING
#include "sw-offload.tmh"
#endif
#include <sal.h>
// till IP header size is 8 bit
#define MAX_SUPPORTED_IPV6_HEADERS (256 - 4)
typedef ULONG IPV6_ADDRESS[4];
// IPv6 Header RFC 2460 (40 bytes)
typedef struct _tagIPv6Header {
UCHAR ip6_ver_tc; // traffic class(low nibble), version (high nibble)
UCHAR ip6_tc_fl; // traffic class(high nibble), flow label
USHORT ip6_fl; // flow label, the rest
USHORT ip6_payload_len; // length of following headers and payload
UCHAR ip6_next_header; // next header type
UCHAR ip6_hoplimit; // hop limit
IPV6_ADDRESS ip6_src_address; //
IPV6_ADDRESS ip6_dst_address; //
} IPv6Header;
typedef union
{
IPv6Header v6;
IPv4Header v4;
} IPHeader;
// IPv6 Header RFC 2460 (n*8 bytes)
typedef struct _tagIPv6ExtHeader {
UCHAR ip6ext_next_header; // next header type
UCHAR ip6ext_hdr_len; // length of this header in 8 bytes unit, not including first 8 bytes
USHORT options; //
} IPv6ExtHeader;
// IP Pseudo Header RFC 768
typedef struct _tagIPv4PseudoHeader {
ULONG ipph_src; // Source address
ULONG ipph_dest; // Destination address
UCHAR ipph_zero; // 0
UCHAR ipph_protocol; // TCP/UDP
USHORT ipph_length; // TCP/UDP length
}tIPv4PseudoHeader;
// IPv6 Pseudo Header RFC 2460
typedef struct _tagIPv6PseudoHeader {
IPV6_ADDRESS ipph_src; // Source address
IPV6_ADDRESS ipph_dest; // Destination address
ULONG ipph_length; // TCP/UDP length
UCHAR z1; // 0
UCHAR z2; // 0
UCHAR z3; // 0
UCHAR ipph_protocol; // TCP/UDP
}tIPv6PseudoHeader;
#define PROTOCOL_TCP 6
#define PROTOCOL_UDP 17
#define IP_HEADER_LENGTH(pHeader) (((pHeader)->ip_verlen & 0x0F) << 2)
#define TCP_HEADER_LENGTH(pHeader) ((pHeader->tcp_flags & 0xF0) >> 2)
static __inline USHORT CheckSumCalculator(ULONG val, PVOID buffer, ULONG len)
{
PUSHORT pus = (PUSHORT)buffer;
ULONG count = len >> 1;
while (count--) val += *pus++;
if (len & 1) val += (USHORT)*(PUCHAR)pus;
val = (((val >> 16) | (val << 16)) + val) >> 16;
return (USHORT)~val;
}
/******************************************
IP header checksum calculator
*******************************************/
static __inline VOID CalculateIpChecksum(IPv4Header *pIpHeader)
{
pIpHeader->ip_xsum = 0;
pIpHeader->ip_xsum = CheckSumCalculator(0, pIpHeader, IP_HEADER_LENGTH(pIpHeader));
}
static __inline tTcpIpPacketParsingResult
ProcessTCPHeader(tTcpIpPacketParsingResult _res, PVOID pIpHeader, ULONG len, USHORT ipHeaderSize)
{
ULONG tcpipDataAt;
tTcpIpPacketParsingResult res = _res;
tcpipDataAt = ipHeaderSize + sizeof(TCPHeader);
res.xxpStatus = ppresXxpIncomplete;
res.TcpUdp = ppresIsTCP;
if (len >= tcpipDataAt)
{
TCPHeader *pTcpHeader = (TCPHeader *)RtlOffsetToPointer(pIpHeader, ipHeaderSize);
res.xxpStatus = ppresXxpKnown;
tcpipDataAt = ipHeaderSize + TCP_HEADER_LENGTH(pTcpHeader);
res.XxpIpHeaderSize = tcpipDataAt;
}
else
{
DPrintf(2, ("tcp: %d < min headers %d", len, tcpipDataAt));
}
return res;
}
static __inline tTcpIpPacketParsingResult
ProcessUDPHeader(tTcpIpPacketParsingResult _res, PVOID pIpHeader, ULONG len, USHORT ipHeaderSize)
{
tTcpIpPacketParsingResult res = _res;
ULONG udpDataStart = ipHeaderSize + sizeof(UDPHeader);
res.xxpStatus = ppresXxpIncomplete;
res.TcpUdp = ppresIsUDP;
res.XxpIpHeaderSize = udpDataStart;
if (len >= udpDataStart)
{
UDPHeader *pUdpHeader = (UDPHeader *)RtlOffsetToPointer(pIpHeader, ipHeaderSize);
USHORT datagramLength = swap_short(pUdpHeader->udp_length);
res.xxpStatus = ppresXxpKnown;
// may be full or not, but the datagram length is known
DPrintf(2, ("udp: len %d, datagramLength %d", len, datagramLength));
}
return res;
}
static __inline tTcpIpPacketParsingResult
QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
{
tTcpIpPacketParsingResult res;
UCHAR ver_len = pIpHeader->v4.ip_verlen;
UCHAR ip_version = (ver_len & 0xF0) >> 4;
USHORT ipHeaderSize = 0;
USHORT fullLength = 0;
res.value = 0;
if (ip_version == 4)
{
ipHeaderSize = (ver_len & 0xF) << 2;
fullLength = swap_short(pIpHeader->v4.ip_length);
DPrintf(3, ("ip_version %d, ipHeaderSize %d, protocol %d, iplen %d",
ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength));
res.ipStatus = (ipHeaderSize >= sizeof(IPv4Header)) ? ppresIPV4 : ppresNotIP;
if (len < ipHeaderSize) res.ipCheckSum = ppresIPTooShort;
if (fullLength) {}
else
{
DPrintf(2, ("ip v.%d, iplen %d", ip_version, fullLength));
}
}
else if (ip_version == 6)
{
UCHAR nextHeader = pIpHeader->v6.ip6_next_header;
BOOLEAN bParsingDone = FALSE;
ipHeaderSize = sizeof(pIpHeader->v6);
res.ipStatus = ppresIPV6;
res.ipCheckSum = ppresCSOK;
fullLength = swap_short(pIpHeader->v6.ip6_payload_len);
fullLength += ipHeaderSize;
while (nextHeader != 59)
{
IPv6ExtHeader *pExt;
switch (nextHeader)
{
case PROTOCOL_TCP:
bParsingDone = TRUE;
res.xxpStatus = ppresXxpKnown;
res.TcpUdp = ppresIsTCP;
res.xxpFull = len >= fullLength ? 1 : 0;
res = ProcessTCPHeader(res, pIpHeader, len, ipHeaderSize);
break;
case PROTOCOL_UDP:
bParsingDone = TRUE;
res.xxpStatus = ppresXxpKnown;
res.TcpUdp = ppresIsUDP;
res.xxpFull = len >= fullLength ? 1 : 0;
res = ProcessUDPHeader(res, pIpHeader, len, ipHeaderSize);
break;
//existing extended headers
case 0:
__fallthrough;
case 60:
__fallthrough;
case 43:
__fallthrough;
case 44:
__fallthrough;
case 51:
__fallthrough;
case 50:
__fallthrough;
case 135:
if (len >= ((ULONG)ipHeaderSize + 8))
{
pExt = (IPv6ExtHeader *)((PUCHAR)pIpHeader + ipHeaderSize);
nextHeader = pExt->ip6ext_next_header;
ipHeaderSize += 8;
ipHeaderSize += pExt->ip6ext_hdr_len * 8;
}
else
{
DPrintf(0, ("[%s] ERROR: Break in the middle of ext. headers(len %d, hdr > %d)", __FUNCTION__, len, ipHeaderSize));
res.ipStatus = ppresNotIP;
bParsingDone = TRUE;
}
break;
//any other protocol
default:
res.xxpStatus = ppresXxpOther;
bParsingDone = TRUE;
break;
}
if (bParsingDone)
break;
}
if (ipHeaderSize <= MAX_SUPPORTED_IPV6_HEADERS)
{
DPrintf(3, ("ip_version %d, ipHeaderSize %d, protocol %d, iplen %d",
ip_version, ipHeaderSize, nextHeader, fullLength));
res.ipHeaderSize = ipHeaderSize;
}
else
{
DPrintf(0, ("[%s] ERROR: IP chain is too large (%d)", __FUNCTION__, ipHeaderSize));
res.ipStatus = ppresNotIP;
}
}
if (res.ipStatus == ppresIPV4)
{
res.ipHeaderSize = ipHeaderSize;
res.xxpFull = len >= fullLength ? 1 : 0;
// bit "more fragments" or fragment offset mean the packet is fragmented
res.IsFragment = (pIpHeader->v4.ip_offset & ~0xC0) != 0;
switch (pIpHeader->v4.ip_protocol)
{
case PROTOCOL_TCP:
{
res = ProcessTCPHeader(res, pIpHeader, len, ipHeaderSize);
}
break;
case PROTOCOL_UDP:
{
res = ProcessUDPHeader(res, pIpHeader, len, ipHeaderSize);
}
break;
default:
res.xxpStatus = ppresXxpOther;
break;
}
}
return res;
}
static __inline USHORT GetXxpHeaderAndPayloadLen(IPHeader *pIpHeader, tTcpIpPacketParsingResult res)
{
if (res.ipStatus == ppresIPV4)
{
USHORT headerLength = IP_HEADER_LENGTH(&pIpHeader->v4);
USHORT len = swap_short(pIpHeader->v4.ip_length);
return len - headerLength;
}
if (res.ipStatus == ppresIPV6)
{
USHORT fullLength = swap_short(pIpHeader->v6.ip6_payload_len);
return fullLength + sizeof(pIpHeader->v6) - (USHORT)res.ipHeaderSize;
}
return 0;
}
static __inline USHORT CalculateIpv4PseudoHeaderChecksum(IPv4Header *pIpHeader, USHORT headerAndPayloadLen)
{
tIPv4PseudoHeader ipph;
USHORT checksum;
ipph.ipph_src = pIpHeader->ip_src;
ipph.ipph_dest = pIpHeader->ip_dest;
ipph.ipph_zero = 0;
ipph.ipph_protocol = pIpHeader->ip_protocol;
ipph.ipph_length = swap_short(headerAndPayloadLen);
checksum = CheckSumCalculator(0, &ipph, sizeof(ipph));
return ~checksum;
}
static __inline USHORT CalculateIpv6PseudoHeaderChecksum(IPv6Header *pIpHeader, USHORT headerAndPayloadLen)
{
tIPv6PseudoHeader ipph;
USHORT checksum;
ipph.ipph_src[0] = pIpHeader->ip6_src_address[0];
ipph.ipph_src[1] = pIpHeader->ip6_src_address[1];
ipph.ipph_src[2] = pIpHeader->ip6_src_address[2];
ipph.ipph_src[3] = pIpHeader->ip6_src_address[3];
ipph.ipph_dest[0] = pIpHeader->ip6_dst_address[0];
ipph.ipph_dest[1] = pIpHeader->ip6_dst_address[1];
ipph.ipph_dest[2] = pIpHeader->ip6_dst_address[2];
ipph.ipph_dest[3] = pIpHeader->ip6_dst_address[3];
ipph.z1 = ipph.z2 = ipph.z3 = 0;
ipph.ipph_protocol = pIpHeader->ip6_next_header;
ipph.ipph_length = swap_short(headerAndPayloadLen);
checksum = CheckSumCalculator(0, &ipph, sizeof(ipph));
return ~checksum;
}
static __inline USHORT CalculateIpPseudoHeaderChecksum(IPHeader *pIpHeader,
tTcpIpPacketParsingResult res,
USHORT headerAndPayloadLen)
{
if (res.ipStatus == ppresIPV4)
return CalculateIpv4PseudoHeaderChecksum(&pIpHeader->v4, headerAndPayloadLen);
if (res.ipStatus == ppresIPV6)
return CalculateIpv6PseudoHeaderChecksum(&pIpHeader->v6, headerAndPayloadLen);
return 0;
}
static __inline BOOLEAN
CompareNetCheckSumOnEndSystem(USHORT computedChecksum, USHORT arrivedChecksum)
{
//According to RFC 1624 sec. 3
//Checksum verification mechanism should treat 0xFFFF
//checksum value from received packet as 0x0000
if(arrivedChecksum == 0xFFFF)
arrivedChecksum = 0;
return computedChecksum == arrivedChecksum;
}
/******************************************
Calculates IP header checksum calculator
it can be already calculated
the header must be complete!
*******************************************/
static __inline tTcpIpPacketParsingResult
VerifyIpChecksum(
IPv4Header *pIpHeader,
tTcpIpPacketParsingResult known,
BOOLEAN bFix)
{
tTcpIpPacketParsingResult res = known;
if (res.ipCheckSum != ppresIPTooShort)
{
USHORT saved = pIpHeader->ip_xsum;
CalculateIpChecksum(pIpHeader);
res.ipCheckSum = CompareNetCheckSumOnEndSystem(pIpHeader->ip_xsum, saved) ? ppresCSOK : ppresCSBad;
if (!bFix)
pIpHeader->ip_xsum = saved;
else
res.fixedIpCS = res.ipCheckSum == ppresCSBad;
}
return res;
}
/*********************************************
Calculates UDP checksum, assuming the checksum field
is initialized with pseudoheader checksum
**********************************************/
static VOID CalculateUdpChecksumGivenPseudoCS(UDPHeader *pUdpHeader, ULONG udpLength)
{
pUdpHeader->udp_xsum = CheckSumCalculator(0, pUdpHeader, udpLength);
}
/*********************************************
Calculates TCP checksum, assuming the checksum field
is initialized with pseudoheader checksum
**********************************************/
static __inline VOID CalculateTcpChecksumGivenPseudoCS(TCPHeader *pTcpHeader, ULONG tcpLength)
{
pTcpHeader->tcp_xsum = CheckSumCalculator(0, pTcpHeader, tcpLength);
}
/************************************************
Checks (and fix if required) the TCP checksum
sets flags in result structure according to verification
TcpPseudoOK if valid pseudo CS was found
TcpOK if valid TCP checksum was found
************************************************/
static __inline tTcpIpPacketParsingResult
VerifyTcpChecksum( IPHeader *pIpHeader, ULONG len, tTcpIpPacketParsingResult known, ULONG whatToFix)
{
USHORT phcs;
tTcpIpPacketParsingResult res = known;
TCPHeader *pTcpHeader = (TCPHeader *)RtlOffsetToPointer(pIpHeader, res.ipHeaderSize);
USHORT saved = pTcpHeader->tcp_xsum;
USHORT xxpHeaderAndPayloadLen = GetXxpHeaderAndPayloadLen(pIpHeader, res);
if (len >= res.ipHeaderSize)
{
phcs = CalculateIpPseudoHeaderChecksum(pIpHeader, res, xxpHeaderAndPayloadLen);
res.xxpCheckSum = CompareNetCheckSumOnEndSystem(phcs, saved) ? ppresPCSOK : ppresCSBad;
if (res.xxpCheckSum != ppresPCSOK || whatToFix)
{
if (whatToFix & pcrFixPHChecksum)
{
if (len >= (ULONG)(res.ipHeaderSize + sizeof(*pTcpHeader)))
{
pTcpHeader->tcp_xsum = phcs;
res.fixedXxpCS = res.xxpCheckSum != ppresPCSOK;
}
else
res.xxpStatus = ppresXxpIncomplete;
}
else if (res.xxpFull)
{
//USHORT ipFullLength = swap_short(pIpHeader->v4.ip_length);
pTcpHeader->tcp_xsum = phcs;
CalculateTcpChecksumGivenPseudoCS(pTcpHeader, xxpHeaderAndPayloadLen);
if (CompareNetCheckSumOnEndSystem(pTcpHeader->tcp_xsum, saved))
res.xxpCheckSum = ppresCSOK;
if (!(whatToFix & pcrFixXxpChecksum))
pTcpHeader->tcp_xsum = saved;
else
res.fixedXxpCS =
res.xxpCheckSum == ppresCSBad || res.xxpCheckSum == ppresPCSOK;
}
else if (whatToFix)
{
res.xxpStatus = ppresXxpIncomplete;
}
}
else if (res.xxpFull)
{
// we have correct PHCS and we do not need to fix anything
// there is a very small chance that it is also good TCP CS
// in such rare case we give a priority to TCP CS
CalculateTcpChecksumGivenPseudoCS(pTcpHeader, xxpHeaderAndPayloadLen);
if (CompareNetCheckSumOnEndSystem(pTcpHeader->tcp_xsum, saved))
res.xxpCheckSum = ppresCSOK;
pTcpHeader->tcp_xsum = saved;
}
}
else
res.ipCheckSum = ppresIPTooShort;
return res;
}
/************************************************
Checks (and fix if required) the UDP checksum
sets flags in result structure according to verification
UdpPseudoOK if valid pseudo CS was found
UdpOK if valid UDP checksum was found
************************************************/
static __inline tTcpIpPacketParsingResult
VerifyUdpChecksum( IPHeader *pIpHeader, ULONG len, tTcpIpPacketParsingResult known, ULONG whatToFix)
{
USHORT phcs;
tTcpIpPacketParsingResult res = known;
UDPHeader *pUdpHeader = (UDPHeader *)RtlOffsetToPointer(pIpHeader, res.ipHeaderSize);
USHORT saved = pUdpHeader->udp_xsum;
USHORT xxpHeaderAndPayloadLen = GetXxpHeaderAndPayloadLen(pIpHeader, res);
if (len >= res.ipHeaderSize)
{
phcs = CalculateIpPseudoHeaderChecksum(pIpHeader, res, xxpHeaderAndPayloadLen);
res.xxpCheckSum = CompareNetCheckSumOnEndSystem(phcs, saved) ? ppresPCSOK : ppresCSBad;
if (whatToFix & pcrFixPHChecksum)
{
if (len >= (ULONG)(res.ipHeaderSize + sizeof(UDPHeader)))
{
pUdpHeader->udp_xsum = phcs;
res.fixedXxpCS = res.xxpCheckSum != ppresPCSOK;
}
else
res.xxpStatus = ppresXxpIncomplete;
}
else if (res.xxpCheckSum != ppresPCSOK || (whatToFix & pcrFixXxpChecksum))
{
if (res.xxpFull)
{
pUdpHeader->udp_xsum = phcs;
CalculateUdpChecksumGivenPseudoCS(pUdpHeader, xxpHeaderAndPayloadLen);
if (CompareNetCheckSumOnEndSystem(pUdpHeader->udp_xsum, saved))
res.xxpCheckSum = ppresCSOK;
if (!(whatToFix & pcrFixXxpChecksum))
pUdpHeader->udp_xsum = saved;
else
res.fixedXxpCS =
res.xxpCheckSum == ppresCSBad || res.xxpCheckSum == ppresPCSOK;
}
else
res.xxpCheckSum = ppresXxpIncomplete;
}
else if (res.xxpFull)
{
// we have correct PHCS and we do not need to fix anything
// there is a very small chance that it is also good UDP CS
// in such rare case we give a priority to UDP CS
CalculateUdpChecksumGivenPseudoCS(pUdpHeader, xxpHeaderAndPayloadLen);
if (CompareNetCheckSumOnEndSystem(pUdpHeader->udp_xsum, saved))
res.xxpCheckSum = ppresCSOK;
pUdpHeader->udp_xsum = saved;
}
}
else
res.ipCheckSum = ppresIPTooShort;
return res;
}
static LPCSTR __inline GetPacketCase(tTcpIpPacketParsingResult res)
{
static const char *const IPCaseName[4] = { "not tested", "Non-IP", "IPv4", "IPv6" };
if (res.xxpStatus == ppresXxpKnown) return res.TcpUdp == ppresIsTCP ?
(res.ipStatus == ppresIPV4 ? "TCPv4" : "TCPv6") :
(res.ipStatus == ppresIPV4 ? "UDPv4" : "UDPv6");
if (res.xxpStatus == ppresXxpIncomplete) return res.TcpUdp == ppresIsTCP ? "Incomplete TCP" : "Incomplete UDP";
if (res.xxpStatus == ppresXxpOther) return "IP";
return IPCaseName[res.ipStatus];
}
static LPCSTR __inline GetIPCSCase(tTcpIpPacketParsingResult res)
{
static const char *const CSCaseName[4] = { "not tested", "(too short)", "OK", "Bad" };
return CSCaseName[res.ipCheckSum];
}
static LPCSTR __inline GetXxpCSCase(tTcpIpPacketParsingResult res)
{
static const char *const CSCaseName[4] = { "-", "PCS", "CS", "Bad" };
return CSCaseName[res.xxpCheckSum];
}
static __inline VOID PrintOutParsingResult(
tTcpIpPacketParsingResult res,
int level,
LPCSTR procname)
{
DPrintf(level, ("[%s] %s packet IPCS %s%s, checksum %s%s", procname,
GetPacketCase(res),
GetIPCSCase(res),
res.fixedIpCS ? "(fixed)" : "",
GetXxpCSCase(res),
res.fixedXxpCS ? "(fixed)" : ""));
}
tTcpIpPacketParsingResult ParaNdis_CheckSumVerify(PVOID buffer, ULONG size, ULONG flags, LPCSTR caller)
{
tTcpIpPacketParsingResult res = QualifyIpPacket(buffer, size);
if (res.ipStatus == ppresIPV4)
{
if (flags & pcrIpChecksum)
res = VerifyIpChecksum(buffer, res, (flags & pcrFixIPChecksum) != 0);
if(res.xxpStatus == ppresXxpKnown)
{
if (res.TcpUdp == ppresIsTCP) /* TCP */
{
if(flags & pcrTcpV4Checksum)
{
res = VerifyTcpChecksum(buffer, size, res, flags & (pcrFixPHChecksum | pcrFixTcpV4Checksum));
}
}
else /* UDP */
{
if (flags & pcrUdpV4Checksum)
{
res = VerifyUdpChecksum(buffer, size, res, flags & (pcrFixPHChecksum | pcrFixUdpV4Checksum));
}
}
}
}
else if (res.ipStatus == ppresIPV6)
{
if(res.xxpStatus == ppresXxpKnown)
{
if (res.TcpUdp == ppresIsTCP) /* TCP */
{
if(flags & pcrTcpV6Checksum)
{
res = VerifyTcpChecksum(buffer, size, res, flags & (pcrFixPHChecksum | pcrFixTcpV6Checksum));
}
}
else /* UDP */
{
if (flags & pcrUdpV6Checksum)
{
res = VerifyUdpChecksum(buffer, size, res, flags & (pcrFixPHChecksum | pcrFixUdpV6Checksum));
}
}
}
}
PrintOutParsingResult(res, 1, caller);
return res;
}
tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, LPCSTR caller)
{
tTcpIpPacketParsingResult res = QualifyIpPacket(buffer, size);
PrintOutParsingResult(res, 1, caller);
return res;
}