Ping命令实现之Lwip—基于stm32F103
1、Ping命令原理
ping命令属于Network layer的ICMP(Internet control massage protol)协议构成,ICMP报文可以分为两大类:
ICMP报文种类 | type | 功能* |
---|---|---|
错误报告报文 | 3 | 目的站不可达 |
4 | 源站抑制 | |
5 | 重定向(改变路由) | |
11 | 数据包超时 | |
12 | 数据包参数错误 | |
查询报文 | 0/8 | 会话请求或回答 |
9/10 | 路由器询问或通告 | |
13/14 | 时间戳请求或回答 | |
15/16 | 信息请求或回答 | |
17/18 | 地址掩码请求或回答 |
ICMP报文格式
Ping 命令在ICMP报文中的类型
ping命令主要是用来测试两个IP地址之间的网络是否能够连通,确定具有一条通路。在Windows
控制台中我们可以使用Ping命令来测试我们的主机是否能与其他主机通信,格式如下:
ping xxx.xxx.xxx.xxx(dest IP_addr)
在测试TCP/IP协议栈是否移植成功时,一个简便的方法就是测试 ping
命令是否能够应答成功,效果如下:
Ping命令的帧格式
先给出ping命令的帧格式
type
:指ICMP报文的类型,例如ICMP两大类报文:差错报告报文和查询报文,其中含有许多不同类型的功能报文,因此对其分类。code
:对于每种不同类型(type)的报文在又含有不同的编码信息,例如差错报告报文中的数据包超时报文,就可分为两种:数据包分片超时和TTL超时。Identifier
:标识符,没有指明其正式定义,可自行定义。Seq number
:序列号,没有正式指明其定义,可自行定义。
2、代码设计
MCU启动Ping数据包接收或发送
void Ping_Init(void)
{
#if FUNC_DBG_ON
printf("run in %s .\r\n",__FUNCTION__);
#endif
IP4_ADDR(&dest_ip,192,168,1,31);
IP4_ADDR(&local_ip,192,168,1,10);
/*bind local IP.*/
printf("local IP address:%4d.%4d.%4d.%4d\r\n",ip4_addr1(&local_ip),\
ip4_addr2(&local_ip),\
ip4_addr3(&local_ip),\
ip4_addr4(&local_ip));
/*bind dest IP.*/
printf("dest IP address:%4d.%4d.%4d.%4d\r\n", ip4_addr1(&dest_ip),\
ip4_addr2(&dest_ip),\
ip4_addr3(&dest_ip),\
ip4_addr4(&dest_ip));
/*allocate a new pcb*/
ping_pcb = raw_new(IP_PROTO_ICMP);//set IP protocol type to ICMP
if(NULL == ping_pcb) {
printf("get n new raw pcb failed.\r\n");
return;
}
PingCmd_start(ping_pcb);
}
void PingCmd_start(struct raw_pcb *pcb)
{
#if FUNC_DBG_ON
printf("run in %s .\r\n",__FUNCTION__);
#endif
raw_bind(pcb,&local_ip);//bind local IP to pcb.
// raw_connect(pcb,&dest_ip);
raw_recv(pcb,recv_callback,NULL);
// ping_cnt--;
/*sys_timeout()为注册一个单次定时事件,当计时结束后,会主动发送REQUEST包,PC会给予响应
* 接收Ping命令时关闭,MCU发起ping请求时打开。
*/
// sys_timeout(PING_DELAY,timeoutHandle,pcb);//set a oneshot time event for periodicly.
}
MCU接收数据
在Windows终端对MCU已设置好的IP地址进行ping命令调试,PC端发出的ICMP报文中type应为0x08
,code为0x00
,identifier和seq number可随意指定,故在MCU端接收代码可设置为如下格式:
u8_t recv_callback(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr)
{ // p->payload point the IP header/Ethernet frame data field.
struct icmp_echo_hdr *icmp_hdr = NULL;
struct ip_hdr *ip_hdr = NULL;
u8_t ip_hlen = 0;
u8_t *data_ptr = NULL;
#if FUNC_DBG_ON
printf("run in %s .\r\n",__FUNCTION__);
#endif
(void)arg;
if(ip_addr_cmp(&dest_ip,addr) &&\
p->tot_len > (IP_HLEN + sizeof(struct icmp_echo_hdr)))//process packet
{
ip_hdr = (struct ip_hdr *)p->payload; //point to IP header
ip_hlen = IPH_HL(ip_hdr)*4; //1 len means 4 bytes.
//adjust p->payload point ICMP header.
if(pbuf_header(p,-ip_hlen))
printf("Adjust header failed.\r\n");
icmp_hdr = (struct icmp_echo_hdr *)p->payload;
if(0x00 == icmp_hdr->code)
{
data_ptr = (u8_t*)p->payload + sizeof(struct icmp_echo_hdr);
printf("icmp_hdr->type %d .\r\n",icmp_hdr->type);
switch (icmp_hdr->type)
{
case PING_REPLY: //receive a reply message.
printf("[Receive message]:%s.\r\n",data_ptr);
printf("[ID]:%x.\r\n",icmp_hdr->id);
printf("[SEQ Number]:%x.\r\n",icmp_hdr->seqno);
sys_timeout(PING_DELAY,timeoutHandle,pcb);//continue to send a ping REQUEST packet.
break;
case PING_REQUEST: //need ack
printf("Receive a REQUEST message.\r\n");
printf("[Receive message]:%s.\r\n",data_ptr);
printf("[ID]:%x.\r\n",icmp_hdr->id);
printf("[SEQ Number]:%x.\r\n",icmp_hdr->seqno);
/*change type,reset checksum*/
ICMPH_TYPE_SET(icmp_hdr,PING_REPLY);//set type.
/*checksum*/
if(icmp_hdr->chksum >= htons(0xffffU - (PING_REQUEST<<8)))
icmp_hdr->chksum +=htons(PING_REQUEST<<8 + 1);
else
icmp_hdr->chksum +=htons(PING_REQUEST<<8);
raw_sendto(pcb,p,addr);
break;
default:
break;
}
}
else
printf("[ERROR]:It's not a ping format packet.\r\n");
}
pbuf_free(p);
return 1;//delete pbuf.
}
该代码块流程如下:
从代码块中可以看出,在处理接收pbuf时与发送pbuf时不同。发送时申请的pbuf为IP层pbuf模型,Lwip默认将IP首部预留给底层函数添加其信息,而payload指针直接指向其数据区;接收时,下层处理函数直接将完整的pbuf数据包传递给pcb控制块,关于为何没有直接将payload指向IP数据区,有待研究。
PC发送ping命令REQUEST包—Wireshark抓包测试
- PC端IP:
192.168.1.31
- MCU端IP:
192.168.1.10
PC终端发起ping请求:
WireShark抓包结果
MCU发起Ping请求
ping数据区域值为This a ping test,only for REQUSET message.
,由于MCU是在上电启动后自动执行ping命令,直接给出WireShark抓包结果。
查看数据是否正确
从结果来看,PC端正确接收数据,至此关于MCU发起、接收ping命令结束,当然,MCU程序设计还有些许不足,没有对ping包进行数量限制,而是持续发送。
工程源码
链接:https://pan.baidu.com/s/1ez-gm1TW0rLBo6xK2aNGFA?pwd=lfds
提取码:lfds