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.
959 lines
34 KiB
959 lines
34 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:
|
|
//
|
|
// 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;
|
|
}
|