- 首页 > 生活 > >
【lwip】09-IPv4协议&超全源码实现分析( 九 )
ip4_output_if_opt_src()
:这也是IP层组包、发送的实现函数,不会用选中的网卡IP地址覆盖传入的源IP地址 。支持IP首部的选项字段 。相关宏:
IP_OPTIONS_SEND
:
- IP首报文首部选项字段宏开关 。
- 如果开启了该宏,则会调用上述带
_opt
字样的函数,操作IP首部报文的选项字段 。
LWIP_IP_HDRINCL
:缺省为
NULL
- 如果把这个宏当目的IP地址传入IP层组包、发送的相关函数
ip4_output_if()
或其底层函数时,表示当前这个pbuf已经组好IP首部了 。 - 一般用于TCP重传 。
LWIP_CHECKSUM_CTRL_PER_NETIF
:
相关变量:
我们就分析
ip4_output_if_opt_src()
函数,比较全 。
ip4_output_if_opt_src()
:
- 先处理选项字段,在处理IP首部其它字段 。
struct pbuf *p
:传输层协议需要发送的数据包pbuf,payload指针已指向传输层协议首部 。const ip4_addr_t *src
:源IP地址 。const ip4_addr_t *dest
:目的IP地址 。u8_t ttl
:IP首部TTL字段 。u8_t tos
:IP首部TOS字段 。u8_t proto
:IP首部上层协议字段 。struct netif *netif
:发送IP数据报的网卡 。void *ip_options
:IP首部选项字段值 。u16_t optlen
:IP首部选项字段的长度 。
概要内容:
- 通过目的IP判断当前pbuf是否已经组好IP报文首部 。如果组好了,就不需要继续重组了 。如tcp重传 。
- 如果传入的pbuf报文还没组好IP报文首部,则根据传入的相关数据和IP报文内容进行组包 。
- 组好包后检查目的IP是否是环回IP(如环回IP、当前网卡的IP),如果是就调用
netif_loop_output()
进行环回处理 。
- 如果不是环回数据包,就需要发到数据链路 。
- IP分片:如果IP报文总长大于网卡MTU,则需要调用
ip4_frag()
进行IP分片 。 - 如果不需要IP分片,直接调用
netif->output()
将IP报文发出 。
err_tip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,u16_t optlen){#endif /* IP_OPTIONS_SEND */struct ip_hdr *iphdr;ip4_addr_t dest_addr;#if CHECKSUM_GEN_IP_INLINEu32_t chk_sum = 0;#endif /* CHECKSUM_GEN_IP_INLINE */LWIP_ASSERT_CORE_LOCKED(); /* 确保在tcpip内核锁内 */LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); /* pbuf没有被其它地方引用 */MIB2_STATS_INC(mib2.ipoutrequests); /* 流量统计 *//* IP首部是否已经组建好 */if (dest != LWIP_IP_HDRINCL) { /* IP首部未组建好 */u16_t ip_hlen = IP_HLEN; /* 默认IP首部长度 */#if IP_OPTIONS_SEND /* 开启了IP首部选项字段操作 */u16_t optlen_aligned = 0;if (optlen != 0) {#if CHECKSUM_GEN_IP_INLINEint i;#endif /* CHECKSUM_GEN_IP_INLINE */if (optlen > (IP_HLEN_MAX - IP_HLEN)) { /* 判断IP首部字段是否超限 *//* optlen 过长,导致IP首部超出限制 */LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n"));/* 状态信息统计 */IP_STATS_INC(ip.err);MIB2_STATS_INC(mib2.ipoutdiscards);return ERR_VAL;}/* 根据协议要求,IP首部选项字段长度要求是4字节的整数倍 */optlen_aligned = (u16_t)((optlen + 3) & ~3); /* 4字节往大对齐 */ip_hlen = (u16_t)(ip_hlen + optlen_aligned); /* 总长度字段 *//* 先处理选项字段 *//* pbuf payload偏移到IP首部选项字段位置 */if (pbuf_add_header(p, optlen_aligned)) {LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));/* payload偏移失败,统计信息返回错误 */IP_STATS_INC(ip.err);MIB2_STATS_INC(mib2.ipoutdiscards);return ERR_BUF;}/* 先处理选项字段,拷贝选项字段值到IP首部中 */MEMCPY(p->payload, ip_options, optlen);if (optlen < optlen_aligned) {/* 多余字节补0 */memset(((char *)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen));}#if CHECKSUM_GEN_IP_INLINE/* 先统计这部分的首部校验和 */for (i = 0; i < optlen_aligned / 2; i++) {chk_sum += ((u16_t *)p->payload)[i];}#endif /* CHECKSUM_GEN_IP_INLINE */}#endif /* IP_OPTIONS_SEND *//* 常见IP首部处理 *//* pbuf payload指针偏移 */if (pbuf_add_header(p, IP_HLEN)) {LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));/* payload偏移失败,统计信息返回错误 */IP_STATS_INC(ip.err);MIB2_STATS_INC(mib2.ipoutdiscards);return ERR_BUF;}iphdr = (struct ip_hdr *)p->payload;LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",(p->len >= sizeof(struct ip_hdr)));IPH_TTL_SET(iphdr, ttl); /* 填写TTL字段 */IPH_PROTO_SET(iphdr, proto); /* 填写上层协议字段 */#if CHECKSUM_GEN_IP_INLINEchk_sum += PP_NTOHS(proto | (ttl << 8)); /* 首部校验和统计 */#endif /* CHECKSUM_GEN_IP_INLINE */ip4_addr_copy(iphdr->dest, *dest); /* 填写目的IP字段 */#if CHECKSUM_GEN_IP_INLINE/* 首部校验和统计 */chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;#endif /* CHECKSUM_GEN_IP_INLINE */IPH_VHL_SET(iphdr, 4, ip_hlen / 4); /* 填写IP版本号+IP首部长度 */IPH_TOS_SET(iphdr, tos); /* 填写TOS服务字段 */#if CHECKSUM_GEN_IP_INLINE/* 首部校验和统计 */chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));#endif /* CHECKSUM_GEN_IP_INLINE */IPH_LEN_SET(iphdr, lwip_htons(p->tot_len)); /* 填写总长度字段 */#if CHECKSUM_GEN_IP_INLINE/* 首部校验和统计 */chk_sum += iphdr->_len;#endif /* CHECKSUM_GEN_IP_INLINE */IPH_OFFSET_SET(iphdr, 0); /* 填写标志字段+分片偏移量字段 */IPH_ID_SET(iphdr, lwip_htons(ip_id)); /* 填写标识字段 */#if CHECKSUM_GEN_IP_INLINE/* 首部校验和统计 */chk_sum += iphdr->_id;#endif /* CHECKSUM_GEN_IP_INLINE */++ip_id; /* ip首部标识,全局值 *//* 填写源IP地址字段 */if (src =https://www.huyubaike.com/biancheng/= NULL) {ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);} else {/* src cannot be NULL here */ip4_addr_copy(iphdr->src, *src);}#if CHECKSUM_GEN_IP_INLINE/* 首部校验和统计 */chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;/* 二次16bit折叠 。因为一次可能会溢出 。第二次是为了解决溢出 。*/chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);chk_sum = (chk_sum >> 16) + chk_sum;chk_sum = ~chk_sum; /* 反码 */IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { /* 当前网卡支持IP首部的checksum功能 */iphdr->_chksum = (u16_t)chk_sum; /* 填写首部校验和字段 。网络字节序,因为统计的时候就以网络字节序统计,所以这里不必转换 */}#if LWIP_CHECKSUM_CTRL_PER_NETIFelse {/* 如果不支持checksum功能 */IPH_CHKSUM_SET(iphdr, 0); /* 填写首部校验和字段为0 */}#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/#else /* CHECKSUM_GEN_IP_INLINE */IPH_CHKSUM_SET(iphdr, 0); /* 默认填写首部校验和字段为0 */#if CHECKSUM_GEN_IPIF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); /* IP首部校验和字段更新为实际值 */}#endif /* CHECKSUM_GEN_IP */#endif /* CHECKSUM_GEN_IP_INLINE */} else { /* IP头已经包含在p 。如TCP重传 */if (p->len
经验总结扩展阅读
-
|心动的信号丨“马卡巴卡”Be了?女博士为啥输给女大学生?
-
-
2022年2月25日出生男宝宝命好不好,八字五行起名最全款
-
-
-
2021牛年胡姓宝宝如何取名 2021年胡姓好听的名字案例
-
-
-
-
它是“天然花青素”隔几天喝一次,护肤养颜,女性最适合吃
-
-
-
无麸质饮食|为什么那么多人都向往“无麸质饮食”?然而这几类人并不适合实施
-
-
-
-
-
-
女神高口碑的面霜推荐!注重修复,滋润锁水,抗氧,尽显女神气质
-