Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

708 lines
25 KiB

// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// Raw IP interface code. This file contains the code for the raw IP
// interface functions, principally send and receive datagram.
//
#include "oscfg.h"
#include "ndis.h"
#include "ip6imp.h"
#include "ip6def.h"
#include "tdi.h"
#include "tdistat.h"
#include "tdint.h"
#include "tdistat.h"
#include "queue.h"
#include "transprt.h"
#include "addr.h"
#include "raw.h"
#include "info.h"
#include "route.h"
#include "security.h"
//
// TDI_CMSG_SPACE generates the following warning.
//
#pragma warning(disable:4116) // unnamed type definition in parentheses
#define NO_TCP_DEFS 1
#include "tcpdeb.h"
//
// REVIEW: Shouldn't this be in an include file somewhere?
//
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, '6WAR')
#endif // POOL_TAGGING
extern KSPIN_LOCK AddrObjTableLock;
//* RawSend - Send a raw datagram.
//
// The real send datagram routine. We assume that the busy bit is
// set on the input AddrObj, and that the address of the SendReq
// has been verified.
//
// We start by sending the input datagram, and we loop until there's
// nothing left on the send queue.
//
void // Returns: Nothing.
RawSend(
AddrObj *SrcAO, // Address Object of endpoint doing the send.
DGSendReq *SendReq) // Datagram send request describing the send.
{
KIRQL Irql0;
RouteCacheEntry *RCE;
NetTableEntryOrInterface *NTEorIF;
NetTableEntry *NTE;
Interface *IF;
IPv6Header UNALIGNED *IP;
PNDIS_PACKET Packet;
PNDIS_BUFFER RawBuffer;
void *Memory = NULL;
IP_STATUS Status;
NDIS_STATUS NdisStatus;
TDI_STATUS ErrorValue;
uint Offset;
uint HeaderLength;
int Hops;
CHECK_STRUCT(SrcAO, ao);
ASSERT(SrcAO->ao_usecnt != 0);
//
// Loop while we have something to send, and can get
// the resources to send it.
//
for (;;) {
CHECK_STRUCT(SendReq, dsr);
//
// Determine NTE to send on (if user cares).
// We do this prior to allocating packet header buffers so
// we know how much room to leave for the link-level header.
//
if (!IsUnspecified(&SrcAO->ao_addr)) {
//
// We need to get the NTE of this bound address.
//
NTE = FindNetworkWithAddress(&SrcAO->ao_addr, SrcAO->ao_scope_id);
if (NTE == NULL) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"RawSend: Bad source address\n"));
ErrorValue = TDI_INVALID_REQUEST;
ReturnError:
//
// If possible, complete the request with an error.
// Free the request structure.
//
if (SendReq->dsr_rtn != NULL)
(*SendReq->dsr_rtn)(SendReq->dsr_context,
ErrorValue, 0);
KeAcquireSpinLock(&DGSendReqLock, &Irql0);
FreeDGSendReq(SendReq);
KeReleaseSpinLock(&DGSendReqLock, Irql0);
goto SendComplete;
}
} else {
//
// We are not binding to any address.
//
NTE = NULL;
}
NTEorIF = CastFromNTE(NTE);
//
// If this is a multicast packet, check if the application
// has specified an interface. Note that ao_mcast_if
// overrides ao_addr if both are specified and they conflict.
//
if (IsMulticast(&SendReq->dsr_addr) && (SrcAO->ao_mcast_if != 0) &&
((NTE == NULL) || (NTE->IF->Index != SrcAO->ao_mcast_if))) {
if (NTE != NULL) {
ReleaseNTE(NTE);
NTE = NULL;
}
IF = FindInterfaceFromIndex(SrcAO->ao_mcast_if);
if (IF == NULL) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"RawSend: Bad mcast interface number\n"));
ErrorValue = TDI_INVALID_REQUEST;
goto ReturnError;
}
NTEorIF = CastFromIF(IF);
} else {
IF = NULL;
}
//
// Get the route.
//
Status = RouteToDestination(&SendReq->dsr_addr, SendReq->dsr_scope_id,
NTEorIF, RTD_FLAG_NORMAL, &RCE);
if (IF != NULL)
ReleaseIF(IF);
if (Status != IP_SUCCESS) {
//
// Failed to get a route to the destination. Error out.
//
if ((Status == IP_PARAMETER_PROBLEM) ||
(Status == IP_BAD_ROUTE))
ErrorValue = TDI_BAD_ADDR;
else if (Status == IP_NO_RESOURCES)
ErrorValue = TDI_NO_RESOURCES;
else
ErrorValue = TDI_DEST_UNREACHABLE;
if (NTE != NULL)
ReleaseNTE(NTE);
goto ReturnError;
}
//
// If our address object didn't have a source address,
// take the one of the sending net from the RCE.
// Otherwise, use address from AO.
//
if (NTE == NULL) {
NTE = RCE->NTE;
AddRefNTE(NTE);
}
//
// Allocate a packet header to anchor the buffer list.
//
NdisAllocatePacket(&NdisStatus, &Packet, IPv6PacketPool);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
"RawSend: Couldn't allocate packet header!?!\n"));
//
// If we can't get a packet header from the pool, we push
// the send request back on the queue and queue the address
// object for when we get resources.
//
OutOfResources:
ReleaseRCE(RCE);
ReleaseNTE(NTE);
KeAcquireSpinLock(&SrcAO->ao_lock, &Irql0);
PUSHQ(&SrcAO->ao_sendq, &SendReq->dsr_q);
PutPendingQ(SrcAO);
KeReleaseSpinLock(&SrcAO->ao_lock, Irql0);
return;
}
InitializeNdisPacket(Packet);
PC(Packet)->CompletionHandler = DGSendComplete;
PC(Packet)->CompletionData = SendReq;
//
// Create our header buffer.
// It will contain the link-level header and possibly the
// IPv6 header. The user has the option of contributing
// the IPv6 header, otherwise we generate it below.
//
Offset = HeaderLength = RCE->NCE->IF->LinkHeaderSize;
if (!AO_HDRINCL(SrcAO))
HeaderLength += sizeof(*IP);
if (HeaderLength > 0) {
Memory = ExAllocatePool(NonPagedPool, HeaderLength);
if (Memory == NULL) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
"RawSend: couldn't allocate header memory!?!\n"));
NdisFreePacket(Packet);
goto OutOfResources;
}
NdisAllocateBuffer(&NdisStatus, &RawBuffer, IPv6BufferPool,
Memory, HeaderLength);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
"RawSend: couldn't allocate buffer!?!\n"));
ExFreePool(Memory);
NdisFreePacket(Packet);
goto OutOfResources;
}
//
// Link the data buffers from the send request onto the buffer
// chain headed by our header buffer. Then attach this chain
// to the packet.
//
NDIS_BUFFER_LINKAGE(RawBuffer) = SendReq->dsr_buffer;
NdisChainBufferAtFront(Packet, RawBuffer);
}
else
NdisChainBufferAtFront(Packet, SendReq->dsr_buffer);
//
// We now have all the resources we need to send.
// Prepare the actual packet.
//
if (!AO_HDRINCL(SrcAO)) {
//
// We can not allow the user to supply extension headers.
// IPv6Send assumes that any extension headers are
// syntactically correct and resident in the first buffer.
// Currently TCPCreate prevents the user from opening raw
// sockets with extension header protocols.
//
ASSERT(!IsExtensionHeader(SrcAO->ao_prot));
//
// We need to provide the IPv6 header.
// Place it after the link-layer header.
//
IP = (IPv6Header UNALIGNED *)((uchar *)Memory + Offset);
IP->VersClassFlow = IP_VERSION;
IP->NextHeader = SrcAO->ao_prot;
IP->Source = NTE->Address;
IP->Dest = SendReq->dsr_addr;
//
// Apply the multicast or unicast hop limit, as appropriate.
//
if (IsMulticast(AlignAddr(&IP->Dest))) {
//
// Also disable multicast loopback, if requested.
//
if (! SrcAO->ao_mcast_loop)
PC(Packet)->Flags |= NDIS_FLAGS_DONT_LOOPBACK;
Hops = SrcAO->ao_mcast_hops;
}
else
Hops = SrcAO->ao_ucast_hops;
if (Hops != -1)
IP->HopLimit = (uchar) Hops;
else
IP->HopLimit = (uchar) RCE->NCE->IF->CurHopLimit;
//
// Allow the AO to receive data when in firewall mode.
//
SET_AO_SENTDATA(SrcAO);
//
// Everything's ready. Now send the packet.
//
// Note that IPv6Send does not return a status code.
// Instead it *always* completes the packet
// with an appropriate status code.
//
IPv6Send(Packet, Offset, IP, SendReq->dsr_size, RCE, 0,
SrcAO->ao_prot, 0, 0);
}
else {
//
// Our header buffer contains only the link-level header.
// The IPv6 header and any extension headers are expected to
// be provided by the user. In some cases the kernel
// will attempt to access the IPv6 header so we must
// ensure that the mappings exist now.
//
if (! MapNdisBuffers(NdisFirstBuffer(Packet))) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
"RawSend(%p): buffer mapping failed\n",
Packet));
IPv6SendComplete(NULL, Packet, IP_GENERAL_FAILURE);
}
else {
//
// Everything's ready. Now send the packet.
//
IPv6SendND(Packet, HeaderLength,
RCE->NCE, &(RCE->NTE->Address));
}
}
UStats.us_outdatagrams++;
//
// Release the route.
//
ReleaseRCE(RCE);
ReleaseNTE(NTE);
SendComplete:
//
// Check the send queue for more to send.
//
KeAcquireSpinLock(&SrcAO->ao_lock, &Irql0);
if (!EMPTYQ(&SrcAO->ao_sendq)) {
//
// More to go. Dequeue next request and loop back to top.
//
DEQUEUE(&SrcAO->ao_sendq, SendReq, DGSendReq, dsr_q);
KeReleaseSpinLock(&SrcAO->ao_lock, Irql0);
} else {
//
// Nothing more to send.
//
CLEAR_AO_REQUEST(SrcAO, AO_SEND);
KeReleaseSpinLock(&SrcAO->ao_lock, Irql0);
return;
}
}
}
//* RawDeliver - Deliver a datagram to a user.
//
// This routine delivers a datagram to a raw user. We're called with
// the AddrObj to deliver on, and with the AddrObjTable lock held.
// We try to find a receive on the specified AddrObj, and if we do
// we remove it and copy the data into the buffer. Otherwise we'll
// call the receive datagram event handler, if there is one. If that
// fails we'll discard the datagram.
//
void // Returns: Nothing.
RawDeliver(
AddrObj *RcvAO, // Address object to receive the datagram.
IPv6Packet *Packet, // Packet handed up by IP.
uint SrcScopeId, // Scope id for source address.
KIRQL Irql0) // IRQL prior to acquiring AddrObj table lock.
{
Queue *CurrentQ;
KIRQL Irql1;
DGRcvReq *RcvReq;
ULONG BytesTaken = 0;
uchar AddressBuffer[TCP_TA_SIZE];
uint RcvdSize;
EventRcvBuffer *ERB = NULL;
uint Position = Packet->Position;
uint Length = Packet->TotalSize;
CHECK_STRUCT(RcvAO, ao);
KeAcquireSpinLock(&RcvAO->ao_lock, &Irql1);
KeReleaseSpinLock(&AddrObjTableLock, Irql1);
if (AO_VALID(RcvAO)) {
CurrentQ = QHEAD(&RcvAO->ao_rcvq);
// Walk the list, looking for a receive buffer that matches.
while (CurrentQ != QEND(&RcvAO->ao_rcvq)) {
RcvReq = QSTRUCT(DGRcvReq, CurrentQ, drr_q);
CHECK_STRUCT(RcvReq, drr);
//
// If this request is a wildcard request (accept from anywhere),
// or matches the source IP address and scope id, deliver it.
//
if (IsUnspecified(&RcvReq->drr_addr) ||
(IP6_ADDR_EQUAL(&RcvReq->drr_addr, Packet->SrcAddr) &&
(RcvReq->drr_scope_id == SrcScopeId))) {
TDI_STATUS Status;
// Remove this from the queue.
REMOVEQ(&RcvReq->drr_q);
// We're done. We can free the AddrObj lock now.
KeReleaseSpinLock(&RcvAO->ao_lock, Irql0);
// Copy the data, and then complete the request.
RcvdSize = CopyToBufferChain(RcvReq->drr_buffer, 0,
Packet->NdisPacket,
Position,
Packet->FlatData,
MIN(Length, RcvReq->drr_size));
ASSERT(RcvdSize <= RcvReq->drr_size);
Status = UpdateConnInfo(RcvReq->drr_conninfo, Packet->SrcAddr,
SrcScopeId, 0);
UStats.us_indatagrams++;
(*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize);
FreeDGRcvReq(RcvReq);
return; // All done.
}
// Not a matching request. Get the next one off the queue.
CurrentQ = QNEXT(CurrentQ);
}
//
// We've walked the list, and not found a buffer.
// Call the receive handler now, if we have one.
//
if (RcvAO->ao_rcvdg != NULL) {
PRcvDGEvent RcvEvent = RcvAO->ao_rcvdg;
PVOID RcvContext = RcvAO->ao_rcvdgcontext;
TDI_STATUS RcvStatus;
ULONG Flags = TDI_RECEIVE_COPY_LOOKAHEAD;
int BufferSize = 0;
PVOID BufferToSend = NULL;
uchar *CurrPosition;
REF_AO(RcvAO);
KeReleaseSpinLock(&RcvAO->ao_lock, Irql0);
BuildTDIAddress(AddressBuffer, Packet->SrcAddr, SrcScopeId, 0);
UStats.us_indatagrams++;
if (IsMulticast(AlignAddr(&Packet->IP->Dest))) {
Flags |= TDI_RECEIVE_MULTICAST;
}
// If the IPV6_PKTINFO or IPV6_HOPLIMIT options were set, then
// create the control information to be passed to the handler.
// Currently this is the only place such options are filled in,
// so we just have one buffer. If other places are added in the
// future, we may want to support a list or array of buffers to
// copy into the user's buffer.
//
if (AO_PKTINFO(RcvAO)) {
BufferSize += TDI_CMSG_SPACE(sizeof(IN6_PKTINFO));
}
if (AO_RCV_HOPLIMIT(RcvAO)) {
BufferSize += TDI_CMSG_SPACE(sizeof(int));
}
if (BufferSize > 0) {
CurrPosition = BufferToSend = ExAllocatePool(NonPagedPool,
BufferSize);
if (BufferToSend == NULL) {
BufferSize = 0;
} else {
if (AO_PKTINFO(RcvAO)) {
DGFillIpv6PktInfo(&Packet->IP->Dest,
Packet->NTEorIF->IF->Index,
&CurrPosition);
// Set the receive flag so the receive handler knows
// we are passing up control info.
//
Flags |= TDI_RECEIVE_CONTROL_INFO;
}
if (AO_RCV_HOPLIMIT(RcvAO)) {
DGFillIpv6HopLimit(Packet->IP->HopLimit, &CurrPosition);
Flags |= TDI_RECEIVE_CONTROL_INFO;
}
}
}
RcvStatus = (*RcvEvent)(RcvContext, TCP_TA_SIZE,
(PTRANSPORT_ADDRESS)AddressBuffer,
BufferSize, BufferToSend, Flags,
Packet->ContigSize, Length, &BytesTaken,
Packet->Data, &ERB);
if (BufferToSend) {
ExFreePool(BufferToSend);
}
if (RcvStatus == TDI_MORE_PROCESSING) {
PIO_STACK_LOCATION IrpSp;
PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation;
//
// We were passed back a receive buffer. Copy the data in now.
// Receive event handler can't have taken more than was in the
// indicated buffer, but in debug builds we'll check this.
//
ASSERT(ERB != NULL);
ASSERT(BytesTaken <= Packet->ContigSize);
//
// For NT, ERBs are really IRPs.
//
IrpSp = IoGetCurrentIrpStackLocation(ERB);
DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG)
&(IrpSp->Parameters);
//
// Copy data to the IRP, skipping the bytes
// that were already taken.
//
Position += BytesTaken;
Length -= BytesTaken;
RcvdSize = CopyToBufferChain(ERB->MdlAddress, 0,
Packet->NdisPacket,
Position,
Packet->FlatData,
Length);
//
// Update the return address info.
//
RcvStatus = UpdateConnInfo(
DatagramInformation->ReturnDatagramInformation,
Packet->SrcAddr, SrcScopeId, 0);
//
// Complete the IRP.
//
ERB->IoStatus.Information = RcvdSize;
ERB->IoStatus.Status = RcvStatus;
IoCompleteRequest(ERB, 2);
} else {
ASSERT((RcvStatus == TDI_SUCCESS) ||
(RcvStatus == TDI_NOT_ACCEPTED));
ASSERT(ERB == NULL);
}
DELAY_DEREF_AO(RcvAO);
return;
} else
UStats.us_inerrors++;
//
// When we get here, we didn't have a buffer to put this data into.
// Fall through to the return case.
//
} else
UStats.us_inerrors++;
KeReleaseSpinLock(&RcvAO->ao_lock, Irql0);
}
//* RawReceive - Receive a Raw datagram.
//
// This routine is called by IP when a Raw datagram arrives. We
// lookup the protocol/address pair in our address table, and deliver
// the data to any users we find.
//
// Note that we'll only get here if all headers in the packet
// preceeding the one we're filtering on were acceptable.
//
// We return TRUE if we find a receiver to take the packet, FALSE otherwise.
//
int
RawReceive(
IPv6Packet *Packet, // Packet IP handed up to us.
uchar Protocol) // Protocol we think we're handling.
{
Interface *IF = Packet->NTEorIF->IF;
KIRQL OldIrql;
AddrObj *ReceivingAO;
AOSearchContext Search;
AOMCastAddr *AMA, *PrevAMA;
int ReceiverFound = FALSE;
uint SrcScopeId, DestScopeId;
uint Loop;
//
// This being the raw receive routine, we perform no checks on
// the packet data.
//
//
// Verify IPSec was performed.
//
if (InboundSecurityCheck(Packet, Protocol, 0, 0, IF) != TRUE) {
//
// No policy was found or the policy found was to drop the packet.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR,
"RawReceive: IPSec Policy caused packet to be refused\n"));
return FALSE; // Drop packet.
}
//
// Set the source's scope id value as appropriate.
//
SrcScopeId = DetermineScopeId(Packet->SrcAddr, IF);
//
// At this point, we've decided it's okay to accept the packet.
// Figure out who to give this packet to.
//
if (IsMulticast(AlignAddr(&Packet->IP->Dest))) {
//
// This is a multicast packet, so we need to find all interested
// AddrObj's. We get the AddrObjTable lock, and then loop through
// all AddrObj's and give the packet to any who are listening to
// this multicast address, interface & protocol.
// REVIEW: We match on interface, NOT scope id. Multicast is weird.
//
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
for (Loop = 0; Loop < AddrObjTableSize; Loop++) {
for (ReceivingAO = AddrObjTable[Loop]; ReceivingAO != NULL;
ReceivingAO = ReceivingAO->ao_next) {
CHECK_STRUCT(ReceivingAO, ao);
if (ReceivingAO->ao_prot != Protocol)
continue;
if ((AMA = FindAOMCastAddr(ReceivingAO,
AlignAddr(&Packet->IP->Dest),
IF->Index, &PrevAMA,
FALSE)) == NULL)
continue;
//
// We have a matching address object. Hand it the packet.
//
RawDeliver(ReceivingAO, Packet, SrcScopeId, OldIrql);
//
// RawDeliver released the AddrObjTableLock, so grab it again.
//
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
ReceiverFound = TRUE;
}
}
} else {
//
// This is a unicast packet. Try to find some AddrObj(s) to
// give it to. We deliver to all matches, not just the first.
//
DestScopeId = DetermineScopeId(AlignAddr(&Packet->IP->Dest), IF);
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
ReceivingAO = GetFirstAddrObj(AlignAddr(&Packet->IP->Dest),
Packet->SrcAddr,
DestScopeId, 0,
Protocol, IF, &Search);
for (; ReceivingAO != NULL; ReceivingAO = GetNextAddrObj(&Search)) {
//
// We have a matching address object. Hand it the packet.
//
RawDeliver(ReceivingAO, Packet, SrcScopeId, OldIrql);
//
// RawDeliver released the AddrObjTableLock, so grab it again.
//
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
ReceiverFound = TRUE;
}
}
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
return ReceiverFound;
}