LWIP之UDP协议

IP协议提供了在各个主机之间传送数据报的功能,但是数据的最终目的地是主机上的特定应用程序。传输层协议就承担了这样的责任,典型的传输层协议有UDP和TCP两种。许多著名的上层应用都是基于UDP实现的,比如DNS、DHCP、IGMP、SNMP等。

UDP协议称为用户数据包协议,是一种无连接、不可靠的传输协议。UDP协议只是简单地完成应用程序到应用程序的交互,并不提供流量控制机制、复杂的差错控制方法、确认机制。虽然UDP可靠性非常差,但是UDP适用于那些轻微数据差错不敏感的应用,比如视频传输、网络电话等。

每台主机都包含称为协议端口的抽象目的点,端口号范围为0~65535,进程绑定到端口号上。这样数据包只要递交到相应的端口即可。

报文格式

源端口号和目的端口号:用于标识发送端和接收端应用进程

总长度:UDP首部长度+UDP数据区长度

校验和: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);
}
发布了208 篇原创文章 · 获赞 90 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/103505902
今日推荐