文档说明
本文档详细解析VPP中的DHCP插件实现,从DHCP协议基础到VPP的具体实现和增强特性,系统性地梳理整个DHCP插件的架构和源码细节。此篇文章主要讲解前三部分:第一部分:DHCP协议基础知识、第二部分:VPP DHCP插件功能概述、第三部分:源码目录结构与模块划分。后续内容在专栏后续文章中讲解。
目录大纲
-
第一部分:DHCP协议基础知识 - 系统介绍DHCP协议的基本概念、DORA交互流程、DHCPv4/v6协议差异以及协议在网络中的角色定位,为理解VPP实现奠定理论基础。
-
第二部分:VPP DHCP插件功能概述 - 全面介绍VPP DHCP插件的功能特性、架构定位、API/CLI支持能力以及多线程处理机制,帮助读者建立对插件的整体认知。
-
第三部分:源码目录结构与模块划分 - 详细解析DHCP插件的源码文件组织方式、各模块职责划分、控制平面与数据平面分离设计,以及模块间的依赖关系。
-
第四部分:DHCPv4客户端实现详解 - 深入剖析DHCPv4客户端的核心数据结构、状态机实现、报文构造与解析、地址安装与路由更新等关键实现细节。
-
第五部分:DHCPv6客户端实现详解 - 全面解析DHCPv6客户端的CP/DP分离架构、IA_NA和IA_PD两种模式的实现机制、前缀委派功能以及DUID管理。
-
第六部分:DHCP Proxy实现详解 - 深入讲解DHCP代理/中继的架构设计、多服务器支持策略、VRF隔离机制、Option 82插入以及Cookie去重机制。
-
第七部分:API接口与CLI命令详解 - 系统说明所有DHCP相关的API接口定义、参数说明、使用示例以及CLI命令的完整语法和配置方法。
-
第八部分:调试与故障排查 - 详细介绍日志系统使用、统计计数器查看、调试命令操作、常见问题诊断方法以及数据包捕获分析技巧。
-
第九部分:扩展与定制 - 讲解如何基于源码添加新的DHCP选项支持、定制客户端和Proxy行为、与外部系统集成以及插件开发最佳实践。
-
第十部分:应用场景与配置实践 - 基于实际CLI命令和配置方法,详细说明边缘路由器、企业网络、多租户等典型应用场景的具体配置步骤和实现效果。
-
第十一部分:安全考虑 - 分析DHCP协议面临的安全威胁(欺骗攻击、DoS攻击等)、VPP提供的安全特性以及实际部署中的安全加固建议。
-
第十二部分:当前限制与未来发展方向 - 总结当前实现的限制(如不支持DHCP服务器、选项支持有限等)以及基于协议标准和实际需求的改进方向。
附录
术语表
DHCP相关术语
DHCP(动态主机配置协议)
- 通俗解释:就像网络世界的"自动售货机",当你连接网络时,自动给你分配IP地址、网关、DNS等配置信息,不需要手动设置。
- 技术说明:一种网络协议,允许网络管理员集中管理和自动分配IP地址及其他网络配置参数。
DHCP客户端(DHCP Client)
- 通俗解释:需要获取网络配置的设备,比如你的电脑、手机。它们会向DHCP服务器"要"IP地址。
- 技术说明:运行DHCP客户端程序的网络设备,通过DHCP协议请求和获取网络配置。
DHCP服务器(DHCP Server)
- 通俗解释:网络中的"管理员",负责给所有设备分配IP地址和网络配置。
- 技术说明:运行DHCP服务器程序的设备,维护IP地址池,响应客户端请求并分配配置。
DHCP中继(DHCP Relay/Proxy)
- 通俗解释:网络中的"传话筒",当客户端和服务器不在同一个网络时,帮助转发消息。
- 技术说明:位于不同网络段之间的设备,接收客户端的DHCP请求并转发给远程的DHCP服务器。
租约(Lease)
- 通俗解释:IP地址的"租用期限",就像租房有租期一样,IP地址也有使用期限。
- 技术说明:DHCP服务器分配给客户端的IP地址有使用时间限制,到期后需要续租或重新获取。
租约时间(Lease Time)
- 通俗解释:IP地址可以使用的时长,比如24小时、7天等。
- 技术说明:DHCP服务器分配给客户端的IP地址的有效使用期限。
T1时间(Renewal Time)
- 通俗解释:租约到期前50%的时间点,客户端会开始尝试续租。
- 技术说明:租约时间的50%,客户端在此时间点开始尝试续租IP地址。
T2时间(Rebinding Time)
- 通俗解释:租约到期前87.5%的时间点,如果续租失败,客户端会尝试重新绑定。
- 技术说明:租约时间的87.5%,如果T1时续租失败,客户端在T2时会尝试重新绑定。
DISCOVER(发现消息)
- 通俗解释:客户端第一次连接网络时发出的"求助信号",询问"有人能给我IP地址吗?"
- 技术说明:DHCPv4客户端发送的广播消息,用于发现可用的DHCP服务器。
OFFER(提供消息)
- 通俗解释:服务器收到DISCOVER后,回复"我可以给你这个IP地址"。
- 技术说明:DHCP服务器响应DISCOVER消息,向客户端提供一个IP地址配置。
REQUEST(请求消息)
- 通俗解释:客户端收到OFFER后,正式"申请"使用这个IP地址。
- 技术说明:客户端发送的消息,请求使用服务器提供的IP地址配置。
ACK(确认消息)
- 通俗解释:服务器确认"好的,这个IP地址归你用了"。
- 技术说明:服务器确认客户端的请求,正式分配IP地址配置。
NAK(否认消息)
- 通俗解释:服务器说"不行,这个IP地址不能给你"。
- 技术说明:服务器拒绝客户端的请求,通常是因为请求的IP地址无效或已被使用。
RELEASE(释放消息)
- 通俗解释:客户端主动说"我不需要这个IP地址了,还给你"。
- 技术说明:客户端主动释放已分配的IP地址,通知服务器可以重新分配。
DECLINE(拒绝消息)
- 通俗解释:客户端发现IP地址有问题(比如冲突),告诉服务器"这个地址不能用"。
- 技术说明:客户端检测到IP地址冲突或其他问题,拒绝使用该地址。
RENEW(续约)
- 通俗解释:租约快到期时,客户端向原服务器申请延长使用时间。
- 技术说明:客户端在T1时间点向原DHCP服务器请求续租IP地址。
REBIND(重新绑定)
- 通俗解释:续约失败后,客户端向任何可用的服务器申请继续使用IP地址。
- 技术说明:客户端在T2时间点,如果续租失败,向任何可用的DHCP服务器请求重新绑定。
SOLICIT(请求消息,DHCPv6)
- 通俗解释:DHCPv6版本的DISCOVER,客户端请求IPv6地址配置。
- 技术说明:DHCPv6客户端发送的消息,用于发现和请求IPv6配置。
ADVERTISE(通告消息,DHCPv6)
- 通俗解释:DHCPv6版本的OFFER,服务器提供IPv6配置。
- 技术说明:DHCPv6服务器响应SOLICIT,提供IPv6地址配置。
CONFIRM(确认消息,DHCPv6)
- 通俗解释:客户端确认之前获取的IPv6地址是否仍然有效。
- 技术说明:客户端移动网络后,确认之前获取的地址在新网络中是否仍然可用。
INFORMATION-REQUEST(信息请求,DHCPv6)
- 通俗解释:客户端只需要配置信息(如DNS),不需要IP地址。
- 技术说明:客户端请求除IP地址外的其他配置信息。
RELAY-FORW(中继转发,DHCPv6)
- 通俗解释:中继设备将客户端的消息封装后转发给服务器。
- 技术说明:DHCPv6中继将客户端消息封装在Relay消息中转发。
RELAY-REPL(中继回复,DHCPv6)
- 通俗解释:服务器通过中继设备回复客户端。
- 技术说明:DHCPv6服务器将回复封装在Relay消息中,通过中继转发给客户端。
DHCP Options(DHCP选项)
- 通俗解释:DHCP消息中的"附加信息",比如DNS服务器地址、网关地址等。
- 技术说明:DHCP消息中可选的配置参数,用于传递额外的网络配置信息。
Option 53(消息类型选项)
- 通俗解释:告诉对方这条消息是什么类型(DISCOVER、OFFER等)。
- 技术说明:标识DHCP消息类型的必需选项。
Option 54(服务器标识符)
- 通俗解释:告诉客户端"我是哪个服务器"。
- 技术说明:标识DHCP服务器的IP地址。
Option 55(参数请求列表)
- 通俗解释:客户端告诉服务器"我需要哪些配置信息"。
- 技术说明:客户端请求的配置参数列表。
Option 61(客户端标识符)
- 通俗解释:客户端的"身份证",用于标识自己。
- 技术说明:唯一标识DHCP客户端的选项。
Option 82(中继代理信息)
- 通俗解释:中继设备在消息中添加的"标签",告诉服务器消息来自哪里。
- 技术说明:中继代理插入的信息,包含电路ID和远程ID,用于标识客户端位置。
Circuit ID(电路ID)
- 通俗解释:Option 82的一部分,标识客户端连接的具体端口或接口。
- 技术说明:中继代理插入的标识符,用于标识客户端连接的物理或逻辑电路。
Remote ID(远程ID)
- 通俗解释:Option 82的一部分,标识中继设备本身。
- 技术说明:中继代理的标识符,用于标识插入Option 82的设备。
VSS(虚拟子网选择)
- 通俗解释:在共享网络中区分不同客户或租户的"标签"。
- 技术说明:Virtual Subnet Selection,允许在共享物理网络中为不同客户提供独立的逻辑子网。
VSS Type 0(ASCII VPN ID)
- 通俗解释:用文字标识虚拟网络,比如"客户A的网络"。
- 技术说明:使用ASCII字符串标识VPN的VSS类型。
VSS Type 1(RFC 2685 VPN-ID)
- 通俗解释:用数字标识虚拟网络,包含企业编号和VPN索引。
- 技术说明:使用OUI(3字节)和VPN索引(4字节)标识VPN的VSS类型。
VSS Type 255(全局默认VPN)
- 通俗解释:表示"默认网络",不区分虚拟网络。
- 技术说明:表示使用全局默认VPN配置。
DUID(DHCP唯一标识符)
- 通俗解释:DHCPv6中客户端的"身份证号",在整个网络中唯一。
- 技术说明:DHCP Unique Identifier,DHCPv6中唯一标识客户端或服务器的标识符。
DUID-LLT(基于链路层地址和时间的DUID)
- 通俗解释:用网卡MAC地址和生成时间组合成的唯一标识。
- 技术说明:DUID类型1,基于链路层地址和时间戳生成。
DUID-EN(基于企业编号的DUID)
- 通俗解释:企业自定义的唯一标识,包含企业编号和唯一值。
- 技术说明:DUID类型2,基于企业编号和唯一标识符生成。
DUID-LL(基于链路层地址的DUID)
- 通俗解释:直接用网卡MAC地址作为唯一标识。
- 技术说明:DUID类型3,基于链路层地址生成。
IA(身份关联)
- 通俗解释:客户端和服务器之间关于地址分配的"合同"。
- 技术说明:Identity Association,DHCPv6中客户端和服务器之间的一组相关地址或前缀的关联。
IA_NA(非临时地址关联)
- 通俗解释:用于获取普通的IPv6地址,可以长期使用。
- 技术说明:Identity Association for Non-temporary Addresses,用于获取非临时IPv6地址。
IA_TA(临时地址关联)
- 通俗解释:用于获取临时的IPv6地址,通常用于隐私保护。
- 技术说明:Identity Association for Temporary Addresses,用于获取临时IPv6地址。
IA_PD(前缀委派关联)
- 通俗解释:用于获取IPv6地址前缀,可以分配给下游网络使用。
- 技术说明:Identity Association for Prefix Delegation,用于获取IPv6前缀,通常用于路由器分配给下游网络。
IAID(身份关联标识符)
- 通俗解释:标识不同的IA,比如一个设备可以有多个IA,用IAID区分。
- 技术说明:Identity Association Identifier,唯一标识一个IA的4字节标识符。
前缀委派(Prefix Delegation)
- 通俗解释:运营商给家庭路由器分配一段IPv6地址,路由器再分配给家里的设备。
- 技术说明:DHCPv6的一种机制,允许路由器从上游获取IPv6前缀,然后分配给下游网络。
前缀组(Prefix Group)
- 通俗解释:VPP中给前缀起的"名字",用于管理和分配前缀。
- 技术说明:VPP中用于组织和过滤前缀的命名组。
有效生存时间(Valid Lifetime)
- 通俗解释:IPv6地址可以使用的总时长。
- 技术说明:IPv6地址或前缀保持有效状态的最大时间。
首选生存时间(Preferred Lifetime)
- 通俗解释:IPv6地址优先使用的时长,超过后虽然有效但不再优先使用。
- 技术说明:IPv6地址或前缀保持首选状态的最大时间。
事务ID(Transaction ID)
- 通俗解释:每次DHCP交互的"会话号",用于匹配请求和回复。
- 技术说明:DHCP消息中的随机数,用于关联请求和响应消息。
Magic Cookie(魔法Cookie)
- 通俗解释:DHCPv4报文中的固定值(99.130.83.99),用于标识这是DHCP消息。
- 技术说明:DHCPv4报文中的固定标识符,值为0x63825363。
广播标志(Broadcast Flag)
- 通俗解释:告诉服务器"请用广播方式回复我"。
- 技术说明:DHCPv4消息中的标志位,指示服务器使用广播方式回复。
GIADDR(网关IP地址)
- 通俗解释:中继设备在DHCPv4消息中填入的自己的IP地址,告诉服务器消息来自哪里。
- 技术说明:DHCPv4消息中的字段,中继代理填入自己的IP地址。
Hop Count(跳数)
- 通俗解释:DHCPv6中继消息经过的中继设备数量,防止无限循环。
- 技术说明:DHCPv6 Relay消息中的计数器,限制中继链的长度。
Link Address(链路地址)
- 通俗解释:DHCPv6中继消息中,标识客户端所在网络的地址。
- 技术说明:DHCPv6 Relay消息中的字段,标识客户端连接的链路地址。
Peer Address(对等地址)
- 通俗解释:DHCPv6中继消息中,标识客户端或上一级中继的地址。
- 技术说明:DHCPv6 Relay消息中的字段,标识消息来源的地址。
Cookie(Cookie机制)
- 通俗解释:VPP Proxy中用于防止重复回复的"标记"。
- 技术说明:VPP DHCP Proxy使用客户端MAC地址生成的标识符,用于跟踪和去重。
Rapid Commit(快速提交)
- 通俗解释:DHCPv6的"快速模式",两步完成地址分配,不用四步。
- 技术说明:DHCPv6的优化机制,允许两步完成地址分配(SOLICIT-REPLY)。
状态码(Status Code)
- 通俗解释:DHCPv6消息中的"结果说明",比如成功、失败、无地址可用等。
- 技术说明:DHCPv6消息中的选项,用于指示操作结果或错误信息。
优先级(Preference)
- 通俗解释:DHCPv6服务器在ADVERTISE中告诉客户端"我有多想为你服务"。
- 技术说明:DHCPv6服务器在ADVERTISE消息中设置的优先级值,客户端优先选择高优先级服务器。
VPP特定术语
VPP(Vector Packet Processing)
- 通俗解释:一个高性能的网络数据包处理框架,就像网络设备的"大脑"。
- 技术说明:思科开源的向量数据包处理平台,用于构建高性能网络功能。
插件(Plugin)
- 通俗解释:VPP的"功能模块",可以添加新功能而不修改核心代码。
- 技术说明:VPP的扩展机制,允许以插件形式添加新功能。
节点(Node)
- 通俗解释:VPP处理数据包的"工作站",每个节点负责一个处理步骤。
- 技术说明:VPP数据包处理的基本单元,每个节点执行特定的处理功能。
Feature Arc(特性弧)
- 通俗解释:数据包处理的"流水线",决定数据包经过哪些节点。
- 技术说明:VPP中定义数据包处理路径的机制,允许动态添加或移除处理节点。
控制平面(Control Plane)
- 通俗解释:负责"决策"的部分,比如配置管理、状态维护。
- 技术说明:网络设备中负责管理和控制的部分,处理配置、路由表更新等。
数据平面(Data Plane)
- 通俗解释:负责"干活"的部分,快速处理数据包转发。
- 技术说明:网络设备中负责数据包转发和处理的部分,追求高性能。
CP/DP分离(Control Plane/Data Plane Separation)
- 通俗解释:把"决策"和"干活"分开,可以独立优化和扩展。
- 技术说明:将控制逻辑和数据转发逻辑分离的设计,提高灵活性和性能。
FIB(转发信息库)
- 通俗解释:路由表的"数据库",存储如何转发数据包的信息。
- 技术说明:Forwarding Information Base,存储路由和转发信息的表。
VRF(虚拟路由转发)
- 通俗解释:在同一个设备上创建多个"独立的路由器",互不干扰。
- 技术说明:Virtual Routing and Forwarding,允许在同一设备上维护多个独立的路由表。
FIB索引(FIB Index)
- 通俗解释:标识是哪个路由表的"编号"。
- 技术说明:VPP内部用于标识特定FIB的索引值。
Table ID(表ID)
- 通俗解释:VRF的"外部名称",用户配置时使用的编号。
- 技术说明:用户可见的VRF标识符,与FIB索引不同。
接口索引(sw_if_index)
- 通俗解释:VPP给每个网络接口分配的"编号"。
- 技术说明:Software Interface Index,VPP内部用于标识网络接口的索引。
邻接(Adjacency)
- 通俗解释:下一跳的"地址簿",知道数据包要发给哪个MAC地址。
- 技术说明:存储下一跳MAC地址等信息的数据结构,用于L2转发。
广播邻接(Broadcast Adjacency)
- 通俗解释:用于发送广播数据包的"地址簿"。
- 技术说明:用于发送广播或组播数据包的邻接条目。
Worker线程(Worker Thread)
- 通俗解释:VPP中实际处理数据包的"工人线程"。
- 技术说明:VPP中负责数据包处理的工作线程,通常每个CPU核心一个。
向量化处理(Vector Processing)
- 通俗解释:一次处理多个数据包,提高效率。
- 技术说明:VPP的核心特性,批量处理数据包以提高性能。
零拷贝(Zero Copy)
- 通俗解释:数据包处理时不需要复制数据,直接操作,提高速度。
- 技术说明:避免数据包在内存中复制,直接操作原始数据的技术。
内存池(Memory Pool)
- 通俗解释:预先分配好的内存"仓库",需要时直接取,不用临时分配。
- 技术说明:预先分配的内存块集合,用于快速分配和释放内存。
API(应用程序接口)
- 通俗解释:程序之间"对话"的接口,外部程序通过API控制VPP。
- 技术说明:Application Programming Interface,定义程序间交互的接口。
CLI(命令行界面)
- 通俗解释:通过命令行"打字"来配置和管理VPP。
- 技术说明:Command Line Interface,通过命令行交互的用户界面。
事件(Event)
- 通俗解释:VPP中发生的"事情",比如DHCP获取到地址了,通知外部程序。
- 技术说明:VPP中用于通知外部程序状态变化的机制。
回调函数(Callback Function)
- 通俗解释:预先注册的"通知函数",特定事件发生时自动调用。
- 技术说明:在特定事件发生时被调用的函数指针。
前缀组(Prefix Group)
- 通俗解释:VPP DHCPv6中给前缀起的"名字",用于管理和分配。
- 技术说明:VPP中用于组织和过滤IPv6前缀的命名组。
客户端池(Client Pool)
- 通俗解释:存储所有DHCP客户端配置的"容器"。
- 技术说明:VPP中用于管理DHCP客户端实例的内存池。
待处理请求(Pending Request)
- 通俗解释:Proxy转发请求后,等待服务器回复时记录的"待办事项"。
- 技术说明:DHCP Proxy中用于跟踪已转发但未收到回复的请求。
Cookie机制(Cookie Mechanism)
- 通俗解释:Proxy用客户端MAC地址生成的"标记",防止重复回复。
- 技术说明:VPP DHCP Proxy使用客户端标识符生成唯一标记,用于请求跟踪。
网络术语
IP地址(IP Address)
- 通俗解释:网络设备的"门牌号",用于标识和定位设备。
- 技术说明:Internet Protocol Address,用于标识网络设备的数字标识符。
IPv4地址
- 通俗解释:32位的IP地址,比如192.168.1.1,目前最常用。
- 技术说明:32位长度的IP地址,通常用点分十进制表示。
IPv6地址
- 通俗解释:128位的IP地址,比如2001:db8::1,地址空间更大。
- 技术说明:128位长度的IP地址,通常用冒号分隔的十六进制表示。
子网掩码(Subnet Mask)
- 通俗解释:区分IP地址中"网络部分"和"主机部分"的"尺子"。
- 技术说明:用于划分IP地址中网络部分和主机部分的掩码。
前缀长度(Prefix Length)
- 通俗解释:IPv6中表示网络部分的位数,比如/64表示前64位是网络部分。
- 技术说明:CIDR表示法中,表示网络部分的位数。
网关(Gateway)
- 通俗解释:网络的"出口",数据包要离开本地网络时,先发给网关。
- 技术说明:连接不同网络的设备,通常是路由器的接口。
默认路由(Default Route)
- 通俗解释:不知道往哪走时,就走这条路,通常是网关。
- 技术说明:目标地址不在路由表中的数据包使用的默认路径。
DNS服务器(DNS Server)
- 通俗解释:网络的"电话簿",把域名(如www.example.com)转换成IP地址。
- 技术说明:Domain Name System Server,提供域名到IP地址的解析服务。
MAC地址(MAC Address)
- 通俗解释:网卡的"身份证号",全球唯一,用于局域网通信。
- 技术说明:Media Access Control Address,网络接口的硬件地址。
UDP(用户数据报协议)
- 通俗解释:一种"快速但不保证送达"的传输协议,DHCP使用它。
- 技术说明:User Datagram Protocol,无连接的传输层协议。
广播(Broadcast)
- 通俗解释:向网络中的所有设备"喊话",大家都听得到。
- 技术说明:向网络中所有设备发送数据包的方式。
组播(Multicast)
- 通俗解释:向网络中的特定"群组"发送消息。
- 技术说明:向一组特定设备发送数据包的方式。
单播(Unicast)
- 通俗解释:点对点通信,只发给一个设备。
- 技术说明:从一个设备发送到另一个特定设备的方式。
端口(Port)
- 通俗解释:网络服务的"门牌号",比如DHCP客户端用68端口,服务器用67端口。
- 技术说明:传输层协议中用于标识应用程序的数字。
DHCP客户端端口(Port 68)
- 通俗解释:DHCP客户端"听"消息的端口。
- 技术说明:UDP端口68,DHCP客户端使用的端口。
DHCP服务器端口(Port 67)
- 通俗解释:DHCP服务器"听"消息的端口。
- 技术说明:UDP端口67,DHCP服务器使用的端口。
DHCPv6客户端端口(Port 546)
- 通俗解释:DHCPv6客户端"听"消息的端口。
- 技术说明:UDP端口546,DHCPv6客户端使用的端口。
DHCPv6服务器端口(Port 547)
- 通俗解释:DHCPv6服务器"听"消息的端口。
- 技术说明:UDP端口547,DHCPv6服务器使用的端口。
VLAN(虚拟局域网)
- 通俗解释:在同一个物理网络上创建多个"逻辑网络",互相隔离。
- 技术说明:Virtual Local Area Network,在物理网络上创建逻辑网络。
VPN(虚拟专用网络)
- 通俗解释:在公共网络上创建"专用网络",就像挖了一条隧道。
- 技术说明:Virtual Private Network,在公共网络上创建专用网络连接。
路由表(Routing Table)
- 通俗解释:存储"怎么走"的表格,告诉数据包该往哪个方向发送。
- 技术说明:存储路由信息的数据结构,用于决定数据包转发路径。
ARP(地址解析协议)
- 通俗解释:把IP地址转换成MAC地址的"翻译器"。
- 技术说明:Address Resolution Protocol,用于将IP地址解析为MAC地址。
ND(邻居发现协议)
- 通俗解释:IPv6版本的ARP,用于发现邻居和解析地址。
- 技术说明:Neighbor Discovery Protocol,IPv6中用于发现邻居和解析地址的协议。
MTU(最大传输单元)
- 通俗解释:数据包能传输的"最大尺寸"。
- 技术说明:Maximum Transmission Unit,网络层可以传输的最大数据包大小。
DSCP(差分服务代码点)
- 通俗解释:IP数据包中的"优先级标签",用于服务质量控制。
- 技术说明:Differentiated Services Code Point,IP头中用于标识服务质量的字段。
重传(Retransmission)
- 通俗解释:发送消息后没收到回复,就"再发一次"。
- 技术说明:发送方在未收到确认时重新发送数据包。
指数退避(Exponential Backoff)
- 通俗解释:重传时,等待时间越来越长,比如1秒、2秒、4秒、8秒。
- 技术说明:重传算法,每次重传的等待时间按指数增长。
超时(Timeout)
- 通俗解释:等待回复的"最大时间",超过就认为失败了。
- 技术说明:等待响应或操作完成的最大时间限制。
缩略语列表
DHCP - Dynamic Host Configuration Protocol(动态主机配置协议)
- 通俗解释:自动分配IP地址和网络配置的协议。
DHCPv4 - DHCP version 4(DHCP版本4)
- 通俗解释:用于IPv4网络的DHCP协议。
DHCPv6 - DHCP version 6(DHCP版本6)
- 通俗解释:用于IPv6网络的DHCP协议。
DUID - DHCP Unique Identifier(DHCP唯一标识符)
- 通俗解释:DHCPv6中唯一标识客户端或服务器的标识符。
IA - Identity Association(身份关联)
- 通俗解释:DHCPv6中客户端和服务器之间关于地址分配的关联。
IA_NA - Identity Association for Non-temporary Addresses(非临时地址关联)
- 通俗解释:用于获取普通IPv6地址的关联类型。
IA_TA - Identity Association for Temporary Addresses(临时地址关联)
- 通俗解释:用于获取临时IPv6地址的关联类型。
IA_PD - Identity Association for Prefix Delegation(前缀委派关联)
- 通俗解释:用于获取IPv6前缀的关联类型。
IAID - Identity Association Identifier(身份关联标识符)
- 通俗解释:唯一标识一个IA的4字节标识符。
VSS - Virtual Subnet Selection(虚拟子网选择)
- 通俗解释:在共享网络中区分不同客户或租户的机制。
VRF - Virtual Routing and Forwarding(虚拟路由转发)
- 通俗解释:在同一设备上创建多个独立路由表的技术。
FIB - Forwarding Information Base(转发信息库)
- 通俗解释:存储路由和转发信息的数据库。
CP - Control Plane(控制平面)
- 通俗解释:负责管理和控制的网络设备部分。
DP - Data Plane(数据平面)
- 通俗解释:负责数据包转发和处理的网络设备部分。
VPP - Vector Packet Processing(向量数据包处理)
- 通俗解释:高性能网络数据包处理框架。
API - Application Programming Interface(应用程序接口)
- 通俗解释:程序之间交互的接口。
CLI - Command Line Interface(命令行界面)
- 通俗解释:通过命令行交互的用户界面。
UDP - User Datagram Protocol(用户数据报协议)
- 通俗解释:无连接的传输层协议。
IP - Internet Protocol(互联网协议)
- 通俗解释:网络层协议,用于数据包路由。
IPv4 - Internet Protocol version 4(互联网协议版本4)
- 通俗解释:32位的IP地址协议。
IPv6 - Internet Protocol version 6(互联网协议版本6)
- 通俗解释:128位的IP地址协议。
MAC - Media Access Control(媒体访问控制)
- 通俗解释:网络接口的硬件地址。
DNS - Domain Name System(域名系统)
- 通俗解释:将域名转换为IP地址的系统。
ARP - Address Resolution Protocol(地址解析协议)
- 通俗解释:将IP地址解析为MAC地址的协议。
ND - Neighbor Discovery(邻居发现)
- 通俗解释:IPv6中用于发现邻居和解析地址的协议。
MTU - Maximum Transmission Unit(最大传输单元)
- 通俗解释:网络层可以传输的最大数据包大小。
DSCP - Differentiated Services Code Point(差分服务代码点)
- 通俗解释:IP头中用于标识服务质量的字段。
CIDR - Classless Inter-Domain Routing(无类域间路由)
- 通俗解释:IP地址和子网掩码的表示方法。
OUI - Organizationally Unique Identifier(组织唯一标识符)
- 通俗解释:MAC地址或企业编号的前3字节,标识厂商或组织。
RFC - Request for Comments(请求评论)
- 通俗解释:互联网技术标准的文档系列。
BOOTP - Bootstrap Protocol(引导协议)
- 通俗解释:DHCP的前身,用于无盘工作站启动。
GIADDR - Gateway IP Address(网关IP地址)
- 通俗解释:DHCPv4消息中中继代理填入的IP地址字段。
T1 - Renewal Time(续约时间)
- 通俗解释:租约时间的50%,客户端开始续租的时间点。
T2 - Rebinding Time(重新绑定时间)
- 通俗解释:租约时间的87.5%,客户端开始重新绑定的时间点。
IRT - Initial Retransmission Time(初始重传时间)
- 通俗解释:DHCPv6中第一次重传的等待时间。
MRT - Maximum Retransmission Time(最大重传时间)
- 通俗解释:DHCPv6中重传等待时间的上限。
MRC - Maximum Retransmission Count(最大重传次数)
- 通俗解释:DHCPv6中允许的最大重传次数。
MRD - Maximum Retransmission Duration(最大重传持续时间)
- 通俗解释:DHCPv6中允许重传的总时间上限。
sw_if_index - Software Interface Index(软件接口索引)
- 通俗解释:VPP内部用于标识网络接口的索引值。
Table ID - Table Identifier(表标识符)
- 通俗解释:用户可见的VRF标识符。
FIB Index - Forwarding Information Base Index(转发信息库索引)
- 通俗解释:VPP内部用于标识特定FIB的索引值。
VLAN - Virtual Local Area Network(虚拟局域网)
- 通俗解释:在物理网络上创建逻辑网络的技术。
VPN - Virtual Private Network(虚拟专用网络)
- 通俗解释:在公共网络上创建专用网络连接的技术。
QoS - Quality of Service(服务质量)
- 通俗解释:网络中对不同流量提供不同服务质量的技术。
L2 - Layer 2(第二层)
- 通俗解释:数据链路层,处理MAC地址和帧转发。
L3 - Layer 3(第三层)
- 通俗解释:网络层,处理IP地址和路由。
DORA - Discover, Offer, Request, Acknowledge(发现、提供、请求、确认)
- 通俗解释:DHCPv4客户端获取地址的四个步骤。
NAK - Negative Acknowledgment(否定确认)
- 通俗解释:服务器拒绝客户端请求的DHCP消息。
ACK - Acknowledgment(确认)
- 通俗解释:服务器确认客户端请求的DHCP消息。
RELAY-FORW - Relay Forward(中继转发)
- 通俗解释:DHCPv6中继转发客户端消息给服务器。
RELAY-REPL - Relay Reply(中继回复)
- 通俗解释:DHCPv6中继转发服务器回复给客户端。
PD - Prefix Delegation(前缀委派)
- 通俗解释:DHCPv6中路由器获取IPv6前缀的机制。
Rapid Commit - Rapid Commit Option(快速提交选项)
- 通俗解释:DHCPv6中两步完成地址分配的优化机制。
Option 82 - Relay Agent Information Option(中继代理信息选项)
- 通俗解释:DHCPv4中中继代理插入的标识信息。
Option 53 - DHCP Message Type Option(DHCP消息类型选项)
- 通俗解释:标识DHCP消息类型的必需选项。
Option 54 - Server Identifier Option(服务器标识符选项)
- 通俗解释:标识DHCP服务器的IP地址。
Option 55 - Parameter Request List Option(参数请求列表选项)
- 通俗解释:客户端请求的配置参数列表。
Option 61 - Client Identifier Option(客户端标识符选项)
- 通俗解释:唯一标识DHCP客户端的选项。
CPE - Customer Premises Equipment(客户驻地设备)
- 通俗解释:安装在客户端的网络设备,如家庭路由器。
PE - Provider Edge(提供商边缘)
- 通俗解释:运营商网络中面向客户的边缘设备。
CE - Customer Edge(客户边缘)
- 通俗解释:客户网络中面向运营商的边缘设备。
WAN - Wide Area Network(广域网)
- 通俗解释:覆盖大范围地理区域的网络。
LAN - Local Area Network(局域网)
- 通俗解释:覆盖小范围地理区域的网络。
ISP - Internet Service Provider(互联网服务提供商)
- 通俗解释:提供互联网接入服务的公司。
CP/DP - Control Plane/Data Plane(控制平面/数据平面)
- 通俗解释:网络设备中管理和转发功能的分离。
第一部分:DHCP协议基础知识
1.1 DHCP协议概述(用“租房子”类比)
本小节目标:
先不碰任何代码,只用生活里的“租房系统”类比,把 DHCP 的场景、角色和大致流程在你脑子里“立起来”。
1.1.1 没有 DHCP 的世界:全靠手工写地址
先想象一个只有几台电脑的小公司,没有 DHCP,只能手工配置 IP:
- 每台电脑都要手动填:
- IP 地址(例如:192.168.1.10)
- 子网掩码(例如:255.255.255.0)
- 网关(例如:192.168.1.1)
- DNS(例如:8.8.8.8)
这就像你搬进一个小区,要自己跑去物业登记:门牌号、楼栋、单元、门禁卡、停车位……全靠人工。
会出现什么问题?
- 配错一个数字就上不了网
- 类比:门牌号写错,快递外卖全送不到
- 电脑一多,IP 容易重复冲突
- 类比:两个租客被安排到同一间房,人到门口才发现“撞房”
- 网络管理员要维护一大堆 Excel 表,繁琐又容易乱
结论:没有 DHCP,大规模网络基本玩不转。
1.1.2 DHCP 出现:自动“分房子”的网络管家
DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)做的事情非常朴素:
只要设备一插网线 / 连上 Wi‑Fi,
就自动拿到一份“网络身份证”和“上网说明书”,不需要任何手工配置。
继续用租房类比:
- DHCP 服务器:小区物业 / 房东
- DHCP 客户端:租客(电脑、手机、路由器……)
- IP 地址:房间号
- 子网掩码:这一栋楼如何划分房间
- 网关:小区大门 / 小区出口
- DNS:小区服务台(帮你把“商场名字”翻译成“具体地址”)
- 租约(Lease):租房合同,有开始时间和到期时间
所以,有 DHCP 和没 DHCP 的差别可以粗暴地理解成:
- 没有 DHCP:
你自己到处找房、选房、谈价格、签合同,全程手工。 - 有了 DHCP:
你只需要说一句“我要租房”,中介一条龙服务,自动给你安排好。
1.1.3 DHCP 的四步大流程:DORA(只讲直觉)
先记一个很重要的缩写:DORA,DHCPv4 的经典四步:
- D – Discover:发现
- O – Offer:提供
- R – Request:请求
- A – Acknowledge:确认
用一个超简化的 ASCII 剧本(左边是电脑,右边是 DHCP 服务器):
【电脑/租客】 【DHCP 服务器/房东】
| 1. 我想上网,有人给我 IP 吗? --> |
| |
| <-- 2. 这里有一个 IP 可以给你 |
| |
| 3. 我确认要用你给的这个 IP --> |
| |
| <-- 4. 好,这个 IP 正式归你一段时间 |
翻译成生活话:
- 第一步:租客大喊“有没有空房?”
- 第二步:房东说“有一间 501,要不要?”
- 第三步:租客说“我要 501。”
- 第四步:房东盖章“行,合同签了,501 归你。”
翻译成协议话:
- 第 1 步:客户端发 DHCP DISCOVER 广播
- 第 2 步:服务器回 DHCP OFFER
- 第 3 步:客户端发 DHCP REQUEST
- 第 4 步:服务器回 DHCP ACK
三个关键点先有个印象:
- 一开始客户端没有 IP,所以用广播“喊人”
- 服务器集中维护一个 IP 地址池,保证不会乱分、不会冲突
- IP 不是永久的,是有“租期”的,到点要续约(类似房租到期)
1.1.4 DHCP 协议在网络中的“站位”:谁和谁说话?
最简单的家用网络,经常就长这样:
┌────────┐ 网线 / Wi‑Fi ┌────────┐
│ 电脑 │ <--------------------------> │ 家用路由器 │
│ 客户端 │ │ DHCP服务器 │
└────────┘ └────────┘
- 你的电脑:DHCP 客户端
- 家用路由器:同时充当
- 上联到运营商的网关
- 下联家庭局域网的 DHCP 服务器
在企业 / 运营商里,常见架构会复杂很多:
- 很多二层/三层网络、很多 VRF、很多 VLAN
- 通常会有集中式 DHCP 服务器 + 各个网段上的 DHCP 中继(Relay)
这些复杂情况,会在第 1.4 小节再用生活场景展开。
1.1.5 DHCP 与 BOOTP 的关系(“老系统升级版”)
BOOTP 可以理解为 DHCP 的“老一辈”:
- 很早期用于给无盘工作站分配 IP 和启动信息
- 配置比较死板,不支持租约续期等灵活机制
DHCP 在 BOOTP 的基础上做了增强:
- 支持**租约(Lease)**概念,可以定期回收/续租 IP
- 支持更多选项(Options),可以携带更丰富的配置
- 兼容 BOOTP 报文格式,设计上是向后兼容 + 向前增强
可以简单理解成:
BOOTP 像老式纸质合同,
DHCP 像升级版的电子合同系统,
既能处理老客户,又更方便维护大规模新客户。
1.2 DHCPv4协议详解:IPv4 世界的“租房合同”
这一节专讲 IPv4 里的 DHCPv4,重点是:
- 消息类型:租房都有哪些动作?
- 报文大致长啥样:固定部分 + 选项部分
- DORA 流程细节:把四步走拆开讲清楚
1.2.1 DHCPv4 消息类型:一整套“租房动作”
继续用“新员工小李来公司上班”的故事。
-
DISCOVER(发现)
- 小李刚到公司,还没有工位,也没有工号。
- 他对着“办公室广播系统”大喊:
“有人能给我个工位吗?” - 技术上:
- 源 IP:0.0.0.0(自己还没 IP)
- 目的 IP:255.255.255.255(广播给所有人)
-
OFFER(提供)
- 人事部(DHCP 服务器)听到了,翻了翻空位表:
“3 楼 15 号工位空着,我可以给你。” - 技术上:
- 报文里填上准备给他的 IP(your_ip)
- 以及子网掩码、网关、DNS、租期等
- 人事部(DHCP 服务器)听到了,翻了翻空位表:
-
REQUEST(请求)
- 小李可能收到了多个部门提供的工位(多个 OFFER),
他选择其中一个,正式说:
“我要你提供的 3‑15 号工位,请帮我办正式入职。”
- 小李可能收到了多个部门提供的工位(多个 OFFER),
-
ACK(确认)
- 人事部最终在系统里把 3‑15 标记为“小李专属”,回一封:
“确认,3‑15 现在正式是你的工位,租期 1 年。” - 客户端拿到 ACK 后,真正把 IP 配到网卡上。
- 人事部最终在系统里把 3‑15 标记为“小李专属”,回一封:
-
NAK(否认)
- 人事部也可能说:
“对不起,这个位已经给别人了 / 你的请求不合法,不能给你。” - 客户端收到 NAK,会放弃当前 IP,重新开始 DISCOVER。
- 人事部也可能说:
-
RELEASE(释放)
- 小李辞职了,和人事说:
“我不用这个工位了,你可以重新安排给别人。” - 客户端主动告诉服务器:这个 IP 我不再使用。
- 小李辞职了,和人事说:
-
INFORM(信息通知)
- 有的电脑已经自己有 IP(手工配好的),
但还想问:
“能不能告诉我 DNS、NTP 之类的配置?” - 服务器就会只提供额外信息,不分配 IP。
- 有的电脑已经自己有 IP(手工配好的),
你可以这样记住这些动作:
DISCOVER/OFFER/REQUEST/ACK = 找房 → 看房 → 确认 → 签合同
NAK = 合同被拒绝
RELEASE = 主动退租
INFORM = 不租房,只想要小区服务信息
1.2.2 DHCPv4 报文结构:一张“标准表格 + 补充条款”
不讲任何比特位,只让你对整体结构有一张“脑内图”。
可以把 DHCPv4 报文抽象成:
┌────────────────────────────┐
│ 固定头部(固定字段) │ ← 谁、在干嘛、基本信息
├────────────────────────────┤
│ Options 区(选项/扩展信息)│ ← 类似合同后面的补充条款
└────────────────────────────┘
再细一点的示意(非真实长度,只帮助理解):
┌─────────┬─────────┬─────────┬─────────┐
│ opcode │ hw_type │ hw_len │ hops │
├─────────┴─────────┴─────────┴─────────┤
│ transaction_id(事务ID) │
├───────────────────────────────────────┤
│ seconds │ flags(含广播标志) │
├───────────────────────────────────────┤
│ client_ip(客户端当前 IP) │
├───────────────────────────────────────┤
│ your_ip(服务器给你的 IP) │
├───────────────────────────────────────┤
│ server_ip(服务器 IP) │
├───────────────────────────────────────┤
│ gateway_ip(中继/网关 IP) │
├───────────────────────────────────────┤
│ client_mac(客户端 MAC 等信息) │
├───────────────────────────────────────┤
│ ……更多固定字段…… │
├───────────────────────────────────────┤
│ magic cookie(魔法 Cookie) │
├───────────────────────────────────────┤
│ options[ ](选项区) │
└───────────────────────────────────────┘
几个关键字段,用生活话再解释一遍:
-
transaction_id(事务 ID)
- 类比:办业务时的流水号
- 用途:请求和应答可以“对号入座”,不会串台
-
flags 里的广播标志(broadcast flag)
- 0:请悄悄给我打电话(单播回复)
- 1:请在大厅广播叫我(广播回复)
- 有些老设备只能听广播,所以需要这个标志
-
client_ip / your_ip / server_ip / gateway_ip
- client_ip:你现在的地址(大多数情况下一开始是 0.0.0.0)
- your_ip:服务器打算分配给你的地址
- server_ip:房东/物业自己的地址
- gateway_ip:中继设备的地址(后面讲 Relay 时会用到)
Options 区就像合同后面的“附加条款”:
- 例如:
- 有没有停车位(网关地址)
- 是否包物业费(DNS、NTP 等)
- 合同有效期多久(租约时间)
在 DHCP 里,比较重要的选项有:
- Option 53:当前这条消息的类型(DISCOVER / OFFER / ACK …)
- Option 1:子网掩码
- Option 3:默认网关
- Option 6:DNS 服务器
- Option 51:租约时间
- Option 54:服务器标识
- Option 82:中继信息(后面讲 Relay 时重点说)
可以用一句话总结:
固定头部 = 谁在和谁说话 + 这是哪一笔业务
Options = 这笔业务的具体条件和细节
1.2.3 DORA 流程细化:再演一遍“新员工入职”
场景设定:
新来的员工“小李”第一天来公司,拿着笔记本插上工位网线。
- DISCOVER:小李大喊“有人有空工位吗?”
小李(电脑):
- 自己还没 IP,只能写 0.0.0.0
- 不知道 DHCP 服务器在哪,只能对整个网广播:
“谁能给我一个 IP?”
技术细节上:
- 源 IP:0.0.0.0
- 目的 IP:255.255.255.255(广播)
- 报文里包含:
- 自己的 MAC 地址
- 一个随机的 transaction_id(流水号)
- OFFER:服务器说“这里有一个 IP 给你”
DHCP 服务器:
- 查了一下自己的地址池,发现 192.168.1.100 空着
- 回信:“我可以给你 192.168.1.100,用 1 天,
网关是 192.168.1.1,DNS 是 8.8.8.8。”
- REQUEST:小李确认“就要你给的这个 IP”
小李:
- 有可能同时收到多个 OFFER(比如不同 DHCP 服务器)
- 他选择其中一个,说:
“我确认要用 192.168.1.100,
由某某服务器分配的这个 IP。”
- ACK:服务器盖章“IP 正式归你用一段时间”
DHCP 服务器:
- 把 192.168.1.100 标记为“已经租给小李”
- 回复 ACK:
“确认,这个 IP 属于你,租期 1 天。
超过这个时间记得来续约。”
客户端拿到 ACK 后会做两件事:
- 在操作系统里真正配置 IP / 掩码 / 网关 / DNS
- 记住租期、T1/T2 时间点,后续自动续约
后面的续约 / 重新绑定,你可以先只记印象:
- 租期过一半(T1)时,客户端向原服务器发 REQUEST 续约
- 如果原服务器没回应,接近租期结束(T2)时,再向所有服务器“求续约”
- 过了租期还续不上,客户端会认为 IP 不再可用,需要重新 DORA
1.3 DHCPv6协议详解:IPv6 世界的“多层小区管理”
这一节不讲代码实现,只希望你能直观感受到:
- DHCPv6 和 DHCPv4 “神似但不相同”
- 多了 DUID、IA_NA、IA_PD 等概念
- 特别适合“给路由器一整段地址,再由它自己管理”
1.3.1 DHCPv6 与 DHCPv4 的直观差异
从“小白视角”看,只要先记住这三点就够了:
-
地址空间不一样
- DHCPv4:管的是 32 位 IPv4 地址(192.168.x.x)
- DHCPv6:管的是 128 位 IPv6 地址(2001:db8::1234)
-
报文结构更“现代”,很多东西放在选项里
- DHCPv4:头部有固定字段 + Options
- DHCPv6:头部字段更少,很多信息通过各种 Option 表达
-
引入了 DUID / IA_NA / IA_PD 等“合同”概念
- 一个设备可以有多个“身份关联”(IA)
- 每个 IA 下面可以有多个地址或前缀
可以粗略理解成:
DHCPv4:给“单个房间”签合同
DHCPv6:既能给单个房间,也能给“一整层楼/一栋楼”签合同
1.3.2 DUID:客户端的“长期身份证”
在 DHCPv4 里,常常用 MAC 地址来标识客户端;
在 DHCPv6 里,引入了更抽象的 DUID(DHCP Unique Identifier):
- 类比:身份证号,不随网卡、接口等变化而轻易变化
- 服务器根据 DUID 来识别“你是谁”
常见 DUID 类型有:
- DUID‑LLT(基于链路层地址 + 时间)
- DUID‑EN(基于企业编号)
- DUID‑LL(只基于链路层地址)
你只要先有个印象:
DHCPv6 识别“客户端是谁”主要靠 DUID,而不仅仅是 MAC。
1.3.3 IA / IA_NA / IA_PD:不同“合同类型”
DHCPv6 把地址/前缀分配抽象成“身份关联”(IA,Identity Association):
- IA:一个“合同大类”,下面可以挂多个地址或前缀
- IA_NA(Non‑temporary Address):非临时地址关联
- 类比:你长期租住的一套房
- IA_TA(Temporary Address):临时地址关联
- 类比:短租的日租房
- IA_PD(Prefix Delegation):前缀委派关联
- 类比:你承包了一整层楼 / 一栋楼,再自己转租
在运营商 + 家用路由器场景里:
- 运营商通过 DHCPv6 给家庭路由器一个 前缀(IA_PD),
- 家庭路由器再把这个前缀“切片”,分配给家里不同网段和设备。
ASCII 小图感受一下“前缀委派”的层级:
运营商 DHCPv6 服务器
│ 分配前缀:2001:db8:1234::/48 (一大块)
▼
家庭路由器(DHCPv6 PD 客户端)
│ 切成多个 /64 前缀:每个网段一段
├─ 2001:db8:1234:1::/64 → 客厅 Wi‑Fi
├─ 2001:db8:1234:2::/64 → 书房有线
└─ 2001:db8:1234:3::/64 → IoT 设备
VPP 的 DHCPv6 插件在这块做了大量增强(前缀组、PD 客户端),
后面章节会结合源码细讲,这里只做概念预热。
1.3.4 DHCPv6 消息类型:和 DHCPv4 对应着记
不强背,只要知道大致“谁对应谁”:
-
SOLICIT
- 类似 DHCPv4 的 DISCOVER
- 客户端向周围“打招呼”:谁能给我 IPv6 地址/前缀?
-
ADVERTISE
- 类似 DHCPv4 的 OFFER
- 服务器说:“我可以提供哪些地址/前缀。”
-
REQUEST / RENEW / REBIND / RELEASE / DECLINE
- 语义和 DHCPv4 大致类似:请求、续约、重新绑定、释放、拒绝
-
CONFIRM
- 用来确认“我之前拿的这个 IPv6 地址在当前网络还合法吗?”
-
INFORMATION‑REQUEST
- 只想要“配置说明书”(DNS、NTP 等),不一定要 IP
-
RELAY‑FORW / RELAY‑REPL
- 中继消息,后面讲 Relay 时重点展开
你可以先记一条粗暴规则:
名字看起来差不多的,大部分语义也差不多,
区别主要在于:DHCPv6 可以玩前缀、玩多级结构。
1.3.5 DHCPv6 报文整体结构:再来一张“脑内图”
最简单的 DHCPv6 客户端消息头,可以简化成:
┌────────────────────────────┐
│ msg_type(消息类型) │
├────────────────────────────┤
│ transaction_id(事务ID) │
├────────────────────────────┤
│ options[ ](各种选项) │
└────────────────────────────┘
可以看到:
- 头部字段比 DHCPv4 少很多
- 绝大部分信息都通过 Options 表达
比如:
- CLIENTID 选项:携带 DUID
- IA_NA / IA_PD 选项:表示你在申请/续约哪些地址或前缀
- STATUS_CODE:告诉你这次操作成功还是失败
- PREFERENCE:服务器自己的“优先级”
和 DHCPv4 的对比总结一下:
- DHCPv4:头部负责很多事情,Options 是“附加条款”
- DHCPv6:头部极简,Options 才是“主角”
1.4 DHCP Relay(中继)机制:跨网段的“楼栋管理员”
这一节的目标:
让你能画出:客户端 → 中继 → 服务器 的“传话链”,
并搞清楚 GIADDR / RELAY‑FORW / RELAY‑REPL 这些词到底在干嘛。
1.4.1 为什么需要中继?——不可能每层楼都放一个物业总台
现实网络通常长这样:
- 一个园区 / 机房有很多不同网段(不同 VLAN、不同子网、不同 VRF)
- 真正的 DHCP 服务器往往只部署在一两个中心网段里
如果照最朴素的方式做:
- 每个网段都放一台 DHCP 服务器
- 你就会有:10、20 甚至更多台服务器要管,配置维护极其麻烦
更合理的做法是:
每个网段放“楼栋管理员”(DHCP Relay),
整个园区只放一台“总物业”(集中 DHCP 服务器)。
生活类比:
- 总物业处:集中 DHCP 服务器
- 每栋楼的楼栋管理员:DHCP Relay(中继)
- 租客:各个网段里的客户端
租客只需要去找楼栋管理员,
楼栋管理员负责把消息捎到物业处,再把结果带回来就行。
1.4.2 DHCPv4 Relay:改写“回信地址”的 GIADDR
在 DHCPv4 里,中继最关键的一个字段叫 GIADDR(Gateway IP Address)。
先看一个 ASCII 图(单层中继):
【客户端网段】 【中继/楼栋管理员】 【DHCP 服务器】
192.168.10.0/24 GIADDR=192.168.10.1 10.0.0.10
客户端 192.168.10.x
| Discover(广播) --> | |
| | 改写并转发给服务器 |
| | GIADDR = 192.168.10.1 |
| | -----------------------> |
| | |
| <-- 中继再转发 OFFER | <-- OFFER(给 GIADDR) |
用“寄信”来形容整个过程:
- 租客(客户端)写了一封“我要租房”的信(DHCP DISCOVER),
但只知道楼层公告栏,不知道物业具体地址,所以用广播。 - 楼栋管理员(中继)收到后:
- 在信里写上自己的地址:GIADDR = 192.168.10.1
相当于在信封上标明:“这封信来自 10 楼 1 号楼栋管理员” - 然后把信单播转发给总物业(DHCP 服务器)。
- 在信里写上自己的地址:GIADDR = 192.168.10.1
- 物业根据 GIADDR:
- 知道这是“10 楼这边的住户”,应该从对应地址池里分配 IP
- 回复 OFFER/ACK 等报文给 GIADDR(中继)
- 楼栋管理员再把结果转发回原租客(广播/单播都可以,看 flag 和实现)。
关键点:
- GIADDR = 中继在客户端那一侧的 IP
- 代表“请求来自哪个网段”
- 决定服务器选哪个地址池、用哪个网关等
- 服务器从不直接和客户端跨网段通信,
所有来往都经过中继这一跳。
1.4.3 Option 82:楼栋管理员在信里夹了一张“说明纸条”
仅有 GIADDR 还不够细,只告诉你来自哪个网段,
但很多运营商/大网希望知道:
- 来自哪根物理网线?
- 交换机上哪个端口?
- 这是哪个中继设备转发的?
于是有了 Option 82(Relay Agent Information Option):
中继在转发 DHCP 报文时,
会在 Options 里塞一张“说明纸条”,
详细写明“我是哪个楼栋管理员、租客是从哪个门进来的”。
Option 82 里面最常见的两个子信息:
- Circuit ID(电路 ID)
- 类比:某栋楼 3 楼 305 房门口的“弱电箱编号”
- 常用来表示:交换机端口、VLAN、接入点等信息
- Remote ID(远程 ID)
- 类比:楼栋管理员的工号/名字
- 常用来唯一标识这个中继设备本身
有了 Option 82,DHCP 服务器可以做很多“高级玩法”:
- 按物理端口/楼栋/房间精细计费
- 防止用户乱插线:某个端口只允许特定用户拿地址
- 故障排查时,可以快速定位是哪一根线、哪一个端口有问题
VPP 的 DHCP Proxy / Relay 实现里,会大量使用 Option 82 和 VSS,
第 6 章解析 Proxy 源码时会经常看到。
1.4.4 DHCPv6 Relay:用“套娃信封”来转发(RELAY‑FORW / RELAY‑REPL)
DHCPv6 的中继机制比 DHCPv4 更“现代”,
不是简单改字段,而是套一层壳,像“套娃信封”一样。
可以这样理解:
- 客户端写了一封信 M(原始 DHCPv6 消息)
- 中继不直接改 M,而是再拿一个大信封把它包起来:
Relay-Forward {
msg_type = RELAY-FORW
hop_count = 当前已经经过的中继层数
link-address = 客户端所在网段的地址
peer-address = 客户端或上一级中继的地址
options[ ] = 一些中继相关的选项(Interface-ID, Remote-ID, VSS 等)
relay-msg = M ← 原始客户端报文被塞在这里
}
服务器收到的是 Relay-Forward,
而不是直接的 SOLICIT/REQUEST:
- 服务器先看外面那层 Relay‑Forward,知道:
- 这封信来自哪个 link‑address(类似“哪一栋楼”)
- peer‑address 是谁(哪个中继发来的)
- 再从 relay‑msg 里取出原始客户端消息,按普通 DHCPv6 逻辑处理。
- 回复时,服务器构造一个 Relay‑Reply,结构类似,只是 msg_type 不同。
- 中继收到 Relay‑Reply 后,拆包,从里面取出服务器给客户端的真正回复,
再转发给客户端。
1.4.5 多级中继:像“多级中介链”
因为 DHCPv6 是“套娃式”的 Relay 设计,所以支持多级中继:
- 一个中继可以把客户端消息转发给上一级中继
- 上一级又可以再加一层 Relay‑Forward 外壳
形象一点的 ASCII 图:
客户端
M
│
▼
中继 A: Relay-Forward{ relay-msg = M }
│
▼
中继 B: Relay-Forward{ relay-msg = Relay-Forward{ relay-msg = M } }
│
▼
DHCPv6 服务器
每一层中继都可以:
- 在自己的那一层 options 里写上本层的标记(比如 Interface-ID、Remote-ID、VSS)
- 增加 hop_count,防止无限循环
当服务器回复时,Relay‑Reply 会和这条“套娃链”精确对应,
一路拆回去,直至回到最初的客户端。
对 VPP 源码的意义:
dhcp6_proxy_node.c里会有很多和 Relay‑Forward/Relay‑Reply 打交道的逻辑- VPP 需要正确处理:
- hop_count 限制
- link‑address / peer‑address 填写
- 多层中继场景下的封装/解封装
1.5 DHCP高级特性:Option 82 与 VSS 的“精细管理”
这一节重点是两个概念:
- Option 82:精确标记“租客从哪根线来的”
- VSS:在同一根物理网线后区分多个“虚拟小区/租户”
1.5.1 再看 Option 82:从“知道是这栋楼”到“精确到哪一扇门”
在 1.4 里我们说过:
- GIADDR 告诉你:来自哪一栋楼/哪一个网段
- 但在大运营商/大园区里,这还不够
运营场景中经常有这样的需求:
- 某个大楼共用一套接入网络
- 不同楼层/不同房间接的是同一台汇聚交换机
- 希望:
- 精确计费到“端口级”
- 某个端口出故障时能迅速定位
这时候 Option 82 就非常关键:
- Circuit ID:可以编码成“交换机编号 + 端口号 + VLAN”等
- Remote ID:可以编码成“接入设备的 MAC / 序列号 / 描述字符串”
举个更具体的例子:
- Circuit ID:
sw1/gi0/1-vlan100 - Remote ID:
access-switch-01
服务器一看 Option 82:
- 知道这是从
access-switch-01的gi0/1口来的 - 可以:
- 从对应的地址池中分配 IP
- 打上对应的计费/安全标签
1.5.2 VSS:同一走廊、不同公司——虚拟子网选择
VSS(Virtual Subnet Selection)解决的是多租户问题:
一根物理网线 / 一个物理广播域后面,
挂着多个“逻辑上完全不同的客户/租户”,
必须让 DHCP 服务器知道:这个租客属于哪个租户/哪张路由表。
生活场景:
- 一栋大型写字楼的同一层,可能有:
- A 公司、B 公司、C 公司……
- 走廊、消防通道、电梯间是共用的(共享物理网络)
- 但每家公司有:
- 各自的门禁、网络策略、网段
VSS 就像在 DHCP 报文中写上:
- “这个请求来自 A 公司”
- “这个请求来自 B 公司”
服务器根据 VSS:
- 把 A 公司的用户分到 A 公司的 VRF / 地址池
- 把 B 公司的用户分到 B 公司的 VRF / 地址池
VSS 的常见表示方式:
- Type 0:ASCII VPN ID
- 直接用字符串,比如:
"Company-A"、"Tenant-001"
- 直接用字符串,比如:
- Type 1:RFC 2685 VPN-ID
- 由 OUI(3 字节)+ VPN Index(4 字节)组成
- 更适合大规模、标准化的多租户场景
- Type 255:默认 VPN
- 表示“走默认的那套配置”
在 VPP 的实现里,你会看到:
dhcp_vss_t结构体,用来存储这些 VSS 信息dhcp_proxy_set_vss()等 API/CLI,用来配置 VSS- Proxy 在转发报文时,会根据 FIB/VRF 查找对应的 VSS,并填入 DHCP 选项里
1.5.3 小结:为什么这些高级特性对 VPP 很重要?
从“网络小白”的视角看,可以这样记:
- Relay(中继):让 DHCP 能跨网段工作,不用每个网段都放服务器
- GIADDR:告诉服务器“来自哪一个网段/哪一栋楼”
- Option 82:进一步告诉服务器“哪一根线、哪个端口、哪个管理员”
- VSS:在同一根线后面,区分不同租户/不同 VPN/不同 VRF
而 VPP 作为一个高性能、面向运营商/数据中心的转发平面:
- 会广泛用到这些机制:
- 既要高性能转发 DHCP 报文
- 又要“带着标签”精细计费、隔离、控制
- 后面在看 DHCP Proxy 和 DHCPv6 PD 客户端源码时,
你会不断看到:- GIADDR、Option 82 的插入和解析
- VSS 信息在数据结构和 API 中的体现
到这里,第 1 章的协议层逻辑已经用生活例子铺完,
接下来第 2 章开始,就会把视角慢慢切到:VPP 插件是怎么把这些逻辑落到代码里的。
第二部分:VPP DHCP插件功能概述
2.1 VPP DHCP插件总览
本小节目标:
在深入源码之前,先对 VPP DHCP 插件有个“整体画像”:
- 它是什么?在 VPP 里扮演什么角色?
- 谁在维护它?成熟度如何?
- 它有哪些“超能力”(API、CLI、多线程等)?
这一节不讲代码实现细节,只做“产品介绍”式的概念梳理。
2.1.1 VPP DHCP插件是什么?用“智能租房系统”类比
如果把 VPP 比作一个大型智能建筑管理系统,那么 DHCP 插件就是其中的**“自动分房系统”**:
┌─────────────────────────────────────────┐
│ VPP 智能建筑管理系统 │
│ ┌──────────┐ ┌──────────┐ ┌────────┐│
│ │ 路由系统 │ │ NAT系统 │ │ ACL系统 ││
│ └──────────┘ └──────────┘ └────────┘│
│ ┌─────────────────────────────────────┐ │
│ │ DHCP插件(自动分房系统) │ │
│ │ - 客户端:帮设备自动要房 │ │
│ │ - 代理:帮不同楼层的设备传话 │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘
VPP DHCP 插件不是独立的 DHCP 服务器软件(比如 ISC DHCP、dnsmasq),
而是一个嵌入在 VPP 路由器/交换机里的 DHCP 功能模块:
- 可以当客户端:VPP 设备自己通过 DHCP 获取 IP 地址
- 可以当中继/代理:帮其他设备转发 DHCP 请求到远程服务器
- 与路由功能深度集成:获取到地址后,自动更新路由表、FIB
类比到生活:
- 传统 DHCP 服务器:像独立的“房产中介公司”,专门做租房业务
- VPP DHCP 插件:像“智能楼宇系统里的分房模块”,和楼宇的其他功能(门禁、电梯、水电)紧密配合
2.1.2 插件基本信息:谁在维护?成熟度如何?
从 VPP 官方信息可以看到:
维护者(Maintainers):
- Dave Barach(dave@barachs.net)
- Neale Ranns(nranns@cisco.com)
这两个名字在 VPP 社区里都是资深贡献者,说明这个插件有专业团队在持续维护。
状态(State):production(生产级)
这意味着:
- ✅ 代码稳定,可以在生产环境使用
- ✅ 功能完整,不是实验性代码
- ✅ 有持续更新和 bug 修复
类比:
这不是“概念车”,而是已经量产上市、路上在跑的车。
功能清单(Features):
从官方描述可以看到,VPP DHCP 插件支持:
-
DHCP client (v4/v6)
- 支持 IPv4 和 IPv6 的客户端功能
- 类比:既能处理“老式小区”(IPv4),也能处理“新式小区”(IPv6)
-
DHCPv6 prefix delegation
- 支持 IPv6 前缀委派
- 类比:不仅能租“单间”,还能承包“整层楼”再转租
-
DHCP Proxy / Option 82
- 支持中继代理功能,并能插入 Option 82 信息
- 类比:不仅能当租客,还能当“楼栋管理员”,帮租客传话并打标签
2.1.3 插件特性:API、CLI、MULTITHREAD 意味着什么?
VPP DHCP 插件标注了三个重要特性:
1. API(应用程序接口)
这意味着:
- 外部程序可以通过编程接口控制 DHCP 插件
- 可以用 Python、C、Go 等语言写脚本自动化管理
- 支持事件通知:DHCP 获取到地址后,可以通知外部程序
生活类比:
传统 DHCP 服务器:只能通过配置文件改设置
VPP DHCP 插件:可以通过 API 编程控制,就像智能家居可以用手机 App 远程控制
2. CLI(命令行界面)
这意味着:
- 可以通过命令行直接配置和查询
- 不需要写代码,管理员敲命令就能用
示例(后面章节会详细讲):
# 配置 DHCP 客户端
set dhcp client intfc GigabitEthernet0/8/0 hostname my-router
# 查看 DHCP 客户端状态
show dhcp client
# 配置 DHCP 代理
set dhcp proxy server 10.0.0.1 src-address 192.168.1.1
生活类比:
CLI 就像“智能楼宇的控制面板”,管理员可以直接在上面操作,不需要写代码。
3. MULTITHREAD(多线程支持)
这意味着:
- 可以并行处理多个 DHCP 请求
- 充分利用多核 CPU,性能更高
- 不会因为一个请求卡住,影响其他请求
生活类比:
传统单线程 DHCP:像只有一个窗口的银行,排队很慢
VPP 多线程 DHCP:像有多个窗口的银行,可以同时服务多个客户
性能对比的直观感受:
传统 DHCP 服务器(单线程):
请求1 → [处理] → 完成
请求2 → [等待] → [处理] → 完成
请求3 → [等待] → [等待] → [处理] → 完成
VPP DHCP 插件(多线程):
请求1 → [Worker 1 处理] → 完成
请求2 → [Worker 2 处理] → 完成 } 同时进行
请求3 → [Worker 3 处理] → 完成
2.1.4 VPP DHCP插件在VPP生态系统中的位置
可以用一个简化的“功能地图”来理解:
┌─────────────────┐
│ 外部应用 │
│ (Python/C/Go) │
└────────┬────────┘
│ API
┌────────▼────────┐
│ VPP 控制平面 │
│ (配置/管理) │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌───────▼────────┐ ┌────────▼────────┐ ┌──────▼──────┐
│ DHCP 插件 │ │ 路由/FIB 模块 │ │ 接口管理 │
│ - 客户端 │ │ - 路由表 │ │ - 网卡 │
│ - 代理 │ │ - 转发表 │ │ - VLAN │
└───────┬────────┘ └────────┬────────┘ └──────┬──────┘
│ │ │
└────────────────────┼────────────────────┘
│
┌────────▼────────┐
│ VPP 数据平面 │
│ (高性能转发) │
└─────────────────┘
关键点:
- DHCP 插件不是孤立的,它和 VPP 的其他模块(路由、接口、FIB)深度集成
- 获取到 IP 地址后,自动更新路由表,不需要手动配置
- 支持多 VRF,可以在不同的虚拟路由表中独立运行
2.1.5 适用场景:VPP DHCP插件适合用在哪儿?
适合的场景:
-
边缘路由器/CPE设备
- 家庭网关、企业分支路由器
- 需要通过 DHCP 从运营商获取 WAN 口地址
- 同时作为 LAN 口的 DHCP 服务器(通过 Proxy 转发)
-
运营商PE设备
- 需要为大量客户提供 DHCP 中继服务
- 需要插入 Option 82 做用户识别和计费
- 需要支持 VSS 做多租户隔离
-
数据中心网关
- 需要高性能 DHCP 处理
- 需要与路由、NAT 等功能深度集成
不适合的场景:
- ❌ 需要完整的 DHCP 服务器功能(VPP 只支持客户端和代理,不支持服务器)
- ❌ 只需要简单的 DHCP 服务,不需要高性能(可以用 dnsmasq 等轻量级方案)
2.1.6 小结:2.1节你需要记住什么?
在进入源码细节之前,先建立这些整体印象:
-
VPP DHCP 插件 = 嵌入在路由器里的 DHCP 功能模块
- 不是独立服务器,而是 VPP 的一部分
- 与路由、FIB 等功能深度集成
-
成熟稳定,生产级代码
- 有专业团队维护
- 可以在生产环境使用
-
三大特性:API、CLI、多线程
- API:可以编程控制
- CLI:可以命令行操作
- 多线程:高性能并行处理
-
支持的功能
- DHCPv4/v6 客户端
- DHCPv6 前缀委派
- DHCP 代理(支持 Option 82、VSS)
下一节(2.2)会详细展开这些功能的具体能力,
但不涉及源码实现,只讲“它能做什么”,不讲“它怎么做的”。
2.2 VPP实现的DHCP功能
本小节目标:
详细展开 VPP DHCP 插件具体能做什么,不涉及源码实现细节。
用生活场景和功能对比,让你清楚理解:
- 客户端功能:VPP 设备自己如何获取 IP
- 代理功能:VPP 设备如何帮其他设备传话
- 特殊功能:安全检测等附加能力
2.2.1 DHCP客户端功能:VPP设备自己当"租客"
生活类比:
VPP DHCP 客户端功能,就是让 VPP 设备自己扮演"租客",通过 DHCP 协议自动获取 IP 地址和网络配置。
2.2.1.1 DHCPv4客户端:IPv4世界的"标准租客"
功能清单:
-
支持标准DHCP客户端流程(DORA)
- 完整实现 DISCOVER → OFFER → REQUEST → ACK 四步流程
- 自动处理重传、超时等异常情况
- 类比:标准的"找房→看房→确认→签合同"流程,一步不少
-
租约管理
- 自动跟踪租约到期时间
- 在 T1 时间点自动续约(RENEW)
- 如果续约失败,在 T2 时间点重新绑定(REBIND)
- 租约到期前自动处理,无需人工干预
- 类比:租房合同到期前,自动提醒续租,不用你操心
-
地址自动配置
- 获取到 IP 后,自动配置到接口
- 自动添加默认路由(指向网关)
- 自动配置 DNS 服务器
- 与 VPP 的 FIB(转发信息库)深度集成,配置立即生效
- 类比:拿到房间钥匙后,自动帮你办好门禁卡、停车位、快递地址登记
使用场景示例:
场景:家庭路由器通过 DHCP 从运营商获取 WAN 口 IP
┌─────────────┐ DHCPv4 ┌─────────────┐
│ VPP路由器 │ ←──────────────────→ │ 运营商DHCP │
│ (客户端) │ DISCOVER/OFFER/ │ 服务器 │
│ │ REQUEST/ACK │ │
└─────────────┘ └─────────────┘
│ │
│ 自动配置: │
│ - WAN口IP: 100.64.1.100 │
│ - 网关: 100.64.1.1 │
│ - DNS: 8.8.8.8 │
│ - 默认路由自动添加 │
▼
[配置完成,可以上网]
关键特性:
- ✅ 零配置:插上网线,自动获取 IP,无需手动设置
- ✅ 自动续约:租约快到期时自动续租,不会断网
- ✅ 与路由集成:获取到地址后,路由表自动更新,立即可以转发数据
2.2.1.2 DHCPv6客户端:IPv6世界的"高级租客"
DHCPv6 客户端功能更强大,支持两种"租房模式":
1. IA_NA(非临时地址关联)- "租单间"模式
- 获取普通的 IPv6 地址,用于日常通信
- 类比:租一套房自己住
- 支持多个地址同时获取(一个设备可以有多个 IPv6 地址)
2. IA_PD(前缀委派关联)- "承包整层楼"模式
- 获取 IPv6 地址前缀(比如
/48或/56) - 获取后可以分配给下游网络使用
- 类比:承包一整层楼,再转租给其他人
使用场景示例:
场景:运营商给家庭路由器分配 IPv6 前缀
┌─────────────┐ DHCPv6 PD ┌─────────────┐
│ 运营商服务器 │ ←──────────→ │ 家庭路由器 │
│ │ 分配前缀: │ (PD客户端) │
│ │ 2001:db8::/48 │ │
└─────────────┘ └──────┬───────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌───────▼──────┐ ┌────────▼────────┐ ┌──────▼──────┐
│ 客厅Wi-Fi │ │ 书房有线网络 │ │ IoT设备网 │
│ 2001:db8:1:: │ │ 2001:db8:2:: │ │ 2001:db8:3::│
└──────────────┘ └─────────────────┘ └─────────────┘
分离的控制平面和数据平面架构:
这是 VPP DHCPv6 客户端的一个重要特性:
- 数据平面(DP):负责实际的数据包收发、重传、超时处理
- 类比:负责"跑腿"的部门,处理具体事务
- 控制平面(CP):负责配置管理、地址应用、策略决策
- 类比:负责"决策"的部门,决定怎么用获取到的地址
为什么这样设计?
传统设计(CP和DP在一起):
配置 → [CP+DP混合] → 结果
问题:配置和转发逻辑耦合,不够灵活
VPP设计(CP和DP分离):
配置 → [CP决策] → [DP执行] → 结果
优势:
- DP可以独立使用(外部程序可以自己写CP)
- CP可以灵活替换(可以用VPP的CP,也可以用外部CP)
- 性能更好(DP专注转发,CP专注管理)
实际应用:
- VPP 自带完整的 CP+DP(开箱即用)
- 也可以只用 DP,外部程序通过 API 控制(高度灵活)
- 适合需要自定义 DHCP 逻辑的场景
2.2.1.3 DHCP客户端功能总结
DHCPv4客户端:
- ✅ 标准 DORA 流程
- ✅ 自动租约管理
- ✅ 自动地址和路由配置
DHCPv6客户端:
- ✅ IA_NA:获取普通 IPv6 地址
- ✅ IA_PD:获取 IPv6 前缀,支持前缀委派
- ✅ CP/DP 分离架构,灵活可扩展
共同特点:
- 零配置,插线即用
- 自动续约,不断网
- 与 VPP 路由系统深度集成
2.2.2 DHCP代理功能:VPP设备当"传话筒"
生活类比:
DHCP 代理功能,就是让 VPP 设备扮演"楼栋管理员"的角色,帮不同楼层的租客(客户端)向物业总部(DHCP 服务器)传话。
2.2.2.1 DHCPv4 Proxy:IPv4世界的"传话筒"
核心功能:
- 多服务器支持
- 可以配置多个 DHCP 服务器
- 客户端的 DISCOVER 请求会同时转发给所有服务器
- 只把第一个回复转发回客户端(避免重复)
- 类比:楼栋管理员同时给多个物业公司发消息,谁先回复就用谁的
工作流程示意:
客户端请求 → VPP Proxy → 服务器1
→ 服务器2 } 同时转发
→ 服务器3
服务器1回复 ← VPP Proxy ← 客户端
(第一个回复被转发,其他的被丢弃)
- Option 82处理
- 自动在客户端请求中插入 Option 82(中继代理信息)
- 包含 Circuit ID(电路ID)和 Remote ID(远程ID)
- 服务器可以根据这些信息识别客户端位置,做精确的地址分配
- 类比:楼栋管理员在租客的申请书上盖章,标注"来自3栋2单元"
Option 82的作用:
没有 Option 82:
客户端 → Proxy → 服务器
服务器只知道"有人要IP",不知道"从哪里来"
有 Option 82:
客户端 → Proxy → 服务器
[插入] Circuit ID: "3栋2单元"
Remote ID: "管理员001"
服务器知道"3栋2单元的管理员001转来的请求"
可以:
- 分配特定地址池的IP
- 做用户识别和计费
- 做安全策略控制
- VSS支持
- 支持虚拟子网选择(Virtual Subnet Selection)
- 可以在 Option 82 中插入 VSS 信息
- 用于多租户场景,区分不同客户/租户
- 类比:在同一栋楼里,区分A公司、B公司、C公司的租客
VSS应用场景:
物理网络:一根光纤
│
▼
┌──────────┐
│ VPP Proxy│
└────┬─────┘
│
┌────┼────┐
│ │ │
客户A 客户B 客户C
(VSS-A)(VSS-B)(VSS-C)
Proxy在转发时插入VSS信息:
- 客户A的请求 → VSS-A标签
- 客户B的请求 → VSS-B标签
- 客户C的请求 → VSS-C标签
服务器根据VSS标签,从不同地址池分配IP
2.2.2.2 DHCPv6 Proxy:IPv6世界的"套娃传话筒"
DHCPv6 Proxy 的功能类似,但实现方式更"现代":
- Relay功能
- 使用 RELAY-FORW 和 RELAY-REPL 消息
- 客户端消息被"套娃"封装在 Relay 消息里
- 支持多级中继(一个中继可以转发给另一个中继)
- 类比:用大信封套小信封,每层都可以加标签
Relay封装示意:
客户端原始消息:
SOLICIT { CLIENTID, IA_NA, ... }
VPP Proxy封装后发给服务器:
RELAY-FORW {
link-address: 2001:db8:1::1
peer-address: 2001:db8:1::2
options: {
Interface-ID: "GigabitEthernet0/8/0"
Remote-ID: "VPP-Router-001"
VSS: "Customer-A"
}
relay-msg: [原始SOLICIT消息] ← 套在这里
}
-
多服务器支持
- 和 DHCPv4 类似,支持配置多个服务器
- 同时转发,只回传第一个回复
-
VSS支持
- 同样支持 VSS,用于多租户场景
- 在 Relay 消息的选项中插入 VSS 信息
DHCPv6 Proxy的优势:
- ✅ 支持多级中继(更灵活的网络架构)
- ✅ 每级中继都可以添加自己的信息(更好的可追溯性)
- ✅ 标准的 DHCPv6 Relay 协议(兼容性好)
2.2.2.3 DHCP代理功能总结
DHCPv4 Proxy:
- ✅ 多服务器支持(冗余、负载分担)
- ✅ Option 82 自动插入(用户识别、计费)
- ✅ VSS 支持(多租户隔离)
DHCPv6 Proxy:
- ✅ 标准 Relay 功能(RELAY-FORW/REPL)
- ✅ 多服务器支持
- ✅ VSS 支持
- ✅ 支持多级中继
共同特点:
- 透明转发(客户端无感知)
- 自动添加位置信息(Option 82 / Relay选项)
- 支持多租户(VSS)
2.2.3 特殊功能:DHCP Client Detection(客户端检测)
生活类比:
这是一个安全功能,就像小区的"门禁系统",检测是否有"未授权的人"在冒充管理员。
功能说明:
-
检测接口上的DHCP客户端流量
- 监控指定接口上是否有设备在发送 DHCP 客户端消息(DISCOVER、REQUEST等)
- 如果检测到,说明这个接口上连接了 DHCP 客户端设备
- 类比:检测到有人在"敲门要房",说明这个端口有设备接入
-
作为安全特性
- 可以用于防止未授权的 DHCP 服务器
- 如果某个接口不应该有 DHCP 客户端(比如服务器端口),检测到客户端流量可以触发告警或阻断
- 类比:如果发现"不应该有租客的地方"有租客在敲门,可能是安全威胁
应用场景:
场景1:防止未授权DHCP服务器
┌──────────┐
│ 客户端 │ → 发送DISCOVER
└────┬─────┘
│
▼
┌──────────┐
│ VPP设备 │ → 检测到客户端流量
│ (检测) │ → 触发安全策略
└──────────┘
场景2:网络监控
- 监控哪些接口有设备接入
- 统计DHCP客户端数量
- 用于网络管理和故障排查
关键特性:
- ✅ 实时检测(不延迟)
- ✅ 可配置(可以启用/禁用)
- ✅ 可集成到安全策略(检测到后可以触发动作)
2.2.4 小结:VPP DHCP功能全景图
用一个"功能地图"总结 VPP DHCP 插件的所有能力:
┌─────────────────────┐
│ VPP DHCP 插件 │
└──────────┬──────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌───────▼────────┐ ┌────────▼────────┐ ┌───────▼────────┐
│ 客户端功能 │ │ 代理功能 │ │ 特殊功能 │
│ │ │ │ │ │
│ DHCPv4客户端 │ │ DHCPv4 Proxy │ │ 客户端检测 │
│ - DORA流程 │ │ - 多服务器 │ │ - 流量监控 │
│ - 租约管理 │ │ - Option 82 │ │ - 安全防护 │
│ - 自动配置 │ │ - VSS │ │ │
│ │ │ │ │ │
│ DHCPv6客户端 │ │ DHCPv6 Proxy │ │ │
│ - IA_NA │ │ - Relay功能 │ │ │
│ - IA_PD │ │ - 多服务器 │ │ │
│ - CP/DP分离 │ │ - VSS │ │ │
└────────────────┘ └─────────────────┘ └────────────────┘
功能覆盖:
- ✅ 客户端:VPP 设备自己获取 IP(v4/v6)
- ✅ 代理:帮其他设备转发 DHCP 请求(v4/v6)
- ✅ 安全:检测和防护未授权 DHCP 行为
适用场景:
- 边缘路由器:WAN 口用客户端,LAN 口用代理
- 运营商设备:大量代理转发,支持 Option 82 和 VSS
- 企业网关:客户端获取地址,代理服务内网
下一节(2.3)会讲解 VPP DHCP 插件的增强特性(高性能、多线程、深度集成等),
这些是 VPP 相比传统 DHCP 实现的优势所在。
2.3 VPP DHCP插件的增强特性
本小节目标:
讲解 VPP DHCP 插件相比传统 DHCP 实现的独特优势。
这些特性让 VPP DHCP 插件特别适合高性能网络设备和复杂网络场景。
用生活类比和性能对比,让你理解"为什么选 VPP DHCP"。
2.3.1 控制平面和数据平面分离设计(DHCPv6)
生活类比:
传统 DHCP 实现就像"小作坊",老板既管决策又管干活;
VPP DHCPv6 就像"大公司",有专门的"决策部门"(CP)和"执行部门"(DP)。
传统架构(CP和DP耦合):
┌─────────────────────────┐
│ 传统DHCP实现 │
│ ┌───────────────────┐ │
│ │ CP + DP 混在一起 │ │
│ │ 配置和转发耦合 │ │
│ └───────────────────┘ │
└─────────────────────────┘
问题:
- 配置逻辑和转发逻辑绑定
- 难以替换或扩展
- 性能优化受限
VPP架构(CP和DP分离):
┌─────────────────────────────────────┐
│ VPP DHCPv6 架构 │
│ ┌──────────────┐ ┌──────────────┐│
│ │ 控制平面(CP) │ │ 数据平面(DP) ││
│ │ - 配置管理 │ │ - 报文收发 ││
│ │ - 策略决策 │ │ - 重传处理 ││
│ │ - 地址应用 │ │ - 超时管理 ││
│ └──────┬────────┘ └──────┬───────┘│
│ │ API/事件 │ │
│ └─────────┬─────────┘ │
│ │ │
│ 通过API通信 │
└─────────────────────────────────────┘
优势:
- CP可以独立替换(用VPP的或外部的)
- DP可以独立使用(外部程序控制)
- 性能更好(DP专注转发)
- 架构更灵活
实际应用场景:
-
使用VPP自带的CP+DP(开箱即用)
- 适合大多数场景
- 配置简单,功能完整
-
只用DP,外部程序写CP(高度定制)
- 适合需要特殊逻辑的场景
- 比如:自定义地址分配策略、与外部系统集成
-
混合模式(灵活组合)
- 部分功能用VPP的CP
- 部分功能用外部CP
- 通过API协调
类比总结:
传统DHCP = 小作坊(老板什么都管)
VPP DHCP = 大公司(部门分工明确,可以灵活调整)
2.3.2 多线程支持:并行处理,性能倍增
生活类比:
传统单线程 DHCP 就像"只有一个窗口的银行",客户要排队;
VPP 多线程 DHCP 就像"有多个窗口的银行",可以同时服务多个客户。
性能对比:
传统单线程DHCP:
时间轴 →
┌─────────────────────────────────────┐
│ 请求1 [处理] │
│ 请求2 [等待] [处理] │
│ 请求3 [等待] [等待] [处理] │
│ 请求4 [等待] [等待] [等待] [处理] │
└─────────────────────────────────────┘
总耗时 = 4个请求的处理时间之和
VPP多线程DHCP(4个Worker线程):
时间轴 →
┌─────────────────────────────────────┐
│ Worker1: 请求1 [处理] │
│ Worker2: 请求2 [处理] │ } 同时进行
│ Worker3: 请求3 [处理] │
│ Worker4: 请求4 [处理] │
└─────────────────────────────────────┘
总耗时 ≈ 1个请求的处理时间
实际性能提升:
- 吞吐量:多线程可以线性提升(4核CPU ≈ 4倍性能)
- 延迟:单个请求的延迟不变,但整体吞吐量大幅提升
- 并发:可以同时处理数千个DHCP请求
适用场景:
- ✅ 高并发场景:运营商PE设备,同时处理大量客户请求
- ✅ 多核服务器:充分利用多核CPU资源
- ✅ 实时性要求:不能因为一个请求卡住,影响其他请求
2.3.3 高性能数据包处理:向量化+零拷贝
生活类比:
传统DHCP处理就像"一个一个搬箱子",效率低;
VPP DHCP处理就像"用传送带批量搬运",效率高。
VPP的核心优化技术:
- 向量化处理(Vector Processing)
- 一次处理一批数据包,而不是一个一个处理
- 类比:传送带一次运多个箱子,而不是一次一个
传统处理方式:
数据包1 → [处理] → 完成
数据包2 → [处理] → 完成
数据包3 → [处理] → 完成
效率:1个/次
VPP向量化处理:
[数据包1, 数据包2, 数据包3, ...] → [批量处理] → 全部完成
效率:N个/次(N通常是8-32)
- 零拷贝(Zero Copy)
- 数据包在内存中不复制,直接操作原始数据
- 类比:直接修改原文件,而不是复制一份再修改
传统方式(有拷贝):
原始数据包 → [复制] → 副本 → [处理] → 结果
问题:内存占用大,速度慢
VPP方式(零拷贝):
原始数据包 → [直接处理] → 结果
优势:内存占用小,速度快
性能数据(理论值):
- 吞吐量:VPP可以处理百万级数据包/秒
- 延迟:微秒级延迟(传统实现是毫秒级)
- CPU利用率:更高效,同样性能下CPU占用更低
2.3.4 与VPP FIB深度集成:自动路由更新
生活类比:
传统DHCP获取地址后,需要手动更新路由表,就像拿到房间钥匙后还要手动登记;
VPP DHCP获取地址后,自动更新路由表,就像拿到钥匙后系统自动登记。
传统方式的问题:
1. DHCP获取IP地址
2. 手动配置接口IP ← 需要人工操作
3. 手动添加默认路由 ← 容易出错
4. 手动更新路由表 ← 繁琐
5. 配置完成
问题:
- 步骤多,容易出错
- 配置不一致
- 需要脚本自动化
VPP方式(自动集成):
1. DHCP获取IP地址
2. 自动配置接口IP ← 自动完成
3. 自动添加默认路由 ← 自动完成
4. 自动更新FIB ← 自动完成
5. 配置完成,立即生效
优势:
- 零配置,零错误
- 配置一致
- 立即生效
FIB集成的具体表现:
- ✅ 地址配置:获取到IP后,自动配置到接口
- ✅ 路由添加:自动添加默认路由(指向网关)
- ✅ 邻接创建:自动创建ARP/ND邻接条目
- ✅ 立即生效:配置后立即可以转发数据包
实际应用:
场景:家庭路由器WAN口通过DHCP获取IP
时间线:
T0: 插上网线
T1: DHCP获取到IP (100.64.1.100)
T2: 自动配置接口IP ← VPP自动完成
T3: 自动添加默认路由 ← VPP自动完成
T4: 自动创建网关邻接 ← VPP自动完成
T5: 可以立即上网 ← 无需等待
传统方式可能需要:
- 写脚本监听DHCP事件
- 手动调用路由配置命令
- 容易出错或延迟
2.3.5 支持多VRF环境:网络隔离
生活类比:
VRF就像"在同一栋楼里,不同公司有独立的门禁系统和房间号系统";
VPP DHCP支持多VRF,就像"物业管理公司可以同时管理多个独立的公司"。
VRF概念:
物理设备:1台VPP路由器
│
┌─────┼─────┐
│ │ │
VRF-A VRF-B VRF-C
(公司A)(公司B)(公司C)
每个VRF:
- 独立的路由表
- 独立的地址空间
- 互相隔离
VPP DHCP的多VRF支持:
-
✅ 每个VRF独立的DHCP配置
- VRF-A可以有DHCP客户端
- VRF-B可以有DHCP代理
- VRF-C可以同时有客户端和代理
-
✅ 地址空间隔离
- VRF-A获取的IP不会和VRF-B冲突
- 每个VRF有独立的地址池
-
✅ 配置独立
- 每个VRF的DHCP配置互不影响
- 可以有不同的服务器、不同的策略
应用场景:
场景:运营商PE设备,服务多个客户
┌─────────────────────────────────┐
│ VPP PE设备 │
│ ┌──────────┐ ┌──────────┐ │
│ │ VRF-A │ │ VRF-B │ │
│ │ (客户A) │ │ (客户B) │ │
│ │ │ │ │ │
│ │ DHCP代理 │ │ DHCP代理 │ │
│ │ 服务器1 │ │ 服务器2 │ │
│ │ VSS-A │ │ VSS-B │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────┘
优势:
- 客户A和客户B完全隔离
- 可以有不同的DHCP服务器
- 可以有不同的地址池
- 配置互不影响
2.3.6 事件驱动的回调机制:实时通知
生活类比:
传统DHCP就像"定期查邮件",可能错过重要信息;
VPP DHCP的事件机制就像"重要事件立即推送通知",实时响应。
事件机制:
传统方式(轮询):
外部程序 → [定期查询] → DHCP状态
问题:
- 延迟(可能几分钟才查到)
- 浪费资源(频繁查询)
- 可能错过事件
VPP方式(事件驱动):
DHCP事件发生 → [立即通知] → 外部程序
优势:
- 实时(毫秒级)
- 高效(只在事件发生时通知)
- 不遗漏
支持的事件类型:
-
✅ DHCPv4客户端完成事件
- 获取到IP地址时触发
- 包含:IP地址、网关、DNS、租期等信息
-
✅ DHCPv6回复事件
- 收到服务器回复时触发
- 包含:地址/前缀、生命周期等信息
-
✅ 租约更新事件
- 续约成功/失败时触发
实际应用:
场景:外部监控系统需要知道DHCP状态
传统方式:
监控系统 → [每5分钟查询一次] → DHCP状态
问题:最多5分钟延迟
VPP事件方式:
DHCP获取IP → [立即发送事件] → 监控系统
优势:实时知道,延迟<1秒
应用:
- 网络监控系统
- 自动化运维脚本
- 计费系统
- 安全审计
2.3.7 灵活的API设计:支持外部控制平面
生活类比:
传统DHCP就像"固定菜单的餐厅",只能选已有的菜;
VPP DHCP的API就像"可以自己点菜的餐厅",灵活定制。
API能力:
-
配置API
- 添加/删除DHCP客户端
- 配置DHCP代理
- 设置VSS信息
-
查询API
- 查询客户端状态
- 查询代理配置
- 导出所有配置
-
事件API
- 订阅DHCP事件
- 接收实时通知
-
数据平面API(DHCPv6)
- 外部程序可以直接控制DP
- 实现自定义的CP逻辑
API使用示例(概念性):
# Python示例(概念性,非真实代码)
# 1. 配置DHCP客户端
dhcp_client_config(
interface="GigabitEthernet0/8/0",
hostname="my-router"
)
# 2. 订阅事件
subscribe_dhcp_events(callback=my_handler)
# 3. 事件回调函数
def my_handler(event):
if event.type == "DHCP_COMPLETE":
print(f"获取到IP: {event.ip_address}")
# 可以触发其他操作,比如更新配置、发送通知等
API的优势:
- ✅ 编程控制:可以用Python、C、Go等语言
- ✅ 自动化:可以写脚本自动化管理
- ✅ 集成:可以与其他系统集成
- ✅ 灵活:可以实现自定义逻辑
2.3.8 小结:VPP DHCP增强特性全景
用一个"特性地图"总结:
┌─────────────────────┐
│ VPP DHCP 增强特性 │
└──────────┬──────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌───────▼────────┐ ┌────────▼────────┐ ┌───────▼────────┐
│ 架构特性 │ │ 性能特性 │ │ 集成特性 │
│ │ │ │ │ │
│ CP/DP分离 │ │ 多线程支持 │ │ FIB深度集成 │
│ - 灵活可扩展 │ │ - 并行处理 │ │ - 自动路由更新 │
│ - 外部CP支持 │ │ - 高并发 │ │ - 立即生效 │
│ │ │ │ │ │
│ │ │ 向量化处理 │ │ 多VRF支持 │
│ │ │ - 批量处理 │ │ - 网络隔离 │
│ │ │ │ │ - 独立配置 │
│ │ │ 零拷贝技术 │ │ │
│ │ │ - 高性能 │ │ 事件驱动 │
│ │ │ - 低延迟 │ │ - 实时通知 │
│ │ │ │ │ │
│ 灵活API │ │ │ │ │
│ - 编程控制 │ │ │ │ │
│ - 自动化 │ │ │ │ │
└────────────────┘ └─────────────────┘ └────────────────┘
核心优势总结:
- 架构灵活:CP/DP分离,可以定制和扩展
- 性能卓越:多线程+向量化+零拷贝,处理能力强
- 深度集成:与VPP路由系统无缝集成,自动配置
- 功能丰富:多VRF、事件驱动、灵活API
2.4 与标准DHCP实现的对比
本小节目标:
通过对比,让你清楚理解 VPP DHCP 插件的定位和优势。
知道"什么时候用 VPP DHCP,什么时候用传统 DHCP"。
2.4.1 VPP DHCP的优势
2.4.1.1 高性能数据平面
对比数据(理论值):
传统DHCP服务器(ISC DHCP):
- 吞吐量:数千请求/秒
- 延迟:毫秒级
- CPU:单线程,利用率低
VPP DHCP插件:
- 吞吐量:百万级数据包/秒
- 延迟:微秒级
- CPU:多线程,利用率高
性能优势的原因:
- 向量化处理:一次处理多个数据包
- 零拷贝:减少内存操作
- 多线程:充分利用多核CPU
- 优化的数据结构:针对高性能设计
适用场景:
- ✅ 高并发场景:运营商PE设备,同时处理大量请求
- ✅ 低延迟要求:实时性要求高的场景
- ✅ 资源受限:需要高效利用CPU和内存
2.4.1.2 与路由器功能深度集成
传统DHCP服务器的问题:
传统架构:
┌──────────┐ ┌──────────┐
│ DHCP服务器│ │ 路由器 │
└────┬──────┘ └────┬─────┘
│ │
│ 获取IP后需要 │
│ 手动配置路由 │
└─────────┬───────────┘
│
需要额外脚本/工具
VPP DHCP的优势:
VPP架构:
┌──────────────────────┐
│ VPP 统一平台 │
│ ┌────────┐ ┌──────┐│
│ │ DHCP │ │ FIB ││
│ │ 插件 │ │路由表││
│ └───┬────┘ └───┬──┘│
│ │ 自动集成 │ │
│ └─────┬─────┘ │
│ │ │
│ 无需额外配置 │
└──────────────────────┘
集成优势:
- ✅ 自动路由更新:获取IP后自动更新路由表
- ✅ 统一管理:DHCP和路由在一个平台管理
- ✅ 配置一致:避免配置不一致的问题
- ✅ 立即生效:配置后立即可以转发数据
2.4.1.3 灵活的架构设计
对比:
传统DHCP服务器:
- 固定架构,难以扩展
- 配置文件和代码耦合
- 难以定制
VPP DHCP插件:
- CP/DP分离,可以替换CP
- API驱动,可以编程控制
- 可以集成到自定义系统
灵活性体现:
- CP/DP分离:可以用外部CP,实现自定义逻辑
- API丰富:可以通过API完全控制
- 事件驱动:可以实时响应状态变化
- 模块化:可以只使用需要的功能
2.4.2 与传统DHCP服务器的区别
2.4.2.1 功能定位不同
传统DHCP服务器(如ISC DHCP、dnsmasq):
- ✅ 完整的DHCP服务器功能
- 维护IP地址池
- 分配IP地址给客户端
- 管理租约
- ✅ 独立运行
- 可以单独部署
- 不依赖其他系统
- ✅ 配置灵活
- 丰富的配置文件选项
- 支持复杂的地址分配策略
VPP DHCP插件:
- ✅ 客户端和代理功能
- 可以当客户端获取IP
- 可以当代理转发请求
- ❌ 不支持服务器功能(不分配IP给客户端)
- ✅ 嵌入在VPP中
- 是VPP的一部分
- 与路由等功能集成
- ✅ 高性能
- 针对高性能网络设备优化
- 适合高并发场景
对比总结:
传统DHCP服务器:
角色:独立的"房产中介公司"
功能:完整的服务器功能
适用:需要分配IP的场景
VPP DHCP插件:
角色:路由器里的"分房模块"
功能:客户端+代理
适用:路由器/交换机设备
2.4.2.2 使用场景对比
什么时候用传统DHCP服务器?
- ✅ 需要完整的DHCP服务器功能
- 比如:企业内网需要分配IP给员工电脑
- 比如:Wi-Fi热点需要分配IP给用户设备
- ✅ 独立部署
- 不需要与路由器深度集成
- 只需要简单的DHCP服务
- ✅ 配置复杂
- 需要复杂的地址分配策略
- 需要丰富的配置选项
什么时候用VPP DHCP插件?
- ✅ 路由器/交换机设备
- 设备本身需要通过DHCP获取WAN口IP
- 设备需要做DHCP中继/代理
- ✅ 高性能要求
- 需要处理大量并发请求
- 需要低延迟
- ✅ 与路由功能集成
- 获取IP后需要自动更新路由
- 需要与VRF、FIB等功能配合
- ✅ 运营商/企业网关场景
- 需要Option 82、VSS等高级功能
- 需要多租户支持
2.4.3 适用场景和限制
2.4.3.1 适用场景总结
1. 边缘路由器/CPE设备
场景:
- WAN口:通过DHCP从运营商获取IP(客户端功能)
- LAN口:通过DHCP代理服务内网设备(代理功能)
优势:
- 自动获取WAN口IP
- 自动更新路由表
- 高性能代理转发
2. 运营商PE设备
场景:
- 为大量客户提供DHCP中继服务
- 需要插入Option 82做用户识别
- 需要VSS做多租户隔离
优势:
- 高性能,支持高并发
- 多VRF支持
- Option 82和VSS支持
3. 数据中心网关
场景:
- 需要高性能DHCP处理
- 需要与路由、NAT等功能集成
- 需要自动化管理
优势:
- 高性能数据平面
- 与VPP其他功能集成
- API支持自动化
2.4.3.2 限制和注意事项
功能限制:
- ❌ 不支持DHCP服务器功能
- 不能分配IP给客户端
- 如果需要服务器功能,需要配合传统DHCP服务器使用
使用限制:
-
⚠️ 需要VPP环境
- 必须在VPP平台上运行
- 不能独立部署
-
⚠️ 学习曲线
- 需要了解VPP的概念(VRF、FIB等)
- 配置方式与传统DHCP服务器不同
最佳实践:
推荐组合:
VPP DHCP插件(客户端+代理)
+
传统DHCP服务器(ISC DHCP/dnsmasq)
=
完整的DHCP解决方案
VPP DHCP插件:
- 路由器自己获取IP(客户端)
- 转发客户端请求到服务器(代理)
传统DHCP服务器:
- 实际分配IP给客户端(服务器)
2.4.4 小结:选择合适的DHCP方案
决策树:
需要DHCP功能?
│
├─ 需要分配IP给客户端?
│ │
│ ├─ 是 → 用传统DHCP服务器(ISC DHCP/dnsmasq)
│ │
│ └─ 否 → 继续
│
├─ 路由器/交换机设备?
│ │
│ ├─ 是 → 考虑VPP DHCP插件
│ │
│ └─ 否 → 继续
│
├─ 需要高性能?
│ │
│ ├─ 是 → VPP DHCP插件
│ │
│ └─ 否 → 传统DHCP服务器
│
└─ 需要与路由功能集成?
│
├─ 是 → VPP DHCP插件
│
└─ 否 → 传统DHCP服务器
总结:
- VPP DHCP插件:适合路由器/交换机设备,需要高性能和深度集成
- 传统DHCP服务器:适合需要完整服务器功能的场景
- 组合使用:VPP DHCP插件(客户端+代理)+ 传统DHCP服务器(服务器)= 最佳方案
2.5 为什么VPP没有实现DHCP服务端?——设计取舍的思考
这一小节单独回答一个常见疑问:
“VPP 这么强,为什么不顺手把 DHCP Server 也实现了?”
核心结论:不是做不到,而是“性价比不高 + 角色不匹配”,刻意不做。
2.5.1 VPP 的角色:高速公路,而不是业务大厅
从定位上看:
- VPP 的核心价值:高性能 L2/L3/L4 数据平面
- 向量化、零拷贝、多线程
- 适合做:转发、NAT、VPN、ACL 等“流水线式”处理
- DHCP 服务器的核心工作:长周期状态管理 + 策略 + 存储
- 维护 IP 地址池、租约表
- 处理租约冲突、保留地址、策略分配
- 做持久化(断电不能丢租约)、HA 同步、统计计费
类比:
- VPP 更像修高速公路、做红绿灯控制的部门
- DHCP Server 更像开业务大厅、办证、管档案的部门
这两类工作都重要,但不一定非要放在同一个进程/框架里。
2.5.2 DHCP服务端已经有成熟生态,VPP没必要重复造轮子
业界已经有大量成熟的 DHCP 服务器实现,例如:
ISC DHCP、Keadnsmasq- 各种厂商自带的 DHCP 服务器、Windows DHCP 等
这些软件:
- 已经实现了复杂的功能:
- 静态绑定、保留地址、多地址池
- 高可用、故障切换、数据库后端
- 丰富的策略和脚本扩展
- 有大量部署经验和运维工具
对 VPP 社区来说:
- 如果在 VPP 里再做一个“可生产级”的 DHCP Server:
- 代码量大,维护成本高
- 功能要追平现有实现并不容易
- 而和“用现成 DHCP Server + VPP Proxy”相比,增量价值有限
所以更合理的方案是:
┌──────────────────────┐
│ 现有DHCP服务器 │
│ (ISC/Kea/dnsmasq…) │
└─────────┬────────────┘
│
│ 普通IP网络 / 本地Socket
│
┌─────────▼────────────┐
│ VPP 数据平面 │
│ + DHCP Proxy/CP │
└──────────────────────┘
VPP 专注:高速转发 + Proxy/Relay + Option82/VSS + 多VRF
DHCP Server 专注:地址池管理 + 租约持久化 + 策略 + HA
2.5.3 架构上“能不能做”和“值不值得做”是两回事
从纯技术角度看,在 VPP 里实现 DHCP Server 当然能做:
- 在插件里维护地址池和租约表
- 收发 DHCP 报文,完成 DORA/RENEW/REBIND 等流程
- 把租约信息持久化到文件或外部存储
但是要做到生产可用,需要额外考虑很多事情:
- 租约持久化格式设计、兼容升级
- 高可用/主备同步协议
- 管理面接口(复杂的 CLI/API/配置文件)
- 和现有 DHCP 生态(Kea/ISC 等)的兼容策略
这些工作本质上都是“控制平面 + 存储系统”的工程,
而 VPP 本身是一个倾向于“无状态高速流水线”的数据面框架。
所以从架构取舍上,社区更倾向于:
- 在 VPP 里只做“和数据面强相关的部分”:
- 客户端(自己要 IP)
- 代理/Relay(帮别人转发,插入 Option82/VSS)
- 把“重控制平面 + 状态管理”的部分交给外部进程:
- 独立 DHCP Server 进程
- 或集成在上层控制平面(自研 CP、SDN 控制器等)中
2.5.4 VPP常见推荐架构:Proxy + 外部Server
在真实部署中,常见的推荐组合是:
┌────────────────────────────────┐
│ 控制平面/服务进程 │
│ - 路由协议/业务逻辑 │
│ - DHCP Server (Kea/dnsmasq) │
└───────────┬────────────────────┘
│
普通IP/本地通信│
│
┌───────────▼───────────────────┐
│ VPP 数据平面 │
│ - DHCP Client (v4/v6) │
│ - DHCP Proxy/Relay │
│ - NAT/ACL/路由/... │
└──────────────────────────────┘
这样做的好处:
- 各司其职:
- VPP 做擅长的高性能转发
- 外部进程做擅长的状态管理和复杂逻辑
- 更利于运维:
- DHCP 配置可以沿用原有工具/经验
- VPP 升级和 DHCP Server 升级可以解耦
2.5.5 小结:不是“做不到”,而是“没必要硬塞进来”
综合来看,VPP 没有实现 DHCP 服务端的原因主要是:
- 定位不同:VPP 是数据平面框架,DHCP Server 是重控制平面组件\n2. 生态已有成熟实现:Kea/ISC/dnsmasq 等已经很好用\n3. 工程性价比不高:要做成生产级 Server 成本很高,收益有限\n4. 更合理的分层:VPP 负责 Client/Proxy,高度优化数据面,Server 交给外部进程\n\n所以,可以把这句话记在心里:\n\n> “VPP 不是做不到 DHCP Server,而是它有意保持自己是一个高性能数据面,不追求在内部塞进所有控制平面业务逻辑。”\n\n在后面的源码分析中,可以留意 DHCP 插件是如何把自己控制在“数据面 + 轻量CP”范围内的,例如:\n- 客户端如何只做必要的租约管理\n- Proxy 如何只做转发和 Option82/VSS 插入\n- 复杂策略和持久化全部交给外部世界处理。\n*** End Patch】"}}
第二部分总结:
通过2.1-2.5小节,我们了解了:
- VPP DHCP插件是什么:嵌入在VPP中的DHCP功能模块
- 它能做什么:客户端功能、代理功能、特殊功能
- 它的优势:高性能、深度集成、灵活架构
- 什么时候用它:路由器/交换机设备,需要高性能和集成
下一部分(第三部分)将深入源码结构,了解这些功能是如何组织实现的。
第三部分:源码目录结构与模块划分
本部分从工程视角看 VPP DHCP 插件的源码布局:
- 第 1、2 部分更多讲“协议和功能是什么”;
- 第 3 部分开始讲“这些功能在代码里是怎么分层、怎么拆文件的”;
- 本章先不深挖每个函数,只讲哪个文件大致负责什么、彼此是什么关系。
3.1 源码文件总览:从目录结构看“谁管哪块儿”
先把目录结构再看一眼,心里有个整体图:
src/plugins/dhcp/
├── CMakeLists.txt # 构建配置
├── FEATURE.yaml # 功能描述 / 插件元信息
├── dhcp.api # DHCP通用API定义
├── client.h / client.c # DHCPv4客户端
├── dhcp_api.c # API实现(dhcp.api)
├── dhcp_test.c # DHCP API测试
├── dhcp_client_detect.c # DHCP客户端检测
├── dhcp_proxy.h / dhcp_proxy.c # DHCP代理核心(v4/v6共用)
├── dhcp4_packet.h / dhcp4_packet.c # DHCPv4报文结构/格式化
├── dhcp4_proxy_node.c # DHCPv4代理数据平面节点
├── dhcp4_proxy_error.def # DHCPv4代理错误码定义
├── dhcp6_packet.h # DHCPv6报文结构/选项
├── dhcp6_proxy_node.c # DHCPv6代理数据平面节点
├── dhcp6_proxy_error.def # DHCPv6代理错误码定义
├── dhcp6_client_common_dp.h/.c # DHCPv6客户端公共数据平面逻辑
├── dhcp6_ia_na_client_cp.h/.c # DHCPv6 IA_NA控制平面
├── dhcp6_ia_na_client_dp.h/.c # DHCPv6 IA_NA数据平面
├── dhcp6_ia_na_client_cp.api # DHCPv6 IA_NA控制平面API定义
├── dhcp6_ia_na_client_cp_api.c # DHCPv6 IA_NA控制平面API实现
├── dhcp6_pd_client_cp.h/.c # DHCPv6 PD控制平面
├── dhcp6_pd_client_dp.h/.c # DHCPv6 PD数据平面
├── dhcp6_pd_client_cp.api # DHCPv6 PD控制平面API定义
├── dhcp6_pd_client_cp_api.c # DHCPv6 PD控制平面API实现
└── dhcp6_pd_doc.rst # DHCPv6 PD设计/使用文档
下面用“翻译成人话”的方式,大致介绍一下每一组文件负责的角色。
详细的功能/状态机/算法,会在 3.2 和后续专章里细讲。
3.1.1 构建入口和插件元信息:CMakeLists.txt / FEATURE.yaml
-
CMakeLists.txt:- 告诉 VPP 构建系统:
- 这个插件的名字叫
dhcp; - 要编译哪些
.c文件; - 哪些
.api文件需要做 API 代码生成; - 哪些
.h要被“安装”为公共头文件,给其他模块引用。
- 这个插件的名字叫
- 通俗讲:“DHCP 插件的工程清单+Makefile 入口”。
- 和其他插件(比如 NAT、ACL)结构基本一致,用的是统一的
add_vpp_plugin(...)宏。
- 告诉 VPP 构建系统:
-
FEATURE.yaml:- 给 VPP 插件管理系统看的一个“名片”:
- 名字(Dynamic Host Configuration Protocol);
- 维护者是谁(Dave/Neale 等);
- 有哪些特性(v4/v6 客户端、PD、Proxy/Option82);
- 当前状态(production);
- 属性(API、CLI、MULTITHREAD)。
- 方便:
- 在
show plugin/ 文档系统里展示信息; - 自动生成特性列表、文档索引等。
- 在
- 给 VPP 插件管理系统看的一个“名片”:
这两个文件本身不含协议逻辑,但决定了这个插件如何被 VPP 编译和识别。
3.1.2 顶层API:dhcp.api / dhcp_api.c
-
dhcp.api:- 用 VPP 的
.api语言定义和 DHCP 插件相关的主控制面消息:- 配置 DHCPv4/v6 Proxy;
- 配置 DHCPv4 客户端;
- 订阅 DHCPv6 事件(reply / PD reply);
- 控制 ping / 版本查询等。
- VPP 的 API 生成器会:
- 把这个文件编译成 C 结构、编解码函数;
- 生成对应的 header,给
dhcp_api.c和外部客户端使用。
- 用 VPP 的
-
dhcp_api.c:- 是“API 入口实现文件”:
- 注册所有 DHCP 相关 API 消息的 handler;
- 每条消息到来时,调用对应的内部函数:
dhcp_proxy_server_add/del、dhcp_proxy_set_vss等(在dhcp_proxy.c);dhcp_client_config(在client.c);- DHCPv6 相关的客户端/事件接口(通过公共 DP/CP 模块)。
- 通俗讲:外部世界(API 客户端)和内部模块之间的“翻译官”。
- 是“API 入口实现文件”:
从耦合关系看:
dhcp_api.c站在最外层,- 往外靠 VPP 的 API 框架和外部程序;
- 往里靠客户端/代理/检测等功能模块。
3.1.3 DHCPv4客户端核心:client.h / client.c
-
client.h:- 定义 v4 客户端的核心数据结构 + 对外接口:
dhcp_client_t:单个客户端实例(状态机、租约、接口索引、定时器等);dhcp_client_main_t:全局管理结构(客户端池、哈希表、指向 VLIB/VNET 的指针等);- 外部可调用的接口:
dhcp_client_config(...):添加/删除客户端配置;dhcp_client_walk(...):遍历所有客户端。
- 其他模块(比如
dhcp_api.c、调试 CLI)通过这个头文件和 v4 客户端模块交互。
- 定义 v4 客户端的核心数据结构 + 对外接口:
-
client.c:- 实现所有 v4 客户端逻辑:
- 收发 DHCPv4 报文;
- 实现 DORA + 续约/重新绑定状态机;
- 更新接口地址、FIB、邻接;
- 触发事件回调(通知上层或 API 客户端)。
- 和其他模块的耦合:
- 用
dhcp4_packet.*里的报文定义/格式化; - 通过 VNET/FIB 接口给接口设置 IP、添加默认路由;
- 通过
dhcp_api.c暴露给外部 API。
- 用
- 实现所有 v4 客户端逻辑:
第三、四部分中关于 DHCPv4 客户端的所有分析,几乎都会围绕这两个文件展开。
3.1.4 客户端检测与测试:dhcp_client_detect.c / dhcp_test.c
-
dhcp_client_detect.c:- 一个功能性“侧车模块”,挂在 VPP 的某些 feature arc 上:
- 观察进出接口的 DHCP 报文;
- 判断这个接口上是否出现 DHCP 客户端行为;
- 可用于安全策略(防未授权客户端/服务器)、监控和运维。
- 耦合关系:
- 向下依赖 VLIB 节点框架(注册节点、计数器、trace);
- 向上通过 API/CLI 开关这个功能(
dhcp_client_detect_enable_disable)。
- 一个功能性“侧车模块”,挂在 VPP 的某些 feature arc 上:
-
dhcp_test.c:- 放的是 API 测试用例:
- 怎么发起 DHCP 配置请求;
- 怎么验证返回的结果;
- 在 VPP 的 test framework 中用于回归测试。
- 对读代码的人来说,它也是一个“如何正确使用 API 的示例集合”。
- 放的是 API 测试用例:
这两个文件不是主数据路径上的“核心逻辑”,但对理解如何在工程中调用 DHCP 插件非常有帮助。
3.1.5 代理核心与数据平面节点:dhcp_proxy.* / dhcp4_proxy_node.c / dhcp6_proxy_node.c
-
dhcp_proxy.h/dhcp_proxy.c:- 把“DHCP 代理/中继”这件事拆成一个相对独立的子系统:
- 配置管理:
- 每个 VRF/协议族(v4/v6)对应的 DHCP 服务器列表;
- 源地址、接收 FIB 索引、VSS 信息等;
- 运行时状态:
- 待响应请求表(cookie/pending 结构);
- VSS 配置池和索引;
- 对外接口:
dhcp_proxy_server_add/del等配置函数;dhcp_proxy_dump/dhcp_send_details等查询/遍历函数;dhcp_maybe_register_udp_ports管理端口注册。
- 配置管理:
- 这里更多是**“控制平面 + 全局状态”**,真正的数据包转发在 node 文件里。
- 把“DHCP 代理/中继”这件事拆成一个相对独立的子系统:
-
dhcp4_proxy_node.c:- 这是 DHCPv4 Proxy 的数据平面节点实现:
- 客户端 → 服务器方向:
- 解析 DHCPv4 报文;
- 根据 GIADDR/VRF 找到对应 proxy 配置;
- 插入 Option82/VSS;
- 选择要转发的服务器(支持多服务器);
- 记录 pending cookie。
- 服务器 → 客户端方向:
- 根据 cookie 找回原客户端;
- 移除/处理 Option82;
- 把报文发回正确的客户端接口。
- 客户端 → 服务器方向:
- 与
dhcp_proxy.c的关系:- node 侧负责“包怎么转”;
- proxy.c 负责“有哪些服务器/VRF/VSS 配置可供使用”。
- 这是 DHCPv4 Proxy 的数据平面节点实现:
-
dhcp6_proxy_node.c:- 类似
dhcp4_proxy_node.c,但针对 DHCPv6 Relay 机制:- 构造/解析 RELAY-FORW / RELAY-REPL 报文;
- 维护多级中继相关字段(hop-count、link-address、peer-address);
- 插入 Interface-ID、Remote-ID、VSS 等 v6 版本的中继信息。
- 与
dhcp6_packet.h紧密耦合,用里面定义的各种 Option 结构。
- 类似
从层次上看:
dhcp_proxy.*比较偏“配置/状态管理”,dhcp4/6_proxy_node.c则是在 VPP graph 中真正处理 DHCP 报文的“执行节点”。
3.1.6 报文结构与帮助函数:dhcp4_packet.* / dhcp6_packet.h
-
dhcp4_packet.h/dhcp4_packet.c:- 定义 DHCPv4 报文头结构、magic cookie、option 类型等;
- 提供
format_dhcp_header/format_dhcp_packet_type等格式化函数; - 主要被:
- v4 客户端(
client.c) - v4 Proxy 节点(
dhcp4_proxy_node.c)
使用。
- v4 客户端(
- 通俗讲:“DHCPv4 报文字典 + printf 工具”。
-
dhcp6_packet.h:- 定义 DHCPv6 的:
- 消息类型枚举;
- 各种 Option(IA_NA、IA_PD、STATUS_CODE、VSS、Interface-ID 等)的结构;
- Relay 头、常用状态码等。
- 被所有 DHCPv6 相关模块(Proxy、IA_NA/PD 客户端 DP)频繁 include。
- 相当于:“DHCPv6 报文字典 + 结构定义中心”。
- 定义 DHCPv6 的:
这些文件几乎不包含“业务决策”,但它们是所有 DHCP 逻辑的基础积木。
3.1.7 DHCPv6 客户端公共 DP:dhcp6_client_common_dp.*
dhcp6_client_common_dp.h/.c:- 把 DHCPv6 客户端(IA_NA + IA_PD)在数据平面上的共性抽出来:
- DUID 生成与管理(DUID-LLT 等);
- 事务 ID(xid)生成;
- 公共的 UDP 端口注册/解除;
- 通用的报文发送/重传框架。
- IA_NA 和 PD 的 DP 文件都依赖这里的公共逻辑:
- 避免重复造轮子;
- 确保 v6 客户端行为在细节上保持一致。
- 把 DHCPv6 客户端(IA_NA + IA_PD)在数据平面上的共性抽出来:
可以把它看成 DHCPv6 客户端 DP 的**“公共基础库”**。
3.1.8 DHCPv6 IA_NA 客户端 CP/DP:dhcp6_ia_na_client_*
-
dhcp6_ia_na_client_cp.h/.c:- 控制平面部分:
- 维护“每个接口上的 IA_NA 客户端配置”;
- 把从 DP 收到的前缀/地址应用到 VPP 接口/路由;
- 提供 CLI/API 调用的后端实现。
- 主要与:
- 接口管理模块(设置 IPv6 地址);
- FIB/路由模块(更新路由);
发生耦合。
- 控制平面部分:
-
dhcp6_ia_na_client_dp.h/.c:- 数据平面部分:
- 负责具体的 DHCPv6 IA_NA 报文构造、重传、状态机;
- 使用
dhcp6_client_common_dp提供的通用功能; - 把收到的 Reply/Advertise 中的地址/状态解析出来,通知 CP。
- 相当于:“IA_NA 客户端的执行引擎”。
- 数据平面部分:
-
dhcp6_ia_na_client_cp.api/dhcp6_ia_na_client_cp_api.c:- 定义并实现针对 IA_NA 控制平面的专用 API:
- 启用/禁用某接口上的 IA_NA 客户端;
- 查询当前 IA_NA 状态等。
- 是控制平面逻辑对外的“接口层”。
- 定义并实现针对 IA_NA 控制平面的专用 API:
这几组文件合起来,构成了一个完整的“DHCPv6 地址客户端子系统”,内部再通过事件/回调同 Proxy/其他模块协作。
3.1.9 DHCPv6 PD 客户端 CP/DP:dhcp6_pd_client_* / dhcp6_pd_doc.rst
-
dhcp6_pd_client_cp.h/.c:- DHCPv6 PD(前缀委派)控制平面逻辑:
- 维护“前缀组”“前缀消费者”等抽象概念;
- 控制哪些接口从哪个前缀组拿前缀;
- 当前缀变化时,重新配置下游接口。
- 是 VPP 里 PD 客户端“业务策略”的中心。
- DHCPv6 PD(前缀委派)控制平面逻辑:
-
dhcp6_pd_client_dp.h/.c:- PD 的数据平面执行引擎:
- 负责构造携带 IA_PD/IAPREFIX 选项的 DHCPv6 报文;
- 实现 PD 的重传/超时/状态机;
- 把收到的前缀列表和生命周期信息汇报给 CP。
- 和 IA_NA 的 DP 部分类似,只是操作对象从“地址”变成“前缀”。
- PD 的数据平面执行引擎:
-
dhcp6_pd_client_cp.api/dhcp6_pd_client_cp_api.c:- 定义/实现 PD 控制平面的 API 接口:
- 启用/禁用 PD 客户端;
- 维护前缀组设置;
- 订阅 PD 相关事件等。
- 定义/实现 PD 控制平面的 API 接口:
-
dhcp6_pd_doc.rst:- 一份相对完整的 DHCPv6 PD 设计和使用文档:
- 讲解 CP/DP 分离的设计;
- 解释前缀组、前缀消费者等概念;
- 提供 CLI 示例和使用建议。
- 对读源码非常有帮助,可以把它当成“官方注释版设计文档”。
- 一份相对完整的 DHCPv6 PD 设计和使用文档:
从模块关系看:
- IA_NA 和 PD 两套子系统结构非常相似:都有 CP / DP / API 三层;
- 二者共享
dhcp6_client_common_dp和dhcp6_packet.h; - 都通过 API 和
dhcp.api整体体系“挂”在 DHCP 插件之下。
小结:
3.1 这一节的目标是:先从“文件列表”过渡到“心里有一张功能地图”。
3.2 会把这些文件重新按照“客户端模块 / 代理模块 / 报文模块 / API 模块 / 辅助模块”重组,
再讲每个模块内部更细的职责和耦合关系。
3.2 模块功能划分
3.1 是“按文件名”看目录,这一节换个角度:
- 按“功能”把这些文件重新分成几个模块;
- 讲每个模块负责哪块业务、和谁打交道;
- 方便你后面查代码时,先想“我要看客户端逻辑/代理逻辑/报文字典”,再定位到对应文件。
3.2.1 客户端模块:VPP 自己当 DHCP“租客”
包含哪些代码?
- DHCPv4 客户端:
client.h/client.c
- DHCPv6 IA_NA 客户端:
dhcp6_ia_na_client_cp.h/.cdhcp6_ia_na_client_dp.h/.cdhcp6_ia_na_client_cp.apidhcp6_ia_na_client_cp_api.c
- DHCPv6 PD 客户端:
dhcp6_pd_client_cp.h/.cdhcp6_pd_client_dp.h/.cdhcp6_pd_client_cp.apidhcp6_pd_client_cp_api.c
统一职责:
让 VPP 设备本身(作为一个路由器)也能像普通主机一样:
- 主动向上游申请 IP/前缀(而不是只给别人转发包);
- 拿到地址/前缀后,自动配置到接口并更新路由/FIB;
- 在租约到期前,自动完成续约,不让业务中断。
通俗比喻:
- 代理模块是“帮别人租房的中介/楼栋管理员”;
- 客户端模块是“VPP 自己也是一个要租房的租客”。
和其他模块的关系(耦合点):
- 向下:
- 使用
dhcp4_packet.*/dhcp6_packet.h里的报文结构去拼/解析 DHCP 报文; - 和 UDP/IP 模块合作在正确的端口/地址上发包;
- 通过 VNET 接口模块给接口配置 IP/前缀;
- 通过 FIB 模块添加默认路由/前缀路由。
- 使用
- 向上:
- 通过
dhcp.api+dhcp6_ia_na_client_cp.api+dhcp6_pd_client_cp.api对外提供 API; - 被 CLI 和外部控制平面调用(比如 Python 控制程序)。
- 通过
从工程视角看,“客户端模块”是 DHCP 插件里和 VPP 其他核心网络模块耦合最深的一块。
3.2.2 代理模块:VPP 当 DHCP“楼栋管理员/中继”的部分
包含哪些代码?
- 代理控制核心:
dhcp_proxy.h/dhcp_proxy.c
- v4 代理数据平面节点:
dhcp4_proxy_node.cdhcp4_proxy_error.def
- v6 代理数据平面节点:
dhcp6_proxy_node.cdhcp6_proxy_error.def
统一职责:
不负责分配 IP,只负责:
- 接收客户端的 DHCP 请求(可能来自很多不同的 VRF/子网);
- 根据配置决定转发给哪个/哪些 DHCP 服务器;
- 在转发前后插入/处理 Option 82、VSS 等中继信息;
- 把服务器的回复原路(或按 cookie)送回客户端。
和第二部分讲的角色对应:
- 代理模块就是“楼栋管理员/中继”:
- 既要帮租客传话;
- 也要在信里加上“哪栋楼/哪根线/哪个 VRF/哪个租户”的标签。
和其他模块的关系(耦合点):
- 向下:
- 依赖 UDP/IP 模块注册 DHCP 端口、发/收 UDP 报文;
- 依赖 FIB 决定报文往哪个出接口/下一跳发;
- 使用
dhcp4_packet.*/dhcp6_packet.h的报文字典拼出正确的包。
- 向上:
- 通过
dhcp.api提供 Proxy 配置/查询 API; - 被 CLI 使用(
set dhcp proxy …、show dhcp proxy等)。
- 通过
从结构上看,代理模块会在 3.3 小节和第 6 部分中被重点讲解,因为它是“DHCP 和转发平面”的关键桥梁。
3.2.3 数据包处理模块:报文结构字典 + 格式化工具
包含哪些代码?
- DHCPv4 报文:
dhcp4_packet.hdhcp4_packet.c
- DHCPv6 报文:
dhcp6_packet.h
统一职责:
- 定义报文长什么样:
- v4 header 各字段(opcode、xid、flags、各类 IP 地址);
- v6 header / relay header / 各种 option 结构;
- 状态码、Option 编号等常量。
- 提供格式化/打印函数:
- 用于 CLI
show命令输出; - 用于调试日志输出。
- 用于 CLI
可以把它们看成:
- “DHCP 协议的结构字典”:所有模块都来查字段/常量定义;
- “printf 小助手”:方便把二进制包变成人能读的字符串。
和其他模块的关系(耦合点):
- v4/v6 客户端、v4/v6 代理节点都会 include 这些头文件;
- 但这些文件本身几乎不反向依赖业务模块,属于“公共基础设施”。
如果你在看业务逻辑时被各种字段名搞晕了,可以先回到这些文件里扫一眼,相当于“查词典”。
3.2.4 API 模块:对外配置/查询/事件的统一入口
包含哪些代码?
- 通用 DHCP API:
dhcp.apidhcp_api.c
- DHCPv6 IA_NA 控制平面 API:
dhcp6_ia_na_client_cp.apidhcp6_ia_na_client_cp_api.c
- DHCPv6 PD 控制平面 API:
dhcp6_pd_client_cp.apidhcp6_pd_client_cp_api.c
统一职责:
- 把“外部世界的需求”翻译成内部调用:
- 配置/删除客户端;
- 配置/删除 Proxy;
- 设置/查询 VSS、前缀组等;
- 订阅 DHCPv6/PD 事件;
- 收集并返回客户端/代理的当前状态。
简单说:API 模块不决策“要不要分 IP”,只负责“别人怎么和 DHCP 插件说话”。
和其他模块的关系(耦合点):
- 向外:
- 依赖 VPP API 框架;
- 被 CLI、外部控制平面(Python/C/Go 程序)调用。
- 向内:
- 调用客户端模块的配置/查询函数;
- 调用代理模块的 add/del/dump 函数;
- 负责把内部结构打包成 API 消息返回。
后面第 7 部分会专门对 API 做详细拆解,这里只需要记住:
API 模块 = 控制/查询/事件的统一入口 + 编解码层。
3.2.5 辅助模块:检测、测试、文档三件套
包含哪些代码?
- 客户端检测:
dhcp_client_detect.c
- 测试:
dhcp_test.c
- 文档:
dhcp6_pd_doc.rst
统一职责:
-
dhcp_client_detect.c:- 提供 DHCP Client Detection 功能(第二部分 2.2.3 提到过);
- 挂在特定接口/arc 上,检查是否有 DHCP 客户端流量;
- 用于安全(防未授权 DHCP)和监控(看哪些口有终端)。
-
dhcp_test.c:- 放各种 DHCP 相关 API 的测试用例;
- 用于 VPP 回归测试,保证改代码不把 API 玩坏;
- 也可以当成“如何正确用这些 API 的示例代码”。
-
dhcp6_pd_doc.rst:- 重点解释 DHCPv6 PD 客户端的设计理念和使用方式;
- 弥补代码中“不太好写在注释里”的那些背景知识;
- 是读 PD 源码前强烈推荐先过一遍的文档。
和其他模块的关系:
- 不在主数据路径上,但直接影响:
- 插件的安全性(检测);
- 可靠性和可维护性(测试);
- 可理解性(文档)。
这一节的目标,是让你在脑子里形成这样一张图:
- 想看“VPP 自己拿地址/前缀”的逻辑 → 找客户端模块;
- 想看“VPP 帮别人转 DHCP”的逻辑 → 找代理模块;
- 想确认报文字段/调试输出 → 找数据包处理模块;
- 想写脚本/控制面接入 → 看 API 模块;
- 想做安全/测试/深入理解 PD → 看辅助模块。
下一节 3.3 会把这些模块再和 VPP 的 IP/FIB/接口/UDP 等子系统串成一张“交互/依赖关系图”。
3.3 模块间依赖关系
这一节的目标:在脑子里画出一张“VPP DHCP 插件和 VPP 其他子系统之间如何交互”的依赖关系图。
理解这些依赖关系,有助于:
- 知道“改 DHCP 代码时,可能影响哪些其他模块”;
- 知道“看 DHCP 代码时,需要同时理解哪些 VPP 核心概念”;
- 知道“为什么 DHCP 插件能自动更新路由、为什么能多 VRF 隔离”。
3.3.1 客户端与FIB的交互:自动路由更新的核心
FIB是什么?
FIB(Forwarding Information Base,转发信息库)是 VPP 的路由表/转发表,决定数据包应该往哪个接口、哪个下一跳发送。
生活类比:
- FIB 就像邮局的“地址簿”,记录“寄到某个地址的包裹,应该走哪条路线、送到哪个邮局”。
DHCP客户端为什么需要和FIB交互?
当 DHCP 客户端获取到 IP 地址后,需要:
- 配置接口IP地址:把获取到的 IP 配置到接口上
- 添加默认路由:添加一条“默认网关”的路由,指向 DHCP 服务器提供的网关地址
- 更新FIB表项:让 FIB 知道“这个接口现在有这个 IP,数据包可以发到这里”
交互流程示意:
DHCP客户端获取到IP地址
│
│ 例如:192.168.1.100/24
│ 网关:192.168.1.1
│
▼
┌─────────────────────────────────┐
│ 1. 调用接口管理API │
│ ip4_add_del_address() │
│ 把IP配置到接口 │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 2. 调用FIB API │
│ ip4_add_del_route() │
│ 添加默认路由: │
│ 0.0.0.0/0 → 192.168.1.1 │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 3. FIB自动更新 │
│ - 接口IP表项更新 │
│ - 路由表更新 │
│ - 邻接表更新(ARP/ND) │
└─────────────────────────────────┘
具体代码层面的交互:
在 client.c 中,当收到 DHCP ACK 后:
// 伪代码示意,非真实代码
dhcp_client_process_ack(dhcp_client_t *client, dhcp_header_t *ack) {
// 1. 提取IP地址和网关
ip4_address_t leased_ip = ack->your_ip_address;
ip4_address_t gateway = ack->router_address; // 从Option 3提取
// 2. 调用接口管理API,配置IP地址
ip4_add_del_address(
client->sw_if_index, // 接口索引
&leased_ip, // IP地址
subnet_mask_width, // 子网掩码长度
is_add=1 // 添加
);
// 3. 调用FIB API,添加默认路由
ip4_add_del_route(
&zero_address, // 0.0.0.0/0(默认路由)
0, // 前缀长度0
&gateway, // 下一跳(网关)
client->sw_if_index, // 出接口
is_add=1 // 添加
);
// 4. FIB内部会自动:
// - 更新路由表
// - 创建/更新邻接条目(ARP/ND)
// - 更新转发表
}
关键点:
- ✅ 自动集成:DHCP 客户端不需要手动调用多个 API,VPP 的接口管理和 FIB 会自动协调
- ✅ 立即生效:配置后,FIB 立即更新,数据包可以立即转发
- ✅ 多VRF支持:每个 VRF 有独立的 FIB,DHCP 客户端配置的 IP 和路由会自动进入对应的 VRF
依赖关系图:
┌─────────────────┐
│ DHCP客户端模块 │
│ (client.c) │
└────────┬────────┘
│
│ 调用API
│
┌────┴────────────────────┐
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ 接口管理模块 │ │ FIB模块 │
│ (ip4_add_ │ │ (ip4_add_ │
│ del_addr) │ │ del_route) │
└─────────────┘ └──────┬───────┘
│
│ 自动更新
▼
┌──────────────┐
│ 转发表/路由表│
│ (立即生效) │
└──────────────┘
3.3.2 客户端与接口管理的交互:IP地址配置的桥梁
接口管理模块是什么?
VPP 的接口管理模块负责:
- 管理所有网络接口(物理接口、VLAN、VXLAN等)
- 给接口配置 IP 地址、MAC 地址
- 管理接口状态(up/down)
生活类比:
- 接口管理就像“房产登记处”,记录“哪个房间(接口)配了哪个门牌号(IP地址)”。
DHCP客户端如何与接口管理交互?
1. 获取接口信息
// DHCP客户端需要知道:
// - 接口索引(sw_if_index)
// - 接口MAC地址(用于DHCP报文)
// - 接口状态(是否up)
dhcp_client_t *client = ...;
u32 sw_if_index = client->sw_if_index;
// 通过VNET接口管理获取MAC地址
vnet_hw_interface_t *hw = vnet_get_hw_interface(vnet_main, sw_if_index);
mac_address_copy(client->client_hardware_address, hw->hw_address);
2. 配置接口IP地址
// 当获取到DHCP ACK后,配置IP地址
ip4_add_del_address(
sw_if_index, // 接口索引
&leased_address, // DHCP获取到的IP
subnet_mask_width, // 子网掩码长度(从DHCP Option 1获取)
is_add=1 // 添加地址
);
3. 监听接口状态变化
// DHCP客户端需要知道接口是否down了
// 如果接口down,可能需要:
// - 停止DHCP请求
// - 清理已配置的IP地址
// - 重置状态机
交互流程示意:
DHCP客户端启动
│
│ 1. 通过接口管理获取接口信息
│ - sw_if_index
│ - MAC地址
│ - 接口状态
│
▼
┌─────────────────────────┐
│ 发送DHCP DISCOVER │
│ (使用MAC地址) │
└────────────┬────────────┘
│
│ 收到ACK
▼
┌─────────────────────────┐
│ 调用接口管理API │
│ ip4_add_del_address() │
│ 配置IP到接口 │
└────────────┬────────────┘
│
│ 接口管理自动:
│ - 更新接口IP列表
│ - 通知FIB更新
│ - 触发ARP/ND
▼
┌─────────────────────────┐
│ 接口现在有IP了 │
│ 可以接收和发送数据包 │
└─────────────────────────┘
关键点:
- ✅ 接口索引(sw_if_index)是纽带:DHCP客户端通过sw_if_index标识接口,接口管理也通过sw_if_index管理接口
- ✅ 自动通知机制:配置IP后,接口管理会自动通知FIB、ARP/ND等模块
- ✅ 状态同步:接口状态变化时,DHCP客户端需要响应(比如接口down时清理配置)
依赖关系图:
┌─────────────────┐
│ DHCP客户端模块 │
│ (client.c) │
└────────┬────────┘
│
│ 1. 查询接口信息
│ vnet_get_hw_interface()
│
▼
┌─────────────────┐
│ 接口管理模块 │
│ (VNET Interface)│
│ │
│ - sw_if_index │
│ - MAC地址 │
│ - 接口状态 │
└────────┬────────┘
│
│ 2. 配置IP地址
│ ip4_add_del_address()
│
▼
┌─────────────────┐
│ 接口IP配置 │
│ (自动通知FIB) │
└─────────────────┘
3.3.3 代理与路由转发的交互:数据包如何找到DHCP服务器
为什么代理需要路由转发?
DHCP代理的核心任务是:
- 接收客户端的DHCP请求
- 转发给远程的DHCP服务器
- 把服务器的回复转发回客户端
关键问题:如何知道DHCP服务器在哪里?如何把数据包送到服务器?
答案:通过FIB路由表查找服务器的路由,然后通过VPP的转发机制发送数据包。
生活类比:
- 代理就像“快递中转站”,收到包裹后,需要查“地址簿(路由表)”知道“这个地址的包裹应该走哪条路线、送到哪个下一站”。
交互流程示意:
客户端发送DHCP请求
│
│ 到达VPP Proxy节点
│
▼
┌─────────────────────────┐
│ 1. 解析请求,确定VRF │
│ 根据接收接口确定 │
│ rx_fib_index │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 2. 查找Proxy配置 │
│ dhcp_get_proxy() │
│ 获取服务器地址列表 │
└────────────┬────────────┘
│
│ 例如:服务器1 = 10.0.0.1
│ 服务器2 = 10.0.0.2
▼
┌─────────────────────────┐
│ 3. 对每个服务器: │
│ - 查找FIB路由 │
│ - 确定出接口/下一跳 │
│ - 构造并发送数据包 │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 4. FIB查找 │
│ ip4_lookup() │
│ 返回: │
│ - 出接口 │
│ - 下一跳MAC地址 │
│ - 邻接条目 │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 5. 通过转发机制发送 │
│ vlib_buffer_enqueue() │
│ 数据包进入转发图 │
└─────────────────────────┘
具体代码层面的交互:
在 dhcp4_proxy_node.c 中,转发客户端请求到服务器:
// 伪代码示意,非真实代码
dhcp4_proxy_to_server_node(vlib_buffer_t *b0) {
// 1. 解析DHCP请求,确定VRF
u32 rx_fib_index = vnet_buffer(b0)->ip.adj_index[VLIB_RX];
// 2. 查找Proxy配置
dhcp_proxy_t *proxy = dhcp_get_proxy(&dhcp_proxy_main,
rx_fib_index,
FIB_PROTOCOL_IP4);
// 3. 遍历所有配置的服务器
for (each server in proxy->dhcp_servers) {
ip4_address_t *server_addr = &server->dhcp_server.ip4;
u32 server_fib_index = server->server_fib_index;
// 4. 在服务器的FIB中查找路由
fib_node_index_t fei = fib_table_lookup(
server_fib_index, // 服务器的FIB索引
server_addr, // 服务器IP地址
32 // /32(主机路由)
);
// 5. 获取转发信息(出接口、下一跳等)
adj_index_t ai = fib_entry_get_adj(fei);
vnet_rewrite_header_t *rw = adj_get_rewrite(ai);
// 6. 构造转发数据包
// - 修改IP头(源地址、目标地址)
// - 插入Option 82
// - 设置GIADDR(DHCPv4)或封装Relay(DHCPv6)
// 7. 通过VPP转发机制发送
vlib_buffer_enqueue_to_next(vm, node, next_index, buffers);
}
}
关键点:
- ✅ 多VRF支持:客户端在VRF-A,服务器可能在VRF-B,Proxy需要在正确的FIB中查找路由
- ✅ 多服务器支持:可以配置多个服务器,Proxy会同时转发给所有服务器
- ✅ 源地址选择:Proxy可以配置源地址(dhcp_src_address),用于服务器回复时的路由查找
依赖关系图:
┌─────────────────┐
│ DHCP Proxy节点 │
│ (proxy_node.c) │
└────────┬────────┘
│
│ 1. 确定VRF和服务器地址
│
▼
┌─────────────────┐
│ Proxy配置模块 │
│ (dhcp_proxy.c) │
│ - 服务器列表 │
│ - VRF映射 │
└────────┬────────┘
│
│ 2. 查找服务器路由
│ fib_table_lookup()
│
▼
┌─────────────────┐
│ FIB模块 │
│ (路由表) │
│ - 查找路由 │
│ - 返回转发信息 │
└────────┬────────┘
│
│ 3. 获取邻接/下一跳
│ adj_get_rewrite()
│
▼
┌─────────────────┐
│ VPP转发机制 │
│ - 数据包排队 │
│ - 转发图处理 │
│ - 发送到出接口 │
└─────────────────┘
反向流程:服务器回复的处理
服务器回复到达VPP
│
│ 到达Proxy节点
│
▼
┌─────────────────────────┐
│ 1. 解析回复 │
│ - 提取Cookie │
│ - 查找pending请求 │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 2. 确定客户端位置 │
│ - 从pending获取 │
│ - 客户端接口/VRF │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 3. 在客户端VRF中查找 │
│ FIB路由到客户端 │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 4. 转发回客户端 │
│ (通过VPP转发机制) │
└─────────────────────────┘
3.3.4 API与各功能模块的绑定关系:控制入口的统一映射
API模块的角色
API模块是外部世界和DHCP插件内部功能之间的桥梁:
- 外部程序(CLI、Python脚本、控制平面)通过API发送请求
- API模块把请求翻译成内部函数调用
- 内部模块执行操作,返回结果
- API模块把结果打包成API消息返回
生活类比:
- API模块就像“前台接待处”,外部访客(API客户端)说出需求,前台翻译给内部各部门(客户端模块、代理模块),再把各部门的回复整理好返回给访客。
API与各模块的绑定关系:
1. DHCPv4客户端API绑定
外部API请求
│
│ dhcp_client_config (dhcp.api)
│
▼
┌─────────────────────────┐
│ dhcp_api.c │
│ dhcp_client_config() │
│ (API handler) │
└────────────┬────────────┘
│
│ 调用内部函数
│
▼
┌─────────────────────────┐
│ client.c │
│ dhcp_client_config() │
│ (实际实现) │
│ │
│ - 创建/删除客户端实例 │
│ - 启动/停止状态机 │
│ - 管理租约 │
└─────────────────────────┘
2. DHCP Proxy API绑定
外部API请求
│
│ dhcp_proxy_config (dhcp.api)
│ dhcp_proxy_set_vss (dhcp.api)
│
▼
┌─────────────────────────┐
│ dhcp_api.c │
│ dhcp_proxy_config() │
│ dhcp_proxy_set_vss() │
│ (API handlers) │
└────────────┬────────────┘
│
│ 调用内部函数
│
▼
┌─────────────────────────┐
│ dhcp_proxy.c │
│ dhcp_proxy_server_add() │
│ dhcp_proxy_server_del() │
│ dhcp_proxy_set_vss() │
│ (实际实现) │
│ │
│ - 管理Proxy配置 │
│ - 管理服务器列表 │
│ - 管理VSS配置 │
└─────────────────────────┘
3. DHCPv6客户端API绑定
外部API请求
│
│ dhcp6_pd_client_enable_disable
│ (dhcp6_pd_client_cp.api)
│
▼
┌─────────────────────────┐
│ dhcp6_pd_client_cp_api.c│
│ (API handler) │
└────────────┬────────────┘
│
│ 调用控制平面
│
▼
┌─────────────────────────┐
│ dhcp6_pd_client_cp.c │
│ (控制平面实现) │
│ │
│ - 管理PD客户端配置 │
│ - 管理前缀组 │
│ - 协调数据平面 │
└────────────┬────────────┘
│
│ 通过API控制数据平面
│
▼
┌─────────────────────────┐
│ dhcp6_pd_client_dp.c │
│ (数据平面实现) │
│ │
│ - 发送/接收DHCPv6报文 │
│ - 管理重传/超时 │
│ - 触发事件通知 │
└─────────────────────────┘
完整的API绑定关系图:
┌─────────────────┐
│ 外部世界 │
│ (CLI/Python/C) │
└────────┬────────┘
│
│ API消息
│
┌────────▼────────┐
│ API模块层 │
│ │
│ dhcp.api │
│ dhcp_api.c │
│ │
│ dhcp6_ia_na_ │
│ client_cp.api │
│ dhcp6_pd_ │
│ client_cp.api │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 客户端模块 │ │ 代理模块 │ │ 检测模块 │
│ │ │ │ │ │
│ client.c │ │ dhcp_proxy.c │ │ dhcp_client_ │
│ │ │ │ │ detect.c │
│ dhcp6_ia_na_ │ │ dhcp4_proxy_ │ │ │
│ client_cp.c │ │ node.c │ │ │
│ │ │ │ │ │
│ dhcp6_pd_ │ │ dhcp6_proxy_ │ │ │
│ client_cp.c │ │ node.c │ │ │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ VPP核心模块 │ │ VPP核心模块 │ │ VPP核心模块 │
│ │ │ │ │ │
│ FIB │ │ FIB │ │ Feature Arc │
│ 接口管理 │ │ 转发机制 │ │ │
│ UDP/IP │ │ UDP/IP │ │ │
└───────────────┘ └───────────────┘ └───────────────┘
API消息流向示例:
示例1:配置DHCPv4客户端
CLI命令:
set dhcp client intfc GigabitEthernet0/8/0 hostname my-router
│
│ 1. CLI解析命令
▼
VPP CLI框架
│
│ 2. 构造API消息
│ dhcp_client_config {
│ is_add = 1
│ client.sw_if_index = ...
│ client.hostname = "my-router"
│ }
▼
dhcp_api.c::dhcp_client_config_handler()
│
│ 3. 调用内部函数
│ dhcp_client_config(...)
▼
client.c::dhcp_client_config()
│
│ 4. 创建客户端实例
│ 启动状态机
▼
DHCP客户端开始工作
│
│ 5. 返回结果
▼
dhcp_api.c 构造回复消息
│
│ 6. 返回给CLI
▼
CLI显示成功/失败
示例2:查询DHCP Proxy配置
CLI命令:
show dhcp proxy
│
│ 1. CLI构造查询API
│ dhcp_proxy_dump { is_ip6 = false }
▼
dhcp_api.c::dhcp_proxy_dump_handler()
│
│ 2. 调用内部函数遍历
│ dhcp_proxy_walk(...)
▼
dhcp_proxy.c::dhcp_proxy_walk()
│
│ 3. 对每个Proxy配置
│ 调用回调函数
│ dhcp_send_details(...)
▼
dhcp_api.c::dhcp_send_details()
│
│ 4. 构造详情消息
│ dhcp_proxy_details { ... }
│ 发送给API客户端
▼
CLI接收并显示配置
关键点:
- ✅ API模块是"翻译层":不包含业务逻辑,只负责消息编解码和函数调用转发
- ✅ 每个功能模块都有对应的API:客户端、代理、检测都有独立的API接口
- ✅ 事件通知机制:内部模块可以通过API框架发送事件给订阅的外部程序
- ✅ 统一的消息格式:所有API消息都遵循VPP的API消息格式,便于外部程序统一处理
3.3.5 模块间依赖关系全景图
最后,用一张完整的图总结所有模块之间的依赖关系:
┌─────────────────┐
│ 外部世界 │
│ (CLI/API客户端) │
└────────┬────────┘
│
│ API消息
│
┌────────▼────────┐
│ API模块层 │
│ (dhcp_api.c) │
└────────┬────────┘
│
┌────────────────────────────┼────────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 客户端模块 │ │ 代理模块 │ │ 检测模块 │
│ │ │ │ │ │
│ client.c │ │ dhcp_proxy.c │ │ dhcp_client_ │
│ dhcp6_*_cp.c │ │ dhcp*_proxy_ │ │ detect.c │
│ dhcp6_*_dp.c │ │ node.c │ │ │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
│ │ │
│ 使用 │ 使用 │ 使用
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ VPP核心子系统 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ FIB │ │ 接口管理 │ │ UDP/IP │ │ Feature │ │
│ │ (路由表) │ │ (VNET) │ │ 协议栈 │ │ Arc │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │ │
│ └─────────────┴─────────────┴─────────────┘ │
│ │ │
│ 转发数据平面 │
└─────────────────────────────────────────────────────────────────┘
│ │ │
│ │ │
│ 依赖 │ 依赖 │ 依赖
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 数据包处理 │ │ 数据包处理 │ │ 数据包处理 │
│ │ │ │ │ │
│ dhcp4_packet.*│ │ dhcp6_packet.h│ │ (共享使用) │
│ dhcp6_packet.h│ │ │ │ │
└───────────────┘ └───────────────┘ └───────────────┘
依赖关系总结:
-
客户端模块:
- 向上:通过API模块暴露接口
- 向下:依赖FIB(路由更新)、接口管理(IP配置)、UDP/IP(报文收发)、数据包处理(报文结构)
-
代理模块:
- 向上:通过API模块暴露接口
- 向下:依赖FIB(查找服务器路由)、UDP/IP(报文转发)、数据包处理(报文结构)
-
API模块:
- 向上:连接外部世界(CLI、控制平面)
- 向下:调用客户端模块、代理模块、检测模块的内部函数
-
数据包处理模块:
- 被所有其他模块使用,提供协议定义和格式化函数
- 不反向依赖其他业务模块
-
检测模块:
- 向上:通过API模块暴露接口
- 向下:依赖Feature Arc、数据包处理模块
关键依赖链:
-
客户端获取IP的完整链路:
API → 客户端模块 → 接口管理 → FIB → 转发生效 -
代理转发的完整链路:
API → 代理模块 → FIB查找 → 转发机制 → 发送到服务器 -
事件通知的完整链路:
客户端/代理模块 → API模块 → 外部订阅者
理解这些依赖关系,有助于:
- 知道修改某个模块时,可能影响哪些其他模块
- 知道调试问题时,应该查看哪些相关模块的代码
- 知道扩展功能时,需要修改哪些模块的接口
437

被折叠的 条评论
为什么被折叠?



