【无OS】基于STM32移植LWIP 1.4.1之DHCP

1.前言

本篇文章介绍在STM32平台移植LwIP DHCP,是基于对DHCP协议工作过程有一定了解后,再在以前实现Ping功能的基础上来实现的。所以在阅读本文之前,可能需要参考一下以前写的文章:

  1. 《DHCP协议工作流程分析》
  2. 《【无OS】基于STM32移植LWIP 1.4.1之Ping》

由于有之前实现Ping功能的基础,所以DHCP的移植很简单,下面将主要介绍LwIP DHCP的流程分析。

2.DHCP功能的移植

LwIP模式已经实现了DHCP客户端的功能,我们只需要打开相关宏就可以。修改如下:

--- a/stm32f429_lwip_basic_ping/User/LAN8742A/netconf.h
+++ b/stm32f429_lwip_basic_ping/User/LAN8742A/netconf.h
@@ -41,6 +41,9 @@
 #define DHCP_ADDRESS_ASSIGNED      3
 #define DHCP_TIMEOUT               4
 #define DHCP_LINK_DOWN             5
+
+#define USE_DHCP       /* enable DHCP, if disabled static address is used */
+
 /* Exported constants --------------------------------------------------------*/
 /* Exported macro ------------------------------------------------------------*/
 /* Exported functions ------------------------------------------------------- */

编译成功后,验证结果截图如下:

3.打印LwIP DHCP Log

为了方便熟悉LwIP DHCP工作流程,需要打印DHCP Log来分析。这里打开了3个宏:

  1. LWIP_DEBUG
  2. UDP_DEBUG
  3. DHCP_DEBUG

修改代码如下:

--- a/stm32f429_lwip_pro_list/2.stm32f429_lwip_basic_dhcp/User/lwipopts.h
+++ b/stm32f429_lwip_pro_list/2.stm32f429_lwip_basic_dhcp/User/lwipopts.h
@@ -209,7 +209,7 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums
    ---------- Lwip Debug options ----------
    ----------------------------------------
 */
-//#define LWIP_DEBUG                      1
+#define LWIP_DEBUG                      1

 #endif /* __LWIPOPTS_H__ */

--- a/stm32f429_lwip_pro_list/2.stm32f429_lwip_basic_dhcp/lwip-1.4.1/src/include/lwip/opt.h
+++ b/stm32f429_lwip_pro_list/2.stm32f429_lwip_basic_dhcp/lwip-1.4.1/src/include/lwip/opt.h
@@ -2071,7 +2071,7 @@
  * UDP_DEBUG: Enable debugging in UDP.
  */
 #ifndef UDP_DEBUG
-#define UDP_DEBUG                       LWIP_DBG_OFF
+#define UDP_DEBUG                       LWIP_DBG_ON
 #endif

 /**
@@ -2099,7 +2099,7 @@
  * DHCP_DEBUG: Enable debugging in dhcp.c.
  */
 #ifndef DHCP_DEBUG
-#define DHCP_DEBUG                      LWIP_DBG_OFF
+#define DHCP_DEBUG                      LWIP_DBG_ON
 #endif

4.LwIP DHCP工作过程

LwIP DHCP工作过程主要包括4个阶段,如下:

  1. 发现阶段(DHCP Discover):即 DHCP 客户端寻找 DHCP 服务器的阶段。
  2. 提供阶段(DHCP Offer):即 DHCP 服务器提供 IP 地址的阶段。
  3. 选择阶段(DHCP Request):即 DHCP 客户端选择某台 DHCP 服务器提供的 IP 地址的阶段。
  4. 确认阶段(DHCP Ack):即 DHCP 服务器确认所提供的 IP 地址的阶段。

下面根据代码和log分析一下这4个阶段。

4.1 发现阶段(DHCP Discover)

调用了dhcp_start函数后,就开启了DHCP的流程。主要是通过UDPDHCP Sever通信。在发现阶段,DHCP客户端通过发送DHCP-DISCOVER报文来寻找DHCP服务器。由于DHCP服务器的IP地址对于客户端来说是未知的,所以DHCP客户端以广播方式发送DHCP-DISCOVER报文。设备的打印Log如下:

[12:52:08.025]dhcp_start(netif=20003e7c) st0
[12:52:08.026]dhcp_start(): starting new DHCP client
[12:52:08.038]dhcp_start(): allocated dhcpudp_bind(ipaddr = 0.0.0.0, port = 68)
[12:52:08.038]udp_bind: bound to 0.0.0.0, port 68
[12:52:08.039]udp_connect: connected to 0.0.0.0,port 68
[12:52:08.040]dhcp_start(): starting DHCP configuration
[12:52:08.050]dhcp_discover()
[12:52:08.051]transaction id xid(abcd0001)
[12:52:08.052]dhcp_discover: making request
[12:52:08.052]dhcp_discover: realloc()ing
[12:52:08.060]dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)
[12:52:08.066]udp_send: added header in given pbuf 20003f34
[12:52:08.072]udp_send: sending datagram of length 316
[12:52:08.073]udp_send: UDP packet length 316
[12:52:08.074]udp_send: UDP checksum 0x0000
[12:52:08.075]udp_send: ip_output_if (,,,,IP_PROTO_UDP,)
[12:52:08.085]dhcp_discover: deleting()ing
[12:52:08.086]dhcp_discover: SELECTING
[12:52:08.086]dhcp_discover(): set request timeout 2000 msecs

根据设备的打印log,然后结合代码整理出DHCP Discover的时序图如下:

4.2 提供阶段(DHCP Offer)

网络中接收到DHCP-DISCOVER报文的DHCP服务器,会选择一个合适的IP地址,连同IP地址租约期限和其他配置信息(如网关地址,域名服务器地址等)一同通过DHCP-OFFER报文发送给DHCP客户端。设备的打印Log如下:

[12:52:08.093]udp_input: received datagram of length 556
[12:52:08.093]UDP header:
[12:52:08.094]+-------------------------------+
[12:52:08.106]|        67     |        68     | (src port, dest port)
[12:52:08.106]+-------------------------------+
[12:52:08.107]|       556     |     0x6d50    | (len, chksum)
[12:52:08.116]+-------------------------------+
[12:52:08.117]udp (192.168.1.102, 68) <-- (192.168.1.1, 67)
[12:52:08.118]udp_input: calculating checksum
[12:52:08.128]dhcp_recv(pbuf = 200086fc) from DHCP server 192.168.1.1 port 67
[12:52:08.128]pbuf->len = 458
[12:52:08.129]pbuf->tot_len = 548
[12:52:08.130]searching DHCP_OPTION_MESSAGE_TYPE
[12:52:08.138]DHCP_OFFER received in DHCP_SELECTING state
[12:52:08.138]dhcp_handle_offer(netif=20003e7c) st0
[12:52:08.139]dhcp_handle_offer(): server 0x0101a8c0
[12:52:08.149]dhcp_handle_offer(): offer for 0x6601a8c0

根据设备的打印log,然后结合代码整理出DHCP Offer的时序图如下:

4.3 选择阶段(DHCP Request)

在选择阶段,如果有多台DHCP服务器向DHCP客户端回应DHCP-OFFER报文,则DHCP客户端只接受第一个收到的DHCP-OFFER报文。然后以广播方式发送DHCP-REQUEST请求报文,该报文中包含Option 54(服务器标识选项),即它选择的DHCP服务器的IP地址信息。设备的打印Log如下:

[12:52:08.150]dhcp_select(netif=20003e7c) st0
[12:52:08.150]transaction id xid(abcd0002)
[12:52:08.151]udp_send: added header in given pbuf 20003f34
[12:52:08.160]udp_send: sending datagram of length 316
[12:52:08.161]udp_send: UDP packet length 316
[12:52:08.161]udp_send: UDP checksum 0x0000
[12:52:08.171]udp_send: ip_output_if (,,,,IP_PROTO_UDP,)
[12:52:08.171]dhcp_select: REQUESTING
[12:52:08.172]dhcp_select(): set request timeout 2000 msecs

根据设备的打印log,然后结合代码整理出DHCP Request的时序图如下:

4.4确认阶段(DHCP Ack)

收到DHCP客户端发送的DHCP-REQUEST请求报文后,DHCP服务器根据DHCP-REQUEST报文中携带的MAC地址来查找有没有相应的租约记录。如果有,则发送DHCP-ACK报文作为应答,通知DHCP客户端可以使用分配的IP地址。设备的打印Log如下:

[12:52:08.173]udp_input: received datagram of length 556
[12:52:08.180]UDP header:
[12:52:08.181]+-------------------------------+
[12:52:08.181]|        67     |        68     | (src port, dest port)
[12:52:08.192]+-------------------------------+
[12:52:08.193]|       556     |     0x544f    | (len, chksum)
[12:52:08.194]+-------------------------------+
[12:52:08.203]udp (192.168.1.102, 68) <-- (192.168.1.1, 67)
[12:52:08.203]udp_input: calculating checksum
[12:52:08.204]dhcp_recv(pbuf = 200084f8) from DHCP server 192.168.1.1 port 67
[12:52:08.205]pbuf->len = 458
[12:52:08.214]pbuf->tot_len = 548
[12:52:08.215]searching DHCP_OPTION_MESSAGE_TYPE
[12:52:08.215]DHCP_ACK received

根据设备的打印log,然后结合代码整理出DHCP Ack的时序图如下:

5.dhcp_start函数分析

dhcp_start函数主要作用是:为网络接口启动DHCP协商,根据dhcp_start源码总结主要流程如下:

dhcp_start函数的源码如下:(备注:为方便分析主要流程,其中代码有删减)

err_t
dhcp_start(struct netif *netif)
{
    
    
	......
	
  /* check hwtype of the netif */
  if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
    
    
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
    return ERR_ARG;
  }

  /* check MTU of the netif */
  if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
    
    
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
    return ERR_MEM;
  }

  /* no DHCP client attached yet? */
  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
  dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));

  /* store this dhcp client in the netif */
  netif->dhcp = dhcp;
  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
    
  /* clear data structure */
  memset(dhcp, 0, sizeof(struct dhcp));
  /* dhcp_set_state(&dhcp, DHCP_OFF); */
  /* allocate UDP PCB */
  dhcp->pcb = udp_new();
  
  ip_set_option(dhcp->pcb, SOF_BROADCAST);
  /* set up local and remote port for the pcb */
  udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
  udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
  /* set up the recv callback and argument */
  udp_recv(dhcp->pcb, dhcp_recv, netif);
  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
  /* (re)start the DHCP negotiation */
  result = dhcp_discover(netif);

  /* Set the flag that says this netif is handled by DHCP. */
  netif->flags |= NETIF_FLAG_DHCP;
  return result;
}

6.dhcp_recv函数分析

dhcp_recv函数主要作用是:如果传入的DHCP消息是属于本设备的响应,则触发对应的状态机。根据dhcp_recv源码总结主要流程如下:

dhcp_recv函数的源码如下:(备注:为方便分析主要流程,其中代码有删减)

static void
dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
{
    
    
	......

  if (reply_msg->op != DHCP_BOOTREPLY) {
    
    
    goto free_pbuf_and_return;
  }
  /* iterate through hardware address and match against DHCP message */
  for (i = 0; i < netif->hwaddr_len; i++) {
    
    
    if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
    
    
      goto free_pbuf_and_return;
    }
  }
  /* match transaction ID against what we expected */
  if (ntohl(reply_msg->xid) != dhcp->xid) {
    
    
    goto free_pbuf_and_return;
  }
  /* option fields could be unfold? */
  if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
    
    
    goto free_pbuf_and_return;
  }

  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
  /* obtain pointer to DHCP message type */
  if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
    
    
    goto free_pbuf_and_return;
  }

  /* read DHCP message type */
  msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
  /* message type is DHCP ACK? */
  if (msg_type == DHCP_ACK) {
    
    
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
    /* in requesting state? */
    if (dhcp->state == DHCP_REQUESTING) {
    
    
      dhcp_handle_ack(netif);
	  ......
    }
	......
  }
  /* received a DHCP_NAK in appropriate state? */
  else if ((msg_type == DHCP_NAK) &&
    ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
     (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING  ))) {
    
    
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
    dhcp_handle_nak(netif);
  }
  /* received a DHCP_OFFER in DHCP_SELECTING state? */
  else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
    
    
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
    dhcp->request_timeout = 0;
    /* remember offered lease */
    dhcp_handle_offer(netif);
  }
  
free_pbuf_and_return:
  dhcp->msg_in = NULL;
  pbuf_free(p);
}

7.资料下载地址

移植成功的完整代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/12959157
资料里面主要是移植成功的完整代码,在2.stm32f429_lwip_basic_dhcp\Doc目录下有分析过程中所用到的资料,如下:

猜你喜欢

转载自blog.csdn.net/ZHONGCAI0901/article/details/109144153
今日推荐