其实发送一个包就这么多.
/* UDP 缓冲区申请 */ udpbuf = netbuf_new(); /* 申请内存 */ netbuf_alloc(udpbuf, strlen((char *)udpdemo_buf)); /* 把数据复制到payload里去. */ memcpy(udpbuf->p->payload, (void *)udpdemo_buf, strlen((const char *)udpdemo_buf)); /* payload 其实也可以直接修改. */ ((uint32_t *)udpbuf->p->payload)[0] = xTaskGetTickCount(); /* 这一步把数据发送出去. */ err = netconn_send(udpconn, udpbuf); /* UDP总会发成功的. */ netbuf_delete(udpbuf);
其中netbuf_new跟进去,发现其实就是一个calloc.
netbuf_alloc就是申请到内存,指针指向申请到的位置.
memcpy是标准函数,不用多说.
netbuf_delete就是删除calloc到的东西.
那么其实就剩下了netconn_send,这个函数看起来应该比较复杂,因为其他地方什么都没干.进去一看,还是apimsg的安全调用.不得不说安全性十足,而我们一开始就知道,直接看fn就行,其他都是安全需要.
1396行的错误判断,我们一开始就见过.就是如果这个conn有问题,就不要往下继续了.
接下来判断PCB是有效的,再跳转到NETCONN_UDP,明显这个函数只适合UDP.LWIP_CHECKSUM_ON_COPY又刚好是打开的,那么就是要校验.而后续一个是udp_send_chksum,一个是udp_sendto_chksum.
如果是本机发,就走到上面得判断,下面的判断就是有目标端口,目标地址什么的,其实最终是调用udp_sendto_chksum的.
进去就是ip_route,因为IP_IS_ANY_TYPE_VAL恒定为0,所以没被编译进去.
实际上,包就是从ip_route这一段,数据就出去了.这就是到达了IP层,记得七层模型里面,TCP还在比较靠上的一层.
可见,ip_route是查找到源网卡,以便后续构建.接着在sendto里面再获取了本地的src_ip(发送者IP).
到了udp_sendto_if_src_chksum,里面很多判断,这个代码尤其长.
/** Same as udp_sendto_if_src(), but with checksum */ err_t udp_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; err_t err; struct pbuf *q; /* q will be sent down the stack */ u8_t ip_proto; u8_t ttl; if ((pcb == NULL) || (dst_ip == NULL) || !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_IPV6 IP_IS_V4(dst_ip) && #endif /* LWIP_IPV6 */ ip_addr_isbroadcast(dst_ip, netif)) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p ", (void *)pcb)); return ERR_VAL; } #endif /* LWIP_IPV4 && IP_SOF_BROADCAST */ /* if the PCB is not yet bound to a port, bind it here */ if (pcb->local_port == 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now ")); err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); if (err != ERR_OK) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed ")); return err; } } /* not enough space to add an UDP header to first pbuf in given p chain? */ if (pbuf_header(p, UDP_HLEN)) { /* allocate header in a separate new pbuf */ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header ")); return ERR_MEM; } if (p->tot_len != 0) { /* chain header q in front of given pbuf p (only if p contains data) */ pbuf_chain(q, p); } /* first pbuf q points to header pbuf */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header pbuf %p before given pbuf %p ", (void *)q, (void *)p)); } else { /* adding space for header within p succeeded */ /* first pbuf q equals given pbuf */ q = p; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p ", (void *)p)); } LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", (q->len >= sizeof(struct udp_hdr))); /* q now represents the packet to be sent */ udphdr = (struct udp_hdr *)q->payload; udphdr->src = lwip_htons(pcb->local_port); udphdr->dest = lwip_htons(dst_port); /* in UDP, 0 checksum means 'no checksum' */ udphdr->chksum = 0x0000; /* Multicast Loop? */ #if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) { q->flags |= PBUF_FLAG_MCASTLOOP; } #endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F" ", q->tot_len)); #if LWIP_UDPLITE /* UDP Lite protocol? */ if (pcb->flags & UDP_FLAGS_UDPLITE) { u16_t chklen, chklen_hdr; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F" ", q->tot_len)); /* set UDP message length in UDP header */ 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" ", chklen)); } /* For UDP-Lite, checksum length of 0 means checksum over the complete packet. (See RFC 3828 chap. 3.1) At least the UDP-Lite header must be covered by the checksum, therefore, if chksum_len has an illegal value, we generate the checksum over the complete packet to be safe. */ chklen_hdr = 0; chklen = q->tot_len; } udphdr->len = lwip_htons(chklen_hdr); /* calculate checksum */ #if CHECKSUM_GEN_UDP IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { #if LWIP_CHECKSUM_ON_COPY if (have_chksum) { chklen = UDP_HLEN; } #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_COPY if (have_chksum) { u32_t acc; acc = udphdr->chksum + (u16_t)~(chksum); udphdr->chksum = FOLD_U32T(acc); } #endif /* LWIP_CHECKSUM_ON_COPY */ /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udphdr->chksum == 0x0000) { udphdr->chksum = 0xffff; } } #endif /* CHECKSUM_GEN_UDP */ ip_proto = IP_PROTO_UDPLITE; } else #endif /* LWIP_UDPLITE */ { /* UDP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F" ", q->tot_len)); udphdr->len = lwip_htons(q->tot_len); /* calculate checksum */ #if CHECKSUM_GEN_UDP IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { /* Checksum is mandatory over IPv6. */ if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { u16_t udpchksum; #if LWIP_CHECKSUM_ON_COPY if (have_chksum) { 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 */ { udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len, src_ip, dst_ip); } /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udpchksum == 0x0000) { udpchksum = 0xffff; } udphdr->chksum = udpchksum; } } #endif /* CHECKSUM_GEN_UDP */ ip_proto = IP_PROTO_UDP; } /* Determine TTL to use */ #if LWIP_MULTICAST_TX_OPTIONS ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl); #else /* LWIP_MULTICAST_TX_OPTIONS */ ttl = pcb->ttl; #endif /* LWIP_MULTICAST_TX_OPTIONS */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F" ", udphdr->chksum)); LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",) ", (u16_t)ip_proto)); /* output to IP */ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif); NETIF_SET_HWADDRHINT(netif, NULL); /* @todo: must this be increased even if error occurred? */ MIB2_STATS_INC(mib2.udpoutdatagrams); /* did we chain a separate header pbuf earlier? */ if (q != p) { /* free the header pbuf */ pbuf_free(q); q = NULL; /* p is still referenced by the caller, and will live on */ } UDP_STATS_INC(udp.xmit); return err; }
看起来是不是又长又头疼.稍微把一些安全性判断去掉.代码就很短了.
/** Same as udp_sendto_if_src(), but with checksum */ err_t udp_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) { struct udp_hdr *udphdr; err_t err; struct pbuf *q; /* q will be sent down the stack */ u8_t ip_proto; u8_t ttl; /* pbuf_header是给添加头信息的.给p增加UDP_HLEN长的头,成功返回0,非0都是失败. */ if (pbuf_header(p, UDP_HLEN)) { /* 申请新的pbuf */ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { return ERR_MEM; } if (p->tot_len != 0) { /* 把新的pbuf放到chain里,就是q的next是p.先发q,然后q是个头,发送完q就发p,数据. */ pbuf_chain(q, p); } } else { /* adding space for header within p succeeded */ /* 因为他是IP头,所以他在pbuf的最开始处. */ q = p; } /* q now represents the packet to be sent */ udphdr = (struct udp_hdr *)q->payload; udphdr->src = lwip_htons(pcb->local_port); udphdr->dest = lwip_htons(dst_port); /* in UDP, 0 checksum means 'no checksum' */ udphdr->chksum = 0x0000; /* Multicast Loop? */ if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) { q->flags |= PBUF_FLAG_MCASTLOOP; } udphdr->len = lwip_htons(q->tot_len); /* calculate checksum */ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { /* Checksum is mandatory over IPv6. */ if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { u16_t udpchksum; if (have_chksum) { 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 { udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len, src_ip, dst_ip); } /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udpchksum == 0x0000) { udpchksum = 0xffff; } udphdr->chksum = udpchksum; } } ip_proto = IP_PROTO_UDP; /* Determine TTL to use */ ttl = pcb->ttl; /* output to IP */ err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif); /* did we chain a separate header pbuf earlier? */ if (q != p) { /* free the header pbuf */ pbuf_free(q); q = NULL; /* p is still referenced by the caller, and will live on */ } return err; }
当我们断点在这个位置的时候,udphdr已经写入(udp头)
查看变量看到.
注意这里是0x781E其实是0x1E78,因为是我们的CPU是LE,但是以太网是BE.正是7800端口,目标端口.
进来后,需要判断have_chksum,这个参数又是超级早传递进来的.当NETBUF_FLAG_CHKSUM是有效时候,这个参数就有效.而默认这个是没的,所以就是全校验(否则,有效就是部分校验).
我们知道有校验就行,不关心怎么校验的.然后继续跑就到ip_output_if_src,这个总是是发包了吧.(确实这样,但是这里还有很多层.).
进入后立马进入ip4_output_if_opt_src函数,又继续用同样方法,添加一个IP头大小的内容.
IP头总算是一帧的最开始,然后到iphdr = (struct ip_hdr *)p->payload就是把iphdr定义为pbuf的开始数据.关于IP头,可以看看定义.(实际上还是不对,还有个叫以太网帧的玩意).
随后960行判断帧有没有大于MTU大小,大于的话还得拆分帧.
记得这个发送其实是到etharp_output,还不是终点,是不是有点恐怖,这深度.不,这还没到,还要封装以太网帧.可见LwIP给我们干了好多啊.