You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2189 lines
66 KiB
2189 lines
66 KiB
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
|
//
|
|
// Copyright (c) 1985-2000 Microsoft Corporation
|
|
//
|
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
|
// You should have received a copy of the Microsoft End-User License Agreement
|
|
// for this software along with this release; see the file "license.txt".
|
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Various subroutines for Internet Protocol Version 6
|
|
//
|
|
|
|
|
|
#include "oscfg.h"
|
|
#include "ndis.h"
|
|
#include "tdi.h"
|
|
#include "tdistat.h"
|
|
#include "tdikrnl.h"
|
|
#include "ip6imp.h"
|
|
#include "ip6def.h"
|
|
#include "fragment.h"
|
|
#include "icmp.h"
|
|
#include "neighbor.h"
|
|
#include "route.h"
|
|
#include "mld.h"
|
|
#include "md5.h"
|
|
#include "ntddksec.h"
|
|
|
|
uint IPv6TickCount = 0;
|
|
|
|
uint RandomValue = 0;
|
|
|
|
|
|
//* GetSystemRandomBits - Ask the KSecDD driver for a block of 'random' bits.
|
|
//
|
|
// This routine requests a block of random bits from the KSecDD driver.
|
|
// Doing so is not cheap - we only use this routine to provide seed values
|
|
// for our other random number generators.
|
|
//
|
|
int // Returns TRUE if successful, FALSE otherwise.
|
|
GetSystemRandomBits(
|
|
unsigned char *Buffer, // Buffer to fill with random data.
|
|
uint Length) // Length of Buffer in bytes.
|
|
{
|
|
UNICODE_STRING DeviceName;
|
|
NTSTATUS NtStatus;
|
|
PFILE_OBJECT FileObject;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIRP pIrp;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
KEVENT kEvent;
|
|
|
|
RtlInitUnicodeString(&DeviceName, DD_KSEC_DEVICE_NAME_U);
|
|
|
|
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
|
|
|
|
//
|
|
// Get the file and device objects for KDSECDD,
|
|
// acquire a reference to the device-object,
|
|
// release the unneeded reference to the file-object,
|
|
// and build the I/O control request to issue to KSecDD.
|
|
//
|
|
|
|
NtStatus = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS,
|
|
&FileObject, &DeviceObject);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE,
|
|
"Tcpip6: IoGetDeviceObjectPointer(KSecDD)=%08x\n",
|
|
NtStatus));
|
|
return FALSE;
|
|
}
|
|
ObReferenceObject(DeviceObject);
|
|
ObDereferenceObject(FileObject);
|
|
|
|
pIrp = IoBuildDeviceIoControlRequest(IOCTL_KSEC_RNG,
|
|
DeviceObject,
|
|
NULL, // No input buffer.
|
|
0,
|
|
Buffer,
|
|
Length,
|
|
FALSE,
|
|
&kEvent,
|
|
&ioStatusBlock);
|
|
|
|
if (pIrp == NULL) {
|
|
ObDereferenceObject(DeviceObject);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Issue the I/O control request, wait for it to complete
|
|
// if necessary, and release the reference to KSecDD's device-object.
|
|
//
|
|
NtStatus = IoCallDriver(DeviceObject, pIrp);
|
|
|
|
if (NtStatus == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&kEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // Not alertable.
|
|
NULL); // No timeout.
|
|
|
|
NtStatus = ioStatusBlock.Status;
|
|
}
|
|
ObDereferenceObject(DeviceObject);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE,
|
|
"Tcpip6: IoCallDriver IOCTL_KSEC_RNG failed %#x\n",
|
|
NtStatus));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//* SeedRandom - Provide a seed value.
|
|
//
|
|
// Called to provide a seed value for the random number generator.
|
|
//
|
|
void
|
|
SeedRandom(const uchar *Seed, uint Length)
|
|
{
|
|
uint OldValue;
|
|
MD5_CTX Context;
|
|
union {
|
|
uint NewValue;
|
|
uchar Buffer[16];
|
|
} Hash;
|
|
|
|
do {
|
|
OldValue = RandomValue;
|
|
MD5Init(&Context);
|
|
MD5Update(&Context, (uchar *)Seed, Length);
|
|
MD5Update(&Context, (uchar *)&OldValue, sizeof OldValue);
|
|
MD5Final(&Context);
|
|
memcpy(Hash.Buffer, Context.digest, MD5DIGESTLEN);
|
|
} while (InterlockedCompareExchange((PLONG)&RandomValue,
|
|
(LONG)Hash.NewValue,
|
|
(LONG)OldValue) != (LONG)OldValue);
|
|
}
|
|
|
|
|
|
//* Random - Generate a pseudo random value between 0 and 2^32 - 1.
|
|
//
|
|
// This routine is a quick and dirty psuedo random number generator.
|
|
// It has the advantages of being fast and consuming very little
|
|
// memory (for either code or data). The random numbers it produces are
|
|
// not of the best quality, however. A much better generator could be
|
|
// had if we were willing to use an extra 256 bytes of memory for data.
|
|
//
|
|
// This routine uses the linear congruential method (see Knuth, Vol II),
|
|
// with specific values for the multiplier and constant taken from
|
|
// Numerical Recipes in C Second Edition by Press, et. al.
|
|
//
|
|
uint
|
|
Random(void)
|
|
{
|
|
uint NewValue, OldValue;
|
|
|
|
//
|
|
// The algorithm is R = (aR + c) mod m, where R is the random number,
|
|
// a is a magic multiplier, c is a constant, and the modulus m is the
|
|
// maximum number of elements in the period. We chose our m to be 2^32
|
|
// in order to get the mod operation for free.
|
|
//
|
|
do {
|
|
OldValue = RandomValue;
|
|
NewValue = (1664525 * OldValue) + 1013904223;
|
|
} while (InterlockedCompareExchange((PLONG)&RandomValue,
|
|
(LONG)NewValue,
|
|
(LONG)OldValue) != (LONG)OldValue);
|
|
|
|
return NewValue;
|
|
}
|
|
|
|
|
|
//* RandomNumber
|
|
//
|
|
// Returns a number randomly selected from a range.
|
|
//
|
|
uint
|
|
RandomNumber(uint Min, uint Max)
|
|
{
|
|
uint Number;
|
|
|
|
//
|
|
// Note that the high bits of Random() are much more random
|
|
// than the low bits.
|
|
//
|
|
Number = Max - Min; // Spread.
|
|
Number = (uint)(((ULONGLONG)Random() * Number) >> 32); // Randomize spread.
|
|
Number += Min;
|
|
|
|
return Number;
|
|
}
|
|
|
|
|
|
//* CopyToBufferChain - Copy received packet to NDIS buffer chain.
|
|
//
|
|
// Copies from a received packet to an NDIS buffer chain.
|
|
// The received packet data comes in two flavors: if SrcPacket is
|
|
// NULL, then SrcData is used. SrcOffset specifies an offset
|
|
// into SrcPacket or SrcData.
|
|
//
|
|
// Length limits the number of bytes copied. The number of bytes
|
|
// copied may also be limited by the destination & source.
|
|
//
|
|
uint // Returns: number of bytes copied.
|
|
CopyToBufferChain(
|
|
PNDIS_BUFFER DstBuffer,
|
|
uint DstOffset,
|
|
PNDIS_PACKET SrcPacket,
|
|
uint SrcOffset,
|
|
uchar *SrcData,
|
|
uint Length)
|
|
{
|
|
PNDIS_BUFFER SrcBuffer = NULL;
|
|
uchar *DstData;
|
|
uint DstSize, SrcSize;
|
|
uint BytesCopied, BytesToCopy;
|
|
|
|
//
|
|
// Skip DstOffset bytes in the destination buffer chain.
|
|
// NB: DstBuffer might be NULL to begin with; that's legitimate.
|
|
//
|
|
for (;;) {
|
|
if (DstBuffer == NULL)
|
|
return 0;
|
|
|
|
NdisQueryBufferSafe(DstBuffer, &DstData, &DstSize, LowPagePriority);
|
|
if (DstData == NULL) {
|
|
//
|
|
// Couldn't map destination buffer into kernel address space.
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
if (DstOffset < DstSize) {
|
|
DstData += DstOffset;
|
|
DstSize -= DstOffset;
|
|
break;
|
|
}
|
|
|
|
DstOffset -= DstSize;
|
|
NdisGetNextBuffer(DstBuffer, &DstBuffer);
|
|
}
|
|
|
|
if (SrcPacket != NULL) {
|
|
//
|
|
// Skip SrcOffset bytes into SrcPacket.
|
|
// NB: SrcBuffer might be NULL to begin with; that's legitimate.
|
|
//
|
|
NdisQueryPacket(SrcPacket, NULL, NULL, &SrcBuffer, NULL);
|
|
|
|
for (;;) {
|
|
if (SrcBuffer == NULL)
|
|
return 0;
|
|
|
|
NdisQueryBuffer(SrcBuffer, &SrcData, &SrcSize);
|
|
|
|
if (SrcOffset < SrcSize) {
|
|
SrcData += SrcOffset;
|
|
SrcSize -= SrcOffset;
|
|
break;
|
|
}
|
|
|
|
SrcOffset -= SrcSize;
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
}
|
|
} else {
|
|
//
|
|
// Using SrcData/SrcOffset instead of SrcPacket/SrcOffset.
|
|
// In this case, we need not initialize SrcBuffer
|
|
// because the copy loop below will never attempt
|
|
// to advance to another SrcBuffer.
|
|
//
|
|
SrcSize = Length;
|
|
SrcData += SrcOffset;
|
|
}
|
|
|
|
//
|
|
// Perform the copy, advancing DstBuffer and SrcBuffer as needed.
|
|
// Normally Length is initially non-zero, so no reason
|
|
// to check Length first.
|
|
//
|
|
for (BytesCopied = 0;;) {
|
|
|
|
BytesToCopy = MIN(MIN(Length, SrcSize), DstSize);
|
|
RtlCopyMemory(DstData, SrcData, BytesToCopy);
|
|
BytesCopied += BytesToCopy;
|
|
|
|
Length -= BytesToCopy;
|
|
if (Length == 0)
|
|
break; // All done.
|
|
|
|
DstData += BytesToCopy;
|
|
DstSize -= BytesToCopy;
|
|
if (DstSize == 0) {
|
|
//
|
|
// We ran out of room in our current destination buffer.
|
|
// Proceed to next buffer on the chain.
|
|
//
|
|
NdisGetNextBuffer(DstBuffer, &DstBuffer);
|
|
if (DstBuffer == NULL)
|
|
break;
|
|
|
|
NdisQueryBuffer(DstBuffer, &DstData, &DstSize);
|
|
}
|
|
|
|
SrcData += BytesToCopy;
|
|
SrcSize -= BytesToCopy;
|
|
if (SrcSize == 0) {
|
|
//
|
|
// We ran out of data in our current source buffer.
|
|
// Proceed to the next buffer on the chain.
|
|
//
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
if (SrcBuffer == NULL)
|
|
break;
|
|
|
|
NdisQueryBuffer(SrcBuffer, &SrcData, &SrcSize);
|
|
}
|
|
}
|
|
|
|
return BytesCopied;
|
|
}
|
|
|
|
|
|
//* CopyPacketToNdis - Copy from an IPv6Packet chain to an NDIS buffer.
|
|
//
|
|
// This is the function we use to copy from a chain of IPv6Packets
|
|
// to ONE NDIS buffer. The caller specifies the source and
|
|
// destination, a maximum size to copy, and an offset into the first
|
|
// packet to start copying from. We copy as much as possible up to the
|
|
// size, and return the size copied.
|
|
//
|
|
// Note that SrcOffset is relative to the beginning of the first packet in
|
|
// the chain, and NOT the current 'Position' in that packet.
|
|
//
|
|
// The source packet chain is not modified in any way by this routine.
|
|
//
|
|
uint // Returns: Bytes copied.
|
|
CopyPacketToNdis(
|
|
PNDIS_BUFFER DestBuf, // Destination NDIS buffer chain.
|
|
IPv6Packet *SrcPkt, // Source packet chain.
|
|
uint Size, // Size in bytes to copy.
|
|
uint DestOffset, // Offset into dest buffer to start copying to.
|
|
uint SrcOffset) // Offset into packet chain to copy from.
|
|
{
|
|
uint TotalBytesCopied = 0; // Bytes we've copied so far.
|
|
uint BytesCopied; // Bytes copied out of each buffer.
|
|
uint DestSize; // Space left in destination.
|
|
void *SrcData; // Current source data pointer.
|
|
uint SrcContig; // Amount of Contiguous data from SrcData on.
|
|
PNDIS_BUFFER SrcBuf; // Current buffer in current packet.
|
|
PNDIS_BUFFER TempBuf; // Used to count through destination chain.
|
|
uint PacketSize; // Total size of current packet.
|
|
NTSTATUS Status;
|
|
|
|
|
|
ASSERT(SrcPkt != NULL);
|
|
|
|
//
|
|
// The destination buffer can be NULL - this is valid, if odd.
|
|
//
|
|
if (DestBuf == NULL)
|
|
return 0;
|
|
|
|
//
|
|
// Limit our copy to the smaller of the requested amount and the
|
|
// available space in the destination buffer chain.
|
|
//
|
|
TempBuf = DestBuf;
|
|
DestSize = 0;
|
|
|
|
do {
|
|
DestSize += NdisBufferLength(TempBuf);
|
|
TempBuf = NDIS_BUFFER_LINKAGE(TempBuf);
|
|
} while (TempBuf);
|
|
|
|
ASSERT(DestSize >= DestOffset);
|
|
DestSize -= DestOffset;
|
|
DestSize = MIN(DestSize, Size);
|
|
|
|
//
|
|
// First, skip SrcOffset bytes into the source packet chain.
|
|
//
|
|
if ((SrcOffset == SrcPkt->Position) && (Size <= SrcPkt->ContigSize)) {
|
|
//
|
|
// One common case is that we want to start from the current Position.
|
|
// REVIEW: This case common enough to be worth this check?
|
|
//
|
|
SrcContig = SrcPkt->ContigSize;
|
|
SrcData = SrcPkt->Data;
|
|
SrcBuf = NULL;
|
|
PacketSize = SrcPkt->TotalSize;
|
|
} else {
|
|
//
|
|
// Otherwise step through packets and buffer regions until
|
|
// we find the desired spot.
|
|
//
|
|
PacketSize = SrcPkt->Position + SrcPkt->TotalSize;
|
|
while (SrcOffset >= PacketSize) {
|
|
// Skip a whole packet.
|
|
SrcOffset -= PacketSize;
|
|
SrcPkt = SrcPkt->Next;
|
|
ASSERT(SrcPkt != NULL);
|
|
PacketSize = SrcPkt->Position + SrcPkt->TotalSize;
|
|
}
|
|
//
|
|
// Found the right packet in the chain, now find desired buffer.
|
|
//
|
|
PacketSize -= SrcOffset;
|
|
if (SrcPkt->NdisPacket == NULL) {
|
|
//
|
|
// This packet must be just a single contiguous region.
|
|
// Finding the right spot is a simple matter of arithmetic.
|
|
//
|
|
SrcContig = PacketSize;
|
|
SrcData = (uchar *)SrcPkt->FlatData + SrcOffset;
|
|
SrcBuf = NULL;
|
|
} else {
|
|
uchar *BufAddr;
|
|
uint BufLen;
|
|
|
|
//
|
|
// There may be multiple buffers comprising this packet.
|
|
// Step through them until we arrive at the right spot.
|
|
//
|
|
SrcBuf = NdisFirstBuffer(SrcPkt->NdisPacket);
|
|
NdisQueryBuffer(SrcBuf, &BufAddr, &BufLen);
|
|
while (SrcOffset >= BufLen) {
|
|
// Skip to the next buffer.
|
|
SrcOffset -= BufLen;
|
|
NdisGetNextBuffer(SrcBuf, &SrcBuf);
|
|
ASSERT(SrcBuf != NULL);
|
|
NdisQueryBuffer(SrcBuf, &BufAddr, &BufLen);
|
|
}
|
|
SrcContig = BufLen - SrcOffset;
|
|
SrcData = BufAddr + BufLen - SrcContig;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We're now at the point where we wish to start copying.
|
|
//
|
|
while (DestSize != 0) {
|
|
uint BytesToCopy;
|
|
|
|
BytesToCopy = MIN(DestSize, SrcContig);
|
|
Status = TdiCopyBufferToMdl(SrcData, 0, BytesToCopy,
|
|
DestBuf, DestOffset, (PULONG)&BytesCopied);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
ASSERT(BytesCopied == BytesToCopy);
|
|
TotalBytesCopied += BytesToCopy;
|
|
|
|
if (BytesToCopy < DestSize) {
|
|
//
|
|
// Not done yet, we ran out of either source packet or buffer.
|
|
// Get next one and fix up pointers/sizes for the next copy.
|
|
//
|
|
DestOffset += BytesToCopy;
|
|
PacketSize -= BytesToCopy;
|
|
if (PacketSize == 0) {
|
|
// Get next packet on chain.
|
|
SrcPkt = SrcPkt->Next;
|
|
ASSERT(SrcPkt != NULL);
|
|
PacketSize = SrcPkt->Position + SrcPkt->TotalSize;
|
|
if (SrcPkt->NdisPacket == NULL) {
|
|
// Single contiguous region.
|
|
SrcData = (uchar *)SrcPkt->FlatData + SrcPkt->Position;
|
|
SrcContig = SrcPkt->TotalSize;
|
|
} else {
|
|
// Potentially multiple buffers.
|
|
SrcBuf = NdisFirstBuffer(SrcPkt->NdisPacket);
|
|
NdisQueryBuffer(SrcBuf, &SrcData, &SrcContig);
|
|
}
|
|
} else {
|
|
// Get next buffer in packet.
|
|
ASSERT(SrcBuf != NULL);
|
|
NdisGetNextBuffer(SrcBuf, &SrcBuf);
|
|
ASSERT(SrcBuf != NULL);
|
|
NdisQueryBuffer(SrcBuf, &SrcData, &SrcContig);
|
|
}
|
|
}
|
|
DestSize -= BytesToCopy;
|
|
}
|
|
|
|
return TotalBytesCopied;
|
|
}
|
|
|
|
|
|
//* CopyPacketToFlatOrNdis - Copy from an IPv6Packet chain to a buffer or MDL.
|
|
//
|
|
// Called during receive processing to copy from an IPv6Packet chain to a
|
|
// flat buffer or an NDIS_BUFFER. We skip SrcOffset bytes into the source
|
|
// chain, and then copy Size bytes. If DestPtr is specified, the transfer
|
|
// occurs directly into the virtual address that it specifies. Otherwise,
|
|
// the transfer occurs into the pages described by DestBuf. Either way,
|
|
// DestOffset specifies the offset at which to begin transferring bytes.
|
|
//
|
|
// Note that SrcOffset is relative to the beginning of the packet, NOT
|
|
// the current 'Position'.
|
|
//
|
|
// The source packet chain is not modified in any way by this routine.
|
|
//
|
|
void // Returns: Nothing.
|
|
CopyPacketToFlatOrNdis(
|
|
PNDIS_BUFFER DestBuf, // Destination MDL entry.
|
|
uchar *DestPtr, // Destination buffer (unstructured memory).
|
|
uint DestOffset, // Offset in DestBuf to start copying to.
|
|
IPv6Packet *SrcPkt, // Source packet chain.
|
|
uint Size, // Size in bytes to copy.
|
|
uint SrcOffset) // Offset in SrcPkt to start copying from.
|
|
{
|
|
uint SrcContig;
|
|
void *SrcData;
|
|
PNDIS_BUFFER SrcBuf;
|
|
uint PacketSize;
|
|
|
|
#if DBG
|
|
IPv6Packet *TempPkt;
|
|
uint TempSize;
|
|
#endif
|
|
|
|
ASSERT(DestBuf != NULL || DestPtr != NULL);
|
|
ASSERT(SrcPkt != NULL);
|
|
|
|
#if DBG
|
|
//
|
|
// In debug versions check to make sure we're copying a reasonable size
|
|
// and from a reasonable offset.
|
|
//
|
|
TempPkt = SrcPkt;
|
|
TempSize = TempPkt->Position + TempPkt->TotalSize;
|
|
TempPkt = TempPkt->Next;
|
|
while (TempPkt != NULL) {
|
|
TempSize += TempPkt->TotalSize;
|
|
TempPkt = TempPkt->Next;
|
|
}
|
|
|
|
ASSERT(SrcOffset <= TempSize);
|
|
ASSERT((SrcOffset + Size) <= TempSize);
|
|
#endif
|
|
|
|
//
|
|
// First, skip SrcOffset bytes into the source packet chain.
|
|
//
|
|
if ((SrcOffset == SrcPkt->Position) && (Size <= SrcPkt->ContigSize)) {
|
|
//
|
|
// One common case is that we want to start from the current Position.
|
|
// REVIEW: This case common enough to be worth this check?
|
|
//
|
|
SrcContig = SrcPkt->ContigSize;
|
|
SrcData = SrcPkt->Data;
|
|
SrcBuf = NULL;
|
|
PacketSize = SrcPkt->TotalSize;
|
|
} else {
|
|
//
|
|
// Otherwise step through packets and buffer regions until
|
|
// we find the desired spot.
|
|
//
|
|
PacketSize = SrcPkt->Position + SrcPkt->TotalSize;
|
|
while (SrcOffset >= PacketSize) {
|
|
// Skip a whole packet.
|
|
SrcOffset -= PacketSize;
|
|
SrcPkt = SrcPkt->Next;
|
|
ASSERT(SrcPkt != NULL);
|
|
PacketSize = SrcPkt->Position + SrcPkt->TotalSize;
|
|
}
|
|
//
|
|
// Found the right packet in the chain, now find desired buffer.
|
|
//
|
|
PacketSize -= SrcOffset;
|
|
if (SrcPkt->NdisPacket == NULL) {
|
|
//
|
|
// This packet must be just a single contiguous region.
|
|
// Finding the right spot is a simple matter of arithmetic.
|
|
//
|
|
SrcContig = PacketSize;
|
|
SrcData = (uchar *)SrcPkt->FlatData + SrcOffset;
|
|
SrcBuf = NULL;
|
|
} else {
|
|
uchar *BufAddr;
|
|
uint BufLen;
|
|
|
|
//
|
|
// There may be multiple buffers comprising this packet.
|
|
// Step through them until we arrive at the right spot.
|
|
//
|
|
SrcBuf = NdisFirstBuffer(SrcPkt->NdisPacket);
|
|
NdisQueryBuffer(SrcBuf, &BufAddr, &BufLen);
|
|
while (SrcOffset >= BufLen) {
|
|
// Skip to the next buffer.
|
|
SrcOffset -= BufLen;
|
|
NdisGetNextBuffer(SrcBuf, &SrcBuf);
|
|
ASSERT(SrcBuf != NULL);
|
|
NdisQueryBuffer(SrcBuf, &BufAddr, &BufLen);
|
|
}
|
|
SrcContig = BufLen - SrcOffset;
|
|
SrcData = BufAddr + BufLen - SrcContig;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We're now at the point where we wish to start copying.
|
|
//
|
|
while (Size != 0) {
|
|
uint BytesToCopy;
|
|
|
|
BytesToCopy = MIN(Size, SrcContig);
|
|
if (DestPtr != NULL) {
|
|
RtlCopyMemory(DestPtr + DestOffset, (uchar *)SrcData, BytesToCopy);
|
|
} else {
|
|
TdiCopyBufferToMdlWithReservedMapping(SrcData, DestBuf, DestOffset,
|
|
BytesToCopy);
|
|
}
|
|
|
|
if (BytesToCopy < Size) {
|
|
//
|
|
// Not done yet, we ran out of either source packet or buffer.
|
|
// Get next one and fix up pointers/sizes for the next copy.
|
|
//
|
|
DestOffset += BytesToCopy;
|
|
PacketSize -= BytesToCopy;
|
|
if (PacketSize == 0) {
|
|
// Get next packet on chain.
|
|
SrcPkt = SrcPkt->Next;
|
|
ASSERT(SrcPkt != NULL);
|
|
PacketSize = SrcPkt->Position + SrcPkt->TotalSize;
|
|
if (SrcPkt->NdisPacket == NULL) {
|
|
// Single contiguous region.
|
|
SrcData = (uchar *)SrcPkt->FlatData + SrcPkt->Position;
|
|
SrcContig = SrcPkt->TotalSize;
|
|
} else {
|
|
// Potentially multiple buffers.
|
|
SrcBuf = NdisFirstBuffer(SrcPkt->NdisPacket);
|
|
NdisQueryBuffer(SrcBuf, &SrcData, &SrcContig);
|
|
}
|
|
} else {
|
|
// Get next buffer in packet.
|
|
ASSERT(SrcBuf != NULL);
|
|
NdisGetNextBuffer(SrcBuf, &SrcBuf);
|
|
ASSERT(SrcBuf != NULL);
|
|
NdisQueryBuffer(SrcBuf, &SrcData, &SrcContig);
|
|
}
|
|
}
|
|
Size -= BytesToCopy;
|
|
}
|
|
}
|
|
|
|
//* CopyToNdisSafe - Copy a flat buffer to an NDIS_BUFFER chain.
|
|
//
|
|
// A utility function to copy a flat buffer to an NDIS buffer chain. We
|
|
// assume that the NDIS_BUFFER chain is big enough to hold the copy amount;
|
|
// in a debug build we'll assert if this isn't true. We return a pointer
|
|
// to the buffer where we stopped copying, and an offset into that buffer.
|
|
// This is useful for copying in pieces into the chain.
|
|
//
|
|
// Input: DestBuf - Destination NDIS_BUFFER chain.
|
|
// pNextBuf - Pointer to next buffer in chain to copy into.
|
|
// SrcBuf - Src flat buffer.
|
|
// Size - Size in bytes to copy.
|
|
// StartOffset - Pointer to start of offset into first buffer in
|
|
// chain. Filled in on return with the offset to
|
|
// copy into next.
|
|
//
|
|
// Returns: TRUE - Successfully copied flat buffer into NDIS_BUFFER chain.
|
|
// FALSE - Failed to copy entire flat buffer.
|
|
//
|
|
int
|
|
CopyToNdisSafe(PNDIS_BUFFER DestBuf, PNDIS_BUFFER * ppNextBuf,
|
|
uchar * SrcBuf, uint Size, uint * StartOffset)
|
|
{
|
|
uint CopySize;
|
|
uchar *DestPtr;
|
|
uint DestSize;
|
|
uint Offset = *StartOffset;
|
|
uchar *VirtualAddress;
|
|
uint Length;
|
|
|
|
ASSERT(DestBuf != NULL);
|
|
ASSERT(SrcBuf != NULL);
|
|
|
|
NdisQueryBufferSafe(DestBuf, &VirtualAddress, &Length,
|
|
LowPagePriority);
|
|
if (VirtualAddress == NULL)
|
|
return FALSE;
|
|
|
|
ASSERT(Length >= Offset);
|
|
DestPtr = VirtualAddress + Offset;
|
|
DestSize = Length - Offset;
|
|
|
|
for (;;) {
|
|
CopySize = MIN(Size, DestSize);
|
|
RtlCopyMemory(DestPtr, SrcBuf, CopySize);
|
|
|
|
DestPtr += CopySize;
|
|
SrcBuf += CopySize;
|
|
|
|
if ((Size -= CopySize) == 0)
|
|
break;
|
|
|
|
if ((DestSize -= CopySize) == 0) {
|
|
DestBuf = NDIS_BUFFER_LINKAGE(DestBuf);
|
|
ASSERT(DestBuf != NULL);
|
|
|
|
NdisQueryBufferSafe(DestBuf, &VirtualAddress, &Length,
|
|
LowPagePriority);
|
|
if (VirtualAddress == NULL)
|
|
return FALSE;
|
|
|
|
DestPtr = VirtualAddress;
|
|
DestSize = Length;
|
|
}
|
|
}
|
|
|
|
*StartOffset = (uint) (DestPtr - VirtualAddress);
|
|
|
|
if (ppNextBuf)
|
|
*ppNextBuf = DestBuf;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//* CopyFlatToNdis - Copy a flat buffer to an NDIS_BUFFER chain.
|
|
//
|
|
// A utility function to copy a flat buffer to an NDIS buffer chain. We
|
|
// assume that the NDIS_BUFFER chain is big enough to hold the copy amount;
|
|
// in a debug build we'll debugcheck if this isn't true. We return a pointer
|
|
// to the buffer where we stopped copying, and an offset into that buffer.
|
|
// This is useful for copying in pieces into the chain.
|
|
//
|
|
PNDIS_BUFFER // Returns: Pointer to next buffer in chain to copy into.
|
|
CopyFlatToNdis(
|
|
PNDIS_BUFFER DestBuf, // Destination NDIS buffer chain.
|
|
uchar *SrcBuf, // Source buffer (unstructured memory).
|
|
uint Size, // Size in bytes to copy.
|
|
uint *StartOffset, // Pointer to Offset info first buffer in chain.
|
|
// Filled on return with offset to copy into next.
|
|
uint *BytesCopied) // Location into which to return # of bytes copied.
|
|
{
|
|
NTSTATUS Status = 0;
|
|
|
|
*BytesCopied = 0;
|
|
|
|
Status = TdiCopyBufferToMdl(SrcBuf, 0, Size, DestBuf, *StartOffset,
|
|
(PULONG)BytesCopied);
|
|
|
|
*StartOffset += *BytesCopied;
|
|
|
|
//
|
|
// Always return the first buffer, since the TdiCopy function handles
|
|
// finding the appropriate buffer based on offset.
|
|
//
|
|
return(DestBuf);
|
|
}
|
|
|
|
|
|
//* CopyNdisToFlat - Copy an NDIS_BUFFER chain to a flat buffer.
|
|
//
|
|
// Copy (a portion of) an NDIS buffer chain to a flat buffer.
|
|
//
|
|
// Returns TRUE if the copy succeeded and FALSE if it failed
|
|
// because an NDIS buffer could not be mapped. If the copy succeeded,
|
|
// returns the Next buffer/offset, for subsequent calls.
|
|
//
|
|
int
|
|
CopyNdisToFlat(
|
|
void *DstData,
|
|
PNDIS_BUFFER SrcBuffer,
|
|
uint SrcOffset,
|
|
uint Length,
|
|
PNDIS_BUFFER *NextBuffer,
|
|
uint *NextOffset)
|
|
{
|
|
void *SrcData;
|
|
uint SrcSize;
|
|
uint Bytes;
|
|
|
|
for (;;) {
|
|
NdisQueryBufferSafe(SrcBuffer, &SrcData, &SrcSize, LowPagePriority);
|
|
if (SrcSize < SrcOffset) {
|
|
SrcOffset -= SrcSize;
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
continue;
|
|
}
|
|
|
|
if (SrcData == NULL)
|
|
return FALSE;
|
|
|
|
Bytes = SrcSize - SrcOffset;
|
|
if (Bytes > Length)
|
|
Bytes = Length;
|
|
|
|
RtlCopyMemory(DstData, (uchar *)SrcData + SrcOffset, Bytes);
|
|
|
|
(uchar *)DstData += Bytes;
|
|
SrcOffset += Bytes;
|
|
Length -= Bytes;
|
|
|
|
if (Length == 0)
|
|
break;
|
|
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
SrcOffset = 0;
|
|
}
|
|
|
|
*NextBuffer = SrcBuffer;
|
|
*NextOffset = SrcOffset;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Checksum support.
|
|
// On NT, there are architecture-specific assembly routines for the core
|
|
// calculation.
|
|
//
|
|
|
|
//* ChecksumPacket - Calculate the Internet checksum of a packet.
|
|
//
|
|
// Calculates the checksum of packet data. The data may be supplied
|
|
// either with the Packet/Offset arguments, or (if Packet is NULL)
|
|
// the Data/Offset arguments. In either case, Length specifies how much
|
|
// data to checksum.
|
|
//
|
|
// The Packet is assumed to contain (at least) Offset + Length bytes.
|
|
//
|
|
// Also calculates and adds-in the pseudo-header checksum,
|
|
// using Source, Dest, Length, and NextHeader.
|
|
//
|
|
// Returns 0 for failure, when an NDIS buffer can not be mapped
|
|
// into kernel address space due to resource shortages.
|
|
//
|
|
ushort
|
|
ChecksumPacket(
|
|
PNDIS_PACKET Packet, // Packet with data to checksum.
|
|
uint Offset, // Offset into packet where data starts.
|
|
uchar *Data, // If Packet is NULL, data to checksum.
|
|
uint Length, // Length of packet data.
|
|
const IPv6Addr *Source, // Source address.
|
|
const IPv6Addr *Dest, // Destination address.
|
|
uchar NextHeader) // Protocol type for pseudo-header.
|
|
{
|
|
PNDIS_BUFFER Buffer = NULL;
|
|
uint Checksum;
|
|
uint PayloadLength;
|
|
uint Size;
|
|
uint TotalSummed;
|
|
|
|
//
|
|
// Start with the pseudo-header.
|
|
//
|
|
Checksum = Cksum(Source, sizeof *Source) + Cksum(Dest, sizeof *Dest);
|
|
PayloadLength = net_long(Length);
|
|
Checksum += (PayloadLength >> 16) + (PayloadLength & 0xffff);
|
|
Checksum += (NextHeader << 8);
|
|
|
|
if (Packet == NULL) {
|
|
//
|
|
// We do not have to initialize Buffer.
|
|
// The checksum loop below will exit before trying to use it.
|
|
//
|
|
Size = Length;
|
|
Data += Offset;
|
|
} else {
|
|
//
|
|
// Skip over Offset bytes in the packet.
|
|
//
|
|
|
|
Buffer = NdisFirstBuffer(Packet);
|
|
for (;;) {
|
|
Size = NdisBufferLength(Buffer);
|
|
|
|
//
|
|
// There is a boundary case here: the Packet contains
|
|
// exactly Offset bytes total, and Length is zero.
|
|
// Checking Offset <= Size instead of Offset < Size
|
|
// makes this work.
|
|
//
|
|
if (Offset <= Size) {
|
|
Data = NdisBufferVirtualAddressSafe(Buffer, LowPagePriority);
|
|
if (Data == NULL)
|
|
return 0;
|
|
|
|
Data += Offset;
|
|
Size -= Offset;
|
|
break;
|
|
}
|
|
|
|
Offset -= Size;
|
|
NdisGetNextBuffer(Buffer, &Buffer);
|
|
ASSERT(Buffer != NULL); // Caller ensures this.
|
|
}
|
|
}
|
|
for (TotalSummed = 0;;) {
|
|
ushort Temp;
|
|
|
|
//
|
|
// Size might be bigger than we need,
|
|
// if there is "extra" data in the packet.
|
|
//
|
|
if (Size > Length)
|
|
Size = Length;
|
|
|
|
Temp = Cksum(Data, Size);
|
|
if (TotalSummed & 1) {
|
|
// We're at an odd offset into the logical buffer,
|
|
// so we need to swap the bytes that Cksum returns.
|
|
Checksum += (Temp >> 8) + ((Temp & 0xff) << 8);
|
|
} else {
|
|
Checksum += Temp;
|
|
}
|
|
TotalSummed += Size;
|
|
|
|
Length -= Size;
|
|
if (Length == 0)
|
|
break;
|
|
// Buffer is always initialized if we reach here.
|
|
NdisGetNextBuffer(Buffer, &Buffer);
|
|
NdisQueryBufferSafe(Buffer, &Data, &Size, LowPagePriority);
|
|
if (Data == NULL)
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Wrap in the carries to reduce Checksum to 16 bits.
|
|
// (Twice is sufficient because it can only overflow once.)
|
|
//
|
|
Checksum = (Checksum >> 16) + (Checksum & 0xffff);
|
|
Checksum += (Checksum >> 16);
|
|
|
|
//
|
|
// Take ones-complement and replace 0 with 0xffff.
|
|
//
|
|
Checksum = (ushort) ~Checksum;
|
|
if (Checksum == 0)
|
|
Checksum = 0xffff;
|
|
|
|
return (ushort) Checksum;
|
|
}
|
|
|
|
//* ConvertSecondsToTicks
|
|
//
|
|
// Convert seconds to timer ticks.
|
|
// A value of INFINITE_LIFETIME (0xffffffff) indicates infinity,
|
|
// for both ticks and seconds.
|
|
//
|
|
uint
|
|
ConvertSecondsToTicks(uint Seconds)
|
|
{
|
|
uint Ticks;
|
|
|
|
Ticks = Seconds * IPv6_TICKS_SECOND;
|
|
if (Ticks / IPv6_TICKS_SECOND != Seconds)
|
|
Ticks = INFINITE_LIFETIME; // Overflow.
|
|
|
|
return Ticks;
|
|
}
|
|
|
|
//* ConvertTicksToSeconds
|
|
//
|
|
// Convert timer ticks to seconds.
|
|
// A value of INFINITE_LIFETIME (0xffffffff) indicates infinity,
|
|
// for both ticks and seconds.
|
|
//
|
|
uint
|
|
ConvertTicksToSeconds(uint Ticks)
|
|
{
|
|
uint Seconds;
|
|
|
|
if (Ticks == INFINITE_LIFETIME)
|
|
Seconds = INFINITE_LIFETIME;
|
|
else
|
|
Seconds = Ticks / IPv6_TICKS_SECOND;
|
|
|
|
return Seconds;
|
|
}
|
|
|
|
//* ConvertMillisToTicks
|
|
//
|
|
// Convert milliseconds to timer ticks.
|
|
//
|
|
uint
|
|
ConvertMillisToTicks(uint Millis)
|
|
{
|
|
uint Ticks;
|
|
|
|
//
|
|
// Use 64-bit arithmetic to guard against intermediate overlow.
|
|
//
|
|
Ticks = (uint) (((unsigned __int64) Millis * IPv6_TICKS_SECOND) / 1000);
|
|
|
|
//
|
|
// If the number of millis is non-zero,
|
|
// then have at least one tick.
|
|
//
|
|
if (Ticks == 0 && Millis != 0)
|
|
Ticks = 1;
|
|
|
|
return Ticks;
|
|
}
|
|
|
|
//* IPv6Timeout - Perform various housekeeping duties periodically.
|
|
//
|
|
// Neighbor discovery, fragment reassembly, ICMP ping, etc. all have
|
|
// time-dependent parts. Check for timer expiration here.
|
|
//
|
|
void
|
|
IPv6Timeout(
|
|
PKDPC MyDpcObject, // The DPC object describing this routine.
|
|
void *Context, // The argument we asked to be called with.
|
|
void *Unused1,
|
|
void *Unused2)
|
|
{
|
|
UNREFERENCED_PARAMETER(MyDpcObject);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(Unused1);
|
|
UNREFERENCED_PARAMETER(Unused2);
|
|
|
|
//
|
|
// Atomically increment our tick count.
|
|
//
|
|
InterlockedIncrement((LONG *)&IPv6TickCount);
|
|
|
|
//
|
|
// Process all multicast groups with timers running. Timers are used to
|
|
// response to membership queries sent to us by the first-hop router.
|
|
//
|
|
// We call MLDTimeout *before* InterfaceTimeout and NetTableTimeout.
|
|
// so that when an interface is first created and a link-local address
|
|
// is assigned, the initial MLD Report for the solicited-node multicast
|
|
// address gets sent *before* the Neighbor Solicit for DAD.
|
|
// Similarly, we join the all-routers link-local multicast group
|
|
// before sending our first RA from an advertising interface.
|
|
//
|
|
if (QueryList != NULL)
|
|
MLDTimeout();
|
|
|
|
//
|
|
// Handle per-interface timeouts.
|
|
//
|
|
InterfaceTimeout();
|
|
|
|
//
|
|
// Handle per-NTE timeouts.
|
|
//
|
|
NetTableTimeout();
|
|
|
|
//
|
|
// Handle routing table timeouts.
|
|
//
|
|
RouteTableTimeout();
|
|
|
|
//
|
|
// If there's a possibility we have one or more outstanding echo requests,
|
|
// call out to ICMPv6 to handle them. Note that since we don't grab the
|
|
// lock here, there may be none by the time we get there. This just saves
|
|
// us from always having to call out.
|
|
//
|
|
if (ICMPv6OutstandingEchos != NULL) {
|
|
//
|
|
// Echo requests outstanding.
|
|
//
|
|
ICMPv6EchoTimeout();
|
|
}
|
|
|
|
//
|
|
// If we might have active reassembly records,
|
|
// call out to handle timeout processing for them.
|
|
//
|
|
if (ReassemblyList.First != SentinelReassembly) {
|
|
ReassemblyTimeout();
|
|
}
|
|
|
|
//
|
|
// Check for expired binding cache entries.
|
|
//
|
|
if (BindingCache.First != SentinelBCE)
|
|
BindingCacheTimeout();
|
|
|
|
//
|
|
// Check for expired site prefixes.
|
|
//
|
|
if (SitePrefixTable != NULL)
|
|
SitePrefixTimeout();
|
|
}
|
|
|
|
//* AdjustPacketBuffer
|
|
//
|
|
// Takes an NDIS Packet that has some spare bytes available
|
|
// at the beginning and adjusts the size of that available space.
|
|
//
|
|
// When we allocate packets, we often do not know a priori on which
|
|
// link the packets will go out. However it is much more efficient
|
|
// to allocate space for the link-level header along with the rest
|
|
// of the packet. Hence we leave space for the maximum link-level header,
|
|
// and each individual link layer uses AdjustPacketBuffer to shrink
|
|
// that space to the size that it really needs.
|
|
//
|
|
// AdjustPacketBuffer is needed because the sending calls (in both
|
|
// the NDIS and TDI interfaces) do not allow the caller to specify
|
|
// an offset of data to skip over.
|
|
//
|
|
// Note that this code is NT-specific, because it knows about the
|
|
// internal fields of NDIS_BUFFER structures.
|
|
//
|
|
void *
|
|
AdjustPacketBuffer(
|
|
PNDIS_PACKET Packet, // Packet to adjust.
|
|
uint SpaceAvailable, // Extra space available at start of first buffer.
|
|
uint SpaceNeeded) // Amount of space we need for the header.
|
|
{
|
|
PMDL Buffer;
|
|
uint Adjust;
|
|
|
|
// Get first buffer on packet chain.
|
|
NdisQueryPacket(Packet, NULL, NULL, &Buffer, NULL);
|
|
|
|
//
|
|
// The remaining space in the packet should all be in the first buffer.
|
|
//
|
|
ASSERT(SpaceAvailable <= Buffer->ByteCount);
|
|
|
|
Adjust = SpaceAvailable - SpaceNeeded;
|
|
if (Adjust == 0) {
|
|
//
|
|
// There is exactly the right amount of space left.
|
|
// This is the common case.
|
|
//
|
|
} else if ((int)Adjust > 0) {
|
|
//
|
|
// There is too much space left.
|
|
// Because NdisSend doesn't have an Offset argument,
|
|
// we need to temporarily "shrink" the buffer.
|
|
//
|
|
(uchar *)Buffer->MappedSystemVa += Adjust;
|
|
Buffer->ByteCount -= Adjust;
|
|
Buffer->ByteOffset += Adjust;
|
|
|
|
if (Buffer->ByteOffset >= PAGE_SIZE) {
|
|
PFN_NUMBER FirstPage;
|
|
|
|
//
|
|
// Need to "remove" the first physical page
|
|
// by shifting the array up one page.
|
|
// Save it at the end of the array.
|
|
//
|
|
FirstPage = ((PPFN_NUMBER)(Buffer + 1))[0];
|
|
RtlMoveMemory(&((PPFN_NUMBER)(Buffer + 1))[0],
|
|
&((PPFN_NUMBER)(Buffer + 1))[1],
|
|
Buffer->Size - sizeof *Buffer - sizeof(PFN_NUMBER));
|
|
((PPFN_NUMBER)((uchar *)Buffer + Buffer->Size))[-1] = FirstPage;
|
|
|
|
(uchar *)Buffer->StartVa += PAGE_SIZE;
|
|
Buffer->ByteOffset -= PAGE_SIZE;
|
|
}
|
|
} else { // Adjust < 0
|
|
//
|
|
// Not enough space.
|
|
// Shouldn't happen in the normal send path.
|
|
// REVIEW: This is a potential problem when forwarding packets
|
|
// from an interface with a short link-level header
|
|
// to an interface with a longer link-level header.
|
|
// Should the forwarding code take care of this?
|
|
//
|
|
ABORTMSG("AdjustPacketBuffer: Adjust < 0");
|
|
}
|
|
|
|
//
|
|
// Save away the adjustment for the completion callback,
|
|
// which needs to undo our work with UndoAdjustPacketBuffer.
|
|
//
|
|
PC(Packet)->pc_adjust = Adjust;
|
|
|
|
//
|
|
// Return a pointer to the buffer.
|
|
//
|
|
return Buffer->MappedSystemVa;
|
|
}
|
|
|
|
//* UndoAdjustPacketBuffer
|
|
//
|
|
// Undo the effects of AdjustPacketBuffer.
|
|
//
|
|
// Note that this code is NT-specific, because it knows about the
|
|
// internal fields of NDIS_BUFFER structures.
|
|
//
|
|
void
|
|
UndoAdjustPacketBuffer(
|
|
PNDIS_PACKET Packet) // Packet we may or may not have previously adjusted.
|
|
{
|
|
uint Adjust;
|
|
|
|
Adjust = PC(Packet)->pc_adjust;
|
|
if (Adjust != 0) {
|
|
PMDL Buffer;
|
|
|
|
//
|
|
// We need to undo the adjustment made in AdjustPacketBuffer.
|
|
// This may including shifting the array of page info.
|
|
//
|
|
|
|
// Get first buffer on packet chain.
|
|
NdisQueryPacket(Packet, NULL, NULL, &Buffer, NULL);
|
|
|
|
if (Buffer->ByteOffset < Adjust) {
|
|
PFN_NUMBER FirstPage;
|
|
|
|
(uchar *)Buffer->StartVa -= PAGE_SIZE;
|
|
Buffer->ByteOffset += PAGE_SIZE;
|
|
|
|
FirstPage = ((PPFN_NUMBER)((uchar *)Buffer + Buffer->Size))[-1];
|
|
RtlMoveMemory(&((PPFN_NUMBER)(Buffer + 1))[1],
|
|
&((PPFN_NUMBER)(Buffer + 1))[0],
|
|
Buffer->Size - sizeof *Buffer - sizeof(PFN_NUMBER));
|
|
((PPFN_NUMBER)(Buffer + 1))[0] = FirstPage;
|
|
}
|
|
|
|
(uchar *)Buffer->MappedSystemVa -= Adjust;
|
|
Buffer->ByteCount += Adjust;
|
|
Buffer->ByteOffset -= Adjust;
|
|
}
|
|
}
|
|
|
|
//* CreateSolicitedNodeMulticastAddress
|
|
//
|
|
// Given a unicast or anycast address, creates the corresponding
|
|
// solicited-node multicast address.
|
|
//
|
|
void
|
|
CreateSolicitedNodeMulticastAddress(
|
|
const IPv6Addr *Addr,
|
|
IPv6Addr *MCastAddr)
|
|
{
|
|
RtlZeroMemory(MCastAddr, sizeof *MCastAddr);
|
|
MCastAddr->s6_bytes[0] = 0xff;
|
|
MCastAddr->s6_bytes[1] = ADE_LINK_LOCAL;
|
|
MCastAddr->s6_bytes[11] = 0x01;
|
|
MCastAddr->s6_bytes[12] = 0xff;
|
|
MCastAddr->s6_bytes[13] = Addr->s6_bytes[13];
|
|
MCastAddr->s6_bytes[14] = Addr->s6_bytes[14];
|
|
MCastAddr->s6_bytes[15] = Addr->s6_bytes[15];
|
|
}
|
|
|
|
//* IP6_ADDR_LTEQ
|
|
//
|
|
// Is the first address <= the second address,
|
|
// in a lexicographic ordering?
|
|
//
|
|
int
|
|
IP6_ADDR_LTEQ(const IPv6Addr *A, const IPv6Addr *B)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
if (A->s6_bytes[i] < B->s6_bytes[i])
|
|
return TRUE;
|
|
else if (A->s6_bytes[i] > B->s6_bytes[i])
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE; // They are equal.
|
|
}
|
|
|
|
//* IsV4Compatible
|
|
//
|
|
// Is this a v4-compatible address?
|
|
//
|
|
// Note that the upper 8 bits of an IPv4 address are not allowed
|
|
// to be zero. If all 104 upper bits of an IPv6 address are zero,
|
|
// it's potentially a valid native IPv6 address (e.g. loopback),
|
|
// NOT a v4-compatible address.
|
|
//
|
|
int
|
|
IsV4Compatible(const IPv6Addr *Addr)
|
|
{
|
|
return ((Addr->s6_words[0] == 0) &&
|
|
(Addr->s6_words[1] == 0) &&
|
|
(Addr->s6_words[2] == 0) &&
|
|
(Addr->s6_words[3] == 0) &&
|
|
(Addr->s6_words[4] == 0) &&
|
|
(Addr->s6_words[5] == 0) &&
|
|
(Addr->s6_bytes[12] != 0));
|
|
}
|
|
|
|
//* CreateV4Compatible
|
|
//
|
|
// Create a v4-compatible address.
|
|
//
|
|
void
|
|
CreateV4Compatible(IPv6Addr *Addr, IPAddr V4Addr)
|
|
{
|
|
Addr->s6_words[0] = 0;
|
|
Addr->s6_words[1] = 0;
|
|
Addr->s6_words[2] = 0;
|
|
Addr->s6_words[3] = 0;
|
|
Addr->s6_words[4] = 0;
|
|
Addr->s6_words[5] = 0;
|
|
* (IPAddr UNALIGNED *) &Addr->s6_words[6] = V4Addr;
|
|
}
|
|
|
|
//* IsV4Mapped
|
|
//
|
|
// Is this a v4-mapped address?
|
|
//
|
|
int
|
|
IsV4Mapped(const IPv6Addr *Addr)
|
|
{
|
|
return ((Addr->s6_words[0] == 0) &&
|
|
(Addr->s6_words[1] == 0) &&
|
|
(Addr->s6_words[2] == 0) &&
|
|
(Addr->s6_words[3] == 0) &&
|
|
(Addr->s6_words[4] == 0) &&
|
|
(Addr->s6_words[5] == 0xffff));
|
|
}
|
|
|
|
//* CreateV4Mapped
|
|
//
|
|
// Create a v4-mapped address.
|
|
//
|
|
void
|
|
CreateV4Mapped(IPv6Addr *Addr, IPAddr V4Addr)
|
|
{
|
|
Addr->s6_words[0] = 0;
|
|
Addr->s6_words[1] = 0;
|
|
Addr->s6_words[2] = 0;
|
|
Addr->s6_words[3] = 0;
|
|
Addr->s6_words[4] = 0;
|
|
Addr->s6_words[5] = 0xffff;
|
|
* (IPAddr UNALIGNED *) &Addr->s6_words[6] = V4Addr;
|
|
}
|
|
|
|
//* IsSolicitedNodeMulticast
|
|
//
|
|
// Is this a solicited-node multicast address?
|
|
// Checks very strictly for the proper format.
|
|
// For example scope values smaller than 2 are not allowed.
|
|
//
|
|
int
|
|
IsSolicitedNodeMulticast(const IPv6Addr *Addr)
|
|
{
|
|
return ((Addr->s6_bytes[0] == 0xff) &&
|
|
(Addr->s6_bytes[1] == ADE_LINK_LOCAL) &&
|
|
(Addr->s6_words[1] == 0) &&
|
|
(Addr->s6_words[2] == 0) &&
|
|
(Addr->s6_words[3] == 0) &&
|
|
(Addr->s6_words[4] == 0) &&
|
|
(Addr->s6_bytes[10] == 0) &&
|
|
(Addr->s6_bytes[11] == 0x01) &&
|
|
(Addr->s6_bytes[12] == 0xff));
|
|
}
|
|
|
|
//* IsEUI64Address
|
|
//
|
|
// Does the address have a format prefix
|
|
// that indicates it uses EUI-64 interface identifiers?
|
|
//
|
|
int
|
|
IsEUI64Address(const IPv6Addr *Addr)
|
|
{
|
|
//
|
|
// Format prefixes 001 through 111, except for multicast.
|
|
//
|
|
return (((Addr->s6_bytes[0] & 0xe0) != 0) &&
|
|
!IsMulticast(Addr));
|
|
}
|
|
|
|
//* IsSubnetRouterAnycast
|
|
//
|
|
// Is this the subnet router anycast address?
|
|
// See RFC 2373.
|
|
//
|
|
int
|
|
IsSubnetRouterAnycast(const IPv6Addr *Addr)
|
|
{
|
|
return (IsEUI64Address(Addr) &&
|
|
(Addr->s6_words[4] == 0) &&
|
|
(Addr->s6_words[5] == 0) &&
|
|
(Addr->s6_words[6] == 0) &&
|
|
(Addr->s6_words[7] == 0));
|
|
}
|
|
|
|
//* IsSubnetReservedAnycast
|
|
//
|
|
// Is this a subnet reserved anycast address?
|
|
// See RFC 2526. It talks about non-EUI-64
|
|
// addresses as well, but IMHO that part
|
|
// of the RFC doesn't make sense. For example,
|
|
// it shouldn't apply to multicast or v4-compatible
|
|
// addresses.
|
|
//
|
|
int
|
|
IsSubnetReservedAnycast(const IPv6Addr *Addr)
|
|
{
|
|
return (IsEUI64Address(Addr) &&
|
|
(Addr->s6_words[4] == 0xfffd) &&
|
|
(Addr->s6_words[5] == 0xffff) &&
|
|
(Addr->s6_words[6] == 0xffff) &&
|
|
((Addr->s6_words[7] & 0x80ff) == 0x80ff));
|
|
}
|
|
|
|
//* IsKnownAnycast
|
|
//
|
|
// As best we can tell from simple inspection,
|
|
// is this an anycast address?
|
|
//
|
|
int
|
|
IsKnownAnycast(const IPv6Addr *Addr)
|
|
{
|
|
return IsSubnetRouterAnycast(Addr) || IsSubnetReservedAnycast(Addr);
|
|
}
|
|
|
|
//* IsInvalidSourceAddress
|
|
//
|
|
// Is this address illegal to use as a source address?
|
|
// We currently flag IPv6 multicast, and embedded IPv4 multicast,
|
|
// broadcast, loopback and unspecified as invalid.
|
|
//
|
|
// Note that this function doesn't attempt to identify anycast addresses
|
|
// in order to flag them as invalid. Whether or not to allow them to
|
|
// be valid source addresses has been a matter of some debate in the
|
|
// working group. We let them pass since we can't tell them all by
|
|
// inspection and we don't see any real problems with accepting them.
|
|
//
|
|
int
|
|
IsInvalidSourceAddress(const IPv6Addr *Addr)
|
|
{
|
|
IPAddr V4Addr;
|
|
|
|
if (IsMulticast(Addr))
|
|
return TRUE;
|
|
|
|
if (IsISATAP(Addr) || // ISATAP
|
|
(((Addr->s6_words[0] == 0) && (Addr->s6_words[1] == 0) &&
|
|
(Addr->s6_words[2] == 0) && (Addr->s6_words[3] == 0)) &&
|
|
((Addr->s6_words[4] == 0) && (Addr->s6_words[5] == 0) &&
|
|
((Addr->s6_words[6] & 0x00ff) != 0)) || // v4-compatible
|
|
((Addr->s6_words[4] == 0) &&
|
|
(Addr->s6_words[5] == 0xffff)) || // v4-mapped
|
|
((Addr->s6_words[4] == 0xffff) &&
|
|
(Addr->s6_words[5] == 0)))) { // v4-translated
|
|
|
|
V4Addr = ExtractV4Address(Addr);
|
|
|
|
} else if (Is6to4(Addr)) {
|
|
|
|
V4Addr = Extract6to4Address(Addr);
|
|
|
|
} else {
|
|
//
|
|
// It's not an IPv6 multicast address, nor does it contain
|
|
// an embedded IPv4 address of some sort, so don't consider
|
|
// it invalid.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check embedded IPv4 address for invalid types.
|
|
//
|
|
return (IsV4Multicast(V4Addr) || IsV4Broadcast(V4Addr) ||
|
|
IsV4Loopback(V4Addr) || IsV4Unspecified(V4Addr));
|
|
}
|
|
|
|
//* IsNotManualAddress
|
|
//
|
|
// Should this address NOT be manually assigned as an address?
|
|
//
|
|
int
|
|
IsNotManualAddress(const IPv6Addr *Addr)
|
|
{
|
|
return (IsMulticast(Addr) ||
|
|
IsUnspecified(Addr) ||
|
|
IsLoopback(Addr) ||
|
|
(IsV4Compatible(Addr) &&
|
|
(V4AddressScope(ExtractV4Address(Addr)) != ADE_GLOBAL)) ||
|
|
(Is6to4(Addr) &&
|
|
(V4AddressScope(Extract6to4Address(Addr)) != ADE_GLOBAL)));
|
|
}
|
|
|
|
//* V4AddressScope
|
|
//
|
|
// Determines the scope of an IPv4 address.
|
|
// See RFC 1918.
|
|
//
|
|
ushort
|
|
V4AddressScope(IPAddr Addr)
|
|
{
|
|
if ((Addr & 0x0000FFFF) == 0x0000FEA9) // 169.254/16 - auto-configured
|
|
return ADE_LINK_LOCAL;
|
|
else if ((Addr & 0x000000FF) == 0x0000000A) // 10/8 - private
|
|
return ADE_SITE_LOCAL;
|
|
else if ((Addr & 0x0000F0FF) == 0x000010AC) // 172.16/12 - private
|
|
return ADE_SITE_LOCAL;
|
|
else if ((Addr & 0x0000FFFF) == 0x0000A8C0) // 192.168/16 - private
|
|
return ADE_SITE_LOCAL;
|
|
else if ((Addr & 0x000000FF) == 0x0000007F) // 127/8 - loopback
|
|
return ADE_LINK_LOCAL;
|
|
else
|
|
return ADE_GLOBAL;
|
|
}
|
|
|
|
//* UnicastAddressScope
|
|
//
|
|
// Examines a unicast address and determines its scope.
|
|
//
|
|
// Note that v4-compatible and 6to4 addresses
|
|
// are deemed to have global scope. They should
|
|
// not be derived from RFC 1918 IPv4 addresses.
|
|
// But even if they are, we will treat the IPv6
|
|
// addresses as global.
|
|
//
|
|
ushort
|
|
UnicastAddressScope(const IPv6Addr *Addr)
|
|
{
|
|
if (IsLinkLocal(Addr))
|
|
return ADE_LINK_LOCAL;
|
|
else if (IsSiteLocal(Addr))
|
|
return ADE_SITE_LOCAL;
|
|
else if (IsLoopback(Addr))
|
|
return ADE_LINK_LOCAL;
|
|
else
|
|
return ADE_GLOBAL;
|
|
}
|
|
|
|
//* AddressScope
|
|
//
|
|
// Examines an address and determines its scope.
|
|
//
|
|
ushort
|
|
AddressScope(const IPv6Addr *Addr)
|
|
{
|
|
if (IsMulticast(Addr))
|
|
return MulticastAddressScope(Addr);
|
|
else
|
|
return UnicastAddressScope(Addr);
|
|
}
|
|
|
|
//* DetermineScopeId
|
|
//
|
|
// Given an address and an associated interface, determine
|
|
// the appropriate value for the scope identifier.
|
|
//
|
|
// DetermineScopeId calculates a "user-level" ScopeId,
|
|
// meaning that loopback and global addresses
|
|
// get special treatment. Therefore, DetermineScopeId
|
|
// is not appropriate for general network-layer use.
|
|
// See also RouteToDestination and FindNetworkWithAddress.
|
|
//
|
|
// Returns the ScopeId.
|
|
//
|
|
uint
|
|
DetermineScopeId(const IPv6Addr *Addr, Interface *IF)
|
|
{
|
|
ushort Scope;
|
|
|
|
if (IsLoopback(Addr) && (IF == LoopInterface))
|
|
return 0;
|
|
|
|
Scope = AddressScope(Addr);
|
|
if (Scope == ADE_GLOBAL)
|
|
return 0;
|
|
|
|
return IF->ZoneIndices[Scope];
|
|
}
|
|
|
|
//* HasPrefix - Does an address have the given prefix?
|
|
//
|
|
int
|
|
HasPrefix(const IPv6Addr *Addr, const IPv6Addr *Prefix, uint PrefixLength)
|
|
{
|
|
const uchar *AddrBytes = Addr->s6_bytes;
|
|
const uchar *PrefixBytes = Prefix->s6_bytes;
|
|
|
|
//
|
|
// Check that initial integral bytes match.
|
|
//
|
|
while (PrefixLength > 8) {
|
|
if (*AddrBytes++ != *PrefixBytes++)
|
|
return FALSE;
|
|
PrefixLength -= 8;
|
|
}
|
|
|
|
//
|
|
// Check any remaining bits.
|
|
// Note that if PrefixLength is zero now, we should not
|
|
// dereference AddrBytes/PrefixBytes.
|
|
//
|
|
if ((PrefixLength > 0) &&
|
|
((*AddrBytes >> (8 - PrefixLength)) !=
|
|
(*PrefixBytes >> (8 - PrefixLength))))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* CopyPrefix
|
|
//
|
|
// Copy an address prefix, zeroing the remaining bits
|
|
// in the destination address.
|
|
//
|
|
void
|
|
CopyPrefix(IPv6Addr *Addr, const IPv6Addr *Prefix, uint PrefixLength)
|
|
{
|
|
uint PLBytes, PLRemainderBits, Loop;
|
|
|
|
PLBytes = PrefixLength / 8;
|
|
PLRemainderBits = PrefixLength % 8;
|
|
for (Loop = 0; Loop < sizeof(IPv6Addr); Loop++) {
|
|
if (Loop < PLBytes)
|
|
Addr->s6_bytes[Loop] = Prefix->s6_bytes[Loop];
|
|
else
|
|
Addr->s6_bytes[Loop] = 0;
|
|
}
|
|
if (PLRemainderBits) {
|
|
Addr->s6_bytes[PLBytes] = (UCHAR)(Prefix->s6_bytes[PLBytes] &
|
|
(0xff << (8 - PLRemainderBits)));
|
|
}
|
|
}
|
|
|
|
//* CommonPrefixLength
|
|
//
|
|
// Calculate the length of the longest prefix common
|
|
// to the two addresses.
|
|
//
|
|
uint
|
|
CommonPrefixLength(const IPv6Addr *Addr, const IPv6Addr *Addr2)
|
|
{
|
|
int i, j;
|
|
|
|
//
|
|
// Find first non-matching byte.
|
|
//
|
|
for (i = 0; ; i++) {
|
|
if (i == sizeof(IPv6Addr))
|
|
return 8 * i;
|
|
|
|
if (Addr->s6_bytes[i] != Addr2->s6_bytes[i])
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find first non-matching bit (there must be one).
|
|
//
|
|
for (j = 0; ; j++) {
|
|
uint Mask = 1 << (7 - j);
|
|
|
|
if ((Addr->s6_bytes[i] & Mask) != (Addr2->s6_bytes[i] & Mask))
|
|
break;
|
|
}
|
|
|
|
return 8 * i + j;
|
|
}
|
|
|
|
//* IntersectPrefix
|
|
//
|
|
// Do the two prefixes overlap?
|
|
//
|
|
int
|
|
IntersectPrefix(const IPv6Addr *Prefix1, uint Prefix1Length,
|
|
const IPv6Addr *Prefix2, uint Prefix2Length)
|
|
{
|
|
return HasPrefix(Prefix1, Prefix2, MIN(Prefix1Length, Prefix2Length));
|
|
}
|
|
|
|
//* MapNdisBuffers
|
|
//
|
|
// Maps the NDIS buffer chain into the kernel address space.
|
|
// Returns FALSE upon failure.
|
|
//
|
|
int
|
|
MapNdisBuffers(NDIS_BUFFER *Buffer)
|
|
{
|
|
uchar *Data;
|
|
|
|
while (Buffer != NULL) {
|
|
Data = NdisBufferVirtualAddressSafe(Buffer, LowPagePriority);
|
|
if (Data == NULL)
|
|
return FALSE;
|
|
|
|
NdisGetNextBuffer(Buffer, &Buffer);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* GetDataFromNdis
|
|
//
|
|
// Retrieves data from an NDIS buffer chain.
|
|
// If the desired data is contiguous, then just returns
|
|
// a pointer directly to the data in the buffer chain.
|
|
// Otherwise the data is copied to the supplied buffer
|
|
// and returns a pointer to the supplied buffer.
|
|
//
|
|
// Returns NULL only if the desired data (offset/size)
|
|
// does not exist in the NDIS buffer chain of
|
|
// if DataBuffer is NULL and the data is not contiguous.
|
|
//
|
|
uchar *
|
|
GetDataFromNdis(
|
|
NDIS_BUFFER *SrcBuffer,
|
|
uint SrcOffset,
|
|
uint Length,
|
|
uchar *DataBuffer)
|
|
{
|
|
void *DstData;
|
|
void *SrcData;
|
|
uint SrcSize;
|
|
uint Bytes;
|
|
|
|
//
|
|
// Look through the buffer chain
|
|
// for the beginning of the desired data.
|
|
//
|
|
for (;;) {
|
|
if (SrcBuffer == NULL)
|
|
return NULL;
|
|
|
|
NdisQueryBuffer(SrcBuffer, &SrcData, &SrcSize);
|
|
if (SrcOffset < SrcSize)
|
|
break;
|
|
|
|
SrcOffset -= SrcSize;
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
}
|
|
|
|
//
|
|
// If the desired data is contiguous,
|
|
// then just return a pointer to it.
|
|
//
|
|
if (SrcOffset + Length <= SrcSize)
|
|
return (uchar *)SrcData + SrcOffset;
|
|
|
|
//
|
|
// If our caller did not specify a buffer,
|
|
// then we must fail.
|
|
//
|
|
if (DataBuffer == NULL)
|
|
return NULL;
|
|
|
|
//
|
|
// Copy the desired data to the caller's buffer,
|
|
// and return a pointer to the caller's buffer.
|
|
//
|
|
DstData = DataBuffer;
|
|
for (;;) {
|
|
Bytes = SrcSize - SrcOffset;
|
|
if (Bytes > Length)
|
|
Bytes = Length;
|
|
|
|
RtlCopyMemory(DstData, (uchar *)SrcData + SrcOffset, Bytes);
|
|
|
|
(uchar *)DstData += Bytes;
|
|
Length -= Bytes;
|
|
|
|
if (Length == 0)
|
|
break;
|
|
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
if (SrcBuffer == NULL)
|
|
return NULL;
|
|
NdisQueryBuffer(SrcBuffer, &SrcData, &SrcSize);
|
|
SrcOffset = 0;
|
|
}
|
|
|
|
return DataBuffer;
|
|
}
|
|
|
|
//* GetIPv6Header
|
|
//
|
|
// Returns a pointer to the IPv6 header in an NDIS Packet.
|
|
// If the header is contiguous, then just returns
|
|
// a pointer to the data directly in the packet.
|
|
// Otherwise the IPv6 header is copied to the supplied buffer,
|
|
// and a pointer to the buffer is returned.
|
|
//
|
|
// Returns NULL only if the NDIS packet is not big enough
|
|
// to contain a header, or if HdrBuffer is NULL and the header
|
|
// is not contiguous.
|
|
//
|
|
IPv6Header UNALIGNED *
|
|
GetIPv6Header(PNDIS_PACKET Packet, uint Offset, IPv6Header *HdrBuffer)
|
|
{
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
|
|
|
|
return (IPv6Header UNALIGNED *)
|
|
GetDataFromNdis(NdisBuffer, Offset, sizeof(IPv6Header),
|
|
(uchar *) HdrBuffer);
|
|
}
|
|
|
|
|
|
//* PacketPullupEx - extend contiguous, aligned data region.
|
|
//
|
|
// Pulls up more data from secondary packet buffers to create a contiguous
|
|
// buffer of at least the requested size with the requested alignment.
|
|
//
|
|
// The alignment requirement is expressed as a power-of-2 multiple
|
|
// plus an offset. For example, 4n+3 means the data address should
|
|
// be a multiple of 4 plus 3.
|
|
// NB: These two arguments are uint not UINT_PTR because we don't
|
|
// need to express very large multiples.
|
|
//
|
|
// For the moment, the alignment multiple should be one or two.
|
|
// This is because Ethernet headers are 14 bytes so in practice
|
|
// requesting 4-byte alignment would cause copying. In the future,
|
|
// NDIS should be fixed so the network-layer header is 8-byte aligned.
|
|
//
|
|
// So if the natural alignment (__builtin_alignof) of the needed type
|
|
// is one or two, then supply the natural alignment and do not
|
|
// use the UNALIGNED keyword. Otherwise, if the needed type
|
|
// contains an IPv6 address, then supply __builtin_alignof(IPv6Addr)
|
|
// (so you can use AddrAlign to access the addresses without copying)
|
|
// and use UNALIGNED. Otherwise, supply 1 and use UNALIGNED.
|
|
//
|
|
// NB: A caller can request a zero size contiguous region
|
|
// with no alignment restriction, to move to the next buffer
|
|
// after processing the current buffer. In this usage,
|
|
// PacketPullupSubr will never fail.
|
|
//
|
|
uint // Returns: new contiguous amount, or 0 if unable to satisfy request.
|
|
PacketPullupSubr(
|
|
IPv6Packet *Packet, // Packet to pullup.
|
|
uint Needed, // Minimum amount of contiguous data to return.
|
|
uint AlignMultiple, // Alignment multiple.
|
|
uint AlignOffset) // Offset from the alignment multiple.
|
|
{
|
|
PNDIS_BUFFER Buffer;
|
|
void *BufAddr;
|
|
IPv6PacketAuxiliary *Aux;
|
|
uint BufLen = 0, Offset, LeftToGo = 0;
|
|
|
|
ASSERT(AlignMultiple <= 2);
|
|
ASSERT((AlignMultiple & (AlignMultiple - 1)) == 0);
|
|
ASSERT(AlignOffset < AlignMultiple);
|
|
|
|
//
|
|
// Check if our caller requested too much data.
|
|
//
|
|
if (Needed > Packet->TotalSize)
|
|
return 0;
|
|
|
|
//
|
|
// Find our current position in the raw packet data.
|
|
// REVIEW: This is exactly PositionPacketAt except
|
|
// we want the Buffer for later use with CopyNdisToFlat.
|
|
//
|
|
if (Packet->NdisPacket == NULL) {
|
|
//
|
|
// Reset our data pointer and contiguous region counter.
|
|
//
|
|
Packet->Data = (uchar *)Packet->FlatData + Packet->Position;
|
|
Packet->ContigSize = Packet->TotalSize;
|
|
}
|
|
else {
|
|
//
|
|
// Scan the NDIS buffer chain until we have reached the buffer
|
|
// containing the current position. Note that if we entered with
|
|
// the position field pointing one off the end of a buffer (a common
|
|
// case), we'll stop at the beginning of the following buffer.
|
|
//
|
|
Buffer = NdisFirstBuffer(Packet->NdisPacket);
|
|
Offset = 0;
|
|
for (;;) {
|
|
NdisQueryBuffer(Buffer, &BufAddr, &BufLen);
|
|
Offset += BufLen;
|
|
if (Packet->Position < Offset)
|
|
break;
|
|
NdisGetNextBuffer(Buffer, &Buffer);
|
|
}
|
|
|
|
//
|
|
// Reset our data pointer and contiguous region counter to insure
|
|
// they reflect the current position in the NDIS buffer chain.
|
|
//
|
|
LeftToGo = Offset - Packet->Position;
|
|
Packet->Data = (uchar *)BufAddr + (BufLen - LeftToGo);
|
|
Packet->ContigSize = MIN(LeftToGo, Packet->TotalSize);
|
|
}
|
|
|
|
//
|
|
// The above repositioning may result in a contiguous region
|
|
// that will satisfy the request.
|
|
//
|
|
if (Needed <= Packet->ContigSize) {
|
|
if ((PtrToUint(Packet->Data) & (AlignMultiple - 1)) == AlignOffset)
|
|
return Packet->ContigSize;
|
|
}
|
|
|
|
//
|
|
// In an attempt to prevent future pullup operations,
|
|
// we actually pull up *more* than the requested amount.
|
|
// But not too much more.
|
|
//
|
|
Needed = MAX(MIN(Packet->ContigSize, MAX_EXCESS_PULLUP), Needed);
|
|
|
|
//
|
|
// Allocate and initialize an auxiliary data region.
|
|
// The data buffer follows the structure in memory,
|
|
// with AlignOffset bytes of padding between.
|
|
//
|
|
Aux = ExAllocatePool(NonPagedPool, sizeof *Aux + AlignOffset + Needed);
|
|
if (Aux == NULL)
|
|
return 0;
|
|
Aux->Next = Packet->AuxList;
|
|
Aux->Data = (uchar *)(Aux + 1) + AlignOffset;
|
|
Aux->Length = Needed;
|
|
Aux->Position = Packet->Position;
|
|
Packet->AuxList = Aux;
|
|
|
|
//
|
|
// We assume that ExAllocatePool returns aligned memory.
|
|
//
|
|
ASSERT((PtrToUint(Aux->Data) & (AlignMultiple - 1)) == AlignOffset);
|
|
|
|
//
|
|
// Copy the packet data to the auxiliary buffer.
|
|
//
|
|
if (Packet->NdisPacket == NULL) {
|
|
RtlCopyMemory(Aux->Data, Packet->Data, Needed);
|
|
}
|
|
else {
|
|
int Ok;
|
|
|
|
Offset = BufLen - LeftToGo;
|
|
Ok = CopyNdisToFlat(Aux->Data, Buffer, Offset, Needed,
|
|
&Buffer, &Offset);
|
|
ASSERT(Ok);
|
|
}
|
|
|
|
//
|
|
// Point our packet's data pointer at the auxiliary buffer.
|
|
//
|
|
Packet->Data = Aux->Data;
|
|
return Packet->ContigSize = Needed;
|
|
}
|
|
|
|
|
|
//* PacketPullupCleanup
|
|
//
|
|
// Cleanup auxiliary data regions that were created by PacketPullup.
|
|
//
|
|
void
|
|
PacketPullupCleanup(IPv6Packet *Packet)
|
|
{
|
|
while (Packet->AuxList != NULL) {
|
|
IPv6PacketAuxiliary *Aux = Packet->AuxList;
|
|
Packet->AuxList = Aux->Next;
|
|
ExFreePool(Aux);
|
|
}
|
|
}
|
|
|
|
|
|
//* AdjustPacketParams
|
|
//
|
|
// Adjust the pointers/value in the packet,
|
|
// to move past some bytes in the packet.
|
|
//
|
|
void
|
|
AdjustPacketParams(
|
|
IPv6Packet *Packet,
|
|
uint BytesToSkip)
|
|
{
|
|
ASSERT((BytesToSkip <= Packet->ContigSize) &&
|
|
(Packet->ContigSize <= Packet->TotalSize));
|
|
|
|
(uchar *)Packet->Data += BytesToSkip;
|
|
Packet->ContigSize -= BytesToSkip;
|
|
Packet->TotalSize -= BytesToSkip;
|
|
Packet->Position += BytesToSkip;
|
|
}
|
|
|
|
|
|
//* PositionPacketAt
|
|
//
|
|
// Adjust the pointers/values in the packet to reflect being at
|
|
// a specific absolute position in the packet.
|
|
//
|
|
void
|
|
PositionPacketAt(
|
|
IPv6Packet *Packet,
|
|
uint NewPosition)
|
|
{
|
|
if (Packet->NdisPacket == NULL) {
|
|
//
|
|
// This packet must be just a single contiguous region.
|
|
// Finding the right spot is a simple matter of arithmetic.
|
|
// We reset to the beginning and then adjust forward.
|
|
//
|
|
Packet->Data = Packet->FlatData;
|
|
Packet->TotalSize += Packet->Position;
|
|
Packet->ContigSize = Packet->TotalSize;
|
|
Packet->Position = 0;
|
|
AdjustPacketParams(Packet, NewPosition);
|
|
} else {
|
|
PNDIS_BUFFER Buffer;
|
|
void *BufAddr;
|
|
uint BufLen, Offset, LeftToGo;
|
|
|
|
//
|
|
// There may be multiple buffers comprising this packet.
|
|
// Step through them until we arrive at the right spot.
|
|
//
|
|
Buffer = NdisFirstBuffer(Packet->NdisPacket);
|
|
Offset = 0;
|
|
for (;;) {
|
|
NdisQueryBuffer(Buffer, &BufAddr, &BufLen);
|
|
Offset += BufLen;
|
|
if (NewPosition < Offset)
|
|
break;
|
|
NdisGetNextBuffer(Buffer, &Buffer);
|
|
}
|
|
LeftToGo = Offset - NewPosition;
|
|
Packet->Data = (uchar *)BufAddr + (BufLen - LeftToGo);
|
|
Packet->TotalSize += Packet->Position - NewPosition;
|
|
Packet->ContigSize = MIN(LeftToGo, Packet->TotalSize);
|
|
Packet->Position = NewPosition;
|
|
}
|
|
}
|
|
|
|
|
|
//* GetPacketPositionFromPointer
|
|
//
|
|
// Determines the Packet 'Position' (offset from start of packet)
|
|
// corresponding to a given data pointer.
|
|
//
|
|
// This isn't very efficient in some cases, so use sparingly.
|
|
//
|
|
uint
|
|
GetPacketPositionFromPointer(
|
|
IPv6Packet *Packet, // Packet containing pointer we're converting.
|
|
uchar *Pointer) // Pointer to convert to a position.
|
|
{
|
|
PNDIS_BUFFER Buffer;
|
|
uchar *BufAddr;
|
|
uint BufLen, Position;
|
|
IPv6PacketAuxiliary *Aux;
|
|
|
|
//
|
|
// If the IPv6Packet has no associated NDIS_PACKET, then we check
|
|
// the flat data region. The packet may still have auxiliary buffers
|
|
// from PacketPullup.
|
|
//
|
|
if (Packet->NdisPacket == NULL) {
|
|
if (((uchar *)Packet->FlatData <= Pointer) &&
|
|
(Pointer < ((uchar *)Packet->FlatData +
|
|
Packet->Position + Packet->TotalSize))) {
|
|
//
|
|
// Our pointer's position is just the difference between it
|
|
// and the start of the flat data region.
|
|
//
|
|
return (uint)(Pointer - (uchar *)Packet->FlatData);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The next place to look is the chain of auxiliary buffers
|
|
// allocated by PacketPullup. This must succeed if there
|
|
// is no associated NDIS_PACKET.
|
|
//
|
|
for (Aux = Packet->AuxList; Aux != NULL; Aux = Aux->Next) {
|
|
if ((Aux->Data <= Pointer) && (Pointer < Aux->Data + Aux->Length))
|
|
return Aux->Position + (uint)(Pointer - Aux->Data);
|
|
}
|
|
|
|
//
|
|
// The last thing we do is search the NDIS buffer chain.
|
|
// This must succeed.
|
|
//
|
|
Buffer = NdisFirstBuffer(Packet->NdisPacket);
|
|
Position = 0;
|
|
for (;;) {
|
|
NdisQueryBuffer(Buffer, &BufAddr, &BufLen);
|
|
if ((BufAddr <= Pointer) && (Pointer < BufAddr + BufLen))
|
|
break;
|
|
Position += BufLen;
|
|
NdisGetNextBuffer(Buffer, &Buffer);
|
|
ASSERT(Buffer != NULL);
|
|
}
|
|
|
|
return Position + (uint)(Pointer - BufAddr);
|
|
}
|
|
|
|
|
|
//* InitializePacketFromNdis
|
|
//
|
|
// Initialize an IPv6Packet from an NDIS_PACKET.
|
|
//
|
|
void
|
|
InitializePacketFromNdis(
|
|
IPv6Packet *Packet,
|
|
PNDIS_PACKET NdisPacket,
|
|
uint Offset)
|
|
{
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
RtlZeroMemory(Packet, sizeof *Packet);
|
|
|
|
NdisGetFirstBufferFromPacket(NdisPacket, &NdisBuffer,
|
|
&Packet->Data,
|
|
&Packet->ContigSize,
|
|
&Packet->TotalSize);
|
|
|
|
Packet->NdisPacket = NdisPacket;
|
|
PositionPacketAt(Packet, Offset);
|
|
}
|
|
|
|
|
|
#if DBG
|
|
//* FormatV6Address - Print an IPv6 address to a buffer.
|
|
//
|
|
// Returns a static buffer containing the address.
|
|
// Because the static buffer is not locked,
|
|
// this function is only useful for debug prints.
|
|
//
|
|
// Returns char * instead of WCHAR * because %ws
|
|
// is not usable at DPC level in DbgPrint.
|
|
//
|
|
char *
|
|
FormatV6Address(const IPv6Addr *Addr)
|
|
{
|
|
static char Buffer[INET6_ADDRSTRLEN];
|
|
|
|
FormatV6AddressWorker(Buffer, Addr);
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
//* FormatV4Address - Print an IPv4 address to a buffer.
|
|
//
|
|
// Returns a static buffer containing the address.
|
|
// Because the static buffer is not locked,
|
|
// this function is only useful for debug prints.
|
|
//
|
|
// Returns char * instead of WCHAR * because %ws
|
|
// is not usable at DPC level in DbgPrint.
|
|
//
|
|
char *
|
|
FormatV4Address(IPAddr Addr)
|
|
{
|
|
static char Buffer[INET_ADDRSTRLEN];
|
|
|
|
FormatV4AddressWorker(Buffer, Addr);
|
|
return Buffer;
|
|
}
|
|
#endif // DBG
|
|
|
|
|
|
#if DBG
|
|
long DebugLogSlot = 0;
|
|
struct DebugLogEntry DebugLog[DEBUG_LOG_SIZE];
|
|
|
|
//* LogDebugEvent - Make an entry in our debug log that some event happened.
|
|
//
|
|
// The debug event log allows for "real time" logging of events
|
|
// in a circular queue kept in non-pageable memory. Each event consists
|
|
// of an id number and a arbitrary 32 bit value.
|
|
//
|
|
void
|
|
LogDebugEvent(uint Event, // The event that took place.
|
|
int Arg) // Any interesting 32 bits associated with event.
|
|
{
|
|
uint Slot;
|
|
|
|
//
|
|
// Get the next slot in the log in a muliprocessor safe manner.
|
|
//
|
|
Slot = InterlockedIncrement(&DebugLogSlot) & (DEBUG_LOG_SIZE - 1);
|
|
|
|
//
|
|
// Add this event to the log along with a timestamp.
|
|
//
|
|
KeQueryTickCount(&DebugLog[Slot].Time);
|
|
DebugLog[Slot].Event = Event;
|
|
DebugLog[Slot].Arg = Arg;
|
|
}
|
|
#endif // DBG
|