天天看点

LWIP学习笔记(2)---IP协议实现细节

IP头

收到的数据首先保存在pbuf结构中,

/* The IPv4 header */
struct ip_hdr {
  /* version / header length */
  PACK_STRUCT_FLD_8(u8_t _v_hl);
  /* type of service */
  PACK_STRUCT_FLD_8(u8_t _tos);
  /* total length */
  PACK_STRUCT_FIELD(u16_t _len);
  /* identification */
  PACK_STRUCT_FIELD(u16_t _id);
  /* fragment offset field */
  PACK_STRUCT_FIELD(u16_t _offset);
#define IP_RF 0x8000U        /* reserved fragment flag */
#define IP_DF 0x4000U        /* don't fragment flag */
#define IP_MF 0x2000U        /* more fragments flag */
#define IP_OFFMASK 0x1fffU   /* mask for fragmenting bits */
  /* time to live */
  PACK_STRUCT_FLD_8(u8_t _ttl);
  /* protocol*/
  PACK_STRUCT_FLD_8(u8_t _proto);
  /* checksum */
  PACK_STRUCT_FIELD(u16_t _chksum);
  /* source and destination IP addresses */
  PACK_STRUCT_FLD_S(ip4_addr_p_t src);
  PACK_STRUCT_FLD_S(ip4_addr_p_t dest);
} PACK_STRUCT_STRUCT;
           

接收数据流程 ip_input

ip_input()调用 ip4_input() 或者 ip6_input().

ip4_input

ip4_input(struct pbuf *p, struct netif *inp)
           

在ip4_input()中,首先拿到缓冲区的实际数据:

iphdr = (struct ip_hdr *)p->payload;

接下来解析数据:

iphdr_hlen = IPH_HL(iphdr); //获取32位字数的IP头长度
iphdr_hlen *= 4;  //计算ip头长度
iphdr_len = lwip_ntohs(IPH_LEN(iphdr));  //以字节为单位获取ip长度
           

如果ip头长度超过第一个pbuf长度或者ip长度超过总pbuf长度,释放pbuf,返回.

接下来检查校验和:

IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
      if (inet_chksum(iphdr, iphdr_hlen) != 0) {

    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
      ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
    ip4_debug_print(p);
    pbuf_free(p);
    IP_STATS_INC(ip.chkerr);
    IP_STATS_INC(ip.drop);
    MIB2_STATS_INC(mib2.ipinhdrerrors);
    return ERR_OK;
  }
}
           

然后复制ip地址到ip_addr_t

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 = inp; 给netif结构体赋值,netif结构用来描述一个硬件网络接口.

不是我的包, 置 netif = NULL;

如果netif = NULL; 在判断是否需要转发该数据包:

if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
	ip4_forward(p, iphdr, inp);
} else{
        IP_STATS_INC(ip.drop);
        MIB2_STATS_INC(mib2.ipinaddrerrors);
        MIB2_STATS_INC(mib2.ipindiscards);
      }
      pbuf_free(p);
      return ERR_OK;  
  }
           

接下来判断包是否由多个分片组成.

是的话,组装数据包 p = ip4_reass§;

接下来判断端口号,根据端口号把数据交付给上层.

#define IP_PROTO_ICMP    1
  #define IP_PROTO_IGMP    2
  #define IP_PROTO_UDP     17
  #define IP_PROTO_UDPLITE 136
  #define IP_PROTO_TCP     6
   // 分别对应:
    icmp_input(p, inp);
    igmp_input(p, inp);
    udp_input(p, inp);
    tcp_input(p, inp);
           

ip6_input

ip6_input(struct pbuf *p, struct netif *inp)

首先拿缓冲区数据

ip6hdr = (struct ip6_hdr *)p->payload;
           

然后判断协议版本

if (IP6H_V(ip6hdr) != 6) {
      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n",
          IP6H_V(ip6hdr)));
      pbuf_free(p);
      IP6_STATS_INC(ip6.err);
      IP6_STATS_INC(ip6.drop);
      return ERR_OK;
    }
           

接下来判断长度:

如果ip头长度超过第一个pbuf长度或者ip长度超过总pbuf长度,释放pbuf,返回.

然后复制 ipv6地址到ip6_addr_t

ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest);
 ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src);
           

接下来判断ipv6地址:

不接受虚拟ipv4地址,不接受组播源地址

接下来检验数据包是否匹配,即这个包是不是给我的.

然后设置 netif = inp;

接下来处理扩展包头

最后根据端口号交付数据.

发送收据 流程 ip_route 和 ip_output_if

ip_route

#define ip_route(src, dest) \
         (IP_IS_V6(dest) ? \
        ip6_route(ip_2_ip6(src), ip_2_ip6(dest)) : \
        ip4_route_src(ip_2_ip4(dest), ip_2_ip4(src)))
           

ip4_route_src () 检测源地址src是否为NULL,如果为NULL ,调用 ip4_route()

获取 src 并返回 netif. 如果不为NULL,直接返回netif.

ip_output_if

#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
         (IP_IS_V6(dest) ? \
         ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \
         ip4_output_if(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif))
           

ip4_output_if() 调用 ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); 在调用 ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);

在调用 ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0)

生成ip头.

获取首部长度

u16_t ip_hlen = IP_HLEN;
           

设置选项 options,同时开始计算校验和 chk_sum

MEMCPY(p->payload, ip_options, optlen);
 if (optlen < optlen_aligned) {
          /* zero the remaining bytes */
         memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
       }
           

生成ip头

iphdr = (struct ip_hdr *)p->payload;
           

设置生存时间和协议类型

IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
           

设置首部长度和服务类型tos

IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
IPH_TOS_SET(iphdr, tos);
           

设置总长度

IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
           

设置偏移量和标识

IPH_OFFSET_SET(iphdr, 0);
IPH_ID_SET(iphdr, lwip_htons(ip_id));
           

设置源地址和目的地址

ip4_addr_copy(iphdr->dest, *dest);
ip4_addr_copy(iphdr->src, *src);
           

设置校验和

PH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
           

最后调用 netif->output(netif, p, dest); 把数据包交付给链路层

继续阅读