
#include "sys_ping.h"
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "lwip/icmp.h"
#include "string.h"
#include "main.h"
#include "usart.h"
#include "timer.h"

void ETH_PingWork(void const * szDestIp) 
{
		long nRet = 0;
		char recvBuf[1024];	
		/*****************第一步：申请SOCKET************************************/	 
		uint32_t ip = inet_addr(szDestIp);	 
		SOCKET sRaw = lwip_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);	 
		if(sRaw == -1)	 
		{	 	
				printf("Cannot create socket! Error %d\r\n", errno);
		}
		// 设置接收超时	 
		struct timeval tv_out;	 
		tv_out.tv_sec = 5;	 
		tv_out.tv_usec = 0;	 
		nRet = setsockopt(sRaw, SOL_SOCKET,SO_RCVTIMEO, &tv_out, sizeof(tv_out));	 
		if(-1 == nRet)		 
		{	 
				printf("Cannot set timeout! %d \r\n",errno);		
		}
 
/*****************第二步：组ICMP包************************************/
 
		// 创建ICMP封包	 
		char buff[sizeof(ICMP_HDR) + 32];	 
		ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
		 
		// 填写ICMP封包数据	 
		pIcmp->icmp_type = 8; // 请求一个ICMP回显		 
		pIcmp->icmp_code = 0;		 
		pIcmp->icmp_id = (uint16_t)0x1234;		 
		pIcmp->icmp_checksum = 0;		 
		pIcmp->icmp_sequence = 0;	 
		pIcmp->icmp_timestamp = 0xffff;	 
		// 填充数据部分，可以为任意	 
		memset(&buff[sizeof(ICMP_HDR)], 'E', 32);
 
 
 
		/*****************第三步：发送ICMP包************************************/
	 
		//设置目的地址		 
		struct sockaddr_in dest;		 
		dest.sin_family = AF_INET;
		dest.sin_port = htons(0);		 
		dest.sin_addr.s_addr = ip;
		//pIcmp->icmp_checksum = checksum((uint16_t*)buff, sizeof(ICMP_HDR) + 32);   //如果ping过去没回应需要屏蔽这条		 
		//printf("icmp_checksum = %x\r\n",pIcmp->icmp_checksum);		 
		nRet = (long)sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (struct sockaddr *)&dest, sizeof(dest));		 
		if(nRet == -1)		 
		{	 
				printf(" sendto() failed: %d \r\n", errno);		 	 
		}	
		printf("sendto = %ld\r\n",nRet);

 
/*****************第四步：接收ICMP包************************************/
 
			 
		struct sockaddr_in from;		 
		socklen_t nLen = sizeof(from);
		while(1)
		{			
				nRet = (long)recvfrom(sRaw, recvBuf, 64, 0, (struct sockaddr *)&from, &nLen);		 
				if(nRet == -1)		 
				{		 
						printf(" recvfrom() failed: %d\r\n", errno);	
						goto __eixt;
				}			
				printf("recvfrom = %ld\r\n",nRet);
		 
		/*****************第五步：解析ICMP包************************************/
		 
				if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))		 
				{
						printf(" Too few bytes from %s \r\n", inet_ntoa(from.sin_addr));
				}
		#if 1 // IP头解析
				IPHeader * header = (IPHeader*)recvBuf;
				struct in_addr a;
				a.s_addr = header->ipSource;
				printf("source ip %s\n", inet_ntoa(a));
				a.s_addr = header->ipDestination;		 
				printf("dest ip %s\n", inet_ntoa(a)); 
		#endif 
				ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // 接收到的数据中包含IP头，IP头大小为20个字节，所以加20得到ICMP头	 
				if(pRecvIcmp->icmp_type != 0)// 回显	 
				{	 
						printf("nonecho type %d recvd \r\n", pRecvIcmp->icmp_type);					 
				}
				if(pRecvIcmp->icmp_id != 0x1234)		 
				{		 
						printf(" someone else's packet! \r\n");				 
				}
				printf(" %d bytes from %s:", (int)nRet, inet_ntoa(from.sin_addr));		 
				printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence);								
				printf(" \r\n");		
__eixt:
				memset(	recvBuf, 0, sizeof(recvBuf));	
				vTaskDelay(100);
	}
}

void ping_task_creat()
{
		osThreadDef(PING, ETH_PingWork, osPriorityBelowNormal, 0, configMINIMAL_STACK_SIZE * 4);
		osThreadCreate (osThread(PING), NULL);
}
 