icmp.c

来自「本程序是一个RS232转网口的。是一个透明传输的模块」· C语言 代码 · 共 273 行

C
273
字号
/*
*****************************************************************************************************
*
*  File name: icmp.c
*****************************************************************************************************
*/
#include "..\head\includes.h"


#define MaxLenPingBuf   10			//PING缓冲区长度

xdata struct ping_table_type ping_table[MaxLenPingBuf];	   //ping表,在外部RAM里
xdata union Ethernet_address ping_ethernet_address;	       //用来ping的以太网地址
xdata union IP_address ping_ip_address;	                   //用于ping命令

/*
*****************************************************************************************************
*FUNC: 初始化PING表
*NOTE: 
*****************************************************************************************************
*/
void InitPingTable(void)
{
	unsigned char i;
	for (i=0; i<MaxLenPingBuf; i++) {
		ping_table[i].status=0;
	}
}
/*
*****************************************************************************************************
*FUNC: 生成ICMP包头CRC校验
*NOTE: 返回值:	CRC校验值
*****************************************************************************************************
*/
unsigned int createicmpcrc()//生成ICMP包CRC校验
{
	unsigned char i;
	union w crctemp;

	crctemp.dwords=0;
	for(i=19;i<39;i++)
		crctemp.dwords=crctemp.dwords+txdnet.words.wordbuf[i];
	while(crctemp.words.high>0)
    	crctemp.dwords=(unsigned long)(crctemp.words.high+crctemp.words.low);
	crctemp.words.low=0xffff-crctemp.words.low;
	return(crctemp.words.low);
}

/*
*****************************************************************************************************
*FUNC: PING请求,实际上,该函数只是在缓冲区里构造一个ICMP包,然后在PING_TABLE里登记该PING
//			记录,该ICMP包的发送是在PING_CYCLE里实现的
*NOTE: 在PING_CYCLE里添查表添加PINGIP的MAC
*****************************************************************************************************
*/
void PingRequest(void)
{
	unsigned char  i;
//	txdnet.etherframe.destnodeid[0]=ping_ethernet_address.words[0];
//	txdnet.etherframe.destnodeid[1]=ping_ethernet_address.words[1];
//	txdnet.etherframe.destnodeid[2]=ping_ethernet_address.words[2];
	txdnet.etherframe.protocal=0x0800;
	txdnet.ipframe.verandihl=0x45;
	txdnet.ipframe.typeofserver=0x00;
	txdnet.ipframe.totallength=60;
    txdnet.ipframe.ttl=0x80;
	txdnet.ipframe.frameindex=frameindex;
	frameindex++;
	txdnet.ipframe.segment=0x0000;
	txdnet.ipframe.protocal=0x0001;                     //icmp
    txdnet.ipframe.crc=0;
    txdnet.ipframe.destip[0]=ping_ip_address.words[0];  //PING ip
    txdnet.ipframe.destip[1]=ping_ip_address.words[1];
    txdnet.ipframe.sourceip[0]=my_ip_address.words[0];
    txdnet.ipframe.sourceip[1]=my_ip_address.words[1];
    txdnet.ipframe.crc=createipheadcrc(&txdnet);
    txdnet.icmpframe.type=0x08;         // is icmp request;
	txdnet.icmpframe.option=0x00;		//该句由Mingtree加
    txdnet.icmpframe.crc=0;
	txdnet.icmpframe.id=0x0300;
	txdnet.icmpframe.seq=frameindex;
    txdnet.icmpframe.crc=createicmpcrc();
	
	for (i=0; i<MaxLenPingBuf; i++) {                        //将该包登记入ping表里
        if (ping_table[i].status == 0) {
            ping_table[i].times=0x3;						 //测试4次
            ping_table[i].ip.dwords=ping_ip_address.dwords;	 //ping命令传入的IP地址;
            ping_table[i].pack=&txdnet;						 //发送缓冲区地址
            ping_table[i].status=4;							 //第一次准备发(用于同步1秒时钟)
            break;
        }
    }
}


/*
*****************************************************************************************************
*FUNC: PING应答
*NOTE: 		//从16开始的原因:1字节的接收状态,1字节下一页指针,2字节以太网包长度,
		//12字节MAC地址,加起来为16字节.接着的信息为包类型,可以拷贝
*****************************************************************************************************
*/
void PingAnswer(void)
{
	unsigned char i;

	if (rxdnet.icmpframe.type == 0x08) {                             //表示是ping请求
    	for (i=16;i<rxdnet.etherframe.uLength;i++) {                 //将数据复制到发送缓冲区
 	    	txdnet.bytes.bytebuf[i]=rxdnet.bytes.bytebuf[i];
		}
    	txdnet.etherframe.uDestID[0]=rxdnet.etherframe.uSourceID[0]; //目的MAC
	    txdnet.etherframe.uDestID[1]=rxdnet.etherframe.uSourceID[1];
    	txdnet.etherframe.uDestID[2]=rxdnet.etherframe.uSourceID[2];

		txdnet.etherframe.uSourceID[0]=rxdnet.etherframe.uDestID[0]; //源MAC
	    txdnet.etherframe.uSourceID[1]=rxdnet.etherframe.uDestID[1];
    	txdnet.etherframe.uSourceID[2]=rxdnet.etherframe.uDestID[2];

	    txdnet.ipframe.ttl=txdnet.ipframe.ttl-1;                     //生存时间
    	txdnet.ipframe.crc=0;
	    txdnet.ipframe.destip[0]=rxdnet.ipframe.sourceip[0];  
    	txdnet.ipframe.destip[1]=rxdnet.ipframe.sourceip[1];
	    txdnet.ipframe.sourceip[0]=my_ip_address.words[0];
    	txdnet.ipframe.sourceip[1]=my_ip_address.words[1];
	    txdnet.ipframe.crc=createipheadcrc(&txdnet);
    	txdnet.icmpframe.type=0x00;                                  // 0:ACK  8:请求
		txdnet.icmpframe.option=0x00;                                // 固定为0配合TYPE
	    txdnet.icmpframe.crc=0;
		txdnet.icmpframe.crc=createicmpcrc();
    	send_packet(&txdnet,rxdnet.etherframe.uLength);
	}
}

/*
*****************************************************************************************************
*FUNC: 重新建立PING数据包
*NOTE: 入参:	需要重构的表索引 
//注意:	在进行ping时,如果要先发送一个ARP数据包,则会对由ping_requeset()函数形成的
//			PING包进行破坏,该函数的功能就是重新构造PING包。构造该包的信息在PING_TABLE里
*****************************************************************************************************
*/
void reconstruct_ping(unsigned char table_index)
{
	txdnet.etherframe.protocal=0x0800;
	txdnet.ipframe.verandihl=0x45;
	txdnet.ipframe.typeofserver=0x00;
	txdnet.ipframe.totallength=60;
    txdnet.ipframe.ttl=0x80;
	txdnet.ipframe.frameindex=frameindex;
	frameindex++;
	txdnet.ipframe.segment=0x0000;
	txdnet.ipframe.protocal=0x0001;//icmp
    txdnet.ipframe.crc=0;
    txdnet.ipframe.destip[0]=ping_table[table_index].ip.words[0];
    txdnet.ipframe.destip[1]=ping_table[table_index].ip.words[1];
    txdnet.ipframe.sourceip[0]=my_ip_address.words[0];
    txdnet.ipframe.sourceip[1]=my_ip_address.words[1];
    txdnet.ipframe.crc=createipheadcrc(&txdnet);
    txdnet.icmpframe.type=0x08;// is icmp request;
	txdnet.icmpframe.option=0x00;		//该句由Mingtree加
    txdnet.icmpframe.crc=0;
	txdnet.icmpframe.id=0x0300;
	txdnet.icmpframe.seq=frameindex;
    txdnet.icmpframe.crc=createicmpcrc();
}

/*
*****************************************************************************************************
*FUNC: PING回显,该函数在收到包,判断包为PING回显后调用,对PING表里的状态进行改变
*NOTE: //PING应答收到后回显
*****************************************************************************************************
*/
void PingEcho(void)
{
	unsigned char i;

	temp_ip_address.words[0]=rxdnet.ipframe.sourceip[0];
	temp_ip_address.words[1]=rxdnet.ipframe.sourceip[1];

	for (i=0; i<MaxLenPingBuf; i++) {
	    if ((ping_table[i].status == 1) && (ping_table[i].ip.dwords == temp_ip_address.dwords)) {
		    ping_table[i].status=2;
		    break;
		}
	}
	HintMsg(1,PingIpStr);
}

/*
*****************************************************************************************************
*FUNC: 定时操作,放在1秒循环任务中
*NOTE: 用于对PING_TABLE进行操作,该函数是处理PING的最重要函数 
	for (i=0; i<MaxLenPingBuf; i++) {                        //将该包登记入ping表里
        if (ping_table[i].status == 0) {
            ping_table[i].times=0x4;						 //测试4次
            ping_table[i].ip.dwords=ping_ip_address.dwords;	 //ping命令传入的IP地址;
            ping_table[i].pack=&txdnet;						 //发送缓冲区地址
            ping_table[i].status=4;							 //第一次准备发(用于同步1秒时钟)
            break;
        }
    }

	1:准备PIN时,置状态为4:
	2:要4时,如果有对应的MAC,置1,如果没有发ARP请求,置3
	3:下一秒进来时,如果是3,查找对应的MAC,如果没有说明ARP没响应,状态置1
    4:发完一包,状态置1
	如果收到PING应答包,会处理void PingEcho(void),如果是该PING IP的应答包,置2说明PING成功
*****************************************************************************************************
*/
void CopyMac2PingTable(unsigned char num)
{
    unsigned char i;

	for (i=0; i<3; i++) {
		ping_table[num].pack->etherframe.uDestID[i]=ping_ethernet_address.words[i]; //填写发送包的MAC地址
		ping_table[num].pack->etherframe.uSourceID[i]=my_ethernet_address.words[i];  //网卡地址
	}

	#if 0
		ping_table[i].pack->etherframe.uDestID[0]=ping_ethernet_address.words[0]; //填写发送包的MAC地址
		ping_table[i].pack->etherframe.uDestID[1]=ping_ethernet_address.words[1];
		ping_table[i].pack->etherframe.uDestID[2]=ping_ethernet_address.words[2];
		ping_table[i].pack->etherframe.uSourceID[0]=my_ethernet_address.words[0];  //网卡地址
	    ping_table[i].pack->etherframe.uSourceID[1]=my_ethernet_address.words[1];
	    ping_table[i].pack->etherframe.uSourceID[2]=my_ethernet_address.words[2];
	#endif
}

void PingCycle(void)                                          //定时操作,放在1秒循环任务中
{
	unsigned char  i;

	for (i=0; i<MaxLenPingBuf; i++) {                         //对PING_TABLE表进行扫
		switch (ping_table[i].status) {
		    case 0:                                           //空闲,直接返回
			     break;
		    case 1:		                                      //已发出但无应答(过了1s还未应答),输出超时
			     HintMsg(2,PingIpStr);                        //在这里添加串口发送代码,先上位机显示主机不可达                          
			     //break;                                     //失败PING 4 次
		    case 2:		                                      //发出且应答.注意,如果收到回显应答,则在PING_ECHO里进行处理,对状态进行修改
			     if (ping_table[i].times > 0) {
				     ping_table[i].times--;                   //成功PING 4 次
				 } else {
				     ping_table[i].status=0;
				     break;
			     }
		    case 4:		                                      //第一次准备发(用于同步1秒时钟)
			     if (FindMac(ping_table[i].ip,&ping_ethernet_address)) {                    //查ARP缓存
                     reconstruct_ping(i);                     //因为发送缓冲区的内容已经发生改变,因此重新构造PING包
					 CopyMac2PingTable(i);                    //拷贝MAC到ETH头部
				     send_packet(ping_table[i].pack,74);      //进行发送
				     ping_table[i].status=1;                  //改变状态
			     } else {
				     ArpRequest(ping_table[i].ip);            //ARP表里没有对应项,发送ARP请求
				     ping_table[i].status=3;                  //改变状态
			     }
			     break;
		    case 3:		                                      //等待ARP
			     if (FindMac(ping_table[i].ip,&ping_ethernet_address)) {  //查ARP缓存
				     reconstruct_ping(i);                     //因为发送缓冲区的内容已经发生改变,因此重新构造PING包
					 CopyMac2PingTable(i);
				     send_packet(ping_table[i].pack,74);      //进行发送	
			     }
			     ping_table[i].status=1;                      //已经发出ARP,但是没发回,改变状态
			     break;
		    default:
			     ping_table[i].status=0;
				 break;
		}
	}
}

⌨️ 快捷键说明

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