使用bash的本机DHCP服务器

我喜欢自动化该过程,并编写自己的自行车来研究这种或那种材料。 我的新目标是在小型网络中发布地址的DHCP服务器,以便可以执行设备的初始配置。

在本文中,我将讨论DHCP协议以及bash的一些细微之处。



最终结果


让我们从头开始,以便清楚地知道我们正在争取什么。

工作示范:



带有脚本的存储库: firemoon777 / bash-dhcp-server

最初的问题


我需要的配置是通过以下方式完成的:我们通过双绞线直接连接到设备,通过DHCP发出临时地址,并使用已创建的脚本对其进行配置。 如此连续十到二十次。

对许多人来说,著名的isc-dhcp-server可以很好地完成其工作,但是,las,它不会通知我的脚本地址已发布,因此您需要以某种方式阻止执行,直到地址被发布为止。

解决方案似乎在表面上:ping直到脸部呈蓝色,直到设备响应为止:

while ! ping -c1 -W1 "$DHCP" | grep -q "time=" do echo "Waiting for $DHCP..." done 

但是这一决定绝对缺乏冒险精神。

理论部分


使用单个DHCP服务器获取地址



DHCP协议在端口67和68上通过UDP运行。服务器始终仅在67上运行,客户端仅在68上运行。由于客户端没有地址(具有地址0.0.0.0),因此广播DHCP数据包。 即 客户端始终将数据包从地址0.0.0.0:68发送到地址255.255.255.255:67,服务器从其地址67发送到地址255.255.255.255:68。

客户端通过四个数据包( DORA接收地址:

  1. 客户端找出DHCP服务器在哪里( D iscover)
  2. 服务器响应并提供其地址(ffer)
  3. 客户端从特定服务器请求建议的地址( R equest)
  4. 服务器同意并发布地址( A ck)

在视觉上,该方案可以表示如下:



使用多个DHCP服务器获取地址



当客户端发送发现时,所有可以听到的服务器都将其要约发送给客户端。 但是客户必须选择一个。 客户端选项在带有选项54(DHCP服务器)的“请求”消息中宣布,其中包含首选DHCP服务器的IP地址。 尽管还将请求发送给网络上的每个人,但只有选项54中指定IP的DHCP服务器会响应。



DHCP报文内容


DHCP数据包由两部分组成:一个常量(大小为236个字节)和一个携带选项(DHCP选项)的变量。

该表包含来自维基百科的DHCP数据包的所有字段
领域内容描述长度(以字节为单位)

消息类型。 例如,它可以采用以下值:BOOTREQUEST(0x01,从客户端到服务器的请求)和BOOTREPLY(0x02,从服务器到客户端的响应)。
1个
类型
硬件地址的类型。 RFC 1700分配的编号中定义了此字段的有效值。 例如,对于以太网MAC地址,此字段设置为0x01。
1个
海伦
硬件地址的长度,以字节为单位。 以太网MAC地址为0x06。
1个
啤酒花
消息通过的中间路由器(所谓的DHCP中继代理 )的数量。 客户端将此字段设置为0x00。
1个
西德
客户端在获取地址的过程开始时生成的4字节的唯一事务标识符。
4

从开始获取地址的过程开始的时间(以秒为单位)。 可能无法使用(在这种情况下,它设置为0x0000)。
2
标志
标志字段是DHCP协议的特殊参数。
2
ciaddr
客户端IP地址。 仅当客户端已经具有其自己的IP地址并且能够响应ARP请求时才填写该字段(如果客户端在租约期满后执行更新地址的过程,则可以这样做)。
4
yiaddr
服务器建议的新客户端IP地址。
4
西阿德
服务器IP地址。 在DHCP子句中返回(请参见下文)。
4
贾德
中继代理程序的IP地址(如果涉及到将DHCP消息传递到服务器的过程)。
4
乍得
客户端的硬件地址(通常是MAC地址)。
16
名字
可选的服务器名称,以空终止的字符串表示。
64
档案
远程下载时,无盘工作站使用的可选服务器文件名。 像sname一样,它表示为以null结尾的字符串。
128
选项
DHCP选项字段。 此处显示了各种其他配置选项。 在该字段的开头,指示了四个特殊字节,其值分别为99、130、83、99(“幻数”),从而允许服务器确定该字段的存在。 该字段的长度是可变的,但是DHCP客户端应准备好接收576字节的DHCP消息(在此消息中, options字段的长度为340字节)。
变数


RFC 2132中所有DHCP选项的列表

DHCP选项的编码如下:
编号长度资料

例如,参数3(建议的网关)的值为10.0.0.1:
3410001个

如果需要传递多个参数,则参数的长度会增加。
例如,在参数6(DNS服务器)中,我们将传输两个地址(1.1.1.1和8.8.4.4):
681个1个1个1个8844

选项字段结尾的符号是参数,其值为255(0xFF),长度为0。

多数情况下,客户端将参数55(他希望接收作为响应的参数列表)放入DHCP Discover中,但是,我们有权不向他提供他所请求的所有内容。

实践部分


最初计划用某种更合适的语言(C)来编写服务器,但是这将是平凡而又简单的。 编写脚本来接管dhcp服务器的功能都可以。

简化版


由于应该将开发的服务器用于通过补丁连接的两个节点的网络,因此采用了以下简化方法:

  • 保证网络上有一个客户端;
  • 确保网络中没有更多的dhcp服务器
  • 发起人决定发出哪个地址
  • DHCP释放和DHCP拒绝被忽略

听众


首先,您需要学习如何接收数据包。 这需要经过认证的同情听众,例如nc。 但并非每个nc都适合这些目的。 Debian的OpenBSD netcat 1.130是合适的,但是Ubuntu的1.105已经不存在了。 运行nc以侦听到达端口67的所有UDP数据包。

 nc -l 0.0.0.0 -up 67 -w0 

由于-w开关的值为0,因此还需要OpenBSD netcat。在接收到一个数据包(UDP广播)之后,传统的nc不会接收更多的数据包,但是不会结束。

原始字节处理


在外壳程序中,使用不可打印的字符(例如空字符)非常困难:它只会忽略它。 DHCP数据包包含许多字节0x00(例如,文件字段)。 问题的解决方案以十六进制转储的形式出现:

  nc -l 0.0.0.0 -up 67 -w0 | stdbuf -o0 od -v -w1 -t x1 -An 

每行一个字节,不输出地址,不跳过重复的字节。 您还可以为stdbuf -o0加香料,以便不缓冲输出。

接收,存储和处理包裹


从od命令stdout中,字节由read命令获取,并添加到数组中。

 msg=() for i in {0..235}; do read -r tmp msg[$i]=$tmp done 

尽管所有值均以十六进制表示,但DHCP选项编号和选项长度最好以通常的十进制形式显示在屏幕/日志中。 为此,您可以使用短输入bash'a:

 $ op=AC $ echo $((16#$op)) 172 

根据请求的类型(发现或请求)编辑接收到的数据包并发回。

回应


但是,发送包裹并不是一件容易的事。 首先,您需要将转储中的字节转换为原始字节,然后一次发送所有数据包。

可以使用printf实用程序使用转义序列进行转换。 为了避免丢失任何内容,请立即将字节写入文件。

 #   >/tmp/dhcp.payload #     for i in ${msg[*]}; do printf "\x$i" >> /tmp/dhcp.payload done 

OpenBSD netcat也用于发送。 但是,如果适用于Ubuntu的1.105版本适合用作侦听器,则它不适合广播UDP消息:我们收到协议不可用错误。

 cat /tmp/dhcp.payload | nc -ub 255.255.255.255 68 -s $SERVER -p 67 -w0 

-b开关允许发送广播消息,这是为什么必须从超级用户下运行服务器的第二个原因。

有什么限制?


此DHCP服务器经过简化设计,就像网络上的单个客户端一样。 但是,它将与多个客户端一起使用。 只要获取最快的地址即可。



结论


尽管尽管很难将bash脚本称为成熟的编程语言,但是您甚至可以解决诸如在网络上发布IP地址之类的问题,而无需为此使用专门设计的软件。 解决特定问题不仅带来欢乐,而且还带来解决方案时打开的新知识。

资料来源


  1. DHCP-维基百科
  2. DHCP和BOOTP参数-IANA

Source: https://habr.com/ru/post/zh-CN435148/


All Articles