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. def语句和参数

    如果调用print()或len()函数,你会传入一些值,放在括号内,在这里成为“参数”.也可以自己定义接受参数的函数.在文件编辑器中输入这个例子: def hello(name): print('He ...

  2. LeetCode 100——相同的树

    1. 题目 2. 解答 针对两棵树的根节点,有下列四种情况: p 和 q 都为空,两棵树相同: p 不为空 q 为空,两棵树不相同: p 为空 q 不为空,两棵树不相同: p 和 q 都不为空,如果两 ...

  3. opencv-学习笔记(2)

    opencv-学习笔记(2) 这章记录了 获取像素点,改变像素点 获取图像的属性(行,列,通道数,数据类型) roi感应区 拆分以及合并图像通道 边缘扩充 opencv获取像素点,改变像素点 ---- ...

  4. Python3 Tkinter-Menu

    1.创建 from tkinter import * root=Tk() menubar=Menu(root) def hello(): print('Hello Menu!') for item i ...

  5. lintcode-138-子数组之和

    138-子数组之和 给定一个整数数组,找到和为零的子数组.你的代码应该返回满足要求的子数组的起始位置和结束位置 注意事项 There is at least one subarray that it' ...

  6. 分布式系统理论-terms

    Distributed programming is the art of solving the same problem that you can solve on a single comput ...

  7. Linux文件传输FTP详解

    ftp命令用来设置文件系统相关功能.ftp服务器在网上较为常见,Linux ftp命令的功能是用命令的方式来控制在本地机和远程机之间传送文件,这里详细介绍Linux ftp命令的一些经常使用的命令,相 ...

  8. Jenkins系列-Jenkins构建触发器

    触发器说明 build whenever a snapshot dependency is built,当job依赖的快照版本被build时,执行本job. 触发远程构建 (例如,使用脚本):这里使用 ...

  9. python 爬虫每天定时启动爬虫任务

     # coding=utf-8 import datetime import time def doSth(): # 这里是执行爬虫的main程序     print '爬虫要开始运转了....'   ...

  10. MySQL event调度

    基本命令 //查看事件调度是否开启 show variables like '%event_scheduler%'; //开启事件调度 SET GLOBAL event_scheduler = ON; ...