使用 DHCP relay 解决跨网段广播

先来温习下 DHCP 的基础概念。

RHEL 上使用的 DHCP 版本叫 ISC DHCP server,通过 yum 安装 dhcp。sample 配置可以在 /usr/share/doc/dhcp-4.1.1/dhcpd.conf.sample 里面找到,参照这个文件修改是个比较快捷有效的方式:
# yum install dhcp
# cp /usr/share/doc/dhcp-4.1.1/dhcpd.conf.sample /etc/dhcp/dhcpd.conf

每修改一次 dhcpd.conf 文件,如果要是其生效,需要 restart dhcpd;或者通过 omshell 使其在 on the fly 的过程中生效。dhcpd.conf 中的配置是 case-insensitive 的。下面几个是参考配置。

下面的 option 只对 subnet 192.168.1.0/24 的网段生效:
subnet 192.168.1.0 netmask 255.255.255.0 {
        option routers                          192.168.1.254;
        option subnet-mask                 255.255.255.0;
        option domain-search              "example.com";
        option domain-name-servers   192.168.1.1;
        option time-offset                      -18000;   
        range                                        192.168.1.10 192.168.1.100;
}

下面的 option 对全局生效:
default-lease-time                  600;
max-lease-time                      7200;
option subnet-mask                255.255.255.0;
option broadcast-address      192.168.1.255;
option routers                         192.168.1.254;
option domain-name-servers 192.168.1.1, 192.168.1.2;
option domain-search             "example.com";
subnet 192.168.1.0 netmask 255.255.255.0 {
   range 192.168.1.10 192.168.1.100;
}

指定特定的 host 分配特定的 IP:
host apex {
   option host-name "apex.example.com";
   hardware ethernet 00:A0:78:8E:9E:AA;
   fixed-address        192.168.1.4;
}

如果一台机器有多块网卡,分了多个网段,可以使用 "shared-network" 声明:
shared-network name {
    option domain-search                "test.redhat.com";
    option domain-name-servers      ns1.redhat.com, ns2.redhat.com;
    option routers                             192.168.0.254;
    more parameters for EXAMPLE shared-network
    subnet 192.168.1.0 netmask 255.255.252.0 {
        parameters for subnet
        range 192.168.1.1 192.168.1.254;
    }
    subnet 192.168.2.0 netmask 255.255.252.0 {
        parameters for subnet
        range 192.168.2.1 192.168.2.254;
    }
}

通过 "group" 将某一类给划分起来:
group {
   option routers                              192.168.1.254;
   option subnet-mask                     255.255.255.0;
   option domain-search                  "example.com";
   option domain-name-servers       192.168.1.1;
   option time-offset                         -18000;
   host apex {
      option host-name "apex.example.com";
      hardware ethernet 00:A0:78:8E:9E:AA;
      fixed-address        192.168.1.4;
   }
   host raleigh {
      option host-name "raleigh.example.com";
      hardware ethernet 00:A1:DD:74:C3:F2;
      fixed-address        192.168.1.6;
   }
}

详细的可以 man dhcp-options。

对于 DHCP server 来说,/var/lib/dhcpd/dhcpd.leases 存储着 client 的 lease 信息,该文件由系统自动生成,无需手动修改。像下面这样:
# cat /var/lib/dhcpd/dhcpd.leases
# The format of this file is documented in the dhcpd.leases(5) manual page.
# This lease file was written by isc-dhcp-4.1.1-P1

lease 192.168.10.93 {
  starts 4 2013/01/31 09:34:57;
  ends 4 2013/01/31 15:29:19;
  tstp 4 2013/01/31 15:29:19;
  cltt 4 2013/01/31 09:34:57;
  binding state free;
  hardware ethernet 80:b3:1c:32:6e:a5;
  set vendorclass = "PXEClient:Arch:00000:UNDI:002001";
}

需要注意的是,该文件所有跟时间相关的都是使用的 UTC,而非本地时间。

当 dhcpd 第一次启动时,如果没有 dhcpd.leases 文件,手动的 touch 一个 /var/lib/dhcpd/dhcpd.leases 文件,否则会启动失败:
# /sbin/service dhcpd start/stop

如果有多块接口,可以通过下面这个指定特定的接口监听 dhcpd:
# cat /etc/sysconfig/dhcpd
DHCPDARGS=eth0

其余可以在 /etc/sysconfig/dhcpd 里面指定的参数:
-p: 该参数指定了 dhcpd 监听的 UDP port,默认是情况是 server 在 67 port 监听请求,68 port 回复客户端。
-f: dhcpd daemon 默认时 backgroud,该参数使得 dhcpd 跑在 foreground,主要用来 debugging。
-d: 默认 log 发送至 /var/log/messages,该参数使得 log 发送到 stderr。
-q: 启动 daemon 的时候,不打印 copyright 的信息。
-cf: 指定配置文件的位置,默认在 /etc/dhcp/dhcpd.conf。
-lf: 指定 lease db 文件的位置,默认在 /var/lib/dhcpd/dhcpd.leases。

DHCP 的客户端的配置,主要在下面这个文件:
/etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
还有个 PEERDNS 选项,yes 表示使用 DHCP server 的 push 过来的 DNS,no 表示不修改 /etc/resolv.conf。详细的可以 man dhclient 以及 dhclient.conf。

对于 Multihomed 的 DHCP server 来说,需要指定监听的端口:
DHCPDARGS="eth0 eth1";

另外,需要指定多个 subnet,这些 subnet 要匹配上面指定的端口:
default-lease-time 600;
max-lease-time 7200;
subnet 10.0.0.0 netmask 255.255.255.0 {
    option subnet-mask  255.255.255.0;
    option routers           10.0.0.1;
    range                        10.0.0.5 10.0.0.15;
}
subnet 172.16.0.0 netmask 255.255.255.0 {
    option subnet-mask 255.255.255.0;
    option routers          172.16.0.1;
    range                       172.16.0.5 172.16.0.15;
}
host example0 {
    hardware ethernet 00:1A:6B:6A:2E:0B;
    fixed-address        10.0.0.20;
}
host example1 {
    hardware ethernet 00:1A:6B:6A:2E:0B;
    fixed-address        172.16.0.20;
}

注意:
1. DHCP 不支持 alias 接口(eth0:x)。
2. fixed-address 后面跟的地址要在 range 分配的 IP pool 之外。
3. *nix DHCP server 对 IP 的分配是从高到低的,而 Windows 的则相反。
对于第二点,官方的说法是 fixed-address 的 IP 不能在 range 的范围内,但是我通过 cobbler 指定的地址来看,这个说法实践中却是可以的,也就是说 fixed-address 是可以在 range 范围内的。

下面开始本篇博客的正题,DHCP Relay agent。
DHCP Relay agent(dhcrelay)可以让没有 dhcpd 的网段获取到 dhcp。
下面根据我们的实际情况来实践一下。首先 DHCP server(192.168.1.1) 的机器在 192.168.1.0/24 网段,除此之外,我们还有 192.168.2.0/24, 192.168.3.0/24 等网段,各个网段通过路由连接。最简单的方式是在每一个网段下面部署一个 DHCP server,但是后果就是维护量比较大,并且资源的利用率比较低下。为此,我们可以通过 DHCP relay 的方式来提高资源的利用率,降低维护的成本,这样,我们只需要一个 DHCP server,最多两个(failover),剩下的在每一个网段部署一台 DHCP relay 就好了,由这台 relay 将 client 的广播包 relay 到那台 server 就好了。

操作的步骤如下,每个网段分别部署一台 DHCP relay:
# yum install dhcp

# cat /etc/sysconfig/dhcrelay
# Command line options here
DHCRELAYARGS=""
# DHCPv4 only
INTERFACES="em1"
# DHCPv4 only
DHCPSERVERS="192.168.1.1"

# /etc/init.d/dhcrelay start
# netstat -tunlp

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
udp        0      0 0.0.0.0:67                  0.0.0.0:*                               19616/dhcrelay

relay 最重要的就是指定 DHCP server 的地址。其余的,注意 iptables、selinux。

根据网上的参考资料说,需要开启 ip forward 以及 bootp relay,根据实际来看,并不需要开启这两个参数:
net.ipv4.conf.all.bootp_relay = 1
net.ipv4.ip_forward = 1

详细的用法可以 man dhcrelay。

以上适用于 rh 系,对于 debian 的来说,有个叫 dhcp-helper 的工具可以帮的上忙,具体使用方法可以看这里

除了使用服务器做 relay 之外,其实具有路由功能的设备同样能完成类似的功能,Cisco 上有个叫 ip help-address 的命令可以完成跟上面的 "DHCPSERVERS" 一样的功能,不过本着分工明确的目的,还是没有让网络设备担当此重任。如果有需求的可以看这里的几篇参考文档(1, 2, 3)。

需要说明的是,cobbler 的官方文档有个叫 dhcp tag 的参数,貌似是可以完成类似的工作,不过参考资料少之又少,就有了上面的曲线方式 ;-)