lwIp源码解析–DHCPv4
一、简介
1.1 lwip版本
lwip 2.1.2
1.2 代码范围
在lwip中包含了DHCPv4客户端的实现,文件路径:
lwip-2.1.2\src\core\ipv4\dhcp.c
lwip-2.1.2\src\include\lwip\dhcp.h
文件代码两千行左右,阅读难度不大。
二、DHCPv4流程和状态机
没有找到电子版,所以在TCP/IP详解卷一中拍了对应照片。
dhcpv4交互流程如下:
dhcp消息头格式如下:
dhcp客户端状态变化图:
三、DHCPv4源码
3.1 启动客户端
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;
}
解析如下:
①入参struct netif *netif 为lwip网卡对应接口;
②NETIF_FLAG_UP未置起,退出,正常情况下netif只要初始化,就要置NETIF_FLAG_UP标志;
LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
③获取netif中的dhcp客户端成员,dhcp的一个指针。存在netif的client_data[]中;
dhcp = netif_dhcp_data(netif);
④ 若netif的MTU小于 DHCP_MAX_MSG_LEN_MIN_REQUIRED(576)字节,认为不支持DHCP协议,返回错误;
⑤如果dhcp == NULL,说明之前并没有申请过DHCP客户端成员,则重新为其mem_malloc() 申请结构体,并且根据索引LWIP_NETIF_CLIENT_DATA_INDEX_DHCP放在NETIF的client_data中;
⑥ 如果dhcp != NULL,说明已经申请过DHCP结构体了,再看下是否申请过UDP的PCB结构体,如果已经申请过了dhcp->pcb_allocated != 0, 则调用函数dhcp_dec_pcb_refcount() ,释放掉原来的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;
}
}
⑦调用dhcp_inc_pcb_refcount() 重新申请udp_pcb, 发送方式为广播,绑定本地客户端端口68, connect服务器端口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_