2020-06-04
关键字:Option60、新增Option、插入一个Option
1、DHCP是什么?
DHCP 全称 Dynamic Host Configuration Protocol,动态主机配置协议。
说人话就是用于路由器给各个电脑、手机、网络设备分配各种地址以使设备能访问网络用的,同时它还兼有管理某一局域网内设备的功能。
DHCP 的前身是 BOOTP,即 Bootstrap Protocol。
DHCP 是一种基于 UDP 协议的局域网通讯协议。
2、Option是什么?
DHCP 协议其实就是一段字节流,不同位置的字节数据代表着不同的含义。它大体上可以分成以下两个组成部分:
1、正文部分
2、附加选项部分
正文部分就是一个DHCP包中必不可少的数据。
附加选项是可选的,即 Option。通常会根据实际的场景需求来决定是否添加。Option部分是直接插在DHCP包的末尾的,它仍旧属于DHCP包。
Option 的作用就是用以扩展 DHCP 的功能的。因为 DHCP 协议除了简单的分配地址外还要实现管理设备的功能,而不同应用场景往往又会催生出不同的需求,为了满足这些客制化的需求,就需要这种可以由用户自定义内容的“Option”可选项了。
Option 的格式简单,大体可以分成三个组成部分:
1、Option代码
1个字节。
2、内容长度
1个字节。
3、内容
内容长度个字节。
其格式如下图所示:
因为 Option 代码就是用来区分不同用途的标识符。因为它占 1 个字节,因此 Option 总共有 256 种(0 ~ 255,事实上只有 254 种可以使用的值,因为 0 和 255 是保留值)。
在一个 DHCP 包中可以插入多个 Option 字段。例如,我们以一个 携带了 Option 字段的 DHCP 报文为例,其 Option 数量与位置如下图所示:
Option 代码的值从 0 ~ 255 都代表了不同的含义,或者说代表了不同的控制类型。每一个代码的详细说明请各位同学自行参阅相关文档,这里给出文档链接如下:
https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xml
3、Option60
Option60 就是指 Option 的代码为 0x3c 的字段值。它通常用于携带设备标识符信息以供 DHCP 服务器鉴权使用。从理论上来说,任何设备都可以接入到 DHCP 服务器并请求获得相应资源。但如果我们的 DHCP 所提供的这种服务是被“商业化”了的,那当然就需要这种鉴权机制了。鉴权不通过,我 DHCP 服务器就可以忽略掉这个设备的服务请求。这种鉴权,通常就是交由 Option60 来实现的。
Option60 的格式如下图所示:
Option60 的配置在 Android4.4 中们于如下代码文件:
.frameworksasecorejavaandroid etEthernetDataTracker.java
在系统起来以后开始 DHCP 获取 DHCP 时会调用 runDhcp() 方法:
private void runDhcp()
然后会在这里读取是否有开启 Option60 的标志以及要携带的数据信息。 Option60 的开启标志与数据信息通常可以在设置APK中设定。因为 Option60 常用于鉴权,因为有些设备是往 Option60 中填写一个通行证信息,并会做适当的加密操作。
runDhcp() 方法中随后会通过 NetworkUtils.runDhcpPlus() 方法来将相应参数传递下去。这个方法是一个 native 方法,它的最终实现位于:
.frameworksasecorejniandroid_net_NetUtils.cpp static jboolean android_net_utils_runDhcpCommon(...)
随后又会调用 dhcp_do_request() 函数继续传递参数,如下图所示:
随后就到以下代码中了:
.systemcorelibnetutilsdhcp_utils.c
这个 dhcp_do_request() 函数所做的事情,基本就是:根据参数组装启动 dhcpcd 服务的命令以及将参数信息写到系统属性中去。如下图所示:
再然后就是 dhcpcd 服务登场了。dhcpcd 服务代码目录如下:
./external/dhcpcd/
dhcpcd 服务在爬起来以后就会跑以下代码的 main() 函数:
.externaldhcpcddhcpcd.c int main(int argc, char **argv)
然后在如下图所示的函数中解析有哪些 Option 是要添加组装要携带的信息的:
.externaldhcpcdif-options.c
这里还是以 Option60 为例,它在 parse_option() 中的解析如下图所示:
再接下来就到真正的发送 DHCP 数据报文的时候了。这个操作于 dhcp.c 代码中实现:
.oxexternaldhcpcddhcp.c
ssize_t make_message(struct dhcp_message **message, const struct interface *iface, uint8_t type)
关于 Option60 的组装如下图所示。
这个指针 p 就是记录 DHCP 报文字节流用的了。
再往下就是将报文送到网卡驱动发送出去了。
4、如何在DHCP网络包中插入一个新的Option?
在 Android 系统中一般都会有 Option60 的完整实现,用户只需在设置APK中设置相应信息即可自动实现 Option60 的字段携带。
但如何新增一个系统尚不支持的字段呢?
其实也很简单。
前面第 3 节介绍的 Option60 的调用组装流程照着来一次就行的了。这里就以新增一个 Option156 来简单说说如何在 DHCP 中插入一个新的 Option字段。
首先的是应用层将相应信息传递下来的流程。这个就不多说了,同学根据自己的实际需求实现就好。
然后我们可以关注 EthernetDataTracker.java 中的 runDhcp() 方法。可以依照 Option60 的做法将 Option156 的信息传递到这里来。如下图所示:
然后,笔者为了偷懒,就直接将 Option156 的信息在这里写到系统属性中去了。这种做法不太好,因为按照常,我们应该将 Option156 的信息一步步传到 C 代码层,根据参数来组装启动 dhcpcd 服务的参数的。
但笔者偷懒了。
就这样吧。
再然后就到 if-option.h 中去新增用于保存 Option156 信息的结构体变量:
.externaldhcpcdif-options.h
接下来是去 if-options.c 中解析 Option156 的信息,并将它拷贝到结构体中。直接复制 Option60 的做法来就行了:
.externaldhcpcdif-options.c
最后去到 dhcp.c 的 make_message() 函数中完成最后的拼装:
.externaldhcpcddhcp.c
如此,开机时抓一个 DHCP 的网络包,就可以发现笔者新增的 Option156 字段信息了,如下图所示:
上图网络包 wireshark 提示了笔者添加的 Option156 值错误的警告。它的意思是说 Option156 这个字段的“内容”的长度只允许是 1。但笔者在这里给它携带了一个长度为 19 个字节的字符串。因为违反了规范,所以 wireshark 给出了警告。但这其实并不重要。只要你的 DHCP 服务器能解析出来,就没有问题。
参考资料:
http://www.360doc.com/content/15/1130/09/8335678_516871788.shtml
https://blog.csdn.net/zzd_zzd/article/details/88372014
https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xml
https://tools.ietf.org/rfc/rfc6926.txt