|
|
// -*- 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:
//
// User Datagram Protocol code.
//
#include "oscfg.h"
#include "ndis.h"
#include "ip6imp.h"
#include "ip6def.h"
#include "icmp.h"
#include "tdi.h"
#include "tdint.h"
#include "tdistat.h"
#include "queue.h"
#include "transprt.h"
#include "addr.h"
#include "udp.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, '6PDU')
#endif // POOL_TAGGING
extern KSPIN_LOCK AddrObjTableLock; extern TDI_STATUS MapIPError(IP_STATUS IPError,TDI_STATUS Default);
//* UDPSend - Send a user 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.
UDPSend( 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; UDPHeader UNALIGNED *UDP; uint PayloadLength; PNDIS_PACKET Packet; PNDIS_BUFFER UDPBuffer; void *Memory; IP_STATUS Status; NDIS_STATUS NdisStatus; TDI_STATUS ErrorValue; uint Offset; uint HeaderLength; uint ChecksumLength = 0; 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.
//
// REVIEW: We may need to add a DHCP case later that checks for
// REVIEW: the AO_DHCP_FLAG and allows src addr to be unspecified.
//
if (!IsUnspecified(&SrcAO->ao_addr)) { //
// Convert the bound address to a NTE.
//
NTE = FindNetworkWithAddress(&SrcAO->ao_addr, SrcAO->ao_scope_id); if (NTE == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR, "UDPSend: 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, "UDPSend: 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, "UDPSend: 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;
//
// Our header buffer has extra space at the beginning for other
// headers to be prepended to ours without requiring further
// allocation calls.
//
Offset = RCE->NCE->IF->LinkHeaderSize; HeaderLength = Offset + sizeof(*IP) + sizeof(*UDP); Memory = ExAllocatePool(NonPagedPool, HeaderLength); if (Memory == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "UDPSend: couldn't allocate header memory!?!\n")); NdisFreePacket(Packet); goto OutOfResources; }
NdisAllocateBuffer(&NdisStatus, &UDPBuffer, IPv6BufferPool, Memory, HeaderLength); if (NdisStatus != NDIS_STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "UDPSend: 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(UDPBuffer) = SendReq->dsr_buffer; NdisChainBufferAtFront(Packet, UDPBuffer);
//
// We now have all the resources we need to send.
// Prepare the actual packet.
//
PayloadLength = SendReq->dsr_size + sizeof(UDPHeader);
//
// Our UDP Header buffer has extra space for other buffers to be
// prepended to ours without requiring further allocation calls.
// Put the actual UDP/IP header at the end of the buffer.
//
IP = (IPv6Header UNALIGNED *)((uchar *)Memory + Offset); IP->VersClassFlow = IP_VERSION; IP->NextHeader = IP_PROTOCOL_UDP; 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;
//
// Fill in UDP Header fields.
//
UDP = (UDPHeader UNALIGNED *)(IP + 1); UDP->Source = SrcAO->ao_port; UDP->Dest = SendReq->dsr_port;
//
// Check if the user specified a partial UDP checksum.
// The possible values are 0, 8, or greater.
//
if ((SrcAO->ao_udp_cksum_cover > PayloadLength) || (SrcAO->ao_udp_cksum_cover == 0) || (SrcAO->ao_udp_cksum_cover == (ushort)-1)) {
//
// The checksum coverage is the default so just use the
// payload length. Or, the checksum coverage is bigger
// than the actual payload so include the payload length.
//
if ((PayloadLength > MAX_IPv6_PAYLOAD) || (SrcAO->ao_udp_cksum_cover == (ushort)-1)) { //
// If the PayloadLength is too large for the UDP Length field,
// set the field to zero. Or for testing:
// if the ao_udp_cksum_cover is -1.
//
UDP->Length = 0; } else { //
// For backwards-compatibility, set the UDP Length field
// to the payload length.
//
UDP->Length = net_short((ushort)PayloadLength); } ChecksumLength = PayloadLength; } else { //
// The checksum coverage is less than the actual payload
// so use it in the length field.
//
UDP->Length = net_short(SrcAO->ao_udp_cksum_cover); ChecksumLength = SrcAO->ao_udp_cksum_cover; }
//
// Compute the UDP checksum. It covers the entire UDP datagram
// starting with the UDP header, plus the IPv6 pseudo-header.
//
UDP->Checksum = 0; UDP->Checksum = ChecksumPacket( Packet, Offset + sizeof *IP, NULL, ChecksumLength, AlignAddr(&IP->Source), AlignAddr(&IP->Dest), IP_PROTOCOL_UDP);
if (UDP->Checksum == 0) { //
// ChecksumPacket failed, so abort the transmission.
//
IPv6SendComplete(NULL, Packet, IP_NO_RESOURCES); } else { //
// 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.
//
UStats.us_outdatagrams++;
IPv6Send(Packet, Offset, IP, PayloadLength, RCE, 0, IP_PROTOCOL_UDP, net_short(UDP->Source), net_short(UDP->Dest)); }
//
// Release the route and NTE.
//
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; } } }
//* UDPDeliver - Deliver a datagram to a user.
//
// This routine delivers a datagram to a UDP user. We're called with
// the AddrObj to deliver on, and with the lock for that AddrObj 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.
UDPDeliver( AddrObj *RcvAO, // AddrObj to receive datagram.
IPv6Packet *Packet, // Packet handed up by IP.
uint SrcScopeId, // Scope id for source address.
ushort SrcPort, // Source port of datagram.
uint Length, // Size of UDP payload data.
KIRQL Irql0) // IRQL prior to acquiring AddrObj table lock.
{ Queue *CurrentQ; DGRcvReq *RcvReq; ULONG BytesTaken = 0; uchar AddressBuffer[TCP_TA_SIZE]; uint RcvdSize; EventRcvBuffer *ERB = NULL; uint Position = Packet->Position;
CHECK_STRUCT(RcvAO, ao);
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, or matches the source IP
// address and scope id, check the port.
//
if (IsUnspecified(&RcvReq->drr_addr) || (IP6_ADDR_EQUAL(&RcvReq->drr_addr, Packet->SrcAddr) && (RcvReq->drr_scope_id == SrcScopeId))) {
//
// The remote address matches, check the port.
// We'll match either 0 or the actual port.
//
if (RcvReq->drr_port == 0 || RcvReq->drr_port == SrcPort) { TDI_STATUS Status;
// The ports matched. 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, SrcPort);
UStats.us_indatagrams++;
(*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize);
FreeDGRcvReq(RcvReq);
return; // All done.
} }
//
// Either the IP address or the port didn't match.
// Get the next one.
//
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, SrcPort);
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;
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, SrcPort);
//
// 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); }
//* UDPReceive - Receive a UDP datagram.
//
// The routine called by IP when a UDP datagram arrived. We look up the
// port/local address pair in our address table, and deliver the data to
// a user if we find one. For multicast frames we may deliver it to
// multiple users.
//
// Returns the next header value. Since no other header is allowed to
// follow the UDP header, this is always IP_PROTOCOL_NONE.
//
uchar UDPReceive( IPv6Packet *Packet) // Packet IP handed up to us.
{ Interface *IF = Packet->NTEorIF->IF; UDPHeader *UDP; KIRQL OldIrql; AddrObj *ReceivingAO; uint Length; ushort Checksum; AOMCastAddr *AMA, *PrevAMA; int MCastReceiverFound; uint SrcScopeId, DestScopeId; uint Loop;
//
// Verify that the source address is reasonable.
//
ASSERT(!IsInvalidSourceAddress(Packet->SrcAddr)); if (IsUnspecified(Packet->SrcAddr)) { UStats.us_inerrors++; return IP_PROTOCOL_NONE; // Drop packet.
}
//
// Verify that we have enough contiguous data to overlay a UDPHeader
// structure on the incoming packet. Then do so.
//
if (! PacketPullup(Packet, sizeof(UDPHeader), __builtin_alignof(UDPHeader), 0)) { // Pullup failed.
UStats.us_inerrors++; if (Packet->TotalSize < sizeof(UDPHeader)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "UDPv6: data buffer too small to contain UDP 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.
} UDP = (UDPHeader *)Packet->Data;
//
// Verify IPSec was performed.
//
if (InboundSecurityCheck(Packet, IP_PROTOCOL_UDP, net_short(UDP->Source), net_short(UDP->Dest), IF) != TRUE) { //
// No policy was found or the policy found was to drop the packet.
//
UStats.us_inerrors++; KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "UDPReceive: IPSec Policy caused packet to be dropped\n")); return IP_PROTOCOL_NONE; // Drop packet.
}
//
// Verify UDP length is reasonable.
//
// NB: If Length < PayloadLength, then UDP-Lite semantics apply.
// We checksum only the UDP Length bytes, but we deliver
// all the bytes to the application.
//
Length = (uint) net_short(UDP->Length); if ((Length > Packet->TotalSize) || (Length < sizeof *UDP)) { //
// UDP jumbo-gram support: if the UDP length is zero,
// then use the payload length from IP.
//
if (Length != 0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "UDPv6: bogus UDP length (%u vs %u payload)\n", Length, Packet->TotalSize)); UStats.us_inerrors++; return IP_PROTOCOL_NONE; // Drop packet.
}
Length = Packet->TotalSize; }
//
// 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 it 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 & port.
// REVIEW: We match on interface, NOT scope id. Multicast is weird.
//
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
MCastReceiverFound = FALSE; for (Loop = 0; Loop < AddrObjTableSize; Loop++) { for (ReceivingAO = AddrObjTable[Loop]; ReceivingAO != NULL; ReceivingAO = ReceivingAO->ao_next) {
CHECK_STRUCT(ReceivingAO, ao);
if (ReceivingAO->ao_prot != IP_PROTOCOL_UDP || ReceivingAO->ao_port != UDP->Dest) continue;
if ((AMA = FindAOMCastAddr(ReceivingAO, AlignAddr(&Packet->IP->Dest), IF->Index, &PrevAMA, FALSE)) == NULL) continue;
//
// We have a matching address object. Trade in the table lock
// for a lock on just this object.
//
KeAcquireSpinLockAtDpcLevel(&ReceivingAO->ao_lock); KeReleaseSpinLockFromDpcLevel(&AddrObjTableLock);
//
// If this is the first AO we've found, verify the checksum.
//
if (!MCastReceiverFound) { Checksum = ChecksumPacket(Packet->NdisPacket, Packet->Position, Packet->FlatData, Length, Packet->SrcAddr, AlignAddr(&Packet->IP->Dest), IP_PROTOCOL_UDP); if ((Checksum != 0xffff) || (UDP->Checksum == 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "UDPReceive: Checksum failed %0x\n", Checksum)); KeReleaseSpinLock(&ReceivingAO->ao_lock, OldIrql); UStats.us_inerrors++; return IP_PROTOCOL_NONE; // Drop packet.
}
//
// Skip over the UDP header.
//
AdjustPacketParams(Packet, sizeof(UDPHeader));
MCastReceiverFound = TRUE; }
UDPDeliver(ReceivingAO, Packet, SrcScopeId, UDP->Source, Packet->TotalSize, OldIrql);
//
// UDPDeliver released the lock on the address object.
// We earlier released the AddrObjTableLock, so grab it again.
//
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql); } }
if (!MCastReceiverFound) UStats.us_noports++;
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
} else { //
// This is a unicast packet. We need to perform the checksum
// regardless of whether or not we find a matching AddrObj,
// since we send an ICMP port unreachable message for unicast
// packets that don't match a port. So verify the checksum now.
//
Checksum = ChecksumPacket(Packet->NdisPacket, Packet->Position, Packet->FlatData, Length, Packet->SrcAddr, AlignAddr(&Packet->IP->Dest), IP_PROTOCOL_UDP); if ((Checksum != 0xffff) || (UDP->Checksum == 0)) { UStats.us_inerrors++; KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "UDPReceive: Checksum failed %0x\n", Checksum)); return IP_PROTOCOL_NONE; // Drop packet.
}
//
// Skip over the UDP header.
//
AdjustPacketParams(Packet, sizeof(UDPHeader));
//
// Try to find an AddrObj to give this packet to.
//
DestScopeId = DetermineScopeId(AlignAddr(&Packet->IP->Dest), IF); KeAcquireSpinLock(&AddrObjTableLock, &OldIrql); ReceivingAO = GetBestAddrObj(AlignAddr(&Packet->IP->Dest), Packet->SrcAddr, DestScopeId, UDP->Dest, IP_PROTOCOL_UDP, IF); if (ReceivingAO != NULL) { //
// We have a matching address object. Trade in the table lock
// for a lock on just this object, and then deliver the packet.
//
KeAcquireSpinLockAtDpcLevel(&ReceivingAO->ao_lock); KeReleaseSpinLockFromDpcLevel(&AddrObjTableLock);
UDPDeliver(ReceivingAO, Packet, SrcScopeId, UDP->Source, Packet->TotalSize, OldIrql);
// Note UDPDeliver released the lock on the address object.
} else { KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
// Send ICMP Destination Port Unreachable.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "UDPReceive: No match for packet's address and port\n"));
ICMPv6SendError(Packet, ICMPv6_DESTINATION_UNREACHABLE, ICMPv6_PORT_UNREACHABLE, 0, IP_PROTOCOL_NONE, FALSE);
UStats.us_noports++; } }
return IP_PROTOCOL_NONE; }
//* UDPControlReceive - handler for UDP control messages.
//
// This routine is called if we receive an ICMPv6 error message that
// was generated by some remote site as a result of receiving a UDP
// packet from us.
//
uchar UDPControlReceive( IPv6Packet *Packet, // Packet handed to us by ICMPv6ErrorReceive.
StatusArg *StatArg) // Error Code, Argument, and invoking IP header.
{ UDPHeader *InvokingUDP; Interface *IF = Packet->NTEorIF->IF; uint SrcScopeId, DestScopeId; KIRQL Irql0; AddrObj *AO;
//
// Handle ICMPv6 errors that are meaningful to UDP clients.
//
switch (StatArg->Status) {
case IP_DEST_ADDR_UNREACHABLE: case IP_DEST_PORT_UNREACHABLE: case IP_DEST_UNREACHABLE:
//
// The next thing in the packet should be the UDP header of the
// original packet which invoked this error.
//
if (! PacketPullup(Packet, sizeof(UDPHeader), __builtin_alignof(UDPHeader), 0)) { // Pullup failed.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "UDPv6: Packet too small to contain UDP header " "from invoking packet\n")); return IP_PROTOCOL_NONE; // Drop packet.
}
InvokingUDP = (UDPHeader *)Packet->Data;
//
// Determining the scope identifiers for the addreses in the
// invoking packet is potentially problematic, since we have
// no way to be certain which interface we sent the packet on.
// Use the interface the icmp error arrived on to determine
// the scope ids for both the local and remote addresses.
//
SrcScopeId = DetermineScopeId(AlignAddr(&StatArg->IP->Source), IF);
KeAcquireSpinLock(&AddrObjTableLock, &Irql0);
AO = GetBestAddrObj(AlignAddr(&StatArg->IP->Source), AlignAddr(&StatArg->IP->Dest), SrcScopeId, InvokingUDP->Source, IP_PROTOCOL_UDP, IF);
if (AO != NULL && AO_VALID(AO) && (AO->ao_errorex != NULL)) {
uchar AddressBuffer[TCP_TA_SIZE]; PVOID ErrContext = AO->ao_errorexcontext; PTDI_IND_ERROR_EX ErrEvent = AO->ao_errorex;;
KeAcquireSpinLockAtDpcLevel(&AO->ao_lock); KeReleaseSpinLockFromDpcLevel(&AddrObjTableLock); REF_AO(AO);
KeReleaseSpinLock(&AO->ao_lock, Irql0);
DestScopeId = DetermineScopeId(AlignAddr(&StatArg->IP->Dest), IF); BuildTDIAddress(AddressBuffer, AlignAddr(&StatArg->IP->Dest), DestScopeId, InvokingUDP->Dest); (*ErrEvent) (ErrContext, MapIPError(StatArg->Status, TDI_DEST_UNREACHABLE), AddressBuffer);
DELAY_DEREF_AO(AO);
} else { KeReleaseSpinLock(&AddrObjTableLock, Irql0); }
}
return IP_PROTOCOL_NONE; }
|