Kea DHCPv4 + IPAM Hook 完整工作流程和Kea DHCPv6 + IPAM Hook 完整工作流程

Kea DHCPv4 + IPAM Hook 完整工作流程

基于代码分析,这是IPv4版本的完整执行流程:


阶段零:Hook 加载(Kea 启动时)

scss 复制代码
Kea 启动 → 加载 libkea_spring_ipam_hookv4.so → load() 执行
   ↓
registerCallout("pkt4_receive", pkt4_receive)   // 注册接收拦截点
registerCallout("lease4_select", lease4_select) // 注册租约选择拦截点
   ↓
等待 DHCPv4 请求

阶段一:请求接收(pkt4_receive)

vbnet 复制代码
1. 客户端发送 DHCPDISCOVER 报文
   ↓
2. Kea 接收报文,触发 pkt4_receive Callout
   ↓
3. Hook 从报文中提取关键信息:
   - MAC 地址(从 chaddr 字段提取,去除非十六进制字符)
   - Hostname(Option 12)
   - Transaction ID(用于日志追踪)
   ↓
4. 生成缓存键:cacheKey = MAC + ":" + subnetId(1)
   ↓
5. 检查内存缓存 g_assignedIpCache:
   ├─ 命中 → 直接使用 cached IP,跳过步骤 6-8
   └─ 未命中 → 继续步骤 6
   ↓
6. 构建 JSON 请求体(buildPayload):
   {
     "request_id": "pkt4-{transid}",
     "client_mac": "{MAC}",
     "client_id": "",
     "client_duid": "",
     "hostname": "{hostname}",
     "subnet_id": 1,
     "relay_ip": "",
     "vendor_class": "",
     "ip_version": 4,
     "timestamp": 0
   }
   ↓
7. 通过 HttpClient::assignFromIpam 调用 IPAM API:
   - URL: http://192.168.100.5:8080/api/v1/dhcp/assign
   - Headers: Authorization: Bearer {token}, X-Internal-Token: {token}
   - Timeout: 连接 500ms,总超时 1000ms
   - 解析响应中的 assigned_ip、gateway、lease_seconds
   ↓
8. IPAM 响应处理:
   ├─ 成功 (HTTP 200) → 解析 assigned_ip (如 192.168.1.100)
   ├─ 失败 → 写入日志,return 0(Kea 使用本地池回退)
   └─ 空 IP → 同上,回退本地池
   ↓
9. 将分配的 IP 写入内存缓存:
   g_assignedIpCache[cacheKey] = assigned_ip
   ↓
10. 写入 MySQL 审计(upsertReservation):
    - hosts 表:INSERT ... ON DUPLICATE KEY UPDATE
    - 字段:dhcp_identifier(MAC HEX), dhcp_identifier_type(0), 
             dhcp4_subnet_id(1), ipv4_address(整型), hostname
    - 注:使用 ON DUPLICATE KEY UPDATE 实现 upsert
   ↓
11. return 0 → Kea 继续处理 DISCOVER

阶段二:租约选择(lease4_select)

kotlin 复制代码
1. Kea 准备从本地池中选择 IP 地址(这是关键的拦截点!)
   ↓
2. 触发 lease4_select Callout
   - 此时 Kea 尚未最终决定分配哪个 IP
   ↓
3. 从缓存读取 IP:
   cacheKey = MAC + ":" + subnetId(1)
   ├─ 无缓存 → 跳过,Kea 使用本地池分配
   └─ 有缓存 → 继续步骤 4
   ↓
4. 【核心差异 - IPv4 vs IPv6】
   
   ⚠️ 重要发现:当前 IPv4 实现中,lease4_select 只做了日志记录,
      没有实际注入 IP 地址!
   
   IPv4 版本是"只读"的:
   - 读取缓存中的 IP
   - 写入日志
   - 没有修改 lease 结构体
   - 没有覆盖 Kea 的本地池分配
   ↓
5. return 0 → Kea 继续处理
   - Kea 会从本地地址池中分配 IP(如 100-200 范围)
   - 而不是使用 IPAM 返回的 IP

完整 DHCPv4 会话流程(当前实现)

bash 复制代码
客户端                    Kea + Hook                    IPAM
   │                          │                          │
   │ ① DHCPDISCOVER          │                          │
   │─────────────────────────>│                          │
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │pkt4_receive │                   │
   │                   │ 提取 MAC    │                   │
   │                   │ 查缓存: 无  │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │                          │ ② POST /assign           │
   │                          │─────────────────────────>│
   │                          │                          │
   │                          │ ③ assigned_ip: 10.0.0.5 │
   │                          │<─────────────────────────│
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │ 写入缓存    │                   │
   │                   │ 写入DB审计  │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │ ④ DHCPOFFER              │                          │
   │<─────────────────────────│                          │
   │   (IP 来自本地池,        │                          │
   │    非 IPAM 分配)         │                          │
   │                          │                          │
   │ ⑤ DHCPREQUEST            │                          │
   │─────────────────────────>│                          │
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │lease4_select│                   │
   │                   │ 查缓存记录  │                   │
   │                   │ 但未修改IP  │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │ ⑥ DHCPACK                │                          │
   │<─────────────────────────│                          │
   │   (仍是本地池IP)         │                          │
   │                          │                          │
   │ 配置完成!               │                          │
   │ 使用本地池地址           │                          │

关键日志追踪点

ini 复制代码
[kea-spring-ipam-hook] pkt4_receive entry
[kea-spring-ipam-hook] pkt4_receive transid=0x1234
[kea-spring-ipam-hook] pkt4_receive mac=44:a8:42:f6:6d:ee
[kea-spring-ipam-hook] pkt4_receive cacheKey=44a842f66dee:1
[kea-spring-ipam-hook] pkt4_receive payload={"client_mac":"44a842f66dee",...}
[kea-spring-ipam-hook] pkt4_receive assigned_ip=10.0.0.5
[kea-spring-ipam-hook] pkt4_receive reservation upsert ok
[kea-spring-ipam-hook] lease4_select entry
[kea-spring-ipam-hook] lease4_select found cached assigned_ip=10.0.0.5
[kea-spring-ipam-hook] lease4_select exit

Kea DHCPv6 + IPAM Hook 完整工作流程

阶段零:Hook 加载(Kea 启动时)

scss 复制代码
Kea 启动 → 加载 libkea_spring_ipam_hookv6.so → load() 执行
   ↓
registerCallout("pkt6_receive", pkt6_receive)  // 注册接收拦截点
registerCallout("pkt6_send", pkt6_send)        // 注册发送拦截点
   ↓
等待 DHCPv6 请求

阶段一:请求接收(pkt6_receive)

rust 复制代码
1. 客户端发送 SOLICIT 报文
   ↓
2. Kea 接收报文,触发 pkt6_receive Callout
   ↓
3. Hook 从报文中提取关键信息:
   - DUID(客户端唯一标识)
   - IAID(IA 标识符,从 Option 3/25/4 中提取)
   - Hostname(Option 12)
   - Transaction ID(用于日志追踪)
   ↓
4. 生成缓存键:cacheKey = "v6:" + DUID + ":" + subnetId(6)
   ↓
5. 检查内存缓存 g_assignedIpCache:
   ├─ 命中 → 直接使用 cached IP,跳过步骤 6-8
   └─ 未命中 → 继续步骤 6
   ↓
6. 构建 JSON 请求体(buildPayloadV6):
   {
     "request_id": "pkt6-{transid}",
     "client_id": "{DUID}",
     "client_duid": "{DUID}",
     "iaid": {IAID},
     "hostname": "{hostname}",
     "subnet_id": 6,
     "ip_version": 6
   }
   ↓
7. 通过 HttpClient::assignFromIpam 调用 IPAM API:
   - URL: http://192.168.100.5:8080/api/v1/dhcp/assign
   - Headers: Authorization: Bearer {token}, X-Internal-Token: {token}
   - Timeout: 连接 500ms,总超时 1000ms
   ↓
8. IPAM 响应处理:
   ├─ 成功 (HTTP 200) → 解析 assigned_ip (如 ::69)
   ├─ 失败 → 清除缓存,return 0(Kea 使用本地池回退)
   └─ 空 IP → 同上,回退本地池
   ↓
9. 将分配的 IP 写入内存缓存:
   g_assignedIpCache[cacheKey] = assigned_ip
   ↓
10. 异步写入 MySQL 审计(upsertReservationV6):
    - hosts 表:写入 DUID + subnet_id + hostname + iaid
    - ipv6_reservations 表:写入 IPv6 地址 + IAID
    - 注:即使写入失败,不影响 DHCP 响应
   ↓
11. return 0 → Kea 继续处理请求

阶段二:响应发送(pkt6_send)

markdown 复制代码
1. Kea 已构建好响应报文(包含从本地池分配的临时地址)
   ↓
2. 触发 pkt6_send Callout
   ↓
3. 从缓存读取 IP:
   cacheKey = "v6:" + DUID + ":" + subnetId(6)
   ├─ 无缓存 → 跳过修改,使用 Kea 原生响应
   └─ 有缓存 → 继续步骤 4
   ↓
4. 【完全重建 IA_NA 选项】(核心逻辑):
   a. 删除 Kea 原有的 IA_NA 选项:
      response->delOption(3)
      → 清除 Kea 从本地池分配的地址
   ↓
   b. 调用 addIA_NA_Option 创建全新的 IA_NA:
      - 创建 Option6IA(3, iaid)
      - 设置 T1 = 43200 秒(12小时)
      - 设置 T2 = 75600 秒(21小时)
      - 创建 Option6IAAddr(5, addr, 86400, 86400)
      - 将 IAADDR 添加到 IA_NA
      - 将 IA_NA 添加到响应包
   ↓
5. 响应包现在只包含 IPAM 分配的 IP(如 ::69)
   ↓
6. return 0 → Kea 发送修改后的响应

阶段三:完整 DHCPv6 会话流程

yaml 复制代码
客户端                    Kea + Hook                    IPAM
   │                          │                          │
   │ ① SOLICIT               │                          │
   │─────────────────────────>│                          │
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │pkt6_receive │                   │
   │                   │ 查缓存: 无  │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │                          │ ② POST /assign           │
   │                          │─────────────────────────>│
   │                          │                          │
   │                          │ ③ assigned_ip: ::69      │
   │                          │<─────────────────────────│
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │ 写入缓存    │                   │
   │                   │ 写入DB审计  │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │ ④ ADVERTISE (::69)      │                          │
   │<─────────────────────────│                          │
   │                          │                          │
   │ ⑤ REQUEST (请求 ::69)   │                          │
   │─────────────────────────>│                          │
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │ 查缓存: ::69 │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │                   ┌──────┴──────┐                   │
   │                   │ pkt6_send   │                   │
   │                   │ 删除旧IA_NA │                   │
   │                   │ 重建新IA_NA │                   │
   │                   └──────┬──────┘                   │
   │                          │                          │
   │ ⑥ REPLY (确认 ::69)     │                          │
   │<─────────────────────────│                          │
   │                          │                          │
   │ 配置成功!               │                          │
   │ 2001:db8:100::69        │                          │

关键技术决策

决策点 实现方式 原因
IP 获取时机 pkt6_receive 中调用 IPAM 尽早获取 IP,为后续响应准备
响应修改位置 pkt6_send 中重建 IA_NA 在 Kea 构建完响应后覆盖,确保最终输出
缓存策略 内存 unordered_map + mutex 避免同一会话重复调用 IPAM
数据库操作 pkt6_receive 中同步执行 审计需要,失败不影响 DHCP 主流程
地址注入方式 删除 IA_NA + 重建 彻底清除 Kea 本地池地址,避免多地址冲突
故障降级 IPAM 失败时 return 0 让 Kea 使用配置的本地池(100-1ff)

日志追踪关键点

ini 复制代码
[kea-spring-ipam-hookv6] pkt6_receive entry
[kea-spring-ipam-hookv6] pkt6_receive transid=0x2b6318
[kea-spring-ipam-hookv6] pkt6_receive duid=00:01:...
[kea-spring-ipam-hookv6] pkt6_receive iaid=423929922
[kea-spring-ipam-hookv6] http request body={...}
[kea-spring-ipam-hookv6] http response code=200
[kea-spring-ipam-hookv6] http response body={"assigned_ip":"2001:db8:100::69"}
[kea-spring-ipam-hookv6] pkt6_receive IPAM assigned: 2001:db8:100::69
[kea-spring-ipam-hookv6] pkt6_send: IA_NA rebuilt with IPAM assigned IP
相关推荐
IPDEEP全球代理1 小时前
住宅IP、移动IP、机房IP有什么区别?
网络·网络协议·tcp/ip
周洲08302 小时前
STM32+ESP8266 WiFi 远程环境监测平台|本地 OLED 显示 + TCP 上传电脑 / 手机
stm32·tcp/ip·电脑
其实防守也摸鱼2 小时前
运维--ip单日获取去重数据量超过500条
运维·学习·tcp/ip·安全·web安全·安全威胁分析·工具
专注_每天进步一点点15 小时前
SLB(绑定弹性公网ip)-gateway-业务pod,gateway上出现reset by peer,业务pod上没有reset by peer
服务器·tcp/ip·gateway
CHANG_THE_WORLD16 小时前
TCP 三次握手彻底解析:SYN、ACK、SEQ、确认号与状态迁移
网络·网络协议·tcp/ip
Bug退散师21 小时前
多路IO复用[select版TCP服务器与poll版TCP服务器]与常用网络编程函数详解
linux·服务器·c语言·网络·驱动开发·tcp/ip
利来利往1 天前
如果你的电脑可以ping ip无法ping域名
网络·网络协议·tcp/ip
2401_873479401 天前
批量IP归属地查询用什么工具?在线API vs 本地离线库,批量场景实测对比
java·网络·数据库·tcp/ip·ip
CHANG_THE_WORLD1 天前
TCP 四次挥手彻底解析:FIN、ACK、SEQ、半关闭与 TIME-WAIT
网络·网络协议·tcp/ip