LWIP之UDP协议
程序员文章站
2024-02-22 18:27:40
...
IP协议提供了在各个主机之间传送数据报的功能,但是数据的最终目的地是主机上的特定应用程序。传输层协议就承担了这样的责任,典型的传输层协议有UDP和TCP两种。许多著名的上层应用都是基于UDP实现的,比如DNS、DHCP、IGMP、SNMP等。
UDP协议称为用户数据包协议,是一种无连接、不可靠的传输协议。UDP协议只是简单地完成应用程序到应用程序的交互,并不提供流量控制机制、复杂的差错控制方法、确认机制。虽然UDP可靠性非常差,但是UDP适用于那些轻微数据差错不敏感的应用,比如视频传输、网络电话等。
每台主机都包含称为协议端口的抽象目的点,端口号范围为0~65535,进程绑定到端口号上。这样数据包只要递交到相应的端口即可。
报文格式
源端口号和目的端口号不用多讲
总长度:首部长度+数据区长度
校验和:UDP校验和比较特殊,包含伪首部、首部和数据区。全0表示不使用校验和,校验和为0时用0xFFFF代替。
先看一下UDP控制块
#define IP_PCB \
ip_addr_t local_ip; \
ip_addr_t remote_ip; \
u8_t netif_idx; \
u8_t so_options; \
u8_t tos; \
u8_t ttl \
IP_PCB_NETIFHINT
/* UDP控制块 */
struct udp_pcb
{
IP_PCB; //IP_PCB相关字段
struct udp_pcb *next; //下一个UDP控制块指针
u8_t flags; //控制块状态
u16_t local_port, remote_port; //本地端口和远程端口
udp_recv_fn recv; //接收回调函数
void *recv_arg; //接收回调函数参数
};
UDP控制块被组织成一张表
下面分析UDP创建和删除的一些API
/* 创建UDP控制块 */
struct udp_pcb *udp_new(void)
{
struct udp_pcb *pcb;
/* 为UDP控制块申请内存 */
pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
if (pcb != NULL)
{
/* 清空UDP控制块 */
memset(pcb, 0, sizeof(struct udp_pcb));
/* TTL值 */
pcb->ttl = UDP_TTL;
}
return pcb;
}
/* 绑定端口号 */
err_t udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
struct udp_pcb *ipcb;
u8_t rebind;
/* 没指定IP */
if (ipaddr == NULL) {
ipaddr = IP4_ADDR_ANY;
}
/* 检查UDP控制块是否已经绑定 */
rebind = 0;
for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
if (pcb == ipcb) {
rebind = 1;
break;
}
}
/* 未指定端口 */
if (port == 0) {
/* 自动分配端口 */
port = udp_new_port();
if (port == 0) {
return ERR_USE;
}
}
/* 指定端口 */
else
{
/* 遍历所有UDP控制块,检查端口是否已经使用 */
for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next)
{
if (pcb != ipcb) {
if ((ipcb->local_port == port) && (ip_addr_cmp(&ipcb->local_ip, ipaddr) ||
ip_addr_isany(ipaddr) || ip_addr_isany(&ipcb->local_ip))) {
return ERR_USE;
}
}
}
}
/* 设置本地IP和端口号 */
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
pcb->local_port = port;
/* 将UDP控制块插入链表 */
if (rebind == 0) {
pcb->next = udp_pcbs;
udp_pcbs = pcb;
}
return ERR_OK;
}
/* 绑定远程端口 */
err_t udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
struct udp_pcb *ipcb;
/* 连接之前必须完成绑定 */
if (pcb->local_port == 0) {
err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
if (err != ERR_OK) {
return err;
}
}
/* 设置远程IP和端口号 */
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
pcb->remote_port = port;
pcb->flags |= UDP_FLAGS_CONNECTED;
/* UDP控制块必须插入链表 */
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;
}
/* 断开远程端口 */
void udp_disconnect(struct udp_pcb *pcb)
{
/* 清空远程IP和端口 */
ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
pcb->remote_port = 0;
pcb->netif_idx = NETIF_NO_INDEX;
udp_clear_flags(pcb, UDP_FLAGS_CONNECTED);
}
/* 注册接收回调函数 */
void udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
{
pcb->recv = recv;
pcb->recv_arg = recv_arg;
}
/* 删除UDP控制块 */
void udp_remove(struct udp_pcb *pcb)
{
struct udp_pcb *pcb2;
/* 从链表中移除 */
if (udp_pcbs == pcb) {
udp_pcbs = udp_pcbs->next;
}
else
{
for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next)
{
if (pcb2->next != NULL && pcb2->next == pcb)
{
pcb2->next = pcb->next;
break;
}
}
}
/* 释放内存 */
memp_free(MEMP_UDP_PCB, pcb);
}
接下来看一下UDP发送API
/* UDP发送 */
err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
{
if (IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
return ERR_VAL;
}
return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
}
err_t udp_sendto(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port)
{
struct netif *netif;
if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
}
/* 查找合适的网络接口 */
if (pcb->netif_idx != NETIF_NO_INDEX) {
netif = netif_get_by_index(pcb->netif_idx);
} else {
netif = ip_route(&pcb->local_ip, dst_ip);
}
if (netif == NULL) {
return ERR_RTE;
}
/* 发送数据包到指定网络接口 */
return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
}
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)
{
const ip_addr_t *src_ip;
if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
}
/* 取出源IP用于伪首部校验和 */
if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
ip4_addr_ismulticast(ip_2_ip4(&pcb->local_ip))) {
src_ip = netif_ip_addr4(netif);
} else {
if (!ip4_addr_cmp(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) {
return ERR_RTE;
}
src_ip = &pcb->local_ip;
}
/* 发送数据包 */
return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip);
}
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;
u8_t ip_proto;
u8_t ttl;
if (!IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||
!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
}
/* 发送数据包之前必须先绑定本地端口 */
if (pcb->local_port == 0) {
err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
if (err != ERR_OK) {
return err;
}
}
/* UDP数据大小 */
if ((u16_t)(p->tot_len + UDP_HLEN) < p->tot_len) {
return ERR_MEM;
}
/* 添加UDP头部 */
if (pbuf_add_header(p, UDP_HLEN)) {
q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
if (q == NULL) {
return ERR_MEM;
}
if (p->tot_len != 0) {
pbuf_chain(q, p);
}
} else {
q = p;
}
/* 填写UDP头部 */
udphdr = (struct udp_hdr *)q->payload;
udphdr->src = lwip_htons(pcb->local_port);
udphdr->dest = lwip_htons(dst_port);
udphdr->chksum = 0x0000;
udphdr->len = lwip_htons(q->tot_len);
/* 计算校验和 */
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
u16_t udpchksum;
{
udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len, src_ip, dst_ip);
}
if (udpchksum == 0x0000) {
udpchksum = 0xffff;
}
udphdr->chksum = udpchksum;
}
}
ip_proto = IP_PROTO_UDP;
ttl = pcb->ttl;
/* 发送数据包 */
err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
/* 释放pbuf */
if (q != p) {
pbuf_free(q);
q = NULL;
}
return err;
}
最后看一下接口函数
/* UDP输入 */
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;
/* 校验数据包长度 */
if (p->len < UDP_HLEN) {
pbuf_free(p);
goto end;
}
/* 取出UDP头部 */
udphdr = (struct udp_hdr *)p->payload;
/* 目的地址是否是广播地址 */
broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
/* 取出源端口和目的端口 */
src = lwip_ntohs(udphdr->src);
dest = lwip_ntohs(udphdr->dest);
pcb = NULL;
prev = NULL;
uncon_pcb = NULL;
/* 遍历所有UDP控制块 */
for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
/* 目的端口和地址都匹配 */
if ((pcb->local_port == dest) &&
(udp_input_local_match(pcb, inp, broadcast) != 0))
{
/* 记录第一个未连接UDP控制块 */
if ((pcb->flags & UDP_FLAGS_CONNECTED) == 0) {
if (uncon_pcb == NULL) {
uncon_pcb = pcb;
} 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;
}
}
}
}
/* 匹配源端口和IP地址 */
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 {
}
break;
}
}
prev = pcb;
}
/* 未完全匹配到目的端口地址、源端口地址、已连接的PCB */
if (pcb == NULL)
{
/* 将第一个目的地址端口都匹配的未连接PCB */
pcb = uncon_pcb;
}
/* 匹配到则认为该数据包是发给自己的 */
if (pcb != NULL) {
for_us = 1;
} else {
if (!ip_current_is_v6()) {
for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
}
}
/* 发给自己的数据包,则进行处理 */
if (for_us) {
/* 进行校验 */
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_UDP) {
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;
}
}
}
/* 移除头部 */
if (pbuf_remove_header(p, UDP_HLEN)) {
pbuf_free(p);
goto end;
}
/* 调用接收回调函数 */
if (pcb != NULL) {
if (pcb->recv != NULL) {
pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
} else {
pbuf_free(p);
goto end;
}
}
/* 目的地不可达ICMP报文 */
else
{
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);
}
pbuf_free(p);
}
}
/* 不是发给自己的直接删除 */
else {
pbuf_free(p);
}
end:
return;
chkerr:
pbuf_free(p);
}