VPP中DHCP插件源码深度解析第一篇:DHCP功能了解以及源码结构初识

文档说明

本文档详细解析VPP中的DHCP插件实现,从DHCP协议基础到VPP的具体实现和增强特性,系统性地梳理整个DHCP插件的架构和源码细节。此篇文章主要讲解前三部分:第一部分:DHCP协议基础知识、第二部分:VPP DHCP插件功能概述、第三部分:源码目录结构与模块划分。后续内容在专栏后续文章中讲解。

目录大纲

  1. 第一部分:DHCP协议基础知识 - 系统介绍DHCP协议的基本概念、DORA交互流程、DHCPv4/v6协议差异以及协议在网络中的角色定位,为理解VPP实现奠定理论基础。

  2. 第二部分:VPP DHCP插件功能概述 - 全面介绍VPP DHCP插件的功能特性、架构定位、API/CLI支持能力以及多线程处理机制,帮助读者建立对插件的整体认知。

  3. 第三部分:源码目录结构与模块划分 - 详细解析DHCP插件的源码文件组织方式、各模块职责划分、控制平面与数据平面分离设计,以及模块间的依赖关系。

  4. 第四部分:DHCPv4客户端实现详解 - 深入剖析DHCPv4客户端的核心数据结构、状态机实现、报文构造与解析、地址安装与路由更新等关键实现细节。

  5. 第五部分:DHCPv6客户端实现详解 - 全面解析DHCPv6客户端的CP/DP分离架构、IA_NA和IA_PD两种模式的实现机制、前缀委派功能以及DUID管理。

  6. 第六部分:DHCP Proxy实现详解 - 深入讲解DHCP代理/中继的架构设计、多服务器支持策略、VRF隔离机制、Option 82插入以及Cookie去重机制。

  7. 第七部分:API接口与CLI命令详解 - 系统说明所有DHCP相关的API接口定义、参数说明、使用示例以及CLI命令的完整语法和配置方法。

  8. 第八部分:调试与故障排查 - 详细介绍日志系统使用、统计计数器查看、调试命令操作、常见问题诊断方法以及数据包捕获分析技巧。

  9. 第九部分:扩展与定制 - 讲解如何基于源码添加新的DHCP选项支持、定制客户端和Proxy行为、与外部系统集成以及插件开发最佳实践。

  10. 第十部分:应用场景与配置实践 - 基于实际CLI命令和配置方法,详细说明边缘路由器、企业网络、多租户等典型应用场景的具体配置步骤和实现效果。

  11. 第十一部分:安全考虑 - 分析DHCP协议面临的安全威胁(欺骗攻击、DoS攻击等)、VPP提供的安全特性以及实际部署中的安全加固建议。

  12. 第十二部分:当前限制与未来发展方向 - 总结当前实现的限制(如不支持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 的经典四步:

  1. D – Discover:发现
  2. O – Offer:提供
  3. R – Request:请求
  4. 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、租期等
  • REQUEST(请求)

    • 小李可能收到了多个部门提供的工位(多个 OFFER),
      选择其中一个,正式说:
      “我要你提供的 3‑15 号工位,请帮我办正式入职。”
  • ACK(确认)

    • 人事部最终在系统里把 3‑15 标记为“小李专属”,回一封:
      “确认,3‑15 现在正式是你的工位,租期 1 年。”
    • 客户端拿到 ACK 后,真正把 IP 配到网卡上。
  • NAK(否认)

    • 人事部也可能说:
      “对不起,这个位已经给别人了 / 你的请求不合法,不能给你。”
    • 客户端收到 NAK,会放弃当前 IP,重新开始 DISCOVER。
  • RELEASE(释放)

    • 小李辞职了,和人事说:
      “我不用这个工位了,你可以重新安排给别人。”
    • 客户端主动告诉服务器:这个 IP 我不再使用。
  • INFORM(信息通知)

    • 有的电脑已经自己有 IP(手工配好的),
      但还想问:
      “能不能告诉我 DNS、NTP 之类的配置?”
    • 服务器就会只提供额外信息,不分配 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 流程细化:再演一遍“新员工入职”

场景设定:
新来的员工“小李”第一天来公司,拿着笔记本插上工位网线。

  1. DISCOVER:小李大喊“有人有空工位吗?”
小李(电脑):
  - 自己还没 IP,只能写 0.0.0.0
  - 不知道 DHCP 服务器在哪,只能对整个网广播:
    “谁能给我一个 IP?”

技术细节上:

  • 源 IP:0.0.0.0
  • 目的 IP:255.255.255.255(广播)
  • 报文里包含:
    • 自己的 MAC 地址
    • 一个随机的 transaction_id(流水号)
  1. OFFER:服务器说“这里有一个 IP 给你”
DHCP 服务器:
  - 查了一下自己的地址池,发现 192.168.1.100 空着
  - 回信:“我可以给你 192.168.1.100,用 1 天,
           网关是 192.168.1.1,DNS 是 8.8.8.8。”
  1. REQUEST:小李确认“就要你给的这个 IP”
小李:
  - 有可能同时收到多个 OFFER(比如不同 DHCP 服务器)
  - 他选择其中一个,说:
    “我确认要用 192.168.1.100,
      由某某服务器分配的这个 IP。”
  1. 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)   |

用“寄信”来形容整个过程:

  1. 租客(客户端)写了一封“我要租房”的信(DHCP DISCOVER),
    但只知道楼层公告栏,不知道物业具体地址,所以用广播。
  2. 楼栋管理员(中继)收到后:
    • 在信里写上自己的地址:GIADDR = 192.168.10.1
      相当于在信封上标明:“这封信来自 10 楼 1 号楼栋管理员”
    • 然后把信单播转发给总物业(DHCP 服务器)。
  3. 物业根据 GIADDR:
    • 知道这是“10 楼这边的住户”,应该从对应地址池里分配 IP
    • 回复 OFFER/ACK 等报文给 GIADDR(中继)
  4. 楼栋管理员再把结果转发回原租客(广播/单播都可以,看 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:

  1. 服务器先看外面那层 Relay‑Forward,知道:
    • 这封信来自哪个 link‑address(类似“哪一栋楼”)
    • peer‑address 是谁(哪个中继发来的)
  2. 再从 relay‑msg 里取出原始客户端消息,按普通 DHCPv6 逻辑处理。
  3. 回复时,服务器构造一个 Relay‑Reply,结构类似,只是 msg_type 不同。
  4. 中继收到 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-01gi0/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 插件支持:

  1. DHCP client (v4/v6)

    • 支持 IPv4 和 IPv6 的客户端功能
    • 类比:既能处理“老式小区”(IPv4),也能处理“新式小区”(IPv6)
  2. DHCPv6 prefix delegation

    • 支持 IPv6 前缀委派
    • 类比:不仅能租“单间”,还能承包“整层楼”再转租
  3. 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插件适合用在哪儿?

适合的场景

  1. 边缘路由器/CPE设备

    • 家庭网关、企业分支路由器
    • 需要通过 DHCP 从运营商获取 WAN 口地址
    • 同时作为 LAN 口的 DHCP 服务器(通过 Proxy 转发)
  2. 运营商PE设备

    • 需要为大量客户提供 DHCP 中继服务
    • 需要插入 Option 82 做用户识别和计费
    • 需要支持 VSS 做多租户隔离
  3. 数据中心网关

    • 需要高性能 DHCP 处理
    • 需要与路由、NAT 等功能深度集成

不适合的场景

  • ❌ 需要完整的 DHCP 服务器功能(VPP 只支持客户端和代理,不支持服务器)
  • ❌ 只需要简单的 DHCP 服务,不需要高性能(可以用 dnsmasq 等轻量级方案)
2.1.6 小结:2.1节你需要记住什么?

在进入源码细节之前,先建立这些整体印象

  1. VPP DHCP 插件 = 嵌入在路由器里的 DHCP 功能模块

    • 不是独立服务器,而是 VPP 的一部分
    • 与路由、FIB 等功能深度集成
  2. 成熟稳定,生产级代码

    • 有专业团队维护
    • 可以在生产环境使用
  3. 三大特性:API、CLI、多线程

    • API:可以编程控制
    • CLI:可以命令行操作
    • 多线程:高性能并行处理
  4. 支持的功能

    • 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世界的"标准租客"

功能清单

  1. 支持标准DHCP客户端流程(DORA)

    • 完整实现 DISCOVER → OFFER → REQUEST → ACK 四步流程
    • 自动处理重传、超时等异常情况
    • 类比:标准的"找房→看房→确认→签合同"流程,一步不少
  2. 租约管理

    • 自动跟踪租约到期时间
    • 在 T1 时间点自动续约(RENEW)
    • 如果续约失败,在 T2 时间点重新绑定(REBIND)
    • 租约到期前自动处理,无需人工干预
    • 类比:租房合同到期前,自动提醒续租,不用你操心
  3. 地址自动配置

    • 获取到 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世界的"传话筒"

核心功能

  1. 多服务器支持
    • 可以配置多个 DHCP 服务器
    • 客户端的 DISCOVER 请求会同时转发给所有服务器
    • 只把第一个回复转发回客户端(避免重复)
    • 类比:楼栋管理员同时给多个物业公司发消息,谁先回复就用谁的

工作流程示意

客户端请求 → VPP Proxy → 服务器1
                        → 服务器2  } 同时转发
                        → 服务器3

服务器1回复 ← VPP Proxy ← 客户端
(第一个回复被转发,其他的被丢弃)
  1. 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
  - 做用户识别和计费
  - 做安全策略控制
  1. 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 的功能类似,但实现方式更"现代":

  1. 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消息]  ← 套在这里
  }
  1. 多服务器支持

    • 和 DHCPv4 类似,支持配置多个服务器
    • 同时转发,只回传第一个回复
  2. 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(客户端检测)

生活类比
这是一个安全功能,就像小区的"门禁系统",检测是否有"未授权的人"在冒充管理员。

功能说明

  1. 检测接口上的DHCP客户端流量

    • 监控指定接口上是否有设备在发送 DHCP 客户端消息(DISCOVER、REQUEST等)
    • 如果检测到,说明这个接口上连接了 DHCP 客户端设备
    • 类比:检测到有人在"敲门要房",说明这个端口有设备接入
  2. 作为安全特性

    • 可以用于防止未授权的 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专注转发)
- 架构更灵活

实际应用场景

  1. 使用VPP自带的CP+DP(开箱即用)

    • 适合大多数场景
    • 配置简单,功能完整
  2. 只用DP,外部程序写CP(高度定制)

    • 适合需要特殊逻辑的场景
    • 比如:自定义地址分配策略、与外部系统集成
  3. 混合模式(灵活组合)

    • 部分功能用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的核心优化技术

  1. 向量化处理(Vector Processing)
    • 一次处理一批数据包,而不是一个一个处理
    • 类比:传送带一次运多个箱子,而不是一次一个
传统处理方式:
  数据包1 → [处理] → 完成
  数据包2 → [处理] → 完成
  数据包3 → [处理] → 完成
  效率:1个/次

VPP向量化处理:
  [数据包1, 数据包2, 数据包3, ...] → [批量处理] → 全部完成
  效率:N个/次(N通常是8-32)
  1. 零拷贝(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能力

  1. 配置API

    • 添加/删除DHCP客户端
    • 配置DHCP代理
    • 设置VSS信息
  2. 查询API

    • 查询客户端状态
    • 查询代理配置
    • 导出所有配置
  3. 事件API

    • 订阅DHCP事件
    • 接收实时通知
  4. 数据平面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        │    │                 │   │                │
│ - 编程控制     │    │                 │   │                │
│ - 自动化       │    │                 │   │                │
└────────────────┘    └─────────────────┘   └────────────────┘

核心优势总结

  1. 架构灵活:CP/DP分离,可以定制和扩展
  2. 性能卓越:多线程+向量化+零拷贝,处理能力强
  3. 深度集成:与VPP路由系统无缝集成,自动配置
  4. 功能丰富:多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:多线程,利用率高

性能优势的原因

  1. 向量化处理:一次处理多个数据包
  2. 零拷贝:减少内存操作
  3. 多线程:充分利用多核CPU
  4. 优化的数据结构:针对高性能设计

适用场景

  • 高并发场景:运营商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驱动,可以编程控制
  - 可以集成到自定义系统

灵活性体现

  1. CP/DP分离:可以用外部CP,实现自定义逻辑
  2. API丰富:可以通过API完全控制
  3. 事件驱动:可以实时响应状态变化
  4. 模块化:可以只使用需要的功能
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 DHCPKea
  • dnsmasq
  • 各种厂商自带的 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 服务端的原因主要是:

  1. 定位不同: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小节,我们了解了:

  1. VPP DHCP插件是什么:嵌入在VPP中的DHCP功能模块
  2. 它能做什么:客户端功能、代理功能、特殊功能
  3. 它的优势:高性能、深度集成、灵活架构
  4. 什么时候用它:路由器/交换机设备,需要高性能和集成

下一部分(第三部分)将深入源码结构,了解这些功能是如何组织实现的。


第三部分:源码目录结构与模块划分

本部分从工程视角看 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(...) 宏。
  • FEATURE.yaml

    • 给 VPP 插件管理系统看的一个“名片”:
      • 名字(Dynamic Host Configuration Protocol);
      • 维护者是谁(Dave/Neale 等);
      • 有哪些特性(v4/v6 客户端、PD、Proxy/Option82);
      • 当前状态(production);
      • 属性(API、CLI、MULTITHREAD)。
    • 方便:
      • show plugin / 文档系统里展示信息;
      • 自动生成特性列表、文档索引等。

这两个文件本身不含协议逻辑,但决定了这个插件如何被 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 和外部客户端使用。
  • dhcp_api.c

    • 是“API 入口实现文件”:
      • 注册所有 DHCP 相关 API 消息的 handler;
      • 每条消息到来时,调用对应的内部函数:
        • dhcp_proxy_server_add/deldhcp_proxy_set_vss 等(在 dhcp_proxy.c);
        • dhcp_client_config(在 client.c);
        • DHCPv6 相关的客户端/事件接口(通过公共 DP/CP 模块)。
    • 通俗讲:外部世界(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 客户端模块交互。
  • client.c

    • 实现所有 v4 客户端逻辑:
      • 收发 DHCPv4 报文;
      • 实现 DORA + 续约/重新绑定状态机;
      • 更新接口地址、FIB、邻接;
      • 触发事件回调(通知上层或 API 客户端)。
    • 和其他模块的耦合:
      • dhcp4_packet.* 里的报文定义/格式化;
      • 通过 VNET/FIB 接口给接口设置 IP、添加默认路由;
      • 通过 dhcp_api.c 暴露给外部 API。

第三、四部分中关于 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)。
  • dhcp_test.c

    • 放的是 API 测试用例
      • 怎么发起 DHCP 配置请求;
      • 怎么验证返回的结果;
      • 在 VPP 的 test framework 中用于回归测试。
    • 对读代码的人来说,它也是一个“如何正确使用 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 文件里。
  • dhcp4_proxy_node.c

    • 这是 DHCPv4 Proxy 的数据平面节点实现
      • 客户端 → 服务器方向:
        • 解析 DHCPv4 报文;
        • 根据 GIADDR/VRF 找到对应 proxy 配置;
        • 插入 Option82/VSS;
        • 选择要转发的服务器(支持多服务器);
        • 记录 pending cookie。
      • 服务器 → 客户端方向:
        • 根据 cookie 找回原客户端;
        • 移除/处理 Option82;
        • 把报文发回正确的客户端接口。
    • dhcp_proxy.c 的关系:
      • node 侧负责“包怎么转”;
      • proxy.c 负责“有哪些服务器/VRF/VSS 配置可供使用”。
  • 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
        使用。
    • 通俗讲:“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 报文字典 + 结构定义中心”

这些文件几乎不包含“业务决策”,但它们是所有 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 客户端 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 状态等。
    • 是控制平面逻辑对外的“接口层”。

这几组文件合起来,构成了一个完整的“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 客户端“业务策略”的中心。
  • dhcp6_pd_client_dp.h/.c

    • PD 的数据平面执行引擎:
      • 负责构造携带 IA_PD/IAPREFIX 选项的 DHCPv6 报文;
      • 实现 PD 的重传/超时/状态机;
      • 把收到的前缀列表和生命周期信息汇报给 CP。
    • 和 IA_NA 的 DP 部分类似,只是操作对象从“地址”变成“前缀”。
  • dhcp6_pd_client_cp.api / dhcp6_pd_client_cp_api.c

    • 定义/实现 PD 控制平面的 API 接口:
      • 启用/禁用 PD 客户端;
      • 维护前缀组设置;
      • 订阅 PD 相关事件等。
  • dhcp6_pd_doc.rst

    • 一份相对完整的 DHCPv6 PD 设计和使用文档
      • 讲解 CP/DP 分离的设计;
      • 解释前缀组、前缀消费者等概念;
      • 提供 CLI 示例和使用建议。
    • 对读源码非常有帮助,可以把它当成“官方注释版设计文档”。

从模块关系看:

  • IA_NA 和 PD 两套子系统结构非常相似:都有 CP / DP / API 三层;
  • 二者共享 dhcp6_client_common_dpdhcp6_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/.c
    • dhcp6_ia_na_client_dp.h/.c
    • dhcp6_ia_na_client_cp.api
    • dhcp6_ia_na_client_cp_api.c
  • DHCPv6 PD 客户端:
    • dhcp6_pd_client_cp.h/.c
    • dhcp6_pd_client_dp.h/.c
    • dhcp6_pd_client_cp.api
    • dhcp6_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.c
    • dhcp4_proxy_error.def
  • v6 代理数据平面节点:
    • dhcp6_proxy_node.c
    • dhcp6_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.h
    • dhcp4_packet.c
  • DHCPv6 报文:
    • dhcp6_packet.h

统一职责

  • 定义报文长什么样
    • v4 header 各字段(opcode、xid、flags、各类 IP 地址);
    • v6 header / relay header / 各种 option 结构;
    • 状态码、Option 编号等常量。
  • 提供格式化/打印函数
    • 用于 CLI show 命令输出;
    • 用于调试日志输出。

可以把它们看成:

  • DHCP 协议的结构字典”:所有模块都来查字段/常量定义;
  • printf 小助手”:方便把二进制包变成人能读的字符串。

和其他模块的关系(耦合点)

  • v4/v6 客户端、v4/v6 代理节点都会 include 这些头文件;
  • 但这些文件本身几乎不反向依赖业务模块,属于“公共基础设施”。

如果你在看业务逻辑时被各种字段名搞晕了,可以先回到这些文件里扫一眼,相当于“查词典”。

3.2.4 API 模块:对外配置/查询/事件的统一入口

包含哪些代码?

  • 通用 DHCP API:
    • dhcp.api
    • dhcp_api.c
  • DHCPv6 IA_NA 控制平面 API:
    • dhcp6_ia_na_client_cp.api
    • dhcp6_ia_na_client_cp_api.c
  • DHCPv6 PD 控制平面 API:
    • dhcp6_pd_client_cp.api
    • dhcp6_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 地址后,需要:

  1. 配置接口IP地址:把获取到的 IP 配置到接口上
  2. 添加默认路由:添加一条“默认网关”的路由,指向 DHCP 服务器提供的网关地址
  3. 更新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│          │               │          │               │
└───────────────┘          └───────────────┘          └───────────────┘

依赖关系总结

  1. 客户端模块

    • 向上:通过API模块暴露接口
    • 向下:依赖FIB(路由更新)、接口管理(IP配置)、UDP/IP(报文收发)、数据包处理(报文结构)
  2. 代理模块

    • 向上:通过API模块暴露接口
    • 向下:依赖FIB(查找服务器路由)、UDP/IP(报文转发)、数据包处理(报文结构)
  3. API模块

    • 向上:连接外部世界(CLI、控制平面)
    • 向下:调用客户端模块、代理模块、检测模块的内部函数
  4. 数据包处理模块

    • 被所有其他模块使用,提供协议定义和格式化函数
    • 不反向依赖其他业务模块
  5. 检测模块

    • 向上:通过API模块暴露接口
    • 向下:依赖Feature Arc、数据包处理模块

关键依赖链

  • 客户端获取IP的完整链路

    API → 客户端模块 → 接口管理 → FIB → 转发生效
    
  • 代理转发的完整链路

    API → 代理模块 → FIB查找 → 转发机制 → 发送到服务器
    
  • 事件通知的完整链路

    客户端/代理模块 → API模块 → 外部订阅者
    

理解这些依赖关系,有助于:

  • 知道修改某个模块时,可能影响哪些其他模块
  • 知道调试问题时,应该查看哪些相关模块的代码
  • 知道扩展功能时,需要修改哪些模块的接口

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值