⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 icmp.c

📁 umon bootloader source code, support mips cpu.
💻 C
字号:
/* icmp.c:
 *	Support some basic ICMP stuff.
 *
 *	General notice:
 *	This code is part of a boot-monitor package developed as a generic base
 *	platform for embedded system designs.  As such, it is likely to be
 *	distributed to various projects beyond the control of the original
 *	author.  Please notify the author of any enhancements made or bugs found
 *	so that all may benefit from the changes.  In addition, notification back
 *	to the author will allow the new user to pick up changes that may have
 *	been made by other users after this version of the code was distributed.
 *
 *	Note1: the majority of this code was edited with 4-space tabs.
 *	Note2: as more and more contributions are accepted, the term "author"
 *		   is becoming a mis-representation of credit.
 *
 *	Original author:	Ed Sutter
 *	Email:				esutter@lucent.com
 *	Phone:				908-582-2351
 */
#include "config.h"
#if INCLUDE_ETHERNET
#include "endian.h"
#include "genlib.h"
#include "ether.h"
#include "stddefs.h"
#include "cli.h"
#include "timer.h"

#define MSECS_PER_HOUR 3600000
#define MSECS_PER_MINUTE 60000
#define MSECS_PER_SECOND 1000

#define ICMP_TIME_ID		0x1111		/* made these up. */
#define ICMP_ECHO_ID		0x2222
#define ICMP_ECHO_DATASIZE	26

static ulong	ICMPTimeStamp;
static int		CheckSrvrResolution;
ushort			ICMPEchoReplyId, ICMPSeqNoRcvd;
int				SendICMPRequest(uchar,uchar *,uchar *,ulong,ushort);

int SendEchoResp(struct ether_header *);

/* Destination unreachable codes: (3rd Edition Comer pg 129) */
char	*IcmpDestUnreachableCode[] = {
		"Network unreachable",
		"Host unreachable",
		"Protocol unreachable",
		"Port unreachable",
		"Fragmentation needed and DF set",
		"Source route failed",
		"???",
};

char *IcmpHelp[] = {
	"ICMP interface",
	"-[c:d:f:rv:] {operation} [args]",
#if INCLUDE_VERBOSEHELP
	"Options...",
	" -c {#}    echo count",
	" -d {#}    delta (hours) relative to GMT",
	" -f {x|d}  hex or decimal output (default is ascii)",
	" -r        check server resolution",
	" -v {var}  load result in shellvar 'var'",
	"Operations...",
	" time {IP}",
	" echo {IP}",
	"Notes...",
	" If '.ns' is appended to time, then time is non-standard.",
	" The 'dfrv' options are used by icmp time, option 'c' is used by echo.",
#endif
	0,
};

int
Icmp(int argc,char *argv[])
{
	ushort	seqno;
	struct	elapsed_tmr tmr;
	uchar	binip[8], binenet[8], buf[32];
	int		timestamp, hour, min, sec, opt, gmt_delta, count;
	char	*varname, format, *nonstandard, *operation, *ipadd;

	gmt_delta = 0;
	count = 1;
	varname = (char *)0;
	format = 'a';
	nonstandard = "";
	CheckSrvrResolution = 0;

	while ((opt=getopt(argc,argv,"c:d:f:rv:")) != -1) {
		switch(opt) {
		case 'c': 	/* count used by ping */
			count = strtol(optarg,(char **)0,0);
			break;
		case 'd': 	/* difference between this time zone and Greenwich */
			gmt_delta = strtol(optarg,(char **)0,0);
			break;
		case 'f':
			format = *optarg;
			break;
		case 'r':
			CheckSrvrResolution = 1;
			break;
		case 'v':
			varname = optarg;
			break;
		default:
			return(CMD_PARAM_ERROR);
		}
	}

	if (argc == optind)
		return(-1);

	operation = argv[optind];
	ipadd = argv[optind+1];
	
	/* If time or echo, do the common up-front stuff here... */
	if (!strcmp(operation,"time") || !strcmp(operation,"echo")) {
		if (argc != optind + 2)
			return(CMD_PARAM_ERROR);

		/* Convert IP address to binary: */
		if (IpToBin(ipadd,binip) < 0)
			return(CMD_SUCCESS);
	}

	if (!strcmp(operation, "time")) {

		/* Get the ethernet address for the IP: */
		if (!ArpEther(binip,binenet,0)) {
			printf("ARP failed for %s\n",ipadd);
			return(CMD_FAILURE);
		}

		ICMPTimeStamp = INVALID_TIMESTAMP;
		SendICMPRequest(ICMP_TIMEREQUEST,binip,binenet,0,0);

		/* Timeout waiting for timestamp after waiting about 2 seconds. */
		startElapsedTimer(&tmr,2000);
		while(msecElapsed(&tmr)) {
			pollethernet();
			if (ICMPTimeStamp != INVALID_TIMESTAMP)
				break;
		}
		if (ELAPSED_TIMEOUT(&tmr)) {
			printf("No response\n");
			return(CMD_FAILURE);
		}

		if (ICMPTimeStamp & NONSTANDARD_TIMESTAMP) {
			ICMPTimeStamp &= ~NONSTANDARD_TIMESTAMP;
			nonstandard = ".ns";
		}

		if (format == 'a') {
			timestamp = ICMPTimeStamp + (gmt_delta*MSECS_PER_HOUR);
			hour = timestamp/MSECS_PER_HOUR;
			timestamp -= hour*MSECS_PER_HOUR;
			min = timestamp/MSECS_PER_MINUTE;
			timestamp -= min*MSECS_PER_MINUTE;
			sec = timestamp/MSECS_PER_SECOND;
			timestamp -= sec*MSECS_PER_SECOND;
			sprintf(buf,"%02d:%02d:%02d.%03d%s",hour,min,sec,timestamp,
				nonstandard);
		}
		else if (format == 'x')
			sprintf(buf,"0x%lx%s",ICMPTimeStamp,nonstandard);
		else if (format == 'd')
			sprintf(buf,"%ld%s",ICMPTimeStamp,nonstandard);
		else
			return(CMD_PARAM_ERROR);
		if (varname)
			setenv(varname,buf);
		else if (!CheckSrvrResolution)
			printf("%s\n",buf);
		CheckSrvrResolution = 0;
	}
	else if (!strcmp(operation, "echo")) {
		seqno = 0;
		while (count-- > 0) {

			/* Is this a self-ping? */
			if (memcmp(binip,BinIpAddr,4) == 0) {
				printf("Yes, I am alive!\n");
				break;
			}
			/* Get the ethernet address for the IP: */
			if (!ArpEther(binip,binenet,0)) {
				printf("ARP failed for %s\n",ipadd);
				if (varname)
					setenv(varname,"NOANSWER");
				continue;
			}

			ICMPEchoReplyId = 0;
			SendICMPRequest(ICMP_ECHOREQUEST,binip,binenet,0,seqno);

			/* Timeout waiting for reply after waiting about 2 secs. */
			startElapsedTimer(&tmr,2000);
			while(!msecElapsed(&tmr)) {
				pollethernet();
				if (ICMPEchoReplyId == ICMP_ECHO_ID)
					break;
			}

			if (ELAPSED_TIMEOUT(&tmr)) {
				printf("no answer from %s\n",ipadd);
				if (varname)
					setenv(varname,"NOANSWER");
			}
			else {
				printf("%s is alive",ipadd);
				if (count) {
					printf(" icmp_seq=%d",ICMPSeqNoRcvd);
					startElapsedTimer(&tmr,1000);
					while(!msecElapsed(&tmr))
						pollethernet();
					seqno++;
				}
				if (varname)
					setenv(varname,"ALIVE");
				printf("\n");
			}
		}
	}
	else
		printf("Unrecognized ICMP op: %s\n",operation);
	return(CMD_SUCCESS);
}

int
processICMP(struct ether_header *ehdr,ushort size)
{
	struct	ip *ipp;
	struct	icmp_hdr *icmpp;
	int	len, i;

	ipp = (struct ip *)(ehdr + 1);

	/* Compute size of ICMP message (IP len - size of IP header): */
	len = ipp->ip_len - ((ipp->ip_vhl & 0x0f) * 4);

	icmpp = (struct icmp_hdr *)((char *)ipp+IP_HLEN(ipp));
	if (icmpp->type == ICMP_ECHOREQUEST) {		/* 3rdEd Comer pg 127 */
		SendEchoResp(ehdr);
		return(0);
	}
	else if (icmpp->type == ICMP_ECHOREPLY) {
		struct icmp_echo_hdr *icmpecho;

		icmpecho = (struct icmp_echo_hdr *)icmpp;
		ICMPEchoReplyId = ecs(icmpecho->id);
		ICMPSeqNoRcvd = ecs(icmpecho->seq);
	}
	else if (icmpp->type == ICMP_DESTUNREACHABLE) { /* 3rdEd Comer pg 129 */
		if (EtherVerbose & SHOW_INCOMING) {
			i = icmpp->code;
			if ((i > 5) || (i < 0))
				i = 6;
			printf("  ICMP: %s (code=%d)\n",
			    IcmpDestUnreachableCode[i],icmpp->code);
		}
		return(0);
	}
	else if (icmpp->type == ICMP_TIMEREPLY) {
		struct icmp_time_hdr *icmptime;
		ulong orig, recv, xmit;

		icmptime = (struct icmp_time_hdr *)icmpp;
		memcpy((char *)&orig,(char *)&icmptime->orig,4);
		memcpy((char *)&recv,(char *)&icmptime->recv,4);
		memcpy((char *)&xmit,(char *)&icmptime->xmit,4);

		ICMPSeqNoRcvd = icmptime->seq;
		if (EtherVerbose & SHOW_INCOMING) {
			printf("    ICMP_TIMEREPLY: orig=0x%lx,recv=0x%lx,xmit=0x%lx\n",
				orig,recv,xmit);
		}
		if (CheckSrvrResolution) {
			static int ICMPTimeStampTbl[20];
	
			if (icmptime->seq < 20) {
				ICMPTimeStampTbl[icmptime->seq] = xmit;
				SendICMPRequest(ICMP_TIMEREQUEST,(uchar *)&ipp->ip_src,
					(uchar *)&ehdr->ether_shost,0,icmptime->seq+1);
			} else {
				
				printf("TS00: %d\n",ICMPTimeStampTbl[0]);
				for(i=1;i<20;i++) {
					printf("TS%02d: %d (dlta=%d)\n",i,
						ICMPTimeStampTbl[i],
						ICMPTimeStampTbl[i]-ICMPTimeStampTbl[i-1]);
				}
				ICMPTimeStamp = xmit;
			}
		}
		else {
			ICMPTimeStamp = xmit;
		}
	}
	else {
		if (EtherVerbose & SHOW_INCOMING) {
			printf("    ICMPTYPE=%d\n",icmpp->type);
		}
		return(-1);
	}
	return(0);
}

/* SendEchoResp():
 * 	Called in response to an ICMP ECHO REQUEST.  Typically used as
 *	a response to a PING.
 */
int
SendEchoResp(struct ether_header *re)
{
	int i, icmp_len, datalen, oddlen;
	ulong	t;
	ushort	*sp, ip_len;
	struct ether_header *te;
	struct ip *ti, *ri;
	struct icmp_echo_hdr *ticmp, *ricmp;

	ri = (struct ip *) (re + 1);
	datalen = ecs(ri->ip_len) - ((ri->ip_vhl & 0x0f) * 4);

	te = EtherCopy(re);
	ti = (struct ip *) (te + 1);
	ti->ip_vhl = ri->ip_vhl;
	ti->ip_tos = ri->ip_tos;
	ti->ip_len = ri->ip_len;
	ti->ip_id = ipId();
	ti->ip_off = 0;
	ti->ip_ttl = UDP_TTL;
	ti->ip_p = IP_ICMP;
	memcpy((char *)&(ti->ip_src.s_addr),(char *)&(ri->ip_dst.s_addr),
		sizeof(struct in_addr));
	memcpy((char *)&(ti->ip_dst.s_addr),(char *)&(ri->ip_src.s_addr),
		sizeof(struct in_addr));

	ticmp = (struct icmp_echo_hdr *) (ti + 1);
	ricmp = (struct icmp_echo_hdr *) (ri + 1);
	ticmp->type = ICMP_ECHOREPLY;
	ticmp->code = 0;
	ticmp->cksum = 0;
	ticmp->id = ricmp->id;
	ticmp->seq = ricmp->seq;
	memcpy((char *)(ticmp+1),(char *)(ricmp+1),datalen-8);

	ip_len = ecs(ti->ip_len);
	if (ip_len & 1) {		/* If size is odd, temporarily bump up ip_len by */
		oddlen = 1;			/* one and add append a null byte for csum. */ 
		((char *)ti)[ip_len] = 0;
		ip_len++;
	}
	else
		oddlen = 0;
	icmp_len = (ip_len - sizeof(struct ip))/2;

	ipChksum(ti);	/* compute checksum of ip hdr: (3rd Edition Comer pg 100) */

	/* compute checksum of icmp message: (3rd Edition Comer pg 126) */
	ticmp->cksum = 0;
	sp = (ushort *) ticmp;
	for (i=0,t=0;i<icmp_len;i++,sp++)
		t += ecs(*sp);
	t = (t & 0xffff) + (t >> 16);
	ticmp->cksum = ~t;

	self_ecs(ticmp->cksum);

	if (oddlen)
		ip_len--;

	sendBuffer(sizeof(struct ether_header) + ip_len);
	if (EtherVerbose & SHOW_OUTGOING)
		printf("  Sent Echo Response\n");
	return(0);
}

/* SendICMPRequest():
 *	Currently supports ICMP_TIMEREQUEST and ICMP_ECHOREQUEST.
 */
int
SendICMPRequest(uchar type,uchar *binip,uchar *binenet,
				ulong origtime,ushort seq)
{
	ulong	csum;
	ushort	*sp;
	uchar	*data;
	int		i, icmp_len;
	struct	ip *ti;
	struct	ether_header *te;
	struct	icmp_time_hdr *ticmp;

	/* Retrieve an ethernet buffer from the driver and populate the */
	/* ethernet level of packet: */
	te = (struct ether_header *) getXmitBuffer();
	memcpy((char *)&te->ether_shost,BinEnetAddr,6);
	memcpy((char *)&te->ether_dhost,binenet,6);
	te->ether_type = ecs(ETHERTYPE_IP);

	/* Move to the IP portion of the packet and populate it appropriately: */
	ti = (struct ip *) (te + 1);
	ti->ip_vhl = IP_HDR_VER_LEN;
	ti->ip_tos = 0;
	if (type == ICMP_TIMEREQUEST)
		ti->ip_len = sizeof(struct ip) + sizeof(struct icmp_time_hdr);
	else 
		ti->ip_len = sizeof(struct ip) + sizeof(struct icmp_echo_hdr) +
			ICMP_ECHO_DATASIZE;

	ti->ip_id = ipId();
	ti->ip_off = 0;
	ti->ip_ttl = UDP_TTL;
	ti->ip_p = IP_ICMP;
	memcpy((char *)&ti->ip_src.s_addr,BinIpAddr,4);
	memcpy((char *)&ti->ip_dst.s_addr,binip,4);

	icmp_len = (ti->ip_len - sizeof(struct ip))/2;
	self_ecs(ti->ip_len);
	self_ecs(ti->ip_off);
	self_ecs(ti->ip_id);

	ipChksum(ti);		/* Compute csum of ip hdr */

	/* Move to the ICMP portion of the packet and populate it appropriately: */
	ticmp = (struct icmp_time_hdr *)(ti+1);
	ticmp->type = type;
	ticmp->code = 0;
	ticmp->seq = ecs(seq);
	if (type == ICMP_TIMEREQUEST) {
		ticmp->id = ICMP_TIME_ID;
		memcpy((char *)&ticmp->orig,(char *)&origtime,4);
		memset((char *)&ticmp->recv,0,4);
		memset((char *)&ticmp->xmit,0,4);
	}
	else {
		ticmp->id = ICMP_ECHO_ID;

		/* Add ICMP_ECHO_DATASIZE bytes of data... */
		data = (uchar *)((struct icmp_echo_hdr *)ticmp+1);
		for(i=0;i<ICMP_ECHO_DATASIZE;i++)
			data[i] = 'a'+i;

	}

	/* compute checksum of icmp message: (3rd Edition Comer pg 126) */
	csum = 0;
	ticmp->cksum = 0;
	sp = (ushort *) ticmp;
	for (i=0;i<icmp_len;i++,sp++)
		csum += ecs(*sp);
	csum = (csum & 0xffff) + (csum >> 16);
	ticmp->cksum = ~csum;

	self_ecs(ticmp->cksum);

	if (type == ICMP_TIMEREQUEST) {
		self_ecl(ticmp->orig);
		self_ecl(ticmp->recv);
		self_ecl(ticmp->xmit);
		sendBuffer(ICMP_TIMERQSTSIZE);
	}
	else
		sendBuffer(ICMP_ECHORQSTSIZE+ICMP_ECHO_DATASIZE);
	return(0);
}

/* Send an ICMP Unreachable message. All ICMP Unreachable messages
 * include the IP header and 64 bits of the datagram that could not be
 * delivered.
 *
 * 're' is the pointer to the packet that was received in response to which
 * the unreachable message is being sent. 'icmp_code' is the code of the
 * ICMP message.
 */
int
SendICMPUnreachable(struct ether_header *re,uchar icmp_code)
{
	int i, len;
	ushort *sp, r_iphdr_len, ip_len;
	ulong t;
	struct ether_header *te;
	struct ip *ti, *ri;
	struct icmp_unreachable_hdr *ticmp;

	ri = (struct ip *) (re + 1);

	/* Length of the received IP hdr */
	r_iphdr_len = ((ri->ip_vhl & 0x0f)<<2);

	te = EtherCopy(re);

	ti = (struct ip *) (te + 1);
	ti->ip_vhl = IP_HDR_VER_LEN;
	ti->ip_tos = 0;
	/* Length of the outgoing IP packet = IP header + ICMP header + 
	 * IP header and 8 bytes of unreachable datagram 
	 */
	ip_len = (IP_HDR_LEN<<2)+sizeof(struct icmp_unreachable_hdr)+r_iphdr_len+8;
	ti->ip_len = ecs(ip_len);
	ti->ip_id = ipId();
	ti->ip_off = 0;
	ti->ip_ttl = UDP_TTL;
	ti->ip_p = IP_ICMP;

	/* Note that we do memcpy instead of struct copy because the ip
	 * header is not not word-aligned. As a result, the source and dest
	 * addresses are not word aligned either. The compiler generates word
	 * copy instructions for struct copy and this causes address alignement
	 * exceptions.
	 */
	memcpy((char *)&(ti->ip_src.s_addr),(char *)&(ri->ip_dst.s_addr),
		sizeof(struct in_addr));
	memcpy((char *)&(ti->ip_dst.s_addr),(char *)&(ri->ip_src.s_addr),
		sizeof(struct in_addr));

	ticmp = (struct icmp_unreachable_hdr *) (ti + 1);
	ticmp->type = ICMP_DESTUNREACHABLE;
	ticmp->code = icmp_code;
	ticmp->cksum = 0;
	ticmp->unused1=0;
	ticmp->unused2=0;

	/* Copy the IP header and 8 bytes of the received datagram */
	memcpy((char *)(ticmp+1),(char *)ri,r_iphdr_len+8);

	ipChksum(ti);	/* compute checksum of ip hdr */

	/* compute checksum of icmp message: (see Comer pg 91) */
	len = (ip_len - sizeof(struct ip))/2;
	ticmp->cksum = 0;
	sp = (ushort *) ticmp;
	for (i=0,t=0;i<len;i++,sp++)
		t += ecs(*sp);
	t = (t & 0xffff) + (t >> 16);
	ticmp->cksum = ~t;
	self_ecs(ticmp->cksum);

	sendBuffer(sizeof(struct ether_header) + ip_len);

	if (EtherVerbose & SHOW_OUTGOING)
		printf("  Sent ICMP Dest Unreachable message. Code='%s'.\n",
		    IcmpDestUnreachableCode[icmp_code]);
	return(0);
}

#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -