LWIPudp_input函数分析

Posted Evan_ZGYF丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LWIPudp_input函数分析相关的知识,希望对你有一定的参考价值。

收录于:

【LWIP】LWIP协议|相关知识汇总|LWIP学习笔记


​​​​​​​

相关链接:

【LWIP】Ip4_input函数分析

——|【LWIP】pbuf_realloc函数分析

【LWIP】tcp_input()函数分析

——|【LWIP】tcp_timewait_input函数解析

——|【LWIP】tcp_listen_input函数分析

【LWIP】tcp_receive函数分析

【LWIP】udp_input函数分析


调用流程:

    数据包首先调用ethernet_input()函数到达数据链路层,去掉以太网头部;
    根据以太网头部类型判断:如果是ARP报文传给调用arp_input()交给ARP协议处理,如果是IP报文就调用ip_input()进入IP层处理;
    ip_input()函数中比较数据报文的目的IP地址,如果与某个网络接口的IP地址相同,则接收这个报文,依照IP头部的协议字段,调用各自协议的输入处理函数;
    如果是UDP类型,调用udp_input()。


函数简析:

    该函数接收来自ip层的UDP包。将所有PCB都遍历,如果有多个绑定,则给每一个进程复制一份数据报,实际调用pcb->recv()。


具体分析:

    1.检测数据包有效性(小于UDP头长度的,free);
    2.获取UDP头,判断是否为广播包,网络字节序转换;
    3.遍历UDP PCB链表,
       将PCB本地addr+端口与UDP目的addr+端口进行比较(记录第一个完美匹配且未连接的PCB),
       若完美匹配,将PCB远程addr+端口与UDP源addr+端口进行比较(第一个完美匹配的PCB移动到UDP PCB链表前端,下次发现快),
       如果没有完美匹配,第一个完美匹配且未连接的PCB获取数据包,
    4.如果这是匹配的,或者它是指向我们的,,做UDP校验和
    5.判断是否是广播包/多播包(若是广播/多播包传递给所有匹配的UDP PCB链表,调用pcb->recv送上用户层),
       若不是广播包/多播包,调用对应的UDP接收函数(调用pcb->recv送上用户层)
    6.没有找到匹配,发送ICMP目标端口不可到达,除非目的地址是广播/多播。


流程图:

 


源码:

/**
 * 处理传入的UDP数据报。
 *
 * 给定一个传入的UDP数据报(作为pbufs的链),
 * 这个函数找到一个对应的UDP PCB,并将pbuf指针交给pcbs recv函数(实际调用pcb->recv())。
 * 如果没有发现pcb,或者数据报不正确,pbuf就会被释放。
 *
 * @参数 p 传入的UDP数据报(p-指向UDP报头)
 * @参数 inp 接收数据报的网络接口。
 *
 */
void
udp_input(struct pbuf *p, struct netif *inp)

  struct udp_hdr *udphdr;    //UDP报头
  struct udp_pcb *pcb, *prev;
  struct udp_pcb *uncon_pcb;
  u16_t src, dest;    //源,目的端口
  u8_t broadcast;    //广播报文(标志位)
  u8_t for_us = 0;

  LWIP_UNUSED_ARG(inp);

  PERF_START;

  UDP_STATS_INC(udp.recv);

  /* 1.检测UDP数据包有效性 */
  /* 检查最小长度(UDP报头) */
  if (p->len < UDP_HLEN) 
    /* 丢弃报文长度 < UDP标准报头长度的报文(错误报文) */
    LWIP_DEBUGF(UDP_DEBUG,
                ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\\n", p->tot_len));
    UDP_STATS_INC(udp.lenerr);
    UDP_STATS_INC(udp.drop);
    MIB2_STATS_INC(mib2.udpinerrors);
    pbuf_free(p);
    goto end;
  

  /* 2.获取UDP头,判断是否为广播包,网络字节序转换 */
  /* 指向UDP报头 */
  udphdr = (struct udp_hdr *)p->payload;

  /* 是否广播报文 ?(标志位) */
  broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());

  LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\\n", p->tot_len));

  /* 将网络字节序转换为主机字节序 */
  src = lwip_ntohs(udphdr->src);
  dest = lwip_ntohs(udphdr->dest);

  udp_debug_print(udphdr);

  /* 打印源端口,目的端口 */
  LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
  ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr());
  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
  ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr());
  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\\n", lwip_ntohs(udphdr->src)));

  pcb = NULL;
  prev = NULL;
  uncon_pcb = NULL;
  /* 3.(记录第一个完美匹配且未连接的PCB)
   * 将PCB本地addr+端口与UDP目的addr+端口进行比较(记录第一个未连接的匹配PCB),
   * 将PCB远程addr+端口与UDP源addr+端口进行比较(记录第一个完美匹配的PCB),
   * 如果没有完美匹配,第一个完美匹配且未连接的PCB获取数据包, */
  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) 
    /* 打印PCB本地和远程地址 */
    LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
    ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip);
    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
    ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip);
    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\\n", pcb->remote_port));

    /* 将PCB本地addr+端口与UDP目的地addr+端口进行比较 */
    if ((pcb->local_port == dest) &&
        (udp_input_local_match(pcb, inp, broadcast) != 0)) 
      if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) &&
          ((uncon_pcb == NULL)
#if SO_REUSE
          /* PCB本地addr不是任意IP地址 */
          || !ip_addr_isany(&pcb->local_ip)
#endif /* SO_REUSE */
          )) 
        /* 第一个未连接的匹配PCB */
        uncon_pcb = pcb;
      

      /* 将PCB远程addr+端口与UDP源addr+端口进行比较 */
      if ((pcb->remote_port == src) &&
          (ip_addr_isany_val(pcb->remote_ip) ||
          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) 
        /* 第一个完全匹配的PCB */
        if (prev != NULL) 
          /* 把pcb移到udp_pcbs的前面,这样就可以下次发现快 */
          prev->next = pcb->next;
          pcb->next = udp_pcbs;
          udp_pcbs = pcb;
         else 
          UDP_STATS_INC(udp.cachehit);
        
        break;
      
    

    prev = pcb;
  
  /* 没有完全匹配的pcb?然后寻找一个未连接的pcb */
  if (pcb == NULL) 
    pcb = uncon_pcb;
  

  /* 4.如果这是匹配的,或者它是指向我们的(检查校验和) */
  if (pcb != NULL) 
    for_us = 1;
   else 
#if LWIP_IPV6
    if (ip_current_is_v6()) 
      for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
    
#endif /* LWIP_IPV6 */
#if LWIP_IPV4
    if (!ip_current_is_v6()) 
      for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
    
#endif /* LWIP_IPV4 */
  

  /* 4.如果这是匹配的,或者它是指向我们的,做UDP校验和 */
  /* 确定为给我们的UDP报文 */
  if (for_us) 
    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\\n"));
#if CHECKSUM_CHECK_UDP
    IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) 
#if LWIP_UDPLITE
      if (ip_current_header_proto() == IP_PROTO_UDPLITE) 
        /* 做UDP Lite校验和 */
        u16_t chklen = lwip_ntohs(udphdr->len);
        if (chklen < sizeof(struct udp_hdr)) 
          if (chklen == 0) 
            /* 对于udp-lite,校验和的长度是对完整包的长度(请参阅RFC 3828章,3.1)。 */
            chklen = p->tot_len;
           else 
            /* 至少,udp-lite头必须由校验和覆盖!(再次,请参阅RFC 3828章,3.1) */
            goto chkerr;
          
        
        if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE,
                     p->tot_len, chklen,
                     ip_current_src_addr(), ip_current_dest_addr()) != 0) 
          goto chkerr;
        
       else
#endif /* LWIP_UDPLITE */
      
    	/* 做UDP校验和 */
        if (udphdr->chksum != 0) 
          if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
                               ip_current_src_addr(),
                               ip_current_dest_addr()) != 0) 
            goto chkerr;
          
        
      
    
#endif /* CHECKSUM_CHECK_UDP */

    /* 去除UDP报头 */
    if (pbuf_header(p, -UDP_HLEN)) 
      /* Can we cope with this failing? Just assert for now */
      LWIP_ASSERT("pbuf_header failed\\n", 0);
      UDP_STATS_INC(udp.drop);
      MIB2_STATS_INC(mib2.udpinerrors);
      pbuf_free(p);
      goto end;
    

    /* 5.判断是否是广播包/多播包(若是广播/多播包传递给所有匹配的UDP PCB链表,调用pcb->recv送上用户层),
       若不是广播包/多播包,调用对应的UDP接收函数(调用pcb->recv送上用户层), */
    if (pcb != NULL) 
      MIB2_STATS_INC(mib2.udpindatagrams);
#if SO_REUSE && SO_REUSE_RXTOALL
      if (ip_get_option(pcb, SOF_REUSEADDR) &&
          (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) 
        /* 如果设置了SOF_REUSEADDR,将广播或多播数据包传送给所有的多播pcbs。*/
        struct udp_pcb *mpcb;
        u8_t p_header_changed = 0;
        s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN);
        for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) 
          if (mpcb != pcb) 
            /* 将PCB本地addr+端口与UDP目的地addr+端口进行比较 */
            if ((mpcb->local_port == dest) &&
                (udp_input_local_match(mpcb, inp, broadcast) != 0)) 
              /* 将数据包拷贝给所有本地匹配项 */
              if (mpcb->recv != NULL) 
                struct pbuf *q;
                /* 为此,将p指针移动到IP报头 */
                if (p_header_changed == 0) 
                  pbuf_header_force(p, hdrs_len);
                  p_header_changed = 1;
                
                q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
                if (q != NULL) 
                  err_t err = pbuf_copy(q, p);
                  if (err == ERR_OK) 
                    /* 将p指针转移到UDP数据 */
                    pbuf_header(q, -hdrs_len);
                    mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
                  
                
              
            
          
        
        if (p_header_changed) 
          /* 再次将p指针转移到UDP数据 */
          pbuf_header(p, -hdrs_len);
        
      
#endif /* SO_REUSE && SO_REUSE_RXTOALL */
      /* 回调 */
      if (pcb->recv != NULL) 
        /* 现在,recv函数负责释放p */
        pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
       else 
        /* 没有recv函数注册?那么我们就得释放pbuf! */
        pbuf_free(p);
        goto end;
      
     else 
      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\\n"));

#if LWIP_ICMP || LWIP_ICMP6
      /* 6.没有找到匹配,发送ICMP目标端口不可到达,除非
       * 目的地址是广播/多播。 */
      if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) 
        /* 将p指针转移到UDP数据 */
        pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
        icmp_port_unreach(ip_current_is_v6(), p);
      
#endif /* LWIP_ICMP || LWIP_ICMP6 */
      UDP_STATS_INC(udp.proterr);
      UDP_STATS_INC(udp.drop);
      MIB2_STATS_INC(mib2.udpnoports);
      pbuf_free(p);
    
   else 
    pbuf_free(p);
  
end:
  PERF_STOP("udp_input");
  return;
#if CHECKSUM_CHECK_UDP
chkerr:
  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
              ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\\n"));
  UDP_STATS_INC(udp.chkerr);
  UDP_STATS_INC(udp.drop);
  MIB2_STATS_INC(mib2.udpinerrors);
  pbuf_free(p);
  PERF_STOP("udp_input");
#endif /* CHECKSUM_CHECK_UDP */

 

以上是关于LWIPudp_input函数分析的主要内容,如果未能解决你的问题,请参考以下文章

数据分析挖掘的作用和意义?

算法设计与分析总结

基于小波变换的信号分离

如何有效地分析相互递归函数

如何有效的运用大数据

为了分析目的,我可以人为地减慢 C 三角函数的速度吗?