|
|
/********************************************************************/ /** Microsoft LAN Manager **/ /** Copyright(c) Microsoft Corp., 1990-1993 **/ /********************************************************************/ /* :ts=4 */
//** RAW.C - Raw IP interface code.
//
// This file contains the code for the Raw IP interface functions,
// principally send and receive datagram.
//
#include "precomp.h"
#include "addr.h"
#include "raw.h"
#include "tlcommon.h"
#include "info.h"
#include "tcpcfg.h"
#include "secfltr.h"
#include "udp.h"
#define NO_TCP_DEFS 1
#include "tcpdeb.h"
#define PROT_IGMP 2
#define PROT_RSVP 46 // Protocol number for RSVP
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'rPCT')
#ifndef CTEAllocMem
#error "CTEAllocMem is not already defined - will override tagging"
#else
#undef CTEAllocMem
#endif
#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'rPCT')
#endif // POOL_TAGGING
void *RawProtInfo = NULL;
extern IPInfo LocalNetInfo;
//** RawSend - 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 RawSend(AddrObj * SrcAO, DGSendReq * SendReq) { PNDIS_BUFFER RawBuffer; UDPHeader *UH; CTELockHandle AOHandle; RouteCacheEntry *RCE; // RCE used for each send.
IPAddr SrcAddr; // Source address IP thinks we should
// use.
uchar DestType; // Type of destination address.
IP_STATUS SendStatus; // Status of send attempt.
ushort MSS; uint AddrValid; IPOptInfo OptInfo; IPAddr OrigSrc; uchar protocol;
CTEStructAssert(SrcAO, ao); ASSERT(SrcAO->ao_usecnt != 0);
protocol = SrcAO->ao_prot;
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(( "RawSend called, prot %u\n", protocol )); }
//* Loop while we have something to send, and can get
// resources to send.
for (;;) {
CTEStructAssert(SendReq, dsr);
// Make sure we have a Raw header buffer for this send. If we
// don't, try to get one.
if ((RawBuffer = SendReq->dsr_header) == NULL) { // Don't have one, so try to get one.
RawBuffer = GetDGHeader(&UH); if (RawBuffer != NULL) SendReq->dsr_header = RawBuffer; 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); return; } } // 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
// send the data.
ASSERT(RawBuffer != 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_opt.ioi_options && (*SrcAO->ao_opt.ioi_options == IP_OPT_ROUTER_ALERT)) { //Temporarily point to ao_opt options to satisfy
//RFC 2113 (router alerts goes onmcast address too)
OptInfo.ioi_options = SrcAO->ao_opt.ioi_options; OptInfo.ioi_optlength = SrcAO->ao_opt.ioi_optlength; } }
ASSERT(!(SrcAO->ao_flags & AO_DHCP_FLAG));
if (SrcAO->ao_opt.ioi_ucastif) { // srcaddr = address the socket is bound to
SrcAddr = SrcAO->ao_addr; // go thru slow path
RCE = NULL;
} else if ((OptInfo.ioi_mcastif) && CLASSD_ADDR(SendReq->dsr_addr)) { // mcast_if is set and this is a mcast send
ASSERT(IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR)); SrcAddr = (*LocalNetInfo.ipi_isvalidindex) (OptInfo.ioi_mcastif); // go thru slow path
RCE = NULL;
} else { SrcAddr = (*LocalNetInfo.ipi_openrce) (SendReq->dsr_addr, OrigSrc, &RCE, &DestType, &MSS, &OptInfo); }
AddrValid = !IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR);
if (AddrValid) {
// The OpenRCE worked. Send it.
if (!IP_ADDR_EQUAL(OrigSrc, NULL_IP_ADDR)) SrcAddr = OrigSrc;
NdisAdjustBufferLength(RawBuffer, 0); NDIS_BUFFER_LINKAGE(RawBuffer) = SendReq->dsr_buffer;
// Now send the packet.
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("RawSend transmitting\n")); }
UStats.us_outdatagrams++; SendStatus = (*LocalNetInfo.ipi_xmit) (RawProtInfo, SendReq, RawBuffer, (uint) SendReq->dsr_size, SendReq->dsr_addr, SrcAddr, &OptInfo, RCE, protocol, SendReq->dsr_context);
// closerce will just return if RCE is NULL
(*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, RawBuffer, SendStatus);
} 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, RawBuffer, IP_SUCCESS); }
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; }
} }
//* RawDeliver - Deliver a datagram to a user.
//
// This routine delivers a datagram to a Raw 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.
// IPH - IP Header
// IPHLength - Bytes in IPH.
// RcvBuf - The IPReceive buffer containing the data.
// RcvSize - Size received, including the Raw header.
// TableHandle - Lock handle for AddrObj table.
//
// Returns: Nothing.
//
void RawDeliver(AddrObj * RcvAO, IPAddr SrcIP, IPHeader UNALIGNED * IPH, uint IPHLength, IPRcvBuf * RcvBuf, uint RcvSize, IPOptInfo * OptInfo, CTELockHandle TableHandle, DGDeliverInfo *DeliverInfo) { Queue *CurrentQ; CTELockHandle AOHandle; DGRcvReq *RcvReq; uint BytesTaken = 0; uchar AddressBuffer[TCP_TA_SIZE]; uint RcvdSize; EventRcvBuffer *ERB = NULL; int BufferSize = 0; PVOID BufferToSend = NULL;
CTEStructAssert(RcvAO, ao);
CTEGetLock(&RcvAO->ao_lock, &AOHandle); CTEFreeLock(&AddrObjTableLock.Lock, AOHandle);
if (AO_VALID(RcvAO)) {
if ((DeliverInfo->Flags & IS_BCAST) && (DeliverInfo->Flags & SRC_LOCAL) && (RcvAO->ao_mcast_loop == 0)) { goto loop_exit; }
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(( "Raw delivering %u byte header + %u data bytes to AO %lx\n", IPHLength, RcvSize, 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, deliver it.
if (IP_ADDR_EQUAL(RcvReq->drr_addr, NULL_IP_ADDR) || IP_ADDR_EQUAL(RcvReq->drr_addr, SrcIP)) {
TDI_STATUS Status; PNDIS_BUFFER DestBuf = RcvReq->drr_buffer; uint DestOffset = 0;
// Remove this from the queue.
REMOVEQ(&RcvReq->drr_q);
// We're done. We can free the AddrObj lock now.
CTEFreeLock(&RcvAO->ao_lock, TableHandle);
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("Copying to posted receive\n")); }
// Copy the header
DestBuf = CopyFlatToNdis(DestBuf, (uchar *) IPH, IPHLength, &DestOffset, &RcvdSize);
// Copy the data and then complete the request.
RcvdSize += CopyRcvToNdis(RcvBuf, DestBuf, RcvSize, 0, DestOffset);
ASSERT(RcvdSize <= RcvReq->drr_size);
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("Copied %u bytes\n", RcvdSize)); }
Status = UpdateConnInfo(RcvReq->drr_conninfo, OptInfo, SrcIP, 0);
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; uint IndicateSize; uint DestOffset; PNDIS_BUFFER DestBuf; ULONG Flags = TDI_RECEIVE_COPY_LOOKAHEAD;
uchar *TempBuf = NULL; ulong TempBufLen = 0;
REF_AO(RcvAO); CTEFreeLock(&RcvAO->ao_lock, TableHandle);
BuildTDIAddress(AddressBuffer, SrcIP, 0);
IndicateSize = IPHLength;
if (((uchar *) IPH + IPHLength) == RcvBuf->ipr_buffer) { //
// The header is contiguous with the data
//
IndicateSize += RcvBuf->ipr_size;
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("RawRcv: header & data are contiguous\n")); } } else {
//if totallength is less than 128,
//put it on a staging buffer
TempBufLen = 128; if ((IPHLength + RcvSize) < 128) { TempBufLen = IPHLength + RcvSize; } TempBuf = CTEAllocMem(TempBufLen); if (TempBuf) { RtlCopyMemory(TempBuf, (uchar *) IPH, IPHLength);
RtlCopyMemory((TempBuf + IPHLength), RcvBuf->ipr_buffer, (TempBufLen - IPHLength));
} }
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("Indicating %u bytes\n", IndicateSize)); }
UStats.us_indatagrams++; if (DeliverInfo->Flags & IS_BCAST) { // This flag is true if this is a multicast, subnet broadcast,
// or broadcast. We need to differentiate to set the right
// receive flags.
//
if (!CLASSD_ADDR(DeliverInfo->DestAddr)) { Flags |= TDI_RECEIVE_BROADCAST; } }
// If the IP_PKTINFO option was set, then create the control
// information to be passed to the handler. Currently only one
// such option exists, so only one ancillary data object is
// created. We should be able to support an array of them as
// more options are added.
//
if (AO_PKTINFO(RcvAO)) { BufferToSend = DGFillIpPktInfo(DeliverInfo->DestAddr, DeliverInfo->LocalAddr, &BufferSize); if (BufferToSend) { // Set the receive flag so the receive handler knows
// we are passing up control info.
//
Flags |= TDI_RECEIVE_CONTROL_INFO; } }
if (TempBuf) {
RcvStatus = (*RcvEvent) (RcvContext, TCP_TA_SIZE, (PTRANSPORT_ADDRESS) AddressBuffer, BufferSize, BufferToSend, Flags, TempBufLen, IPHLength + RcvSize, &BytesTaken, (uchar *) TempBuf, &ERB);
CTEFreeMem(TempBuf);
} else {
RcvStatus = (*RcvEvent) (RcvContext, TCP_TA_SIZE, (PTRANSPORT_ADDRESS) AddressBuffer, BufferSize, BufferToSend, Flags, IndicateSize, IPHLength + RcvSize, &BytesTaken, (uchar *) IPH, &ERB); }
if (BufferToSend) { ExFreePool(BufferToSend); }
if (RcvStatus == TDI_MORE_PROCESSING) {
ASSERT(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.
ASSERT(BytesTaken <= RcvBuf->ipr_size);
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("ind took %u bytes\n", BytesTaken)); }
{ #if !MILLEN
PIO_STACK_LOCATION IrpSp; PTDI_REQUEST_KERNEL_RECEIVEDG DatagramInformation;
IrpSp = IoGetCurrentIrpStackLocation(ERB); DatagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) & (IrpSp->Parameters);
DestBuf = ERB->MdlAddress; #else // !MILLEN
DestBuf = ERB->erb_buffer; #endif // MILLEN
DestOffset = 0;
if (BytesTaken < IPHLength) {
// Copy the rest of the IP header
DestBuf = CopyFlatToNdis( DestBuf, (uchar *) IPH + BytesTaken, IPHLength - BytesTaken, &DestOffset, &RcvdSize );
BytesTaken = 0; } else { BytesTaken -= IPHLength; RcvdSize = 0; }
// Copy the data
RcvdSize += CopyRcvToNdis( RcvBuf, DestBuf, RcvSize - BytesTaken, BytesTaken, DestOffset );
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("Copied %u bytes\n", RcvdSize)); }
#if !MILLEN
//
// Update the return address info
//
RcvStatus = UpdateConnInfo( DatagramInformation->ReturnDatagramInformation, OptInfo, SrcIP, 0);
//
// Complete the IRP.
//
ERB->IoStatus.Information = RcvdSize; ERB->IoStatus.Status = RcvStatus; IoCompleteRequest(ERB, 2); #else // !MILLEN
//
// Call the completion routine.
//
(*ERB->erb_rtn) (ERB->erb_context, TDI_SUCCESS, RcvdSize); #endif // MILLEN
}
} else { ASSERT( (RcvStatus == TDI_SUCCESS) || (RcvStatus == TDI_NOT_ACCEPTED) );
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(( "Data %s taken\n", (RcvStatus == TDI_SUCCESS) ? "all" : "not" )); }
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++;
loop_exit:
CTEFreeLock(&RcvAO->ao_lock, TableHandle);
}
//* RawRcv - Receive a Raw datagram.
//
// The routine called by IP when a Raw 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 Raw.
// 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 RawRcv(void *IPContext, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, IPAddr SrcAddr, IPHeader UNALIGNED * IPH, uint IPHLength, IPRcvBuf * RcvBuf, uint Size, uchar IsBCast, uchar Protocol, IPOptInfo * OptInfo) { CTELockHandle AOTableHandle; AddrObj *ReceiveingAO; uchar SrcType, DestType; AOSearchContextEx Search; IP_STATUS Status = IP_DEST_PROT_UNREACHABLE; ulong IsLocal = 0; uint IfIndex; uint Deliver; DGDeliverInfo DeliverInfo = {0};
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("RawRcv prot %u size %u\n", IPH->iph_protocol, Size)); }
SrcType = (*LocalNetInfo.ipi_getaddrtype) (Src); DestType = (*LocalNetInfo.ipi_getaddrtype) (Dest);
if (SrcType == DEST_LOCAL) { DeliverInfo.Flags |= SRC_LOCAL; } IfIndex = (*LocalNetInfo.ipi_getifindexfromnte) (IPContext, IF_CHECK_NONE);
// 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(SrcType)) { if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || !IsBCast) { UStats.us_inerrors++; return IP_SUCCESS; // Bad src address.
} }
// Set the rest of our DeliverInfo for RawDeliver to consume.
//
DeliverInfo.Flags |= IsBCast ? IS_BCAST : 0; DeliverInfo.LocalAddr = LocalAddr; DeliverInfo.DestAddr = Dest;
// Get the AddrObjTable lock, and then try to find some AddrObj(s) to give
// this to. We deliver to all addr objs registered for the protocol and
// address.
CTEGetLock(&AddrObjTableLock.Lock, &AOTableHandle);
if (!SecurityFilteringEnabled || IsPermittedSecurityFilter(SrcAddr, IPContext, PROTOCOL_RAW, Protocol) || (RcvBuf->ipr_flags & IPR_FLAG_PROMISCUOUS)) {
ReceiveingAO = GetFirstAddrObjEx( LocalAddr, 0, // port is zero
Protocol, IfIndex, &Search );
if (ReceiveingAO != NULL) { do { // Default behavior is not to deliver unless requested
Deliver = FALSE;
// Deliver if socket is bound/joined appropriately
// Case 1: bound to destination IP address
// Case 2: bound to INADDR_ANY (but not ifindex)
// Case 3: bound to ifindex
if ((IP_ADDR_EQUAL(ReceiveingAO->ao_addr, LocalAddr) || ((ReceiveingAO->ao_bindindex == 0) && (IP_ADDR_EQUAL(ReceiveingAO->ao_addr, NULL_IP_ADDR))) || (ReceiveingAO->ao_bindindex == IfIndex)) && ((ReceiveingAO->ao_prot == IPH->iph_protocol) || (ReceiveingAO->ao_prot == Protocol) || (ReceiveingAO->ao_prot == 0))) { switch(DestType) { case DEST_LOCAL: Deliver = TRUE; break; case DEST_MCAST: Deliver = MCastAddrOnAO(ReceiveingAO, Dest, Src); break; } }
// Otherwise, see whether AO is promiscuous
if (!Deliver && (IfIndex == ReceiveingAO->ao_promis_ifindex)) { if (ReceiveingAO->ao_rcvall && ((ReceiveingAO->ao_prot == IPH->iph_protocol) || (ReceiveingAO->ao_prot == Protocol) || (ReceiveingAO->ao_prot == 0))) { Deliver = TRUE; } else if ((ReceiveingAO->ao_rcvall_mcast) && CLASSD_ADDR(Dest) && ((ReceiveingAO->ao_prot == IPH->iph_protocol) || (ReceiveingAO->ao_prot == Protocol) || (ReceiveingAO->ao_prot == 0))) { Deliver = TRUE; } else if ((ReceiveingAO->ao_absorb_rtralert) && ((*LocalNetInfo.ipi_isrtralertpacket) (IPH))) { Deliver = TRUE; } }
if (Deliver) { RawDeliver( ReceiveingAO, Src, IPH, IPHLength, RcvBuf, Size, OptInfo, AOTableHandle, &DeliverInfo );
// RawDeliver frees the lock so we have to get it back
CTEGetLock(&AddrObjTableLock.Lock, &AOTableHandle); } ReceiveingAO = GetNextAddrObjEx(&Search); } while (ReceiveingAO != NULL); Status = IP_SUCCESS; } else { UStats.us_noports++; }
}
CTEFreeLock(&AddrObjTableLock.Lock, AOTableHandle);
return Status; }
//* RawStatus - Handle a status indication.
//
// This is the Raw 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 RawStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, IPAddr OrigSrc, IPAddr Src, ulong Param, void *Data) {
IF_TCPDBG(TCP_DEBUG_RAW) { TCPTRACE(("RawStatus called\n")); }
// 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.
//
// Delete any security filters associated with this address
//
DeleteProtocolSecurityFilter(OrigDest, PROTOCOL_RAW);
return; } if (StatusCode == IP_ADDR_ADDED) {
//
// 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_RAW, (NDIS_HANDLE) Data);
return; } } }
|