|
|
// -*- 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:
//
// Receive routines for Internet Protocol Version 6.
//
#include "oscfg.h"
#include "ndis.h"
#include "ip6imp.h"
#include "ip6def.h"
#include "icmp.h"
#include "route.h"
#include "fragment.h"
#include "mobile.h"
#include "security.h"
#include "info.h"
#include "ipsec.h"
struct ReassemblyList ReassemblyList;
typedef struct Options { uint JumboLength; // Length of packet excluding IPv6 header.
IPv6RouterAlertOption UNALIGNED *Alert; IPv6HomeAddressOption UNALIGNED *HomeAddress; IPv6BindingUpdateOption UNALIGNED *BindingUpdate; } Options;
int ParseOptions( IPv6Packet *Packet, // The packet handed to us by IPv6Receive.
uchar HdrType, // Hop-by-hop or destination.
IPv6OptionsHeader *Hdr, // Header with following data.
uint HdrLength, // Length of the entire options area.
Options *Opts); // Return option values to caller.
extern void TCPRcvComplete(void);
//* IPv6ReceiveComplete - Handle a receive complete.
//
// Called by the lower layer when receives are temporarily done.
//
void IPv6ReceiveComplete(void) { // REVIEW: Original IP implementation had code here to call every
// REVIEW: UL protocol's receive complete routine (yes, all of them) here.
TCPRcvComplete(); }
//
// By default, test pullup in checked builds.
//
#ifndef PULLUP_TEST
#define PULLUP_TEST DBG
#endif
#if PULLUP_TEST
#define PULLUP_TEST_MAX_BUFFERS 8
#define PULLUP_TEST_MAX_BUFFER_SIZE 32
//* PullupTestChooseDistribution
//
// Choose a random distribution.
// Divides Size bytes into NumBuffers pieces,
// and returns the result in the Counts array.
//
void PullupTestChooseDistribution( uint Counts[], uint NumBuffers, uint Size) { uint i; uint ThisBuffer;
//
// We are somewhat biased towards cutting the packet
// up into small pieces with a large remainder.
// This puts the fragment boundaries at the beginning,
// where the headers are.
//
for (i = 0; i < NumBuffers - 1; i++) { ThisBuffer = RandomNumber(1, PULLUP_TEST_MAX_BUFFER_SIZE);
//
// Make sure that each segment has non-zero length.
//
if (ThisBuffer > Size - (NumBuffers - 1 - i)) ThisBuffer = Size - (NumBuffers - 1 - i);
Counts[i] = ThisBuffer; Size -= ThisBuffer; } Counts[i] = Size; }
//* PullupTestCreatePacket
//
// Given an IPv6 packet, creates a new IPv6 packet
// that can be handed up the receive path.
//
// We randomly fragment the IPv6 packet into multiple buffers.
// This tests pull-up processing in the receive path.
//
// Returns NULL if any memory allocation fails.
//
IPv6Packet * PullupTestCreatePacket(IPv6Packet *Packet) { IPv6Packet *TestPacket;
//
// We mostly want to test discontiguous packets.
// But occasionally test a contiguous packet.
//
if (RandomNumber(0, 10) == 0) { //
// We need to create a contiguous packet.
//
uint Padding; uint MemLen; void *Mem;
//
// We insert some padding to vary the alignment.
//
Padding = RandomNumber(0, 16); MemLen = sizeof *TestPacket + Padding + Packet->TotalSize; TestPacket = ExAllocatePoolWithTagPriority(NonPagedPool, MemLen, IP6_TAG, LowPoolPriority); if (TestPacket == NULL) return NULL; Mem = (void *)((uchar *)(TestPacket + 1) + Padding);
if (Packet->NdisPacket == NULL) { RtlCopyMemory(Mem, Packet->Data, Packet->TotalSize); } else { PNDIS_BUFFER NdisBuffer; uint Offset; int Ok;
NdisBuffer = NdisFirstBuffer(Packet->NdisPacket); Offset = Packet->Position; Ok = CopyNdisToFlat(Mem, NdisBuffer, Offset, Packet->TotalSize, &NdisBuffer, &Offset); ASSERT(Ok); }
RtlZeroMemory(TestPacket, sizeof *TestPacket); TestPacket->Data = TestPacket->FlatData = Mem; TestPacket->ContigSize = TestPacket->TotalSize = Packet->TotalSize; TestPacket->NTEorIF = Packet->NTEorIF; TestPacket->Flags = Packet->Flags; } else { //
// Create a packet with multiple NDIS buffers.
// Start with an over-estimate of the size of the MDLs we need.
//
uint NumPages = (Packet->TotalSize >> PAGE_SHIFT) + 2; uint MdlRawSize = sizeof(MDL) + (NumPages * sizeof(PFN_NUMBER)); uint MdlAlign = __builtin_alignof(MDL) - 1; uint MdlSize = (MdlRawSize + MdlAlign) &~ MdlAlign; uint Padding; uint MemLen; uint Counts[PULLUP_TEST_MAX_BUFFERS]; uint NumBuffers; void *Mem; PNDIS_PACKET NdisPacket; PNDIS_BUFFER NdisBuffer; uint Garbage = 0xdeadbeef; uint i;
//
// Choose the number of buffers/MDLs that we will use
// and the distribution of bytes into those buffers.
//
NumBuffers = RandomNumber(1, PULLUP_TEST_MAX_BUFFERS); PullupTestChooseDistribution(Counts, NumBuffers, Packet->TotalSize);
//
// Allocate all the memory that we will need.
// (Actually a bit of an over-estimate.)
// We insert some padding to vary the initial alignment.
//
Padding = RandomNumber(0, 16); MemLen = (sizeof *TestPacket + sizeof(NDIS_PACKET) + NumBuffers * (MdlSize + sizeof Garbage) + Padding + Packet->TotalSize); TestPacket = ExAllocatePoolWithTagPriority(NonPagedPool, MemLen, IP6_TAG, LowPoolPriority); if (TestPacket == NULL) return NULL; NdisPacket = (PNDIS_PACKET)(TestPacket + 1); NdisBuffer = (PNDIS_BUFFER)(NdisPacket + 1); Mem = (void *)((uchar *)NdisBuffer + NumBuffers * MdlSize + Padding);
//
// Initialize the NDIS packet and buffers.
//
RtlZeroMemory(NdisPacket, sizeof *NdisPacket); for (i = 0; i < NumBuffers; i++) { MmInitializeMdl(NdisBuffer, Mem, Counts[i]); MmBuildMdlForNonPagedPool(NdisBuffer); NdisChainBufferAtBack(NdisPacket, NdisBuffer); RtlCopyMemory((uchar *)Mem + Counts[i], &Garbage, sizeof Garbage);
(uchar *)Mem += Counts[i] + sizeof Garbage; (uchar *)NdisBuffer += MdlSize; }
//
// Copy data to the new packet.
//
CopyToBufferChain((PNDIS_BUFFER)(NdisPacket + 1), 0, Packet->NdisPacket, Packet->Position, Packet->FlatData, Packet->TotalSize);
//
// Initialize the new packet.
//
InitializePacketFromNdis(TestPacket, NdisPacket, 0); TestPacket->NTEorIF = Packet->NTEorIF; TestPacket->Flags = Packet->Flags; }
return TestPacket; } #endif // PULLUP_TEST
//* IPv6Receive - Receive an incoming IPv6 datagram.
//
// This is the routine called by the link layer module when an incoming IPv6
// datagram is to be processed. We validate the datagram and decide what to
// do with it.
//
// The Packet->NTEorIF field holds the NTE or interface that is receiving
// the packet. Typically this is an interface, but there are some tunnel
// situations where the link layer has already found an NTE.
//
// Either the caller should hold a reference to the NTE or interface
// across the call, or the caller can place a reference in the Packet
// with PACKET_HOLDS_REF. If the caller specifies PACKET_HOLDS_REF,
/// IPv6Receive will release the reference.
//
// There is one exception: the caller can supply an interface
// with zero references (not using PACKET_HOLDS_REF),
// if the interface is being destroyed but IF->Cleanup has not yet returned.
//
// NB: The datagram may either be held in a NDIS_PACKET allocated by the
// link-layer or the interface driver (in which case 'Packet->NdisPacket'
// is non-NULL and 'Data' points to the first data buffer in the buffer
// chain), or the datagram may still be held by NDIS (in which case
// 'Packet->NdisPacket' is NULL and 'Data' points to a buffer containing
// the entire datagram).
//
// NB: We do NOT check for link-level multi/broadcasts to
// IPv6 unicast destinations. In the IPv4 world, receivers dropped
// such packets, but in the IPv6 world they are accepted.
//
// Returns count of references for the packet.
// For now, this should always be zero.
// Someday in the future this might be used to indicate
// that the IPv6 layer has not finished its receive processing.
//
// Callable from DPC context, not from thread context.
//
int IPv6Receive(IPv6Packet *Packet) { uchar NextHeader; // Current header's NextHeader field.
uchar (*Handler)(); SALinkage *ThisSA, *NextSA; int PktRefs;
ASSERT((Packet->FlatData == NULL) != (Packet->NdisPacket == NULL)); ASSERT(Packet->NTEorIF != NULL); ASSERT(Packet->SAPerformed == NULL);
IPSIncrementInReceiveCount();
//
// Ensure that the packet is accessible in the kernel address space.
// If any mappings fail, just drop the packet.
// In practice, the packet buffers are usually already mapped.
// But they may not be, for example in loopback.
//
if (Packet->NdisPacket != NULL) { NDIS_BUFFER *Buffer;
Buffer = NdisFirstBuffer(Packet->NdisPacket); if (! MapNdisBuffers(Buffer)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "IPv6Receive(%p): buffer mapping failed\n", Packet)); IPSInfo.ipsi_indiscards++; return 0; // Drop the packet.
} }
#if PULLUP_TEST
Packet = PullupTestCreatePacket(Packet); if (Packet == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "IPv6Receive(%p): PullupTestCreatePacket failed\n", Packet)); IPSInfo.ipsi_indiscards++; return 0; // Drop the packet.
} #endif
//
// Iteratively switch out to the handler for each successive next header
// until we reach a handler that reports no more headers follow it.
//
// NB: We do NOT check NTE->DADStatus here.
// That is the responsibility of higher-level protocols.
//
NextHeader = IP_PROTOCOL_V6; // Always first header in packet.
do { //
// Current header indicates that another header follows.
// See if we have a handler for it.
//
Handler = ProtocolSwitchTable[NextHeader].DataReceive; if (Handler == NULL) {
//
// We don't have a handler for this header type,
// so see if there is a raw receiver for it.
//
if (!RawReceive(Packet, NextHeader)) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6 Receive: Next Header type %u not handled.\n", NextHeader)); //
// There isn't a raw receiver either.
// Send an ICMP error message.
// ICMP Pointer value is the offset from the start of the
// incoming packet's IPv6 header to the offending field.
//
ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_UNRECOGNIZED_NEXT_HEADER, Packet->NextHeaderPosition - Packet->IPPosition, NextHeader, FALSE);
IPSInfo.ipsi_inunknownprotos++; } else { IPSIncrementInDeliverCount(); }
break; // We can't do anything more with this packet.
}
NextHeader = (*Handler)(Packet); } while (NextHeader != IP_PROTOCOL_NONE);
//
// If this packet holds a reference, free it now.
//
if (Packet->Flags & PACKET_HOLDS_REF) { if (IsNTE(Packet->NTEorIF)) ReleaseNTE(CastToNTE(Packet->NTEorIF)); else ReleaseIF(CastToIF(Packet->NTEorIF)); }
//
// Clean up any contiguous regions left by PacketPullup.
//
PacketPullupCleanup(Packet);
//
// Clean up list of SA's performed.
//
for (ThisSA = Packet->SAPerformed; ThisSA != NULL; ThisSA = NextSA) { ReleaseSA(ThisSA->This); NextSA = ThisSA->Next; ExFreePool(ThisSA); }
PktRefs = Packet->RefCnt; #if PULLUP_TEST
ExFreePool(Packet); #endif
return PktRefs; }
//* IPv6HeaderReceive - Handle a IPv6 header.
//
// This is the routine called to process an IPv6 header, a next header
// value of 41 (e.g. as would be encountered with v6 in v6 tunnels). To
// avoid code duplication, it is also used to process the initial IPv6
// header found in all IPv6 packets, in which mode it may be viewed as
// a continuation of IPv6Receive.
//
uchar IPv6HeaderReceive( IPv6Packet *Packet) // Packet handed to us by IPv6Receive.
{ uint PayloadLength; uchar NextHeader; int Forwarding; // TRUE means Forwarding, FALSE means Receiving.
//
// Sanity-check ContigSize & TotalSize.
// Higher-level code in the receive path relies on these conditions.
//
ASSERT(Packet->ContigSize <= Packet->TotalSize);
//
// If we are decapsulating a packet,
// remember that this packet was originally tunneled.
//
// Some argue that decapsulating and receiving
// the inner packet on the same interface as the outer packet
// is incorrect: the inner packet should be received
// on a tunnel interface distinct from the original interface.
// (This approach introduces some issues with handling
// IPsec encapsulation, especially tunnel-mode IPsec between peers
// where you want the inner & outer source address to be the same.)
//
// In any case, for now we receive the inner packet on the original
// interface. However, this introduces a potential security
// problem. An off-link node can send an encapsulated packet
// that when decapsulated, appears to have originated from
// an on-link neighbor. This is a security problem for ND.
// We can not conveniently decrement the HopLimit (to make ND's
// check against 255 effective in this case), because the packet
// is read-only. Instead, we remember that the packet is tunneled
// and check this flag bit in the ND code.
//
if (Packet->IP != NULL) { Packet->Flags |= PACKET_TUNNELED; Packet->Flags &= ~PACKET_SAW_HA_OPT; // Forget if we saw one.
Packet->SkippedHeaderLength = 0;
//
// If we've already done some IPSec processing on this packet,
// then this is a tunnel header and the preceeding IPSec header
// is operating in tunnel mode.
//
if (Packet->SAPerformed != NULL) Packet->SAPerformed->Mode = TUNNEL; } else { //
// In the reassembly path, we remember if the fragments were
// tunneled but we do not have a Packet->IP.
//
ASSERT((((Packet->Flags & PACKET_TUNNELED) == 0) || (Packet->Flags & PACKET_REASSEMBLED)) && ((Packet->Flags & PACKET_SAW_HA_OPT) == 0) && (Packet->SAPerformed == NULL)); }
//
// Make sure we have enough contiguous bytes for an IPv6 header, otherwise
// attempt to pullup that amount. Then stash away a pointer to the header
// and also remember the offset into the packet at which it begins (needed
// to calculate an offset for certain ICMP error messages).
//
if (! PacketPullup(Packet, sizeof(IPv6Header), __builtin_alignof(IPv6Addr), 0)) { // Pullup failed.
if (Packet->TotalSize < sizeof(IPv6Header)) KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6HeaderReceive: " "Packet too small to contain IPv6 header\n")); IPSInfo.ipsi_inhdrerrors++; return IP_PROTOCOL_NONE; } Packet->IP = (IPv6Header UNALIGNED *)Packet->Data; Packet->IPPosition = Packet->Position; Packet->NextHeaderPosition = Packet->Position + FIELD_OFFSET(IPv6Header, NextHeader);
//
// Skip over IPv6 header (note we keep our pointer to it).
//
AdjustPacketParams(Packet, sizeof(IPv6Header));
//
// Check the IP version is correct.
// We specifically do NOT check HopLimit.
// HopLimit is only checked when forwarding.
//
if ((Packet->IP->VersClassFlow & IP_VER_MASK) != IP_VERSION) { // Silently discard the packet.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6HeaderReceive: bad version\n")); IPSInfo.ipsi_inhdrerrors++; return IP_PROTOCOL_NONE; }
//
// We use a separate pointer to refer to the source address so that
// later options can change it.
//
Packet->SrcAddr = AlignAddr(&Packet->IP->Source);
//
// Protect against attacks that use bogus source addresses.
//
if (IsInvalidSourceAddress(Packet->SrcAddr)) { // Silently discard the packet.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6HeaderReceive: source address is invalid\n")); return IP_PROTOCOL_NONE; } if (IsLoopback(Packet->SrcAddr) && ((Packet->Flags & PACKET_LOOPED_BACK) == 0)) { // Silently discard the packet.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6HeaderReceive: loopback source addr from wire?\n")); return IP_PROTOCOL_NONE; }
if (IsNTE(Packet->NTEorIF)) { NetTableEntry *NTE;
//
// We were called with an NTE.
// Our caller (or the packet itself) should be holding a reference.
// The NTE holds an interface reference.
//
NTE = CastToNTE(Packet->NTEorIF);
//
// Verify that the packet's destination address is
// consistent with this NTE.
//
if (!IP6_ADDR_EQUAL(AlignAddr(&Packet->IP->Dest), &NTE->Address)) { Interface *IF = NTE->IF;
//
// We can't accept this new header on this NTE.
// Convert to an Interface and punt to forwarding code below.
//
if (Packet->Flags & PACKET_HOLDS_REF) { AddRefIF(IF); ReleaseNTE(NTE); } else { //
// Our caller holds a reference for the NTE,
// which holds a reference for the interface.
// So the packet does not need to hold a reference.
//
} Packet->NTEorIF = CastFromIF(IF); goto Forward; }
//
// We are Receiving the packet.
//
Forwarding = FALSE;
} else { NetTableEntryOrInterface *NTEorIF; ushort Type;
//
// We were called with an Interface.
// In some situations, there is no reference for this interface
// and the interface is being destroyed. FindAddressOnInterface
// will return NULL in that case. After this point, we must ensure
// that the interface does have a reference, by having the packet
// hold a reference for the interface or a reference for an NTE
// on the interface.
//
NTEorIF = FindAddressOnInterface(CastToIF(Packet->NTEorIF), AlignAddr(&Packet->IP->Dest), &Type); if (NTEorIF == NULL) { //
// The interface is being destroyed.
//
IPSInfo.ipsi_indiscards++; return IP_PROTOCOL_NONE; }
//
// FindAddressOnInterface returned a reference to NTEorIF
// (which could be an interface or an NTE). We either need
// to put this reference into the packet, or release it
// if the packet already holds an appropriate reference.
//
if (Type == ADE_NONE) { //
// If the packet does not hold a reference for the interface,
// give it one now.
//
ASSERT(NTEorIF == Packet->NTEorIF); if (Packet->Flags & PACKET_HOLDS_REF) { //
// The packet already holds an interface reference,
// so our reference is not neeeded.
//
ReleaseIF(CastToIF(NTEorIF)); } else { //
// Give the packet our interface reference.
//
Packet->Flags |= PACKET_HOLDS_REF; }
//
// The address is not assigned to this interface. Check to see
// if it is appropriate for us to forward this packet.
// If not, drop it. At this point, we are fairly
// conservative about what we will forward.
//
Forward: if (!(CastToIF(Packet->NTEorIF)->Flags & IF_FLAG_FORWARDS) || (Packet->Flags & PACKET_NOT_LINK_UNICAST) || IsUnspecified(AlignAddr(&Packet->IP->Source)) || IsLoopback(AlignAddr(&Packet->IP->Source))) { //
// Drop the packet with no ICMP error.
//
IPSInfo.ipsi_inaddrerrors++; return IP_PROTOCOL_NONE; }
//
// No support yet for forwarding multicast packets.
//
if (IsUnspecified(AlignAddr(&Packet->IP->Dest)) || IsLoopback(AlignAddr(&Packet->IP->Dest)) || IsMulticast(AlignAddr(&Packet->IP->Dest))) { //
// Send an ICMP error.
//
ICMPv6SendError(Packet, ICMPv6_DESTINATION_UNREACHABLE, ICMPv6_COMMUNICATION_PROHIBITED, 0, Packet->IP->NextHeader, FALSE); IPSInfo.ipsi_inaddrerrors++; return IP_PROTOCOL_NONE; }
//
// We do the actual forwarding below...
//
Forwarding = TRUE;
} else { //
// If we found a unicast ADE, then remember the NTE.
// Conceptually, we think of the packet as holding
// the reference to the NTE. Normally for multicast/anycast
// addresses, we delay our choice of an appropriate NTE
// until it is time to reply to the packet.
//
if (IsNTE(NTEorIF)) { NetTableEntry *NTE = CastToNTE(NTEorIF); Interface *IF = NTE->IF;
ASSERT(CastFromIF(IF) == Packet->NTEorIF);
if (!IsValidNTE(NTE)) { //
// The unicast address is not valid, so it can't
// receive packets. The address may be assigned
// to some other node, so forwarding is appropriate.
//
// Ensure that the packet holds an interface reference.
//
if (!(Packet->Flags & PACKET_HOLDS_REF)) { //
// The packet does not already hold an interface ref,
// so give it one.
//
AddRefIF(IF); Packet->Flags |= PACKET_HOLDS_REF; } //
// Now our NTE reference is not needed.
//
ReleaseNTE(NTE); goto Forward; }
//
// Ensure that the packet holds a reference for the NTE,
// which holds an interface reference.
//
if (Packet->Flags & PACKET_HOLDS_REF) { //
// The packet already holds an interface reference.
// Release that reference and give the packet
// our NTE reference.
//
ReleaseIF(IF); } else { //
// The packet does not hold a reference.
// Give the packet our NTE reference.
//
Packet->Flags |= PACKET_HOLDS_REF; } Packet->NTEorIF = CastFromNTE(NTE); } else { //
// Ensure that the packet holds an interface reference.
//
ASSERT(NTEorIF == Packet->NTEorIF); if (Packet->Flags & PACKET_HOLDS_REF) { //
// The packet already holds an interface reference,
// so our reference is not needed.
//
ReleaseIF(CastToIF(NTEorIF)); } else { //
// Give our interface reference to the packet.
//
Packet->Flags |= PACKET_HOLDS_REF; } }
//
// We found an ADE on this IF to accept the packet,
// so we will be Receiving it.
//
Forwarding = FALSE; } }
//
// At this point, the Forwarding variable tells us
// if we are forwarding or receiving the packet.
//
//
// Before processing any headers, including Hop-by-Hop,
// check that the amount of payload the IPv6 header thinks is present
// can actually fit inside the packet data area that the link handed us.
// Note that a Payload Length of zero *might* mean a Jumbo Payload option.
//
PayloadLength = net_short(Packet->IP->PayloadLength); if (PayloadLength > Packet->TotalSize) { // Silently discard the packet.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "IPv6HeaderReceive: Header's PayloadLength is greater than " "the amount of data received\n")); IPSInfo.ipsi_inhdrerrors++; return IP_PROTOCOL_NONE; }
//
// Check for Hop-by-Hop Options.
//
if (Packet->IP->NextHeader == IP_PROTOCOL_HOP_BY_HOP) { int RetVal;
//
// If there is a Jumbo Payload option, HopByHopOptionsReceive
// will adjust the packet size. Otherwise we take care of it
// now, before reading the Hop-by-Hop header.
//
if (PayloadLength != 0) { Packet->TotalSize = PayloadLength; if (Packet->ContigSize > PayloadLength) Packet->ContigSize = PayloadLength; }
//
// Parse the Hop-by-Hop options.
//
RetVal = HopByHopOptionsReceive(Packet); if (RetVal < 0) { //
// The packet had bad Hop-by-Hop Options.
// Drop it.
//
IPSInfo.ipsi_inhdrerrors++; return IP_PROTOCOL_NONE; } NextHeader = (uchar)RetVal; // Truncate to 8 bits.
} else { //
// No Jumbo Payload option. Adjust the packet size.
//
Packet->TotalSize = PayloadLength; if (Packet->ContigSize > PayloadLength) Packet->ContigSize = PayloadLength;
//
// No Hop-by-Hop options.
//
NextHeader = Packet->IP->NextHeader; }
//
// Check if we are forwarding this packet.
//
if (Forwarding) { IPv6Header UNALIGNED *FwdIP; NDIS_PACKET *FwdPacket; NDIS_STATUS NdisStatus; uint Offset; uint MemLen; uchar *Mem; uint TunnelStart = NO_TUNNEL, IPSecBytes = 0; IPSecProc *IPSecToDo; uint Action; RouteCacheEntry *RCE; IP_STATUS Status;
//
// Verify IPSec was performed.
//
if (InboundSecurityCheck(Packet, 0, 0, 0, CastToIF(Packet->NTEorIF)) != TRUE) { //
// No policy was found or the policy indicated to drop the packet.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "IPv6Receive: " "IPSec lookup failed or policy was to drop\n")); IPSInfo.ipsi_inaddrerrors++; return IP_PROTOCOL_NONE; }
//
// At this time, we need to copy the incoming packet,
// for several reasons: We can't hold the Packet
// once IPv6HeaderReceive returns, yet we need to queue
// packet to forward it. We need to modify the packet
// (in IPv6Forward) by decrementing the hop count,
// yet our incoming packet is read-only. Finally,
// we need space in the outgoing packet for the outgoing
// interface's link-level header, which may differ in size
// from that of the incoming interface. Someday, we can
// implement support for returning a non-zero reference
// count from IPv6Receive and only copy the incoming
// packet's header to construct the outgoing packet.
//
//
// Find a route to the new destination.
//
Status = RouteToDestination(AlignAddr(&Packet->IP->Dest), 0, Packet->NTEorIF, RTD_FLAG_LOOSE, &RCE); if (Status != IP_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "IPv6HeaderReceive: " "No route to destination for forwarding.\n")); ICMPv6SendError(Packet, ICMPv6_DESTINATION_UNREACHABLE, ICMPv6_NO_ROUTE_TO_DESTINATION, 0, NextHeader, FALSE); IPSInfo.ipsi_outnoroutes++; return IP_PROTOCOL_NONE; }
//
// Find the Security Policy for this outbound traffic.
//
IPSecToDo = OutboundSPLookup(AlignAddr(&Packet->IP->Source), AlignAddr(&Packet->IP->Dest), 0, 0, 0, RCE->NCE->IF, &Action);
if (IPSecToDo == NULL) { //
// Check Action.
//
if (Action == LOOKUP_DROP) { // Drop packet.
ReleaseRCE(RCE); IPSInfo.ipsi_inaddrerrors++; return IP_PROTOCOL_NONE; } else { if (Action == LOOKUP_IKE_NEG) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "IPv6HeaderReceive: IKE not supported yet.\n")); ReleaseRCE(RCE); IPSInfo.ipsi_inaddrerrors++; return IP_PROTOCOL_NONE; } }
//
// With no IPSec to perform, IPv6Forward won't be changing the
// outgoing interface from what we currently think it will be.
// So we can use the exact size of its link-level header.
//
Offset = RCE->NCE->IF->LinkHeaderSize;
} else { //
// Calculate the space needed for the IPSec headers.
//
IPSecBytes = IPSecBytesToInsert(IPSecToDo, &TunnelStart, NULL);
if (TunnelStart != 0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "IPv6HeaderReceive: IPSec Tunnel mode only.\n")); FreeIPSecToDo(IPSecToDo, IPSecToDo->BundleSize); ReleaseRCE(RCE); IPSInfo.ipsi_inaddrerrors++; return IP_PROTOCOL_NONE; }
//
// The IPSec code in IPv6Forward might change the outgoing
// interface from what we currently think it will be.
// Leave the max amount of space for its link-level header.
//
Offset = MAX_LINK_HEADER_SIZE; }
PayloadLength = Packet->TotalSize; MemLen = Offset + sizeof(IPv6Header) + PayloadLength + IPSecBytes;
NdisStatus = IPv6AllocatePacket(MemLen, &FwdPacket, &Mem); if (NdisStatus != NDIS_STATUS_SUCCESS) { if (IPSecToDo) { FreeIPSecToDo(IPSecToDo, IPSecToDo->BundleSize); } ReleaseRCE(RCE); IPSInfo.ipsi_indiscards++; return IP_PROTOCOL_NONE; // We can't forward.
}
FwdIP = (IPv6Header UNALIGNED *)(Mem + Offset + IPSecBytes);
//
// Copy from the incoming packet to the outgoing packet.
//
CopyPacketToBuffer((uchar *)FwdIP, Packet, sizeof(IPv6Header) + PayloadLength, Packet->IPPosition);
//
// Send the outgoing packet.
//
IPv6Forward(Packet->NTEorIF, FwdPacket, Offset + IPSecBytes, FwdIP, PayloadLength, TRUE, // OK to Redirect.
IPSecToDo, RCE);
if (IPSecToDo) { FreeIPSecToDo(IPSecToDo, IPSecToDo->BundleSize); }
ReleaseRCE(RCE);
return IP_PROTOCOL_NONE; } // end of if (Forwarding)
//
// Packet is for this node.
// Note: We may only be an intermediate node and not the packet's final
// destination, if there is a routing header.
//
return NextHeader; }
//* ReassemblyInit
//
// Initialize data structures required for fragment reassembly.
//
void ReassemblyInit(void) { KeInitializeSpinLock(&ReassemblyList.Lock); ReassemblyList.First = ReassemblyList.Last = SentinelReassembly; KeInitializeSpinLock(&ReassemblyList.LockSize); }
//* ReassemblyUnload
//
// Cleanup the fragment reassembly data structures and
// prepare for stack unload.
//
void ReassemblyUnload(void) { //
// We are called after all interfaces have been destroyed,
// so the reassemblies should already be gone.
//
ASSERT(ReassemblyList.Last == SentinelReassembly); ASSERT(ReassemblyList.Size == 0); }
//* ReassemblyRemove
//
// Cleanup the fragment reassembly data structures
// when an interface becomes invalid.
//
// Callable from DPC or thread context.
//
void ReassemblyRemove(Interface *IF) { Reassembly *DeleteList = NULL; Reassembly *Reass, *NextReass; KIRQL OldIrql;
KeAcquireSpinLock(&ReassemblyList.Lock, &OldIrql); for (Reass = ReassemblyList.First; Reass != SentinelReassembly; Reass = NextReass) { NextReass = Reass->Next;
if (Reass->IF == IF) { //
// Remove this reassembly.
// If it is not already being deleted,
// put it on our temporary list.
//
RemoveReassembly(Reass); KeAcquireSpinLockAtDpcLevel(&Reass->Lock); if (Reass->State == REASSEMBLY_STATE_DELETING) { //
// Note that it has been removed from the list.
//
Reass->State = REASSEMBLY_STATE_REMOVED; } else { Reass->Next = DeleteList; DeleteList = Reass; } KeReleaseSpinLockFromDpcLevel(&Reass->Lock); } } KeReleaseSpinLock(&ReassemblyList.Lock, OldIrql);
//
// Actually free the reassemblies that we removed above.
//
while ((Reass = DeleteList) != NULL) { DeleteList = Reass->Next; DeleteReassembly(Reass); } }
//* FragmentReceive - Handle a IPv6 datagram fragment.
//
// This is the routine called by IPv6 when it receives a fragment of an
// IPv6 datagram, i.e. a next header value of 44. Here we attempt to
// reassemble incoming fragments into complete IPv6 datagrams.
//
// If a later fragment provides data that conflicts with an earlier
// fragment, then we use the first-arriving data.
//
// We silently drop the fragment and stop reassembly in several
// cases that are not specified in the spec, to prevent DoS attacks.
// These include partially overlapping fragments and fragments
// that carry no data. Legitimate senders should never generate them.
//
uchar FragmentReceive( IPv6Packet *Packet) // Packet handed to us by IPv6Receive.
{ Interface *IF = Packet->NTEorIF->IF; FragmentHeader UNALIGNED *Frag; Reassembly *Reass; ushort FragOffset; PacketShim *Shim, *ThisShim, **MoveShim; uint NextHeaderPosition;
IPSInfo.ipsi_reasmreqds++;
//
// We can not reassemble fragments that have had IPsec processing.
// It can't work because the IPsec headers in the unfragmentable part
// of the offset-zero fragment will authenticate/decrypt that fragment.
// Then the same headers would be copied to the reassembled packet.
// They couldn't possibly successfully authenticate/decrypt again.
// Also see RFC 2401 B.2.
//
if (Packet->SAPerformed != NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "FragmentReceive: IPsec on fragment\n")); //
// The spec does not tell us what ICMP error to generate in this case,
// but flagging the fragment header seems reasonable.
//
goto BadFragment; }
//
// If a jumbo payload option was seen, send an ICMP error.
// Set ICMP pointer to the offset of the fragment header.
//
if (Packet->Flags & PACKET_JUMBO_OPTION) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "FragmentReceive: jumbo fragment\n"));
BadFragment: //
// The NextHeader value passed to ICMPv6SendError
// is IP_PROTOCOL_FRAGMENT because we haven't moved
// past the fragment header yet.
//
ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, Packet->Position - Packet->IPPosition, IP_PROTOCOL_FRAGMENT, FALSE); goto Failed; // Drop packet.
}
//
// Verify that we have enough contiguous data to overlay a FragmentHeader
// structure on the incoming packet. Then do so.
//
if (! PacketPullup(Packet, sizeof *Frag, 1, 0)) { // Pullup failed.
if (Packet->TotalSize < sizeof *Frag) ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, FIELD_OFFSET(IPv6Header, PayloadLength), IP_PROTOCOL_NONE, FALSE); goto Failed; // Drop packet.
} Frag = (FragmentHeader UNALIGNED *) Packet->Data;
//
// Remember offset to this header's NextHeader field.
// But don't overwrite offset to previous header's NextHeader just yet.
//
NextHeaderPosition = Packet->Position + FIELD_OFFSET(FragmentHeader, NextHeader);
//
// Skip over fragment header.
//
AdjustPacketParams(Packet, sizeof *Frag);
//
// Lookup this fragment triple (Source Address, Destination
// Address, and Identification field) per-interface to see if
// we've already received other fragments of this packet.
//
Reass = FragmentLookup(IF, Frag->Id, AlignAddr(&Packet->IP->Source), AlignAddr(&Packet->IP->Dest)); if (Reass == NULL) { //
// We hold the global reassembly list lock.
//
// Handle a special case first: if this is the first, last, and only
// fragment, then we can just continue parsing without reassembly.
// Test both paths in checked builds.
//
if ((Frag->OffsetFlag == 0) #if DBG
&& ((int)Random() < 0) #endif
) { //
// Return next header value.
//
KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock); Packet->NextHeaderPosition = NextHeaderPosition; Packet->SkippedHeaderLength += sizeof(FragmentHeader); IPSInfo.ipsi_reasmoks++; return Frag->NextHeader; }
//
// We must avoid creating new reassembly records
// if the interface is going away, to prevent races
// with DestroyIF/ReassemblyRemove.
//
if (IsDisabledIF(IF)) { KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock); goto Failed; }
//
// This is the first fragment of this datagram we've received.
// Allocate a reassembly structure to keep track of the pieces.
//
Reass = ExAllocatePoolWithTagPriority( NonPagedPool, sizeof(struct Reassembly), IP6_TAG, LowPoolPriority); if (Reass == NULL) { KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "FragmentReceive: Couldn't allocate memory!?!\n")); goto Failed; }
KeInitializeSpinLock(&Reass->Lock); Reass->State = REASSEMBLY_STATE_NORMAL;
RtlCopyMemory(&Reass->IPHdr, Packet->IP, sizeof(IPv6Header)); Reass->IF = IF; Reass->Id = Frag->Id; Reass->ContigList = NULL; #if DBG
Reass->ContigEnd = NULL; #endif
Reass->GapList = NULL; Reass->Timer = DEFAULT_REASSEMBLY_TIMEOUT; Reass->Marker = 0; Reass->MaxGap = 0; //
// We must initialize DataLength to an invalid value.
// Initializing to zero doesn't work.
//
Reass->DataLength = (uint)-1; Reass->UnfragmentLength = 0; Reass->UnfragData = NULL; Reass->Flags = 0; Reass->Size = REASSEMBLY_SIZE_PACKET;
//
// Add new Reassembly struct to front of the ReassemblyList.
// Acquires the reassembly record lock and
// releases the global reassembly list lock.
//
AddToReassemblyList(Reass); } else { //
// We have found and locked an existing reassembly structure.
// Because we remove the reassembly structure in every
// error situation below, an existing reassembly structure
// must have a shim that has been successfully added to it.
//
ASSERT((Reass->ContigList != NULL) || (Reass->GapList != NULL)); }
//
// At this point, we have a locked reassembly record.
// We do not hold the global reassembly list lock
// while we perform the relatively expensive work
// of copying the fragment.
//
ASSERT(Reass->State == REASSEMBLY_STATE_NORMAL);
//
// Update the saved packet flags from this fragment packet.
// We are really only interested in PACKET_NOT_LINK_UNICAST.
//
Reass->Flags |= Packet->Flags;
FragOffset = net_short(Frag->OffsetFlag) & FRAGMENT_OFFSET_MASK;
//
// Send ICMP error if this fragment causes the total packet length
// to exceed 65,535 bytes. Set ICMP pointer equal to the offset to
// the Fragment Offset field.
//
if (FragOffset + Packet->TotalSize > MAX_IPv6_PAYLOAD) { DeleteFromReassemblyList(Reass); ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (Packet->Position - sizeof(FragmentHeader) + (uint)FIELD_OFFSET(FragmentHeader, OffsetFlag) - Packet->IPPosition), ((FragOffset == 0) ? Frag->NextHeader : IP_PROTOCOL_NONE), FALSE); goto Failed; }
if ((Packet->TotalSize == 0) && (Frag->OffsetFlag != 0)) { //
// We allow a moot fragment header (Frag->OffsetFlag == 0),
// because some test programs might generate them.
// (The first/last/only check above catches this in free builds.)
// But otherwise, we disallow fragments that do not actually
// carry any data for DoS protection.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "FragmentReceive: zero data fragment\n")); DeleteFromReassemblyList(Reass); return IP_PROTOCOL_NONE; }
//
// If this is the last fragment (more fragments bit not set), then
// remember the total data length, else, check that the length
// is a multiple of 8 bytes.
//
if ((net_short(Frag->OffsetFlag) & FRAGMENT_FLAG_MASK) == 0) { if (Reass->DataLength != (uint)-1) { //
// We already received a last fragment.
// This can happen if a packet is duplicated.
//
if (FragOffset + Packet->TotalSize != Reass->DataLength) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "FragmentReceive: second last fragment\n")); DeleteFromReassemblyList(Reass); return IP_PROTOCOL_NONE; } } else { //
// Set expected data length from this fragment.
//
Reass->DataLength = FragOffset + Packet->TotalSize;
//
// Do we have any fragments beyond this length?
//
if ((Reass->Marker > Reass->DataLength) || (Reass->MaxGap > Reass->DataLength)) goto BadFragmentBeyondData; } } else { if ((Packet->TotalSize % 8) != 0) { //
// Length is not multiple of 8, send ICMP error with a pointer
// value equal to offset of payload length field in IP header.
//
DeleteFromReassemblyList(Reass); ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, FIELD_OFFSET(IPv6Header, PayloadLength), ((FragOffset == 0) ? Frag->NextHeader : IP_PROTOCOL_NONE), FALSE); goto Failed; // Drop packet.
}
if ((Reass->DataLength != (uint)-1) && (FragOffset + Packet->TotalSize > Reass->DataLength)) { //
// This fragment falls beyond the data length.
// As part of our DoS prevention, drop the reassembly.
//
BadFragmentBeyondData: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "FragmentReceive: fragment beyond data length\n")); DeleteFromReassemblyList(Reass); return IP_PROTOCOL_NONE; } }
//
// Allocate and initialize a shim structure to hold the fragment data.
//
Shim = ExAllocatePoolWithTagPriority( NonPagedPool, sizeof *Shim + Packet->TotalSize, IP6_TAG, LowPoolPriority); if (Shim == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "FragmentReceive: Couldn't allocate memory!?!\n")); DeleteFromReassemblyList(Reass); goto Failed; }
IncreaseReassemblySize(Reass, REASSEMBLY_SIZE_FRAG + Packet->TotalSize); Shim->Len = (ushort)Packet->TotalSize; Shim->Offset = FragOffset; Shim->Next = NULL;
//
// Determine where this fragment fits among the previous ones.
//
// There is no good reason for senders to ever generate overlapping
// fragments. However, packets may sometimes be duplicated in the network.
// If we receive a fragment that duplicates previously received fragments,
// then we just discard it. If we receive a fragment that only partially
// overlaps previously received fragments, then we assume a malicious
// sender and just drop the reassembly. This gives us better behavior
// under some kinds of DoS attacks, although the upper bound on reassembly
// buffers (see CheckReassemblyQuota) is the ultimate protection.
//
if (FragOffset == Reass->Marker) { //
// This fragment extends the contiguous list.
//
if (Reass->ContigList == NULL) { //
// We're first on the list.
// We use info from the (first) offset zero fragment to recreate
// the original datagram. Info in a second offset zero fragment
// is ignored.
//
ASSERT(FragOffset == 0); ASSERT(Reass->UnfragData == NULL); Reass->ContigList = Shim;
// Save the next header value.
Reass->NextHeader = Frag->NextHeader;
//
// Grab the unfragmentable data, i.e. the extension headers that
// preceded the fragment header.
//
Reass->UnfragmentLength = (ushort) ((Packet->Position - sizeof(FragmentHeader)) - (Packet->IPPosition + sizeof(IPv6Header)));
if (Reass->UnfragmentLength != 0) { Reass->UnfragData = ExAllocatePoolWithTagPriority( NonPagedPool, Reass->UnfragmentLength, IP6_TAG, LowPoolPriority); if (Reass->UnfragData == NULL) { // Out of memory!?! Clean up and drop packet.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "FragmentReceive: " "Couldn't allocate memory?\n")); // Will also free Shim because of Reass->ContigList.
DeleteFromReassemblyList(Reass); goto Failed; } IncreaseReassemblySize(Reass, Reass->UnfragmentLength); CopyPacketToBuffer(Reass->UnfragData, Packet, Reass->UnfragmentLength, Packet->IPPosition + sizeof(IPv6Header));
Reass->NextHeaderOffset = Packet->NextHeaderPosition - Packet->IPPosition; } else Reass->NextHeaderOffset = FIELD_OFFSET(IPv6Header, NextHeader);
//
// We need to have the IP header of the offset-zero fragment.
// (Every fragment normally will have the same IP header,
// except for PayloadLength, and unfragmentable headers,
// but they might not.) ReassembleDatagram and
// CreateFragmentPacket both need it.
//
// Of the 40 bytes in the header, the 32 bytes in the source
// and destination addresses are already correct.
// So we just copy the other 8 bytes now.
//
RtlCopyMemory(&Reass->IPHdr, Packet->IP, 8);
} else { //
// Add us to the end of the list.
//
Reass->ContigEnd->Next = Shim; } Reass->ContigEnd = Shim;
//
// Increment our contiguous extent marker.
//
Reass->Marker += (ushort)Packet->TotalSize;
//
// Now peruse the non-contiguous list here to see if we already
// have the next fragment to extend the contiguous list, and if so,
// move it on over. Repeat until we can't.
//
MoveShim = &Reass->GapList; while ((ThisShim = *MoveShim) != NULL) { if (ThisShim->Offset == Reass->Marker) { //
// This fragment now extends the contiguous list.
// Add it to the end of the list.
//
Reass->ContigEnd->Next = ThisShim; Reass->ContigEnd = ThisShim; Reass->Marker += ThisShim->Len;
//
// Remove it from non-contiguous list.
//
*MoveShim = ThisShim->Next; ThisShim->Next = NULL; } else if (ThisShim->Offset > Reass->Marker) { //
// This fragment lies beyond the contiguous list.
// Because the gap list is sorted, we can stop now.
//
break; } else { //
// This fragment overlaps the contiguous list.
// For DoS prevention, drop the reassembly.
//
BadFragmentOverlap: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "FragmentReceive: overlapping fragment\n")); DeleteFromReassemblyList(Reass); return IP_PROTOCOL_NONE; } } } else { //
// Check whether the fragment duplicates data already in
// the contiguous list.
//
if (Shim->Offset <= Reass->Marker) { if (Shim->Offset + Shim->Len > Reass->Marker) { //
// We have a partial overlap.
//
ExFreePool(Shim); goto BadFragmentOverlap; }
//
// We already have all the data. Don't bother distinguishing
// between an exact duplicate and a partial overlap, just
// ignore the new fragment.
//
goto Duplicate; } //
// Exile this fragment to the non-contiguous (gap) list.
// The gap list is sorted by Offset.
//
MoveShim = &Reass->GapList; for (;;) { ThisShim = *MoveShim; if (ThisShim == NULL) { //
// Insert Shim at the end of the gap list.
//
Reass->MaxGap = Shim->Offset + Shim->Len; break; }
if (Shim->Offset < ThisShim->Offset) { //
// Check for partial overlap.
//
if (Shim->Offset + Shim->Len > ThisShim->Offset) { ExFreePool(Shim); goto BadFragmentOverlap; }
//
// OK, insert Shim before ThisShim.
//
break; } else if (ThisShim->Offset < Shim->Offset) { //
// Check for partial overlap.
//
if (ThisShim->Offset + ThisShim->Len > Shim->Offset) { ExFreePool(Shim); goto BadFragmentOverlap; }
//
// OK, insert Shim somewhere after ThisShim.
// Keep looking for the right spot.
//
MoveShim = &ThisShim->Next; } else { //
// If the new fragment duplicates the old,
// then just ignore the new fragment.
//
if (Shim->Len == ThisShim->Len) { Duplicate: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "FragmentReceive: duplicate fragment\n")); ExFreePool(Shim); KeReleaseSpinLockFromDpcLevel(&Reass->Lock); return IP_PROTOCOL_NONE; } else { ExFreePool(Shim); goto BadFragmentOverlap; } } }
Shim->Next = *MoveShim; *MoveShim = Shim; }
//
// Now that we have added the shim to the reassembly record
// and passed various checks (particularly DoS checks),
// copy the actual fragment data to the shim.
//
CopyPacketToBuffer(PacketShimData(Shim), Packet, Packet->TotalSize, Packet->Position);
if (Reass->Marker == Reass->DataLength) { //
// We have received all the fragments.
// Because of the overlapping/data-length/zero-size sanity checks
// above, when this happens there should be no fragments
// left on the gap list. However, ReassembleDatagram does not
// rely on having an empty gap list.
//
ASSERT(Reass->GapList == NULL); ReassembleDatagram(Packet, Reass); } else { //
// Finally, check if we're too close to our limit for
// reassembly buffers. If so, drop this packet. Otherwise,
// wait for more fragments to arrive.
//
CheckReassemblyQuota(Reass); } return IP_PROTOCOL_NONE;
Failed: IPSInfo.ipsi_reasmfails++; return IP_PROTOCOL_NONE; }
//* FragmentLookup - look for record of previous fragments from this datagram.
//
// A datagram on an interface is uniquely identified by its
// {source address, destination address, identification field} triple.
// This function checks our reassembly list for previously
// received fragments of a given datagram.
//
// If an existing reassembly record is found,
// it is returned locked.
//
// If there is no existing reassembly record, returns NULL
// and leaves the global reassembly list locked.
//
// Callable from DPC context, not from thread context.
//
Reassembly * FragmentLookup( Interface *IF, // Receiving interface.
ulong Id, // Fragment identification field to match.
const IPv6Addr *Src, // Source address to match.
const IPv6Addr *Dst) // Destination address to match.
{ Reassembly *Reass;
KeAcquireSpinLockAtDpcLevel(&ReassemblyList.Lock);
for (Reass = ReassemblyList.First;; Reass = Reass->Next) { if (Reass == SentinelReassembly) { //
// Return with the global reassembly list lock still held.
//
return NULL; }
if ((Reass->IF == IF) && (Reass->Id == Id) && IP6_ADDR_EQUAL(&Reass->IPHdr.Source, Src) && IP6_ADDR_EQUAL(&Reass->IPHdr.Dest, Dst)) { //
// Is this reassembly record being deleted?
// If so, ignore it.
//
KeAcquireSpinLockAtDpcLevel(&Reass->Lock); ASSERT((Reass->State == REASSEMBLY_STATE_NORMAL) || (Reass->State == REASSEMBLY_STATE_DELETING));
if (Reass->State == REASSEMBLY_STATE_DELETING) { KeReleaseSpinLockFromDpcLevel(&Reass->Lock); continue; }
//
// Return with the reassembly record lock still held.
//
KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock); return Reass; } } }
//* AddToReassemblyList
//
// Add the reassembly record to the list.
// It must NOT already be on the list.
//
// Called with the global reassembly list lock held.
// Returns with the reassembly record lock held.
//
// Callable from DPC context, not from thread context.
//
void AddToReassemblyList(Reassembly *Reass) { Reassembly *AfterReass = SentinelReassembly;
Reass->Prev = AfterReass; (Reass->Next = AfterReass->Next)->Prev = Reass; AfterReass->Next = Reass;
KeAcquireSpinLockAtDpcLevel(&ReassemblyList.LockSize); ReassemblyList.Size += Reass->Size; KeReleaseSpinLockFromDpcLevel(&ReassemblyList.LockSize);
//
// We must acquire the reassembly record lock
// *before* releasing the global reassembly list lock,
// to prevent the reassembly from diappearing underneath us.
//
KeAcquireSpinLockAtDpcLevel(&Reass->Lock); KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock); }
//* RemoveReassembly
//
// Remove a reassembly record from the list.
//
// Called with the global reassembly lock held.
// The reassembly record lock may be held.
//
void RemoveReassembly(Reassembly *Reass) { Reass->Prev->Next = Reass->Next; Reass->Next->Prev = Reass->Prev;
KeAcquireSpinLockAtDpcLevel(&ReassemblyList.LockSize); ReassemblyList.Size -= Reass->Size; KeReleaseSpinLockFromDpcLevel(&ReassemblyList.LockSize); }
//* IncreaseReassemblySize
//
// Increase the size of the reassembly record.
// Called with the reassembly record lock held.
//
// Callable from DPC context, not from thread context.
//
void IncreaseReassemblySize(Reassembly *Reass, uint Size) { Reass->Size += Size; KeAcquireSpinLockAtDpcLevel(&ReassemblyList.LockSize); ReassemblyList.Size += Size; KeReleaseSpinLockFromDpcLevel(&ReassemblyList.LockSize); }
//* DeleteReassembly
//
// Delete a reassembly record.
//
void DeleteReassembly(Reassembly *Reass) { PacketShim *ThisShim, *PrevShim;
//
// Free ContigList if populated.
//
PrevShim = ThisShim = Reass->ContigList; while (ThisShim != NULL) { PrevShim = ThisShim; ThisShim = ThisShim->Next; ExFreePool(PrevShim); }
//
// Free GapList if populated.
//
PrevShim = ThisShim = Reass->GapList; while (ThisShim != NULL) { PrevShim = ThisShim; ThisShim = ThisShim->Next; ExFreePool(PrevShim); }
//
// Free unfragmentable data.
//
if (Reass->UnfragData != NULL) ExFreePool(Reass->UnfragData);
ExFreePool(Reass); }
//* DeleteFromReassemblyList
//
// Remove and delete the reassembly record.
// The reassembly record MUST be on the list.
//
// Callable from DPC context, not from thread context.
// Called with the reassembly record lock held,
// but not the global reassembly list lock.
//
void DeleteFromReassemblyList(Reassembly *Reass) { //
// Mark the reassembly as being deleted.
// This will prevent someone else from freeing it.
//
ASSERT(Reass->State == REASSEMBLY_STATE_NORMAL); Reass->State = REASSEMBLY_STATE_DELETING; KeReleaseSpinLockFromDpcLevel(&Reass->Lock);
KeAcquireSpinLockAtDpcLevel(&ReassemblyList.Lock); KeAcquireSpinLockAtDpcLevel(&Reass->Lock); ASSERT((Reass->State == REASSEMBLY_STATE_DELETING) || (Reass->State == REASSEMBLY_STATE_REMOVED));
//
// Remove the reassembly record from the list,
// if someone else hasn't already removed it.
//
if (Reass->State != REASSEMBLY_STATE_REMOVED) RemoveReassembly(Reass);
KeReleaseSpinLockFromDpcLevel(&Reass->Lock); KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock);
//
// Delete the reassembly record.
//
DeleteReassembly(Reass); }
//* CheckReassemblyQuota
//
// Delete reassembly record if necessary,
// to keep the reassembly buffering under quota.
//
// Callable from DPC context, not from thread context.
// Called with the reassembly record lock held,
// but not the global reassembly list lock.
//
void CheckReassemblyQuota(Reassembly *Reass) { int Prune = FALSE; uint Threshold = ReassemblyList.Limit / 2;
//
// Decide whether to drop the reassembly record based on a RED-like
// algorithm. If the total size is less than 50% of the max, never
// drop. If the total size is over the max, always drop. If between
// 50% and 100% full, drop based on a probability proportional to the
// amount over 50%. This is an O(1) algorithm which is proportionally
// biased against large packets, and against sources which send more
// packets. This should provide a decent level of protection against
// DoS attacks.
//
KeAcquireSpinLockAtDpcLevel(&ReassemblyList.LockSize); if ((ReassemblyList.Size > Threshold) && (RandomNumber(0, Threshold) < ReassemblyList.Size - Threshold)) Prune = TRUE; KeReleaseSpinLockFromDpcLevel(&ReassemblyList.LockSize);
if (Prune) { //
// Delete this reassembly record.
// We do not send ICMP errors in this situation.
// The reassembly timer has not expired.
// This is more analogous to a router dropping packets
// when a queue gets full, and no ICMP error is sent
// in that situation.
//
#if DBG
char Buffer1[INET6_ADDRSTRLEN], Buffer2[INET6_ADDRSTRLEN];
FormatV6AddressWorker(Buffer1, &Reass->IPHdr.Source); FormatV6AddressWorker(Buffer2, &Reass->IPHdr.Dest); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "CheckReassemblyQuota: Src %s Dst %s Id %x\n", Buffer1, Buffer2, Reass->Id)); #endif
DeleteFromReassemblyList(Reass); } else KeReleaseSpinLockFromDpcLevel(&Reass->Lock); }
typedef struct ReassembledReceiveContext { WORK_QUEUE_ITEM WQItem; IPv6Packet Packet; uchar Data[]; } ReassembledReceiveContext;
//* ReassembledReceive
//
// Receive a reassembled packet.
// This function is called from a kernel worker thread context.
// It prevents "reassembly recursion".
//
void ReassembledReceive(PVOID Context) { ReassembledReceiveContext *rrc = (ReassembledReceiveContext *) Context; KIRQL Irql; int PktRefs;
//
// All receive processing normally happens at DPC level,
// so we must pretend to be a DPC, so we raise IRQL.
// (System worker threads typically run at PASSIVE_LEVEL).
//
KeRaiseIrql(DISPATCH_LEVEL, &Irql); PktRefs = IPv6Receive(&rrc->Packet); ASSERT(PktRefs == 0); KeLowerIrql(Irql); ExFreePool(rrc); }
//* ReassembleDatagram - put all the fragments together.
//
// Called when we have all the fragments to complete a datagram.
// Patch them together and pass the packet up.
//
// We allocate a single contiguous buffer and copy the fragments
// into this buffer.
// REVIEW: Instead use ndis buffers to chain the fragments?
//
// Callable from DPC context, not from thread context.
// Called with the reassembly record lock held,
// but not the global reassembly list lock.
//
// Deletes the reassembly record.
//
void ReassembleDatagram( IPv6Packet *Packet, // The packet being currently received.
Reassembly *Reass) // Reassembly record for fragmented datagram.
{ uint DataLen; uint TotalLength; uint memptr = sizeof(IPv6Header); PacketShim *ThisShim, *PrevShim; ReassembledReceiveContext *rrc; IPv6Packet *ReassPacket; uchar *ReassBuffer; uchar *pNextHeader;
DataLen = Reass->DataLength + Reass->UnfragmentLength; ASSERT(DataLen <= MAX_IPv6_PAYLOAD); TotalLength = sizeof(IPv6Header) + DataLen;
//
// Allocate memory for buffer and copy fragment data into it.
// At the same time we allocate space for context information
// and an IPv6 packet structure.
//
rrc = ExAllocatePoolWithTagPriority( NonPagedPool, sizeof *rrc + TotalLength, IP6_TAG, LowPoolPriority); if (rrc == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "ReassembleDatagram: Couldn't allocate memory!?!\n")); DeleteFromReassemblyList(Reass); IPSInfo.ipsi_reasmfails++; return; }
//
// We must take a reference on the interface before
// DeleteFromReassemblyList releases the record lock.
//
ReassPacket = &rrc->Packet; ReassBuffer = rrc->Data;
//
// Generate the original IP hdr and copy it and any unfragmentable
// data into the new packet. Note we have to update the next header
// field in the last unfragmentable header (or the IP hdr, if none).
//
Reass->IPHdr.PayloadLength = net_short((ushort)DataLen); RtlCopyMemory(ReassBuffer, (uchar *)&Reass->IPHdr, sizeof(IPv6Header));
RtlCopyMemory(ReassBuffer + memptr, Reass->UnfragData, Reass->UnfragmentLength); memptr += Reass->UnfragmentLength;
pNextHeader = ReassBuffer + Reass->NextHeaderOffset; ASSERT(*pNextHeader == IP_PROTOCOL_FRAGMENT); *pNextHeader = Reass->NextHeader;
//
// Run through the contiguous list, copying data over to our new packet.
//
PrevShim = ThisShim = Reass->ContigList; while(ThisShim != NULL) { RtlCopyMemory(ReassBuffer + memptr, PacketShimData(ThisShim), ThisShim->Len); memptr += ThisShim->Len; if (memptr > TotalLength) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "ReassembleDatagram: packets don't add up\n")); } PrevShim = ThisShim; ThisShim = ThisShim->Next;
ExFreePool(PrevShim); }
//
// Initialize the reassembled packet structure.
//
RtlZeroMemory(ReassPacket, sizeof *ReassPacket); AddRefIF(Reass->IF); ReassPacket->NTEorIF = CastFromIF(Reass->IF); ReassPacket->FlatData = ReassBuffer; ReassPacket->Data = ReassBuffer; ReassPacket->ContigSize = TotalLength; ReassPacket->TotalSize = TotalLength; ReassPacket->Flags = PACKET_HOLDS_REF | PACKET_REASSEMBLED | (Reass->Flags & PACKET_INHERITED_FLAGS);
//
// Explicitly null out the ContigList which was freed above and
// clean up the reassembly struct. This also drops our lock
// on the reassembly struct.
//
Reass->ContigList = NULL; DeleteFromReassemblyList(Reass);
IPSInfo.ipsi_reasmoks++;
//
// Receive the reassembled packet.
// If the current fragment was reassembled,
// then we should avoid another level of recursion.
// We must prevent "reassembly recursion".
// Test both paths in checked builds.
//
if ((Packet->Flags & PACKET_REASSEMBLED) #if DBG
|| ((int)Random() < 0) #endif
) { ExInitializeWorkItem(&rrc->WQItem, ReassembledReceive, rrc); ExQueueWorkItem(&rrc->WQItem, CriticalWorkQueue); } else { int PktRefs = IPv6Receive(ReassPacket); ASSERT(PktRefs == 0); UNREFERENCED_PARAMETER(PktRefs); ExFreePool(rrc); } }
//* CreateFragmentPacket
//
// Recreates the first fragment packet for purposes of notifying a source
// of a 'fragment reassembly time exceeded'.
//
IPv6Packet * CreateFragmentPacket( Reassembly *Reass) { PacketShim *FirstFrag; IPv6Packet *Packet; FragmentHeader *FragHdr; uint PayloadLength; uint PacketLength; uint MemLen; uchar *Mem;
//
// There must be a first (offset-zero) fragment.
//
FirstFrag = Reass->ContigList; ASSERT((FirstFrag != NULL) && (FirstFrag->Offset == 0));
//
// Allocate memory for creating the first fragment, i.e. the first
// buffer in our contig list. We include space for an IPv6Packet.
//
PayloadLength = (Reass->UnfragmentLength + sizeof(FragmentHeader) + FirstFrag->Len); PacketLength = sizeof(IPv6Header) + PayloadLength; MemLen = sizeof(IPv6Packet) + PacketLength; Mem = ExAllocatePoolWithTagPriority(NonPagedPool, MemLen, IP6_TAG, LowPoolPriority); if (Mem == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "CreateFragmentPacket: Couldn't allocate memory!?!\n")); return NULL; }
Packet = (IPv6Packet *) Mem; Mem += sizeof(IPv6Packet);
Packet->Next = NULL; Packet->IP = (IPv6Header UNALIGNED *) Mem; Packet->IPPosition = 0; Packet->Data = Packet->FlatData = Mem; Packet->Position = 0; Packet->ContigSize = Packet->TotalSize = PacketLength; Packet->NdisPacket = NULL; Packet->AuxList = NULL; Packet->Flags = 0; Packet->SrcAddr = AlignAddr(&Packet->IP->Source); Packet->SAPerformed = NULL; // Our caller must initialize Packet->NTEorIF.
AdjustPacketParams(Packet, sizeof(IPv6Header));
//
// Copy the original IPv6 header into the packet.
// Note that FragmentReceive ensures that
// Reass->IPHdr, Reass->UnfragData, and FirstFrag
// are all consistent.
//
RtlCopyMemory(Mem, (uchar *)&Reass->IPHdr, sizeof(IPv6Header)); Mem += sizeof(IPv6Header);
ASSERT(Reass->IPHdr.PayloadLength == net_short((ushort)PayloadLength));
//
// Copy the unfragmentable data into the packet.
//
RtlCopyMemory(Mem, Reass->UnfragData, Reass->UnfragmentLength); Mem += Reass->UnfragmentLength;
//
// Create a fragment header in the packet.
//
FragHdr = (FragmentHeader *) Mem; Mem += sizeof(FragmentHeader);
//
// Note that if the original offset-zero fragment had
// a non-zero value in the Reserved field, then we will
// not recreate it properly. It shouldn't do that.
//
FragHdr->NextHeader = Reass->NextHeader; FragHdr->Reserved = 0; FragHdr->OffsetFlag = net_short(FRAGMENT_FLAG_MASK); FragHdr->Id = Reass->Id;
//
// Copy the original fragment data into the packet.
//
RtlCopyMemory(Mem, PacketShimData(FirstFrag), FirstFrag->Len);
return Packet; }
//* ReassemblyTimeout - Handle a reassembly timer event.
//
// This routine is called periodically by IPv6Timeout to check for
// timed out fragments.
//
void ReassemblyTimeout(void) { Reassembly *ThisReass, *NextReass; Reassembly *Expired = NULL;
//
// Scan the ReassemblyList checking for expired reassembly contexts.
//
KeAcquireSpinLockAtDpcLevel(&ReassemblyList.Lock); for (ThisReass = ReassemblyList.First; ThisReass != SentinelReassembly; ThisReass = NextReass) { NextReass = ThisReass->Next;
//
// First decrement the timer then check if it has expired. If so,
// remove the reassembly record. This is basically the same code
// as in DeleteFromReassemblyList().
//
ThisReass->Timer--;
if (ThisReass->Timer == 0) { RemoveReassembly(ThisReass);
KeAcquireSpinLockAtDpcLevel(&ThisReass->Lock); ASSERT((ThisReass->State == REASSEMBLY_STATE_NORMAL) || (ThisReass->State == REASSEMBLY_STATE_DELETING));
if (ThisReass->State == REASSEMBLY_STATE_DELETING) { //
// Note that we've removed it from the list already.
//
ThisReass->State = REASSEMBLY_STATE_REMOVED; } else { //
// Move this reassembly context to the expired list.
// We must take a reference on the interface
// before releasing the reassembly record lock.
//
AddRefIF(ThisReass->IF); ThisReass->Next = Expired; Expired = ThisReass; } KeReleaseSpinLockFromDpcLevel(&ThisReass->Lock); } } KeReleaseSpinLockFromDpcLevel(&ReassemblyList.Lock);
//
// Now that we no longer need the reassembly list lock,
// we can send ICMP errors at our leisure.
//
while ((ThisReass = Expired) != NULL) { Interface *IF = ThisReass->IF; #if DBG
char Buffer1[INET6_ADDRSTRLEN], Buffer2[INET6_ADDRSTRLEN];
FormatV6AddressWorker(Buffer1, &ThisReass->IPHdr.Source); FormatV6AddressWorker(Buffer2, &ThisReass->IPHdr.Dest); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "ReassemblyTimeout: Src %s Dst %s Id %x\n", Buffer1, Buffer2, ThisReass->Id)); #endif
Expired = ThisReass->Next;
//
// Send ICMP error IF we have received the first fragment.
// NB: Checking Marker != 0 is wrong, because we might have
// received a zero-length first fragment.
//
if (ThisReass->ContigList != NULL) { IPv6Packet *Packet;
Packet = CreateFragmentPacket(ThisReass); if (Packet != NULL) { NetTableEntryOrInterface *NTEorIF; ushort Type;
NTEorIF = FindAddressOnInterface(IF, &ThisReass->IPHdr.Dest, &Type); if (NTEorIF != NULL) { Packet->NTEorIF = NTEorIF;
ICMPv6SendError(Packet, ICMPv6_TIME_EXCEEDED, ICMPv6_REASSEMBLY_TIME_EXCEEDED, 0, Packet->IP->NextHeader, FALSE);
if (IsNTE(NTEorIF)) ReleaseNTE(CastToNTE(NTEorIF)); else ReleaseIF(CastToIF(NTEorIF)); }
ExFreePool(Packet); } }
//
// Delete the reassembly record.
//
ReleaseIF(IF); DeleteReassembly(ThisReass); } }
//* DestinationOptionsReceive - Handle IPv6 Destination options.
//
// This is the routine called to process a Destination Options Header,
// a next header value of 60.
//
uchar DestinationOptionsReceive( IPv6Packet *Packet) // Packet handed to us by IPv6Receive.
{ IPv6OptionsHeader *DestOpt; uint ExtLen; Options Opts;
//
// Verify that we have enough contiguous data to overlay a Destination
// Options Header structure on the incoming packet. Then do so.
//
if (! PacketPullup(Packet, sizeof *DestOpt, __builtin_alignof(IPv6OptionsHeader), 0)) { if (Packet->TotalSize < sizeof *DestOpt) { BadPayloadLength: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "DestinationOptionsReceive: Incoming packet too small" " to contain destination options header\n")); ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, FIELD_OFFSET(IPv6Header, PayloadLength), IP_PROTOCOL_NONE, FALSE); } return IP_PROTOCOL_NONE; // Drop packet.
} DestOpt = (IPv6OptionsHeader *) Packet->Data;
//
// Check that length of destination options also fit in remaining data.
// The options must also be aligned for any addresses in them.
//
ExtLen = (DestOpt->HeaderExtLength + 1) * EXT_LEN_UNIT; if (! PacketPullup(Packet, ExtLen, MAX(__builtin_alignof(IPv6OptionsHeader), __builtin_alignof(IPv6Addr)), 0)) { if (Packet->TotalSize < ExtLen) goto BadPayloadLength; return IP_PROTOCOL_NONE; // Drop packet.
} DestOpt = (IPv6OptionsHeader *) Packet->Data;
//
// Remember offset to this header's NextHeader field.
//
Packet->NextHeaderPosition = Packet->Position + FIELD_OFFSET(IPv6OptionsHeader, NextHeader);
//
// Skip over the extension header.
// We need to do this now so subsequent ICMP error generation works.
//
AdjustPacketParams(Packet, ExtLen);
//
// Parse options in this extension header. If an error occurs
// while parsing the options, discard packet.
//
if (!ParseOptions(Packet, IP_PROTOCOL_DEST_OPTS, DestOpt, ExtLen, &Opts)) { return IP_PROTOCOL_NONE; // Drop packet.
}
//
// The processing of any additional options should be added here,
// before the home address option.
//
//
// Process the home address option.
//
if (Opts.HomeAddress) { if (IPv6RecvHomeAddress(Packet, Opts.HomeAddress)) { //
// Couldn't process the home address option. Drop the packet.
//
return IP_PROTOCOL_NONE; } }
//
// Process binding update option.
//
// Note that the Mobile IP spec says that the effects of processing the
// Home Address option should not be visible until all other options in
// the same Destination Options header have been processed. Although
// we process the Binding Update option after the Home Address option,
// we achieve the same effect by requiring IPv6RecvBindingUpdate to
// know that the Packet->SrcAddr has already been updated.
//
if (Opts.BindingUpdate) { if (IPv6RecvBindingUpdate(Packet, Opts.BindingUpdate)) { //
// Couldn't process the binding update. Drop the packet.
//
return IP_PROTOCOL_NONE; } }
//
// Return next header value.
//
return DestOpt->NextHeader; }
//* HopByHopOptionsReceive - Handle a IPv6 Hop-by-Hop Options.
//
// This is the routine called to process a Hop-by-Hop Options Header,
// next header value of 0.
//
// Note that this routine is not a normal handler in the Protocol Switch
// Table. Instead, it receives special treatment in IPv6HeaderReceive.
// Because of this, it returns -1 instead of IP_PROTOCOL_NONE on error.
//
int HopByHopOptionsReceive( IPv6Packet *Packet) // Packet handed to us by IPv6Receive.
{ IPv6OptionsHeader *HopByHop; uint ExtLen; Options Opts;
//
// Verify that we have enough contiguous data to overlay a minimum
// length Hop-by-Hop Options Header. Then do so.
//
if (! PacketPullup(Packet, sizeof *HopByHop, __builtin_alignof(IPv6OptionsHeader), 0)) { if (Packet->TotalSize < sizeof *HopByHop) { BadPayloadLength: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "HopByHopOptionsReceive: Incoming packet too small" " to contain Hop-by-Hop Options header\n")); ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, FIELD_OFFSET(IPv6Header, PayloadLength), IP_PROTOCOL_NONE, FALSE); } return -1; // Drop packet.
} HopByHop = (IPv6OptionsHeader *) Packet->Data;
//
// Check that length of the Hop-by-Hop options also fits in remaining data.
// The options must also be aligned for any addresses in them.
//
ExtLen = (HopByHop->HeaderExtLength + 1) * EXT_LEN_UNIT; if (! PacketPullup(Packet, ExtLen, MAX(__builtin_alignof(IPv6OptionsHeader), __builtin_alignof(IPv6Addr)), 0)) { if (Packet->TotalSize < ExtLen) goto BadPayloadLength; return -1; // Drop packet.
} HopByHop = (IPv6OptionsHeader *) Packet->Data;
//
// Remember offset to this header's NextHeader field.
//
Packet->NextHeaderPosition = Packet->Position + FIELD_OFFSET(IPv6OptionsHeader, NextHeader);
//
// Skip over the extension header.
// We need to do this now so subsequent ICMP error generation works.
//
AdjustPacketParams(Packet, ExtLen);
//
// Parse options in this extension header. If an error occurs
// while parsing the options, discard packet.
//
if (!ParseOptions(Packet, IP_PROTOCOL_HOP_BY_HOP, HopByHop, ExtLen, &Opts)) { return -1; // Drop packet.
}
//
// If we have a valid Jumbo Payload Option, use its value as
// the packet PayloadLength.
//
if (Opts.JumboLength) { uint PayloadLength = Opts.JumboLength;
ASSERT(Packet->IP->PayloadLength == 0);
//
// Check that the jumbo length is big enough to include
// the extension header length. This must be true because
// the extension-header length is at most 11 bits,
// while the jumbo length is at least 16 bits.
//
ASSERT(PayloadLength > ExtLen); PayloadLength -= ExtLen;
//
// Check that the amount of payload specified in the Jumbo
// Payload value fits in the buffer handed to us.
//
if (PayloadLength > Packet->TotalSize) { // Silently discard data.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "HopByHopOptionsReceive: " "Jumbo payload length too big\n")); return -1; }
//
// As in IPv6HeaderReceive, adjust the TotalSize to be exactly the
// IP payload size (assume excess is media padding).
//
Packet->TotalSize = PayloadLength; if (Packet->ContigSize > PayloadLength) Packet->ContigSize = PayloadLength;
//
// Set the jumbo option packet flag.
//
Packet->Flags |= PACKET_JUMBO_OPTION; } else if (Packet->IP->PayloadLength == 0) { //
// We should have a Jumbo Payload option,
// but we didn't find it. Send an ICMP error.
//
ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, FIELD_OFFSET(IPv6Header, PayloadLength), HopByHop->NextHeader, FALSE); return -1; }
//
// Return next header value.
//
return HopByHop->NextHeader; }
//* ParseOptions - Routine for generic header options parsing.
//
// Returns TRUE if the options were successfully parsed.
// Returns FALSE if the packet should be discarded.
//
int ParseOptions( IPv6Packet *Packet, // The packet handed to us by IPv6Receive.
uchar HdrType, // Hop-by-hop or destination.
IPv6OptionsHeader *Hdr, // Header with following data.
uint HdrLength, // Length of the entire options area.
Options *Opts) // Return option values to caller.
{ uchar *OptPtr; uint OptSizeLeft; OptionHeader *OptHdr; uint OptLen;
ASSERT((HdrType == IP_PROTOCOL_DEST_OPTS) || (HdrType == IP_PROTOCOL_HOP_BY_HOP));
//
// Zero out the Options struct that is returned.
//
RtlZeroMemory(Opts, sizeof *Opts);
//
// Skip over the extension header.
//
OptPtr = (uchar *)(Hdr + 1); OptSizeLeft = HdrLength - sizeof *Hdr;
//
// Note that if there are multiple options
// of the same type, we just use the last one encountered
// unless the spec says specifically it is an error.
//
while (OptSizeLeft > 0) {
//
// First we check the option length and ensure that it fits.
// We move OptPtr past this option while leaving OptHdr
// for use by the option processing code below.
//
OptHdr = (OptionHeader *) OptPtr; if (OptHdr->Type == OPT6_PAD_1) { //
// This is a special pad option which is just a one byte field,
// i.e. it has no length or data field.
//
OptLen = 1; } else { //
// This is a multi-byte option.
//
if ((sizeof *OptHdr > OptSizeLeft) || ((OptLen = sizeof *OptHdr + OptHdr->DataLength) > OptSizeLeft)) { //
// Bad length, generate error and discard packet.
//
ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, &Hdr->HeaderExtLength) - Packet->IPPosition), Hdr->NextHeader, FALSE); return FALSE; } } OptPtr += OptLen; OptSizeLeft -= OptLen;
switch (OptHdr->Type) { case OPT6_PAD_1: case OPT6_PAD_N: break;
case OPT6_JUMBO_PAYLOAD: if (HdrType != IP_PROTOCOL_HOP_BY_HOP) goto BadOptionType;
if (OptHdr->DataLength != sizeof Opts->JumboLength) goto BadOptionLength;
if (Packet->IP->PayloadLength != 0) { //
// Jumbo option encountered when IP payload is not zero.
// Send ICMP error, set pointer to offset of this option type.
//
goto BadOptionType; }
Opts->JumboLength = net_long(*(ulong UNALIGNED *)(OptHdr + 1)); if (Opts->JumboLength <= MAX_IPv6_PAYLOAD) { //
// Jumbo payload length is not jumbo, send ICMP error.
// ICMP pointer is set to offset of jumbo payload len field.
//
goto BadOptionData; } break;
case OPT6_ROUTER_ALERT: if (HdrType != IP_PROTOCOL_HOP_BY_HOP) goto BadOptionType;
if (OptLen != sizeof *Opts->Alert) goto BadOptionLength;
if (Opts->Alert != NULL) { //
// Can only have one router alert option.
//
goto BadOptionType; }
//
// Return the pointer to the router alert struct.
//
Opts->Alert = (IPv6RouterAlertOption UNALIGNED *)(OptHdr + 1); break;
case OPT6_HOME_ADDRESS: if (!(MobileIPv6Mode & MOBILE_CORRESPONDENT)) goto BadOptionType;
if (HdrType != IP_PROTOCOL_DEST_OPTS) goto BadOptionType;
if (OptLen < sizeof *Opts->HomeAddress) goto BadOptionLength;
//
// Return the pointer to the home address option
// after checking to make sure the address is reasonable.
// The option must be aligned so that the home address
// is appropriately aligned.
//
Opts->HomeAddress = (IPv6HomeAddressOption UNALIGNED *)OptHdr; if (((UINT_PTR)&Opts->HomeAddress->HomeAddress % __builtin_alignof(IPv6Addr)) != 0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "ParseOptions: misaligned home address\n")); goto BadOptionType; } if (IsInvalidSourceAddress(AlignAddr(&Opts->HomeAddress->HomeAddress)) || IsUnspecified(AlignAddr(&Opts->HomeAddress->HomeAddress)) || IsLoopback(AlignAddr(&Opts->HomeAddress->HomeAddress))) { //
// Address contained in option is invalid.
// Send ICMP error, set pointer to offset of home address.
//
goto BadOptionData; } break;
case OPT6_BINDING_UPDATE: if (!(MobileIPv6Mode & MOBILE_CORRESPONDENT)) goto BadOptionType;
if (HdrType != IP_PROTOCOL_DEST_OPTS) goto BadOptionType;
//
// At a minimum, the binding update must include all of the
// base header fields.
//
if (OptLen < sizeof(IPv6BindingUpdateOption)) { //
// draft-ietf-mobileip-ipv6-13 sec 8.2 says we must
// silently drop the packet. Normally we would
// goto BadOptionLength to send an ICMP error.
//
return FALSE; }
//
// Save pointer to the binding update option. Note we still
// need to do further length checking.
//
Opts->BindingUpdate = (IPv6BindingUpdateOption UNALIGNED *)OptHdr; break;
default: if (OPT6_ACTION(OptHdr->Type) == OPT6_A_SKIP) { //
// Ignore the unrecognized option.
//
break; } else if (OPT6_ACTION(OptHdr->Type) == OPT6_A_DISCARD) { //
// Discard the packet.
//
return FALSE; } else { //
// Send an ICMP error.
//
ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_UNRECOGNIZED_OPTION, (GetPacketPositionFromPointer(Packet, &OptHdr->Type) - Packet->IPPosition), Hdr->NextHeader, OPT6_ACTION(OptHdr->Type) == OPT6_A_SEND_ICMP_ALL); return FALSE; // discard the packet.
} } }
return TRUE;
BadOptionType: ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, &OptHdr->Type) - Packet->IPPosition), Hdr->NextHeader, FALSE); return FALSE; // discard packet.
BadOptionLength: ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, &OptHdr->DataLength) - Packet->IPPosition), Hdr->NextHeader, FALSE); return FALSE; // discard packet.
BadOptionData: ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, (uchar *)(OptHdr + 1)) - Packet->IPPosition), Hdr->NextHeader, FALSE); return FALSE; // discard packet.
}
//* ExtHdrControlReceive - generic extension header skip-over routine.
//
// Routine for processing the extension headers in an ICMP error message
// before delivering the error message to the upper-layer protocol.
//
uchar ExtHdrControlReceive( IPv6Packet *Packet, // Packet handed to us by ICMPv6ErrorReceive.
StatusArg *StatArg) // ICMP Error code and offset pointer.
{ uchar NextHdr = StatArg->IP->NextHeader; uint HdrLen;
for (;;) { switch (NextHdr) { case IP_PROTOCOL_HOP_BY_HOP: case IP_PROTOCOL_DEST_OPTS: case IP_PROTOCOL_ROUTING: { ExtensionHeader *ExtHdr; // Generic exension header.
//
// Here we take advantage of the fact that all of these extension
// headers share the same first two fields (except as noted below).
// Since those two fields (Next Header and Header Extension Length)
// provide us with all the information we need to skip over the
// header, they're all we need to look at here.
//
if (! PacketPullup(Packet, sizeof *ExtHdr, __builtin_alignof(ExtensionHeader), 0)) { if (Packet->TotalSize < sizeof *ExtHdr) { PacketTooSmall: //
// Pullup failed. There isn't enough of the invoking
// packet included in the error message to figure out
// what upper layer protocol it originated with.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "ExtHdrControlReceive: " "Incoming ICMP error packet " "doesn't contain enough of invoking packet\n")); } return IP_PROTOCOL_NONE; // Drop packet.
}
ExtHdr = (ExtensionHeader *) Packet->Data; HdrLen = (ExtHdr->HeaderExtLength + 1) * EXT_LEN_UNIT;
//
// Now that we know the actual length of this extension header,
// skip over it.
//
// REVIEW: We could rework this to use PositionPacketAt
// REVIEW: here instead of PacketPullup as we don't need to
// REVIEW: look at the data we're skipping over. Better?
//
if (! PacketPullup(Packet, HdrLen, 1, 0)) { if (Packet->TotalSize < HdrLen) goto PacketTooSmall; return IP_PROTOCOL_NONE; // Drop packet.
}
NextHdr = ExtHdr->NextHeader; break; }
case IP_PROTOCOL_FRAGMENT: { FragmentHeader UNALIGNED *FragHdr;
if (! PacketPullup(Packet, sizeof *FragHdr, 1, 0)) { if (Packet->TotalSize < sizeof *FragHdr) goto PacketTooSmall; return IP_PROTOCOL_NONE; // Drop packet.
}
FragHdr = (FragmentHeader UNALIGNED *) Packet->Data;
if ((net_short(FragHdr->OffsetFlag) & FRAGMENT_OFFSET_MASK) != 0) { //
// We can only continue parsing if this
// fragment has offset zero.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "ExtHdrControlReceive: " "non-zero-offset fragment\n")); return IP_PROTOCOL_NONE; }
HdrLen = sizeof *FragHdr; NextHdr = FragHdr->NextHeader; break; }
case IP_PROTOCOL_AH: { AHHeader UNALIGNED *AH;
//
// Skip over Authentication Header proper.
//
if (!PacketPullup(Packet, sizeof(AHHeader), 1, 0)) { if (Packet->TotalSize < sizeof(AHHeader)) goto PacketTooSmall; return IP_PROTOCOL_NONE; // Drop packet.
}
AH = (AHHeader UNALIGNED *)Packet->Data; AdjustPacketParams(Packet, sizeof(AHHeader));
//
// Skip over Authentication Data.
// Note: we don't attempt any verification of this packet.
//
// In the event of a bad packet, HdrLen could underflow here,
// but this is okay since (being unsigned) it will fail the
// subsequent PacketPullup and length check.
//
HdrLen = ((AH->PayloadLen + 2) * 4) - sizeof(AHHeader); if (!PacketPullup(Packet, HdrLen, 1, 0)) { if (Packet->TotalSize < HdrLen) goto PacketTooSmall; return IP_PROTOCOL_NONE; // Drop packet.
}
NextHdr = AH->NextHeader; break; }
case IP_PROTOCOL_ESP: //
// REVIEW - What is the correct thing here?
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "ExtHdrControlReceive: found ESP\n")); return IP_PROTOCOL_NONE;
default: //
// We came to a header that we do not recognize,
// so we can not continue parsing here.
// But our caller might recognize this header type.
//
return NextHdr; }
//
// Move past this extension header.
//
AdjustPacketParams(Packet, HdrLen); } }
//* RoutingReceive - Handle the IPv6 Routing Header.
//
// Called from IPv6Receive when we encounter a Routing Header,
// next header value of 43.
//
uchar RoutingReceive( IPv6Packet *Packet) // Packet handed to us by link layer.
{ IPv6RoutingHeader *RH; uint HeaderLength; uint SegmentsLeft; uint NumAddresses, i; IPv6Addr *Addresses; IP_STATUS Status; uchar *Mem; uint MemLen, Offset; NDIS_PACKET *FwdPacket; NDIS_STATUS NdisStatus; IPv6Header UNALIGNED *FwdIP; IPv6RoutingHeader UNALIGNED *FwdRH; IPv6Addr UNALIGNED *FwdAddresses; IPv6Addr FwdDest; int Delta; uint PayloadLength; uint TunnelStart = NO_TUNNEL, IPSecBytes = 0; IPSecProc *IPSecToDo; RouteCacheEntry *RCE; uint Action;
//
// Verify that we have enough contiguous data,
// then get a pointer to the routing header.
//
if (! PacketPullup(Packet, sizeof *RH, __builtin_alignof(IPv6RoutingHeader), 0)) { if (Packet->TotalSize < sizeof *RH) { BadPayloadLength: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "RoutingReceive: Incoming packet too small" " to contain routing header\n")); ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, FIELD_OFFSET(IPv6Header, PayloadLength), IP_PROTOCOL_NONE, FALSE); } return IP_PROTOCOL_NONE; // Drop packet.
} RH = (IPv6RoutingHeader *) Packet->Data;
//
// Now get the entire routing header.
// Also align for the address array.
//
HeaderLength = (RH->HeaderExtLength + 1) * EXT_LEN_UNIT; if (! PacketPullup(Packet, HeaderLength, MAX(__builtin_alignof(IPv6RoutingHeader), __builtin_alignof(IPv6Addr)), 0)) { if (Packet->TotalSize < HeaderLength) goto BadPayloadLength; return IP_PROTOCOL_NONE; // Drop packet.
} RH = (IPv6RoutingHeader *) Packet->Data;
//
// Remember offset to this header's NextHeader field.
//
Packet->NextHeaderPosition = Packet->Position + FIELD_OFFSET(IPv6RoutingHeader, NextHeader);
//
// Move past the routing header.
// We need to do this now so subsequent ICMP error generation works.
//
AdjustPacketParams(Packet, HeaderLength);
//
// If SegmentsLeft is zero, we proceed directly to the next header.
// We must not check the Type value or HeaderLength.
//
SegmentsLeft = RH->SegmentsLeft; if (SegmentsLeft == 0) { //
// Return next header value.
//
return RH->NextHeader; }
//
// If we do not recognize the Type value, generate an ICMP error.
//
if (RH->RoutingType != 0) { ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, &RH->RoutingType) - Packet->IPPosition), RH->NextHeader, FALSE); return IP_PROTOCOL_NONE; // No further processing of this packet.
}
//
// We must have an integral number of IPv6 addresses
// in the routing header.
//
if (RH->HeaderExtLength & 1) { ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, &RH->HeaderExtLength) - Packet->IPPosition), RH->NextHeader, FALSE); return IP_PROTOCOL_NONE; // No further processing of this packet.
}
NumAddresses = RH->HeaderExtLength / 2;
//
// Sanity check SegmentsLeft.
//
if (SegmentsLeft > NumAddresses) { ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, &RH->SegmentsLeft) - Packet->IPPosition), RH->NextHeader, FALSE); return IP_PROTOCOL_NONE; // No further processing of this packet.
}
//
// Sanity check the destination address.
// Packets carrying a Type 0 Routing Header must not
// be sent to a multicast destination.
//
if (IsMulticast(AlignAddr(&Packet->IP->Dest))) { //
// Just drop the packet, no ICMP error in this case.
//
return IP_PROTOCOL_NONE; // No further processing of this packet.
}
i = NumAddresses - SegmentsLeft; Addresses = AlignAddr((IPv6Addr UNALIGNED *) (RH + 1));
//
// Sanity check the new destination.
// RFC 2460 doesn't mention checking for an unspecified address,
// but I think it's a good idea. Similarly, for security reasons,
// we also check the scope of the destination. This allows
// applications to check the scope of the eventual destination address
// and know that the packet originated within that scope.
// RFC 2460 says to discard the packet without an ICMP error
// (at least when the new destination is multicast),
// but I think an ICMP error is helpful in this situation.
//
if (IsMulticast(&Addresses[i]) || IsUnspecified(&Addresses[i]) || (UnicastAddressScope(&Addresses[i]) < UnicastAddressScope(AlignAddr(&Packet->IP->Dest)))) {
ICMPv6SendError(Packet, ICMPv6_PARAMETER_PROBLEM, ICMPv6_ERRONEOUS_HEADER_FIELD, (GetPacketPositionFromPointer(Packet, (uchar *) &Addresses[i]) - Packet->IPPosition), RH->NextHeader, FALSE); return IP_PROTOCOL_NONE; // No further processing of this packet.
}
//
// Verify IPSec was performed.
//
if (InboundSecurityCheck(Packet, 0, 0, 0, Packet->NTEorIF->IF) != TRUE) { //
// No policy was found or the policy indicated to drop the packet.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "RoutingReceive: " "IPSec lookup failed or policy was to drop\n")); return IP_PROTOCOL_NONE; // Drop packet.
}
//
// Find a route to the new destination.
//
Status = RouteToDestination(&Addresses[i], 0, Packet->NTEorIF, RTD_FLAG_LOOSE, &RCE); if (Status != IP_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "RoutingReceive: " "No route to destination for forwarding.\n")); ICMPv6SendError(Packet, ICMPv6_DESTINATION_UNREACHABLE, ICMPv6_NO_ROUTE_TO_DESTINATION, 0, RH->NextHeader, FALSE); return IP_PROTOCOL_NONE; }
//
// For security reasons, we prevent source routing
// in some situations. Check those now.
//
if (Packet->NTEorIF->IF->Flags & IF_FLAG_FORWARDS) { //
// The interface is forwarding, so source-routing is allowed.
//
} else if ((Packet->NTEorIF->IF == RCE->NCE->IF) && (SegmentsLeft == 1) && IP6_ADDR_EQUAL(&Addresses[i], AlignAddr(&Packet->IP->Source))) { //
// Same-interface rule says source-routing is allowed,
// because the host is not acting as a conduit
// between two networks. See RFC 1122 section 3.3.5.
// Furthermore, we only allow round-trip source-routing
// because that's the only useful scenario that we know of
// for hosts. This prevents unanticipated bad uses.
//
} else { //
// We can not allow this use of source-routing.
// Instead of reporting an error, we could
// redo RouteToDestination with RTD_FLAG_STRICT
// to constrain to the same interface.
// However, an ICMP error is more in keeping
// with the treatment of scoped source addresses,
// which can produce a destination-unreachable error.
//
ReleaseRCE(RCE); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "RoutingReceive: Inappropriate route.\n")); ICMPv6SendError(Packet, ICMPv6_DESTINATION_UNREACHABLE, ICMPv6_COMMUNICATION_PROHIBITED, 0, RH->NextHeader, FALSE); return IP_PROTOCOL_NONE; }
//
// Find the Security Policy for this outbound traffic.
// The source address is the same but the destination address is the
// next hop from the routing header.
//
IPSecToDo = OutboundSPLookup(AlignAddr(&Packet->IP->Source), &Addresses[i], 0, 0, 0, RCE->NCE->IF, &Action);
if (IPSecToDo == NULL) { //
// Check Action.
//
if (Action == LOOKUP_DROP) { // Drop packet.
ReleaseRCE(RCE); return IP_PROTOCOL_NONE; } else { if (Action == LOOKUP_IKE_NEG) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "RoutingReceive: IKE not supported yet.\n")); ReleaseRCE(RCE); return IP_PROTOCOL_NONE; } }
//
// With no IPSec to perform, IPv6Forward won't be changing the
// outgoing interface from what we currently think it will be.
// So we can use the exact size of its link-level header.
//
Offset = RCE->NCE->IF->LinkHeaderSize;
} else { //
// Calculate the space needed for the IPSec headers.
//
IPSecBytes = IPSecBytesToInsert(IPSecToDo, &TunnelStart, NULL);
if (TunnelStart != 0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "RoutingReceive: IPSec Tunnel mode only.\n")); FreeIPSecToDo(IPSecToDo, IPSecToDo->BundleSize); ReleaseRCE(RCE); return IP_PROTOCOL_NONE; }
//
// The IPSec code in IPv6Forward might change the outgoing
// interface from what we currently think it will be. Play it
// safe and leave the max amount of space for its link-level header.
//
Offset = MAX_LINK_HEADER_SIZE; }
//
// The packet has passed all our checks.
// We can construct a revised packet for transmission.
// First we allocate a packet, buffer, and memory.
//
// NB: The original packet is read-only for us. Furthermore
// we can not keep a pointer to it beyond the return of this
// function. So we must copy the packet and then modify it.
//
// Packet->IP->PayloadLength might be zero with jumbograms.
Delta = Packet->Position - Packet->IPPosition; PayloadLength = Packet->TotalSize + Delta - sizeof(IPv6Header); MemLen = Offset + sizeof(IPv6Header) + PayloadLength + IPSecBytes;
NdisStatus = IPv6AllocatePacket(MemLen, &FwdPacket, &Mem); if (NdisStatus != NDIS_STATUS_SUCCESS) { if (IPSecToDo) { FreeIPSecToDo(IPSecToDo, IPSecToDo->BundleSize); } ReleaseRCE(RCE); return IP_PROTOCOL_NONE; // No further processing of this packet.
}
FwdIP = (IPv6Header UNALIGNED *)(Mem + Offset + IPSecBytes); FwdRH = (IPv6RoutingHeader UNALIGNED *) ((uchar *)FwdIP + Delta - HeaderLength); FwdAddresses = (IPv6Addr UNALIGNED *) (FwdRH + 1);
//
// Now we copy from the original packet to the new packet.
//
CopyPacketToBuffer((uchar *)FwdIP, Packet, sizeof(IPv6Header) + PayloadLength, Packet->IPPosition);
//
// Fix up the new packet - put in the new destination address
// and decrement SegmentsLeft.
// NB: We pass the Reserved field through unmodified!
// This violates a strict reading of the spec,
// but Steve Deering has confirmed that this is his intent.
//
FwdDest = *AlignAddr(&FwdAddresses[i]); *AlignAddr(&FwdAddresses[i]) = *AlignAddr(&FwdIP->Dest); *AlignAddr(&FwdIP->Dest) = FwdDest; FwdRH->SegmentsLeft--;
//
// Forward the packet. This decrements the Hop Limit and generates
// any applicable ICMP errors (Time Limit Exceeded, Destination
// Unreachable, Packet Too Big). Note that previous ICMP errors
// that we generated were based on the unmodified incoming packet,
// while from here on the ICMP errors are based on the new FwdPacket.
//
IPv6Forward(Packet->NTEorIF, FwdPacket, Offset + IPSecBytes, FwdIP, PayloadLength, FALSE, // Don't Redirect.
IPSecToDo, RCE);
if (IPSecToDo) { FreeIPSecToDo(IPSecToDo, IPSecToDo->BundleSize); }
ReleaseRCE(RCE); return IP_PROTOCOL_NONE; // No further processing of this packet.
}
|