【lwip】09-IPv4协议&超全源码实现分析( 八 )

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

推荐阅读