// -*- 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