udhcpd源码分析3--IP租赁管理
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租赁管理的更多相关文章
- 鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存 | 百篇博客分析OpenHarmony源码 | v17.01
百篇博客系列篇.本篇为: v17.xx 鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...
- 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么 | 百篇博客分析OpenHarmony源码 | v16.02
百篇博客系列篇.本篇为: v16.xx 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪 ...
- dubbo源码分析6-telnet方式的管理实现
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- udhcpd源码分析2--读取配置文件
1:重要的结构体 读取配置文件信息到全局的结构体struct server_config_t server_config中,这个结构在很多文件中都有引用到很重要. /* dhcpd.h */ stru ...
- udhcpd源码分析4--获取client报文及发包动作
1:重要的结构体 获取的报文是UDP的payload部分,结构体struct dhcpMessage描述了dhcp报文的结构. /* packet.h */ struct dhcpMessage { ...
- [Abp 源码分析]八、缓存管理
0.简介 缓存在一个业务系统中十分重要,常用的场景就是用来储存调用频率较高的数据.Abp 也提供了一套缓存机制供用户使用,在使用 Abp 框架的时候可以通过注入 ICacheManager 来新建/设 ...
- dubbo源码分析1-reference bean创建
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- dubbo源码分析2-reference bean发起服务方法调用
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- dubbo源码分析3-service bean的创建与发布
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
随机推荐
- leetcode-最长上升子序列LIS
转载原文地址:http://www.cnblogs.com/GodA/p/5180560.html 给定一个无序的整数数组,找到其中最长上升子序列的长度. 示例: 输入: [10,9,2,5,3,7, ...
- java 流 文件 IO
Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基本类 ...
- pthon web框架flask(二)--快速入门
快速入门 迫切希望上手?本文提供了一个很好的 Flask 介绍.假设你已经安装 Flask, 如果还没有安装话,请浏览下 安装 . 一个最小的应用 一个最小的应用看起来像这样: from flask ...
- Python对文本文件逐行扫描,将含有关键字的行存放到另一文件
#逐行统计关键字行数,并将关键字所在行存放在新的文件中 keyword = "INFO" b = open("C:\\Users\\xxx\\Documents\\new ...
- POJ 2540 Hotter Colder(半平面交)
Description The children's game Hotter Colder is played as follows. Player A leaves the room while p ...
- 关于LNMP常见问题和性能方面的个人理解
简单整理,自己做备忘的,不为其他作任何参考- PHP程序 1.开启慢日志,过滤超时时间为1s的方法,针对性优化,可以通过添加缓存方式解决. 2.过滤access日志,统计哪些请求较多较为频繁,是否合理 ...
- [贪心经典算法]Kruskal算法
Kruskal算法的高效实现需要一种称作并查集的结构.我们在这里不介绍并查集,只介绍Kruskal算法的基本思想和证明,实现留在以后讨论. Kruskal算法的过程: (1) 将全部边按照权值由小到大 ...
- lintcode-172-删除元素
172-删除元素 给定一个数组和一个值,在原地删除与值相同的数字,返回新数组的长度. 元素的顺序可以改变,并且对新的数组不会有影响. 样例 给出一个数组 [0,4,4,0,0,2,4,4],和值 4 ...
- lintcode-15-全排列
全排列 给定一个数字列表,返回其所有可能的排列. 注意事项 你可以假设没有重复数字. 样例 给出一个列表[1,2,3],其全排列为: [ [1,2,3], [1,3,2], [2,1,3], [2,3 ...
- pythoh使用 xpath去除空格空格
html_str = """ <!DOCTYPE html> <html lang="en"> <head> &l ...