bootp dhclient NetworkManager dbus dnsmasq resolvconf avahi关系厘清(持续更新)

本文解析了DHCP客户端(dhclient)的工作流程及其与NetworkManager的交互方式,包括读取配置文件、发送探测报文的过程及NetworkManager的启动与通信机制。
在前一篇里面,其实只通过剖析代码确定了glibc通过resolv.conf来访问本地域名服务,resolvconf通过读取某路径下的文件把带nameserver的行写入resolv.conf两件事。接下来在继续追域名服务dnsmasq过程中就不断遇到新概念,比如题目中的6个,本文目标就是抽丝剥茧地厘清六者的关系,让自己在每个面试中都能讲这个讲半小时。

1.dhclient和bootp
    通过conf了解大概信息,proc和man查找conf位置。/var/lib/NetworkManager/dhclient-wlan0.conf,第一句话就是“由 NetworkManger 创建,合并自 /etc/dhcp/dhclient.conf”。conf内说明了要求和服务器沟通的的细节,比如需要请求 subnet-mask, broadcast-address, time-offset, routers...,需要发送host-name "liu3-CW65S"。还有一个lease文件/var/lib/NetworkManager/dhclient-1a3a1a3c-6e6a-4883-a3c5-3674000ae53e-wlan0.lease,看样子是会在读取conf后读取lease文件,文件内保存了上次会话的配置信息,上一篇文章可见的每一个request都会带上上一次获得的ip地址,想必也是从lease文件读取的信息吧。
    程序的启动参数有,-d强制前台运行,当然现在咋又是在后台了,还是看源码。dpkg -S /sbin/dhclient,apt-get source isc-dhcp-client即可。发现-d主要是no_daemon=1,quiet=0。no_daemon的唯一作用是把pid输出到文件,在我机器上是/run/sendsigs.omit.d/network-manager.dhclient-wlan0.pid。pid写入文件比如在nginx中可以用于重启或重新加载配置、刷新日志文件时找到conf对应的pid之后对进行发送相应的信号。对于dhclient来说没有pid文件-r -x这种强制关闭的命令行命名就不起效果,好了找到答案了,-d并不是man中说的要强制前台执行,而是要写pid,从而能从命令行把程序关掉。-sf /usr/lib/NetworkManager/nm-dhcp-client.action,这个选项,竟然是个可执行文件,还得看源码。
    在script_go函数内,的确是先fork再execve (scriptName, argv, envp)执行了脚本。函数是在用socket+ioctl取得接口列表并取得对应的lease后执行的。二进制可执行文件不可读,但是man是很清晰的。这个action脚本本身不适宜修改,用户自定义行为一般在conf实现,也可以用action脚本的hook。脚本有一个叫$reason的环境变量说明了当前脚本的执行动作,比如取得lease后的reason是“preinit”,对于preinit脚本会设置接口的ip为0.0.0.0,广播地址为255.255.255.255,像这种初始ip和初始广播地址是和网络接口强相关的,也许就是用一个额外的脚本实现的原因吧(还有个原因是用户定制函数用shell实现比较方便也好加载)。
    用action改了接口配置后首先进入state_init发送探测(DHCPDISCOVER)报文,make_discover内构造报文,可以细看下,因为可以顺便把bootp也给解决了。
    报文结构是一个struct dhcp_packet,完全和tcp/ip详解16-2的图一模一样。到此,bootp和dhclient的关系已经理清了,bootp是引导程序协议,dhclient基于此协议并利用特定厂商信息字段传递dhcp协议私有信息。这一点在上一篇的tcpdump输出内也得到了佐证。标识直接用的random()(种子用的时间和mac地址与),四个ip地址只设置了网关ip(重要,待进一步验证),具体其他bootp公共部分如何设置的可以看代码,基本都比较直观。dhcp部分以"\143\202\123\143"魔数打头说明是本协议,剩下的代码很长,先略过。在send_discover函数内发送探测报文,探测报文有超时重试机制。最终的发送是在send_packet里通过sendto进行的,那么writev的文件描述符在哪儿打开的呢?在dhcpv4_client_assignments函数内,先查找getservbyname有没有dhcpc的udp记录,因为/etc/services内没有,所以直接吧全局变量local_port写死为了68。之后在discover_interfaces内if_register_send内if_register_socket addr->sin_port = local_port;就帮顶了68,socket还设置了SO_REUSEADDR(用以重启,当然udp无所谓)\SO_BROADCAST(用以广播)\IP_RECVPKTINFO、SO_BINDTODEVICE。从上一篇文章tcpdump输出实例也可以得知通过68端口和dp服务器67端口通信的就是dhclient。
    发送具体逻辑在send_packet内,发送动作sendto在一个可选的while语句中,如果定义了IGNORE_HOSTUNREACH,那么对于立即返回的EHOSTUNREACH或者ECONNREFUSED错误会重试十次。然而我并不认为sendto会返回这两个错误,recvfrom差不多,并且对于udp,recvfrom取得的错误也无法确定是哪一个数据报的错误。接下来设定超时时间isc_interval_set定义找不到,反正是时隔一个间隔后重新调用发送探测报文的函数send_discover,直到已经收到回复报文(通过一个全局变量确定)或者探测足够多次之后放弃探测,并调用action脚本执行TIMEOUT命令,改命令尝试久的release信息是否可用,可用是指用旧的ip能ping通旧的路由器。
    如果没猜错isc_interval_set用的类似注册信号处理函数立即返回了,那么重复发送探测报文的算是一个线程,另外的一个线程在68端口上开了个tcp的监听。注册监听事件和超时、select等待这一块有时间可以会看下,感觉是个小型的nginx了(isc到底是个啥?)omapi_accept里有tcp新链接处理的逻辑,就不深入了。
    总之,dhclient读conf获取dhcp协议信息,读release获取上一次本机动态配置,运行action修改接口参数,再基于bootp从68端口由0.0.0.0向255.255.255.255发送udp探测报文,并在68端口建立tcp监听端口accept链接,之后大概动态分配服务器会和这个68建立长链接。尽管还有一大档子细节没弄明白,但是基本功能有个大概了。
    在没弄明白的很多问题中,dhclient是怎么启动的,dhclient是怎么更新resolve.conf的这两个得弄清楚。

2.NetworkManager
    启动:ubuntu的init启动过程见http://www.cnblogs.com/cassvin/archive/2011/12/25/ubuntu_init_analysis.html,写得清楚。那直接打开/etc/init/network-manager.conf,可见networkmanager的打开依赖local-filesystems事件(mountall命令)、started dbus、static-network-up。之后不带参数地执行/usr/sbin/NetworkManager。先readelf -s下,发现符号表竟然一屏都显示不完,说明这趟水很深。lsof -p发现一行NetworkMa 783 root    3u     unix 0xf6826080      0t0   14984 socket,类似的有三行unix域和一行FIFO。那么先从进程间通信方式下手吧。
    strace nmcli nm status &>temp,发现了两行socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0) = 4,connect(4, {sa_family=AF_LOCAL, sun_path="/var/run/dbus/system_bus_socket"}, 33) = 0。后面的recvmsg,sendmsg都是在这个4上进行的。看来这里已经不能避免了解dbus了。
    先看一系列博客补充知识http://blog.youkuaiyun.com/flowingflying/article/details/5410820,再看nmcli源码,和dhclient比起来还便于用gdb调。在扒之前得了解下glib的知识。https://developer.gnome.org/gobject/stable/gobject-Type-Information.html,额,貌似竟然是试图在c语言基础上实现类的一个库,比如g_string_new,怀着为什么有了c++还要glib+gobject的问题上sof,是linux图形界面gtk是纯c开发的,因为实际需求和开发接口统一发明了这一套,有的变态面试官如果出“如何用c语言实现面向对象”,可以了解下这个。言归正传,nmcli.c代码一开始是解析参数,之后直接就是bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error)。dbus相关的基础知识http://blog.youkuaiyun.com/flowingflying/article/details/5455327可以学习到,个人感觉作为一个比tcpip简单不少的进程间的通讯协议,函数有点太多了不容易记忆。不过看起来dbus+glib是个重要知识点,本文暂停,先恶补下。
`BOOTP` 和 `DHCP` 是计算机网络中用于主机自动获取 IP 地址的协议,它们之间有着密切的历史和技术关系。 --- ### 回答问题:**BOOTP 和 DHCP 的关系是什么?** **DHCP(Dynamic Host Configuration Protocol)是 BOOTP(Bootstrap Protocol)的扩展和继承者。** 换句话说: > ✅ **DHCP 基于 BOOTP 设计,并向后兼容 BOOTP 报文格式,但提供了更多功能,尤其是动态地址分配、租期管理和更灵活的配置机制。** --- ### 1. 历史背景 - **BOOTP** 出现在 1985 年(RFC 951),最初设计用于无盘工作站从网络启动时获取: - IP 地址 - 启动服务器地址 - 启动镜像文件名 - 它使用静态配置:管理员必须手动为每个客户端绑定 MAC 地址和 IP 地址。 - **DHCP** 在 1993 年由 IETF 提出(RFC 1541,后来被 RFC 2131 取代),在 BOOTP 的基础上增加了: - 动态 IP 分配(可回收) - 租期机制(Lease Time) - 更丰富的选项(如 DNS、网关、NTP 等) --- ### 2. 技术对比兼容性 | 特性 | BOOTP | DHCP | |------|-------|------| | 报文格式 | 使用 UDP,固定长度字段 | 在 BOOTP 基础上增加“Options”字段 | | IP 分配方式 | 静态(基于 MAC 预配置) | 动态、自动或手动绑定 | | 租期管理 | 不支持(永久分配) | 支持租期、续租、释放 | | 向后兼容 | ❌ | ✅ DHCP 服务器可服务 BOOTP 客户端 | | 扩展性 | 差 | 强(通过 Option 字段扩展) | > 🔍 **关键点**:DHCP 报文结构完全兼容 BOOTP 的头部(前 300 多字节相同),只是在其后的 “vendor extensions” 字段中使用了新的编码方式(称为 DHCP Options),实现了功能扩展。 --- ### 3. DHCP 如何利用 BOOTP 兼容性? DHCP 服务器可以识别两种客户端: - **纯 BOOTP 客户端**:只发送标准 BOOTP 请求 → DHCP 服务器返回静态分配的 IP。 - **DHCP 客户端**:在 options 字段包含 `option 53`(DHCP Message Type)→ 服务器按 DHCP 流程处理。 这意味着在一个网络中,一个 DHCP 服务器可以同时服务旧的无盘终端(BOOTP)和现代 PC(DHCP)。 --- ### 4. 典型工作流程对比 #### BOOTP 流程(简化): ```text Client → (广播) BOOTREQUEST → Server → (单播/广播) BOOTREPLY → 给客户端返回预设的 IP 和启动信息 ``` > ❗ IP 地址是永久分配的,不会过期。 #### DHCP 流程(四步:DORA): ```text 1. Discover → 客户端广播寻找 DHCP 服务器 2. Offer ← 服务器回应可用 IP 3. Request → 客户端选择一个 Offer 并请求该 IP 4. Ack ← 服务器确认分配(带租期) ``` 此外还支持: - `DHCP Decline`, `DHCP Release`, `DHCP Inform` 等消息。 --- ### 5. 使用的端口号(相同传输层机制) 两者都使用 UDP 协议: | 协议 | 源端口 | 目的端口 | 说明 | |------|--------|----------|------| | BOOTP/DHCP Client → Server | 68 | 67 | 客户端发请求 | | BOOTP/DHCP Server → Client | 67 | 68 | 服务端回响应 | > 注意:客户端没有 IP 地址,所以通常以 `0.0.0.0` 作为源地址,目的地址为 `255.255.255.255`(广播)。 --- ### 总结 | 关系总结 | 内容 | |---------|------| | ✅ 继承关系 | DHCP 是 BOOTP 的增强版 | | ✅ 格式兼容 | DHCP 报文基于 BOOTP 结构 | | ✅ 功能扩展 | DHCP 支持动态分配、租期、更多配置选项 | | ✅ 实际应用 | 现代网络几乎全部使用 DHCP;BOOTP 仅存在于遗留系统 | ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值