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