udhcpd_main的Super loop
到这一步,DHCP服务器开始提供具体的服务,super loop主要包括建立socket监听及信号处理、获取并提取报文、根据state和报文内容做出响应。
建立Socket监听和signal处理器
若未建立本地socket监听或监听意外关闭,重新建立,若建立失败,则打印log并退出。本地监听地址端口:SERVER_PORT (67),监听硬件接口:server_config.interface,监听地址:INADDR_ANY(所有地址)
if (server_socket < 0) {
server_socket = listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
server_config.interface);
}
使用select+FD模型,对socket和socketpair进行监控
/* 添加server_socket和signal_pipe进rfds集合*/
max_sock = udhcp_sp_fd_set(&rfds, server_socket);
/* int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
{
FD_ZERO(rfds);
FD_SET(signal_pipe[0], rfds);
if (extra_fd >= 0) {
fcntl(extra_fd, F_SETFD, FD_CLOEXEC);
FD_SET(extra_fd, rfds);
}
return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;*/
/* 如果auto_time不为0,更新等待时间tv.tv_sec为auto_time的剩余时间 */
if (server_config.auto_time) {
tv.tv_sec = timeout_end - monotonic_sec();
tv.tv_usec = 0;
}
/* 如果auto_time为0,或tv_sec大于0时,建立select等待server_socket和signal_pipe的信号 */
if (!server_config.auto_time || tv.tv_sec > 0) {
max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0];
/* 对两个fd都进行可读性检测 */
retval = select(max_sock + 1, &rfds, NULL, NULL,
/*如果auto_time不为0,则非阻塞,超时时间为上述的剩余时间;如果为0,则time设为NULL,select将一直阻塞直到某个fd上接收到信号*/
server_config.auto_time ? &tv : NULL);
} else retval = 0; /* If we already timed out, fall through */
/* 若直到超时都没有接收到信号,则立即写lease文件,并更新time_end */
if (retval == 0) {
write_leases();
timeout_end = monotonic_sec() + server_config.auto_time;
continue;
}
if (retval < 0 && errno != EINTR) {
DEBUG("error on select");
continue;
}
/* 若signal_pipe接收到可读signal(signal_handler将signal写入signal_pipe[1],根据socketpair的特性,此时signal_pipe[0]将可读,产生一个可读的信号) */
switch (udhcp_sp_read(&rfds)) {
/* 接收到SIGUSR1,立即写leases,并更新time_end */
case SIGUSR1:
bb_info_msg("Received a SIGUSR1");
write_leases();
/* why not just reset the timeout, eh */
timeout_end = monotonic_sec() + server_config.auto_time;
continue;
case SIGTERM:
bb_info_msg("Received a SIGTERM");
goto ret0;
case 0: break; /* no signal */
default: continue; /* signal or error (probably EINTR) */
}
这一步的主要目的就是对server_socket和socketpair建立监听,并根据对socketpair的信号情况及leases结构的内容,执行write_leases函数,该函数将最新的leases结构体里的内容写入lease_file,根据yiaddr、remaining和当前时间来更新lease_time,每次执行完write_leases函数和,都有更新time_end时刻,write_leases定义于files.c中。
获取和提取报文
调用udhcp_get_packet函数从server_socket接收数据报文填充到packet,注意该函数在调用read读取报文之后,会对报文内的数据进行简单校验,包