LwIP之ICMP协议

随声附和 提交于 2019-12-12 10:03:13

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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!