【程序】在STM32单片机上实现基于LwIP 2.1.3协议栈raw API的DHCP服务器,为其他设备分配IPv4地址(20220122版)

本程序是参考了udhcp-0.9.8后编写的。
使用方法:

ip4_addr_t ip4addr, netmask, gw;
struct dhcpd_config dhcpd;
struct netif wifi_88w8801_uap;

IP4_ADDR(&ip4addr, 192, 168, 20, 1); // 板子IP地址
IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码
ip4_addr_set_zero(&gw); // 默认网关
netif_add(&wifi_88w8801_uap, &ip4addr, &netmask, &gw, NULL, ethernetif_init, netif_input);
netif_set_up(&wifi_88w8801_uap);

// 配置IP地址池和DNS服务器的地址
dhcpd_config_init(&dhcpd);
IP4_ADDR(&dhcpd.start, 192, 168, 20, 100);
IP4_ADDR(&dhcpd.end, 192, 168, 20, 200);
IP4_ADDR(&dhcpd.dns_servers[0], 8, 8, 8, 8);
IP4_ADDR(&dhcpd.dns_servers[1], 8, 8, 4, 4);
dhcpd_start(&wifi_88w8801_uap, &dhcpd);

此DHCP服务器支持静态MAC地址绑定,要添加或删除绑定请使用dhcpd_add_static_lease函数(参数ipaddr=NULL表示删除)。
使用dhcpd_print_leases函数可以显示当前DHCP客户端列表。

lwipopts.h中需要配置的选项:

#ifndef LWIP_LWIPOPTS_H
#define LWIP_LWIPOPTS_H

#define NO_SYS 1 // 无操作系统
#define SYS_LIGHTWEIGHT_PROT 0 // 不进行临界区保护

#define LWIP_NETCONN 0
#define LWIP_SOCKET 0

#define MEM_ALIGNMENT 4 // STM32单片机是32位的单片机, 因此是4字节对齐的
#define MEM_SIZE 10240 // lwip的mem_malloc函数使用的堆内存的大小

// 配置UDP
#define MEMP_NUM_UDP_PCB 6 // UDP PCB个数

// 配置DHCP
#define LWIP_DHCP 1
#define LWIP_NETIF_HOSTNAME 1

// 配置DNS
#define LWIP_DNS 1

// 配置DHCPD
#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + 1) // DHCPD需要用到1个timeout定时器
#if !LWIP_DHCP
// 在不启用DHCP的情况下也能使用DHCPD, 但是需要为IP地址0.0.0.0开放DHCP UDP端口号
#define LWIP_IP_ACCEPT_UDP_PORT(port) ((port) == LWIP_IANA_PORT_DHCP_CLIENT)
#endif

// 广播包过滤器
// 如果打开了这个过滤器, 那么就需要在套接字上设置SOF_BROADCAST选项才能收发广播数据包
//#define IP_SOF_BROADCAST 1
//#define IP_SOF_BROADCAST_RECV 1

// 开启调试信息输出
#define LWIP_DEBUG
#define DHCPD_DEBUG LWIP_DBG_ON

#endif

dhcpd.h:

#ifndef __DHCPD_H
#define __DHCPD_H

#if LWIP_IPV4 && LWIP_ARP

#ifndef DHCPD_DEBUG
#define DHCPD_DEBUG LWIP_DBG_OFF
#endif

#ifndef DHCPD_ALWAYS_INCLUDE_DNS
#define DHCPD_ALWAYS_INCLUDE_DNS 1 // 如果没有在config结构体中配置DNS地址, 则DNS地址为接口地址
#endif

#define DHCPD_BROADCAST_FLAG 0x8000
#define DHCPD_IPCHECK_TIMEOUT 100
#define DHCPD_OPTIONS_LEN 308

#ifdef LWIP_HDR_PROT_DHCP_H
#ifndef DHCP_HTYPE_ETH
#include <lwip/prot/iana.h>

#define DHCP_HTYPE_ETH LWIP_IANA_HWTYPE_ETHERNET
#define DHCP_CLIENT_PORT LWIP_IANA_PORT_DHCP_CLIENT
#define DHCP_SERVER_PORT LWIP_IANA_PORT_DHCP_SERVER
#endif
#else
struct dhcp_msg;
#endif

// IP地址搜索顺序
enum dhcpd_tlease_type
{
  DHCPD_TLEASE_TYPE_STATIC = 0, // 静态绑定的IP地址
  DHCPD_TLEASE_TYPE_REQUESTED = 1, // 客户端请求的IP地址
  DHCPD_TLEASE_TYPE_UNUSED = 2, // 未使用的IP地址
  DHCPD_TLEASE_TYPE_EXPIRED = 3 // 已使用但已过期的IP地址
};

// DHCP服务器配置信息
struct dhcpd_config
{
  ip4_addr_t start; // 地址池中的第一个地址
  ip4_addr_t end; // 地址池中的最后一个地址
  ip4_addr_t dns_servers[2]; // DNS服务器地址
  uint32_t lease; // 默认租期 (单位为秒)
  uint32_t decline_time; // 主机拒绝使用某IP地址 (DHCP DECLINE), 多长时间不重试这个地址 (单位为秒)
  uint32_t conflict_time; // 分配地址时检测到地址被其他主机占用, 多长时间不重试这个地址 (单位为秒)
  uint32_t offer_time; // 地址通过OFFER消息发给主机后, 主机通过REQUEST消息确认使用前, 地址保留多长时间 (单位为秒)
  uint32_t min_lease; // 客户端可请求的最短租期 (单位为秒)
};

// DHCP客户端主机名
struct dhcpd_hostname
{
  char *name; // 主机名
  int namelen; // 主机名的长度
  int capacity; // 分配的内存块的容量
};

// IP地址表
struct dhcpd_lease
{
  uint8_t chaddr[16]; // 客户端MAC地址
  ip4_addr_t yiaddr; // 分配到的IP地址
  uint32_t expiring_time; // 到期时间 (单位为毫秒)
  struct dhcpd_hostname hostname;
  struct dhcpd_lease *prev, *next;
};

// 静态IP地址绑定
#ifdef ETH_HWADDR_LEN
struct dhcpd_static_lease
{
  uint8_t macaddr[ETH_HWADDR_LEN];
  ip4_addr_t ipaddr;
  struct dhcpd_static_lease *prev, *next;
};
#else
struct dhcpd_static_lease;
#endif

// 正在检验的IP地址
struct dhcpd_tentative_lease
{
  enum dhcpd_tlease_type type; // IP地址类型
  uint8_t chaddr[16]; // 客户端MAC地址
  ip4_addr_t yiaddr; // 分配的IP地址
  struct pbuf *resp; // 待发送的DHCP回应
  struct dhcpd_hostname hostname; // 客户端主机名
  struct dhcpd_tentative_lease *prev, *next;
};

// DHCP服务器状态
struct dhcpd_state
{
  struct dhcpd_config config; // DHCP服务器配置
  struct netif *netif; // 网络接口
  struct dhcpd_lease *leases; // 分配的IP地址
  struct dhcpd_tentative_lease *tentative_leases; // 检验中的IP地址
  struct dhcpd_static_lease *static_leases; // 静态IP地址绑定
  struct dhcpd_state *next;
};

// MAC地址查询完毕后调用的回调函数
typedef void (*dhcpd_ip_callback)(void *arg, struct netif *netif, const ip4_addr_t *ipaddr, int exists, const struct eth_addr *ethaddr);

// MAC地址查询的回调函数列表
struct dhcpd_ip_callback_entry
{
  dhcpd_ip_callback callback;
  void *arg;
  struct dhcpd_ip_callback_entry *next;
};

// MAC地址查询
struct dhcpd_ip_status
{
  struct netif *netif; // 局域网所在的网络接口
  ip4_addr_t ipaddr; // 查询的IP地址
  uint32_t time; // 查询开始时间
  int retry; // 剩余重试次数
  
  struct dhcpd_ip_callback_entry *callbacks; // 回调函数列表
  struct dhcpd_ip_status *prev, *next; // 上一条和下一条查询
};

struct dhcpd_lease *dhcpd_add_lease(struct dhcpd_state *state, const uint8_t *chaddr, const ip4_addr_t *yiaddr, uint32_t lease_time);
void dhcpd_add_common_options(struct dhcpd_state *state, struct dhcp_msg *packet);
int dhcpd_add_option(struct dhcp_msg *packet, uint8_t code, uint8_t len, const void *data);
err_t dhcpd_add_static_lease(struct netif *netif, const uint8_t *macaddr, const ip4_addr_t *ipaddr);
err_t dhcpd_check_ip(struct netif *netif, const ip4_addr_t *ipaddr, dhcpd_ip_callback callback, void *arg);
void dhcpd_clear_lease(struct dhcpd_state *state, const uint8_t *chaddr, const ip4_addr_t *yiaddr);
int dhcpd_compare_chaddr(const uint8_t *chaddr, const uint8_t *mac);
void dhcpd_config_init(struct dhcpd_config *config);
struct pbuf *dhcpd_create_msg(u8_t type, struct dhcp_msg *client_packet, const ip4_addr_t *server_ip);
struct dhcpd_lease *dhcpd_find_lease_by_chaddr(struct dhcpd_state *state, const uint8_t *chaddr);
struct dhcpd_lease *dhcpd_find_lease_by_yiaddr(struct dhcpd_state *state, const ip4_addr_t *yiaddr);
struct dhcpd_lease *dhcpd_find_oldest_expired_lease(struct dhcpd_state *state);
struct dhcpd_tentative_lease *dhcpd_find_tentative_lease_by_chaddr(struct dhcpd_state *state, const uint8_t *chaddr);
struct dhcpd_tentative_lease *dhcpd_find_tentative_lease_by_yiaddr(struct dhcpd_state *state, const ip4_addr_t *yiaddr, const struct dhcpd_tentative_lease *except);
int dhcpd_get_option(struct dhcp_msg *packet, uint8_t code, uint8_t len, void *data);
uint8_t *dhcpd_get_option_string(struct dhcp_msg *packet, uint8_t code);
struct dhcpd_static_lease *dhcpd_get_static_lease(struct netif *netif, const uint8_t *macaddr);
struct dhcpd_static_lease *dhcpd_get_static_lease_by_chaddr(struct dhcpd_state *state, const uint8_t *chaddr);
struct dhcpd_static_lease *dhcpd_get_static_lease_by_yiaddr(struct dhcpd_state *state, const ip4_addr_t *yiaddr);
struct dhcpd_static_lease *dhcpd_get_static_lease_by_yiaddr_packed(struct dhcpd_state *state, const ip4_addr_p_t *yiaddr);
err_t dhcpd_init(void);
void dhcpd_init_msg(struct pbuf *p, u8_t type, struct dhcp_msg *client_packet, const ip4_addr_t *server_ip);
int dhcpd_is_expired_lease(const struct dhcpd_lease *lease);
int dhcpd_is_pool_address(struct dhcpd_state *state, const ip4_addr_t *addr);
int dhcpd_is_static_lease(struct dhcpd_state *state, const struct dhcpd_lease *lease);
int dhcpd_is_valid_tlease(struct dhcpd_state *state, struct dhcpd_tentative_lease *tlease);
void dhcpd_print_leases(struct netif *netif);
err_t dhcpd_send_packet(struct netif *netif, struct pbuf *p);
err_t dhcpd_start(struct netif *netif, const struct dhcpd_config *config);
int dhcpd_update_hostname(struct dhcpd_hostname *hostname, const uint8_t *opt);
int dhcpd_update_hostname_from_packet(struct dhcpd_hostname *hostname, struct dhcp_msg *client_packet);
struct dhcpd_state *netif_dhcpd_data(struct netif *netif);

#endif
#endif

dhcpd.c:

/* Dynamic Host Configuration Protocol: https://tools.ietf.org/html/rfc2131 */
/* DHCP Options and BOOTP Vendor Extensions: https://tools.ietf.org/html/rfc2132 */
/* udhcp-0.9.8.tar.gz: https://udhcp.busybox.net/ */
// 注意: 使用前必须确认lwip的sys_timeout函数能正常工作, 否则服务器分配不了IP地址
//       若sys_timeout函数不能正常工作, 请在lwipopts.h中扩大MEMP_NUM_SYS_TIMEOUT的值
#include <lwip/etharp.h>
#include <lwip/prot/dhcp.h>
#include <lwip/sys.h>
#include <lwip/timeouts.h>
#include <lwip/udp.h>
#include <string.h>
#include "dhcpd.h"

#if LWIP_IPV4 && LWIP_ARP

static void dhcpd_check_ip_status(void *arg);
static void dhcpd_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
static err_t dhcpd_send_ack(struct dhcpd_state *state, struct dhcp_msg *client_packet, struct dhcpd_lease *lease);
static err_t dhcpd_send_inform(struct dhcpd_state *state, struct dhcp_msg *client_packet);
static err_t dhcpd_send_nak(struct dhcpd_state *state, struct dhcp_msg *client_packet);
static err_t dhcpd_send_offer(struct dhcpd_state *state, struct dhcp_msg *client_packet);
static void dhcpd_send_offer_callback_1(struct dhcpd_state *state, struct dhcpd_tentative_lease *tlease);
static void dhcpd_send_offer_callback_2(void *arg, struct netif *netif, const ip4_addr_t *ipaddr, int exists, const struct eth_addr *ethaddr);
static err_t dhcpd_send_offer_callback_3(struct dhcpd_state *state, struct dhcpd_tentative_lease *tlease, int succeeded);
static err_t dhcpd_send_offer_callback_4(struct dhcpd_state *state, struct pbuf *p, const ip4_addr_t *yiaddr);

static struct dhcpd_ip_status *dhcpd_ip_status; // ARP请求队列
static struct dhcpd_state *dhcpd_list;
static struct udp_pcb *dhcpd_upcb;
static uint8_t dhcpd_ip_checking;

/* 记录为客户端分配好的IP地址 */
struct dhcpd_lease *dhcpd_add_lease(struct dhcpd_state *state, const uint8_t *chaddr, const ip4_addr_t *yiaddr, uint32_t lease_time)
{
  struct dhcpd_lease *lease;
  
  // 删除以前分配给客户端的旧地址, 然后新建一个条目存储新地址
  dhcpd_clear_lease(state, chaddr, yiaddr);
  lease = dhcpd_find_oldest_expired_lease(state); // 优先利用已过期的条目
  if (lease == NULL)
  {
    lease = mem_malloc(sizeof(struct dhcpd_lease)); // 没有过期条目可用, 则新建一个
    if (lease != NULL)
    {
      // 将新建的条目添加到链表中
      memset(lease, 0, sizeof(struct dhcpd_lease));
      lease->next = state->leases;
      if (state->leases != NULL)
        state->leases->prev = lease;
      state->leases = lease;
    }
  }
  
  // 将新地址存储到条目中
  if (lease != NULL)
  {
    if (chaddr != NULL)
      memcpy(lease->chaddr, chaddr, DHCP_CHADDR_LEN);
    else
      memset(lease->chaddr, 0, DHCP_CHADDR_LEN);
    lease->yiaddr = *yiaddr;
    lease->expiring_time = sys_now() + 1000 * lease_time;
  }
  return lease;
}

/* 在DHCP消息中添加常用选项 */
void dhcpd_add_common_options(struct dhcpd_state *state, struct dhcp_msg *packet)
{
  ip4_addr_t dns[2];
  uint8_t dns_cnt = 0;
  
  ip4_addr_set(&packet->siaddr, netif_ip4_addr(state->netif)); // DHCP服务器IP地址
  dhcpd_add_option(packet, DHCP_OPTION_SUBNET_MASK, 4, netif_ip4_netmask(state->netif)); // 子网掩码
  dhcpd_add_option(packet, DHCP_OPTION_ROUTER, 4, netif_ip4_addr(state->netif)); // 默认网关
  
  // DNS服务器IP地址
  if (ip4_addr_isany_val(state->config.dns_servers[0]))
  {
#if DHCPD_ALWAYS_INCLUDE_DNS
    ip4_addr_set(&dns[0], &packet->siaddr);
    dns_cnt++;
#endif
  }
  else
  {
    dns[0] = state->config.dns_servers[0];
    dns_cnt++;
  }
  if (!ip4_addr_isany_val(state->config.dns_servers[1]))
  {
    dns[dns_cnt] = state->config.dns_servers[1];
    dns_cnt++;
  }
  if (dns_cnt != 0)
    dhcpd_add_option(packet, DHCP_OPTION_DNS_SERVER, dns_cnt * sizeof(ip4_addr_t), dns);
}

/* 在DHCP消息中添加DHCP选项 */
int dhcpd_add_option(struct dhcp_msg *packet, uint8_t code, uint8_t len, const void *data)
{
  int i = 0;
  
  // 找到结束标记的位置
  while (packet->options[i] != DHCP_OPTION_END)
  {
    if (packet->options[i] == DHCP_OPTION_PAD)
      i++;
    else
      i += packet->options[i + 1] + 2;
  }
  
  // 检查剩余空间是否能容纳一个DHCP选项和一个结束标记
  if (i + len + 3 > DHCPD_OPTIONS_LEN)
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: Option 0x%02x did not fit into the packet!\n", __FUNCTION__, code));
    return -1;
  }
  
  // 添加选项
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: adding option 0x%02x\n", __FUNCTION__, code));
  packet->options[i] = code;
  packet->options[i + 1] = len;
  memcpy(packet->options + i + 2, data, len);
  packet->options[i + len + 2] = DHCP_OPTION_END;
  return i; // 返回添加的选项的位置
}

/* 添加或删除静态IP地址绑定 */
err_t dhcpd_add_static_lease(struct netif *netif, const uint8_t *macaddr, const ip4_addr_t *ipaddr)
{
  struct dhcpd_state *state;
  struct dhcpd_static_lease *p;
  
  state = netif_dhcpd_data(netif);
  if (state == NULL)
    return ERR_ARG;
  if (!ip4_addr_isany(ipaddr))
  {
    // 判断IP地址是否位于同一网段中 (不一定非要位于地址池中)
    if (!ip4_addr_netcmp(netif_ip4_addr(netif), ipaddr, netif_ip4_netmask(netif)))
      return ERR_ARG; // 不在同一网段
    if (ip4_addr_cmp(netif_ip4_addr(netif), ipaddr))
      return ERR_ARG; // 与网络接口地址相同
    if (ip4_addr_isbroadcast(ipaddr, netif))
      return ERR_ARG; // 是广播地址
    
    // 判断IP地址是否已被其他MAC地址静态绑定
    for (p = state->static_leases; p != NULL; p = p->next)
    {
      if (ip4_addr_cmp(&p->ipaddr, ipaddr) && memcmp(p->macaddr, macaddr, ETH_HWADDR_LEN) != 0)
        return ERR_ARG;
    }
  }
  
  p = dhcpd_get_static_lease(netif, macaddr);
  if (p != NULL)
  {
    if (!ip4_addr_isany(ipaddr))
    {
      // 修改绑定
      ip4_addr_set(&p->ipaddr, ipaddr);
    }
    else
    {
      // 删除绑定
      if (p->prev == NULL)
      {
        LWIP_ASSERT("state->static_leases == p", state->static_leases == p);
        state->static_leases = p->next;
      }
      else
        p->prev->next = p->next;
      if (p->next != NULL)
        p->next->prev = p->prev;
      mem_free(p);
    }
  }
  else
  {
    if (!ip4_addr_isany(ipaddr))
    {
      // 添加绑定
      p = mem_malloc(sizeof(struct dhcpd_static_lease));
      if (p == NULL)
        return ERR_MEM;
      ip4_addr_set(&p->ipaddr, ipaddr);
      memcpy(p->macaddr, macaddr, ETH_HWADDR_LEN);
      
      p->prev = NULL;
      p->next = state->static_leases;
      if (state->static_leases != NULL)
        state->static_leases->prev = p;
      state->static_leases = p;
    }
  }
  return ERR_OK;
}

/* 检查网络中是否存在指定IP地址的主机 */
// 回调函数始终会被调用, 其中的参数exists: -1表示查询失败, 0表示IP未使用, 1表示IP已有主机使用
err_t dhcpd_check_ip(struct netif *netif, const ip4_addr_t *ipaddr, dhcpd_ip_callback callback, void *arg)
{
  const ip4_addr_t *ipret;
  int ret;
  struct dhcpd_ip_callback_entry *m;
  struct dhcpd_ip_status *p;
  struct eth_addr ethaddr, *ethret;
  
  LWIP_ASSERT("callback != NULL", callback != NULL);
  
  if (!netif_is_link_up(netif))
  {
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: link is down\n", __FUNCTION__));
    callback(arg, netif, ipaddr, -1, NULL);
    return ERR_ARG;
  }
  
  if (ip4_addr_cmp(ipaddr, netif_ip4_addr(netif)))
  {
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP %s is for netif\n", __FUNCTION__, ip4addr_ntoa(ipaddr)));
    memcpy(ethaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
    callback(arg, netif, ipaddr, 1, &ethaddr);
    return ERR_OK;
  }
  
  ret = etharp_find_addr(netif, ipaddr, &ethret, &ipret);
  if (ret != -1)
  {
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP %s is in ARP cache\n", __FUNCTION__, ip4addr_ntoa(ipaddr)));
    ethaddr = *ethret;
    callback(arg, netif, ipaddr, 1, &ethaddr);
    return ERR_OK;
  }
  
  m = mem_malloc(sizeof(struct dhcpd_ip_callback_entry));
  if (m == NULL)
  {
    callback(arg, netif, ipaddr, -1, NULL);
    return ERR_MEM;
  }
  m->callback = callback;
  m->arg = arg;
  m->next = NULL;
  
  for (p = dhcpd_ip_status; p != NULL; p = p->next)
  {
    if (p->netif == netif && ip4_addr_cmp(ipaddr, &p->ipaddr))
      break;
  }
  
  if (p == NULL)
  {
    p = mem_malloc(sizeof(struct dhcpd_ip_status));
    if (p == NULL)
    {
      mem_free(m);
      callback(arg, netif, ipaddr, -1, NULL);
      return ERR_MEM;
    }
    
    p->netif = netif;
    p->ipaddr = *ipaddr;
    p->time = sys_now();
    p->retry = 3;
    
    p->callbacks = m;
    p->prev = NULL;
    p->next = dhcpd_ip_status;
    
    if (dhcpd_ip_status != NULL)
      dhcpd_ip_status->prev = p;
    dhcpd_ip_status = p;
  }
  else
  {
    m->next = p->callbacks;
    p->callbacks = m;
  }
  
  if (!dhcpd_ip_checking)
  {
    dhcpd_ip_checking = 1;
    sys_timeout(DHCPD_IPCHECK_TIMEOUT, dhcpd_check_ip_status, NULL);
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: DHCPD timer is started\n", __FUNCTION__));
  }
  
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP %s is not in ARP cache, sending ARP request\n", __FUNCTION__, ip4addr_ntoa(ipaddr)));
  etharp_request(netif, ipaddr);
  return ERR_INPROGRESS;
}

/* 定时检查是否收到ARP回应 */
static void dhcpd_check_ip_status(void *arg)
{
  const ip4_addr_t *ipret;
  int ret;
  struct dhcpd_ip_callback_entry *m, *n;
  struct dhcpd_ip_status *p, *q;
  struct eth_addr ethaddr, *ethret;
  
  // 遍历所有要查的IP地址
  p = dhcpd_ip_status;
  while (p != NULL)
  {
    q = p->next;
    
    // 检查当前IP地址是否有ARP表项
    ret = etharp_find_addr(p->netif, &p->ipaddr, &ethret, &ipret);
    if (ret == -1)
    {
      // 没有表项
      ethret = NULL;
      p->retry--;
      if (p->retry != 0)
      {
        // 再查一次
        LWIP_DEBUGF(DHCPD_DEBUG, ("%s: resend ARP request for IP %s. retry=%d\n", __FUNCTION__, ip4addr_ntoa(&p->ipaddr), p->retry));
        etharp_request(p->netif, &p->ipaddr);
      }
      else
      {
        // 不查了, 网络中不存在当前IP地址
        LWIP_DEBUGF(DHCPD_DEBUG, ("%s: no ARP response for IP %s\n", __FUNCTION__, ip4addr_ntoa(&p->ipaddr)));
      }
    }
    else
    {
      // 查到了ARP表项, 说明网络中存在当前IP地址
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: received ARP response for IP %s\n", __FUNCTION__, ip4addr_ntoa(&p->ipaddr)));
      ethaddr = *ethret;
      ethret = &ethaddr;
      p->retry = 0; // 不查了
    }
    
    if (p->retry == 0)
    {
      // 调用当前IP地址绑定的所有回调函数
      m = p->callbacks;
      p->callbacks = NULL;
      while (m != NULL)
      {
        m->callback(m->arg, p->netif, &p->ipaddr, ethret != NULL, ethret);
        
        n = m->next;
        mem_free(m);
        m = n;
      }
      
      // 将当前IP地址从ARP请求队列移除
      if (p->prev == NULL)
      {
        LWIP_ASSERT("dhcpd_ip_status == p", dhcpd_ip_status == p);
        dhcpd_ip_status = p->next;
      }
      else
        p->prev->next = p->next;
      if (p->next != NULL)
        p->next->prev = p->prev;
      mem_free(p);
    }
    
    p = q;
  }
  
  if (dhcpd_ip_status == NULL)
  {
    dhcpd_ip_checking = 0;
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: DHCPD timer is stopped\n", __FUNCTION__));
  }
  else
    sys_timeout(DHCPD_IPCHECK_TIMEOUT, dhcpd_check_ip_status, NULL);
}

/* 清空所有跟chaddr和yiaddr有关的记录 */
void dhcpd_clear_lease(struct dhcpd_state *state, const uint8_t *chaddr, const ip4_addr_t *yiaddr)
{
  int i;
  struct dhcpd_lease *p, *q;
  
  // 如果chaddr为全0, 则认为没有提供chaddr
  if (chaddr != NULL)
  {
    for (i = 0; i < DHCP_CHADDR_LEN; i++)
    {
      if (chaddr[i] != 0)
        break;
    }
    if (i == DHCP_CHADDR_LEN)
      chaddr = NULL;
  }
  
  p = state->leases;
  while (p != NULL)
  {
    if ((chaddr != NULL && memcmp(p->chaddr, chaddr, DHCP_CHADDR_LEN) == 0) || (!ip4_addr_isany(yiaddr) && ip4_addr_cmp(&p->yiaddr, yiaddr)))
    {
      // 删除p
      q = p->next;
      dhcpd_update_hostname(&p->hostname, NULL);
      
      if (p->prev == NULL)
      {
        LWIP_ASSERT("state->leases == p", state->leases == p);
        state->leases = p->next;
      }
      else
        p->prev->next = p->next;
      if (p->next != NULL)
        p->next->prev = p->prev;
      mem_free(p);
      
      p = q;
    }
    else
      p = p->next;
  }
}

/* 检查chaddr是否和MAC地址匹配 */
int dhcpd_compare_chaddr(const uint8_t *chaddr, const uint8_t *mac)
{
  int i;
  
  if (chaddr == NULL || mac == NULL)
    return 0;
  
  for (i = 0; i < DHCP_CHADDR_LEN; i++)
  {
    if (i < ETH_HWADDR_LEN)
    {
      if (chaddr[i] != mac[i])
        return 0;
    }
    else
    {
      if (chaddr[i] != 0)
        return 0;
    }
  }
  return 1;
}

/* 设置默认配置 */
void dhcpd_config_init(struct dhcpd_config *config)
{
  memset(config, 0, sizeof(struct dhcpd_config));
  config->lease = 86400;
  config->decline_time = 3600;
  config->conflict_time = 3600;
  config->offer_time = 60;
  config->min_lease = 60;
}

/* 创建空白DHCPD消息 */
struct pbuf *dhcpd_create_msg(u8_t type, struct dhcp_msg *client_packet, const ip4_addr_t *server_ip)
{
  struct pbuf *p;
  
  // dhcp_msg结构体很大, 如果直接在函数中声明局部变量, 则很容易导致栈溢出 (而且不容易发现)
  // 所以改用动态分配比较合适
  p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + DHCPD_OPTIONS_LEN, PBUF_RAM);
  if (p != NULL)
  {
    LWIP_ASSERT("dhcpd msg is not chained", p->next == NULL);
    dhcpd_init_msg(p, type, client_packet, server_ip);
  }
  else
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: pbuf_alloc() failed\n", __FUNCTION__));
  
  return p;
}

/* 根据MAC地址查找分配记录 */
struct dhcpd_lease *dhcpd_find_lease_by_chaddr(struct dhcpd_state *state, const uint8_t *chaddr)
{
  struct dhcpd_lease *p;
  
  for (p = state->leases; p != NULL; p = p->next)
  {
    if (memcmp(p->chaddr, chaddr, DHCP_CHADDR_LEN) == 0)
      break;
  }
  return p;
}

/* 根据IP地址查找分配记录 */
struct dhcpd_lease *dhcpd_find_lease_by_yiaddr(struct dhcpd_state *state, const ip4_addr_t *yiaddr)
{
  struct dhcpd_lease *p;
  
  for (p = state->leases; p != NULL; p = p->next)
  {
    if (ip4_addr_cmp(&p->yiaddr, yiaddr))
      break;
  }
  return p;
}

/* 获取过期时间最长的记录 */
struct dhcpd_lease *dhcpd_find_oldest_expired_lease(struct dhcpd_state *state)
{
  struct dhcpd_lease *oldest = NULL;
  struct dhcpd_lease *p;
  int diff, max_diff = 0;
  
  for (p = state->leases; p != NULL; p = p->next)
  {
    diff = sys_now() - p->expiring_time; // diff>0代表过期了
    if (max_diff < diff)
    {
      max_diff = diff;
      oldest = p;
    }
  }
  return oldest;
}

/* 获取正在为客户端分配的IP地址 */
struct dhcpd_tentative_lease *dhcpd_find_tentative_lease_by_chaddr(struct dhcpd_state *state, const uint8_t *chaddr)
{
  struct dhcpd_tentative_lease *p;
  
  for (p = state->tentative_leases; p != NULL; p = p->next)
  {
    if (memcmp(p->chaddr, chaddr, DHCP_CHADDR_LEN) == 0)
      break;
  }
  return p;
}

/* 获取正在分配的IP地址对应的客户端 */
struct dhcpd_tentative_lease *dhcpd_find_tentative_lease_by_yiaddr(struct dhcpd_state *state, const ip4_addr_t *yiaddr, const struct dhcpd_tentative_lease *except)
{
  struct dhcpd_tentative_lease *p;
  
  for (p = state->tentative_leases; p != NULL; p = p->next)
  {
    if (p != except && ip4_addr_cmp(&p->yiaddr, yiaddr))
      break;
  }
  return p;
}

/* 将DHCP消息中的选项复制到变量中 */
int dhcpd_get_option(struct dhcp_msg *packet, uint8_t code, uint8_t len, void *data)
{
  uint8_t *opt;
  
  opt = dhcpd_get_option_string(packet, code);
  if (opt == NULL || opt[1] > len)
    return -1;
  
  memcpy(data, opt + 2, opt[1]);
  if (opt[1] != len)
    memset((uint8_t *)data + opt[1], 0, len - opt[1]);
  return opt[1]; // 返回选项的实际长度
}

/* 在DHCP消息中查找指定选项 */
uint8_t *dhcpd_get_option_string(struct dhcp_msg *packet, uint8_t code)
{
  int curr = DHCP_OVERLOAD_NONE; // 所在的选项区域
  int i = 0; // 选项区域的位置
  int len = DHCPD_OPTIONS_LEN; // 选项区域长度
  int over = 0; // 扩展选项区域
  uint8_t *optionptr = packet->options; // 选项区域首地址
  
  while (i < len)
  {
    if (optionptr[i] == code)
    {
      // 找到了指定的选项
      if (i + optionptr[i + 1] + 3 > len)
        break; // 如果i后面的剩余空间无法容纳这个选项加一个结束标记, 则认为数据包有问题
      return optionptr + i;
    }
    
    // 处理特殊选项
    switch (optionptr[i])
    {
      case DHCP_OPTION_PAD:
        i++;
        break;
      case DHCP_OPTION_OVERLOAD:
        if (i + optionptr[i + 1] + 1 >= len)
        {
          LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: bogus packet, option fields too long\n", __FUNCTION__));
          return NULL;
        }
        over = optionptr[i + 3];
        i += optionptr[1] + 2;
        break;
      case DHCP_OPTION_END:
        if (curr == DHCP_OVERLOAD_NONE && (over & DHCP_OVERLOAD_FILE))
        {
          optionptr = packet->file;
          i = 0;
          len = DHCP_FILE_LEN;
          curr = DHCP_OVERLOAD_FILE;
        }
        else if (curr == DHCP_OVERLOAD_FILE && (over & DHCP_OVERLOAD_SNAME))
        {
          optionptr = packet->sname;
          i = 0;
          len = DHCP_SNAME_LEN;
          curr = DHCP_OVERLOAD_SNAME;
        }
        else
          return NULL;
        break;
      default:
        i += optionptr[1 + i] + 2;
    }
  }
  return NULL;
}

/* 获取静态IP地址绑定信息 */
struct dhcpd_static_lease *dhcpd_get_static_lease(struct netif *netif, const uint8_t *macaddr)
{
  struct dhcpd_state *state;
  struct dhcpd_static_lease *p;
  
  state = netif_dhcpd_data(netif);
  if (state == NULL)
    return NULL;
  
  for (p = state->static_leases; p != NULL; p = p->next)
  {
    if (memcmp(p->macaddr, macaddr, ETH_HWADDR_LEN) == 0)
      return p;
  }
  return NULL;
}

/* 根据chaddr获取静态IP地址绑定信息 */
struct dhcpd_static_lease *dhcpd_get_static_lease_by_chaddr(struct dhcpd_state *state, const uint8_t *chaddr)
{
  if (!dhcpd_compare_chaddr(chaddr, chaddr))
    return 0; // chaddr不是MAC地址的格式
  
  return dhcpd_get_static_lease(state->netif, chaddr);
}

/* 根据IP地址获取静态IP地址绑定信息 */
struct dhcpd_static_lease *dhcpd_get_static_lease_by_yiaddr(struct dhcpd_state *state, const ip4_addr_t *yiaddr)
{
  struct dhcpd_static_lease *p;
  
  for (p = state->static_leases; p != NULL; p = p->next)
  {
    if (ip4_addr_cmp(&p->ipaddr, yiaddr))
      return p;
  }
  return NULL;
}

struct dhcpd_static_lease *dhcpd_get_static_lease_by_yiaddr_packed(struct dhcpd_state *state, const ip4_addr_p_t *yiaddr)
{
  ip4_addr_t addr;
  
  ip4_addr_set(&addr, yiaddr);
  return dhcpd_get_static_lease_by_yiaddr(state, &addr);
}

/* 初始化DHCP服务器 */
err_t dhcpd_init(void)
{
  err_t err;
  struct udp_pcb *upcb;
  
  // 一个UDP端口号只能绑定到一个upcb上
  // 所以需要为DHCPD建立一个公共的upcb, 无法为每个netif建立单独的upcb
  if (dhcpd_upcb != NULL)
    return ERR_OK;
  
#if !LWIP_DHCP
#if !defined(LWIP_IP_ACCEPT_UDP_PORT) || !LWIP_IP_ACCEPT_UDP_PORT(DHCP_CLIENT_PORT)
#error "DHCP messages cannot be received"
#endif
#endif
#if MEMP_NUM_SYS_TIMEOUT == LWIP_NUM_SYS_TIMEOUT_INTERNAL
#error "sys_timeout() may not work in DHCP server"
#endif
  
  upcb = udp_new();
  if (upcb == NULL)
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: udp_new() failed!\n", __FUNCTION__));
    return ERR_MEM;
  }
  
  ip_set_option(upcb, SOF_BROADCAST);
  err = udp_bind(upcb, IP4_ADDR_ANY, DHCP_SERVER_PORT);
  if (err != ERR_OK)
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: udp_bind() failed! err=%d\n", __FUNCTION__, err));
    udp_remove(upcb);
    return err;
  }
  
  udp_recv(upcb, dhcpd_recv, NULL);
  dhcpd_upcb = upcb;
  return ERR_OK;
}

/* 初始化DHCPD消息内容 */
void dhcpd_init_msg(struct pbuf *p, u8_t type, struct dhcp_msg *client_packet, const ip4_addr_t *server_ip)
{
  struct dhcp_msg *packet = p->payload;
  
  memset(packet, 0, sizeof(struct dhcp_msg));
  switch (type)
  {
    case DHCP_DISCOVER:
    case DHCP_REQUEST:
    case DHCP_RELEASE:
    case DHCP_INFORM:
      packet->op = DHCP_BOOTREQUEST;
      break;
    case DHCP_OFFER:
    case DHCP_ACK:
    case DHCP_NAK:
      packet->op = DHCP_BOOTREPLY;
      break;
  }
  packet->htype = DHCP_HTYPE_ETH;
  packet->hlen = ETH_HWADDR_LEN;
  packet->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
  packet->options[0] = DHCP_OPTION_END;
  dhcpd_add_option(packet, DHCP_OPTION_MESSAGE_TYPE, 1, &type);
  
  if (client_packet != NULL)
  {
    packet->xid = client_packet->xid;
    memcpy(packet->chaddr, client_packet->chaddr, DHCP_CHADDR_LEN);
    packet->flags = client_packet->flags;
    packet->giaddr = client_packet->giaddr;
    packet->ciaddr = client_packet->ciaddr;
  }
  if (server_ip != NULL)
    dhcpd_add_option(packet, DHCP_OPTION_SERVER_ID, 4, server_ip);
}

/* 检查IP地址是否已过期 */
int dhcpd_is_expired_lease(const struct dhcpd_lease *lease)
{
  int diff;
  
  // 考虑到sys_now()溢出的情况, 先作差, 转成有符号数后, 判断差值是否为负
  // 不可以直接比较谁大谁小
  diff = lease->expiring_time - sys_now();
  return diff < 0;
}

/* 检查IP地址是否位于地址池中 */
int dhcpd_is_pool_address(struct dhcpd_state *state, const ip4_addr_t *addr)
{
  uint32_t value, start, end;
  
  if (ip4_addr_cmp(addr, netif_ip4_addr(state->netif)))
    return 0;
  
  value = ntohl(ip4_addr_get_u32(addr));
  start = ntohl(ip4_addr_get_u32(&state->config.start));
  end = ntohl(ip4_addr_get_u32(&state->config.end));
  return value >= start && value <= end;
}

/* 判断某个租约是否为静态绑定 */
int dhcpd_is_static_lease(struct dhcpd_state *state, const struct dhcpd_lease *lease)
{
  struct dhcpd_static_lease *p;
  
  p = dhcpd_get_static_lease_by_chaddr(state, lease->chaddr);
  if (p == NULL)
    return 0; // MAC地址不存在静态IP地址绑定
  return ip4_addr_cmp(&lease->yiaddr, &p->ipaddr); // 若IP地址相等, 则为静态绑定
}

/* 检查tlease是否在链表中 */
// 如果tlease不在链表中, 说明这个tlease指向的内存空间已被释放, 不能再使用
int dhcpd_is_valid_tlease(struct dhcpd_state *state, struct dhcpd_tentative_lease *tlease)
{
  struct dhcpd_tentative_lease *p;
  
  for (p = state->tentative_leases; p != NULL; p = p->next)
  {
    if (p == tlease)
      return 1;
  }
  return 0;
}

/* 显示当前DHCP服务器分配的IP地址列表 */
void dhcpd_print_leases(struct netif *netif)
{
  struct dhcpd_state *state;
  struct dhcpd_lease *p, *oldest;
  
  state = netif_dhcpd_data(netif);
  if (state == NULL)
  {
    printf("DHCP server is not running.\n");
    return;
  }
  
  printf("[DHCPD client list] now=%u\n", sys_now());
  oldest = dhcpd_find_oldest_expired_lease(state);
  for (p = state->leases; p != NULL; p = p->next)
  {
    if (p->hostname.name != NULL)
      printf("%s ", p->hostname.name);
    printf("%02X:%02X:%02X:%02X:%02X:%02X", p->chaddr[0], p->chaddr[1], p->chaddr[2], p->chaddr[3], p->chaddr[4], p->chaddr[5]);
    printf(" %s %u", ip4addr_ntoa(&p->yiaddr), p->expiring_time);
    if (dhcpd_is_static_lease(state, p))
      printf(" [static]");
    if (dhcpd_is_expired_lease(p))
    {
      printf(" [expired]");
      if (p == oldest)
        printf(" [oldest]");
    }
    printf("\n");
  }
}

/* 处理收到的DHCP数据包 */
static void dhcpd_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
  err_t err;
  ip4_addr_t requested_ip, server_ip;
  int ret;
  struct dhcp_msg *packet;
  struct dhcpd_lease *lease;
  struct dhcpd_static_lease *slease_chaddr, *slease_yiaddr;
  struct dhcpd_state *state;
  struct netif *netif;
  uint8_t type;
  uint8_t *hostname_opt;
  
  netif = ip_current_input_netif(); // 获取接收到当前UDP数据包的网络接口
  state = netif_dhcpd_data(netif);
  if (state == NULL)
    goto end;
  
  if (p->next != NULL)
  {
    p = pbuf_coalesce(p, PBUF_RAW);
    if (p->next == NULL)
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: coalesced pbuf data\n", __FUNCTION__));
    else
    {
      LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: pbuf_coalesce() failed!\n", __FUNCTION__));
      goto end;
    }
  }
  
  packet = p->payload;
  LWIP_DEBUGF(DHCPD_DEBUG, ("[DHCP client] addr=%s, port=%d, len=%d, xid=%#x", ipaddr_ntoa(addr), port, p->len, packet->xid));
  hostname_opt = dhcpd_get_option_string(packet, DHCP_OPTION_HOSTNAME);
  if (hostname_opt != NULL && hostname_opt[1] != 0)
    LWIP_DEBUGF(DHCPD_DEBUG, (", hostname=%.*s\n", hostname_opt[1], (char *)&hostname_opt[2]));
  else
    LWIP_DEBUGF(DHCPD_DEBUG, ("\n"));
  if (packet->cookie != PP_HTONL(DHCP_MAGIC_COOKIE))
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: received bogus message, ignoring\n", __FUNCTION__));
    goto end;
  }
  
  // DHCP报文类型
  ret = dhcpd_get_option(packet, DHCP_OPTION_MESSAGE_TYPE, sizeof(type), &type);
  if (ret == -1)
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: couldn't get option from packet, ignoring\n", __FUNCTION__));
    goto end;
  }
  
  lease = dhcpd_find_lease_by_chaddr(state, packet->chaddr);
  if (lease != NULL)
    dhcpd_update_hostname(&lease->hostname, hostname_opt);
  switch (type)
  {
    case DHCP_DISCOVER:
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: received DISCOVER\n", __FUNCTION__));
      
      // 寻找一个未使用的IP地址, 然后发给客户端
      // 这个IP地址仅保留很短的时间(offer_time)
      err = dhcpd_send_offer(state, packet);
      if (err != ERR_OK && err != ERR_INPROGRESS)
        LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: failed to send OFFER\n", __FUNCTION__));
      break;
    case DHCP_REQUEST:
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: received REQUEST\n", __FUNCTION__));
    
      ret = dhcpd_get_option(packet, DHCP_OPTION_REQUESTED_IP, sizeof(requested_ip), &requested_ip);
      if (ret == -1)
        ip4_addr_set_any(&requested_ip);
      
      ret = dhcpd_get_option(packet, DHCP_OPTION_SERVER_ID, sizeof(server_ip), &server_ip);
      if (ret == -1)
        ip4_addr_set_any(&server_ip);
      
      slease_chaddr = dhcpd_get_static_lease_by_chaddr(state, packet->chaddr);
      slease_yiaddr = dhcpd_get_static_lease_by_yiaddr(state, &requested_ip);
      if (lease != NULL)
      {
        if (!ip4_addr_isany_val(server_ip))
        {
          /* SELECTING State */
          LWIP_DEBUGF(DHCPD_DEBUG, ("%s: server_ip = %s\n", __FUNCTION__, ip4addr_ntoa(&server_ip)));
          if (ip4_addr_cmp(&server_ip, netif_ip4_addr(netif)) && !ip4_addr_isany_val(requested_ip))
          {
            if (slease_chaddr != NULL && !ip4_addr_cmp(&requested_ip, &slease_chaddr->ipaddr))
            {
              // 选择的地址不是静态IP表中绑定的地址, 不回应
            }
            else if (slease_yiaddr != NULL && !dhcpd_compare_chaddr(packet->chaddr, slease_yiaddr->macaddr))
            {
              // 其他客户端静态绑定了当前IP地址, 不回应
            }
            else if (ip4_addr_cmp(&requested_ip, &lease->yiaddr))
              dhcpd_send_ack(state, packet, lease);
          }
        }
        else
        {
          if (!ip4_addr_isany_val(requested_ip))
          {
            /* INIT-REBOOT State */
            if (slease_chaddr != NULL && !ip4_addr_cmp(&requested_ip, &slease_chaddr->ipaddr))
              dhcpd_send_nak(state, packet);
            else if (slease_yiaddr != NULL && !dhcpd_compare_chaddr(packet->chaddr, slease_yiaddr->macaddr))
              dhcpd_send_nak(state, packet);
            else if (ip4_addr_cmp(&requested_ip, &lease->yiaddr))
              dhcpd_send_ack(state, packet, lease);
            else
              dhcpd_send_nak(state, packet);
          }
          else
          {
            /* RENEWING or REBINDING State */
            slease_yiaddr = dhcpd_get_static_lease_by_yiaddr_packed(state, &packet->ciaddr);
            if (slease_chaddr != NULL && !ip4_addr_cmp(&packet->ciaddr, &slease_chaddr->ipaddr))
              dhcpd_send_nak(state, packet);
            else if (slease_yiaddr != NULL && !dhcpd_compare_chaddr(packet->chaddr, slease_yiaddr->macaddr))
              dhcpd_send_nak(state, packet);
            else if (ip4_addr_cmp(&packet->ciaddr, &lease->yiaddr))
              dhcpd_send_ack(state, packet, lease);
            else
              dhcpd_send_nak(state, packet);
          }
        }
      }
      else if (!ip4_addr_isany_val(server_ip))
      {
        /* SELECTING State */
      }
      else if (!ip4_addr_isany_val(requested_ip))
      {
        /* INIT-REBOOT State */
        // 客户端已重启, 需要验证旧地址是否还能继续用
        if (slease_chaddr != NULL && !ip4_addr_cmp(&requested_ip, &slease_chaddr->ipaddr))
        {
          // 旧地址不是静态IP表中绑定的地址
          LWIP_DEBUGF(DHCPD_DEBUG, ("%s: requested IP address %s is not equal to the statically allocated one\n", __FUNCTION__, ip4addr_ntoa(&requested_ip)));
          dhcpd_send_nak(state, packet); // 禁止使用
        }
        else if (slease_yiaddr != NULL && !dhcpd_compare_chaddr(packet->chaddr, slease_yiaddr->macaddr))
        {
          // 其他客户端静态绑定了这个旧地址
          LWIP_DEBUGF(DHCPD_DEBUG, ("%s: requested IP address %s is already bound to another client\n", __FUNCTION__, ip4addr_ntoa(&requested_ip)));
          dhcpd_send_nak(state, packet); // 禁止使用
        }
        else
        {
          lease = dhcpd_find_lease_by_yiaddr(state, &requested_ip); // 检查旧地址有没有被占用
          if (lease != NULL)
          {
            if (dhcpd_is_expired_lease(lease))
            {
              // 地址已过期, 不发送回应, 让客户端等待一段时间后重新开始DISCOVER过程获取新地址
              // 这期间暂时用旧地址
              LWIP_DEBUGF(DHCPD_DEBUG, ("%s: requested IP address %s has expired\n", __FUNCTION__, ip4addr_ntoa(&requested_ip)));
              memset(lease->chaddr, 0, DHCP_CHADDR_LEN);
            }
            else
            {
              // 地址已有其他客户端使用, 必须立即更换
              LWIP_DEBUGF(DHCPD_DEBUG, ("%s: requested IP address %s is already in use by another client\n", __FUNCTION__, ip4addr_ntoa(&requested_ip)));
              dhcpd_send_nak(state, packet);
            }
          }
          else if (!dhcpd_is_pool_address(state, &requested_ip))
          {
            // 客户端使用的是不合法的旧地址, 不能继续使用
            // 必须立即重新开始DISCOVER过程, 获取正确的地址
            LWIP_DEBUGF(DHCPD_DEBUG, ("%s: requested IP address %s is not in the pool\n", __FUNCTION__, ip4addr_ntoa(&requested_ip)));
            dhcpd_send_nak(state, packet);
          }
          else
          {
            // 请求的IP地址是空闲地址, 暂时先不发送回应, 等待客户端重新开始DISCOVER过程
            // 这期间客户端可以继续使用这个地址
            LWIP_DEBUGF(DHCPD_DEBUG, ("%s: requested IP address %s is in the pool but not yet allocated, remaining silent\n", __FUNCTION__, ip4addr_ntoa(&requested_ip)));
          }
        }
      }
      else
      {
         /* RENEWING or REBINDING State */
      }
      break;
    case DHCP_DECLINE:
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: received DECLINE\n", __FUNCTION__));
      if (lease != NULL)
      {
        // 将IP地址设为不可用 (一段时间内)
        memset(lease->chaddr, 0, DHCP_CHADDR_LEN);
        lease->expiring_time = sys_now() + 1000 * state->config.decline_time;
      }
      break;
    case DHCP_RELEASE:
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: received RELEASE\n", __FUNCTION__));
      if (lease != NULL)
      {
        // 保留客户端与IP的绑定关系, 只是将租约设为已过期, 而不删除租约记录
        lease->expiring_time = sys_now();
      }
      break;
    case DHCP_INFORM:
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: received INFORM\n", __FUNCTION__));
      dhcpd_send_inform(state, packet);
      break;
    default:
      LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: unsupported DHCP message (%#x) -- ignoring\n", __FUNCTION__, type));
  }

end:
  pbuf_free(p);
}

/* 允许客户端使用分配的IP地址 */
static err_t dhcpd_send_ack(struct dhcpd_state *state, struct dhcp_msg *client_packet, struct dhcpd_lease *lease)
{
  err_t err;
  int ret;
  struct dhcp_msg *packet;
  struct pbuf *p;
  uint32_t lease_time;
  
  // 创建ACK回应报文, 并填入客户端的IP地址
  p = dhcpd_create_msg(DHCP_ACK, client_packet, netif_ip4_addr(state->netif));
  if (p == NULL)
    return ERR_MEM;
  packet = p->payload;
  ip4_addr_set(&packet->yiaddr, &lease->yiaddr);
  
  // 计算租期
  ret = dhcpd_get_option(client_packet, DHCP_OPTION_LEASE_TIME, sizeof(lease_time), &lease_time); // 客户端请求的租期
  if (ret != -1)
  {
    lease_time = ntohl(lease_time);
    if (lease_time < state->config.min_lease || lease_time > state->config.lease)
      lease_time = state->config.lease;
  }
  else
    lease_time = state->config.lease;
  
  // 将租期填入ACK回应报文
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: lease time %u\n", __FUNCTION__, lease_time));
  lease_time = htonl(lease_time);
  dhcpd_add_option(packet, DHCP_OPTION_LEASE_TIME, sizeof(lease_time), &lease_time);
  
  // 发送ACK回应报文, 并更新IP地址表中的租期
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: sending ACK to %s\n", __FUNCTION__, ip4addr_ntoa(&lease->yiaddr)));
  dhcpd_add_common_options(state, packet);
  err = dhcpd_send_packet(state->netif, p);
  if (err == ERR_OK)
    lease->expiring_time = sys_now() + 1000 * ntohl(lease_time);
  pbuf_free(p);
  return err;
}

/* 将网络信息发给客户端, 不分配IP地址 */
static err_t dhcpd_send_inform(struct dhcpd_state *state, struct dhcp_msg *client_packet)
{
  err_t err;
  struct pbuf *p;
  
  p = dhcpd_create_msg(DHCP_ACK, client_packet, netif_ip4_addr(state->netif));
  if (p == NULL)
    return ERR_MEM;
  
  dhcpd_add_common_options(state, p->payload);
  err = dhcpd_send_packet(state->netif, p);
  pbuf_free(p);
  return err;
}

/* 告诉客户端, IP地址不能使用 */
static err_t dhcpd_send_nak(struct dhcpd_state *state, struct dhcp_msg *client_packet)
{
  err_t err;
  struct pbuf *p;
  
  p = dhcpd_create_msg(DHCP_NAK, client_packet, netif_ip4_addr(state->netif));
  if (p == NULL)
    return ERR_MEM;
  
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: sending NAK\n", __FUNCTION__));
  err = dhcpd_send_packet(state->netif, p);
  pbuf_free(p);
  return err;
}

/* 临时分配一个IP地址, 然后发给客户端 */
static err_t dhcpd_send_offer(struct dhcpd_state *state, struct dhcp_msg *client_packet)
{
  int ret;
  ip4_addr_t reqaddr;
  struct dhcpd_lease *lease;
  struct dhcpd_static_lease *slease;
  struct dhcpd_tentative_lease *tlease;
  struct pbuf *resp;
  uint32_t lease_time, now, req_lease_time;
  
  // 准备好待发送的OFFER回应 (resp)
  tlease = dhcpd_find_tentative_lease_by_chaddr(state, client_packet->chaddr);
  if (tlease != NULL)
  {
    // 如果客户端重传了DISCOVER请求, 而之前的请求还没处理完, 则根据新请求修改回应的内容 (比如xid号)
    resp = tlease->resp;
    dhcpd_init_msg(resp, DHCP_OFFER, client_packet, netif_ip4_addr(state->netif));
    dhcpd_update_hostname_from_packet(&tlease->hostname, client_packet);
  }
  else
  {
    // 如果是新客户端的DISCOVER请求, 则需要分配新缓冲区存放OFFER回应报文的内容
    resp = dhcpd_create_msg(DHCP_OFFER, client_packet, netif_ip4_addr(state->netif)); // dhcpd_create_msg = pbuf_alloc + dhcpd_init_msg
    if (resp == NULL)
      return ERR_MEM;
  }
  
  // 计算OFFER回应中要填入的租期值
  now = sys_now();
  lease = dhcpd_find_lease_by_chaddr(state, client_packet->chaddr);
  if (lease != NULL && !dhcpd_is_expired_lease(lease))
    lease_time = (lease->expiring_time - now) / 1000; // 当前剩余租期
  else
    lease_time = state->config.lease; // 默认租期
  ret = dhcpd_get_option(client_packet, DHCP_OPTION_LEASE_TIME, sizeof(req_lease_time), &req_lease_time); // 客户端请求的租期
  if (ret != -1)
  {
    lease_time = ntohl(req_lease_time);
    if (lease_time > state->config.lease)
      lease_time = state->config.lease;
  }
  if (lease_time < state->config.min_lease)
    lease_time = state->config.lease;
  
  // 将计算出来的租期填入OFFER回应报文
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: lease time %u\n", __FUNCTION__, lease_time));
  lease_time = htonl(lease_time);
  dhcpd_add_option(resp->payload, DHCP_OPTION_LEASE_TIME, sizeof(lease_time), &lease_time);
  
  dhcpd_add_common_options(state, resp->payload);
  if (tlease != NULL)
  {
    // 如果之前的DISCOVER请求还未完成, 那么根据当前DISCOVER报文更新完OFFER回应的内容后就在这里退出
    LWIP_ASSERT("tlease != NULL && lease == NULL\n", lease == NULL);
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: busy allocating IP address\n", __FUNCTION__));
    return ERR_INPROGRESS;
  }
  
  if (lease != NULL)
  {
    // 如果已经分配了临时IP地址, 则更新IP地址表中的租期
    // 因为目前IP地址还是临时分配的, 所以这里应该加的是offer_time, 不应该是lease_time
    lease->expiring_time = now + 1000 * state->config.offer_time;
    
    // 将临时IP地址发给客户端
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP %s belongs to the client\n", __FUNCTION__, ip4addr_ntoa(&lease->yiaddr)));
    return dhcpd_send_offer_callback_4(state, resp, &lease->yiaddr);
  }
  else
  {
    // 如果还没有分配临时IP地址, 则在地址池中查找可用的IP地址
    tlease = mem_malloc(sizeof(struct dhcpd_tentative_lease));
    if (tlease == NULL)
    {
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: failed to allocate tlease\n", __FUNCTION__));
      pbuf_free(resp);
      return ERR_MEM;
    }
    memset(tlease, 0, sizeof(struct dhcpd_tentative_lease));
    
    tlease->type = DHCPD_TLEASE_TYPE_UNUSED;
    memcpy(tlease->chaddr, client_packet->chaddr, DHCP_CHADDR_LEN); // 客户端MAC地址
    tlease->yiaddr = state->config.start; // 从地址池的开头开始查找
    tlease->resp = resp; // 暂存未发送的OFFER回应报文, 等IP地址找到了再发送
    dhcpd_update_hostname_from_packet(&tlease->hostname, client_packet); // 暂存客户端主机名
    
    // 将tlease插入链表
    tlease->next = state->tentative_leases;
    if (state->tentative_leases != NULL)
      state->tentative_leases->prev = tlease;
    state->tentative_leases = tlease;
    
    slease = dhcpd_get_static_lease_by_chaddr(state, client_packet->chaddr);
    if (slease != NULL)
    {
      // 使用静态绑定的IP地址
      // 使用前先检查局域网中是否存在, 再决定是否发offer
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: client has been bound to %s\n", __FUNCTION__, ip4addr_ntoa(&slease->ipaddr)));
      tlease->type = DHCPD_TLEASE_TYPE_STATIC;
      tlease->yiaddr = slease->ipaddr;
    }
    else
    {
      // 使用客户端请求的IP地址
      // 使用前先检查lease, slease中是否有重复项, 但不检查tlease, 因为reqaddr比tlease的优先级更高
      ret = dhcpd_get_option(client_packet, DHCP_OPTION_REQUESTED_IP, sizeof(reqaddr), &reqaddr);
      if (ret != -1 && dhcpd_is_pool_address(state, &reqaddr))
      {
        lease = dhcpd_find_lease_by_yiaddr(state, &reqaddr);
        if (lease == NULL || dhcpd_is_expired_lease(lease))
        {
          slease = dhcpd_get_static_lease_by_yiaddr(state, &reqaddr);
          if (slease == NULL)
          {
            LWIP_DEBUGF(DHCPD_DEBUG, ("%s: client requested to use IP %s\n", __FUNCTION__, ip4addr_ntoa(&reqaddr)));
            tlease->type = DHCPD_TLEASE_TYPE_REQUESTED;
            tlease->yiaddr = reqaddr;
          }
        }
      }
    }
    
    dhcpd_send_offer_callback_1(state, tlease);
    return ERR_INPROGRESS;
  }
}

// 地址搜索
// 第一遍首先尝试客户端请求使用的IP地址(DHCPD_TLEASE_TYPE_REQUESTED)
// 第二遍搜索地址表中未使用的IP地址(DHCPD_TLEASE_TYPE_UNUSED)
// 第三遍搜索地址表中已使用但过期了的IP地址(DHCPD_TLEASE_TYPE_EXPIRED)
static void dhcpd_send_offer_callback_1(struct dhcpd_state *state, struct dhcpd_tentative_lease *tlease)
{
  struct dhcpd_lease *lease;
  struct dhcpd_static_lease *other_slease;
  struct dhcpd_tentative_lease *other_tlease;
  uint32_t value, start, end;
  
  LWIP_ASSERT("tlease is valid", dhcpd_is_valid_tlease(state, tlease));
  if (tlease->type == DHCPD_TLEASE_TYPE_UNUSED || tlease->type == DHCPD_TLEASE_TYPE_EXPIRED)
  {
    // 在value和end区间内搜索可用地址
    start = ntohl(ip4_addr_get_u32(&state->config.start));
    end = ntohl(ip4_addr_get_u32(&state->config.end));
    value = ntohl(ip4_addr_get_u32(&tlease->yiaddr));
    while (value <= end)
    {
      ip4_addr_set_u32(&tlease->yiaddr, htonl(value));
      if (!ip4_addr_cmp(&tlease->yiaddr, netif_ip4_addr(state->netif)))
      {
        lease = dhcpd_find_lease_by_yiaddr(state, &tlease->yiaddr); // 检查地址表中是否已被占用
        if (lease == NULL || (tlease->type == DHCPD_TLEASE_TYPE_EXPIRED && dhcpd_is_expired_lease(lease))) // 没有被占用或者已过期
        {
          other_tlease = dhcpd_find_tentative_lease_by_yiaddr(state, &tlease->yiaddr, tlease); // 检查其他客户端是否也临时分配了当前地址
          if (other_tlease == NULL) // 没有被临时分配
          {
            other_slease = dhcpd_get_static_lease_by_yiaddr(state, &tlease->yiaddr); // 检查其他客户端是否静态绑定了当前地址
            if (other_slease == NULL) // 没有被静态绑定
            {
              LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP %s is selected\n", __FUNCTION__, ip4addr_ntoa(&tlease->yiaddr)));
              break; // 选定
            }
          }
        }
      }
      
      if (value == end && tlease->type == DHCPD_TLEASE_TYPE_UNUSED)
      {
        // 所有的地址都已分配完毕, 检查地址表中有没有过期了的地址可以利用
        LWIP_DEBUGF(DHCPD_DEBUG, ("%s: no unused IP address, trying to find an expired one\n", __FUNCTION__));
        tlease->type = DHCPD_TLEASE_TYPE_EXPIRED;
        value = start;
      }
      else
        value++;
    }
    
    if (value > end)
    {
      // 未找到可用地址
      dhcpd_send_offer_callback_3(state, tlease, 0);
      return;
    }
  }
  
  // 在局域网上检查是否有手工配置了相同IP地址的主机
  dhcpd_check_ip(state->netif, &tlease->yiaddr, dhcpd_send_offer_callback_2, tlease);
}

// 处理地址检查结果
static void dhcpd_send_offer_callback_2(void *arg, struct netif *netif, const ip4_addr_t *ipaddr, int exists, const struct eth_addr *ethaddr)
{
  struct dhcpd_lease *lease = NULL;
  struct dhcpd_state *state;
  struct dhcpd_tentative_lease *tlease = arg;
  uint32_t newaddr;
  
  state = netif_dhcpd_data(netif);
  LWIP_ASSERT("tlease is valid", dhcpd_is_valid_tlease(state, tlease));
  if (exists && dhcpd_compare_chaddr(tlease->chaddr, ethaddr->addr))
    exists = 0;
  
  if (exists == 0)
  {
    // 地址可用, 再次检查是否已被分配
    lease = dhcpd_find_lease_by_yiaddr(state, ipaddr);
    if (lease == NULL || dhcpd_is_expired_lease(lease))
    {
      // 分配成功
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP address %s is available\n", __FUNCTION__, ip4addr_ntoa(ipaddr)));
      dhcpd_send_offer_callback_3(state, tlease, 1);
      return;
    }
    
    // 否则地址不可用
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP address %s is unavailable\n", __FUNCTION__, ip4addr_ntoa(ipaddr)));
  }
  else if (exists == -1)
  {
    // 无法检查IP, 直接认为IP分配失败
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: failed to check IP address %s\n", __FUNCTION__, ip4addr_ntoa(ipaddr)));
    dhcpd_send_offer_callback_3(state, tlease, 0);
    return;
  }
  else
  {
    // 地址不可用, 继续搜索
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: IP address %s belongs to someone, reserving it for %u seconds\n", __FUNCTION__, 
      ip4addr_ntoa(ipaddr), state->config.conflict_time));
    lease = dhcpd_add_lease(state, NULL, ipaddr, state->config.conflict_time); // 设置为已占用
    if (lease != NULL)
      memcpy(lease->chaddr, ethaddr->addr, ETH_HWADDR_LEN);
  }
  
  if (tlease->type == DHCPD_TLEASE_TYPE_STATIC)
  {
    // 静态绑定的IP地址不可用, 停止搜索
    dhcpd_send_offer_callback_3(state, tlease, 0);
    return;
  }
  else if (tlease->type == DHCPD_TLEASE_TYPE_REQUESTED)
  {
    // 客户端请求的IP地址不可用, 从地址池开头搜索
    tlease->type = DHCPD_TLEASE_TYPE_UNUSED;
    tlease->yiaddr = state->config.start;
  }
  else if (lease == NULL)
  {
    // 本来, "将不可用地址设为已占用"这个操作成功的话, callback_1会跳过当前地址, 继续查找下一个未使用或已过期的地址
    // 如果失败, callback_1就不知道当前地址不可用, 为防止死循环, 地址值要加1, 跳过当前不可用地址
    newaddr = ntohl(ip4_addr_get_u32(&tlease->yiaddr));
    newaddr = htonl(newaddr + 1);
    ip4_addr_set_u32(&tlease->yiaddr, newaddr);
  }
  dhcpd_send_offer_callback_1(state, tlease);
}

// 将临时分配的IP地址添加到地址表, 然后发送OFFER回应
// 函数调用后, 指针tlease以及缓冲区tlease->resp不能再使用
static err_t dhcpd_send_offer_callback_3(struct dhcpd_state *state, struct dhcpd_tentative_lease *tlease, int succeeded)
{
  err_t err;
  struct dhcpd_lease *lease = NULL;
  struct pbuf *p = tlease->resp;
  
  // 将临时分配的IP地址添加到地址表, 有效时间为offer_time
  LWIP_ASSERT("tlease is valid", dhcpd_is_valid_tlease(state, tlease));
  if (succeeded)
    lease = dhcpd_add_lease(state, tlease->chaddr, &tlease->yiaddr, state->config.offer_time);
  
  // 将主机名从tlease复制到lease中
  if (lease != NULL)
  {
    dhcpd_update_hostname(&lease->hostname, NULL); // 释放lease原有的hostname占用的内存
    lease->hostname = tlease->hostname; // 复制
    memset(&tlease->hostname, 0, sizeof(struct dhcpd_hostname)); // 清空tlease里面的hostname, 但不释放内存, 因为内存现在被lease引用了
  }
  else
    dhcpd_update_hostname(&tlease->hostname, NULL); // 释放tlease里面hostname占用的内存
  
  // 删除tlease
  if (tlease->prev == NULL)
  {
    LWIP_ASSERT("state->tentative_leases == tlease", state->tentative_leases == tlease);
    state->tentative_leases = tlease->next;
  }
  else
    tlease->prev->next = tlease->next;
  if (tlease->next != NULL)
    tlease->next->prev = tlease->prev;
  tlease->resp = NULL; // 这段内存后续由p指针接管
  mem_free(tlease);
  tlease = NULL;
  
  // 发送OFFER回应, 将临时分配的IP地址告诉客户端
  if (succeeded)
  {
    if (lease != NULL)
    {
      err = dhcpd_send_offer_callback_4(state, p, &lease->yiaddr);
      p = NULL;
    }
    else
    {
      LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: lease pool is full -- OFFER abandoned\n", __FUNCTION__));
      err = ERR_MEM;
    }
  }
  else
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: no IP addresses to give -- OFFER abandoned\n", __FUNCTION__));
    err = ERR_OK;
  }
  
  if (p != NULL)
    pbuf_free(p);
  return err;
}

// 发送OFFER回应
// 函数调用后, 指针p不能再使用
static err_t dhcpd_send_offer_callback_4(struct dhcpd_state *state, struct pbuf *p, const ip4_addr_t *yiaddr)
{
  err_t err;
  struct dhcp_msg *packet = p->payload;
  
  ip4_addr_set(&packet->yiaddr, yiaddr);
  LWIP_DEBUGF(DHCPD_DEBUG, ("%s: sending OFFER of %s\n", __FUNCTION__, ip4addr_ntoa(yiaddr)));
  err = dhcpd_send_packet(state->netif, p);
  pbuf_free(p);
  return err;
}

/* 发送DHCP消息 */
err_t dhcpd_send_packet(struct netif *netif, struct pbuf *p)
{
  err_t err;
  ip_addr_t addr;
  struct dhcp_msg *packet = p->payload;
  
  if (ip4_addr_isany_val(packet->giaddr))
  {
    // 直接发送
    err = udp_sendto_if(dhcpd_upcb, p, IP_ADDR_BROADCAST, DHCP_CLIENT_PORT, netif);
  }
  else
  {
    // 中转发送
    LWIP_DEBUGF(DHCPD_DEBUG, ("%s: Forwarding packet to relay\n", __FUNCTION__));
    ip_addr_copy_from_ip4(addr, packet->giaddr);
    err = udp_sendto(dhcpd_upcb, p, &addr, DHCP_SERVER_PORT);
  }

  if (err != ERR_OK)
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: udp_sendto() failed! err=%d\n", __FUNCTION__, err));
  return err;
}

/* 在网络接口上启动DHCP服务器 */
err_t dhcpd_start(struct netif *netif, const struct dhcpd_config *config)
{
  err_t err;
  struct dhcpd_state *state;
  
  if (dhcpd_upcb == NULL)
  {
    err = dhcpd_init();
    if (err != ERR_OK)
      return err;
  }
  
  if (netif_dhcpd_data(netif) != NULL)
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: DHCP server is already running on the interface!\n", __FUNCTION__));
    return ERR_ARG;
  }
  else if (ip4_addr_isany(netif_ip4_addr(netif)))
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: netif does not have an IP address!\n", __FUNCTION__));
    return ERR_ARG;
  }
  
  state = mem_malloc(sizeof(struct dhcpd_state));
  if (state == NULL)
  {
    LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: mem_malloc() failed!\n", __FUNCTION__));
    return ERR_MEM;
  }
  
  memset(state, 0, sizeof(struct dhcpd_state));
  state->config = *config;
  state->netif = netif;
  state->next = dhcpd_list;
  dhcpd_list = state;
  return ERR_OK;
}

/* 将opt中的名称复制到hostname结构体中 */
int dhcpd_update_hostname(struct dhcpd_hostname *hostname, const uint8_t *opt)
{
  const uint8_t empty_opt[2] = {DHCP_OPTION_HOSTNAME, 0};
  char *p;
  
  if (opt == NULL)
    opt = empty_opt;
  
  if (opt[0] != DHCP_OPTION_HOSTNAME)
    return -1;
  
  if (opt[1] == 0)
  {
    // 清除hostname并释放内存空间
    if (hostname->name != NULL)
    {
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: delete hostname \"%s\"\n", __FUNCTION__, hostname->name));
      mem_free(hostname->name);
      hostname->name = NULL;
    }
    hostname->namelen = 0;
    hostname->capacity = 0;
    return 1;
  }
  else
  {
    if (hostname->name == NULL)
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: add hostname \"%.*s\"\n", __FUNCTION__, opt[1], (const char *)(opt + 2)));
    else
    {
      if (hostname->namelen == opt[1] && memcmp(hostname->name, opt + 2, opt[1]) == 0)
        return 0;
      LWIP_DEBUGF(DHCPD_DEBUG, ("%s: change hostname from \"%s\" to \"%.*s\"\n", __FUNCTION__, hostname->name, opt[1], (const char *)(opt + 2)));
    }
    
    if (opt[1] + 1 >= hostname->capacity)
    {
      // 容量不够, 需要重新分配内存
      p = mem_malloc(opt[1] + 1);
      if (p == NULL)
      {
        LWIP_DEBUGF(DHCPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("%s: mem_malloc() failed! namelen=%u\n", __FUNCTION__, opt[1]));
        return -1; // 内存不足
      }
      if (hostname->name != NULL)
        mem_free(hostname->name);
      hostname->name = p;
      hostname->capacity = opt[1] + 1;
    }
    
    hostname->namelen = opt[1];
    memcpy(hostname->name, (const char *)(opt + 2), hostname->namelen);
    memset(hostname->name + hostname->namelen, 0, hostname->capacity - hostname->namelen);
    return 1;
  }
}

/* 将数据包中的主机名复制到hostname结构体中 */
int dhcpd_update_hostname_from_packet(struct dhcpd_hostname *hostname, struct dhcp_msg *client_packet)
{
  uint8_t *opt;
  
  opt = dhcpd_get_option_string(client_packet, DHCP_OPTION_HOSTNAME);
  return dhcpd_update_hostname(hostname, opt);
}

/* 获取网络接口的DHCPD状态信息 */
struct dhcpd_state *netif_dhcpd_data(struct netif *netif)
{
  struct dhcpd_state *state;
  
  for (state = dhcpd_list; state != NULL; state = state->next)
  {
    if (state->netif == netif)
      break;
  }
  return state;
}

#endif

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值