书接上文《LwIP分析之在Linux上运行LwIP的第一种方法》,第一种运行LwIP协议栈的方法,因对源码有侵入性,所以我们不作推荐。本文介绍是第二种方法,该方法只需要修改相应的源码配置,即可在本地运行起来。然后在本地使用curl可以调试服务端的httpd服务(其他功能开启方法类似,请读者自行研究)。
方法二的基本原理
该方法,我们直接使用LwIP官方提供的contrib示例代码,contrib针对Linux的移植文件目录是lwip-STABLE-2_2_0_RELEASE/contrib/ports/unix。
- 该方法的核心原理是通过创建TAP/TUN虚拟网卡 tap0,使用该虚拟网卡连接主机TCP/IP协议栈和LwIP协议栈。
- 我们知道TAP虚拟网卡 tap0 的一端默认连接主机Linux协议栈,另一端如同真实网卡的网线接入口。
- 而contrib提供的 example_app 服务程序,依据TAP虚拟网卡的编码方法,监听虚拟网卡 tap0 的另一端。然后该程序可以接收 tap0 另一端发过来的数据,也可以给另一端发送数据。
- 客户端通过调用curl向服务端也就是LwIP协议栈发送数据时,该数据是先发往本机Linux协议栈,通过Linux协议栈的应用层、Linux协议栈的传输层,到达Linux协议栈的网络层(IP层)之后,Linux协议栈根据路由表发现,发给LwIP的数据需要从虚拟网卡 tap0 发出。
- Linux协议栈向 tap0 发出数据之后,tap0 的另一端接到通知有数据到达,此时LwIP的服务程序 example_app ,开始从网卡读取数据包,将这些数据包抛给 LwIP 协议栈,然后数据包开始在LwIP协议栈中逐层流转和处理。
- LwIP协议栈的数据链路层、网络层、传输层依次处理之后,到达应用层的httpd功能服务模块,httpd分析http请求处理之后,根据http请求,原路返回一段 html 文件。
其请求数据流转图,可使用下图描述:
修改相关配置
首先,先下载源代码并解压,使用如下命令:
1 | $ wget https://github.com/lwip-tcpip/lwip/archive/refs/tags/STABLE-2_2_0_RELEASE.tar.gz |
第一步:创建配置文件 lwipcfg.h。
我们在 lwip-STABLE-2_2_0_RELEASE/contrib/examples/example_app 目录下创建配置文件 lwipcfg.h ,本文复制同目录的 lwipcfg.h.example 文件。使用下面的命令:
1
2
3$ cd lwip-STABLE-2_2_0_RELEASE/contrib/examples/example_app
$
$ cp lwipcfg.h.example lwipcfg.h
创建过程如下图所示:
第二步:修改配置文件 lwipcfg.h
第一处源文件内容:
1 | /* If these 2 are not defined, the corresponding config setting is used */ |
第一处修改后内容:
1 | /* If these 2 are not defined, the corresponding config setting is used */ |
目的是禁止使用DHCP和AUTOIP,服务使用我们自己指定的IP。
第二处源文件内容:
1 |
第二处修改后内容:
1 |
由于我本地局域网192.168.0.0/24和192.168.1.0/24是真实网络,为了不产生路由冲突,我把LwIP的IP地址配置在了192.168.3.0/24,所以才做上述修改。如果你的网络没有使用上述网段,可以不做修改。
第三处源文件内容:
1 |
第三处修改后内容:
1 |
目的是打开httpd服务,然后我们可以使用curl访问LwIP协议栈上的http服务。如果要测试其他功能,方法类似。
经过创建和修改 lwipcfg.h 配置文件之后,修改前和修改后的对比如下图所示:
大家也可以直接下载查看我配置的 lwipcfg.h 。
编译LwIP
配置文件修改完成之后,进入 lwip-STABLE-2_2_0_RELEASE/contrib/ports/unix/example_app 目录,开始编译LwIP。
1 | $ cd lwip-STABLE-2_2_0_RELEASE/contrib/ports/unix/example_app |
编译过程如下图所示:
编译完成之后,本地生成 example_app 和 makefsdata 两个可执行程序,其中 example_app 就是我们需要的测试程序。
运行LwIP
首先注意一点,LwIP的测试代码会自动调用 ifconfig 命令。所以如果你当前机器上没有安装,需要提前使用如下命令安装 ifconfig:
1 | $ sudo apt install net-tools |
第一步:使用root权限,启动 example_app 服务端程序,启动过程和结果如下图所示:
第二步:在使用客户端调用 example_app 之前,我们先分析下该服务程序的启动对系统的影响:
首先,我们先看下对当前系统网络接口的影响情况:
下图是启动 example_app 服务端之前的网卡列表:
下图是启动 example_app 服务端之后的网卡列表:
可见,example_app 启动之后,该服务程序创建了一个TAP/TUN类型的虚拟网卡,并为其配置了IP地址192.168.3.1。
然后,我们再看下对当前系统路由表的影响情况:
下图是启动 example_app 服务端之前的路由表:
下图是启动 example_app 服务端之后的路由表:
- 前文提到过,example_app 服务端的运行依赖 ifconfig 命令。
- 这里就可以看的出来,服务程序会调用 ifconfig 来配置虚拟网卡:/sbin/ifconfig tap0 inet 192.168.3.1 netmask 255.255.255.0。
- 它这样做的目的是给虚拟网卡配置一个IP,同时添加一条路由规则。192.168.3.1将作为192.168.3.0/24网段的默认网关,告诉主机协议栈,发往192.168.3.0/24的数据,需发往 tap0 这个网络接口。
第三步:使用curl开始调用服务端
我们新启一个本机的仿真终端,然后向 example_app 服务端发起http请求:
1 | $ curl http://192.168.3.200 |
调用成功之后,如下图所示的一样,服务端会返回html片段:
至此,我们在本地可以正确的调用LwIP协议栈,而LwIP也能够正常处理请求,并将处理结果返回给我们,达成了在Linux上运行协议栈的目的。
但当前的这种方法,也存在一个问题:
对服务端的调用请求,只能从本机发起请求,而不能从其他主机来访问 example_app。
如果只是为了测试和调试,该方法完全满足需要。
如果希望可以从本机或者其他主机,都能调起LwIP协议栈,请看后续博文介绍第三种方法。