lwip源码分析 之 IP协议 数据输入(一)

一 导读

ip层是lwip代码的核心领域,它负责将以太网数据传递上来的数据整理并传递给传输层,或者将数据转发到其他网络。其中涉及ICMP,IGMP,DHCP等辅助协议。这章先介绍ip层的输入函数,搞清楚以太网将数据传递给ip层后,ip层如何将数据整理或转发。

二,输入代码

1,接口函数

网络接口接收到数据后,调用以下函数将数据传递给ip层。该函数将根据ipv4 or ipv6 调用真正的处理函数。

//以太网调用该函数
err_t
ip_input(struct pbuf *p, struct netif *inp)
{
    
    
  if (p != NULL) {
    
    
    if (IP_HDR_GET_VERSION(p->payload) == 6) {
    
    
      return ip6_input(p, inp);
    }
    return ip4_input(p, inp);
  }
  return ERR_VAL;
}

2,ipv4 输入

该函数首先检查输入ip数据报的首部各项是否正确,再判断是否给本地的数据,最后将数据交给传输层。详细的逻辑看代码注释

err_t
//pbuf的payload指向ip首部,inp:数据传入的接口
ip4_input(struct pbuf *p, struct netif *inp)
{
    
    
  struct ip_hdr *iphdr; //输入分组的首部
  struct netif *netif;  
  u16_t iphdr_hlen;   //首部长度
  u16_t iphdr_len;  //总长度
  int check_ip_src = 1;	//是否检查ip数据源地址

  iphdr = (struct ip_hdr *)p->payload;  //获取ip首部
  //判断ip首部是否是ipv4
  if (IPH_V(iphdr) != 4) {
    
    
    pbuf_free(p);
    return ERR_OK;
  }

  iphdr_hlen = IPH_HL(iphdr); //获取首部长度(字为单位)
  iphdr_hlen *= 4;  //将ip首部转换为字节单位
  iphdr_len = lwip_ntohs(IPH_LEN(iphdr)); //获取ip总字节数

  //若pbuf的总长度大于ip首部标志的总长度,则修剪pbuf的长度(因为其中有ip的填充数据)
  if (iphdr_len < p->tot_len) {
    
    
    pbuf_realloc(p, iphdr_len);
  }

  //检查ip首部是否错误:1,ip首部的数据必须放在一个pbuf中。2,ip首部长度符合?
  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
    
    
    pbuf_free(p);
    return ERR_OK;
  }

#if CHECKSUM_CHECK_IP
 	//检验ip分组的校验和
  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
    
    
    if (inet_chksum(iphdr, iphdr_hlen) != 0) {
    
     
      pbuf_free(p);
      return ERR_OK;
    }
  }
#endif

  //将输入数据的ip源和目的地址复制到全局变量中,方便处理
  ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
  ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);

  //接下来确定网络接口netif
  if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
    
    
    //ip分组的目的地址是多播地址
    //TODO inp接口开启了IGMP且加入了该组播,则该分组就是给inp的
    if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
    
    
    
      ip4_addr_t allsystems;
      IP4_ADDR(&allsystems, 224, 0, 0, 1);  //224.0.0.1代表本地子网所有主机;
      if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) &&
          ip4_addr_isany(ip4_current_src_addr()))
      {
    
     
        //报文dest = 224.0.0.1 ;src =0.0.0.0 说明是网络中的路由器发送的数据
        check_ip_src = 0;
      }
      netif = inp;  //inp就是ip分组对应的接口
    } else {
    
    
      netif = NULL;
    }
  
  } else {
    
      //不是多播地址

    int first = 1;
    //先判断接收到该分组的接口是否就是分组的目的地址,再从netif链表中遍历,确定netif
    netif = inp;  
    do {
    
    
      //接口已经使能且配置完成?
      if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))){
    
    
        //若分组目的地址是 单播给该接口 或 广播地址 或是netif中的广播地址 则确定就是该netif接口
        if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
            ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
            ) {
    
    
          break;  //netif就是对应的接口,跳出循环
        }
#if LWIP_AUTOIP
        /* connections to link-local addresses must persist after changing
           the netif's address (RFC3927 ch. 1.9) */
        if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
    
    
          LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
              netif->name[0], netif->name[1]));
          /* break out of for loop */
          break;
        }
#endif /* LWIP_AUTOIP */
      }
      if (first) {
    
    
        //开始遍历netif链表
#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
        /* Packets sent to the loopback address must not be accepted on an
         * interface that does not have the loopback address assigned to it,
         * unless a non-loopback interface is used for loopback traffic. */
        //对于loopback 特殊处理
        if (ip4_addr_isloopback(ip4_current_dest_addr())) {
    
    
          netif = NULL;
          break;
        }
#endif 
        first = 0;
        netif = netif_list; //获取netif_list的第一个接口
      } else {
    
    
        netif = netif->next;  //匹配下一个接口
      }
      if (netif == inp) {
    
    
        netif = netif->next;  //跳过inp,因为之前已经匹配失败
      }
    } while (netif != NULL);
  }

#if IP_ACCEPT_LINK_LAYER_ADDRESSING

  //dhcp消息:使用udp传输,dhcp服务端口67,客户端口68
  //之前的步骤匹配不到对应接口,判断是否是dhcp服务器发送的dhcp消息
  if (netif == NULL) {
    
    
    //该分组是udp数据
    if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
    
     
      struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);  //获取udp首部
      //且udp目的端口是68,说明这时服务器发送的dhcp消息
      if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
    
    
        netif = inp;
        check_ip_src = 0; //不检查分组的源地址
      }
    }
  }
#endif 

  //检查源地址合法性
  if (check_ip_src && !ip4_addr_isany_val(*ip4_current_src_addr())) //源地址不是0.0.0.0;正常的ip数据
  {
    
    
    //若数据源地址是广播数据,不合法
    if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
        (ip4_addr_ismulticast(ip4_current_src_addr()))) {
    
    
      pbuf_free(p);
      return ERR_OK;
    }
  }

  //没有对应的接口,分组不是给本地的,转发
  if (netif == NULL) {
    
    
	//不是广播的数据才能转发
    if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
    
    
      ip4_forward(p, iphdr, inp); //转发分组
    } 
    pbuf_free(p); //释放内存
    return ERR_OK;
  }
	//到这,说明数据报是给本地的
  //数据包是否是一个分片?(根据MF标志和分片偏移判断)
  if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
    
    

    //!嵌入式互联网中的数据量较小,一般不会有数据分片
#if IP_REASSEMBLY /* packet fragment reassembly code present? */
    p = ip4_reass(p); //重组ip分片
    if (p == NULL) {
    
    
      return ERR_OK;
    }
    iphdr = (struct ip_hdr *)p->payload;
#else 
    pbuf_free(p); //不重装ip分组
    return ERR_OK;
#endif 
  }
  //更新全局变量
  ip_data.current_netif = netif;
  ip_data.current_input_netif = inp;
  ip_data.current_ip4_header = iphdr;
  ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;

#if LWIP_RAW
  /* raw input did not eat the packet? */
  if (raw_input(p, inp) == 0)
#endif /* LWIP_RAW */
  {
    
    
    //移动payload后移到上层数据首部
    pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
    //传递给上层协议
    switch (IPH_PROTO(iphdr)) {
    
    
#if LWIP_UDP
    case IP_PROTO_UDP:
#if LWIP_UDPLITE
    case IP_PROTO_UDPLITE:
#endif /* LWIP_UDPLITE */
      udp_input(p, inp);
      break;
#endif /* LWIP_UDP */
#if LWIP_TCP
    case IP_PROTO_TCP:

      tcp_input(p, inp);
      break;
#endif /* LWIP_TCP */
#if LWIP_ICMP
    case IP_PROTO_ICMP:
     
      icmp_input(p, inp);
      break;
#endif /* LWIP_ICMP */
#if LWIP_IGMP
    case IP_PROTO_IGMP:
      igmp_input(p, inp, ip4_current_dest_addr());
      break;
#endif /* LWIP_IGMP */
    default:
#if LWIP_ICMP
	  //发送icmp协议不可达报文,除非对方是个广播
      if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
          !ip4_addr_ismulticast(ip4_current_dest_addr())) {
    
    
        pbuf_header_force(p, iphdr_hlen); //将payload重新指向ip首部
        p->payload = iphdr;
        icmp_dest_unreach(p, ICMP_DUR_PROTO);//发送协议不可达报文
      }
#endif /* LWIP_ICMP */
      pbuf_free(p);	//最后释放报文
    }
  }

  /* @todo: this is not really necessary... */
  ip_data.current_netif = NULL;
  ip_data.current_input_netif = NULL;
  ip_data.current_ip4_header = NULL;
  ip_data.current_ip_header_tot_len = 0;
  ip4_addr_set_any(ip4_current_src_addr());
  ip4_addr_set_any(ip4_current_dest_addr());

  return ERR_OK;
}

3,ipv6 输入

猜你喜欢

转载自blog.csdn.net/weixin_44821644/article/details/111638632