简介:kernel-aodv_v2.2.2 是 AODV 按需距离矢量路由协议在 Linux 内核中的实现版本,面向研究无线自组织网络、需要动手部署与调试路由协议的开发者与高校师生。资源包共 48 个文件,约 53KB,以 20 个 c 源文件与 21 个 h 头文件为主体,覆盖路由发现、路由维护、邻居探测等核心模块,另含 Makefile、启动脚本、gateway 配置及 README、CHANGE_LOG 等说明文档,便于编译加载与二次开发。已有 195 人学习。通过该实现可深入理解 RREQ/RREP 交互、路由表管理与 RERR 撤销机制,并借助 tcpdump、netstat、dmesg 等工具完成抓包分析与内核日志排错,是掌握无基础设施网络路由原理的实用参考。
1. kernel-aodv_v2.2.2 到底解决了什么:从内核态 AODV 的编译翻车说起
如果你在 Linux 内核模块里折腾过 AODV 路由协议,大概率见过kernel-aodv_v2.2.2这个包名。它不是一个用户态守护进程,而是把 AODV 的报文处理、路由表维护直接塞进内核网络栈的补丁式实现。我第一次拿到它时,以为解压、make、insmod 三步就能跑,结果在 5.4 内核上直接编译报错,struct net_device的成员对不上,proc_create的签名也变了。这个版本号 v2.2.2 对应的是一套面向旧内核(常见 2.6.x 到 3.x)的代码基线,直接拿到新内核上编译,翻车是常态。它适合谁?适合需要做无线自组网、应急通信、传感器网络路由验证的工程师,尤其是那些不想在用户态用olsrd或aodv-uu做转发、希望路由决策走内核快速路径的场景。这一章不急着贴代码,先把“它是什么、为什么难搞、值不值得搞”说清楚,后面再一步步拆编译、加载、调参和排错。
2. 内核态 AODV 的编译环境与源码结构:先让 v2.2.2 能过编译
2.1 为什么 kernel-aodv_v2.2.2 不能直接 make
kernel-aodv_v2.2.2的源码树通常包含aodv/、include/、Makefile和若干.patch文件。它的 Makefile 写死了KERNELDIR ?= /lib/modules/$(shell uname -r)/build,但代码里大量使用了旧内核的 API。比如init_timer在 4.15 之后被移除,del_timer_sync的返回值处理也变了;proc_create在 3.10 之后要求传入struct proc_dir_entry *parent;skb_trim和pskb_trim的语义在 4.x 有调整。直接make的报错往往从第一个.c文件开始,几十个 error 滚屏。我的做法是先把内核版本降下来,或者用容器/虚拟机跑一个 3.10 或 3.16 的内核,这是最省血泪经验的路径。如果必须上 5.x,那就得逐文件改 API,工作量不小。
2.2 源码目录里哪些文件必须改
先看目录结构,常见布局如下:
kernel-aodv_v2.2.2/ ├── Makefile ├── aodv/ │ ├── aodv.c │ ├── aodv_dev.c │ ├── aodv_route.c │ ├── aodv_timer.c │ └── aodv_socket.c ├── include/ │ └── aodv.h └── patches/ └── kernel-2.6.32.patchaodv.c是模块入口,aodv_dev.c处理网络设备事件,aodv_route.c维护路由表,aodv_timer.c管定时器,aodv_socket.c负责内核 socket 收发。编译前先确认include/aodv.h里的AODV_VERSION是不是"2.2.2",有些包会混版本。然后检查Makefile里的obj-m是否指向aodv.o,以及ccflags-y有没有加-I$(src)/include。如果缺这个 include 路径,编译时找不到aodv.h,报错会误导你去改代码。
2.3 在 3.10 内核上跑通的最小命令序列
假设你已经有一台 3.10 内核的机器,或者用virtme起了对应内核的轻量环境,操作如下:
# 解压源码包 tar -xvf kernel-aodv_v2.2.2.tar.gz cd kernel-aodv_v2.2.2 # 确认内核头文件路径存在 ls /lib/modules/$(uname -r)/build # 清理旧编译产物 make clean # 编译模块,指定内核源码目录 make KERNELDIR=/lib/modules/$(uname -r)/build # 查看生成的 .ko ls -l aodv/aodv.ko逻辑说明:make clean不是可有可无,旧包里的.o文件可能来自不同内核,不清理会链接出错。KERNELDIR必须指向当前运行内核的 build 目录,不能指向/usr/src/linux-headers-*里不匹配的版本。参数说明:如果你的内核是 3.16,KERNELDIR同样指向/lib/modules/3.16.0-*/build;如果编译报No such file or directory,先apt install linux-headers-$(uname -r)或对应发行版的 kernel-devel 包。
2.4 加载模块前必须确认的三个参数
编译出aodv.ko后,不要急着insmod。先看模块参数:
modinfo aodv/aodv.ko常见参数有debug_level、hello_interval、route_timeout。debug_level默认可能是 0,调成 3 能看到详细报文日志;hello_interval单位是毫秒,默认 1000,在节点密集场景要调大避免广播风暴;route_timeout默认 3000 毫秒,移动快时调小,静态拓扑调大。加载命令:
sudo insmod aodv/aodv.ko debug_level=3 hello_interval=2000 route_timeout=5000加载后用dmesg | tail -20看内核日志,如果出现aodv: module loaded且没有Unknown symbol,说明模块入口跑通了。如果报Unknown symbol in module,通常是内核版本不匹配或依赖模块没加载,先modprobe相关依赖再试。
3. 把 AODV 挂到无线接口上:配置、验证与第一个路由表
3.1 接口绑定与 proc 文件系统交互
模块加载后,AODV 不会自动接管所有网卡。常见做法是通过 proc 接口指定设备名:
# 查看 AODV 是否创建了 proc 入口 ls /proc/net/aodv # 将 wlan0 加入 AODV 管理 echo "add wlan0" > /proc/net/aodv/dev # 确认接口状态 cat /proc/net/aodv/dev逻辑说明:/proc/net/aodv/dev是 v2.2.2 里常见的控制入口,写入add <ifname>后,模块会在该接口上开启 AODV 报文监听。参数说明:如果ls /proc/net/aodv不存在,说明模块没加载成功或 proc 创建失败,回到dmesg看错误。有些包用的是ioctl而不是 proc,那就需要写一个小工具调用SIOCAODVADD,但 v2.2.2 多数还是 proc 风格。
3.2 用 ping 触发路由发现并观察路由表
绑定接口后,先确认无线接口本身在 ad-hoc 模式且 IP 配好:
sudo ip link set wlan0 down sudo iwconfig wlan0 mode ad-hoc essid test-mesh sudo ip link set wlan0 up sudo ip addr add 10.0.0.1/24 dev wlan0然后在另一台节点上配10.0.0.2/24,从第一台 ping 第二台:
ping -c 3 10.0.0.2ping 的同时查看 AODV 路由表:
cat /proc/net/aodv/route如果路由表里出现10.0.0.2且NextHop是wlan0的 MAC 或对应接口,说明 RREQ/RREP 交互成功。如果表是空的,先看dmesg有没有RREQ received或RREP sent。常见问题是 ad-hoc 模式没生效,iwconfig显示Mode:Managed,那 AODV 报文根本发不出去。
3.3 三个必调参数:hello_interval、route_timeout、net_diameter
hello_interval控制 HELLO 报文周期。默认 1000ms 在 10 个节点以内没问题,超过 20 个节点建议 2000ms 到 3000ms,否则信道竞争严重。route_timeout是路由条目过期时间,默认 3000ms 对低速移动合适,高速移动(比如车载)要降到 1500ms,静态节点可以升到 10000ms 减少重建。net_diameter是网络直径,影响 RREQ 的 TTL 初始值,默认 30 跳,小网络改成 5 到 10 能减少无效广播。修改方式:
echo 2000 > /proc/net/aodv/hello_interval echo 5000 > /proc/net/aodv/route_timeout echo 10 > /proc/net/aodv/net_diameter参数说明:这些值不是越大越好。hello_interval太大,链路断裂发现慢;太小,广播风暴。route_timeout太大,失效路由被继续使用,丢包;太小,路由频繁重建,开销高。建议先用默认值跑通,再根据dmesg里的route expired和RREQ retry计数调整。
3.4 验证路由是否真的走了内核快速路径
用户态 AODV 的转发要经过 socket 拷贝,内核态 AODV 直接在netif_receive_skb附近处理。验证方法:在中间节点上抓包,看 UDP 数据包的 IP TTL 是否递减,以及是否没有经过用户态进程。更直接的是cat /proc/net/aodv/stats,看forwarded_packets计数是否增长。如果计数不增长但 ping 通了,可能走的是普通 IP 转发而不是 AODV 路由,检查ip route有没有冲突的静态路由。
4. 避坑与排查:kernel-aodv_v2.2.2 最常见的五类翻车
4.1 编译报错implicit declaration of function 'init_timer'
现象:make时大量error: implicit declaration of function 'init_timer',以及struct timer_list没有function成员。原因:内核 4.15 之后init_timer被移除,改用timer_setup。解决:要么降内核到 4.14 以下,要么手动改aodv_timer.c,把init_timer(&t); t.function = fn; t.data = data;改成timer_setup(&t, fn, 0);,并把回调签名改成void fn(struct timer_list *t)。改完还要处理t.data的获取方式,用from_timer宏。
4.2 加载模块报Unknown symbol in module
现象:insmod失败,dmesg显示aodv: Unknown symbol crc_ccitt或类似。原因:依赖的 CRC 模块没加载,或者内核配置没开CONFIG_CRC_CCITT。解决:先modprobe crc_ccitt,如果找不到模块,说明内核编译时没选这个选项,需要重新配置内核或换一个带该选项的内核。另一个常见符号是proc_create,如果内核版本太老没有这个函数,得改回create_proc_entry。
4.3 路由表一直为空,ping 不通
现象:模块加载成功,接口也 add 了,但/proc/net/aodv/route始终为空,ping 目标超时。原因:ad-hoc 模式没生效,或者两台节点的 ESSID 不一致,或者 AODV 的 RREQ 被防火墙丢弃。解决:iwconfig确认Mode:Ad-Hoc和ESSID一致;iptables -L看有没有 DROP 规则;tcpdump -i wlan0 -n udp port 654看 AODV 报文(AODV 常用 UDP 654)有没有发出和收到。如果只有发出没有收到,检查信道是否相同。
4.4 路由震荡,dmesg频繁打印route expired和new route
现象:路由表条目反复出现又消失,ping 时通时断。原因:hello_interval和route_timeout比例失调,或者无线链路本身不稳定。解决:把hello_interval调到route_timeout的 1/3 到 1/2,比如hello_interval=2000、route_timeout=6000。同时用iwconfig wlan0 rate 5.5M固定速率,避免速率自适应导致链路抖动。如果还是震荡,检查是否有两个节点同时声称到同一目标的路由,AODV 的序列号机制可能被旧条目干扰,清空路由表echo "clear" > /proc/net/aodv/route再试。
4.5 卸载模块时内核 oops
现象:rmmod aodv时内核报 oops,或者模块引用计数不为零无法卸载。原因:定时器没删干净,或者 proc 条目没移除,或者 skb 队列还有未释放的包。解决:卸载前先echo "del wlan0" > /proc/net/aodv/dev,然后rmmod aodv。如果还报错,检查aodv_timer.c里的del_timer_sync是否在所有路径都调用了。旧代码有时在模块退出函数里漏掉remove_proc_entry,手动补上。生产环境不要频繁rmmod,加载后稳定运行即可。
5. 进阶:把 v2.2.2 改造成可观测、可调优的测试平台
5.1 用 tracepoint 替代 printk 做低开销观测
v2.2.2 默认用printk打日志,debug_level=3时每包都打,在高速链路上直接拖垮系统。我的习惯是保留printk做粗粒度,同时加一个tracepoint。在include/aodv.h里定义TRACE_EVENT(aodv_rreq, ...),在aodv_socket.c的 RREQ 处理分支里调用trace_aodv_rreq(...)。然后用户态用trace-cmd record -e aodv:*抓取,开销比printk低一个数量级。这个改动需要内核支持 tracepoint,3.10 以上都行。改完后debug_level可以降到 1,只保留错误日志。
5.2 参数调优的量化方法:用 iperf 和路由收敛时间做指标
不要凭感觉调hello_interval。搭一个 3 节点线性拓扑,A 到 C 经过 B。在 A 上跑iperf -c C -u -b 1M -t 60,同时用脚本每 100ms 读一次/proc/net/aodv/route记录路由是否存在。统计路由中断次数和平均恢复时间。然后改hello_interval从 1000 到 3000,重复测试。我的经验是:节点数少于 5 时,1000ms 的收敛最快;节点数 10 到 20 时,2000ms 的综合吞吐最高;超过 20 节点,3000ms 加net_diameter=5能减少广播碰撞。这些数字随无线环境变化,但方法可复现。
5.3 一个具体技巧:用 netlink 替代 proc 做配置下发
proc 接口在并发写入时会丢配置,而且没有确认机制。进阶做法是加一个 netlink 家族,在aodv.c的初始化里netlink_kernel_create,定义AODV_CMD_SET_PARAM和AODV_CMD_GET_ROUTE。用户态用libnl或直接socket(AF_NETLINK, SOCK_RAW, NETLINK_AODV)发送。这样配置有 ACK,路由查询可以批量返回,适合自动化测试。改造成本大约 200 行代码,但后续做参数扫描时省很多事。我一般会先保留 proc 做兼容,netlink 作为主通道。
5.4 验证改造是否成功的三个检查点
第一,trace-cmd report里能看到 RREQ、RREP、RERR 事件,且时间戳间隔合理。第二,iperf在参数调优后路由中断次数下降 50% 以上。第三,rmmod时没有 oops,dmesg干净。如果这三点都过,说明 v2.2.2 已经从一个“能跑就行”的旧包,变成了可观测、可调优的测试平台。我自己的习惯是每次改完内核模块,先跑 30 分钟稳定性测试,再上真实场景。希望帮到你。
本文还有配套的精品资源,点击获取