这个事情的 motivation 是被 harry 拉去上了一门叫 “高等计算机网络“ 的课, 而 harry 和 gyc 是本科生网络原理课的助教, 于是大作业的内容就是抱着 harry 的大腿给小朋友们做的硬件实验做一个测试平台. (harry: 你的老本行, 造 oj) 因为 laekov 的硬件水平四舍五入等于没有 (gg) 所以负责写前端.
VUE + ChartJS = gg
laekov 多年没写过前端了, 没想到 angular 都已经 gg 了. 在 “尽量轻量化, 以后烧进 flash” 的指导思想 (还有 harry 的 push) 下选择了 vue.
功能里有一个重要的需求是画一张同学们的路由器带宽-帧大小的曲线. 要是有 matplotlib 就好了, 然而既然是在写 web 就只能搞 js 的轮子了.
然而 chartjs 需要给它一个生的 canvas DOM object. 但是 vue 的制造者似乎就是非常不喜欢开发者拿生的 DOM, 甚至把 document 对象都给屏蔽了. (????)
还好 vue 提供了 $ref
勉强能搞到生的 dom. 然后新的问题来了, 如何在 v-for
遇到 “折线图” 这种元素的时候去让 chartjs 画个图? 一个问题是 v-for
执行过程中 DOM 元素是不完整的. 这时候拿 chartjs 去画图会报错. 解决方案自然是, setTimeout
一会再画…
虽然 laekov 的前端水平是很山寨的, 不过总之参照 vtune 的 web 前端糊出了一个 tanlabs-speed-tester 的前端 like this.
为啥 ping 包发不出去啊? 请您配 netns
临近期末报告, harry 终于把测速硬件造好了, laekov 一直以为自己写个 html 页面就完事了, 直到 harry 告诉 laekov 前面的配 rip, ping 也都是 laekov 的工作.
于是菜 laekov 开始 xio 习如何给 linux 配 ip.(雾
如题所示, laekov 发现的第一个问题是无法通过一根网线从一个端口 ping 另一个端口, harry 告诉 laekov 原因是 kernel 会觉得 laekov 在瞎搞, 并试图把包直接发到 lo.
这个时候就需要 netns - network namespace. 这个功能可以把不同的 interface 关进不同的 namespace, 相当于拥有了若干个插着不同 interface 的机器.
然而发现 gyc 配的 zynq petalinux kernel 并没有 netns 支持? 于是 harry 教 laekov 在 linux kernel 源码里 make menuconfig
, 然后发现怎么 config 没生效??? gyc: 请使用 petalinux-config -c kernel
. 星.
有了 netns 之后就可以把不同的 interface 分隔开用了, 也可以 ping 通网线了.
怎么发路由? 请您配鸟. 什么鸟东西?
同学的路由器需要支持 RIP 协议收发路由.
虽然 laekov 本科的时候也造过 RIP, 但是时隔多年显然是无法使用的, 而且 laekov 显然没有好好读 RFC. (harry: pia) 在 harry 的指导下从 jiegec 那里抄来了一份 bird 的 RIP 配置. bird 是一个集成了很多 protocol 的网络工具, 可以通过配置的方式来发路由. 只要能编译出一个 arm 上的 bird, 问题似乎就解决了?
bird 使用了 autoconf. 而 petalinux 的 build 工具也支持 autotools. 于是 laekov 开心地写了一个 bbfile, 然后开始 petalinux-build
. 然后就发现, ???, 为啥 automake 挂了? 为啥你没有 AC_MSG_ERROR
? 是不是缺了什么依赖包? (尝试在 host 上编译 bird 的时候仿佛 apt 了一大堆东西比如 ncurses). 折腾了一天的 bird 无果. 最后只好走了另一条路,
CC=arm-linux-gnueabi-gcc-8 LDFLAGS='-static -static-libgcc' ../configure --host=arm-linux-gnueabi --disable-client
虽然并不优美但是总之能编译出能在嵌入式 arm 系统里跑的 bird 和 birdcl 了. birdcl 是一个没有 ncurses 所以不能按上键和 tab 自动补全但是还算能用的 birdc (一个控制 bird 的类 shell). 开心地跑起来, 发现没有收到路由??? 检查配置文件是对的啊??? log 也打出来了啊? 为啥 tcpdump 不出来呢?
因为怀疑是 bird2 和 jigec 的 bird1 配置文件不兼容 (虽然魔改了但是不知道魔改得对不对), 于是 laekov 果断 git checkout v1.6.3
重新编译然后直接用 jiegec 的配置文件, 果然 work 了! 赞.
被鸟打爆了? 鸟被打爆了?
一边开发测试仪一边就有同学来测试测试仪了. 然而当 laekov 在 RIP 里面开心地打了一个 200
之后同学表示他们只收到了五条路由? 同学拿出了自己的测试脚本告诉 laekov, “你看, 我自己每发一条睡一会, 可以发出 2k 条路由!” laekov 表示慌张, 同学表示是不是 laekov 的鸟发路由发得太快了, 毕竟同学们是在自己造的 CPU 上跑 RIP, 同学的路由器上的 LED 还在疯狂闪烁(表示丢包了). 于是 twd2 发出指示: 请给鸟限速.
然而某科学的喵喵鸟由于 rust 交叉编译的原因一直牌咕咕的状态. 所以 laekov 只能魔改普通鸟. laekov 在鸟发包的时候加了 usleep
, 然后发现睡时间短了没用, 睡久了 birdcl 就无法通过 unix socket 控制鸟了. 十分完蛋.
此时 twd2 和 gyc 发现 iproute2 里面有一个功能叫 tc, 全称叫 traffic control, 可以对 interface 进行限速! twd2 还很良心地向 laekov 提供了一段神秘 tc 代码, 据称可以将端口发送速率限制到 1.4Mbps.
然而 gyc 的 petalinux 依然没有 tc 支持. gyc 向 laekov 发送了一段神秘的 linux kernel config, 并告诉 laekov 只要配上它们就可以了.
laekov 在配置 kernel 的时候不幸 menuconfig 卡死了, 杀掉之后发现所有 config 都没了, 只好从头再配.
laekov 发现经常有 menuconfig 选项并没有出现在列表里, 原因是它们的依赖没有被满足. 于是工具人 laekov 熟练掌握了递归配置 linux kernel config 依赖的 function.
加上一堆 gyc 指定的一堆 tcp congestion control 的 kernel config 之后, tc 脚本的错误信息发生了一些变化: failed to talk to kernel
???
于是 laekov 从一个(已经找不到了)的 github kernel config commit 里把一些别的神秘的 =y
的 config 都给配上了, 然后 tc 就 work 了! 终于可以给鸟限速了.
然后刚才那组同学回来测, 发现还是无法收到全部路由? laekov 尝试每次让鸟随机发一些路由, 而不是按顺序发, 虽然可以缓慢地收到了, 但是感觉不太对?
几天之后, 同学 report: 他们 drop 了所有包含 16
条以上 RIP 记录的 RIP 包. 所以所有整个的 RIP 包 (25条) 都被 drop 了. 所以只剩 n mod 25
条记录可以被写入. laekov: ????????
jiegec 挂科了
由于喵喵鸟彻底咕了, 所以 laekov 被 twd2 push 从真实的路由表里拿路由, 而不是发一些乱七八糟的东西. 于是 laekov 从 twd 那里拿了一份路由表配置到自己的端口上, 并写了一个随机 ip 生成器来生成指定子网里的 ip 以供 ping 测试.
然后当 laekov 接上 jiegec 的软路由参考实现来测试的时候, 神奇的事情发生了: 一条 /23 的记录下随机生成的某些 ip 无法被正确 ping 通!??? jiegec 的 router 打出 log: mask len: 24
.
laekov 于是检查了 jiegec 的代码, 发现 jiegec 的 mask 竟然是 len = 32 - __builtin_clz(mask)
??? 而 mask 是 0x00feffff
. 所以 jiegec 的 router 处理所有非 8
的整数倍的 mask 都是错的. (为此 debug 了一下午)
后来听说 jiegec 用的所有测例都是 8
的整数倍 mask. >_>
Summary
在造测试仪的过程中学习了 buildroot 之外的另一个嵌入式 linux 编译工具, petalinux, 虽然难用, 但是至少可以配置 kernel config.
学习了如何使用 iproute 配 netns 和 tc.
学习了交叉编译鸟的时候的一些骚操作, 比如 static abi.
学习了新时代的前端框架 vue.
涨姿势了.