lwip source code analysis -- DHCPv4

Keywords: TCPIP dhcp

lwip source code analysis - DHCPv4

1, Introduction
  1.1 lwip version
   lwip 2.1.2

  1.2 code range
  the implementation of DHCPv4 client is included in lwip. File path:

    lwip-2.1.2\src\core\ipv4\dhcp.c   
    lwip-2.1.2\src\include\lwip\dhcp.h

  the file code is about 2000 lines, which is not difficult to read.

2, DHCPv4 process and state machine

3, DHCPv4 source code
  3.1 start the client

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

  LWIP_ASSERT_CORE_LOCKED();
  LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
  LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
  dhcp = netif_dhcp_data(netif);
  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));

  /* 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? */
  if (dhcp == NULL) {
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): mallocing new DHCP client\n"));
    dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
    if (dhcp == NULL) {
      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
      return ERR_MEM;
    }

    /* store this dhcp client in the netif */
    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
    /* already has DHCP client attached */
  } else {
    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));

    if (dhcp->pcb_allocated != 0) {
      dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
    }
    /* dhcp is cleared below, no need to reset flag*/
  }

  /* clear data structure */
  memset(dhcp, 0, sizeof(struct dhcp));
  /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */

  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));

  if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
    return ERR_MEM;
  }
  dhcp->pcb_allocated = 1;

  if (!netif_is_link_up(netif)) {
    /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
    dhcp_set_state(dhcp, DHCP_STATE_INIT);
    return ERR_OK;
  }

  /* (re)start the DHCP negotiation */
  result = dhcp_discover(netif);
  if (result != ERR_OK) {
    /* free resources allocated above */
    dhcp_release_and_stop(netif);
    return ERR_MEM;
  }
  return result;
}

  the analysis is as follows:
   ① the input parameter struct netif *netif is the corresponding interface of lwip network card;

   ②NETIF_ FLAG_ If up is not set, exit. Normally, if netif is initialized, it will be set to NETIF_FLAG_UP flag;

    LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);

   ③ obtain the dhcp client member in netif and a pointer to dhcp. Client with netif_ In data [];

     dhcp = netif_dhcp_data(netif); 

   ④ if MTU of netif is less than DHCP_MAX_MSG_LEN_MIN_REQUIRED (576) bytes, it is considered that DHCP protocol is not supported, and an error is returned;

   ⑤ if dhcp == NULL, it indicates that no DHCP client member has been applied for before, and then re set its mem_malloc() applies for the structure and LwIP according to the index_ NETIF_ client_data_ INDEX_ DHCP is placed in the client of netif_ Data;

   ⑥ if DHCP= NULL, indicating that DHCP structure has been applied. Check whether UDP PCB structure has been applied. If DHCP - > PCB has been applied_ allocated != 0, the DHCP function is called_ dec_ pcb_ Refcount(), releasing the original udp_pcb;

static void
dhcp_dec_pcb_refcount(void)
{
  LWIP_ASSERT("dhcp_pcb_refcount(): refcount error", (dhcp_pcb_refcount > 0));
  dhcp_pcb_refcount--;

  if (dhcp_pcb_refcount == 0) {
    udp_remove(dhcp_pcb);
    dhcp_pcb = NULL;
  }
}

⑦ call dhcp_inc_pcb_refcount() reapply for udp_pcb, the transmission mode is broadcast, bind local client port 68 and connect server port 67.

static err_t
dhcp_inc_pcb_refcount(void)
{
  if (dhcp_pcb_refcount == 0) {
    LWIP_ASSERT("dhcp_inc_pcb_refcount(): memory leak", dhcp_pcb == NULL);

    /* allocate UDP PCB */
    dhcp_pcb = udp_new();

    if (dhcp_pcb == NULL) {
      return ERR_MEM;
    }

    ip_set_option(dhcp_pcb, SOF_BROADCAST);

    /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
    udp_bind(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_CLIENT);
    udp_connect(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_SERVER);
    udp_recv(dhcp_pcb, dhcp_recv, NULL);
  }

  dhcp_pcb_refcount++;

  return ERR_OK;
}

   ⑧ dhcp_recv() is UDP_ Accept callback function of recv(). After the client sends DHCP data, the udp_recv processes the reply from the server, and then sets the flag DHCP - > PCB_ allocated = 1.

   ⑨ if there is no data on the network at that time! netif_is_link_up(netif), set the DHCP state machine to DHCP_STATE_INIT, from network to link_ DHCP when up_ network_ Rediscover in changed() processing;

  if (!netif_is_link_up(netif)) {
    /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
    dhcp_set_state(dhcp, DHCP_STATE_INIT);
    return ERR_OK;
  }

⑩ conversely, if the network card is linked_ Up indicates that there is a network status at that time. Send DISCOVER message and broadcast to find DHCP server; If the sending fails, dhcp_release_and_stop() sends a release message to release udp_pcb;

  /* (re)start the DHCP negotiation */
  result = dhcp_discover(netif);
  if (result != ERR_OK) {
    /* free resources allocated above */
    dhcp_release_and_stop(netif);
    return ERR_MEM;
  }

  3.2 client sends DISCOVER
static err_t
dhcp_discover(struct netif *netif);
Get DHCP CLIENT object from NETIF. struct dhcp *dhcp = netif_dhcp_data(netif);
DHCP - > offered_ ip_ Set addr to 0.0.0.0. This variable is also the IP that SERVER will provide.
Set DHCP state machine to DHCP_STATE_SELECTING indicates entering DHCP after sending_ STATE_ In the selecting status, select one of the replies after you are ready to accept OFFER.

apply DHCP Message structure, dhcp_create_msg,Fill in the relevant information after the application is successful OPTIONS Send after field,
among dhcp_create_msg,Function, that is, according to DHCP Message headers and OPTIONS Fill in the content.
xid It is a random number. The server needs to backfill this number XID. OP Yes 1.Note to fill in chaddr Fill in local mac Address, cookie Is a magic word, fixed is 0 x63825363 .After that, keep up OPTIONS. Then fill in the first one OPTIONS, 35 1 1 Indicates that the message type, one byte, is DISCOVER. 
Fill in other OPTIONS And so on. dhcp_discover_request_options Indicates the information requested by the server. The corresponding type is 55.
     udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);

Finally, send, where the destination address is broadcast, the port is 67, and the local address is IP4_ADDR_ANY, 0.0.0.0, so it has not been assigned to an IP address at this time.

3.3 processing reply
static void
dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)

 stay dhcp_recv Processing messages sent by the server in OFFER Message or ACK\NACK Message, depending on the message type.
 
 Only processing OP yes REPLY The news, so OP Also fill in the right. matching chaddr and XID,Need and DISCOVER agreement.

 dhcp_parse_reply according to TLV Structural analysis OPTIONS, Where options are placed dhcp_rx_options_given, dhcp_rx_options_val The contents of the options are stored in an array.

Index is dhcp_option_idx. Description: the supported OPTIONS contents are 4 bytes.
msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
DHCP is processed sequentially according to the message type_ ACK, DHCP_NAK, DHCP_OFFER.
DHCP_ACK:

   If current is REQUESTING, Description is send request Later replies, dhcp_handle_ack, Determined according to the content of the reply DHCP The relevant parameter information of is saved in DHCP In, including T,T1,T2,NTP perhaps DNS Wait./

Gateway, etc.
If ARP is supported, send ARP and DHCP according to the provided IP address_ Check to see if this IP address is unique on the network. If ARP is not supported, the IP and other information will be used directly, and the status will be set to dhcp_set_state(dhcp, DHCP_STATE_BOUND);
dhcp_ In check, set the state machine to DHCP_STATE_CHECKING.
Send the ARP query frame according to the IP of the server OFFER.
result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
Set certain DHCP - > request_ Timeout will gradually decrease > request in the timer_ Timeout, when > request_ Callback when timeout = = 1. dhcp_timeout(netif); It is processed here. DHCP - > tries < = 1 is retransmitted once, but it is still not returned. It is directly bound to the network card.
dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr) register with ARP_ In the reply callback function, if it is my IP reply, send a decline request.

If there is no reply within the specified time, this IP is unique. If else if ((DHCP - > state = = dhcp_state_rebooting) | (DHCP - > state = = dhcp_state_rebinding)||
(DHCP - > state = = dhcp_state_renewing) these three states are not the first application states, indicating that ARP verification has been performed previously. ARP phase is not required.

 netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr); yes NETIF set up IP,Mask and gateway information. The network card was bound successfully.

DHCP_STATE_REQUESTING Set status, request message reply REQUEST package DHCP_REQUEST. Fill in server IP And client IP And other information, notify all DHCP Server, "I have selected the server and my IP The broadcast was sent out.

  
 If the answer is DHCP_NAK, Description the request was rejected by the server. It may be request IP The server cannot provide. Called at this time. dhcp_handle_nak Set status in DHCP_STATE_BACKING_OFF. Local IP Set to 0.0.0.0. retransmission DISCOVER. 
 
If match OFFER Package,((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_STATE_SELECTING))

dhcp_handle_offer performs OFFER packet processing.

Posted by kidbrax on Wed, 13 Oct 2021 10:24:32 -0700