diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/tcp/udp.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/udp.c b/private/ntos/tdi/tcpip/tcp/udp.c new file mode 100644 index 000000000..c9c284403 --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/udp.c @@ -0,0 +1,673 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** UDP.C - UDP protocol code. +// +// This file contains the code for the UDP protocol functions, +// principally send and receive datagram. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#include "tdistat.h" +#ifdef VXD +#include "tdivxd.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "udp.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" +#include "secfltr.h" + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'uPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'uPCT') + +#endif // POOL_TAGGING + +#endif // NT + +EXTERNAL_LOCK(AddrObjTableLock) + +void *UDPProtInfo = NULL; + +extern IPInfo LocalNetInfo; + +#ifdef CHICAGO +extern uchar TransportName[]; +#endif + +#ifdef CHICAGO +extern int RegisterAddrChangeHndlr(void *Handler, uint Add); +extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context, + uint Added); +#endif + + + +//** UDPSend - Send a 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 q. +// +// Input: SrcAO - Pointer to AddrObj doing the send. +// SendReq - Pointer to sendreq describing send. +// +// Returns: Nothing +// +void +UDPSend(AddrObj *SrcAO, DGSendReq *SendReq) +{ + UDPHeader *UH; + PNDIS_BUFFER UDPBuffer; + CTELockHandle HeaderHandle, AOHandle; + RouteCacheEntry *RCE; // RCE used for each send. + IPAddr SrcAddr; // Source address IP thinks we should + // use. + uchar DestType; // Type of destination address. + ushort UDPXsum; // Checksum of packet. + ushort SendSize; // Size we're sending. + IP_STATUS SendStatus; // Status of send attempt. + ushort MSS; + uint AddrValid; + IPOptInfo *OptInfo; + IPAddr OrigSrc; + + CTEStructAssert(SrcAO, ao); + CTEAssert(SrcAO->ao_usecnt != 0); + + //* Loop while we have something to send, and can get + // resources to send. + for (;;) { + + CTEStructAssert(SendReq, dsr); + + // Make sure we have a UDP header buffer for this send. If we + // don't, try to get one. + if ((UDPBuffer = SendReq->dsr_header) == NULL) { + // Don't have one, so try to get one. + CTEGetLock(&DGSendReqLock, &HeaderHandle); + UDPBuffer = GetDGHeader(); + if (UDPBuffer != NULL) + SendReq->dsr_header = UDPBuffer; + else { + // Couldn't get a header buffer. Push the send request + // back on the queue, and queue the addr object for when + // we get resources. + CTEGetLock(&SrcAO->ao_lock, &AOHandle); + PUSHQ(&SrcAO->ao_sendq, &SendReq->dsr_q); + PutPendingQ(SrcAO); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + CTEFreeLock(&DGSendReqLock, HeaderHandle); + return; + } + CTEFreeLock(&DGSendReqLock, HeaderHandle); + } + + // At this point, we have the buffer we need. Call IP to get an + // RCE (along with the source address if we need it), then compute + // the checksum and send the data. + CTEAssert(UDPBuffer != NULL); + + if (!CLASSD_ADDR(SendReq->dsr_addr)) { + // This isn't a multicast send, so we'll use the ordinary + // information. + OrigSrc = SrcAO->ao_addr; + OptInfo = &SrcAO->ao_opt; + } else { + OrigSrc = SrcAO->ao_mcastaddr; + OptInfo = &SrcAO->ao_mcastopt; + } + + if (!(SrcAO->ao_flags & AO_DHCP_FLAG)) { + SrcAddr = (*LocalNetInfo.ipi_openrce)(SendReq->dsr_addr, + OrigSrc, &RCE, &DestType, &MSS, OptInfo); + + AddrValid = !IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR); + } else { + // This is a DHCP send. He really wants to send from the + // NULL IP address. + SrcAddr = NULL_IP_ADDR; + RCE = NULL; + AddrValid = TRUE; + } + + if (AddrValid) { + // The OpenRCE worked. Compute the checksum, and send it. + + if (!IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR)) + SrcAddr = OrigSrc; + + UH = (UDPHeader *)((uchar *)NdisBufferVirtualAddress(UDPBuffer) + + LocalNetInfo.ipi_hsize); + NdisBufferLength(UDPBuffer) = sizeof(UDPHeader); + NDIS_BUFFER_LINKAGE(UDPBuffer) = SendReq->dsr_buffer; + UH->uh_src = SrcAO->ao_port; + UH->uh_dest = SendReq->dsr_port; + SendSize = SendReq->dsr_size + sizeof(UDPHeader); + UH->uh_length = net_short(SendSize); + UH->uh_xsum = 0; + + if (AO_XSUM(SrcAO)) { + // Compute the header xsum, and then call XsumNdisChain + UDPXsum = XsumSendChain(PHXSUM(SrcAddr, SendReq->dsr_addr, + PROTOCOL_UDP, SendSize), UDPBuffer); + + // We need to negate the checksum, unless it's already all + // ones. In that case negating it would take it to 0, and + // then we'd have to set it back to all ones. + if (UDPXsum != 0xffff) + UDPXsum =~UDPXsum; + + UH->uh_xsum = UDPXsum; + + } + + // We've computed the xsum. Now send the packet. + UStats.us_outdatagrams++; + SendStatus = (*LocalNetInfo.ipi_xmit)(UDPProtInfo, SendReq, + UDPBuffer, (uint)SendSize, SendReq->dsr_addr, SrcAddr, + OptInfo, RCE, PROTOCOL_UDP); + + (*LocalNetInfo.ipi_closerce)(RCE); + + // If it completed immediately, give it back to the user. + // Otherwise we'll complete it when the SendComplete happens. + // Currently, we don't map the error code from this call - we + // might need to in the future. + if (SendStatus != IP_PENDING) + DGSendComplete(SendReq, UDPBuffer); + + } else { + TDI_STATUS Status; + + if (DestType == DEST_INVALID) + Status = TDI_BAD_ADDR; + else + Status = TDI_DEST_UNREACHABLE; + + // Complete the request with an error. + (*SendReq->dsr_rtn)(SendReq->dsr_context, Status, 0); + // Now free the request. + SendReq->dsr_rtn = NULL; + DGSendComplete(SendReq, UDPBuffer); + } + + CTEGetLock(&SrcAO->ao_lock, &AOHandle); + + if (!EMPTYQ(&SrcAO->ao_sendq)) { + DEQUEUE(&SrcAO->ao_sendq, SendReq, DGSendReq, dsr_q); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + } else { + CLEAR_AO_REQUEST(SrcAO, AO_SEND); + CTEFreeLock(&SrcAO->ao_lock, AOHandle); + 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 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. +// +// Input: RcvAO - AO to receive the datagram. +// SrcIP - Source IP address of datagram. +// SrcPort - Source port of datagram. +// RcvBuf - The IPReceive buffer containing the data. +// RcvSize - Size received, including the UDP header. +// TableHandle - Lock handle for AddrObj table. +// +// Returns: Nothing. +// +void +UDPDeliver(AddrObj *RcvAO, IPAddr SrcIP, ushort SrcPort, IPRcvBuf *RcvBuf, + uint RcvSize, IPOptInfo *OptInfo, CTELockHandle TableHandle, uchar IsBCast) +{ + Queue *CurrentQ; + CTELockHandle AOHandle; + DGRcvReq *RcvReq; + uint BytesTaken = 0; + uchar AddressBuffer[TCP_TA_SIZE]; + uint RcvdSize; + EventRcvBuffer *ERB = NULL; + + CTEStructAssert(RcvAO, ao); + + CTEGetLock(&RcvAO->ao_lock, &AOHandle); + CTEFreeLock(&AddrObjTableLock, AOHandle); + + 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); + + CTEStructAssert(RcvReq, drr); + + // If this request is a wildcard request, or matches the source IP + // address, check the port. + + if (IP_ADDR_EQUAL(RcvReq->drr_addr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(RcvReq->drr_addr, SrcIP)) { + + // The local 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. + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + + // Call CopyRcvToNdis, and then complete the request. + RcvdSize = CopyRcvToNdis(RcvBuf, RcvReq->drr_buffer, + RcvReq->drr_size, sizeof(UDPHeader), 0); + + CTEAssert(RcvdSize <= RcvReq->drr_size); + + Status = UpdateConnInfo(RcvReq->drr_conninfo, OptInfo, + SrcIP, SrcPort); + + UStats.us_indatagrams++; + + (*RcvReq->drr_rtn)(RcvReq->drr_context, Status, RcvdSize); + + FreeDGRcvReq(RcvReq); + + return; + } + } + + // 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 recv. + // handler now. + + if (RcvAO->ao_rcvdg != NULL) { + PRcvDGEvent RcvEvent = RcvAO->ao_rcvdg; + PVOID RcvContext = RcvAO->ao_rcvdgcontext; + TDI_STATUS RcvStatus; + CTELockHandle OldLevel; + ULONG Flags = TDI_RECEIVE_COPY_LOOKAHEAD; + + + REF_AO(RcvAO); + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + + BuildTDIAddress(AddressBuffer, SrcIP, SrcPort); + + UStats.us_indatagrams++; + if (IsBCast) { + Flags |= TDI_RECEIVE_BROADCAST; + } + RcvStatus = (*RcvEvent)(RcvContext, TCP_TA_SIZE, + (PTRANSPORT_ADDRESS)AddressBuffer, OptInfo->ioi_optlength, + OptInfo->ioi_options, Flags, + RcvBuf->ipr_size - sizeof(UDPHeader), + RcvSize - sizeof(UDPHeader), &BytesTaken, + RcvBuf->ipr_buffer + sizeof(UDPHeader), &ERB); + + if (RcvStatus == TDI_MORE_PROCESSING) { + CTEAssert(ERB != NULL); + + // We were passed back a receive buffer. Copy the data in now. + + // He can't have taken more than was in the indicated + // buffer, but in debug builds we'll check to make sure. + + CTEAssert(BytesTaken <= (RcvBuf->ipr_size - sizeof(UDPHeader))); + +#ifdef VXD + RcvdSize = CopyRcvToNdis(RcvBuf, ERB->erb_buffer, + ERB->erb_size, sizeof(UDPHeader) + BytesTaken, 0); + + // + // Call the completion routine. + // + (*ERB->erb_rtn)(ERB->erb_context, TDI_SUCCESS, RcvdSize); + +#endif // VXD + +#ifdef NT + { + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation; + + IrpSp = IoGetCurrentIrpStackLocation(ERB); + DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) + &(IrpSp->Parameters); + + // + // Copy the remaining data to the IRP. + // + RcvdSize = CopyRcvToNdis(RcvBuf, ERB->MdlAddress, + RcvSize - sizeof(UDPHeader) - BytesTaken, + sizeof(UDPHeader) + BytesTaken, 0); + + // + // Update the return address info + // + RcvStatus = UpdateConnInfo( + DatagramInformation->ReturnDatagramInformation, + OptInfo, SrcIP, SrcPort); + + // + // Complete the IRP. + // + ERB->IoStatus.Information = RcvdSize; + ERB->IoStatus.Status = RcvStatus; + IoCompleteRequest(ERB, 2); + } +#endif // NT + + } + else { + CTEAssert( + (RcvStatus == TDI_SUCCESS) || + (RcvStatus == TDI_NOT_ACCEPTED) + ); + + CTEAssert(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++; + + CTEFreeLock(&RcvAO->ao_lock, TableHandle); + +} + + +//* UDPRcv - 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 broadcast +// frames we may deliver it to multiple users. +// +// Entry: IPContext - IPContext identifying physical i/f that +// received the data. +// Dest - IPAddr of destionation. +// Src - IPAddr of source. +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the packet +// IPH - IP Header. +// IPHLength - Bytes in IPH. +// RcvBuf - Pointer to receive buffer chain containing data. +// Size - Size in bytes of data received. +// IsBCast - Boolean indicator of whether or not this came in as +// a bcast. +// Protocol - Protocol this came in on - should be UDP. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception. Anything other than IP_SUCCESS will cause +// IP to send a 'port unreachable' message. +// +IP_STATUS +UDPRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPH, uint IPHLength, IPRcvBuf *RcvBuf, + uint IPSize, uchar IsBCast, uchar Protocol, IPOptInfo *OptInfo) +{ + UDPHeader UNALIGNED *UH; + CTELockHandle AOTableHandle; + AddrObj *ReceiveingAO; + uint Size; + uchar DType; + + DType = (*LocalNetInfo.ipi_getaddrtype)(Src); + + // The following code relies on DEST_INVALID being a broadcast dest type. + // If this is changed the code here needs to change also. + if (IS_BCAST_DEST(DType)) { + if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || !IsBCast) { + UStats.us_inerrors++; + return IP_SUCCESS; // Bad src address. + } + } + + UH = (UDPHeader *)RcvBuf->ipr_buffer; + + Size = (uint)(net_short(UH->uh_length)); + + if (Size < sizeof(UDPHeader)) { + UStats.us_inerrors++; + return IP_SUCCESS; // Size is too small. + } + + if (Size != IPSize) { + // Size doesn't match IP datagram size. If the size is larger + // than the datagram, throw it away. If it's smaller, truncate the + // recv. buffer. + if (Size < IPSize) { + IPRcvBuf *TempBuf = RcvBuf; + uint TempSize = Size; + + while (TempBuf != NULL) { + TempBuf->ipr_size = MIN(TempBuf->ipr_size, TempSize); + TempSize -= TempBuf->ipr_size; + TempBuf = TempBuf->ipr_next; + } + } else { + // Size is too big, toss it. + UStats.us_inerrors++; + return IP_SUCCESS; + } + } + + + if (UH->uh_xsum != 0) { + if (XsumRcvBuf(PHXSUM(Src, Dest, PROTOCOL_UDP, Size), RcvBuf) != 0xffff) { + UStats.us_inerrors++; + return IP_SUCCESS; // Checksum failed. + } + } + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + +#ifdef SECFLTR + // + // See if we are filtering the destination interface/port. + // + if ( !SecurityFilteringEnabled || + IsPermittedSecurityFilter( + SrcAddr, + IPContext, + PROTOCOL_UDP, + (ulong) net_short(UH->uh_dest) + ) + ) + { +#endif // SECFLTR + + // Try to find an AddrObj to give this to. In the broadcast case, we + // may have to do this multiple times. If it isn't a broadcast, just + // get the best match and deliver it to them. + + if (!IsBCast) { + ReceiveingAO = GetBestAddrObj(Dest, UH->uh_dest, PROTOCOL_UDP); + if (ReceiveingAO != NULL) { + UDPDeliver(ReceiveingAO, Src, UH->uh_src, RcvBuf, Size, + OptInfo, AOTableHandle, IsBCast); + return IP_SUCCESS; + } else { + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + UStats.us_noports++; + return IP_GENERAL_FAILURE; + } + } else { + // This is a broadcast, we'll need to loop. + + AOSearchContext Search; + + DType = (*LocalNetInfo.ipi_getaddrtype)(Dest); + + ReceiveingAO = GetFirstAddrObj(LocalAddr, UH->uh_dest, PROTOCOL_UDP, + &Search); + if (ReceiveingAO != NULL) { + do { + if ((DType != DEST_MCAST) || + ((DType == DEST_MCAST) && + MCastAddrOnAO(ReceiveingAO, Dest))) { + UDPDeliver(ReceiveingAO, Src, UH->uh_src, RcvBuf, Size, + OptInfo, AOTableHandle, IsBCast); + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + } + ReceiveingAO = GetNextAddrObj(&Search); + } while (ReceiveingAO != NULL); + } else + UStats.us_noports++; + } + +#ifdef SECFLTR + } +#endif // SECFLTR + + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + return IP_SUCCESS; +} + +//* UDPStatus - Handle a status indication. +// +// This is the UDP status handler, called by IP when a status event +// occurs. For most of these we do nothing. For certain severe status +// events we will mark the local address as invalid. +// +// Entry: StatusType - Type of status (NET or HW). NET status +// is usually caused by a received ICMP +// message. HW status indicate a HW +// problem. +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is NET status, the original dest. of +// DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local +// or remote). +// Param - Additional information for status - i.e. the +// param field of an ICMP message. +// Data - Data pertaining to status - for NET status, this +// is the first 8 bytes of the original DG. +// +// Returns: Nothing +// +void +UDPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, + IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data) +{ + // If this is a HW status, it could be because we've had an address go + // away. + if (StatusType == IP_HW_STATUS) { + + if (StatusCode == IP_ADDR_DELETED) { + // + // An address has gone away. OrigDest identifies the address. + // +#ifndef _PNP_POWER + // + // This is done via TDI notifications in the PNP world. + // + InvalidateAddrs(OrigDest); + +#endif // _PNP_POWER + +#ifdef SECFLTR + // + // Delete any security filters associated with this address + // + DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_UDP); + +#endif // SECFLTR + + return; + } + + if (StatusCode == IP_ADDR_ADDED) { + +#ifdef SECFLTR + // + // An address has materialized. OrigDest identifies the address. + // Data is a handle to the IP configuration information for the + // interface on which the address is instantiated. + // + AddProtocolSecurityFilter(OrigDest, PROTOCOL_UDP, + (NDIS_HANDLE) Data); +#endif // SECFLTR + + return; + } + +#ifdef CHICAGO + if (StatusCode == IP_UNLOAD) { + // IP is telling us we're being unloaded. First, deregister + // with VTDI, and then call CTEUnload(). + (void)TLRegisterProtocol(PROTOCOL_UDP, NULL, NULL, NULL, NULL); + +#ifdef UDP_ONLY + // Only do the following in the UDP_ONLY version. TCP does it in + // the generic version. + TLRegisterDispatch(TransportName, NULL); + (void)RegisterAddrChangeHndlr(AddrChange, FALSE); + CTEUnload(TransportName); +#endif // UDP_ONLY + + return; + } +#endif // CHICAGO + } +} + |