diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/tcpip/ip/icmp.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/tdi/tcpip/ip/icmp.c')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/icmp.c | 1698 |
1 files changed, 1698 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/icmp.c b/private/ntos/tdi/tcpip/ip/icmp.c new file mode 100644 index 000000000..5faafa486 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/icmp.c @@ -0,0 +1,1698 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** icmp.c - IP ICMP routines. +// +// This module contains all of the ICMP related routines. +// + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "icmp.h" +#include "info.h" +#include "iproute.h" +#include "ipinit.h" +#include "ipxmit.h" +#include <icmpif.h> + +extern ProtInfo IPProtInfo[]; // Protocol information table. + +extern void *IPRegisterProtocol(uchar, void *, void *, void *, void *); +extern ULStatusProc FindULStatus(uchar); +extern uchar IPUpdateRcvdOptions(IPOptInfo *, IPOptInfo *, IPAddr, IPAddr); +extern void IPInitOptions(IPOptInfo *); +extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *); +extern IP_STATUS IPFreeOptions(IPOptInfo *); +extern uchar IPGetLocalAddr(IPAddr, IPAddr *); +void ICMPRouterTimer(NetTableEntry *); + +extern NDIS_HANDLE BufferPool; + +extern NetTableEntry *NetTableList; // Pointer to the net table list. +extern ProtInfo *RawPI; // Raw IP protinfo + +DEFINE_LOCK_STRUCTURE(ICMPHeaderLock) +ICMPHeader *ICMPHeaderList; +uint CurrentICMPHeaders; +uint MaxICMPHeaders; + +ICMPStats ICMPInStats; +ICMPStats ICMPOutStats; + + +#ifdef NT +#ifdef ALLOC_PRAGMA + +void ICMPInit(uint NumBuffers); + +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ); + +#pragma alloc_text(INIT, ICMPInit) +#pragma alloc_text(PAGE, ICMPEchoRequest) + +#endif // ALLOC_PRAGMA +#endif // NT + + +//* UpdateICMPStats - Update ICMP statistics. +// +// A routine to update the ICMP statistics. +// +// Input: Stats - Pointer to stat. structure to update (input or output). +// Type - Type of stat to update. +// +// Returns: Nothing. +// +void +UpdateICMPStats(ICMPStats *Stats, uchar Type) +{ + switch (Type) { + case ICMP_DEST_UNREACH: + Stats->icmps_destunreachs++; + break; + case ICMP_TIME_EXCEED: + Stats->icmps_timeexcds++; + break; + case ICMP_PARAM_PROBLEM: + Stats->icmps_parmprobs++; + break; + case ICMP_SOURCE_QUENCH: + Stats->icmps_srcquenchs++; + break; + case ICMP_REDIRECT: + Stats->icmps_redirects++; + break; + case ICMP_TIMESTAMP: + Stats->icmps_timestamps++; + break; + case ICMP_TIMESTAMP_RESP: + Stats->icmps_timestampreps++; + break; + case ICMP_ECHO: + Stats->icmps_echos++; + break; + case ICMP_ECHO_RESP: + Stats->icmps_echoreps++; + break; + case ADDR_MASK_REQUEST: + Stats->icmps_addrmasks++; + break; + case ADDR_MASK_REPLY: + Stats->icmps_addrmaskreps++; + break; + default: + break; + } + +} + +//** GetICMPBuffer - Get an ICMP buffer, and allocate an NDIS_BUFFER that maps it. +// +// A routine to allocate an ICMP buffer and map an NDIS_BUFFER to it. +// +// Entry: Size - Size in bytes header buffer should be mapped as. +// Buffer - Pointer to pointer to NDIS_BUFFER to return. +// +// Returns: Pointer to ICMP buffer if allocated, or NULL. +// +ICMPHeader * +GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer) +{ + CTELockHandle Handle; + ICMPHeader **Header; + NDIS_STATUS Status; + + + CTEGetLock(&ICMPHeaderLock, &Handle); + + Header = (ICMPHeader **)ICMPHeaderList; + + if (Header == NULL) { + // Couldn't get a header from our free list. Try to allocate one. + Header = CTEAllocMem(sizeof(ICMPHeader) + sizeof(IPHeader) + + sizeof(IPHeader) + MAX_OPT_SIZE + 8); + if (Header == NULL) { + CTEFreeLock(&ICMPHeaderLock, Handle); + return (ICMPHeader *) NULL; + } + CurrentICMPHeaders++; + } + else { + ICMPHeaderList = *Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + + NdisAllocateBuffer(&Status, Buffer, BufferPool, Header, Size + + sizeof(IPHeader)); + + if (Status == NDIS_STATUS_SUCCESS) { + NdisBufferLength(*Buffer) = Size; + Header = (ICMPHeader **)((uchar *)Header + sizeof(IPHeader)); + + (*(ICMPHeader **)&Header)->ich_xsum = 0; + return (ICMPHeader *)Header; + } + + // Couldn't get an NDIS_BUFFER, free the ICMP buffer. + CTEGetLock(&ICMPHeaderLock, &Handle); + + if (CurrentICMPHeaders > MaxICMPHeaders) { + CurrentICMPHeaders--; + CTEFreeMem(Header); + } else { + *Header = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + + return (ICMPHeader *)NULL; +} + +//** FreeICMPBuffer - Free an ICMP buffer. +// +// This routine puts an ICMP buffer back on our free list. +// +// Entry: Buffer - Pointer to NDIS_BUFFER to be freed. +// +// Returns: Nothing. +// +void +FreeICMPBuffer(PNDIS_BUFFER Buffer) +{ + CTELockHandle Handle; + ICMPHeader **Header; + uint Length; + + NdisQueryBuffer(Buffer, (PVOID *)&Header, &Length); + CTEGetLock(&ICMPHeaderLock, &Handle); + if (CurrentICMPHeaders > MaxICMPHeaders) { + CurrentICMPHeaders--; + CTEFreeMem(Header); + } else { + *Header = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + NdisFreeBuffer(Buffer); +} + +//** DeleteEC - Remove an EchoControl from an NTE, and return a pointer to it. +// +// This routine is called when we need to remove an echo control structure from +// an NTE. We walk the list of EC structures on the NTE, and if we find a match +// we remove it and return a pointer to it. +// +// Entry: NTE - Pointer to NTE to be searched. +// Seq - Seq. # identifting the EC. +// +// Returns: Pointer to the EC if it finds it. +// +EchoControl * +DeleteEC(NetTableEntry *NTE, ushort Seq) +{ + EchoControl *Prev, *Current; + CTELockHandle Handle; + + CTEGetLock(&NTE->nte_lock, &Handle); + Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next); + Current = NTE->nte_echolist; + while(Current != (EchoControl *)NULL) + if (Current->ec_seq == Seq) { + Prev->ec_next = Current->ec_next; + break; + } + else { + Prev = Current; + Current = Current->ec_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + return Current; + +} + +//** ICMPSendComplete< - Complete an ICMP send. +// +// This routine is called when an ICMP send completes. We free the header buffer, +// the data buffer if there is one, and the NDIS_BUFFER chain. +// +// Entry: DataPtr - Pointer to data buffer, if any. +// BufferChain - Pointer to NDIS_BUFFER chain. +// +// Returns: Nothing +// +void +ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain) +{ + PNDIS_BUFFER DataBuffer; + + NdisGetNextBuffer(BufferChain, &DataBuffer); + FreeICMPBuffer(BufferChain); + + if (DataBuffer != (PNDIS_BUFFER)NULL) { // We had data with this ICMP send. +#ifdef DEBUG + if (DataPtr == (void *)NULL) + DEBUGCHK; +#endif + CTEFreeMem(DataPtr); + NdisFreeBuffer(DataBuffer); + } + +} + +//* XsumBufChain - Checksum a chain of buffers. +// +// Called when we need to checksum an IPRcvBuf chain. +// +// Input: BufChain - Buffer chain to be checksummed. +// +// Returns: The checksum. +// +ushort +XsumBufChain(IPRcvBuf *BufChain) +{ + ulong CheckSum = 0; + + if (BufChain == NULL) + DEBUGCHK; + + do { + CheckSum += (ulong)xsum(BufChain->ipr_buffer, BufChain->ipr_size); + BufChain = BufChain->ipr_next; + } while (BufChain != NULL); + + // Fold the checksum down. + CheckSum = (CheckSum >> 16) + (CheckSum & 0xffff); + CheckSum += (CheckSum >> 16); + + return (ushort)CheckSum; +} + + +//** SendEcho - Send an ICMP Echo or Echo response. +// +// This routine sends an ICMP echo or echo response. The Echo/EchoResponse may +// carry data. If it does we'll copy the data here. The request may also have +// options. Options are not copied, as the IPTransmit routine will copy options. +// +// Entry: Dest - Destination to send to. +// Source - Source to send from. +// Type - Type of request (ECHO or ECHO_RESP) +// ID - ID of request. +// Seq - Seq. # of request. +// Data - Pointer to data (NULL if none). +// DataLength - Length in bytes of data +// OptInfo - Pointer to IP Options structure. +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendEcho(IPAddr Dest, IPAddr Source, uchar Type, ushort ID, ushort Seq, + IPRcvBuf *Data, uint DataLength, IPOptInfo *OptInfo) +{ + uchar *DataBuffer = (uchar *)NULL; // Pointer to data buffer. + PNDIS_BUFFER HeaderBuffer, Buffer; // Buffers for our header and user data. + NDIS_STATUS Status; + ICMPHeader *Header; + ushort header_xsum; + IP_STATUS IStatus; // Status of transmit + + ICMPOutStats.icmps_msgs++; + + Header = GetICMPBuffer(sizeof(ICMPHeader), &HeaderBuffer); + if (Header == (ICMPHeader *)NULL) { + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + +#ifdef DEBUG + if (Type != ICMP_ECHO_RESP && Type != ICMP_ECHO) + DEBUGCHK; +#endif + + Header->ich_type = Type; + Header->ich_code = 0; + *(ushort *)&Header->ich_param = ID; + *((ushort *)&Header->ich_param + 1) = Seq; + header_xsum = xsum(Header, sizeof(ICMPHeader)); + Header->ich_xsum = ~header_xsum; + + // If there's data, get a buffer and copy it now. If we can't do this fail the request. + if (DataLength != 0) { + ulong TempXsum; + uint BytesToCopy, CopyIndex; + + DataBuffer = CTEAllocMem(DataLength); + if (DataBuffer == (void *)NULL) { // Couldn't get a buffer + FreeICMPBuffer(HeaderBuffer); + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + BytesToCopy = DataLength; + CopyIndex = 0; + do { + uint CopyLength; +#ifdef DEBUG + if (Data == NULL) { + DEBUGCHK; + break; + } +#endif + + CopyLength = MIN(BytesToCopy, Data->ipr_size); + + CTEMemCopy(DataBuffer + CopyIndex, Data->ipr_buffer, CopyLength); + Data = Data->ipr_next; + CopyIndex += CopyLength; + BytesToCopy -= CopyLength; + } while (BytesToCopy); + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, DataBuffer, DataLength); + if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get an NDIS_BUFFER + CTEFreeMem(DataBuffer); + FreeICMPBuffer(HeaderBuffer); + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + // Compute rest of xsum. + TempXsum = (ulong)header_xsum + (ulong)xsum(DataBuffer, DataLength); + TempXsum = (TempXsum >> 16) + (TempXsum & 0xffff); + TempXsum += (TempXsum >> 16); + Header->ich_xsum = ~(ushort)TempXsum; + NDIS_BUFFER_LINKAGE(HeaderBuffer) = Buffer; + } + + + UpdateICMPStats(&ICMPOutStats, Type); + + IStatus = IPTransmit(IPProtInfo, DataBuffer, HeaderBuffer, + DataLength + sizeof(ICMPHeader), Dest, Source, OptInfo, NULL, + PROT_ICMP); + + if (IStatus != IP_PENDING) + ICMPSendComplete(DataBuffer, HeaderBuffer); + + return IStatus; +} + +//** SendICMPMsg - Send an ICMP message +// +// This is the general ICMP message sending routine, called for most ICMP sends besides +// echo. Basically, all we do is get a buffer, format the info, copy the input +// header, and send the message. +// +// Entry: Src - IPAddr of source. +// Dest - IPAddr of destination +// Type - Type of request. +// Code - Subcode of request. +// Pointer - Pointer value for request. +// Data - Pointer to data (NULL if none). +// DataLength - Length in bytes of data +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendICMPMsg(IPAddr Src, IPAddr Dest, uchar Type, uchar Code, ulong Pointer, + uchar *Data, uchar DataLength) +{ + PNDIS_BUFFER HeaderBuffer; // Buffer for our header + ICMPHeader *Header; + IP_STATUS IStatus; // Status of transmit + IPOptInfo OptInfo; // Options for this transmit. + + + ICMPOutStats.icmps_msgs++; + + Header = GetICMPBuffer(sizeof(ICMPHeader) + DataLength, &HeaderBuffer); + if (Header == (ICMPHeader *)NULL) { + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + + Header->ich_type = Type; + Header->ich_code = Code; + Header->ich_param = Pointer; + if (Data) + CTEMemCopy(Header + 1, Data, DataLength); + Header->ich_xsum = ~xsum(Header, sizeof(ICMPHeader) + DataLength); + + IPInitOptions(&OptInfo); + + UpdateICMPStats(&ICMPOutStats, Type); + + IStatus = IPTransmit(IPProtInfo, NULL, HeaderBuffer, + DataLength + sizeof(ICMPHeader), Dest, Src, &OptInfo, NULL, + PROT_ICMP); + + if (IStatus != IP_PENDING) + ICMPSendComplete(NULL, HeaderBuffer); + + return IStatus; + +} + +//** SendICMPErr - Send an ICMP error message +// +// This is the routine used to send an ICMP error message, such as Destination Unreachable. +// We examine the header to find the length of the data, and also make sure we're not +// replying to another ICMP error message or a broadcast message. Then we call SendICMPMsg +// to send it. +// +// Entry: Src - IPAddr of source. +// Header - Pointer to IP Header that caused the problem. +// Type - Type of request. +// Code - Subcode of request. +// Pointer - Pointer value for request. +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendICMPErr(IPAddr Src, IPHeader UNALIGNED *Header, uchar Type, uchar Code, + ulong Pointer) +{ + uchar HeaderLength; // Length in bytes if header. + uchar DType; + + HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + + if (Header->iph_protocol == PROT_ICMP) { + ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *) + ((uchar *)Header + HeaderLength); + + if (ICH->ich_type != ICMP_ECHO) + return IP_SUCCESS; + } + + // Don't respond to sends to a broadcast destination. + DType = GetAddrType(Header->iph_dest); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType)) + return IP_SUCCESS; + + // Don't respond if the source address is bad. + DType = GetAddrType(Header->iph_src); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType) || + (IP_LOOPBACK(Header->iph_dest) && DType != DEST_LOCAL)) + return IP_SUCCESS; + + // Make sure the source we're sending from is good. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || GetAddrType(Src) != DEST_LOCAL) + return IP_SUCCESS; + + // Double check to make sure it's an initial fragment. + if ((Header->iph_offset & IP_OFFSET_MASK) != 0) + return IP_SUCCESS; + + return SendICMPMsg(Src, Header->iph_src, Type, Code, Pointer, (uchar *)Header, + (uchar)(HeaderLength + 8)); + +} + + +//** ICMPTimer - Timer for ICMP +// +// This is the timer routine called periodically by global IP timer. We walk through +// the list of pending pings, and if we find one that's timed out we remove it and +// call the finish routine. +// +// Entry: NTE - Pointer to NTE being timed out. +// +// Returns: Nothing +// +void +ICMPTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + EchoControl *TimeoutList = (EchoControl *)NULL; // Timed out entries. + EchoControl *Prev, *Current; + ulong Now = CTESystemUpTime(); + + CTEGetLock(&NTE->nte_lock, &Handle); + Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next); + Current = NTE->nte_echolist; + while(Current != (EchoControl *)NULL) + if ((Current->ec_active) && (Current->ec_to < Now)) { // This one's timed out. + Prev->ec_next = Current->ec_next; + // Link him on timed out list. + Current->ec_next = TimeoutList; + TimeoutList = Current; + Current = Prev->ec_next; + } + else { + Prev = Current; + Current = Current->ec_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + // Now go through the timed out entries, and call the completion routine. + while (TimeoutList != (EchoControl *)NULL) { + EchoRtn Rtn; + + Current = TimeoutList; + TimeoutList = Current->ec_next; + + Rtn = (EchoRtn)Current->ec_rtn; + (*Rtn)(Current, IP_REQ_TIMED_OUT, NULL, 0, NULL); + } + + // + // [BUGBUG] Disabled for 4.0 sp2 + // + // ICMPRouterTimer(NTE); + +} + +//* CompleteEcho - Complete an echo request. +// +// Called when we need to complete an echo request, either because of a response +// or a received ICMP error message. We look it up, and then call the completion routine. +// +// Input: Header - Pointer to ICMP header causing completion. +// Status - Final status of request. +// Data - Data to be returned, if any. +// DataSize - Size in bytes of data. +// OptInfo - Option info structure. +// +// Returns: Nothing. +// +void +CompleteEcho(ICMPHeader UNALIGNED *Header, IP_STATUS Status, IPRcvBuf *Data, uint DataSize, + IPOptInfo *OptInfo) +{ + ushort NTEContext; + EchoControl *EC; + EchoRtn Rtn; + NetTableEntry *NTE; + + // Look up and remove the matching echo control block. + NTEContext = (*(ushort UNALIGNED *)&Header->ich_param); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if (NTEContext == NTE->nte_context) + break; + + if (NTE == NULL) + return; // Bad context value. + + EC = DeleteEC(NTE, *(((ushort UNALIGNED *)&Header->ich_param) + 1)); + if (EC != (EchoControl *)NULL) { // Found a match. + Rtn = (EchoRtn)EC->ec_rtn; + (*Rtn)(EC, Status, Data, DataSize, OptInfo); + } + + +} + +//** ICMPStatus - ICMP status handling procedure. +// +// This is the procedure called during a status change, either from an incoming ICMP +// message or a hardware status change. ICMP ignores most of these, unless we get an +// ICMP status message that was caused be an echo request. In that case we will complete +// the corresponding echo request with the appropriate error code. +// +// Input: StatusType - Type of status (NET or HW) +// 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 +ICMPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, IPAddr OrigSrc, IPAddr Src, + ulong Param, void *Data) +{ + if (StatusType == IP_NET_STATUS) { + ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *)Data; + // ICH is the datagram that caused the message. + + if (ICH->ich_type == ICMP_ECHO) { // And it was an echo request. + IPRcvBuf RcvBuf; + + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_buffer = (uchar *)&Src; + RcvBuf.ipr_size = sizeof(IPAddr); + CompleteEcho(ICH, StatusCode, &RcvBuf, sizeof(IPAddr), NULL); + } + } + +} + +//* ICMPMapStatus - Map an ICMP error to an IP status code. +// +// Called by ICMP status when we need to map from an incoming ICMP error code and type +// to an ICMP status. +// +// Entry: Type - Type of ICMP error. +// Code - Subcode of error. +// +// Returns: Corresponding IP status. +// +IP_STATUS +ICMPMapStatus(uchar Type, uchar Code) +{ + switch (Type) { + + case ICMP_DEST_UNREACH: + switch (Code) { + case NET_UNREACH: + case HOST_UNREACH: + case PROT_UNREACH: + case PORT_UNREACH: + return IP_DEST_UNREACH_BASE + Code; + break; + case FRAG_NEEDED: + return IP_PACKET_TOO_BIG; + break; + case SR_FAILED: + return IP_BAD_ROUTE; + break; + case DEST_NET_UNKNOWN: + case SRC_ISOLATED: + case DEST_NET_ADMIN: + case NET_UNREACH_TOS: + return IP_DEST_NET_UNREACHABLE; + break; + case DEST_HOST_UNKNOWN: + case DEST_HOST_ADMIN: + case HOST_UNREACH_TOS: + return IP_DEST_HOST_UNREACHABLE; + break; + default: + return IP_DEST_NET_UNREACHABLE; + } + break; + case ICMP_TIME_EXCEED: + if (Code == TTL_IN_TRANSIT) + return IP_TTL_EXPIRED_TRANSIT; + else + return IP_TTL_EXPIRED_REASSEM; + break; + case ICMP_PARAM_PROBLEM: + return IP_PARAM_PROBLEM; + break; + case ICMP_SOURCE_QUENCH: + return IP_SOURCE_QUENCH; + break; + default: + return IP_GENERAL_FAILURE; + break; + } + +} + +void +SendRouterSolicitation(NetTableEntry *NTE) +{ + if (NTE->nte_rtrdiscovery) { + SendICMPMsg(NTE->nte_addr, NTE->nte_rtrdiscaddr, ICMP_ROUTER_SOLICITATION, + 0, 0, NULL, 0); + } +} + +//** ICMPRouterTimer - Timeout default gateway entries +// +// This is the router advertisement timeout handler. When a router +// advertisement is received, we add the routers to our default gateway +// list if applicable. We then run a timer on the entries and refresh +// the list as new advertisements are received. If we fail to hear an +// update for a router within the specified lifetime we will delete the +// route from our routing tables. +// + +void +ICMPRouterTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + IPRtrEntry *rtrentry; + IPRtrEntry *temprtrentry; + IPRtrEntry *lastrtrentry = NULL; + uint SendIt = FALSE; + + CTEGetLock(&NTE->nte_lock, &Handle); + rtrentry = NTE->nte_rtrlist; + while (rtrentry != NULL) { + if (--rtrentry->ire_lifetime == 0) { + if (lastrtrentry == NULL) { + NTE->nte_rtrlist = rtrentry->ire_next; + } else { + lastrtrentry->ire_next = rtrentry->ire_next; + } + temprtrentry = rtrentry; + rtrentry = rtrentry->ire_next; +// DbgPrint("DeleteRoute: RtrAddr = %08x\n",temprtrentry->ire_addr); + DeleteRoute(NULL_IP_ADDR, DEFAULT_MASK, + temprtrentry->ire_addr, NTE->nte_if); + CTEFreeMem(temprtrentry); + } else { + lastrtrentry = rtrentry; + rtrentry = rtrentry->ire_next; + } + } + if (NTE->nte_rtrdisccount != 0) { + NTE->nte_rtrdisccount--; + if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_SOLICITING) && + ((NTE->nte_rtrdisccount%SOLICITATION_INTERVAL) == 0)) { + SendIt = TRUE; + } + if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_DELAYING) && + (NTE->nte_rtrdisccount == 0)) { + NTE->nte_rtrdisccount = (SOLICITATION_INTERVAL)*(MAX_SOLICITATIONS-1); + NTE->nte_rtrdiscstate = NTE_RTRDISC_SOLICITING; + SendIt = TRUE; + } + } + CTEFreeLock(&NTE->nte_lock, Handle); + if (SendIt) { + SendRouterSolicitation(NTE); + } + +} + +//** ProcessRouterAdvertisement - Process a router advertisement +// +// This is the router advertisement handler. When a router advertisement +// is received, we add the routers to our default gateway list if applicable. +// + +uint +ProcessRouterAdvertisement(IPAddr Src, IPAddr LocalAddr, NetTableEntry *NTE, + ICMPRouterAdHeader UNALIGNED *AdHeader, IPRcvBuf *RcvBuf, uint Size) +{ + uchar NumAddrs = AdHeader->irah_numaddrs; + uchar AddrEntrySize = AdHeader->irah_addrentrysize; + ushort Lifetime = net_short(AdHeader->irah_lifetime); + ICMPRouterAdAddrEntry UNALIGNED *RouterAddr = (ICMPRouterAdAddrEntry UNALIGNED *)RcvBuf->ipr_buffer; + uint i; + CTELockHandle Handle; + IPRtrEntry *rtrentry; + IPRtrEntry *lastrtrentry = NULL; + int Update = FALSE; + +// DbgPrint("ProcessRouterAdvertisement: NumAddrs = %d\n",NumAddrs); +// DbgPrint("ProcessRouterAdvertisement: AddrEntrySize = %d\n",AddrEntrySize); +// DbgPrint("ProcessRouterAdvertisement: Lifetime = %d\n",Lifetime); + + if ((NumAddrs == 0) || (AddrEntrySize < 2)) // per rfc 1256 + return FALSE; + + CTEGetLock(&NTE->nte_lock, &Handle); + for ( i=0; i<NumAddrs; i++, RouterAddr++) { + if ((RouterAddr->irae_addr & NTE->nte_mask) != (NTE->nte_addr & NTE->nte_mask)) { + continue; + } + if (!IsRouteICMP(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, NTE->nte_if)) { + continue; + } + +// DbgPrint("ProcessRouterAdvertisement: RtrAddr = %08x\n",RouterAddr->irae_addr); +// DbgPrint("ProcessRouterAdvertisement: RtrPreference = %d\n",net_long(RouterAddr->irae_preference)); + rtrentry = NTE->nte_rtrlist; + while (rtrentry != NULL) { + if (rtrentry->ire_addr == RouterAddr->irae_addr) { + rtrentry->ire_lifetime = Lifetime*2; + if (rtrentry->ire_preference != RouterAddr->irae_preference) { + rtrentry->ire_preference = RouterAddr->irae_preference; + Update = TRUE; + } + break; + } + lastrtrentry = rtrentry; + rtrentry = rtrentry->ire_next; + } + + if (rtrentry == NULL) { + rtrentry = (IPRtrEntry *) CTEAllocMem(sizeof(IPRtrEntry)); + if (rtrentry == NULL) { + return FALSE; + } + rtrentry->ire_next = NULL; + rtrentry->ire_addr = RouterAddr->irae_addr; + rtrentry->ire_preference = RouterAddr->irae_preference; + rtrentry->ire_lifetime = Lifetime*2; + if (lastrtrentry == NULL) { + NTE->nte_rtrlist = rtrentry; + } else { + lastrtrentry->ire_next = rtrentry; + } + Update = TRUE; + } + + if (Update && (RouterAddr->irae_preference != (long)0x00000080)) { // per rfc 1256 +// DbgPrint("AddRoute: RtrAddr = %08x\n",RouterAddr->irae_addr); + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, + NTE->nte_if, NTE->nte_mss, + (uint)(1000-net_long(RouterAddr->irae_preference)), // invert for metric + IRE_PROTO_ICMP, ATYPE_OVERRIDE, NULL); + } + Update = FALSE; + } + CTEFreeLock(&NTE->nte_lock, Handle); + + return TRUE; +} + +//** ICMPRcv - Receive an ICMP datagram. +// +// Called by the main IP code when we receive an ICMP datagram. The action we +// take depends on what the DG is. For some DGs, we call upper layer status +// handlers. For Echo Requests, we call the echo responder. +// +// Entry: NTE - Pointer to NTE on which ICMP message was received. +// 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 +// IPHdr - Pointer to IP Header +// IPHdrLength - Bytes in Header. +// RcvBuf - ICMP message buffer. +// Size - Size in bytes of ICMP message. +// IsBCast - Boolean indicator of whether or not this came in +// as a bcast. +// Protocol - Protocol this came in on. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception +// +IP_STATUS +ICMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol, + IPOptInfo *OptInfo) +{ + ICMPHeader UNALIGNED *Header; + void *Data; // Pointer to data received. + IPHeader UNALIGNED *IPH; // Pointer to IP Header in error messages. + uint HeaderLength; // Size of IP header. + ULStatusProc ULStatus; // Pointer to upper layer status procedure. + IPOptInfo NewOptInfo; + uchar DType; + uint PassUp = FALSE; + + + ICMPInStats.icmps_msgs++; + + DType = GetAddrType(Src); + if (Size < sizeof(ICMPHeader) || DType == DEST_INVALID || + IS_BCAST_DEST(DType) || (IP_LOOPBACK(Dest) && DType != DEST_LOCAL) || + XsumBufChain(RcvBuf) != (ushort)0xffff) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Bad checksum. + } + + Header = (ICMPHeader UNALIGNED *)RcvBuf->ipr_buffer; + + + RcvBuf->ipr_buffer += sizeof(ICMPHeader); + RcvBuf->ipr_size -= sizeof(ICMPHeader); + + // Set up the data pointer for most requests, i.e. those that take less + // than MIN_FIRST_SIZE data. + + if (Size -= sizeof(ICMPHeader)) + Data = (void *)(Header + 1); + else + Data = (void *)NULL; + + switch (Header->ich_type) { + + case ICMP_DEST_UNREACH: + case ICMP_TIME_EXCEED: + case ICMP_PARAM_PROBLEM: + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + + if (Data == NULL || Size < sizeof(IPHeader)) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // No data, error. + } + + IPH = (IPHeader UNALIGNED *)Data; + HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + if (Size < (HeaderLength + MIN_ERRDATA_LENGTH)) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Not enough data for this + // ICMP message. + } + + // Make sure that the source address of the datagram that triggered + // the message is one of ours. + + if (GetAddrType(IPH->iph_src) != DEST_LOCAL) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Bad src in header. + } + + if (Header->ich_type != ICMP_REDIRECT) { + + UpdateICMPStats(&ICMPInStats, Header->ich_type); + + if (ULStatus = FindULStatus(IPH->iph_protocol)) { + (void)(*ULStatus)(IP_NET_STATUS, + ICMPMapStatus(Header->ich_type, Header->ich_code), + IPH->iph_dest, IPH->iph_src, Src, Header->ich_param, + (uchar *)IPH + HeaderLength); + } + if (Header->ich_code == FRAG_NEEDED) + RouteFragNeeded( + IPH, + (ushort)net_short( + *((ushort UNALIGNED *)&Header->ich_param + 1) + ) + ); + } else { + ICMPInStats.icmps_redirects++; + Redirect(NTE, Src, IPH->iph_dest, IPH->iph_src, + Header->ich_param); + } + + PassUp = TRUE; + + break; + + + case ICMP_ECHO_RESP: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_echoreps++; + // Look up and remove the matching echo control block. + CompleteEcho(Header, IP_SUCCESS, RcvBuf, Size, OptInfo); + + PassUp = TRUE; + + break; + + case ICMP_ECHO: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_echos++; + + // Create our new optinfo structure. + IPInitOptions(&NewOptInfo); + NewOptInfo.ioi_tos = OptInfo->ioi_tos; + NewOptInfo.ioi_flags = OptInfo->ioi_flags; + + // If we have options, we need to reverse them and update any + // record route info. We can use the option buffer supplied by the + // IP layer, since we're part of him. + if (OptInfo->ioi_options != (uchar *)NULL) + IPUpdateRcvdOptions(OptInfo, &NewOptInfo, Src, LocalAddr); + + + SendEcho(Src, LocalAddr, ICMP_ECHO_RESP, + *(ushort UNALIGNED *)&Header->ich_param, + *((ushort UNALIGNED *)&Header->ich_param + 1), + RcvBuf, Size, &NewOptInfo); + + IPFreeOptions(&NewOptInfo); + break; + + case ADDR_MASK_REQUEST: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_addrmasks++; + + Dest = Src; + + SendICMPMsg(LocalAddr, Dest, ADDR_MASK_REPLY, 0, Header->ich_param, + (uchar *)&NTE->nte_mask, sizeof(IPMask)); + break; + + case ICMP_ROUTER_ADVERTISEMENT: + if (Header->ich_code != 0) + return IP_SUCCESS; // Code must be 0 as per RFC1256 + if (NTE->nte_rtrdiscovery) { + if (!ProcessRouterAdvertisement(Src, LocalAddr, NTE, + (ICMPRouterAdHeader *)&Header->ich_param, RcvBuf, Size)) + return IP_SUCCESS; // An error was returned + } + PassUp = TRUE; + break; + + case ICMP_ROUTER_SOLICITATION: + if (Header->ich_code != 0) + return IP_SUCCESS; // Code must be 0 as per RFC1256 + PassUp = TRUE; + break; + + default: + PassUp = TRUE; + UpdateICMPStats(&ICMPInStats, Header->ich_type); + break; + } + + // + // Pass the packet up to the raw layer if applicable. + // + if (PassUp && (RawPI != NULL)) { + if (RawPI->pi_rcv != NULL) { + // + // Restore the original values. + // + RcvBuf->ipr_buffer -= sizeof(ICMPHeader); + RcvBuf->ipr_size += sizeof(ICMPHeader); + Size += sizeof(ICMPHeader); + Data = (void *) Header; + + (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr, + IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo); + } + } + + return IP_SUCCESS; +} + + +//** ICMPEcho - Send an echo to the specified address. +// +// Entry: ControlBlock - Pointer to an EchoControl structure. This structure +// must remain valid until the req. completes. +// Timeout - Time in milliseconds to wait for response. +// Data - Pointer to data to send with echo. +// DataSize - Size in bytes of data. +// Callback - Routine to call when request is responded to or times out. +// Dest - Address to be pinged. +// OptInfo - Pointer to opt info structure to use for ping. +// +// Returns: IP_STATUS of attempt to ping.. +// +IP_STATUS +ICMPEcho(EchoControl *ControlBlock, ulong Timeout, void *Data, uint DataSize, EchoRtn Callback, + IPAddr Dest, IPOptInfo *OptInfo) +{ + IPAddr Dummy; + NetTableEntry *NTE; + CTELockHandle Handle; + ushort Seq; + IP_STATUS Status; + IPOptInfo NewOptInfo; + IPRcvBuf RcvBuf; + uint MTU; + Interface *IF; + uchar DType; + EchoControl *Current; + + if (OptInfo->ioi_ttl == 0) + return IP_BAD_OPTION; + + IPInitOptions(&NewOptInfo); + NewOptInfo.ioi_ttl = OptInfo->ioi_ttl; + NewOptInfo.ioi_flags = OptInfo->ioi_flags; + NewOptInfo.ioi_tos = OptInfo->ioi_tos & 0xfc; + + if (OptInfo->ioi_optlength != 0) { + Status = IPCopyOptions(OptInfo->ioi_options, OptInfo->ioi_optlength, + &NewOptInfo); + + if (Status != IP_SUCCESS) + return Status; + } + + if (!IP_ADDR_EQUAL(NewOptInfo.ioi_addr, NULL_IP_ADDR)) + Dest = NewOptInfo.ioi_addr; + + DType = GetAddrType(Dest); + if (DType == DEST_INVALID) { + IPFreeOptions(&NewOptInfo); + return IP_BAD_DESTINATION; + } + + if ((IF = LookupNextHopWithBuffer(Dest, NULL_IP_ADDR, &Dummy, &MTU, 0x1, NULL, 0)) == NULL) { + IPFreeOptions(&NewOptInfo); + return IP_DEST_HOST_UNREACHABLE; // Don't know how to get there. + } + + // Loop through the NetTable, looking for a matching NTE. + CTEGetLock(&RouteTableLock, &Handle); + if (DHCPActivityCount != 0) + NTE = NULL; + else + NTE = BestNTEForIF(Dummy, IF); + CTEFreeLock(&RouteTableLock, Handle); + +#ifdef _PNP_POWER + // We're done with the interface, so dereference it. + DerefIF(IF); +#endif + + if (NTE == NULL) { + // Couldn't find a matching NTE. This is very bad. + //DEBUGCHK; + + DbgPrint("ICMP: Failed to find NTE when going to %x\n",Dest); + + IPFreeOptions(&NewOptInfo); + return IP_DEST_HOST_UNREACHABLE; + } + + // Figure out the timeout. + ControlBlock->ec_to = CTESystemUpTime() + Timeout; + ControlBlock->ec_rtn = Callback; + ControlBlock->ec_active = 0; // Prevent from timing out until sent + CTEGetLock(&NTE->nte_lock, &Handle); + // Link onto ping list, and get seq. # */ + Seq = ++NTE->nte_icmpseq; + ControlBlock->ec_seq = Seq; + ControlBlock->ec_next = NTE->nte_echolist; + NTE->nte_echolist = ControlBlock; + CTEFreeLock(&NTE->nte_lock, Handle); + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_buffer = Data; + RcvBuf.ipr_size = DataSize; + Status = SendEcho(Dest, NTE->nte_addr, ICMP_ECHO, NTE->nte_context, + Seq, &RcvBuf, DataSize, &NewOptInfo); + + IPFreeOptions(&NewOptInfo); + + if (Status != IP_PENDING && Status != IP_SUCCESS) { // We had an error on the send. + if (DeleteEC(NTE, Seq) != (EchoControl *)NULL) + return Status; // We found it. + } + + // + // If the request is still pending, activate the timer + // + CTEGetLock(&NTE->nte_lock, &Handle); + + for ( + Current = NTE->nte_echolist; + Current != (EchoControl *)NULL; + Current = Current->ec_next + ) { + if (Current == ControlBlock) { + ControlBlock->ec_active = 1; // start the timer + break; + } + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + return IP_PENDING; + +} + + +//** ICMPEchoRequest - Common dispatch routine for echo requests +// +// This is the routine called by the OS-specific code on behalf of a user to issue an +// echo request. +// +// Entry: InputBuffer - Pointer to an ICMP_ECHO_REQUEST structure. +// InputBufferLength - Size in bytes of the InputBuffer. +// ControlBlock - Pointer to an EchoControl structure. This +// structure must remain valid until the +// request completes. +// Callback - Routine to call when request is responded to +// or times out. +// +// Returns: IP_STATUS of attempt to ping. +// +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ) +{ + PICMP_ECHO_REQUEST requestBuffer; + struct IPOptInfo optionInfo; + PUCHAR endOfRequestBuffer; + IP_STATUS status; + + +#ifdef NT + + PAGED_CODE(); + +#endif //NT + + requestBuffer = (PICMP_ECHO_REQUEST) InputBuffer; + endOfRequestBuffer = ((PUCHAR) requestBuffer) + InputBufferLength; + + // + // Validate the request. + // + if (InputBufferLength < sizeof(ICMP_ECHO_REQUEST)) { + status = IP_BUF_TOO_SMALL; + goto common_echo_exit; + } + + if (requestBuffer->DataSize > 0) { + if ( (requestBuffer->DataOffset < sizeof(ICMP_ECHO_REQUEST)) + || + ( ( ((PUCHAR)requestBuffer) + requestBuffer->DataOffset + + requestBuffer->DataSize + ) + > + endOfRequestBuffer + ) + ) { + status = IP_GENERAL_FAILURE; + goto common_echo_exit; + } + } + + if (requestBuffer->OptionsSize > 0) { + if ( (requestBuffer->OptionsOffset < sizeof(ICMP_ECHO_REQUEST)) + || + ( ( ((PUCHAR)requestBuffer) + requestBuffer->OptionsOffset + + requestBuffer->OptionsSize + ) + > + endOfRequestBuffer + ) + ) { + status = IP_GENERAL_FAILURE; + goto common_echo_exit; + } + } + + // + // Copy the options to a local structure. + // + if (requestBuffer->OptionsValid) { + optionInfo.ioi_optlength = requestBuffer->OptionsSize; + + if (requestBuffer->OptionsSize > 0) { + optionInfo.ioi_options = ((uchar *) requestBuffer) + + requestBuffer->OptionsOffset; + } + else { + optionInfo.ioi_options = NULL; + } + optionInfo.ioi_addr = 0; + optionInfo.ioi_ttl = requestBuffer->Ttl; + optionInfo.ioi_tos = requestBuffer->Tos; + optionInfo.ioi_flags = requestBuffer->Flags; + } + else { + optionInfo.ioi_optlength = 0; + optionInfo.ioi_options = NULL; + optionInfo.ioi_addr = 0; + optionInfo.ioi_ttl = DEFAULT_TTL; + optionInfo.ioi_tos = 0; + optionInfo.ioi_flags = 0; + } + + status = ICMPEcho( + ControlBlock, + requestBuffer->Timeout, + ((uchar *)requestBuffer) + requestBuffer->DataOffset, + requestBuffer->DataSize, + Callback, + (IPAddr) requestBuffer->Address, + &optionInfo + ); + +common_echo_exit: + + return(status); + +} // ICMPEchoRequest + + +//** ICMPEchoComplete - Common completion routine for echo requests +// +// This is the routine is called by the OS-specific code to process an +// ICMP echo response. +// +// Entry: OutputBuffer - Pointer to an ICMP_ECHO_REPLY structure. +// OutputBufferLength - Size in bytes of the OutputBuffer. +// Status - The status of the reply. +// Data - The reply data (may be NULL). +// DataSize - The amount of reply data. +// OptionInfo - A pointer to the reply options +// +// Returns: The number of bytes written to the output buffer +// +ulong +ICMPEchoComplete( + EchoControl *ControlBlock, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ) +{ + PICMP_ECHO_REPLY replyBuffer; + IPRcvBuf *dataBuffer; + uchar optionsLength; + uchar *tmp; + ulong bytesReturned = sizeof(ICMP_ECHO_REPLY); + + + replyBuffer = (PICMP_ECHO_REPLY) ControlBlock->ec_replybuf; + dataBuffer = (IPRcvBuf *) Data; + + if (OptionInfo != NULL) { + optionsLength = OptionInfo->ioi_optlength; + } + else { + optionsLength = 0; + } + + // + // Initialize the reply buffer + // + replyBuffer->Options.OptionsSize = 0; + replyBuffer->Options.OptionsData = (unsigned char FAR *) (replyBuffer + 1); + replyBuffer->DataSize = 0; + replyBuffer->Data = replyBuffer->Options.OptionsData; + + if ( (Status != IP_SUCCESS) && (DataSize == 0)) { + // + // Timed out or internal error. + // + replyBuffer->Reserved = 0; // indicate no replies. + replyBuffer->Status = Status; + } + else { + if (Status != IP_SUCCESS) { + // + // A message other than an echo reply was received. + // The IP Address of the system that reported the error is + // in the data buffer. There is no other data. + // + CTEAssert(dataBuffer->ipr_size == sizeof(IPAddr)); + + CTEMemCopy( + &(replyBuffer->Address), + dataBuffer->ipr_buffer, + sizeof(IPAddr) + ); + + DataSize = 0; + dataBuffer = NULL; + } + // else { + // + // BUGBUG - we currently depend on the fact that the destination + // address is still in the request buffer. The reply address + // should just be a parameter to this function. In NT, this + // just works since the input and output buffers are the same. + // In the VXD, the destination address must be put into the + // reply buffer by the OS-specific code. + // } + + // + // Check that the reply buffer is large enough to hold all the data. + // + if ( ControlBlock->ec_replybuflen < + (sizeof(ICMP_ECHO_REPLY) + DataSize + optionsLength) + ) { + // + // Not enough space to hold the reply. + // + replyBuffer->Reserved = 0; // indicate no replies + replyBuffer->Status = IP_BUF_TOO_SMALL; + } + else { + replyBuffer->Reserved = 1; // indicate one reply + replyBuffer->Status = Status; + replyBuffer->RoundTripTime = CTESystemUpTime() - + ControlBlock->ec_starttime; + + // + // Copy the reply options. + // + if (OptionInfo != NULL) { + replyBuffer->Options.Ttl = OptionInfo->ioi_ttl; + replyBuffer->Options.Tos = OptionInfo->ioi_tos; + replyBuffer->Options.Flags = OptionInfo->ioi_flags; + replyBuffer->Options.OptionsSize = optionsLength; + + if (optionsLength > 0) { + + CTEMemCopy( + replyBuffer->Options.OptionsData, + OptionInfo->ioi_options, + optionsLength + ); + } + } + + // + // Copy the reply data + // + replyBuffer->DataSize = (ushort) DataSize; + replyBuffer->Data = replyBuffer->Options.OptionsData + + replyBuffer->Options.OptionsSize; + + if (DataSize > 0) { + uint bytesToCopy; + + CTEAssert(Data != NULL); + + tmp = replyBuffer->Data; + + while (DataSize) { + CTEAssert(dataBuffer != NULL); + + bytesToCopy = (DataSize > dataBuffer->ipr_size) ? + dataBuffer->ipr_size : DataSize; + + CTEMemCopy( + tmp, + dataBuffer->ipr_buffer, + bytesToCopy + ); + + tmp += bytesToCopy; + DataSize -= bytesToCopy; + dataBuffer = dataBuffer->ipr_next; + } + } + + bytesReturned += replyBuffer->DataSize + optionsLength; + + // + // Convert the kernel pointers to offsets from start of reply buffer. + // + replyBuffer->Options.OptionsData = (unsigned char FAR *) + (((unsigned long) replyBuffer->Options.OptionsData) - + ((unsigned long) replyBuffer)); + + replyBuffer->Data = (void FAR *) + (((unsigned long) replyBuffer->Data) - + ((unsigned long) replyBuffer)); + } + } + + return(bytesReturned); +} + + +#ifdef VXD + +struct _pending_echo { + EchoControl ControlBlock; + CTEBlockStruc BlockStruc; +}; + +typedef struct _pending_echo PendingEcho; + + +//** VXDEchoComplete - OS-specific icmp echo completion routine. +// +// This routine is called by the OS-indepenent code to process a completed +// ICMP echo request. It calls common code to package the response. +// +// Entry: Context - A pointer to an EchoControl structure. +// Status - The status of the request +// Data - A pointer to the response data. +// DataSize - The amount of response data. +// OptionInfo - A pointer to the options contained in the reply. +// +// Returns: Nothing. +// +void +VXDEchoComplete( + void *Context, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ) +{ + PendingEcho *pendingEcho; + EchoControl *controlBlock; + ulong bytesReturned; + + + controlBlock = (EchoControl *) Context; + + bytesReturned = ICMPEchoComplete( + controlBlock, + Status, + Data, + DataSize, + OptionInfo + ); + + // + // The request thread will copy the returned byte count from + // the control block. + // + controlBlock->ec_replybuflen = bytesReturned; + + // + // Signal waiting request thread. + // + pendingEcho = STRUCT_OF(PendingEcho, controlBlock, ControlBlock); + CTESignal(&(pendingEcho->BlockStruc), Status); + + return; +} + + +//** VXDEchoRequest - Send an echo to the specified address. +// +// This routine dispatches an echo request on the VXD platform to the +// common code +// +// Entry: InBuf - Pointer to input buffer. +// InBufLen - Pointer to input buffer length. +// OutBuf - Pointer to output buffer. +// OutBufLen - Pointer to output buffer length. +// +// Returns: DWORD Win32 completion status. +// +ULONG +VXDEchoRequest( + void * InBuf, + ulong * InBufLen, + void * OutBuf, + ulong * OutBufLen + ) +{ + IP_STATUS ipStatus; + PendingEcho pendingEcho; + + pendingEcho.ControlBlock.ec_starttime = CTESystemUpTime(); + pendingEcho.ControlBlock.ec_replybuf = OutBuf; + pendingEcho.ControlBlock.ec_replybuflen = *OutBufLen; + CTEInitBlockStruc(&pendingEcho.BlockStruc); + + ipStatus = ICMPEchoRequest( + InBuf, + *InBufLen, + &(pendingEcho.ControlBlock), + VXDEchoComplete + ); + + if (ipStatus == IP_PENDING) { + ipStatus = CTEBlock(&(pendingEcho.BlockStruc)); + + if (ipStatus == IP_SUCCESS) { + PICMP_ECHO_REQUEST requestBuffer; + PICMP_ECHO_REPLY replyBuffer; + + // + // BUGBUG: + // + // It is necessary to copy the original destination address into + // the reply buffer because the src address is not provided to + // the completion routine for an echo response. This is the only + // reason why the signalling status is the reply status instead + // of just success. + // + requestBuffer = (PICMP_ECHO_REQUEST) InBuf; + replyBuffer = (PICMP_ECHO_REPLY) OutBuf; + + replyBuffer->Address = requestBuffer->Address; + } + + // + // The ioctl is considered successful as long as we can submit + // the request. The status of the request is contained in the + // reply buffer. The completion routine stuffed the return + // buffer length back into the control block. + // + ipStatus = IP_SUCCESS; + *OutBufLen = pendingEcho.ControlBlock.ec_replybuflen; + } + else { + // + // An internal error of some kind occurred. Since the VXD can + // return the IP_STATUS directly, do so. + // + CTEAssert(ipStatus != IP_SUCCESS); + + *OutBufLen = 0; + } + + return(ipStatus); +} + +#endif // VXD + + +#pragma BEGIN_INIT +//** ICMPInit - Initialize ICMP. +// +// This routine initializes ICMP. All we do is allocate and link up some header buffers, +/// and register our protocol with IP. +// +// Entry: NumBuffers - Number of ICMP buffers to allocate. +// +// Returns: Nothing +// +void +ICMPInit(uint NumBuffers) +{ + ICMPHeader **IHP; // Pointer to current ICMP header. + + + CTEInitLock(&ICMPHeaderLock); + MaxICMPHeaders = NumBuffers; + CurrentICMPHeaders = 0; + ICMPHeaderList= (ICMPHeader *)NULL; + + while (NumBuffers--) { + IHP = (ICMPHeader **) CTEAllocMem( + sizeof(ICMPHeader) + sizeof(IPHeader) + + sizeof(IPHeader) + MAX_OPT_SIZE + 8 + ); + + if (IHP == (ICMPHeader **)NULL) { + break; + } + + *IHP = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)IHP; + CurrentICMPHeaders++; + } + + IPRegisterProtocol(PROT_ICMP, ICMPRcv, ICMPSendComplete, ICMPStatus, NULL); + +} + +#pragma END_INIT |