/** receive callback function */ udp_recv_fn recv; /** user-supplied argument for the recv callback */ void *recv_arg; };
IP_PCB控制块结构体定义,准确来说,它是所有控制块的父类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/** This is the common part of all PCB types. It needs to be at the beginning of a PCB type definition. It is located here so that changes to this common part are made in one location instead of having to change all PCB structs. */ #define IP_PCB \ /* ip addresses in network byte order */ \ ip_addr_t local_ip; \ ip_addr_t remote_ip; \ /* Bound netif index */ \ u8_t netif_idx; \ /* Socket options */ \ u8_t so_options; \ /* Type Of Service */ \ u8_t tos; \ /* Time To Live */ \ u8_t ttl \ /* link layer address resolution hint */ \ IP_PCB_NETIFHINT
/** @ingroup udp_raw * Same as @ref udp_sendto_if, but with source address */ 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) { 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 the PCB is not yet bound to a port, bind it here */ if (pcb->local_port == 0) { err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); if (err != ERR_OK) { return err; } }
/* packet too large to add a UDP header without causing an overflow? */ if ((u16_t)(p->tot_len + UDP_HLEN) < p->tot_len) { return ERR_MEM; }
/* not enough space to add an UDP header to first pbuf in given p chain? */ if (pbuf_add_header(p, UDP_HLEN)) { /* allocate header in a separate new pbuf */ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); if (q == NULL) { return ERR_MEM; } if (p->tot_len != 0) { /* chain header q in front of given pbuf p */ pbuf_chain(q, p); } } else { q = p; } udphdr = (struct udp_hdr *)q->payload; udphdr->src = lwip_htons(pcb->local_port); udphdr->dest = lwip_htons(dst_port); udphdr->chksum = 0x0000;
接收函数有点长,所以笔者删除了一些条件编译和不重要的注释。 /** * If no pcb is found or the datagram is incorrect, the * pbuf is freed. * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) * @param inp network interface on which the datagram was received. */ 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;
/* is broadcast packet ? */ broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
/* convert src and dest ports to host byte order */ src = lwip_ntohs(udphdr->src); dest = lwip_ntohs(udphdr->dest);
pcb = NULL; prev = NULL; uncon_pcb = NULL;
for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { /* compare PCB local addr+port to UDP destination addr+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) { /* the first unconnected matching PCB */ uncon_pcb = pcb; #if LWIP_IPV4 } else if (broadcast && ip4_current_dest_addr()->addr == IPADDR_BROADCAST) { /* global broadcast address (only valid for IPv4; match was checked before) */ if (!IP_IS_V4_VAL(uncon_pcb->local_ip) || !ip4_addr_eq(ip_2_ip4(&uncon_pcb->local_ip), netif_ip4_addr(inp))) { /* uncon_pcb does not match the input netif, check this pcb */ if (IP_IS_V4_VAL(pcb->local_ip) && ip4_addr_eq(ip_2_ip4(&pcb->local_ip), netif_ip4_addr(inp))) { /* better match */ uncon_pcb = pcb; } } #endif /* LWIP_IPV4 */ } }
/* compare PCB remote addr+port to UDP source addr+port */ if ((pcb->remote_port == src) && (ip_addr_isany_val(pcb->remote_ip) || ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = udp_pcbs; udp_pcbs = pcb; } else { UDP_STATS_INC(udp.cachehit); } break; } }
prev = pcb; } /* no fully matching pcb found? then look for an unconnected pcb */ if (pcb == NULL) { pcb = uncon_pcb; }
/* Check checksum if this is a match or if it was directed at us. */ if (pcb != NULL) { for_us = 1; } else { #if LWIP_IPV4 if (!ip_current_is_v6()) { for_us = ip4_addr_eq(netif_ip4_addr(inp), ip4_current_dest_addr()); } #endif /* LWIP_IPV4 */ }
if (for_us) { #if LWIP_UDPLITE if (ip_current_header_proto() == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ u16_t chklen = lwip_ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { /* For UDP-Lite, checksum length of 0 means checksum over the complete packet (See RFC 3828 chap. 3.1) */ chklen = p->tot_len; } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ 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 /* LWIP_UDPLITE */ if (pbuf_remove_header(p, UDP_HLEN)) { pbuf_free(p); goto end; }
if (pcb != NULL) { /* callback */ if (pcb->recv != NULL) { /* now the recv function is responsible for freeing p */ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); goto end; } } else { #if LWIP_ICMP || LWIP_ICMP6 /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) { /* move payload pointer back to ip header */ pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN)); icmp_port_unreach(ip_current_is_v6(), p); } #endif /* LWIP_ICMP || LWIP_ICMP6 */ pbuf_free(p); } } else { pbuf_free(p); } end: return; }
BSD实现
在BSD中UDP头部数据结构如下:
1 2 3 4 5 6 7 8 9 10
/* * Udp protocol header. * Per RFC 768, September, 1981. */ struct udphdr { u_short uh_sport; /* source port */ u_short uh_dport; /* destination port */ short uh_ulen; /* udp length */ u_short uh_sum; /* udp checksum */ };
void udp_input(m, iphlen) register struct mbuf *m; int iphlen; { register struct ip *ip; register struct udphdr *uh; register struct inpcb *inp; struct mbuf *opts = 0; int len; struct ip save_ip;
udpstat.udps_ipackets++; //此时还没有实现备份IP选项,因此需要丢弃 if (iphlen > sizeof (struct ip)) { ip_stripoptions(m, (struct mbuf *)0); iphlen = sizeof(struct ip); } //如果IP/UDP长度不合理,那么重新安排mbuf链,使第一个mbuf至少有28个字节 ip = mtod(m, struct ip *); if (m->m_len < iphlen + sizeof(struct udphdr)) { if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { udpstat.udps_hdrops++;//状态标志位,这些代码不影响理解 return; } ip = mtod(m, struct ip *); } uh = (struct udphdr *)((caddr_t)ip + iphlen);
/* * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ len = ntohs((u_short)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len) { udpstat.udps_badlen++; goto bad; } m_adj(m, len - ip->ip_len); /* ip->ip_len = len; */ } /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ save_ip = *ip;
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) { struct socket *last; /* * Deliver a multicast or broadcast datagram to *all* sockets * for which the local and remote addresses and ports match * those of the incoming datagram. This allows more than * one process to receive multi/broadcasts on the same port. * (This really ought to be done for unicast datagrams as * well, but that would cause problems with existing * applications that open both address-specific sockets and * a wildcard socket listening to the same port -- they would * end up receiving duplicates of every unicast datagram. * Those applications open the multiple sockets to overcome an * inadequacy of the UDP socket interface, but for backwards * compatibility we avoid the problem here rather than * fixing the interface. Maybe 4.5BSD will remedy this?) */
/* * Construct sockaddr format source address. */ udp_in.sin_port = uh->uh_sport; udp_in.sin_addr = ip->ip_src; m->m_len -= sizeof (struct udpiphdr); m->m_data += sizeof (struct udpiphdr); /* * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ last = NULL; for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) { if (inp->inp_lport != uh->uh_dport) continue; if (inp->inp_laddr.s_addr != INADDR_ANY) { if (inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; } if (inp->inp_faddr.s_addr != INADDR_ANY) { if (inp->inp_faddr.s_addr != ip->ip_src.s_addr || inp->inp_fport != uh->uh_sport) continue; }
if (last != NULL) { struct mbuf *n;
if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in, n, (struct mbuf *)0) == 0) { m_freem(n); udpstat.udps_fullsock++; } else sorwakeup(last); } } last = inp->inp_socket; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids searching * through all pcbs in the common case of a non-shared * port. It * assumes that an application will never * clear these options after setting them. */ if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0)) break; }
if (last == NULL) { /* * No matching pcb found; discard datagram. * (No need to send an ICMP Port Unreachable * for a broadcast or multicast datgram.) */ udpstat.udps_noportbcast++; goto bad; } if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in, m, (struct mbuf *)0) == 0) { udpstat.udps_fullsock++; goto bad; } sorwakeup(last); return; }