动态主机配置协议 DHCP (扩展OVS) 电脑版发表于:2022/5/25 13:45  >#动态主机配置协议 DHCP [TOC] DHCP简介 ------------ tn2>动态主机设置协议(英语:Dynamic Host Configuration Protocol,缩写:DHCP),又称动态主机组态协定,是一个用于IP网络的网络协议,位于OSI模型的应用层,使用 DHCP 协议工作,主要有两个用途:<br/> —用于内部网或网络服务供应商自动分配IP地址给用户 —用于内部网管理员对所有电脑作中央管理<br/> DHCP 用一台或一组 DHCP 服务器来管理网络参数的分配,这种方案具有容错性。即使在一个仅拥有少量机器的网络中,DHCP 仍然是有用的,因为一台机器可以几乎不造成任何影响地被增加到本地网络中。<br/> 甚至对于那些很少改变地址的服务器来说,DHCP 仍然被建议用来设置它们的地址。如果服务器需要被重新分配地址的时候,就尽可能不去做更改。对于一些设备,如路由器和防火墙,则不应使用DHCP。<br/> DHCP 也可用于直接为服务器和桌面计算机分配地址,并且透过一个 PPP 代理,也可为拨接及宽带的主机,以及住宅 NAT 网关和路由器分配地址。DHCP 一般不适用于使用在无边际路由器和 DNS 服务器上。 ### 更多细节 >- DHCP是在BOOTP(Bootstrap Protocol)基础上发展而来,但BOOTP运行在相对静态(每台主机都有固定的网络连接)的环境中,管理员为每台主机配置专门的BOOTP参数文件,该文件会在相当长的时间内保持不变。DHCP从以下两方面对BOOTP进行了扩展: 1. DHCP允许计算机动态地获取IP地址,而不是静态为每台主机指定地址。 2. DHCP能够分配其他配置参数,例如客户端的启动配置文件,使客户端仅用一个消息就获取它所需要的所有配置信 息。 DHCP协议由RFC 2131定义,采用客户端/服务器通信模式,由客户端(DHCP Client)向服务器(DHCP Server) 提出配置申请,服务器返回为客户端分配的配置信息。 DHCP可以提供两种地址分配机制,网络管理员可以根据网络需求为不同的主机选择不同的分配策略。 - 动态分配机制:通过DHCP为主机分配一个有使用期限(这个使用期限通常叫做租期)的IP地址。 这种分配机制适用于主机需要临时接入网络或者空闲地址数小于网络主机总数且主机不需要永久连接网络的场景。 - 静态分配机制:网络管理员通过DHCP为指定的主机分配固定的IP地址。 相比手工静态配置IP地址,通过DHCP方式静态分配机制避免人工配置发生错误,方便管理员统一维护管理。 DHCP受益主要有以下两点: 1. 降低客户端的配置和维护成本 DHCP易配置部署,对于非技术用户,DHCP能够将客户端与配置相关的操作降至最低,并能够降低远程部署和维 护成本。 2. 集中管理 DHCP服务器可以管理多个网段的配置信息,当某个网段的配置发生变化时,管理员只需要更新DHCP服务器上的 相关配置即可。 ### 租期 >DHCP服务器给每个分配给客户端的IP地址定义一个使用期限,该使用期限被称为租期。在租期到期前,DHCP客户端 如果仍需要使用该IP地址,可以请求延长租期;如果不需要,可以主动释放该IP地址。在没有其他空闲地址可用的情 况下,DHCP服务器会把客户端主动释放的IP地址分配给其他客户端。 DHCP服务器动态分配的所有IP地址都受租期时长的限制,不同的DHCP服务器配置的租期时长可以不同。静态分配的 IP地址不受租期时长的限制,使用期限为无限长。 DHCP客户端不会等到租期到期后再申请IP地址,这样会导致IP地址被服务器回收,然后分配给其他客户端。为保证能 够使用原来的IP地址,客户端会在租期到期前的某个时间点就开始申请延长租期。 ### 地址池 >地址池指的是DHCP服务器可以为客户端分配的所有IP地址的集合。除IP地址外,地址池内还可以配置租期、子网掩码、 默认网关等网络参数。在DHCP服务器为客户端分配IP地址时,这些网络参数也一并分配给客户端。 根据创建方式的不同,地址池可以分为基于接口方式的地址池和基于全局方式的地址池。 - 基于接口方式的地址池:在DHCP服务器与客户端相连的接口上配置IP地址,地址池是跟此接口地址所属同一网段 的IP地址,且地址池中地址只能分配给此接口下的客户端。这种配置方式简单,仅适用于DHCP服务器与客户端在 同一个网段的场景。 - 基于全局方式的地址池:在系统视图下创建指定网段的地址池,且地址池中地址可以分配给设备所有接口下的客户 端。当DHCP服务器与客户端不在同一个网段时,需要部署DHCP中继。<br/> DHCP服务器依据是否部署DHCP中继来选择地址池。无DHCP中继场景下,DHCP服务器选择与接收DHCP请求报文 的接口IP地址处于同一网段的地址池。有DHCP中继场景下,DHCP服务器选择与DHCP请求报文中giaddr字段(标识 客户端所在网段)位于同一网段的地址池。 根据客户端的数量和接入断开的时间、频率来确定地址池内需要部署的IP地址数量。 根据IP地址的使用情况,地址池中的IP地址可以分为多种状态。 经典网络 ------------ tn2>一般的网络结构如下:  tn2>**DHCP服务器**<br/> DHCP服务器负责从地址池中选择IP地址分配至DHCP客户端,还可以为DHCP客户端提供其他网络参数,如默认网关地址、 DNS服务器地址和WINS服务器地址。DHCP服务器可以接收处理来自本网段或跨网段由DHCP中继转发的DHCP请求报文。<br/> **DHCP客户端**<br/> DHCP客户端发送DHCP请求报文、通过BOOTP或DHCP协议请求获取IP地址等网络参数的设备。例如,IP电话、PC、手机、无 盘工作站等。<br/> **DHCP中继**<br/> DHCP中继负责转发DHCP服务器和DHCP客户端之间的DHCP报文,协助DHCP服务器向DHCP客户端动态分配网络参数的设备。 DHCP客户端广播发送请求报文(即目的IP地址为255.255.255.255),位于同一网段内的DHCP服务器能够接收请求报文。如果 DHCP客户端和DHCP服务器不在同一个网段,DHCP服务器无法接收来自客户端的请求报文,此时,需要通过DHCP中继来转发 DHCP报文。不同于传统的IP报文转发,DHCP中继接收到DHCP请求或应答报文后,会重新修改报文格式并生成一个新的DHCP 报文再进行转发。 报文分析 ------------ tn2>我们通过winshark来抓取DHCP的包。  tn2>从这上面我们可以发现有4种不同类型的包,但其实有8种类型。 | 类型 | 描述 | | ------------ | ------------ | | DHCP DISCOVER | DHCP客户端首次登录网络时进行DHCP交互过程发送的第一个报文,用来寻找DHCP服务器。 | | DHCP OFFER | DHCP服务器用来响应DHCP DISCOVER报文,此报文携带了各种配置信息。 | | DHCP REQUEST | 此报文用于以下三种用途。<br/>1.客户端初始化后,发送广播的DHCP REQUEST报文来回应服务器的DHCP OFFER报文。<br/>2.客户端重启后,发送广播的DHCP REQUEST报文来确认先前被分配的IP地址等配置信息。<br/>3.当客户端已经和某个IP地址绑定后,发送DHCP REQUEST单播或广播报文来更新IP地址的租约。 | | DHCP ACK | 服务器对客户端的DHCP REQUEST报文的确认响应报文,客户端收到此报文后,才真正获得了IP地址和相关的配置信息。 | | DHCP NAK | 服务器对客户端的DHCP REQUEST报文的拒绝响应报文,例如DHCP服务器收到DHCP REQUEST报文后,没有找到相应的租约记录,则发送DHCP NAK报文作为应答,告知DHCP客户端无法分配合适IP地址。 | | DHCP DECLINE | 当客户端发现服务器分配给它的IP地址发生冲突时会通过发送此报文来通知服务器,并且会重新向服务器申请地址。 | | DHCP RELEASE | 客户端可通过发送此报文主动释放服务器分配给它的IP地址,当服务器收到此报文后,可将这个IP地址分配给其它的客户端。 | | DHCP INFORM | DHCP客户端获取IP地址后,如果需要向DHCP服务器获取更为详细的配置信息(网关地址、DNS服务器地址),则向DHCP服务器发送DHCP INFORM请求报文。 | tn2>DHCP报文中各个字段的含义: | 字段 | 长度 | 含义 | | ------------ | ------------ | ------------ | | op (op code) | 1字节 | 表示报文的类型,取值为1或2,含义如下:<br/>1:客户端请求报文<br/>2:服务器响应报文 | | htype (hardware type) | 1字节 | 表示硬件类型。不同的硬件类型取值不同,最常见的值是1,表示以太网(10Mb)。 | | hlen (hardware length) | 1字节 | 表示硬件地址长度,以太网的值为6。 | | hops | 1字节 | 表示当前的DHCP报文经过的DHCP中继的数目。该字段由客户端或服务器设置为0,每经过一个DHCP中继时,该字段加1。此字段的作用是限制DHCP报文.所经过的DHCP中继数目。服务器和客户端之间的DHCP中继数目不能超过16个,也就是Hops值不能大于16,否则DHCP报文将被丢弃。 | | xid | 4字节 | 表示DHCP客户端选取的随机数,使DHCP服务器的回复与DHCP客户端的报文相关联。 | | secs (seconds) | 2字节 | 表示客户端从开始获取地址或地址续租更新后所用的时间,单位是秒。 | | flags | 2字节 | 表示标志字段。只有标志字段的最高位才有意义,其余的15位均被置为0。最高位被解释为单播或者广播响应标志位,内容如下所示:<br/>0:客户端请求服务器以单播形式发送响应报文<br/>1:客户端请求服务器以广播形式发送响应报文 | | ciaddr (client ip address) | 4字节 | 表示客户端的IP地址。可以是服务器分配给客户端的IP地址或者客户端已有的IP地址。客户端在初始化状态时没有IP地址,此字段为0.0.0.0。IP地址0.0.0.0仅在采用DHCP方式的系统启动时允许本主机利用它进行临时的通信,不是有效目的地址。 | | yiaddr (your client ip address) | 4字节 | 表示服务器分配给客户端的IP地址。当服务器进行DHCP响应时,将分配给客户端的IP地址填入此字段。 | | siaddr (server ip address) | 4字节 | DHCP客户端获得启动配置信息的服务器的IP地址。 | | giaddr(gateway ip address) | 4字节 | 表示第一个DHCP中继的IP地址。当客户端发出DHCP请求时,如果服务器和客户端不在同一个网段,那么第一个DHCP中继在将DHCP请求报文转发给DHCP服务器时,会把自己的IP地址填入此字段,DHCP服务器会根据此字段来判断出客户端所在的网段地址,从而选择合适的地址池,为客户端分配该网段的IP地址。<br/>服务器还会根据此地址将响应报文发送给此DHCP中继,再由DHCP中继将此报文转发给客户端。<br/>若在到达DHCP服务器前经过了多个DHCP中继,该字段作为客户端所在的网段的标记,填充了第一个DHCP中继的IP地址后不会再变更,只是每经过一个DHCP中继,hops字段的数值会加1。 | | chaddr (client hardware address) | 16字节 | 表示客户端的MAC地址,此字段与前面的`hardware type`和`hardware length`保持一致。当客户端发出DHCP请求时,将自己的硬件地址填入此字段。对于以太网,当`hardware type`和`hardware length`分别为`1`和`6`时,此字段必须填入6字节的以太网MAC地址。 | | sname (server host name) | 64字节 | 表示客户端获取配置信息的服务器名字。此字段由DHCP服务器填写,是可选的。如果填写,必须是一个以0结尾的字符串。 | | file (file name) | 128字节 | 表示客户端需要获取的启动配置文件名。此字段由DHCP服务器填写,随着DHCP地址分配的同时下发至客户端。本字段是可选的,如果填写,必须是一个以0结尾的字符串。 | | options | 可变 | 表示DHCP的选项字段,最多为312字节。DHCP通过此字段包含了DHCP报文类型,服务器分配给终端的配置信息,如网关IP地址,DNS服务器的IP地址,客户端可以使用IP地址的有效租期等信息。 | DHCP代理[OpenStack] ------------ tn2>大致所使用的流程如下:  >### 使用Dnsmasq进程实现DHCP ```bash # 使用 ovs bridge 连接 DHCP namespace interface。 interface_driver =neutron.agent.linux.interface.OVSInterfaceDriver # 当创建 network 并在 subnet 上 enable DHCP 时,网络节点上的 DHCP agent 会启动一个 dnsmasq 进程为该 network 提供 DHCP 服务。 # 查看Controller上的DHCP Agent 进程 ps -ef | grep dnsmasq ```  ```bash # 查看子网 neutron subnet-list ip netns # 查看DHCP Agent的TAP ip netns exec qdhcp-e2027b43-f621-4da7-ae24-fc59c5894ebe ip a # 查看ovs bridge上的dhcp server tap设备 ovs-vsctl show # 当虚机孵化完成以后,此时虚机开机发起dhcp的discover消息,位于同一个vlan的都可以收到此广播消息 ``` tn2>在创建 instance 时,Neutron 会为其分配一个 port,里面包含了 MAC 和 IP 地址信息。这些信息会同步更新到 dnsmasq 的 host 文件,所以我们会看到有read的动作。 DHCP - Docker ------------ tn2>当Docker以桥接的方式启动容器时,容器内部的IP是经过DHCP获取的,例如:172.17.0.2/16,且每重启依次IP都会发生变动。  >### 安装环境 ```bash # 拉取 sudo docker pull networkboot/dhcpd # 安装openswatch yum install -y https://www.rdoproject.org/repos/rdo-release.rpm sudo yum install openvswitch libibverbs -y sudo systemctl enable --now openvswitch # 安装ovs-docker wget http://github.com/openvswitch/ovs/raw/master/utilities/ovs-docker chmod +x ovs-docker mv ovs-docker /usr/bin/ ``` >### 模拟实践 tn2>接下来我们通过一台容器DHCP服务器,和三台ovs的交换机,与三台连接不同交换机的容器主机。  tn2>首先生成交换机vswitch0、vswitch1、vswitch2 ```bash sudo ovs-vsctl add-br vswitch0 sudo ovs-vsctl add-br vswitch1 sudo ovs-vsctl add-br vswitch2 ``` tn2>创建veth pair v1与v2,并激活 ```bash sudo ip link add v1 type veth peer name v2 # 激活 sudo ip link set dev v1 up sudo ip link set dev v2 up ``` tn2>给v1配置ip及子网掩码 ```bash sudo ifconfig v1 192.168.1.254 netmask 255.255.255.0 ``` tn2>创建DHCP服务器,监听v1 ,挂载本地data文件夹,data里面中只有dhcpd.conf文件。 ```bash mkdir -p ~/emxbook/docker-dhcpd/data vim ~/emxbook/docker-dhcpd/data/dhcpd.conf sudo docker run -it \ --rm --net host \ --init \ --name=dhcpServer1 \ -v ~/emxbook/docker-dhcpd/data:/data \ networkboot/dhcpd v1 ``` tn2> dhcpd.conf配置内容如下: ```bash subnet 192.168.1.0 netmask 255.255.255.0 { option routers 192.168.1.1; option subnet-mask 255.255.255.0; option domain-name "emx.local"; option domain-name-servers 8.8.8.8; range 192.168.1.2 192.168.1.100; } ```  tn2> 将v2连接到交换机上。 ```bash sudo ovs-vsctl add-port vswitch0 v2 ``` tn2>让交换机vswitch0、vswitch1、vswitch2彼此相连。 ```bash sudo ovs-vsctl add-port vswitch0 s0p0 sudo ovs-vsctl set Interface s0p0 type=patch sudo ovs-vsctl set Interface s0p0 options:peer=s1p1 sudo ovs-vsctl add-port vswitch1 s1p1 sudo ovs-vsctl set Interface s1p1 type=patch sudo ovs-vsctl set Interface s1p1 options:peer=s0p0 sudo ovs-vsctl add-port vswitch0 s0p1 sudo ovs-vsctl set Interface s0p1 type=patch sudo ovs-vsctl set Interface s0p1 options:peer=s2p1 sudo ovs-vsctl add-port vswitch2 s2p1 sudo ovs-vsctl set Interface s2p1 type=patch sudo ovs-vsctl set Interface s2p1 options:peer=s0p1 ``` tn2>创建主机h1、h2、h3,并将其连接到交换机上,其中ubunut:5是添加了基本net-tools、isc-dhcp-client,创建的时候记得使用`--cap-add=NET_ADMIN` ```bash sudo docker run -itd --network=none --name=h1 --cap-add=NET_ADMIN burlyluo/nettoolbox sudo docker run -itd --network=none --name=h2 --cap-add=NET_ADMIN burlyluo/nettoolbox sudo docker run -itd --network=none --name=h3 --cap-add=NET_ADMIN burlyluo/nettoolbox sudo ovs-docker add-port vswitch0 eth1 h1 sudo ovs-docker add-port vswitch1 eth1 h2 sudo ovs-docker add-port vswitch2 eth1 h3 ``` tn2>可以使用floot-light控制交换机vswitch0、vswitch1、vswitch2交换机(选择) ```bash sudo ovs-vsctl set-controller vswitch0 tcp:127.0.0.1:6653 sudo ovs-vsctl set-controller vswitch1 tcp:127.0.0.1:6653 sudo ovs-vsctl set-controller vswitch2 tcp:127.0.0.1:6653 ``` tn2>接着我们进入每一个容器,执行`dhclient eth1`来获取客户端的ip地址。最后ping 测试一下完全没问题。 ```bash docker exec -it h1 /bin/bash ifconfig dhclient eth1 ifconfig ```    tn2>当然我们还可以抓包尝试。 Trunk端口 ------------ >### Trunk简介 tn2>这个端口是交换机之间或者交换机和上层设备之间的通信端口,用于干道链路。这种端口的存在就是为了多个vlan的跨越交换机进行传递。 一个trunk端口可以拥有一个主vlan和多个副vlan。 在交换机之间传递tagged frame。允许多个VLAN通过,可以与PVID不同; 收到不带tagged frame的数据帧时,打上主PVID 并转发; 收到带tagged frame数据帧时,检查VLAN ID,如果允许并且VLAN ID与PVID相同,去掉tagged直接转发,如果允许并且VLAN ID与PVID不同,直接转发原数据帧。 tn>简单来讲:可以同时传输多个VLAN,只在两个交换机之间配置trunk。  ```bash # 使用docker创建四个不带网卡的容器 docker run -itd --network=none --name=vm01 --privileged centos:7 /bin/bash docker run -itd --network=none --name=vm02 --privileged centos:7 /bin/bash docker run -itd --network=none --name=vm03 --privileged centos:7 /bin/bash docker run -itd --network=none --name=vm04 --privileged centos:7 /bin/bash # 创建OVS网桥 ovs-vsctl add-port vswitch0 ovs-vsctl add-port vswitch1 # 使用ovs-docker工具给容器添加网卡到ovs网桥 ovs-docker add-port vswitch0 eth0 vm01 --ipaddress=192.168.1.2/24 ovs-docker add-port vswitch1 eth0 vm02 --ipaddress=192.168.1.3/24 ovs-docker add-port vswitch0 eth0 vm03 --ipaddress=192.168.1.4/24 ovs-docker add-port vswitch1 eth0 vm04 --ipaddress=192.168.1.5/24 # 链接vswitch0和vswitch1 ovs-vsctl add-port vswitch0 patch_to_vswitch1 ovs-vsctl add-port vswitch1 patch_to_vswitch0 ovs-vsctl set interface patch_to_vswitch1 type=patch ovs-vsctl set interface patch_to_vswitch0 type=patch ovs-vsctl set interface patch_to_vswitch0 options:peer=patch_to_vswitch1 ovs-vsctl set interface patch_to_vswitch1 options:peer=patch_to_vswitch0 # 查看配置情况 ovs-vsctl show # 查看所对应的容器ID ovs-vsctl list interface <xxxx> | grep container_id ovs-vsctl list interface <xxxx> | grep container_id ovs-vsctl list interface <xxxx> | grep container_id ovs-vsctl list interface <xxxx> | grep container_id # 找出相关vm所对应的id vm01 tag 100 vm02 tag 100 vm03 tag 200 vm04 tag 200 # 打上相关tag标签 ovs-vsctl set port 8e1d9abd3ab64_l tag=100 ovs-vsctl set port 1cc95bf649264_l tag=100 ovs-vsctl set port c6df4ef55f864_l tag=200 ovs-vsctl set port 5be29191e0a14_l tag=200 # 设置网桥之间的patch端口的vlan模式为trunk ovs-vsctl set port patch_to_vswitch1 VLAN_mode=trunk ovs-vsctl set port patch_to_vswitch0 VLAN_mode=trunk ovs-vsctl set port patch_to_vswitch0 trunk=100,200 ovs-vsctl set port patch_to_vswitch1 trunk=100,200 # 测试Trunk ovs-ctl set port patch_to_vswitch0 trunk=200 ovs-vsctl set port patch_to_vswitch1 trunk=200 # 查看flow table ovs-ofctl dump-flows vswitch0 ```