【lwip】11-UDP协议&源码分析( 七 )

  • 填充UDP报文首部的几个字段 。
  • 其中,UDP首部的长度字段和校验和需要按协议类型和相关宏处理 。
  • 最后把UDP报文转交给IP层:调用ip_output_if_src()转发出去 。
  • /** @ingroup udp_raw * Same as @ref udp_sendto_if, but with source address */err_tudp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip){#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP /* 需要计算校验和 */return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0, src_ip); /* 校验和字段先填入0 , 后面会计算 */}/** Same as udp_sendto_if_src(), but with checksum */err_tudp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,u16_t dst_port, struct netif *netif, u8_t have_chksum,u16_t chksum, const ip_addr_t *src_ip){#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */struct udp_hdr *udphdr; /* UDP首部 */err_t err;struct pbuf *q; /* 组装好的UDP报文,用于推到发送缓冲区 */u8_t ip_proto; /* IP协议 */u8_t ttl; /* TTL */LWIP_ASSERT_CORE_LOCKED(); /* 内核锁内 *//* 校验必要参数 */LWIP_ERROR("udp_sendto_if_src: invalid pcb", pcb != NULL, return ERR_ARG);LWIP_ERROR("udp_sendto_if_src: invalid pbuf", p != NULL, return ERR_ARG);LWIP_ERROR("udp_sendto_if_src: invalid dst_ip", dst_ip != NULL, return ERR_ARG);LWIP_ERROR("udp_sendto_if_src: invalid src_ip", src_ip != NULL, return ERR_ARG);LWIP_ERROR("udp_sendto_if_src: invalid netif", netif != NULL, return ERR_ARG);/* UDP控制块绑定的本地IP类型和需要组装的IP报文中的源IP地址和目的IP地址类型一致 */if (!IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {return ERR_VAL;}#if LWIP_IPV4 && IP_SOF_BROADCAST /* 支持发送广播数据 *//* broadcast filter? */if (!ip_get_option(pcb, SOF_BROADCAST) &&#if LWIP_IPV6IP_IS_V4(dst_ip) &&#endif /* LWIP_IPV6 */ip_addr_isbroadcast(dst_ip, netif)) {/* 如果本次UDP报文的目的IP是广播地址,但是用户又没有设置SOF_BROADCAST选项,则不能广播 */LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));return ERR_VAL;}#endif /* LWIP_IPV4 && IP_SOF_BROADCAST *//* 如果这个UDP控制块还没有绑定到本地端口 , 则将其绑定 */if (pcb->local_port == 0) {LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); /* 绑定本地IP(包括任意IP)和本地端口号 */if (err != ERR_OK) { /* 绑定失败就不能发送 */LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));return err;}}if ((u16_t)(p->tot_len + UDP_HLEN) < p->tot_len) {return ERR_MEM; /* UDP报文溢出 */}/* pbuf扩展UDP首部 */if (pbuf_add_header(p, UDP_HLEN)) {/* 原pbuf长度不足,需要申请相关首部空间:UDP首部+IP报文 */q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);if (q == NULL) {LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));return ERR_MEM; /* 申请UDP报文空间失败 */}if (p->tot_len != 0) {/* 把p拼接到q去 */pbuf_chain(q, p);}LWIP_DEBUGF(UDP_DEBUG,("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));} else { /* p这个pbuf可以扩展UDP首部空间 */q = p;LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));}LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",(q->len >= sizeof(struct udp_hdr)));/* 至此,q就是需要发送的UDP报文pbuf *//* 组装UDP报文 */udphdr = (struct udp_hdr *)q->payload;udphdr->src = https://www.huyubaike.com/biancheng/lwip_htons(pcb->local_port); /* UDP报文源端口 */udphdr->dest = lwip_htons(dst_port); /* UDP报文目的端口 *//* UDP报文校验和字段 , 先初始化为0,表示不计算校验和 */udphdr->chksum = 0x0000;#if LWIP_MULTICAST_TX_OPTIONS /* 组播TX */if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {/* 如果当前这路UDP支持组播环回,且目的IP是个组播IP,则标记当前pbuf为要环回的UDP组播 */q->flags |= PBUF_FLAG_MCASTLOOP;}#endif /* LWIP_MULTICAST_TX_OPTIONS */LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));#if LWIP_UDPLITE /* 支持UDP LITE协议 */if (pcb->flags & UDP_FLAGS_UDPLITE) { /* 当前这路UDP为UDP LITE协议 */u16_t chklen, chklen_hdr;LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));/* 设置UDP首部总长度字段和伪首部UDP总长度字段为需要进行校验和的长度 */chklen_hdr = chklen = pcb->chksum_len_tx;if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { /* 需要进行校验和的数据量不能超出现有数据量 */if (chklen != 0) {LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));}/* 对于UDP LITE协议 , 校验和长度字段为0时,表示对整个UDP报文进行校验和计算(校验和字段除外) 。(See RFC 3828 chap. 3.1) */chklen_hdr = 0; /* UDP LITE校验和长度字段 */chklen = q->tot_len; /* UDP LITE需要进行校验和的UDP报文数据长度 */}udphdr->len = lwip_htons(chklen_hdr); /* UDP报文校验和长度字段值 */#if CHECKSUM_GEN_UDP /* UDP支持校验和 */IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { /* 网卡需要检查UDP校验和 */#if LWIP_CHECKSUM_ON_COPY /* 拷贝时需要计算校验和 */if (have_chksum) {chklen = UDP_HLEN; /* UDP数据已经有校验和,则还需要再计算UDP首部的校验和 */}#endif /* LWIP_CHECKSUM_ON_COPY */udphdr->chksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDPLITE,q->tot_len, chklen, src_ip, dst_ip); /* 计算校验和(含伪首部) */#if LWIP_CHECKSUM_ON_COPYif (have_chksum) {/* 如果是已经有校验和 , 则把UDP首部(函伪首部)的校验和和已有的UDP数据校验和进行校验和即可 */u32_t acc;acc = udphdr->chksum + (u16_t)~(chksum);udphdr->chksum = FOLD_U32T(acc);}#endif /* LWIP_CHECKSUM_ON_COPY *//* 校验和刚好为0时必须变成0xffff,因为在UDP协议中 , 校验和字段为0表示“没有校验和” */if (udphdr->chksum == 0x0000) {udphdr->chksum = 0xffff;}}#endif /* CHECKSUM_GEN_UDP *//* IP协议字段标记为UDPLITE协议 */ip_proto = IP_PROTO_UDPLITE;} else#endif /* LWIP_UDPLITE */{/* UDP 协议 */LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));udphdr->len = lwip_htons(q->tot_len);#if CHECKSUM_GEN_UDP /* UDP支持校验和计算 */IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { /* 网卡需要检查UDP校验和 *//* 校验和在IPv6中是必须的 */if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {u16_t udpchksum;#if LWIP_CHECKSUM_ON_COPYif (have_chksum) {/* UDP数据已生成校验和,则只需要继续计算UDP首部的校验和即可 */u32_t acc;udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP,q->tot_len, UDP_HLEN, src_ip, dst_ip);acc = udpchksum + (u16_t)~(chksum);udpchksum = FOLD_U32T(acc);} else#endif /* LWIP_CHECKSUM_ON_COPY */{ /* 不使用UDP数据区计算好的校验和 , 咱们UDP协议自己独立生成 */udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len,src_ip, dst_ip); /* 计算UDP报文校验和 */}/* 校验和刚好为0时必须变成0xffff,因为在UDP协议中,校验和字段为0表示“没有校验和” */if (udpchksum == 0x0000) {udpchksum = 0xffff;}/* 设置UDP报文校验和 */udphdr->chksum = udpchksum;}}#endif /* CHECKSUM_GEN_UDP *//* IP协议字段标记为UDP协议 */ip_proto = IP_PROTO_UDP;}/* TTL相关 */#if LWIP_MULTICAST_TX_OPTIONS /* 多播TX的TTL */ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl);#else /* LWIP_MULTICAST_TX_OPTIONS */ttl = pcb->ttl; /* UDP控制块中的默认TTL */#endif /* LWIP_MULTICAST_TX_OPTIONS */LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto));/* 把UDP报文涉及网卡用户数据配置到这个网卡中 */NETIF_SET_HINTS(netif, &(pcb->netif_hints));/* UDP报文转交给IP层 */err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);NETIF_RESET_HINTS(netif);/* @todo: must this be increased even if error occurred? */MIB2_STATS_INC(mib2.udpoutdatagrams);if (q != p) {/* 释放在这里申请的pbuf */pbuf_free(q);q = NULL;/* p is still referenced by the caller, and will live on */}UDP_STATS_INC(udp.xmit);return err;}

    推荐阅读