Lwip协议详解(基于Lwip 2.1.0)-ICMP协议 (未完待续)

4、ICMP协议

4.1 原理

IP协议提供的是一种不可靠无连接的数据服务,在IP数据报被交互到最终目的主机的过程中,网络中每一个路由器都是自主运行,它们根据数据报中的目的IP地址为数据报选择的最佳路径。
ICMP报文是使用IP数据报来封装和发送的,携带ICMP报文的IP数据报完全像携带其他类型数据的数据报在网络中被转发,没有额外的可靠性和优先级。

ICMP报文包括:
(1)ICMP差错报文;
(2)ICMP查询报文;
常见查询报文:回送请求或回答、路由器询问和通告、时间戳请求或回答、信息请求或回答、地址请求或回答。

ICMP报文的两次封装:

Lwip协议详解(基于Lwip 2.1.0)-ICMP协议 (未完待续)_第1张图片
ICMP报文格式:

Lwip协议详解(基于Lwip 2.1.0)-ICMP协议 (未完待续)_第2张图片
ICMP报文分类
Lwip协议详解(基于Lwip 2.1.0)-ICMP协议 (未完待续)_第3张图片
在windows控制台上使用ping命令来测试开发板上的协议栈是否移植成功,其本质就是向开发板发送一个ICMP回送请求报文,开发板接收到这样的报文后,会产生一个回答报文并返回给主机;当widows主机正确接收到这个回答报文时,说明移植成功。

ICMP回送请求或回答报文格式:

Lwip协议详解(基于Lwip 2.1.0)-ICMP协议 (未完待续)_第4张图片

4.2 Lwip的实现源码

引发超时的具体原因有两种:
(1)数据报TTL为0;
(2)分片重装时间超时;
发送一个数据地址不可达差错报文

void
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{
  MIB2_STATS_INC(mib2.icmpoutdestunreachs);
  icmp_send_response(p, ICMP_DUR, t);
}

发送一个数据报超时差错报文:

void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{
  MIB2_STATS_INC(mib2.icmpouttimeexcds);
  icmp_send_response(p, ICMP_TE, t);
}

发送一个ICMP差错报文
(1)目的站不可达;
(2)数据报超时;
(3)源站抑制
(4)重定向(改变路由)
(5)数据报参数错误

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;

  /* increase number of messages attempted to send */
  MIB2_STATS_INC(mib2.icmpoutmsgs);

  /* ICMP header + IP header + 8 bytes of data */
  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
                 PBUF_RAM);
  if (q == NULL) {
    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
    MIB2_STATS_INC(mib2.icmpouterrors);
    return;
  }
  LWIP_ASSERT("check that first pbuf can hold icmp message",
              (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));

  iphdr = (struct ip_hdr *)p->payload;
  LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
  ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
  LWIP_DEBUGF(ICMP_DEBUG, (" to "));
  ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
  LWIP_DEBUGF(ICMP_DEBUG, ("\n"));

  icmphdr = (struct icmp_echo_hdr *)q->payload;
  icmphdr->type = type;
  icmphdr->code = code;
  icmphdr->id = 0;
  icmphdr->seqno = 0;

  /* copy fields from original packet */
  SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
          IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);

  ip4_addr_copy(iphdr_src, iphdr->src);
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
  {
    ip4_addr_t iphdr_dst;
    ip4_addr_copy(iphdr_dst, iphdr->dest);
    netif = ip4_route_src(&iphdr_src, &iphdr_dst);
  }
#else
  netif = ip4_route(&iphdr_src);
#endif
  if (netif != NULL) {
    /* calculate checksum */
    icmphdr->chksum = 0;
#if CHECKSUM_GEN_ICMP
    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
      icmphdr->chksum = inet_chksum(icmphdr, q->len);
    }
#endif
    ICMP_STATS_INC(icmp.xmit);
    ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
  }
  pbuf_free(q);
}

你可能感兴趣的:(笔记)