lwip源码分析 之 DHCP协议实现(一)

一,dhcp协议简介

dhcp协议是动态主机配置协议,是互联网的基本协议。lwip内核也实现了该协议。关于lwip协议的具体内容,请先转到:https://blog.csdn.net/lm409/article/details/80298225

lwip实现dhcp的代码在core/dhcp.c中。

二,源码解析

应用程序要使用dhcp协议,只需要在代码中调用:

err_t dhcp_start(struct netif *netif);

并在定时调用以下两个定时器,就能获得ip地址给网口。

/** 每一分钟调用一次 */
void dhcp_coarse_tmr(void);
/** 500ms调用一次 */
void dhcp_fine_tmr(void);

首先认识描述dhcp客户端的结构体:

2.1 dhcp结构体

struct dhcp
{
    
    
  u32_t xid;  //事务ID,随机数,有客户端生成,服务器Reply时,会把Request中的Transaction拷贝到Reply报文中。
  
  struct udp_pcb *pcb;  //连接dhcp服务器的udp控制块
  
  struct dhcp_msg *msg_in;  //dhcp输入报文
  
  u8_t state; //当前dhcp状态机
  
  u8_t tries; //重请求次数
  u8_t subnet_mask_given;	//是否有子网掩码

  struct pbuf *p_out; /* 输出的报文的pbuf pbuf of outcoming msg */ 
  struct dhcp_msg *msg_out; /* 输出的报文 outgoing msg */
  u16_t options_out_len; /* 输出的报文的选项长度 outgoing msg options length */
  //请求超时时间
  u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
  u16_t t1_timeout;  /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
  u16_t t2_timeout;  /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */

  ip_addr_t server_ip_addr;   //dhcp服务器地址
  ip_addr_t offered_ip_addr;  //服务器提供的ip
  ip_addr_t offered_sn_mask;  //服务器提供的子网掩码
  ip_addr_t offered_gw_addr;  //服务器提供的网关地址
 
  //dhcp报文选项字段中的时间
  u32_t offered_t0_lease; /* 租借地址的时间 lease period (in seconds) */
  u32_t offered_t1_renew; /* 续租的时间 recommended renew time (usually 50% of lease period) */
  u32_t offered_t2_rebind; /* 租约重新设定的时间 recommended rebind time (usually 66% of lease period)  */
  /* @todo: LWIP_DHCP_BOOTP_FILE configuration option?
     integrate with possible TFTP-client for booting? */
};

dhcp报文实质上是udp报文,故结构体有udp控制块。其中需要关注与时间有关的变量:

  u16_t request_timeout; //请求超时时间
  u16_t t1_timeout;  //续租超时时间
  u16_t t2_timeout;		//重绑定超时时间

这三个变量会在每次定时函数被执行时减1,当他们其中一个为0时,表示超时时间到,会执行相应的代码来使dhcp客户端保持正常运行。另外三个时间变量也与此相关:

  u32_t offered_t0_lease; /* 租借地址的时间 lease period (in seconds) */
  u32_t offered_t1_renew; /* 续租的时间 recommended renew time (usually 50% of lease period) */
  u32_t offered_t2_rebind;/* 租约重新设定的时间 recommended rebind time (usually 66% of lease period)  */

这三个变量是由dhcp服务器返回的报文里的选项参数决定的,每一个dhcp服务器报文到来,这三个变量就会对timeout进行更新。
在这里插入图片描述
最后还要注意dhcp客户端状态的转换,dhcp客户端的状态由10种,但我们只需要知道主要的4种:

#define DHCP_OFF          0 
#define DHCP_REQUESTING   1 
#define DHCP_INIT         2 
#define DHCP_REBOOTING    3 
#define DHCP_REBINDING    4 
#define DHCP_RENEWING     5
#define DHCP_SELECTING    6
#define DHCP_INFORMING    7
#define DHCP_CHECKING     8
#define DHCP_PERMANENT    9
#define DHCP_BOUND        10

与dhcp建立过程对应的状态如图:
设备刚刚启动dhcp客户端时:
在这里插入图片描述
续约时间到,dhcp客户端续约当前的ip。
在这里插入图片描述

2.2 开始广播

有了以上的认识,对代码逻辑的理解就会更简单。应用代码中,只需要调用dhcp_start()就能开启dhcp功能,那么这个函数它干嘛了?

err_t
dhcp_start(struct netif *netif)
{
    
    
  struct dhcp *dhcp;
  err_t result = ERR_OK;

  LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
  dhcp = netif->dhcp;
  netif->flags &= ~NETIF_FLAG_DHCP; //清除dhcp成功标志

  //检查网络接口是否是以太网
  if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
    
    
    return ERR_ARG;
  }

  /* check MTU of the netif */
  if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
    
    
    return ERR_MEM;
  }

  //该网络接口未使用过dhcp客户端
  if (dhcp == NULL) {
    
    
    dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));  //分配一个dhcp内存
    if (dhcp == NULL) {
    
    
      return ERR_MEM;
    }
    netif->dhcp = dhcp; //保存dhcp客户端到当前网口

  } else {
    
      //接口已经有一个dhcp客户端
    if (dhcp->pcb != NULL) {
    
    
      udp_remove(dhcp->pcb);  //为了重新配置ip,需要先将dhcp客户端的pcb移除
    }
    LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
    LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
  }
    
  memset(dhcp, 0, sizeof(struct dhcp)); //清零dhcp内存

  dhcp->pcb = udp_new();  //创建新的udp控制块
  if (dhcp->pcb == NULL) {
    
    
    return ERR_MEM;
  }
  dhcp->pcb->so_options |= SOF_BROADCAST; //开启发送和接收的广播

  udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); //绑定本地ip(全0)和端口 dhcp客户端专属端口68
  udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);  //连接到远程dhcp服务器端口67
 
  udp_recv(dhcp->pcb, dhcp_recv, netif);  //设置dhcp接收回调函数和参数

  result = dhcp_discover(netif);  //dhcp客户端开始运行
  if (result != ERR_OK) {
    
    
    dhcp_stop(netif);	//错了,停止dhcp
    return ERR_MEM;
  }
  netif->flags |= NETIF_FLAG_DHCP;  //置位dhcp成功
  return result;
}

**可见这个函数主要是为dhcp结构体分配内存,并建立了一个udp连接,注册好dhcp的回调函数(当收到ducp服务器发送的报文时会进入回调函数),并调用dhcp_discover();函数开始广播dhcp发现报文。**接着看这个函数:

static err_t
dhcp_discover(struct netif *netif)
{
    
    
  struct dhcp *dhcp = netif->dhcp;  //使用当前网口的dhcp客户端
  err_t result = ERR_OK;
  u16_t msecs;
  ip_addr_set_any(&dhcp->offered_ip_addr);  //初始化dhcp->offered_ip_addr为0
  dhcp_set_state(dhcp, DHCP_SELECTING); //dhcp为状态
  
  result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER); //创建dhcp发现报文首部
  if (result == ERR_OK) {
    
    
    //填充发现报文的选项字段(较为复杂,参考协议)
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));

    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));

    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
    dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
    dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
    dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
    dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);

    dhcp_option_trailer(dhcp);
    //为p_out减少内存
    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);  
	//将p_out的数据发送,等待dhcp服务器返回报文
    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);  
    
    dhcp_delete_msg(dhcp);  //释放dhcp发现报文的内存
    
  } else {
    
    

  }
  dhcp->tries++;  //重发次数+1
  msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;  //计算请求超时时间

  return result;
}

该函数先将dhcp客户端设置为DHCP_SELECTING状态,然后调用dhcp_create_msg()构造dhcp发现报文并填充选项字段,调用udp_sendto_if()将udp报文广播出去,并设置请求超时时间,等待dhcp客户端的回答。

那么dhcp发现报文如何构造,下图所示dhcp报文内容:
在这里插入图片描述

dhcp_create_msg():就是按照上图构造dhcp报文的。

static err_t
dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
{
    
    
  u16_t i;

  static u32_t xid = 0xABCD0000;	//静态的事务id

  //申请dhcp报文的pbuf
  dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
  if (dhcp->p_out == NULL) {
    
    
    return ERR_MEM;
  }

  //如果是一个新的dhcp客户端,xid就更新。重绑定的情况下xid不需要更新
  if (dhcp->tries == 0) {
    
    
      xid++;
  }
  dhcp->xid = xid;

  //dhcp_msg指向dhcp报文的缓存地址
  dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;

  //对dhcp报文填充内容,也就是填充入p_out->payload;
  dhcp->msg_out->op = DHCP_BOOTREQUEST;

  dhcp->msg_out->htype = DHCP_HTYPE_ETH;
  dhcp->msg_out->hlen = netif->hwaddr_len;
  dhcp->msg_out->hops = 0;
  dhcp->msg_out->xid = htonl(dhcp->xid);
  dhcp->msg_out->secs = 0;
  /* we don't need the broadcast flag since we can receive unicast traffic
     before being fully configured! */
  dhcp->msg_out->flags = 0;
  ip_addr_set_zero(&dhcp->msg_out->ciaddr); //设置当前客户端ip为0
  
  //下面根据报文类型和dhcp客户端的不同,有不同的逻辑
  if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) ||
      ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
       ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
    
    
    //通知,拒绝类型的报文
    //重新请求或重新绑定状态下的请求报文
   //以上情况的dhcp客户端都已经有ip地址了,所以构造的报文的ciaddr为接口地址
    ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
  }
  //下面三个地址清零,这是服务器即将分配的地址
  ip_addr_set_zero(&dhcp->msg_out->yiaddr);
  ip_addr_set_zero(&dhcp->msg_out->siaddr);
  ip_addr_set_zero(&dhcp->msg_out->giaddr);
  for (i = 0; i < DHCP_CHADDR_LEN; i++) {
    
    
    //将网口硬件地址复制到报文硬件地址选项
    dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/;
  }
  //清零服务端名字
  for (i = 0; i < DHCP_SNAME_LEN; i++) {
    
    
    dhcp->msg_out->sname[i] = 0;
  }
  for (i = 0; i < DHCP_FILE_LEN; i++) {
    
    
    dhcp->msg_out->file[i] = 0;
  }
  dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
  dhcp->options_out_len = 0;  //初始选项长度为0
  //用一个随便的数组填充报文的选项字段
  for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
    
    
    dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
  }
	//填充报文选项字段中的报文类型
  dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
  dhcp_option_byte(dhcp, message_type);
  return ERR_OK;
}

2.3 回调接口

广播完成后,只需等待dhcp服务器们发来reply信息。当udp控制块收到dhcp服务端的消息时,就会调用设置的回调函数。

static void
dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
{
    
    
  struct netif *netif = (struct netif *)arg;	//获取网口
  struct dhcp *dhcp = netif->dhcp;
  struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; //获取返回的数据
  u8_t msg_type;
  u8_t i;
  
  //检查返回数据长度
  if (p->len < DHCP_MIN_REPLY_LEN) {
    
    
    goto free_pbuf_and_return;
  }
  //检查报文是否是reply类型
  if (reply_msg->op != DHCP_BOOTREPLY) {
    
    
    goto free_pbuf_and_return;
  }
 
  //检查reply报文的客户端硬件地址跟本地网口地址对不对得上
  for (i = 0; i < netif->hwaddr_len; i++) {
    
    
    if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
    
    
      goto free_pbuf_and_return;	//对不上
    }
  }

  //检查事物id与当前dhcp客户端的xid对不对
  if (ntohl(reply_msg->xid) != dhcp->xid) {
    
    
    goto free_pbuf_and_return;
  }
  //解析reply报文的内容
  if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
    
    
    goto free_pbuf_and_return;
  }

  //检查选项字段中是否有Message Type
  if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
    
    
    goto free_pbuf_and_return;
  }

  //获取报文类型
  msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);

  if (msg_type == DHCP_ACK) {
    
    
    if (dhcp->state == DHCP_REQUESTING) {
    
    
      //dhcp报文是ack类型,且客户端处于请求状态(联系上图)
      dhcp_handle_ack(netif);
#if DHCP_DOES_ARP_CHECK
      /* check if the acknowledged lease address is already in use */
      dhcp_check(netif);
#else
      
      dhcp_bind(netif); //将dhcp申请到的ip绑定到netif
#endif
    }
    
    //以下三种状态的dhcp也能进行绑定
    else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
    
    
      dhcp_bind(netif);
    }
  }
  //根据dhcp协议,服务器返回nack报文,客户端需要重新广播,重新获取ip
  else if ((msg_type == DHCP_NAK) &&
    ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
     (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING  ))) {
    
    
    dhcp_handle_nak(netif);	//重新广播获取ip
  }
  
  //offer类型的报文且dhcp处于selecting状态(结合上图)
  else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
    
    
    dhcp->request_timeout = 0;	//关闭请求超时

    dhcp_handle_offer(netif); //保存ip 并进入请求状态
  }
free_pbuf_and_return:
  dhcp->msg_in = NULL;
  pbuf_free(p);
}

回调函数首先解析dhcp报文的内容和选项字段,选项字段的值被保存在全局变量 dhcp_rx_options_val[] 中。然后根据报文类型,dhcp客户端所处的状态,判断当前dhcp连接的状态,进行进一步的处理。如图,一共有三种情况需要进一步处理。
在这里插入图片描述

a,发送请求

假设客户端发出发现广播,接收到服务器们的offer报文,接下来客户端应该选择一个服务器,广播请求报文,请求指定服务器的ip。这个功能在dhcp_handle_offer(netif);实现,dhcp_select(netif);则完成了请求报文的构造与发送功能。

//保存服务器的ip和分配的ip
static void
dhcp_handle_offer(struct netif *netif)
{
    
    
  struct dhcp *dhcp = netif->dhcp;

  //服务器ip不为0
  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
    
    
    //获取服务器ip
    ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
    //获取服务器分配的ip
    ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
    dhcp_select(netif);	//广播请求报文
  } else {
    
    

  }
}
static err_t
dhcp_select(struct netif *netif)
{
    
    
  struct dhcp *dhcp = netif->dhcp;
  err_t result;
  u16_t msecs;
  dhcp_set_state(dhcp, DHCP_REQUESTING);  //请求状态

  //构造请求报文,填充选项字段,并广播出去。
  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
  if (result == ERR_OK) {
    
    
    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));

    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
    //将offer的ip地址填入“ 请求的IP地址 ”选项
    dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));

    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
    //将服务器地址填入
    dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr)));

    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
    dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
    dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
    dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
    dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);

    dhcp_option_trailer(dhcp);
    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
	//通过网口将请求报文广播,等待服务器的ack
    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
    dhcp_delete_msg(dhcp);	//删除报文
    
  } else {
    
    
  }
  dhcp->tries++;	//重试次数+1
  msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;	//计算超时时间
  return result;
}

b,等待ack

接收到服务器返回的ack报文后,提取报文中的时间,ip地址信息到dhcp客户端,并由此更新dhcp的超时时间,最后设置与dhcp对应的网口的ip地址,掩码和网关,使能网口。到此,网口就有了一个属于自己的ip地址了。

static void
dhcp_handle_ack(struct netif *netif)
{
    
    
  struct dhcp *dhcp = netif->dhcp;
  
	//先将本地dhcp结构的两个变量清零
  ip_addr_set_zero(&dhcp->offered_sn_mask);
  ip_addr_set_zero(&dhcp->offered_gw_addr);

  //以下都是根据ack报文的内来给dhcp客户端的变量赋值
  
  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
    
    
    //更新租约时间
    dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
  }

  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
    
    
  	//更新续约时间
    dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
  } else {
    
    
    //否则,计算续约时间
    dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
  }

  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
    
    
    dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
  } else {
    
    
    dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
  }

  //复制服务器提供的ip到dhcp客户端
  ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);

  //获取子网掩码字段
  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
    
    
    //复制子网掩码到dhcp
    ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
    dhcp->subnet_mask_given = 1;//标记
  } else {
    
    
    dhcp->subnet_mask_given = 0;
  }

  //获取网关字段
  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
    
    
    ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
  }
}
static void
dhcp_bind(struct netif *netif)
{
    
    
  u32_t timeout;
  struct dhcp *dhcp;
  ip_addr_t sn_mask, gw_addr;
  dhcp = netif->dhcp;

  //由ack报文提供的租约时间 更新dhcp客户端的超时时间
  if (dhcp->offered_t1_renew != 0xffffffffUL) {
    
    
    timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
    if(timeout > 0xffff) {
    
    
      timeout = 0xffff;
    }
    dhcp->t1_timeout = (u16_t)timeout;
    if (dhcp->t1_timeout == 0) {
    
    
      dhcp->t1_timeout = 1;
    }
  }

  if (dhcp->offered_t2_rebind != 0xffffffffUL) {
    
    
    timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
    if(timeout > 0xffff) {
    
    
      timeout = 0xffff;
    }
    dhcp->t2_timeout = (u16_t)timeout;
    if (dhcp->t2_timeout == 0) {
    
    
      dhcp->t2_timeout = 1;
    }
  }

  //获取子网掩码
  if (dhcp->subnet_mask_given) {
    
    
    ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
  } else {
    
    
    //无提供掩码
    u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
    if (first_octet <= 127) {
    
    
      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
    } else if (first_octet >= 192) {
    
    
      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
    } else {
    
    
      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
    }
  }
  //获取网关地址
  ip_addr_copy(gw_addr, dhcp->offered_gw_addr);

  if (ip_addr_isany(&gw_addr)) {
    
    
	//无提供网关,则将网络的第一个主机作为网关
    ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
    ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
  }

  //更新网口的ip,子网掩码,网关为dhcp获取到的
  netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
  netif_set_netmask(netif, &sn_mask);
  netif_set_gw(netif, &gw_addr);
  netif_set_up(netif);  //使能网口

  dhcp_set_state(dhcp, DHCP_BOUND); //绑定状态
}

2.4 其他情况

剩下的情况包括租约时间的更新,重新绑定等,这些与时间相关的代码,下一章,定时器
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44821644/article/details/114048135
今日推荐