背景#

最近因工作需要接触了 H3C 的 iNode 客户端, 参考其官网对 iNode 的描述:

iNode智能客户端是H3C公司自行设计开发的基于Windows的多业务接入客户端软件,提供802.1x、Portal、VPN等多种认证方式,可以与H3C以太网交换机、路由器、VPN网关等网络设备共同组网,实现对局域网、广域网、VPN、无线接入的用户认证,是对用户终端进行身份验证、安全状态评估以及安全策略实施的主体,可以按照企业接入安全策略的要求,实现基于角色/身份的权限和安全控制。

以此为发端, 本来主要讨论 DHCP 协议的设计与实现, 以期最后通过修改 DHCP 协议实现 私有 的 DHCP 协议, 实现一定条件下限制网络访问的目的.

同时亦声明本文仅就技术上讨论一种私有协议实现的可能性, 只有如何实现以及是否可行并不在本文讨论范围内.

分析#

DHCP 协议前身是 BOOTP 协议, 当前关于 DHCP 最新的信息可在 RFC 2131 中查阅, 另外 DHCPv6 则可在 RFC 3315 中定义. 不过本文当前仅讨论 DHCPv4. DHCP 全称 Dynamic Host Configuration Protocol, 即 动态主机配置协议, 是一种使网络管理员能够集中管理和自动分配 IP 地址的通信协议. 在 IP 网络中每个连接 Internet 的设备都需要分配唯一的 IP 地址, DHCP 协议可使得网络管理员采用中心化的方式分配 IP 地址, 并对 IP 地址的使用进行必要的管理, 当某台计算机移动到网络其他位置时, 能自动收到新的 IP 地址请求.

同时 DHCP 使用了 租约 的概念, 这使得在局域网中可以最大化地使用有限的 IP 地址资源, 同时 DHCP 也支持为计算机分配静态地址.

  • DHCP 协议是一种基于 UDP 之上的应用层协议, 主要是因为在依据 DHCP 获取 IP 地址前计算机尚无有效的 IP 地址, 此时 客户端 可利用 UDP 的广播特性向局域网内广播 DHCP 报文, 同时 DHCP 服务器 亦采用广播的形式向局域网内响应对应请求. DHCPv4 定义了以下几种消息:
  • DHCPDISCOVER 消息用于 客户端 广播以确定局域网内 服务器.
  • DHCPOFFER 消息用于服务端响应客户端的 DHCPDISCOVER 消息并向客户端提供配置参数.
  • DHCPREQUEST 消息主要用于客户端依据服务端提供的配置参数向服务端请求 IP 地址.
  • DHCPACK 消息用于服务端向客户端依据配置参数确认分配的 IP 地址.
  • DHCPNAK 消息用于服务端通知客户端网络地址错误或客户端地址过期等.
  • DHCPDECLINE 消息用于客户端向服务端指示已拥有网络地址.
  • DHCPRELEASE 消息用于客户端告知服务端释放对应的网络地址.
  • DHCPINFORM 消息用于客户端向服务端请求本地配置.

RFC 2131 在定义 DHCP 的几种消息的同时也定义了 DHCP 报文格式:

   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
   +---------------+---------------+---------------+---------------+
   |                            xid (4)                            |
   +-------------------------------+-------------------------------+
   |           secs (2)            |           flags (2)           |
   +-------------------------------+-------------------------------+
   |                          ciaddr  (4)                          |
   +---------------------------------------------------------------+
   |                          yiaddr  (4)                          |
   +---------------------------------------------------------------+
   |                          siaddr  (4)                          |
   +---------------------------------------------------------------+
   |                          giaddr  (4)                          |
   +---------------------------------------------------------------+
   |                                                               |
   |                          chaddr  (16)                         |
   |                                                               |
   |                                                               |
   +---------------------------------------------------------------+
   |                                                               |
   |                          sname   (64)                         |
   +---------------------------------------------------------------+
   |                                                               |
   |                          file    (128)                        |
   +---------------------------------------------------------------+
   |                                                               |
   |                          options (variable)                   |
   +---------------------------------------------------------------+
  • op 字段定义了消息的方向 1 为 客户端 向 服务端 发生请求, 2 则为 服务端 发回的响应.

  • htype 字段定义了 硬件地址类型, 1 为 10mb 以太网, 具体定义可参见 RFC 1700, 在 DHCP 中一般使用 1.

  • hlen 字段表示 硬件地址长度, 依据 htype 定义当 htype 为 1, 即 以太网 时, 该字段为 6, 也即 MAC 地址长度.

  • hops 字段表示 DHCP 协议经过的节点数, 客户端在初始发送 DHCP 请求时将其设置为 0, DHCP 协议的 中继 节点可选地增加该字段的值.

  • xid 字段表示 事务 ID, 客户端选择一个随机数, 并在其后客户端发送或是服务端发回响应过程中使用该值.

  • secs 字段为客户端获取 IP 到进行地址 续约 时所经历的秒数.

  • flags 字段当前在 DHCP 协议中仅采用了高 1 位, 后续的 15 位当前都是保留的, 使用的 1 位主要用于提示 DHCP 服务端 或 中继 节点是否将广播信息发送到客户端所在的子网.

  • ciaddr 字段为 客户端 IP 地址, 仅当客户端在 BOUND, RENEW 或者 REBINDING 状态时才填充该字段.

  • yiaddr 字段表示 your 客户端 IP 地址.

  • siaddr 字段为下一 服务端 节点 IP, 仅当在 DHCPOFFER 和 DHCPACK 请求中填充该字段.

  • giaddr 字段为 中继节点 IP 地址.

  • chaddr 字段为 客户端 硬件地址, 一般为 MAC 地址.

  • sname 字段为可选的 服务器主机名, 并且以 null 字符串结束, 也即 0x00.

  • file 字段为 启动文件名, 在 DHCPDISCOVER 消息中以常规文件名或 null 表示, 在 DHCPOFFER 消息中以全限定路径名表示.

  • options 字段是变长的可选项. 其中首 4 位为固定的 magic cookie - [0x63, 0x82, 0x53, 0x63].

                Server          Client          Server
            (not selected)                    (selected)

                  v               v               v
                  |               |               |
                  |     Begins initialization     |
                  |               |               |
                  | _____________/|\____________  |
                  |/DHCPDISCOVER | DHCPDISCOVER  \|
                  |               |               |
              Determines          |          Determines
             configuration        |         configuration
                  |               |               |
                  |\             |  ____________/ |
                  | \________    | /DHCPOFFER     |
                  | DHCPOFFER\   |/               |
                  |           \  |                |
                  |       Collects replies        |
                  |             \|                |
                  |     Selects configuration     |
                  |               |               |
                  | _____________/|\____________  |
                  |/ DHCPREQUEST  |  DHCPREQUEST\ |
                  |               |               |
                  |               |     Commits configuration
                  |               |               |
                  |               | _____________/|
                  |               |/ DHCPACK      |
                  |               |               |
                  |    Initialization complete    |
                  |               |               |
                  .               .               .
                  .               .               .
                  |               |               |
                  |      Graceful shutdown        |
                  |               |               |
                  |               |\ ____________ |
                  |               | DHCPRELEASE  \|
                  |               |               |
                  |               |        Discards lease
                  |               |               |
                  v               v               v

上图展示了局域网内 DHCP 信令通信的流程.

  • 客户端在本地局域网内广播 DHCPDISCOVER 消息, 如果 DHCP 服务端与客户端不处于同一物理网段则可通过可选的 中继 节点进行中继转发.
  • 局域网内所有服务端都可能通过 DHCPOFFER 消息对 DHCPDISCOVER 消息进行响应, 同时在 DHCPOFFER 消息内通过 yiaddr 字段指示可用的网络地址, 同时也在 options 字段内包含额外的参数.
  • 客户端可能会从一或多个服务端收到收到一或多个 DHCPOFFER 消息. 客户端根据在 DHCPOFFER 消息内包含的参数通过广播 DHCPREQUEST 消息选择一个服务端进行响应, 并通过 服务端标识(server identifier) 指示具体选择的服务器. 同时在 yiaddr 字段内保存欲请求的 IP.
  • 对于未被选中的服务端收到 DHCPREQUEST 广播时(根据 server identifier 字段)明确客户端拒绝了其预分配的 IP 地址. 针对被选中的服务端则内部提交并将对应网络地址与客户端相绑定, 同时通过 DHCPACK 消息通知客户端. 在 DHCPACK 消息内的 yiaddr 字段同时包含选择的网络地址. 如果被选中的服务端无法响应 DHCPREQUEST 消息(如待分配的网络地址已被使用)则应当通过 DHCPNAK 向客户端响应.
  • 客户端收到 DHCPACK 时需根据 options 字段内配置的参数进行最后的检查(如通过 ARP 检查分配的网络地址是否可用), 如果客户端发现该网络地址已被使用则应当通过 DHCPDECLINE 向服务端进行响应并重启以上配置流程. 为防止过多的网络流量客户端应当至少等待 10s 再次进行配置. 如果客户端收到 DHCPNAK 则立即重启配置流程.
  • 客户端可通过在 DHCPRELEASE 消息的 client identifier 或 chaddr 字段内包含标识以告知服务端其已放弃对网络地址的租用.

客户端可能收到来自服务端的 DHCPOFFER, DHCPACK 和 DHCPNAK 三种消息; 服务端可收到来自客户端的 DHCPDISCOVER, DHCPREQUEST, DHCPDECLINE, DHCPRELEASE 和 DHCPINFORM 五种消息. 下图则详细描述了 DHCP 客户端的状态间流转:

 --------                               -------
|        | +-------------------------->|       |<-------------------+
| INIT-  | |     +-------------------->| INIT  |                    |
| REBOOT |DHCPNAK/         +---------->|       |<---+               |
|        |Restart|         |            -------     |               |
 --------  |  DHCPNAK/     |               |                        |
    |      Discard offer   |      -/Send DHCPDISCOVER               |
-/Send DHCPREQUEST         |               |                        |
    |      |     |      DHCPACK            v        |               |
 -----------     |   (not accept.)/   -----------   |               |
|           |    |  Send DHCPDECLINE |           |                  |
| REBOOTING |    |         |         | SELECTING |<----+            |
|           |    |        /          |           |     |DHCPOFFER/  |
 -----------     |       /            -----------   |  |Collect     |
    |            |      /                  |   |       |  replies   |
DHCPACK/         |     /  +----------------+   +-------+            |
Record lease, set|    |   v   Select offer/                         |
timers T1, T2   ------------  send DHCPREQUEST      |               |
    |   +----->|            |             DHCPNAK, Lease expired/   |
    |   |      | REQUESTING |                  Halt network         |
    DHCPOFFER/ |            |                       |               |
    Discard     ------------                        |               |
    |   |        |        |                   -----------           |
    |   +--------+     DHCPACK/              |           |          |
    |              Record lease, set    -----| REBINDING |          |
    |                timers T1, T2     /     |           |          |
    |                     |        DHCPACK/   -----------           |
    |                     v     Record lease, set   ^               |
    +----------------> -------      /timers T1,T2   |               |
               +----->|       |<---+                |               |
               |      | BOUND |<---+                |               |
  DHCPOFFER, DHCPACK, |       |    |            T2 expires/   DHCPNAK/
   DHCPNAK/Discard     -------     |             Broadcast  Halt network
               |       | |         |            DHCPREQUEST         |
               +-------+ |        DHCPACK/          |               |
                    T1 expires/   Record lease, set |               |
                 Send DHCPREQUEST timers T1, T2     |               |
                 to leasing server |                |               |
                         |   ----------             |               |
                         |  |          |------------+               |
                         +->| RENEWING |                            |
                            |          |----------------------------+
                             ----------

OPTIONS#

options 定义为 2 字节的 Code 和 1 字节的 Len, 而后则依据 Len 包含指定长度字节的数据体. 由于当前 options 众多当前仅列举一些比较重要的 options.

  1. magic cookie, 正如前文所述, 在 DHCP 报文内包含 4 字节的 magic cookie, 其值为 [0x63, 0x82, 0x53, 0x63].

  2. DHCP Message Type 该选项编码为 53, 其后在 Len 内定义了 1 字节的数据体, 即 Type, 而 Type 在为 0x01 ~ 0x08 若干种不同的 DHCP 消息类型.

           Value   Message Type
           -----   ------------
             1     DHCPDISCOVER
             2     DHCPOFFER
             3     DHCPREQUEST
             4     DHCPDECLINE
             5     DHCPACK
             6     DHCPNAK
             7     DHCPRELEASE
             8     DHCPINFORM
    
        Code   Len  Type
       +-----+-----+-----+
       |  53 |  1  | 1-9 |
       +-----+-----+-----+
    
  3. Client-identifier 该选项编码为 61, 其后在 Len 内定义了 n 字节长度的数据体, 其中包含 Type 1 字节和 Client-Identifier n-1 字节. 其中 Client-Identifier 主要用于服务端在其数据库内鉴别查找客户端, 在 DHCP 语义环境下 Type 通常为 0x01 即 硬件地址(MAC), 同时 Client-Identifier 至少为 2 字节, 如果为 MAC 地址的话则为 6 字节.

       Code   Len   Type  Client-Identifier
       +-----+-----+-----+-----+-----+---
       |  61 |  n  |  t1 |  i1 |  i2 | ...
       +-----+-----+-----+-----+-----+---
    
  4. Requested IP Address 该选项用于客户端在 DHCPDISCOVER 消息中向服务端请求指定的 IP.

        Code   Len          Address
       +-----+-----+-----+-----+-----+-----+
       |  50 |  4  |  a1 |  a2 |  a3 |  a4 |
       +-----+-----+-----+-----+-----+-----+
    
  5. Parameter Request List 该选项用于 DHCP 客户端向服务端请求特定的配置.

        Code   Len   Option Codes
       +-----+-----+-----+-----+---
       |  55 |  n  |  c1 |  c2 | ...
       +-----+-----+-----+-----+---
    

    当前可选的配置有:

    Subnet Mask - 0x01
    Router Option - 0x03
    Domain Name Server Option - 0x06
    Network Time Protocol Servers Option - 0x42
    
  6. End Option 该选项为结束标识, 用于标识消息体的结束, 对应客户端发送的消息可立即结束于此处, 对应服务端发回的消息 RFC 要求 options 字段需填充至少 312 且至多 576 字节.

        Code
       +-----+
       | 255 |
       +-----+
    
  7. Pad Option 该选项主要用于字段填充.

        Code
       +-----+
       |  0  |
       +-----+
    
  8. Subnet Mask 该选项用于指示客户端子网掩码, 其 Len 为固定 4 字节(DHCPv4), 后续 4 字节为对应的子网掩码值. 同时如果服务端同时提供了 router option 则 Subnet Mask 应该位于前部.

        Code   Len        Subnet Mask
       +-----+-----+-----+-----+-----+-----+
       |  1  |  4  |  m1 |  m2 |  m3 |  m4 |
       +-----+-----+-----+-----+-----+-----+
    
  9. Router Option 该选项主要用于列举当前网络可路由的 IP 地址, 同时其 Len 最少为 4 且永远为 4 的倍数.

        Code   Len         Address 1               Address 2
       +-----+-----+-----+-----+-----+-----+-----+-----+--
       |  3  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
       +-----+-----+-----+-----+-----+-----+-----+-----+--
    
  10. Renewal (T1) Time Value 该选项主要用于指示从客户端获取 IP 地址到续期该 IP 所需间隔时间. T1 Interval 为 4 字节 32 位整数, 单位为 秒.

        Code   Len         T1 Interval
       +-----+-----+-----+-----+-----+-----+
       |  58 |  4  |  t1 |  t2 |  t3 |  t4 |
       +-----+-----+-----+-----+-----+-----+
    

REF#

  1. RFC 2131 - Dynamic Host Configuration Protocol
  2. RFC 2132 - DHCP Options and BOOTP Vendor Extensions