LwIP之ICMP协议
ICMP(网际控制报文协议),ICMP数据包是封装在IP数据包中的,由于IP不是为可靠传输服务设计的,ICMP的目的主要是用于在TCP/IP网络中发送和控制消息。主要应用有Ping、Traceroute和MTU测试。
ICMP报文的种类有三大种类,即ICMP差错报文、控制报文、请求/应答报文,各大类型报文又分为多种类型报文。
差错报文:
(1) 特点:
1.ICMP差错报文都是有路由器发送到源主机的。
2.ICMP报文只提供IP数据报的差错报告,并不采取处理措施,差错处理由应用程序处理。
3.传输过程中可能丢失、损坏,甚至被抛弃。
4.ICMP差错报文是伴随着抛弃出错的IP数据报而产生的。
5.为了防止广播风暴,以下情况不会产生ICMP差错报文。
1)ICMP差错报文 2)目的地址是广播或多播 3)链路层广播的数据报 4)不是IP分片的第一片 5)源地址是零地址、回送地址、广播地址或多播地址
(2)信息不可达报文:
1.目的机硬件故障或关机
2.目标地址不存在
3.网关不知道去往目的机的路径
(3)超时报文:
1.为了避免无限制的在网中循环,IP协议采用
1)在数据报头设置TTL域 2)对分片数据报采用定时器技术
2.当报文超时出现时,路由器或目的机立即丢弃该数据报,并向信源机发送超时报文
控制报文:
(1)拥塞控制与源站控制报文:
1.当路由器接收比处理快或者传入比传出快时就会产生拥塞
2.路由器通过发送源站抑制报文来抑制主机发送速率
3.源主机一段时间内没有收到抑制报文,便认为抑制解除,逐渐恢复原来的数据流量
(2)路由控制与重定向报文:
1.当源主机要向目标主机发送IP数据报时,则把IP数据报发送给默认路器1,再由路由器1经过选路发送给路由器2,路由器2发送给目标主机
2.如果路由器1和路由器2在同一网络,路由器1发现源主机可直接发送IP数据报给路由器2,就会向源主机发送重定向报文,以后源主机将直接发送IP报文给路由器2
请求/应答报文:
(1)回送请求与应答报文:
1.测试目标主机和路由器是否可以到达
(2)时戳请求与应答报文:
1.同步互联网中各个主机的时钟
(3)地址掩码请求和应答报文
1.用于无盘系统在引导过程中获取子网掩码。启动时广播地址掩码请求,路由器收到请求后回送一个包含32位地址掩码应答报文
报文格式,ICMP报文由8字节首部和可变长数据部分组成。不同类型的ICMP报文,首部格式有一定差异,但是首部前4字节的字段对所有类型通用。
类型:标识了ICMP具体类型
代码:进一步指出产生该类型ICMP的原因
校验和:整个ICMP的校验和
LWIP实现了ICMP的部分功能:信息不可达、超时报文、回送查询报文
/* 目的站不可达 */
void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{
icmp_send_response(p, ICMP_DUR, t);
}
/* 超时 */
void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{
icmp_send_response(p, ICMP_TE, t);
}
/* 发送ICMP响应 */
static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
{
struct pbuf *q;
struct ip_hdr *iphdr;
/* we can use the echo header here */
struct icmp_echo_hdr *icmphdr;
ip4_addr_t iphdr_src;
struct netif *netif;
/* 为ICMP数据包申请内存 */
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, PBUF_RAM);
if (q == NULL)
{
return;
}
/* 取出IP头部 */
iphdr = (struct ip_hdr *)p->payload;
/* 装填IP头部 */
icmphdr = (struct icmp_echo_hdr *)q->payload;
icmphdr->type = type;
icmphdr->code = code;
icmphdr->id = 0;
icmphdr->seqno = 0;
/* 拷贝IP首部和数据区前8字节 */
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
/* 源ip */
ip4_addr_copy(iphdr_src, iphdr->src);
/* 查找合适的网络接口 */
netif = ip4_route(&iphdr_src);
if (netif != NULL)
{
/* CRC计算 */
icmphdr->chksum = 0;
icmphdr->chksum = inet_chksum(icmphdr, q->len);
/* 发送数据包 */
ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
}
/* 释放数据包 */
pbuf_free(q);
}
/* ICMP输入 */
void icmp_input(struct pbuf *p, struct netif *inp)
{
u8_t type;
struct icmp_echo_hdr *iecho;
const struct ip_hdr *iphdr_in;
u16_t hlen;
const ip4_addr_t *src;
/* IP首部长度 */
iphdr_in = ip4_current_header();
hlen = IPH_HL_BYTES(iphdr_in);
if (hlen < IP_HLEN) {
goto lenerr;
}
if (p->len < sizeof(u16_t) * 2) {
goto lenerr;
}
/* 判断类型 */
type = *((u8_t *)p->payload);
switch (type)
{
/* 差错报文 */
case ICMP_ER:
break;
/* 回送查询 */
case ICMP_ECHO:
src = ip4_current_dest_addr();
/* 组播地址不响应 */
if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
goto icmperr;
}
/* 广播地址不响应 */
if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
goto icmperr;
}
/* 合法性检查 */
if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
goto lenerr;
}
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP)
{
if (inet_chksum_pbuf(p) != 0) {
pbuf_free(p);
return;
}
}
/* 填充数据包 */
iecho = (struct icmp_echo_hdr *)p->payload;
if (pbuf_add_header(p, hlen)) {
} else {
err_t ret;
struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
ip4_addr_copy(iphdr->src, *src);
ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
ICMPH_TYPE_SET(iecho, ICMP_ER);
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);
} else {
iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS(ICMP_ECHO << 8));
}
}
IPH_TTL_SET(iphdr, ICMP_TTL);
IPH_CHKSUM_SET(iphdr, 0);
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
}
/* 发送数据包 */
ret = ip4_output_if(p, src, LWIP_IP_HDRINCL, ICMP_TTL, 0, IP_PROTO_ICMP, inp);
if (ret != ERR_OK) {
}
}
break;
}
pbuf_free(p);
return;
lenerr:
pbuf_free(p);
return;
}
上一篇: FreeRTOS队列使用
下一篇: java实现多个文件压缩成压缩包