ESP-IDF库之DHCP服务器代码分析

相关信息

ESP-IDF版本:V4.1
DHCP服务器相关文件路径:
在这里插入图片描述必要结构:
在这里插入图片描述在这里插入图片描述

  1. dhcps_msg:DHCP报文数据结构;
  2. list_node:每个Client申请的IP租赁信息链表节点,pnode指向实际的IP租赁信息结构、pnext指向下一个节点;
  3. dhcps_pool:实际保存的IP租赁信息结构,包括IP地址、MAC地址、以及lease_timer剩余租赁时间;
  4. dhcps_lease_t:保存DHCP可分配的IP地址范围,包括enable使能、start_ip起始地址、以及end_ip结束地址;

必要变量:
在这里插入图片描述存储模式:
  通常情况下,CPU在内存中存储模式为小端模式,而网络数据传输中采用大端模式,所以数据在传输之前,需要将内存中储存的数据由小端模式转换为大端模式,才能进行传输。

*请注意:在后面的代码分析中,小端模式被称作主机模式、大端模式被称作网络模式。

DHCP服务器启动

  系统调用void dhcps_start(struct netif *netif, ip4_addr_t ip)函数启动DHCP服务,netif表示使用的网卡设备,ip表示DHCP服务器IP,网络模式存储。该函数主要完成以下几个功能:

  1. 为DHCP服务器创建一个UDP对象,用于网络通信;
  2. 调用 dhcps_poll_set函数初始化IP租赁地址池,设置起始和结束IP地址;
  3. 初始化可租赁IP地址计数,client_address_plus.addr 设置为IP租赁地址池的起始地址;
  4. 调用lwip库函数udp_bind将DHCP服务器的UDP对象与DHCP服务器IP以及端口绑定;
  5. 调用lwip库函数udp_recv等待客户端的数据,数据处理由handle_dhcp函数实现;

IP租赁地址池初始化

  在DHCP服务器启动过程中,调用static void dhcps_poll_set(u32_t ip)函数对IP租赁地址池初始化,其主要初始化部分代码如下:

/*获取地址,ip为网络模式,local_ip和softap_ip为主机模式*/
local_ip = softap_ip = htonl(ip);
/*获取subnet信息,oftap_ip为主机模式*/
softap_ip &= 0xFFFFFF00;
/*获取内网编号,local_ip为主机模式*/
local_ip &= 0xFF;

/*下面的范围调整为何设置为128,目前不太清楚用意,有可能为自定义*/
/*若内网编号大于128,local_ip为主机模式*/
if (local_ip >= 0x80)
{
   
	/*调整内网编号,DHCPS_MAX_LEASE=0x64*/
	local_ip -= DHCPS_MAX_LEASE;
} 
else
{
   
	/*不大于128则+1用于起始地址*/
	local_ip ++;
}

/*初始化地址池结构*/
bzero(&dhcps_poll, sizeof(dhcps_poll));
/*设置起始地址为 域信息|新的域编号,dhcps_poll.start_ip.addr 为主机模式*/
dhcps_poll.start_ip.addr = softap_ip | local_ip;
/*设置结束地址为 与信息|新的域编号-最大分配数量-1,dhcps_poll.end_ip为主机模式*/
dhcps_poll.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1);
/*大小端转换*/ 
/*dhcps_poll.start_ip为网络模式*/
dhcps_poll.start_ip.addr = htonl(dhcps_poll.start_ip.addr);
/*dhcps_poll.end_ip为网络模式*/
dhcps_poll.end_ip.addr = htonl(dhcps_poll.end_ip.addr);

DHCP服务器数据接收与处理

  DHCP服务器启动后,通过UDP在端口67等待Client的请求报文,当lwip收到报文后,会交给static void handle_dhcp(void *arg,struct udp_pcb *pcb,struct pbuf *p,const ip_addr_t *addr,u16_t port)函数进行报文处理。该函数主要完成以下几个功能:

  1. 定义一个DHCP报文帧格式的对象,用于保存接收的报文struct dhcps_msg *pmsg_dhcps = NULL;
  2. 将数据从pbuf结构(lwip数据结构,详情参建lwip文档)拷贝到 pmsg_dhcps,并获取报文长度保存到tlen中;
  3. 调用parse_msg(pmsg_dhcps, tlen – 240)对报文进行解析,240见注解1。tlen-240表示options内数据的长度;
  4. 根据解析结果,若为OFFER状态则发送OFFER报文、若为ACK状态则发送ACK报文、NAK状态则发送NAK报文;
  5. 释放相关内存;

*注解1:240字节长度=
op(1)+htype(1)+hlen(1)+hops(1)+xid(4)+secs(2)+flags(2)+ciaddr(4)+yiaddr(4)+siaddr(4)+giaddr(4)+chaddr(16)+sname(64)+file(128)+magic_cookie(4);

报文解析

  DHCP报文解析过程由static s16_t parse_msg(struct dhcps_msg *m, u16_t len)实现。首先,系统检查options中包含的magic cookie字段是否为0x63538263,如果不满足,则丢弃这个报文。在协议规定中,如果DHCP报文中存在options,那么必须在options开头写入magic cookie标志。随后系统在dhcps_poll对象中选择一个可用的IP地址,dhcps_poll规定了Client可分配的IP地址范围。系统通过两个变量获取当前可用的IP,分别是first_address和client_address_plus,第一个变量保存最小的可用IP地址,第二个变量保存下一个可分配的IP地址。具体信息由下面的代码进行分析:

ip4_addr_t addr_tmp;

/*定义一个地址池对象*/
struct dhcps_pool *pdhcps_pool = NULL;
/*定义一个链表节点*/
list_node *pnode = NULL;
list_node *pback_node = NULL;
/*定义第一个可用ip地址*/
ip4_addr_t first_address;
bool flag = false;

/*获取ip地址池中的第一个ip地址,dhcps_poll.start_ip,first_address为网络模式*/
first_address.addr = dhcps_poll.start_ip.addr;
/*获取客户端当前可用的地址,client_address_plus以及client_address为网络模式*/
client_address.addr = client_address_plus.addr;
/*IP租赁更新标志*/
renew = false;

if (plist != NULL) 
{
   
	for (pback_node = plist; pback_node != NULL; pback_node = pback_node->pnext) 
	{
   
		pdhcps_pool = pback_node->pnode;
		/*检查当前Client的mac地址是否以前申请过IP租赁信息*/
		if (memcmp(pdhcps_pool->mac, m->chaddr
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值