欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

LwIP之ICMP协议

程序员文章站 2024-02-22 18:31:58
...



ICMP(网际控制报文协议),ICMP数据包是封装在IP数据包中的,由于IP不是为可靠传输服务设计的,ICMP的目的主要是用于在TCP/IP网络中发送和控制消息。主要应用有Ping、Traceroute和MTU测试。

ICMP报文的种类有三大种类,即ICMP差错报文、控制报文、请求/应答报文,各大类型报文又分为多种类型报文。

LwIP之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字节的字段对所有类型通用。

LwIP之ICMP协议

类型:标识了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;
}
相关标签: LwIP