lwIp源码解析–udp
一、简介 ?? 1.1 lwip版本 ?? lwip 2.1.2 ?? 1.2 代码路径
lwip-2.1.2\src\core\udp.c
?? 代码行数1.3K,UDP是无连接、无状态的传输层协议,在实现上比TCP要简单很多。
二、源码解析 ?? 源码分为四部分,分别是UDP头部定义、UDP初始化函数、UDP发送函数、UDP接收函数。
?? 2.1 udp 头部
struct udp_hdr {
PACK_STRUCT_FIELD(u16_t src);
PACK_STRUCT_FIELD(u16_t dest);
PACK_STRUCT_FIELD(u16_t len);
PACK_STRUCT_FIELD(u16_t chksum);
} PACK_STRUCT_STRUCT;
?? udp的头部有八个字节,分别是源/目的端口号、UDP头部加上数据的总长度len(此时的总长度和IP头的负载长度的一致)、以及校验和 chksum(此处的校验和要加上12字节的伪头部,包括源/目的IP地址、协议号、UDP长度)
?? 2.2 初始化函数
struct udp_pcb * udp_new (void);
struct udp_pcb * udp_new_ip_type(u8_t type);
void udp_remove (struct udp_pcb *pcb);
err_t udp_bind (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
u16_t port);
void udp_bind_netif (struct udp_pcb *pcb, const struct netif* netif);
err_t udp_connect (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
u16_t port);
void udp_disconnect (struct udp_pcb *pcb);
void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv,
void *recv_arg);
?? 2.2.1 创建udp_pcb协议控制块
struct udp_pcb *
udp_new(void)
{
struct udp_pcb *pcb;
LWIP_ASSERT_CORE_LOCKED();
pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
if (pcb != NULL) {
memset(pcb, 0, sizeof(struct udp_pcb));
pcb->ttl = UDP_TTL;
#if LWIP_MULTICAST_TX_OPTIONS
udp_set_multicast_ttl(pcb, UDP_TTL);
#endif
}
return pcb;
}
struct udp_pcb *
udp_new_ip_type(u8_t type)
{
struct udp_pcb *pcb;
LWIP_ASSERT_CORE_LOCKED();
pcb = udp_new();
#if LWIP_IPV4 && LWIP_IPV6
if (pcb != NULL) {
IP_SET_TYPE_VAL(pcb->local_ip, type);
IP_SET_TYPE_VAL(pcb->remote_ip, type);
}
#else
LWIP_UNUSED_ARG(type);
#endif
return pcb;
}
?? 以上两个函数都是创建udp_pcb协议控制块,差别在于udp_new_ip_type() 可以传入type指定pcb对应的IP类型为IPv6、IPv4或IPv6/v4。而 udp_new() 默认创建的pcb对应IP类型为IPv4,因为IPv4的IP TYPE值是0,如下所示:
enum lwip_ip_addr_type {
IPADDR_TYPE_V4 = 0U,
IPADDR_TYPE_V6 = 6U,
IPADDR_TYPE_ANY = 46U
};
?? 2.2.2 绑定和连接 ?? 创建了udp_pcb之后,必须通过bind()或者connect()操作,将pcb加入到pcb链表struct udp_pcb *udp_pcbs中,才能由内核管理接收和发送数据。其中插入链表采用的是头插法。
err_t
udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
struct udp_pcb *ipcb;
u8_t rebind;
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
ip_addr_t zoned_ipaddr;
#endif
LWIP_ASSERT_CORE_LOCKED();
#if LWIP_IPV4
if (ipaddr == NULL) {
ipaddr = IP4_ADDR_ANY;
}
#else
LWIP_ERROR("udp_bind: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
#endif
LWIP_ERROR("udp_bind: invalid pcb", pcb != NULL, return ERR_ARG);
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr);
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
rebind = 0;
for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
if (pcb == ipcb) {
rebind = 1;
break;
}
}
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNKNOWN)) {
ip_addr_copy(zoned_ipaddr, *ipaddr);
ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
ipaddr = &zoned_ipaddr;
}
#endif
if (port == 0) {
port = udp_new_port();
if (port == 0) {
LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
return ERR_USE;
}
} else {
for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
if (pcb != ipcb) {
#if SO_REUSE
if (!ip_get_option(pcb, SOF_REUSEADDR) ||
!ip_get_option(ipcb, SOF_REUSEADDR))
#endif
{
if ((ipcb->local_port == port) &&
(ip_addr_cmp(&ipcb->local_ip, ipaddr) || ip_addr_isany(ipaddr) ||
ip_addr_isany(&ipcb->local_ip))) {
LWIP_DEBUGF(UDP_DEBUG,
("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
return ERR_USE;
}
}
}
}
}
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
pcb->local_port = port;
mib2_udp_bind(pcb);
if (rebind == 0) {
pcb->next = udp_pcbs;
udp_pcbs = pcb;
}
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
ip_addr_debug_print_val(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, pcb->local_ip);
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port));
return ERR_OK;
}
?? bind() 函数将传入的IP地址和端口号绑定到pcb本地IP(pcb->local_ip)和端口号(pcb->local_port)中,并且加入到udp_pcbs链表。同时也做了机制防止一个pcb重复绑定以及不同pcb绑定同一个IP和端口。
err_t
udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
struct udp_pcb *ipcb;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("udp_connect: invalid pcb", pcb != NULL, return ERR_ARG);
LWIP_ERROR("udp_connect: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
if (pcb->local_port == 0) {
err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
if (err != ERR_OK) {
return err;
}
}
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
if (IP_IS_V6(&pcb->remote_ip) &&
ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
}
#endif
pcb->remote_port = port;
pcb->flags |= UDP_FLAGS_CONNECTED;
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
ip_addr_debug_print_val(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
pcb->remote_ip);
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port));
for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
if (pcb == ipcb) {
return ERR_OK;
}
}
pcb->next = udp_pcbs;
udp_pcbs = pcb;
return ERR_OK;
}
?? 由client调用udp_connect() 函数,绑定remote ip和端口,置为UDP_FLAGS_CONNECTED,同时添加到udp_pcb中。
?? 2.2.3 绑定接受处理函数
void
udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("udp_recv: invalid pcb", pcb != NULL, return);
pcb->recv = recv;
pcb->recv_arg = recv_arg;
}
?? pcb->recv() 由udp_input() 函数调用,处理对端发来的udp数据。udp_recv_fn recv回调函数自行实现。无论是客户端还是服务端,都需要绑定此回调函数。
?? 2.2.4 绑定网卡
void
udp_bind_netif(struct udp_pcb *pcb, const struct netif *netif)
{
LWIP_ASSERT_CORE_LOCKED();
if (netif != NULL) {
pcb->netif_idx = netif_get_index(netif);
} else {
pcb->netif_idx = NETIF_NO_INDEX;
}
}
?? 将pcb->netif_idx = netif->num + 1; 从而与netif的编号绑定,绑定后此pcb的输入输出都指定一个网卡。
?? 2.3 发送函数
err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *dst_ip, u16_t dst_port,
struct netif *netif);
err_t udp_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);
err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *dst_ip, u16_t dst_port);
err_t udp_send (struct udp_pcb *pcb, struct pbuf *p);
#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
err_t udp_sendto_if_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);
err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *dst_ip, u16_t dst_port,
u8_t have_chksum, u16_t chksum);
err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
u8_t have_chksum, u16_t chksum);
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
?? 发送函数的调用顺序依次为: udp_send() -> udp_sendto() -> udp_sendto_chksum() -> udp_sendto_if() -> udp_sendto_if_chksum() -> udp_sendto_if_src() -> udp_sendto_if_src_chksum();
?? 函数的参数也是逐渐增多,udp_sendto() 比udp_send() 多了目的IP和端口,udp_sendto_if() 比udp_sendto() 多了选择的netif, udp_sendto_if_src() 比udp_sendto_if() 多了源IP。最后调用ip_output_if_src() 发送函数传到下一层进行封包。
?? udp_sendto_if_src_chksum() 时加入了伪头部计算,如果校验和为0, 设置为0xffff.
?? 2.4 接收处理函数 ??接收函数udp_input() 由ip_put() 函数调用, 目的就是经过IP选择之后将UDP数据报传递到udp_pcb的回调函数recv中处理。
void
udp_input(struct pbuf *p, struct netif *inp)
{
struct udp_hdr *udphdr;
struct udp_pcb *pcb, *prev;
struct udp_pcb *uncon_pcb;
u16_t src, dest;
u8_t broadcast;
u8_t for_us = 0;
LWIP_UNUSED_ARG(inp);
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("udp_input: invalid pbuf", p != NULL);
LWIP_ASSERT("udp_input: invalid netif", inp != NULL);
PERF_START;
UDP_STATS_INC(udp.recv);
if (p->len < UDP_HLEN) {
LWIP_DEBUGF(UDP_DEBUG,
("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
UDP_STATS_INC(udp.lenerr);
UDP_STATS_INC(udp.drop);
MIB2_STATS_INC(mib2.udpinerrors);
pbuf_free(p);
goto end;
}
udphdr = (struct udp_hdr *)p->payload;
broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
src = lwip_ntohs(udphdr->src);
dest = lwip_ntohs(udphdr->dest);
udp_debug_print(udphdr);
LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
ip_addr_debug_print_val(UDP_DEBUG, *ip_current_dest_addr());
LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
ip_addr_debug_print_val(UDP_DEBUG, *ip_current_src_addr());
LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src)));
pcb = NULL;
prev = NULL;
uncon_pcb = NULL;
for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
ip_addr_debug_print_val(UDP_DEBUG, pcb->local_ip);
LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
ip_addr_debug_print_val(UDP_DEBUG, pcb->remote_ip);
LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));
if ((pcb->local_port == dest) &&
(udp_input_local_match(pcb, inp, broadcast) != 0)) {
if ((pcb->flags & UDP_FLAGS_CONNECTED) == 0) {
if (uncon_pcb == NULL) {
uncon_pcb = pcb;
#if LWIP_IPV4
} else if (broadcast && ip4_current_dest_addr()->addr == IPADDR_BROADCAST) {
if (!IP_IS_V4_VAL(uncon_pcb->local_ip) || !ip4_addr_cmp(ip_2_ip4(&uncon_pcb->local_ip), netif_ip4_addr(inp))) {
if (IP_IS_V4_VAL(pcb->local_ip) && ip4_addr_cmp(ip_2_ip4(&pcb->local_ip), netif_ip4_addr(inp))) {
uncon_pcb = pcb;
}
}
#endif
}
#if SO_REUSE
else if (!ip_addr_isany(&pcb->local_ip)) {
uncon_pcb = pcb;
}
#endif
}
if ((pcb->remote_port == src) &&
(ip_addr_isany_val(pcb->remote_ip) ||
ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
if (prev != NULL) {
prev->next = pcb->next;
pcb->next = udp_pcbs;
udp_pcbs = pcb;
} else {
UDP_STATS_INC(udp.cachehit);
}
break;
}
}
prev = pcb;
}
if (pcb == NULL) {
pcb = uncon_pcb;
}
if (pcb != NULL) {
for_us = 1;
} else {
#if LWIP_IPV6
if (ip_current_is_v6()) {
for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
}
#endif
#if LWIP_IPV4
if (!ip_current_is_v6()) {
for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
}
#endif
}
if (for_us) {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
#if CHECKSUM_CHECK_UDP
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_UDP) {
#if LWIP_UDPLITE
if (ip_current_header_proto() == IP_PROTO_UDPLITE) {
u16_t chklen = lwip_ntohs(udphdr->len);
if (chklen < sizeof(struct udp_hdr)) {
if (chklen == 0) {
chklen = p->tot_len;
} else {
goto chkerr;
}
}
if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE,
p->tot_len, chklen,
ip_current_src_addr(), ip_current_dest_addr()) != 0) {
goto chkerr;
}
} else
#endif
{
if (udphdr->chksum != 0) {
if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
ip_current_src_addr(),
ip_current_dest_addr()) != 0) {
goto chkerr;
}
}
}
}
#endif
if (pbuf_remove_header(p, UDP_HLEN)) {
LWIP_ASSERT("pbuf_remove_header failed\n", 0);
UDP_STATS_INC(udp.drop);
MIB2_STATS_INC(mib2.udpinerrors);
pbuf_free(p);
goto end;
}
if (pcb != NULL) {
MIB2_STATS_INC(mib2.udpindatagrams);
#if SO_REUSE && SO_REUSE_RXTOALL
if (ip_get_option(pcb, SOF_REUSEADDR) &&
(broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) {
struct udp_pcb *mpcb;
for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
if (mpcb != pcb) {
if ((mpcb->local_port == dest) &&
(udp_input_local_match(mpcb, inp, broadcast) != 0)) {
if (mpcb->recv != NULL) {
struct pbuf *q;
q = pbuf_clone(PBUF_RAW, PBUF_POOL, p);
if (q != NULL) {
mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
}
}
}
}
}
}
#endif
if (pcb->recv != NULL) {
pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
} else {
pbuf_free(p);
goto end;
}
} else {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
#if LWIP_ICMP || LWIP_ICMP6
if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
icmp_port_unreach(ip_current_is_v6(), p);
}
#endif
UDP_STATS_INC(udp.proterr);
UDP_STATS_INC(udp.drop);
MIB2_STATS_INC(mib2.udpnoports);
pbuf_free(p);
}
} else {
pbuf_free(p);
}
end:
PERF_STOP("udp_input");
return;
#if CHECKSUM_CHECK_UDP
chkerr:
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n"));
UDP_STATS_INC(udp.chkerr);
UDP_STATS_INC(udp.drop);
MIB2_STATS_INC(mib2.udpinerrors);
pbuf_free(p);
PERF_STOP("udp_input");
#endif
}
?? 先进行校验和校验,然后从udp_pcbs链表中找对应的udp_pcb,主要是匹配本地IP和端口,同时考虑广播的情况。找到udp_pcb之后将负载数据传入接口中,回调用户的接收函数。
|