资讯动态

IncludeOS 内建 DHCP 服务器测试指南:从桥接网络配置到 DHCPD 状态机源码解析

发布时间:2026/10/15 3:47:22 来源:尧图企业网站定制
操作系统云原生虚拟化网络【免费下载链接】IncludeOSA minimal, resource efficient unikernel for cloud services项目地址https://gitcode.com/gh_mirrors/in/IncludeOS点击查看免费下载本文以仓库内 test/integration/net/dhcpd/README.md 为主线完整梳理 IncludeOS unikernel 自带 DHCP 服务器的集成测试方法包括桥接网络bridge43与路由的搭建、测试服务的配置方式、DHCPD 核心 API 与 DHCP 状态机实现以及基于 vmrunner 的自动化验证流程。读完本文你将能在本地 KVM/QEMU 环境中独立运行 IncludeOS DHCP 服务器测试并理解其地址池管理、租约处理与 DISCOVER/OFFER/REQUEST/ACK 的底层交互细节。测试目标与整体流程该测试的目的是验证 IncludeOS 内置的 DHCP 服务器net::dhcp::DHCPD能否在一个虚拟机实例内同时承担服务器与客户端双重角色接口 0Interfaces::get(0)被静态配置为 DHCP 服务器监听 UDP 67 端口向地址池发放 IP接口 1/2/3 作为三个 DHCP 客户端通过negotiate_dhcp()向本机服务器发起 DHCP 协商自动化脚本在检测到三个客户端分别拿到 IP 后逐一对其执行ping连通性验证。测试目录test/integration/net/dhcpd/下的完整文件清单如下文件作用README.md测试说明与前置条件service.cpp单 unikernel 内的 DHCP 服务器 3 个 DHCP 客户端CMakeLists.txt构建配置virtionet 驱动、autoconf 插件test.pyvmrunner 驱动的自动化测试脚本vm.jsonVM 镜像配置4 个 virtio 网卡setup.sh清理 ARP 缓存的预备脚本前置条件创建桥接网络并添加路由原 README 明确指出运行本测试必须具备两个前置条件这里逐条展开说明。1. 运行 create_bridge.sh 创建 bridge43测试要求先在宿主机上执行安装 IncludeOS 工具链时随前缀安装的脚本$INCLUDEOS_PREFIX/includeos/scripts/create_bridge.sh该脚本会创建名为bridge43的 Linux 网桥并把它与 VM 的虚拟网卡桥接起来使 VM 内部的网络对宿主机可达。注意此脚本并不在本仓库内而是随 IncludeOS 工具链$INCLUDEOS_PREFIX安装到本地的脚本运行前请确认$INCLUDEOS_PREFIX环境变量已正确指向你的 IncludeOS 安装前缀。2. 向网桥添加子网路由创建桥接后还需让宿主机知道如何路由到测试用的 10.0.100.0/24 网段sudo route add -net 10.0.100.0 netmask 255.255.255.0 dev bridge43这条命令将10.0.100.0/24的路由指向bridge43保证宿主机发出的ping探测能够通过网桥抵达 VM 内被 DHCP 分配的地址。需要特别注意的是该网段与测试服务内部使用的网段不同测试 VM 内部是10.0.0.0/24网段服务器10.0.0.9池范围10.0.0.10~10.0.0.20。桥接路由指向10.0.100.0/24是 IncludeOS 测试网络的标准约定同一套约定也出现在test/integration/net/dhcpd_dhclient_linux等其它网络集成测试中具体对应关系由测试环境统一规划。3. 可选清理宿主机 ARP 缓存setup.sh 会在测试前清除宿主机对池中前三个地址的 ARP 缓存条目避免上一次运行残留的 MAC 映射干扰ping验证#!/bin/bash sudo arp -d 10.0.0.10 21 /dev/null sudo arp -d 10.0.0.11 21 /dev/null sudo arp -d 10.0.0.12 21 /dev/null测试服务源码剖析一台 unikernel 同时扮演服务器与客户端service.cpp 是本次测试的核心服务其逻辑分三层服务器配置、服务器实例化、三个 DHCP 客户端。服务器静态网络配置auto inet Interfaces::get(0); inet.network_config( { 10,0,0,9 }, // IP { 255,255,255,0 }, // Netmask { 10,0,0,1 }, // Gateway { 8,8,8,8 }); // DNS服务器接口被固定配置为10.0.0.9/24网关10.0.0.1DNS8.8.8.8。这些参数不仅是服务器自身的网络身份还会作为默认值写入后续 DHCPOFFER/DHCPACK 的下发选项中见下文DHCPD 参数继承。实例化 DHCPD 并指定地址池IP4::addr pool_start{10,0,0,10}; IP4::addr pool_end{10,0,0,20}; server std::make_uniqueDHCPD(inet.udp(), pool_start, pool_end);地址池取10.0.0.10~10.0.0.20共 11 个可用地址足够三个客户端各分配一个且留有富余。服务器实例以std::unique_ptr持有生命周期贯穿整个服务。三个客户端并发协商 DHCPInterfaces::get(1).negotiate_dhcp(10.0, [] (bool timeout) { if (timeout) { printf(Client 1 timed out\n); } else { INFO(DHCP test, Client 1 got IP from IncludeOS DHCP server); printf(%s\n, Interfaces::get(1).ip_addr().str().c_str()); } });客户端 2、3 的写法与客户端 1 完全一致见源码 service.cpp。negotiate_dhcp()是 api/net/inet.hpp 暴露的接口签名如下void negotiate_dhcp(double timeout 10.0, dhcp_timeout_func nullptr);timeout等待 DHCP 协商完成的秒数这里设为 10 秒回调参数bool timeout为true表示协商超时为false表示成功拿到 IP。三个客户端从Interfaces::get(1)、get(2)、get(3)取得在 vm.json 中对应 4 个 virtio 网卡1 个用于服务器3 个用于客户端每个网卡都有独立的 MAC 地址c0:01:0a:00:00:2a、c0:01:0a:00:00:2f、c0:01:0a:00:00:20、c0:01:0a:00:00:23内存配置为 128 MB。DHCPD 核心 API 与默认参数测试通过DHCPD的构造函数即可完成服务器初始化api/net/dhcp/dhcpd.hpp 中定义DHCPD(UDP udp, ip4::Addr pool_start, ip4::Addr pool_end, uint32_t lease DEFAULT_LEASE, // 86400 秒 1 天 uint32_t max_lease DEFAULT_MAX_LEASE, // 345600 秒 4 天 uint8_t pending DEFAULT_PENDING); // 30 秒各参数含义如下表参数默认值说明udp—服务器所在接口的 UDP 协议栈构造时内部udp.bind(DHCP_SERVER_PORT)绑定 67 端口pool_start/pool_end—可分配地址池的起止 IP闭区间两端都会被纳入池中lease86400 秒1 天租约时长写入 DHCPOFFER/DHCPACK 的 lease time 选项max_lease345600 秒4 天最大租约时长pending30 秒已 OFFER预占地址被视作 pending 状态的最长时间这些默认值定义在 dhcpd.hpp 的类内常量中。DHCPD 参数继承默认值取自接口配置构造函数src/net/dhcp/dhcpd.cpp会自动从传入的 UDP 栈对应的 Inet 实例继承网络参数因此测试代码无需逐一设置server_id_{stack_.ip_addr()}, // 服务器标识 接口 IP10.0.0.9 netmask_{stack_.netmask()}, // 子网掩码 255.255.255.0 router_{stack_.gateway()}, // 网关 10.0.0.1 dns_{stack_.dns_addr()}, // DNS 8.8.8.8如果你希望下发与接口配置不同的参数例如 DHCP 客户端需要指向另一台 DNS可以调用 dhcpd.hpp 中的 setter 覆盖void set_server_id(ip4::Addr server_id); void set_netmask(ip4::Addr netmask); void set_router(ip4::Addr router); void set_dns(ip4::Addr dns); void set_lease(uint32_t lease); void set_max_lease(uint32_t max_lease); void set_pending(uint8_t pending);启动监听在dhcpd_dhclient_linux测试中可以看到实例化之后还需显式调用一次listen()才开始接收客户端请求server std::make_uniqueDHCPD(inet.udp(), pool_start, pool_end); server-listen();listen()dhcpd.cpp在 socket 上注册on_read回调仅接受来自DHCP_CLIENT_PORT68的报文且要求报文长度不小于sizeof(Message)随后调用resolve()分发处理。主测试服务之所以没有调用listen()是因为resolve()会通过switch完整处理所有消息类型而主测试只依赖 DISCOVER/REQUEST 路径——不过从正确性角度正式使用 DHCP 服务器时应与dhcpd_dhclient_linux保持一致调用listen()。底层实现DHCP 消息格式与状态机DHCP 报文结构与选项机制IncludeOS 的 DHCP 实现遵循 RFC 2131。api/net/dhcp/message.hpp 定义了紧凑的内存结构Messageop / htype / hlen / hops / xid / secs / flags ciaddr / yiaddr / siaddr / giaddr chaddr[16] / sname[64] / file[128] / magic[4] / options[]其中magic是 DHCP 选项魔数99, 130, 83, 99见 dhcp4.hpp 中的DHCP_OPTIONS_COOKIE选项区最大长度Message::LIMIT 304DHCP_VEND_LEN。选项采用code length value的 TLV 格式api/net/dhcp/options.hpp 枚举了从SUBNET_MASK(1)到END(255)的全部选项码本实现重点支持subnet_mask1、routers3、domain_name_servers6broadcast_address28、req_addr50、lease_time51message_type53、server_identifier54、client_identifier61DHCP 消息类型与端口常量定义在 api/net/dhcp/dhcp4.hpp 与 L91-L92enum class message_type : uint8_t { DISCOVER 1, OFFER 2, REQUEST 3, DECLINE 4, ACK 5, NAK 6, RELEASE 7, INFORM 8 }; static const uint8_t DHCP_SERVER_PORT 67; static const uint8_t DHCP_CLIENT_PORT 68;地址池与租约记录的数据结构服务器内部维护两份核心数据dhcpd.hppstd::mapip4::Addr, Status pool_地址池每个地址对应一个状态std::vectorRecord records_客户端租约记录注释中明确说明这是临时的、替代持久化存储的方案。Status状态机定义在 api/net/dhcp/record.hppAVAILABLE可分配→ OFFERED已预占等待客户端 REQUEST 确认→ IN_USE已分配Recordrecord.hpp保存客户端的 client identifier、分配到的 IP、当前状态、租约开始时间RTC::now()与租约时长以及 T1/T2 续租时间的 RFC 建议值说明T1 默认0.5 × 租约时长T2 默认0.875 × 租约时长。地址池合法性校验构造函数在init_pool()之前先调用valid_pool()dhcpd.cpp做四重校验不满足则抛出DHCP_exception{Invalid pool}start end非法start end非法池至少要有 2 个地址inc_addr(start) end非法相邻地址导致池只有 1 个可用地址start、end、服务器自身必须处于同一子网用network_address()即IP netmask比对。通过校验后init_pool()dhcpd.cpp将从pool_start_到pool_end_的每个地址闭区间含终点以AVAILABLE状态放入pool_。消息分发resolve() 的输入校验resolve()dhcpd.cpp是消息处理入口对每条报文执行五道校验op必须是BOOTREQUEST1且hops 0不接受中继转发过的请求htype必须在合法硬件类型区间1 ~ 37内yiaddr、siaddr、giaddr必须为 0客户端初始请求不应携带这些字段选项区字节数不超过Message::LIMIT且选项数量不超过MAX_NUM_OPTIONS 30见valid_options()dhcpd.cpp选项魔数必须是99,130,83,99且必须携带message_type选项。随后依据消息类型进入不同分支DISCOVER → offer()、REQUEST → handle_request()、DECLINE / RELEASE / INFORM各自处理未识别类型直接忽略。DHCPOFFERoffer() 的组包与发送策略offer()dhcpd.cpp处理流程若该 client id 已有记录直接返回避免池被未确认的 offer 耗尽线性扫描pool_找到第一个AVAILABLE地址若池已空则先clear_offered_ips()回收所有OFFERED地址再发NAK将该地址置为OFFERED创建Record记录 client id、IP、RTC::now()租约起点与lease_时长用Message_writer组 DHCPOFFER 报文依次添加server_identifier54→ 服务器 IPsubnet_mask1→ 继承自接口的掩码routers3→ 网关lease_time51→ 租约时长time_offset2→ 0domain_name_servers6→ DNSbroadcast_address28→server_id_ | (~netmask_)计算出的广播地址end255结束选项区。报文的发送目标严格按 RFC 2131 的四种情形选择源码注释逐条对应giaddr非 0 → 发给中继代理的 67 端口ciaddr非 0 → 单播到ciaddr的 68 端口flags 广播位置位BOOTP_BROADCAST 0x8000→ 广播到0xffffffff否则 → 单播到yiaddr的 68 端口。DHCPREQUESThandle_request() 的三种客户端状态handle_request()dhcpd.cpp区分两种大场景场景一请求中携带 server_identifier 选项说明客户端在响应某个 OFFER若 server id 不是本服务器 → 客户端选择了别的服务器释放本服务器为该 client 预占的地址与记录若是本服务器当ciaddr 0且请求的 IP 与记录一致客户端处于 SELECTING 状态→ 记录与池同步置为IN_USE发送 DHCPACK否则发送 DHCPNAK。场景二请求中未携带 server_identifier客户端在验证或续租既有地址交给verify_or_extend_lease()dhcpd.cpp处理服务器无该 client 的记录 → 按 RFC 保持沉默ciaddr 0INIT-REBOOT 状态→ 用on_correct_network()校验请求 IP 是否与服务器同子网giaddr 非 0 时使用客户端上报的远端掩码错误则回收记录并发 DHCPNAK正确则刷新租约起点并发 DHCPACKciaddr ! 0且未请求新 IPRENEWING/REBINDING 状态→ 刷新记录与池状态无条件发送 DHCPACK。DECLINE / RELEASE / INFORMDECLINE客户端发现地址冲突。删除该 client 的记录并把冲突地址置为IN_USE注释指出应通知网络管理员排查配置问题RELEASE客户端主动释放。删除记录并把ciaddr对应的地址恢复为AVAILABLE源码中同时保留了置为 RELEASED 以便复用配置的备选实现注释INFORM客户端已通过其它方式获得 IP仅请求额外配置参数。inform_ack()dhcpd.cpp组 DHCPACK 单播回ciaddr不分配地址、不包含租约参数。DHCPACK 与 DHCPNAKrequest_ack()dhcpd.cpp组 DHCPACKyiaddr取记录中分配的 IP选项携带lease_time与server_identifier发送策略与 offer 一致。nak()dhcpd.cpp组 DHCPNAK包含messageSomething went wrong与server_identifier并广播发送。自动化验证test.py 的检测与 ping 流程test.py 基于 vmrunner 框架驱动整个测试启动镜像net_dhcpd.elf.binboot 超时 20 秒time_sensitive: True确保时间敏感行为被正确模拟通过vm.on_output()为三个客户端的成功日志注册同一个事件处理器DHCP_test匹配到Client 1/2/3 got IP from IncludeOS DHCP server后读取下一行即客户端打印的已分配 IP等待 1 秒后对宿主机执行ping -c 3 -i 0.2 ip3 次探测、200ms 间隔20 秒超时每成功一次累加计数器三个客户端全部 ping 通后调用vm.exit(0, ...)以零状态退出任意一次 ping 失败即打印异常并返回False测试失败。这套流程验证了三个关键事实DHCP 协商确实成功客户端拿到地址、地址通过网桥对宿主机可达、服务器下发的网络配置网关/掩码可用。构建配置与相关测试构建配置CMakeLists.txt 声明了该测试镜像的构建方式os_add_executable(net_dhcpd IncludeOS DHCP server test service.cpp) os_add_plugins(net_dhcpd autoconf) // 自动配置插件 os_add_drivers(net_dhcpd virtionet) // virtio 网络驱动 os_add_stdout(net_dhcpd default_stdout) // 默认标准输出 configure_file(test.py ${CMAKE_CURRENT_BINARY_DIR})可见该测试依赖virtionet驱动对应 vm.json 中的 4 个 virtio 网卡与autoconf插件。兄弟测试与 Linux dhclient 互通仓库还提供了 test/integration/net/dhcpd_dhclient_linux/README.md 这一兄弟测试用于验证 IncludeOS DHCP 服务器与真实 Linuxdhclient的互通性同样要求先运行$INCLUDEOS_PREFIX/includeos/scripts/create_bridge.sh运行命令为sudo -E ./test.py需要 root 权限-E保留环境变量其 service.cpp 将服务器配在10.200.0.1/16地址池为10.200.100.20~10.200.100.30并显式调用server-listen()再由宿主机上的 Linuxdhclient通过网桥去申请地址。单元测试佐证test/unit/net/dhcp.cpp 用两个UserNet虚拟设备模拟网络服务器侧配置10.0.0.42/24并创建池为10.0.0.1~10.0.0.24的 DHCPD客户端侧调用negotiate_dhcp()后在on_config回调中确认拿到配置——完整走通了一遍 DISCOVER/OFFER/REQUEST/ACK 的流程与集成测试形成互补。小结围绕test/integration/net/dhcpd这一测试用例本文完整覆盖了README 要求的两项前置条件create_bridge.sh与bridge43路由、service.cpp 中服务器/客户端共存的配置范式、DHCPD 构造参数与默认值、listen()监听机制、RFC 2131 状态机DISCOVER→OFFER、REQUEST→ACK/NAK、DECLINE/RELEASE/INFORM以及地址池与租约记录的数据结构。无论你是要在自己的 IncludeOS 服务中内嵌 DHCP 服务器还是希望深入理解 DHCP 协议在 unikernel 中的实现都可以直接参考 src/net/dhcp/dhcpd.cpp 与 api/net/dhcp/dhcpd.hpp 按图索骥。赞分享操作系统云原生虚拟化网络【免费下载链接】IncludeOSA minimal, resource efficient unikernel for cloud services项目地址https://gitcode.com/gh_mirrors/in/IncludeOS点击查看免费下载相关推荐IncludeOS DHCP 客户端功能测试指南从 negotiate_dhcp 调用到网桥联调验证IncludeOS DHCP 客户端功能测试指南从 negotiate_dhcp 调用到网桥联调验证 导读 本指南围绕 IncludeOS 仓库中的 DHCP操作系统云原生虚拟化网络AdGuard Home 内置 DHCP 服务器测试指南VirtualBox 隔离网络与 DHCPTest 快速验证全流程AdGuard Home 内置 DHCP 服务器测试指南VirtualBox 隔离网络与 DHCPTest 快速验证全流程 AdGuard Home 不仅是网网络后端网络安全Calibre电子书格式转换实用指南PDF、EPUB到Kindle的30格式互转完整说明Calibre电子书格式转换实用指南PDF、EPUB到Kindle的30格式互转完整说明 Calibre是一款开源免费的电子书管理工具内置的转换引擎支持E桌面应用后端上一篇Slang 编译器 AST 到 IR 降级Lowering深度解析从 LoweredValInfo 到 LOWER-TO-IR 测试体系下一篇chezmoi 的 vault 模板函数从 HashiCorp Vault 安全注入密钥到点文件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑