构建调试Linux内核网络代码的环境MenuOS系统

1 环境准备&切换内核

准备环境,将Ubuntu 18.04的内核更换为5.0.1。

首先编译内核,依次执行以下命令:

$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.1.tar.xz #下载内核源码
$ xz -d linux-5.0.1.tar.xz
$ tar -xvf linux-5.0.1.tar
$ cd linux-5.0.1
$ sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev #安装编译工具
$ make defconfig #按照默认配置生成.config
$ make menuconfig # Kernel hacking—>Compile-time checks and compiler options  ---> [*] Compile the kernel with debug info 
$ make -j*       # *为cpu核心数,为加快编译速度推荐尽可能多的核心数

编译之后建议通过系统快照备份系统。然后更换内核,执行以下命令:

$ sudo make modules_install
$ sudo make install
$ sudo update-grub
$ reboot
$ uname -a

这时,内核就切换好了,结果如下:

内核切换为5.0.1之后,安装qemu虚拟机,开始构造MenuOS。依次执行以下命令:

$ sudo apt install qemu
$ git clone https://github.com/mengning/menu.git
$ cd menu
$ sudo apt-get install libc6-dev-i386 # 在64位环境下编译32位需安装
$ make rootfs
$ cd ~
$ qemu-system-x86_64 -kernel linux5.0.1/arch/x86_64/boot/bzImage  -initrd rootfs.img

此时,qemu虚拟机就运行起来了:

2 装载MenuOS

接下来基于BusyBox构造Linux系统:

$ wget https://busybox.net/downloads/busybox-1.30.1.tar.bz2
$ tar -xvf busybox-1.30.1.tar.bz2
$ cd busybox-1.30.1/
$ make help#可以得到一些编译busybox的帮助信息
$ make defconfig
$ make menuconfig#修改如下配置:enable:Settings –> build options –> build busybox as a static binary(no share libs)
$ make -j4

然后准备根目录映像,并安装busybox到根目录映像中:

$ dd if=/dev/zero of=rootfs.img bs=1M count=128
$ mkfs.ext4 rootfs.img
$ mkdir rootfs
$ sudo mount -o loop rootfs.img rootfs
$ ~/busybox-1.30.1$ sudo make CONFIG_PREFIX=../rootfs/ install
$ sudo umount rootfs
$ qemu-system-x86_64 -kernel linux-5.0.1/arch/x86_64/boot/bzImage -hda rootfs.img -append "root=/dev/sda init=/bin/ash"

这时,MenuOS就装载好了。

3 构建MenuOS网络功能

将 TCP 服务端集成到 MenuOS 系统中:

$ cd ~  
$ git clone https://github.com/mengning/linuxnet.git
$ cd linuxnet/lab3
$ make
$ make rootfs

这时MenuOS增添了replyhi和hello命令。

4 构建Linux内核的gdb调试环境

重新配置编译内核使之携带调试信息:

$ cd ~/linux-5.0.1
$ make defconfig
$ make menuconfig
$ Kernel hacking—>Compile-time checks and compiler options  ---> [*] Compile the kernel with debug info 
$ make -j4#重新编译(时间较长)

再修改一下~/linuxnet/lab3/Makefile文件,将内核版本修改为5.0.1。测试一下新添加的replyhi和hello命令:

再在Makefile中的qemu命令中添加-append nokaslr -s -S参数,修改完成后,重新打开Qemu。

$ cd ~/linuxnet/lab3
$ make rootfs
#保持当前终端和Qemu运行,重新打开一个终端,运行gdb
$ gdb
(gdb)file ~/LinuxKernel/linux-5.0.1/vmlinux
(gdb)target remote:1234
(gdb)break start_kernel#设置断点对start_kernel进行跟踪
(gdb)c
(gdb)list

这时可以看到start_kernel函数的上下文,说明调试环境已经搭建好了:

接下来我们依照这个方法追踪hello/hi程序对socket的调用。

5 调试网络程序

我们将断点设置在 TCP 协议中的tcp_v4_connect 函数和 inet_csk_accept 函数上。

$ cd ~/linuxnet/lab3
$ make rootfs
#保持当前终端和Qemu运行,重新打开一个终端,运行gdb
$ gdb
(gdb)file ~/linux-5.0.1/vmlinux
(gdb)target remote:1234
(gdb)break tcp_v4_connect
(gdb)break inet_csk_accept
(gdb)c
MenuOS>>replyhi#在MenuOS中执行
MenuOS>>hello#在MenuOS中执行
(gdb)list

这时可以看到tcp连接建立时的上下文

(gdb) c
Continuing.

Breakpoint 4, tcp_v4_connect (sk=0xffff888006520880, uaddr=0xffffc90000043e20, 
    addr_len=16) at net/ipv4/tcp_ipv4.c:203
203	{
(gdb) list
198		return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr);
199	}
200	
201	/* This will initiate an outgoing connection. */
202	int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
203	{
204		struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
205		struct inet_sock *inet = inet_sk(sk);
206		struct tcp_sock *tp = tcp_sk(sk);
207		__be16 orig_sport, orig_dport;

(gdb) c
Continuing.

Breakpoint 5, inet_csk_accept (sk=0xffff888006520000, flags=2, 
    err=0xffffc900006efddc, kern=false) at net/ipv4/inet_connection_sock.c:442
442	{
(gdb) list
437	
438	/*
439	 * This will accept the next outstanding connection.
440	 */
441	struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
442	{
443		struct inet_connection_sock *icsk = inet_csk(sk);
444		struct request_sock_queue *queue = &icsk->icsk_accept_queue;
445		struct request_sock *req;
446		struct sock *newsk;

TCP 的三次握手从用户程序的角度看就是客户端 connect 和服务端 accept 建立起连接时背后完成的工作,在内核 socket 接口层这两个 socket API 函数对应着 sys_connect 和 sys_accept 函数,进一步对应着 sock->opt->connect 和 sock->opt->accept 两个函数指针,在 TCP 协议中这两个函数指针对应着 tcp_v4_connect 函数和 inet_csk_accept 函数。

这时用于调试Linux内核网络代码的环境MenuOS系统就搭建完成了,可以清楚地看到两个 socket API 函数对应着 sys_connect 和 sys_accept 函数在 TCP 协议中这两个函数指针对应着 tcp_v4_connect 函数和 inet_csk_accept 函数。

参考资料

https://github.com/mengning/net

posted @ 2019-12-10 19:07  Litosty  阅读(783)  评论(0编辑  收藏  举报