/*********************************************************************
*
* Internet Control Message Protocol (ICMP) Server
* Module for Microchip TCP/IP Stack
* -Provides "ping" diagnostics
* -Reference: RFC 792
*
*********************************************************************
* FileName: ICMP.c
* Dependencies: IP, ARP
* Processor: PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
* Compiler: Microchip C32 v1.05 or higher
* Microchip C30 v3.12 or higher
* Microchip C18 v3.30 or higher
* HI-TECH PICC-18 PRO 9.63PL2 or higher
* Company: Microchip Technology, Inc.
*
* Software License Agreement
*
* Copyright (C) 2002-2009 Microchip Technology Inc. All rights
* reserved.
*
* Microchip licenses to you the right to use, modify, copy, and
* distribute:
* (i) the Software when embedded on a Microchip microcontroller or
* digital signal controller product ("Device") which is
* integrated into Licensee's product; or
* (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
* ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
* used in conjunction with a Microchip ethernet controller for
* the sole purpose of interfacing with the ethernet controller.
*
* You should refer to the license agreement accompanying this
* Software for additional information regarding your rights and
* obligations.
*
* THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
* WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
* LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
* PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
* BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
* THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
* SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
* (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
*
*
* Author Date Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Howard Schlunder 03/16/07 Original
********************************************************************/
#define __ICMP_C
#include "TCPIP Stack/TCPIP.h"
#if defined(STACK_USE_ICMP_SERVER) || defined(STACK_USE_ICMP_CLIENT)
#if defined(STACK_USE_ICMP_CLIENT)
// ICMP Timeout Value
#define ICMP_TIMEOUT (4ul*TICK_SECOND)
// ICMP Packet Structure
typedef struct
{
BYTE vType;
BYTE vCode;
WORD wChecksum;
WORD wIdentifier;
WORD wSequenceNumber;
WORD wData;
} ICMP_PACKET;
// ICMP Sequence Number
static WORD wICMPSequenceNumber;
// ICMP tick timer variable
static DWORD ICMPTimer;
// ICMP Flag structure
static struct
{
unsigned char bICMPInUse:1; // Indicates that the ICMP Client is in use
unsigned char bReplyValid:1; // Indicates that a correct Ping response to one of our pings was received
unsigned char bRemoteHostIsROM:1; // Indicates that a remote host name was passed as a ROM pointer argument
} ICMPFlags = {0x00};
// ICMP Static Variables
static union
{
union
{
ROM BYTE *szROM;
BYTE *szRAM;
} RemoteHost;
NODE_INFO ICMPRemote;
} StaticVars;
// ICMP State Machine Enumeration
static enum
{
SM_IDLE = 0,
SM_DNS_SEND_QUERY,
SM_DNS_GET_RESPONSE,
SM_ARP_SEND_QUERY,
SM_ARP_GET_RESPONSE,
SM_ICMP_SEND_ECHO_REQUEST,
SM_ICMP_GET_ECHO_RESPONSE
} ICMPState;
#endif
/*********************************************************************
* Function: void ICMPProcess(void)
*
* PreCondition: MAC buffer contains ICMP type packet.
*
* Input: *remote: Pointer to a NODE_INFO structure of the
* ping requester
* len: Count of how many bytes the ping header and
* payload are in this IP packet
*
* Output: Generates an echo reply, if requested
* Validates and sets ICMPFlags.bReplyValid if a
* correct ping response to one of ours is received.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
void ICMPProcess(NODE_INFO *remote, WORD len)
{
DWORD_VAL dwVal;
// Obtain the ICMP header Type, Code, and Checksum fields
MACGetArray((BYTE*)&dwVal, sizeof(dwVal));
// See if this is an ICMP echo (ping) request
if(dwVal.w[0] == 0x0008u)
{
// Validate the checksum using the Microchip MAC's DMA module
// The checksum data includes the precomputed checksum in the
// header, so a valid packet will always have a checksum of
// 0x0000 if the packet is not disturbed.
if(MACCalcRxChecksum(0+sizeof(IP_HEADER), len))
return;
// Calculate new Type, Code, and Checksum values
dwVal.v[0] = 0x00; // Type: 0 (ICMP echo/ping reply)
dwVal.v[2] += 8; // Subtract 0x0800 from the checksum
if(dwVal.v[2] < 8u)
{
dwVal.v[3]++;
if(dwVal.v[3] == 0u)
dwVal.v[2]++;
}
// Wait for TX hardware to become available (finish transmitting
// any previous packet)
while(!IPIsTxReady());
// Position the write pointer for the next IPPutHeader operation
// NOTE: do not put this before the IPIsTxReady() call for WF compatbility
MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER));
// Create IP header in TX memory
IPPutHeader(remote, IP_PROT_ICMP, len);
// Copy ICMP response into the TX memory
MACPutArray((BYTE*)&dwVal, sizeof(dwVal));
MACMemCopyAsync(-1, -1, len-4);
while(!MACIsMemCopyDone());
// Transmit the echo reply packet
MACFlush();
}
#if defined(STACK_USE_ICMP_CLIENT)
else if(dwVal.w[0] == 0x0000u) // See if this an ICMP Echo reply to our request
{
// Get the sequence number and identifier fields
MACGetArray((BYTE*)&dwVal, sizeof(dwVal));
// See if the identifier matches the one we sent
if(dwVal.w[0] != 0xEFBE)
return;
if(dwVal.w[1] != wICMPSequenceNumber)
return;
// Validate the ICMP checksum field
IPSetRxBuffer(0);
if(CalcIPBufferChecksum(sizeof(ICMP_PACKET))) // Two bytes of payload were sent in the echo request
return;
// Flag that we received the response and stop the timer ticking
ICMPFlags.bReplyValid = 1;
ICMPTimer = TickGet() - ICMPTimer;
}
#endif
}
#if defined(STACK_USE_ICMP_CLIENT)
/*********************************************************************
* Function: void ICMPSendPing(DWORD dwRemoteIP)
*
* PreCondition: ICMPBeginUsage() returned TRUE
*
* Input: dwRemoteIP: IP Address to ping. Must be stored
* big endian. Ex. 192.168.0.1 should be
* passed as 0xC0A80001.
*
* Output: Begins the process of transmitting an ICMP echo
* request. This normally involves an ARP
* resolution procedure first.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
void ICMPSendPing(DWORD dwRemoteIP)
{
ICMPFlags.bReplyValid = 0;
ICMPTimer = TickGet();
StaticVars.ICMPRemote.IPAddr.Val = dwRemoteIP;
ICMPState = SM_ARP_SEND_QUERY;
}
#if defined(STACK_USE_DNS)
/*********************************************************************
* Function: void ICMPSendPingToHost (BYTE * szRemoteHost)
*
* PreCondition: ICMPBeginUsage() returned TRUE
*
* Input: szRemoteHost: Host name to ping. Must be stored
* in RAM if being called by PIC18.
* Ex. www.microchip.com
*
* Output: Begins the process of transmitting an ICMP echo
* request. This normally involves an ARP
* resolution procedure first.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
void ICMPSendPingToHost(BYTE * szRemoteHost)
{
ICMPFlags.bReplyValid = 0;
ICMPTimer = TickGet();
ICMPFlags.bRemoteHostIsROM = 0;
StaticVars.RemoteHost.szRAM = szRemoteHost;
ICMPState = SM_DNS_SEND_QUERY;
}
#if defined(__18CXX)
/*********************************************************************
* Function: void ICMPSendPingToHostROM (ROM BYTE * szRemoteHost)
*
* PreCondition: ICMPBeginUsage() returned TRUE
*
* Input: szRemoteHost: Host name to ping. Must be stored
* in ROM. Should only be called by PIC18.
* Ex. www.microchip.com
*
* Output: None
*
* Side Effects: None
*
* Overview: Begins the process of transmitting an ICMP echo
* request. This normally involves an ARP
* resolution procedure first.
*
* Note: None
********************************************************************/
void ICMPSendPingToHostROM(ROM BYTE * szRemoteHost)
{
ICMPFlags.bReplyValid = 0;
ICMPTimer = TickGet();
ICMPFlags.bRemoteHostIsROM = 1;
StaticVars.RemoteHost.szROM = szRemoteHost;
ICMPState = SM_DNS_SEND_QUERY;
}
#endif
#endif
/*********************************************************************
* Function: LONG ICMPGetReply(void)
*
* PreCondition: ICMPBeginUsage() returned TRUE and ICMPSendPing()
* was called
*
* Input: None
*
* Output: -3: Could not resolve hostname (DNS timeout or
* hostname invalid)
* -2: No response received yet
* -1: Operation timed out (longer than ICMP_TIMEOUT)
* has elapsed.
* >=0: Number of TICKs that elapsed between
* initial ICMP transmission and reception of
* a valid echo.
*
* Side Effects: None
*
* Overview: None
*
* Note: None
********************************************************************/
LONG ICMPGetReply(void)
{
ICMP_PACKET ICMPPacket;
switch(ICMPState)
{
#if defined(STACK_USE_DNS)
case SM_DNS_SEND_QUERY:
// Obtain DNS module ownership
if(!DNSBeginUsage())
break;
// Send DNS query
if(ICMPFlags.bRemoteHostIsROM)
DNSResolveROM(StaticVars.RemoteHost.szROM, DNS_TYPE_A);
else
DNSResolve(StaticVars.RemoteHost.szRAM, DNS_TYPE_A);
ICMPState = SM_DNS_GET_RESPONSE;
break;
case SM_DNS_GET_RESPONSE:
// See if DNS is done, and if so, get the remote IP address
if(!DNSIsResolved(&StaticVars.ICMPRemote.IPAddr))
break;
// Free the DNS module
DNSEndUsage();
// Return error code if the DNS query failed
if(StaticVars.ICMPRemote.IPAddr.Val == 0x00000000ul)
{
ICMPState = SM_IDLE;
return -3;
}
ICMPState = SM_ARP_SEND_QUERY;
// No break;
#endif
case SM_ARP_SEND_QUERY:
ARPResolve(&StaticVars.ICMPRemote.IPAddr);
ICMPState = SM_ARP_GET_RESPONSE;
break;
case SM_ARP_GET_RESPONSE:
// See if the ARP reponse was successfully received
if(!ARPIsResolved(&StaticVars.ICMPRemote.IPAddr, &StaticVars.ICMPRemote.MACAddr))
break;
ICMPState = SM_ICMP_SEND_ECHO_REQUEST;
// No break;
case SM_ICMP_SEND_ECHO_REQUEST:
if(!IPIsTxReady())
break;
// Set up the ping packet
ICMPPacket.vType = 0x08; // 0x08: Echo (ping) request
ICMPPacket.vCode = 0x00;
ICMPPacket.wChecksum = 0x0000;
ICMPPacket.wIdentifier = 0xEFBE;
wICMPSequenceNumber++;
ICMPPacket.wSequenceNumber = wICMPSequenceNumber;
ICMPPacket.wData = 0x2860;
ICMPPacket.wChecksum = CalcIPChecksum((BYTE*)&ICMPPacket, sizeof(ICMPPacket));
// Record the current time. This will be used as a basis for
// finding the echo response time, which exludes the ARP and DNS
// steps
ICMPTimer = TickGet();
// Position the write pointer for the next IPPutHeader operation
MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER));
// Create IP header in TX memory
IPPutHeader(&StaticVars.ICMPRemote, IP_PROT_ICMP, sizeof(ICMPPacket));
MACPutArray((BYTE*)&ICMPPacket, sizeof(ICMPPacket));
MACFlush();
// Echo sent, advance state
ICMPState = SM_ICMP_GET_ECHO_RESPONSE;
break;
case SM_ICMP_GET_ECHO_RESPONSE:
// See if the echo was successfully received
if(ICMPFlags.bReplyValid)
return (LONG)ICMPTimer;
break;
// SM_IDLE or illegal/impossible state:
default:
return -1;
}
// See if the DNS/ARP/echo request timed out
if(TickGet() - ICMPTimer > ICMP_TIMEOUT)
{
// Free DNS module if we have it in use
#if defined(STACK_USE_DNS)
if(ICMPState == SM_DNS_GET_RESPONSE)
DNSEndUsage();
#endif
// Stop ICMP echo test and return error to caller
ICMPState = SM_IDLE;
return -1;
}
// Still working. No response to report yet.
return -2;
}
/*********************************************************************
* Function: BOOL ICMPBeginUsage(void)
*
* PreCondition: None
*
* Input: None
*
* Output: TRUE: You have successfully gained ownership of
* the ICMP client module and can now use the
* ICMPSendPing() and ICMPGetReply() functions.
* FALSE: Some other application is using the ICMP
* client module. Calling ICMPSendPing()
* will corrupt the other application's ping
* result.
*
* Side Effects: None
*
* Overview: Claims ownership of the ICMP module.
*
* Note: None
********************************************************************/
BOOL ICMPBeginUsage(void)
{
if(ICMPFlags.bICMPInUse)
return FALSE;
ICMPFlags.bICMPInUse = TRUE;
return TRUE;
}
/*********************************************************************
* Function: void ICMPEndUsage(void)
*
* PreCondition: ICMPBeginUsage() was called by you and it
* returned TRUE.
*
* Input: None
*
* Output: Your ownership of the ICMP module is released.
* You can no longer use ICMPSendPing().
*
* Side Effects: None
*
* Overview: Gives up ownership of the ICMP module.
*
* Note: None
********************************************************************/
void ICMPEndUsage(void)
{
ICMPFlags.bICMPInUse = FALSE;
}
#endif //#if defined(STACK_USE_ICMP_CLIENT)
#endif //#if defined(STACK_USE_ICMP_SERVER) || defined(STACK_USE_ICMP_CLIENT)
|