udhcp源码详解 - 图文

更新时间:2023-12-05 14:38:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

udhcp源码详解(一)之文件组织结构(dhcp server) --转

udhcp目录下有十几个源文件,一个源文件相对应一个模块,完成一系列相关的功能,例如在static_leases.c主要针对static_lease链表增删查找等操作。

dhcpd.c—— 整个dhcp server运行的主线,server开始运行是从udhcpd_main函数开 | 始,相当于我们程序的main入口。在dhcpd_main将各个功能模块组合起 | 来实现我们的dhcp server。 |

arpping.c——这个文件里只有一个函数arpping,server在给客户机分配IP地址 | 从IP地址池里找到a Free IP后,会调用arpping函数看网络上是否 | 主机使用该地址。 |

common.c — 引用文件的注释:Functions for debugging and logging as well as some

| other simple helper functions. |

file.c —— 文件名可以看出她是跟文件打交道的。file.c里有个很重要的函数 | read_config,读取默认配置信息和从配置文件udhcpd.conf文件里读 | 取配置信息,从网上Down个udhcpd.conf,她和Linux下其他配置文 | 件格式一样,以后写这类函数时候可以模仿着写。 |

leases.c—— 针对在dhcpd.h文件里声明的struct dhcpOfferedAddr结构的操作函 | 数。 |

option.c—— 在dhcp交互的数据报文中有个选项字段options,该文件里的函数操作 | 对象也就是选项信息。 |

serverpacket.c 数据报文发送之前,根据不同情况对各个字段的填充相应的信息。 | | | |

| packet.c—— dhcp报文的发送信息封装与向网络发送。 |

pidfile.c—— Functions to assist in the writing and removing of pidfiles. | 主要针对 /var/run/udhcpd.pid |

signalpipe.c— server会监听两个文件描述符,其中一个是”管道”描述符,该描述符 | 的相应操作就在signalpipe.c文件里,另外还有对signal的设置…… |

socket.c—— socket套接字创建,interface信息读取。 |

static_leases.c 对在dhcpd.h里定义的struct static_lease结构体的相应操作函数。

相对应得头文件是对函数声明,以及结构体的定义和宏的定义。

udhcp详解源码(序)

最近负责接入模块,包括dhcp、ipoe和pppoe等等。所以需要对dhcp和ppp这几个

app的源代码进行一些分析。网上有比较好的文章,参考并补充自己的分析。

这篇udhcp详解是基于busybox-1.20里的 …/busybox-1.20/networking/udhcp/目录下的源码进行讲解与分析,主要是对udhcp server端源码进行的讲解,client端相对于server端相对简单一些,源代码上面表现的实现思想大体相似。

该系列的讲解分析是在源代码的基础上,所以并没有打算以单独章节来讲解DHCP协议工作交互流程,也就是已经了解掌握了DHCP协议,用C来实现而已。

udhcp源码详解(二)--转

定义的数据结构对于C程序的重要性,不言而喻。面向对象设计的程序是一个个对象的集合,而面向过程语言设计的程序则是数据结构与算法的集合。 下面来分析的是dhcp server中的定义结构体: 1)、在packet.h文件里定义了DHCP报文的格式:

struct dhcpMessage报文的字段虽然都有注释,但还是有必要讲下options字段。

options在大多文档中的说法是可选字段,大小不定,对于这个字段的重要性没有太多的强调。其实在DHCP交互过程,客户得到IP的配置过程,这个options字段有着很重要的作用,传递个很多不可或缺的信息。

例如Server与Client交互时,数据包的类型,是通过该字段的指示的。还有Client要成功连接到互联网,不只是需要IP,还需要其他的配置信息,如Dns、Router、Subnet等信息,

Server就是通过options字段把这些信息传递给Client。(查看options支持哪些选项信息可以查看文档RFC2132) 问题来啦!!

这么多信息都放到一个字段,怎么合理的组织在一起呢,怎么能让交互双方准确的从这个字段取到相应的信息呢?

options字段才用“CLV“方式组织数据信息,OPT_CODE:标识号,唯一标识后面的信息内容;占1byte;OPT_LEN:长度,表示后面信息内容的长度,占1byte;value(OPT_DATA):信息内容,其长度由OPT_LEN所指定,以byte为单位(RFC2132文档给出所有支持选项的OPT_CODE,和可以确定长度的OPT_LEN的值) CLV 的数据组织方式: 0 1 2 +Length Code Length

DATA

这是一种很漂亮的把多种数据信息组织在一个字段的方式,后面会看到对options字段相应的操作函数,这些函数就是根据CLV的方式对数据进行提取或者组织的。 另外options字段存储的信息分为三大类:

① DHCP_PADDING 填充字节 //读取的信息时候注意跳过 ② DHCP_END 结束标志 //标志options字段的结束 ③ CLV组织的有价值信息 //real value for us

options字段还有个让人纠结的情况——选项过载,其实也没什么,在后面遇再说吧! 2)、在dhcpd.h里定义里一个贯穿整个Server端程序的结构体struct server_config_t

上面的注释是源文件上的,本来的打算翻译下的,看了下注释很直白,没什么好翻译的,只是讲下其中的专业术语(好像是这么说的)。

network order 网络字节序, host order 主机字节序 相信大家了解她们的区别吧。 (定义类型是 uint32_t … ,说明变量是以network order存储的 常用数据类型 int long … ,说明变量是以host order存储的) 下面讲解下其中一些重要的成员:

① start,end可分配地址空间,每个客户的请求获得的IP都在这个内。IP地址池。 ② struct option_set结构体的定义也在dhcpd.h里

Option set翻译过来:选项集合,就是的该结构的意义。上面分析struct dhcpMessage报文里有个选项字段options,她的值就是根据该集合(Option List)填写赋值的。 集合(Option List)里每个结点是一个选项信息,数据CLV的组织方式的。

与报文中options字段的区别是,报文里用一个options字段存储了所有的选项信息,options_set而是把一个一个的选项信息用链表链接起来。

③ 下面的几个是与租赁期限有关的成员变量

unsigned long lease client请求的租赁期限最大值,>lease,就以lease为租赁期限;client请求未指明租赁期限,lease作为其租赁期限(静态租赁的默认租期)。

unsigned long decline_time server的DHCPOFFER报文提供一个IP给client,client检测IP已经被其他主机使用,发送DHCPDECLINE报文给server,server接到该报文后,把IP添加到动态租赁数组里,租赁租期就是decline_time,对应的MAC为blank_chaddr(黑户,很形象^-^,实际值是全0)

unsigned long confict_time server在IP地址池找个a free IP时,检测到IP已被网络中的主机所使用,会把IP添加到动态租赁数组里,租赁期限就是confict_time,MAC:blank_chaddr。 unsigned long offer_time server发送DHCPOFFER报文时,即向client提供了IP地址,

server会把IP和对应得MAC添加的动态租赁表里,但这个IP不一定会被client使用,所以添加到动态租赁表里的租赁期限要短,offer_time就是这个租赁期限(default : 60s)。(当server接收到DHCPREQUEST的时候会把租赁期限修改成请求的租赁期限)。 unsigned long min_lease client端的请求租赁期限不能小于min_lease。 ④ 与保存租赁信息有关的两个成员变量:

char remaining 摘自dhcpd.conf里的注释(以及翻译和注解): # If remaining is true (default), udhcpd will store the time # remaining for each lease in the udhcpd leases file. This is # for embedded systems that cannot keep time between reboots. # If you set remaining to no, the absolute time that the lease # expires at will be stored in the dhcpd.leases file.

# 如果剩下的就是true(默认),udhcpd将存储时间文件中 # 的每个udhcpd租赁租赁剩余。

# 这对于嵌入式系统,不能保持在重新启动的时间。 # (即重新启动就不算入租赁时间里) # 如果您设置其余为NO,绝对时间,

# 租赁期满时将被储存在dhcpd.leases文件档案。 # 绝对时间,例: starts 0 2000/01/30 08:02:54; # ends 5 2000/02/04 08:02:54; # 而嵌入式存储的是租赁剩余时间 # 即 leases[i].expires - time(0) 的值

unsigne long auto_time how long should udhcpd wait before writing a config file. if this is zero, it will only write one on SIGUSR1

多长时间把动态租赁表里的信息写入文件(dhcpd.leases)里。 Auto_time = 0的,只有等到SIGUSR1信号的时候才写。 ⑤ struct static_lease *static_lease /* dhcpd.h */ struct static_lease{

uint8_t *mac; uint32_t *ip;

struct static_lease *next; };

因为DHCP允许手动为client端配置IP,server端管理这些手动配置的IP就是使用该结构。 在dhcpd.c文件里声明定义一个struct server_config_t的全局变量server_config,server对于client的响应交互都必须有这个变量的参与。

3)、server端对于租赁出去的IP的管理基于以下这个结构体:

uint8_t chaddr[16]; 客户机的 MAC地址; uint32_t yiaddr; 客户机租赁的IP地址; uint32_t expires; 客户机租赁IP的到期时间

(是未来的一个时间点,是从1970.1.1午夜开始到租赁到期时刻的秒数)

这里有些奇怪,使用uint32_t声明的expires存储方式用的是host order, 这是因为server在把租赁记录保存到dhcpd.leases文件时使用的是network order方式保存的。(个人认为声明为unsigned long类型更为合适)

在dhcpd.c文件里声明定义一个 指向struct dhcpOferedAddr类型数组的全局指针变量leases。leases指向的数组大小由IP地址池大小决定的。

Server端主要的结构体就是这些,他们是整个server端程序跑起来的基础。还有一些其他结构体的设计是为了某些函数特别定制的,在分析具体函数再做讲解。

udhcp源码详解(三)上 之配置信息的读取

上节介绍了存储管理配置信息的结构体struct server_config_t,该结构体贯穿整个server端程序的运行。

在dhcpd.c里的用该结构体定义个一个全局的变量: struct server_config_t server_config;

在server启动做的第一件事就是初始化这个全局变量server_config:

现在就进入read_config函数,一睹其真容!

学习这个函数,在以后遇到要读取配置信息可以参照read_config函数的写法。 read_config函数的定义是在file.c文件里。 分析read_config函数之前先来看一个结构体数组: 结构体的定义:

该结构体数组的定义以及初始化:

结构体名和数组名可以看出,config_keyword,配置信息关键字,正如其名,是辅助读取配置信息的。

数组里的每个config_keyword结构体元素中的keyword成员的值,与server_config_t里的成员变量名一一对应,该成员指示的就是配置的是server_config_t里的哪个成员。 读取配置信息的操作,配置信息读取存储到哪里,以及配置信息的默认值,这些由config_keywork里的后面的3个成员给出的。

成员heandler是一个函数指针,指向的函数即提供了怎么读取配置信息,以及把配置信息写到指定地址内。

arg, def则作为handler指向函数的两个各参数,arg告诉函数把配置信息存储到的地址,def则是配置信息的值,def是配置信息的默认值。

keyword数组里每个元素里的函数指针handler指向的这些函数,其定义都在file.c文件里。 handler函数的类型 : int (const *handler)(const char *line, void *arg)

1) read_ip: 把字符串格式的IP地址转换成uint32_t的IP格式,并存储到arg所指的内存。 2) read_u32: 把字符串格式的数转换成uint32_t类型数字,并存储到arg所指的内存。 3) read_yn: line字符串是“yes” 还是 “no”?若“yes”,将arg所指的内存赋1,反之,赋0 4) read_str:首先free(*arg)释放掉*arg指的内存,再根据字符串line的大小分配内存,并把line里的值复制到里面,让*arg指向新分配的内存。

5) read_opt: 从字符串line里读取options写到*arg所指的struct option_set链表里。(链表的每个结点data以CLV方式组织数据)。

read_opt对选项的信息的读取借助里也借助结构数组(在options.c里定义的),所以函数比较复杂,这里只用知道函数作用。

6) read_staticlease:从line字符串里读取MAC、IP地址字符串里MAC与IP用’/t’or’=’作为分隔)再把MAC和IP添加到*arg所指的static_lease链表里。static_lease链表是dhcp Server管理已租赁出去的IP和获得该IP的客户机的MAC绑定。

7) read_mac: 从字符串格式MAC地址里读出MAC并转化的MAC存储arg所指的内存里 上面的read_xxx函数,read成功返回1, 失败返回0。

上面讲解的这么的多都是read_config函数的基础,好啦,现在正式进入read_config函数:

若read_config函数到这就结束了,server依靠这些默认配置的值也可以工作(options字段没有默认值,不能给client其他的网络配置,但可以给client端分配IP的),但这样的话,程序就缺乏灵活性,IP地址池不能修改,租赁时间无法设定等。

上面这段就是从文件里读取配置信息,下面是摘自udhcp.conf文件的部分配置信息:

while(fgets(buffer, READ_CONFIG_BUF_SIZE, in))

每次从文件里读取一行,存放在buffer里,在对buffer处理,空行、注释行(还有只有token, 没有line的)直接跳过继续读取下一行。

token = strtok(buffer, “ /t”); 取到该行的keyword (文件里key与value之间使用空格或’/t’) line = strtok(NULL, “”); 取到该行的配置信息值

这样(还需简单处理下)就可以根据keywords数组把从文件读取到的配置信息写到server_config相应的成员里。

差不多了,总结下read_config函数,她对配置的对象全局变量server_conifg,通过一个辅助结构数组来对server_config里的成员变量进行赋值的。真正对于成员的赋值操作都是通过数组里元素的函数指针handler指向的函数进行的。

read_config只是做了从文件的每行读出信息,并作些处理,得到token和line,利用token在数组里找到对应的handler和arg,………

udhcp源码详解(三) 下 之配置信息的读取

上节讲解了read_config函数,读取配置信息到server_config的相应成员变量里,但

read_config函数只负责把配置信息重文件里读出来,具体怎么把信息填写到指定的地址内,是调用keywords数组里元素的handler成员指向的函数完成。这节就是讲解完成具体的写入操作中比较复杂的函数。

1) int read_opt(const char *line, void *arg)

在read_config从udhcpd.conf里读到 token = “opt” 或 “option”; 摘自udhcp.conf里的options配置信息 调用handler: read_opt

const_line的从udhcpd.conf的读出的line line的值 例:

dns 192.168.10.2 192.168.10.10 subnet 255.255.255.0 等

arg的值是 &(server_config.options)

即是把line的值存储到头结点为*arg的链表里去。

read_opt函数与read_config有点类似,也借助一个辅助的数组进行信息添加。

在以前章节有讲解过选项信息的存储组织方式,CLV:Code + Length + Value,value我们文件里读出来了,Code和Length在哪? 现在来看下这个辅助数组: 元素结构体的定义:

该结构体定义的数组:

这个辅助数组的做法与read_config里的有些类似,name即是keyword,flag可以求出Length,code即Code。

讲解这些函数真是痛苦,给你下套,而且是一环套一环的!

得到了每个选项信息里的value,其值保存在opt指向的内存,value长度为length,Code在struct dhcp_option类型的option里。

调用attach_option函数把选项信息的添加到server_config.options选项链表里。 /*

* add an option to the opt_list *

* attach_option函数把从文件里读出的以options 或opt标识的配置信息 * 在函数read_opt,通过option->flag处理好了line里val 值 存放在buffer * 再把配置信息填入到struct option_set的链表里 *

* 参数:opt_list: &(server_config.options)

* option: 根据选项头确定的dhcp_options数组里的一项

* buffer 配置信息(去除了option/opt和选项头,而且已经转换过了) * length 配置信息的大小(以字节为单位) */

static void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);

添加到链表里分为两种情况

①链表里没有与将要添加的选项信息的Code一样的结点,直接添加到链表即可。

②链表里已有与将要添加的选项信息的Code一样的结点,该Code选项信息flag选项里OPT_LIST(例如:dns选项的value是IP List 有两个IP):

}

2)、int read_staticlease(const char *line, void *arg) udhcpd.conf里static lease:

udhcp源码详解(四) 之租赁IP的管理

Server端对于租赁出去的IP的管理是基于结构体dhcpOfferedAddr的,该结构体的定义是在leases.c文件里:(结构体的成员介绍说明见详解之数据结构) [cpp] view plaincopy

1. struct dhcpOfferedAddr { 2. uint8_t chaddr[16];

3. uint32_t yiaddr; /* network order */ 4. uint32_t expires; /* host order */ 5. };

在dhcpd.c文件里用该结构体定义指向该结构数组的指针leases; /* dhcpd.c */ #include

struct dhcpOfferedAddr *leases;

在读取完配置信息server_config后,就可以依据配置信息的内存为该结构数组申请内存空间了:

本文来源:https://www.bwwdw.com/article/eq8t.html

Top