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); 把数据包交付给链路层
上一篇: 消息队列-ActiveMQ P2P模式
下一篇: Lucene分词器