1:重要的结构体

  全局链表的成员struct dhcpOfferedAddr *leases 记录了当前租赁出去的IP信息

/* leases.h */

struct dhcpOfferedAddr {
u_int8_t chaddr[];
u_int32_t yiaddr; /* network order */
u_int32_t expires; /* host order */
};

  结构体三个成员分别记录客户端MAC(为什么不是6字节?),租赁出去的IP地址,以及到期时间(time(0) + server_config.lease).

2:读入lease_file

/* dhcpd.c */

leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
memset(leases, , sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
read_leases(server_config.lease_file);

  根据配置文件中的max_leases参数分配空间,read_leases读入lease_file文件,将记录的IP租赁信息更新到leases链表中,有了leases链表还要有lease_file文件的原因是一旦udhcpd异常挂掉,重启之后能够恢复之前的租赁IP信息到leases链表里.

/* files.c */

/*
* 将lease_file文件中记录的租赁出去的IP信息更新到链表struct dhcpOfferedAddr *leases中
* read_leases函数主要是在udhcpd意外重启后恢复之前租赁出去IP的信息到struct dhcpOfferedAddr *leases中
* add_lease是具体更新struct dhcpOfferedAddr *leases链表的函数,比如在server发送offer报文后应该将租赁
出去的IP记录到链表中,这时候会调用add_lease函数
*/
void read_leases(char *file)
{
FILE *fp;
unsigned int i = ;
struct dhcpOfferedAddr lease; if (!(fp = fopen(file, "r"))) {
LOG(LOG_ERR, "Unable to open %s for reading", file);
return;
} while (i < server_config.max_leases && (fread(&lease, sizeof lease, , fp) == )) {
/* ADDME: is it a static lease */
if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
lease.expires = ntohl(lease.expires);
if (!server_config.remaining) lease.expires -= time();
if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
LOG(LOG_WARNING, "Too many leases while loading %s\n", file);
break;
}
i++;
}
}
DEBUG(LOG_INFO, "Read %d leases", i);
fclose(fp);
}

  与其方向相反的,在程序适当时候需要将leases链表中的信息更新到lease_file文件中就调用write_lease函数

/*
通过遍历struct dhcpOfferedAddr *leases指向的链表更新lease_file文件内容,
server_config.remaining 为真表示lease_file文件中存储的过期时间是绝对时间
(time(0) + expires)否则存储相对时间(expires)
*/
void write_leases(void)
{
FILE *fp;
unsigned int i;
char buf[];
time_t curr = time();
unsigned long lease_time; if (!(fp = fopen(server_config.lease_file, "w"))) {
LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file);
return;
} for (i = ; i < server_config.max_leases; i++) {
if (leases[i].yiaddr != ) {
if (server_config.remaining) {
if (lease_expired(&(leases[i])))//如果地址过期设置为0
lease_time = ;
else lease_time = leases[i].expires - curr;
} else lease_time = leases[i].expires;
lease_time = htonl(lease_time);
fwrite(leases[i].chaddr, , , fp);
fwrite(&(leases[i].yiaddr), , , fp);
fwrite(&lease_time, , , fp);
}
}
fclose(fp); if (server_config.notify_file) {
sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file);
system(buf);
}
}

  有关leases链表的操作函数都在leases.c文件中,主要有下面这些:

/* leases.c */

1: struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease)

/*
add a lease into the table, clearing out any old ones
先清空chaddr,yiaddr对应的链表节点,找到最早到期的节点,将新节点赋值到最早到期的节点位置
如果没有到期的IP则返回NULL
*/
struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease)
{
struct dhcpOfferedAddr *oldest; /* clean out any old ones */
clear_lease(chaddr, yiaddr); oldest = oldest_expired_lease(); if (oldest) {
memcpy(oldest->chaddr, chaddr, );
oldest->yiaddr = yiaddr;
oldest->expires = time() + lease;
} return oldest;
}

2: void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr)

/*
clear every lease out that chaddr OR yiaddr matches and is nonzero
遍历leases链表找到chaddr或yiaddr对应的节点,将节点置0.
*/
void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr)
{
unsigned int i, j; for (j = ; j < && !chaddr[j]; j++);
/* j==16 表示chaddr数组为空,只需要比较yiaddr(小技巧) */ for (i = ; i < server_config.max_leases; i++)
if ((j != && !memcmp(leases[i].chaddr, chaddr, )) ||
(yiaddr && leases[i].yiaddr == yiaddr)) {
memset(&(leases[i]), , sizeof(struct dhcpOfferedAddr));
}
}

3: struct dhcpOfferedAddr *oldest_expired_lease(void)

/*
Find the oldest expired lease, NULL if there are no expired leases
找到leases链表中最早到期的节点,返回节点地址.没有到期节点返回NULL
*/
struct dhcpOfferedAddr *oldest_expired_lease(void)
{
struct dhcpOfferedAddr *oldest = NULL;
unsigned long oldest_lease = time();
unsigned int i; for (i = ; i < server_config.max_leases; i++)
if (oldest_lease > leases[i].expires) {
oldest_lease = leases[i].expires;
oldest = &(leases[i]);
}
return oldest; }

4: int lease_expired(struct dhcpOfferedAddr *lease)

/*
true if a lease has expired
IP租赁到期返回true否则返回false
*/
int lease_expired(struct dhcpOfferedAddr *lease)
{
return (lease->expires < (unsigned long) time());
}

5: struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr)

/*
Find the first lease that matches chaddr, NULL if no match
通过chaddr值找到leases中节点,返回节点地址.
*/
struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr)
{
unsigned int i; for (i = ; i < server_config.max_leases; i++)
if (!memcmp(leases[i].chaddr, chaddr, )) return &(leases[i]); return NULL;
}

6: struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr)

/*
Find the first lease that matches yiaddr, NULL is no match
通过yiaddr值找到leases中节点,返回节点地址.
*/
struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr)
{
unsigned int i; for (i = ; i < server_config.max_leases; i++)
if (leases[i].yiaddr == yiaddr) return &(leases[i]); return NULL;
}

7: u_int32_t find_address(int check_expired)

/* find an assignable address, it check_expired is true, we check all the expired leases as well.
* Maybe this should try expired leases by age...
* 在地址池中返回一个没有被分配的地址.
*/
u_int32_t find_address(int check_expired)
{
u_int32_t addr, ret;
struct dhcpOfferedAddr *lease = NULL; addr = ntohl(server_config.start); /* addr is in host order here */
for (;addr <= ntohl(server_config.end); addr++) { /* 排除地址池中.0和.255结尾的地址 */
/* ie, 192.168.55.0 */
if (!(addr & 0xFF)) continue; /* ie, 192.168.55.255 */
if ((addr & 0xFF) == 0xFF) continue; /* lease is not taken */
ret = htonl(addr);
if ((!(lease = find_lease_by_yiaddr(ret)) || /* or it expired and we are checking for expired leases */
(check_expired && lease_expired(lease))) && /* and it isn't on the network */
!check_ip(ret)) {
return ret;
break;
}
}
return ;
}

8: int check_ip(u_int32_t addr)

/*
check is an IP is taken, if it is, add it to the lease table
检测此地址是否有在被其它lan pc所使用,检测的方式是用此IP广播arp报文,
根据是否有回应判断此IP是否被占用.(比如某个lan pc 是使用的static IP,
且此IP在udhcpd的地址池中,在udhcpd分配ip给客户端时必须做此检查(检查有就要将此IP添加到leases链表中表示已被分配),
否则会造成IP冲突).
*/
int check_ip(u_int32_t addr)
{
struct in_addr temp;
/* arpping 发送一个arp广播包,经过一段时间等待后如果此IP没有被局域网内的主机使用就收不到单播回复,返回1 */ 
if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == ) {
temp.s_addr = addr;
LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds",
inet_ntoa(temp), server_config.conflict_time);
     /* blank_chaddr 黑户? 因为不知道使用这个IP的主机的MAC地址 */
add_lease(blank_chaddr, addr, server_config.conflict_time);
return ;
} else return ;
}

 3:总结

  所有被租赁出去的IP地址,客户端MAC和到期时间(绝对时间-->leases链表中的到期时间都是绝对时间,而保存到lease_file中的到期时间有两种,绝对时间和相对时间<配置文件中remaining为no则和leases链表一致保存绝对时间,为yes则保存相对时间>),维护在两个地方,一个当然就是struct dhcpOfferedAddr *leases指针指向的全局链表,另一个就是配置文件指定的lease_file本地文件.本地文件主要是为了防止udhcpd异常重启后租赁信息的丢失而设置的,在files.c文件中的read_leases函数就是读取lease_file文件中的记录通过add_lease来恢复之前的被租赁出去的IP信息.而write_leases函数的方向刚好相反,它是将leases链表中的信息更新到lease_file文件中做记录.

  add_lease是更新struct dhcpOfferedAddr *leases链表的触发者,所以租赁IP的更新主要靠add_lease函数,add_lease只在sendACK和sendOffer中被调用,也就是说有新的客户端来连接就会更新一下链表(将旧的信息替换为新的信息)。而write_leases函数则会在server收到SIGUSER1信号或者socket空闲时不停将struct dhcpOfferedAddr *leases链表信息记录到lease_file中。

  

udhcpd源码分析3--IP租赁管理的更多相关文章

  1. 鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存 | 百篇博客分析OpenHarmony源码 | v17.01

    百篇博客系列篇.本篇为: v17.xx 鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...

  2. 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么 | 百篇博客分析OpenHarmony源码 | v16.02

    百篇博客系列篇.本篇为: v16.xx 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪 ...

  3. dubbo源码分析6-telnet方式的管理实现

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  4. udhcpd源码分析2--读取配置文件

    1:重要的结构体 读取配置文件信息到全局的结构体struct server_config_t server_config中,这个结构在很多文件中都有引用到很重要. /* dhcpd.h */ stru ...

  5. udhcpd源码分析4--获取client报文及发包动作

    1:重要的结构体 获取的报文是UDP的payload部分,结构体struct dhcpMessage描述了dhcp报文的结构. /* packet.h */ struct dhcpMessage { ...

  6. [Abp 源码分析]八、缓存管理

    0.简介 缓存在一个业务系统中十分重要,常用的场景就是用来储存调用频率较高的数据.Abp 也提供了一套缓存机制供用户使用,在使用 Abp 框架的时候可以通过注入 ICacheManager 来新建/设 ...

  7. dubbo源码分析1-reference bean创建

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  8. dubbo源码分析2-reference bean发起服务方法调用

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  9. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

随机推荐

  1. 1. 两数之和【Leetcode中国,by java】

    给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], target ...

  2. OpenMPI 集群配置

    现在有2台机器,希望可以尝试一下在多台机器上跑MPI的感觉,所以跑之前就得配置,先参考网址: https://www.cnblogs.com/awy-blog/p/3402949.html: 1. 配 ...

  3. 【zabbix 监控】第二章 安装测试被监控主机

    客户端安装测试 一.准备两台被监控主机,分别做如下操作: web129:192.168.19.129 web130:192.168.19.130 [root@web129 ~]#yum -y inst ...

  4. [模板]非递归线段树(zkw的变异版本)

    类似于zkw,但空间只用两倍,zkw要4倍. 链接 可以下传标记,打熟后很好码. #include <set> #include <cmath> #include <cs ...

  5. priority_queue(优先队列):排序不去重

    C++优先队列类似队列,但是在这个数据结构中的元素按照一定的断言排列有序. 头文件:#include<queue> 参数:priority_queue<Type, Container ...

  6. python 读取 log日志的编码问题

    1.我要读取log日志的”执行成功”的个数,log日志编码格式为GBK 2.显示报错,大致意思是说utf-8的代码不能解析log日志 3.后来想想把log日志用GBK编码读出来,写到新文件中,用utf ...

  7. matlab中设置colorbar为几种规定颜色

    我们可以通过修改colormap的值来达到这种目的. 一般来说colormap的值是64*3的矩阵,64代表64种颜色,3列是这种颜色的RGB值,不过归一化了. 如果你想将colorbar颜色设成6种 ...

  8. 个人在git配置SSH Key遇到的问题以及解决方案

    第一次用git上传代码到github,在这过程中遇到很多问题,在输入git命令的时候都小心翼翼,因为一不小心感觉就会出错.. 英语不好..在敲入git命令过程中各种错误提示勉强翻译下才看得懂 最后输入 ...

  9. lintcode-162-矩阵归零

    162-矩阵归零 给定一个m×n矩阵,如果一个元素是0,则将其所在行和列全部元素变成0. 需要在原矩阵上完成操作. 样例 给出一个矩阵 [ [1, 2], [0, 3] ] 返回 [ [0, 2], ...

  10. CentOS基础命令

    为网卡配置静态IP地址建议通过交互式界面nmtui进行配置 firewalld和iptablesCentOS7使用firewald取代原来的iptables,但实际上底层还是iptables,在上层加 ...