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