summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/ip/icmp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/ip/icmp.c1698
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