开发linux kernel是一件很有趣的事情,但缺乏调试工具往往会使这一过程也充满痛苦。QEMU作为一种虚拟化方案,提供了一种将kernel运行在user space,利用gdb进行调试的方法。善加利用,可以大大提高效率。

关闭编译linux kernel的优化

linux kernel不支持关掉编译优化选项,这给直接用gdb调试造成很多困难。常常会发现想要看的变量的值被编译器优化掉了。这里提供一个patch,可以将linux kernel的编译优化级别设置为'-Og',这样能解决掉大部分因优化引入的问题。

1
2
General setup
-> Compiler optimization level

选择“DEBUG”就可以使能这一功能。 这里提供一个相对精简的linux kernel config,里面包含了大部分网络相关的功能。

准备initial ram disk

modify initramfs_desc

1
2
3
4
5
dir     /bin                                      0755 0 0
nod     /dev/zero                                 0666 0 0 c 1 5
slink   /bin/sh         busybox             0755 0 0
file    /sbin/busybox   /sbin/busybox             0755 0 0
file    /init           initramfs/init.sh         0755 0 0

run command gen_init_cpio

1
usr/gen_init_cpio initramfs_desc | gzip > initramfs.img

为了准备一些网络工具,我们需要把他们编译称静态的版本。

  • iperf

    1
    
    ./configure  "LDFLAGS=--static"  --without-openssl  --enable-static --disable-shared ; make ;
    

boot kernel with qemu

1
qemu-system-x86_64  -nographic  -kernel arch/x86/boot/bzImage -initrd initramfs.img -append  "console=ttyS0 nokaslr" -s

‘nokaslr’ 的目的是为了能够用gdb调试,没有它你会发现断点不能正常工作。

network

QEMU的网络配置可以分为前端和后端。前端的网络配置是指在如何创建Guest OS中可见的网卡,而Guest OS中的网卡要想和Host OS的网络进行通信,或多个Guest OS之间想要互相通信,就需要通过后端的网络配置,在Host OS里创建和Guest OS里的网卡对应的网卡。这样在Guest OS中网卡收到的数据,就会被QEMU传递给Host OS中对应的网卡。

TAP NIC + Bridge

最常见的配置方法,就是在Host网络中创建一个单独的bridge, 然后为每一个Guest OS中的网卡在Host OS中创建一个类型为TAP的网卡, 然后将这些TAG网卡都挂载在bridge下。这种方法开销小,传输速度快,有一点麻烦的地方,就是每次启动虚拟机时,都要将Host OS中创建的网卡挂载在bridge下。 不过QEMU提供了一个脚本,来帮助我们自动完成这个工作。

配置‘qemu-bridge-helper’

1
2
3
4
sudo ip link add name br0 type bridge
sudo ip link set dev br0 up
echo "allow br0" >> /etc/qemu/bridge.conf
sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper

启动虚拟机,配置网络

1
qemu-system-x86_64 -nographic  -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append  "console=ttyS0  nokaslr"  -net nic,netdev=n1,model=virtio,macaddr=52:54:00:12:34:56  -netdev tap,id=n1,"helper=/usr/lib/qemu/qemu-bridge-helper" -s

这里,'-netdev tap,id=n1,“helper=/path/to/qemu-bridge-helper”' 的作用是在Host网络中创建一个类型为TAP的网卡,其id为’n1',调用 qemu-bridge-helper脚本将其自动挂载在我们之前在'/etc/qemu/bridge.conf’中配置的’br0’上。 ’-net nic,netdev=n1,model=virtio,macaddr=52:54:00:12:34:56’的含义是在Guest OS里创建一个网卡,使用的driver是 virtio,通过‘netdev=n1’, 这个虚拟网卡会和Host OS里的网卡连接起来。

ipv4 only

主机A:

1
2
qemu-system-x86_64 -nographic  -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append  "console=ttyS0  nokaslr"  -net nic,model=virtio,macaddr=52:54:00:12:34:56  -net  socket,listen=localhost:12345
ifconfig eth0 10.0.0.1

主机B:

1
2
qemu-system-x86_64 -nographic  -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append  "console=ttyS0  nokaslr"  -net nic,model=virtio,macaddr=52:54:00:12:34:58   -net  socket,connect=localhost:12345
ifconfig eth0 10.0.0.2

reference

http://www.h7.dion.ne.jp/~qemu-win/HowToNetwork-en.html

https://blog.nelhage.com/2013/12/lightweight-linux-kernel-development-with-kvm/

debug

gdb ./vmlinux target remote localhost:1234 continue

reference

https://lwn.net/Articles/660404/

IPv6 Big Packets issue with qemu

setup

three qemu virtual machine.

  1. machine A with two nics connect to B and C qemu-system-x86_64 -nographic -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append “console=ttyS0 nokaslr” -net nic,model=virtio,macaddr=52:54:00:12:34:56 -net socket,listen=localhost:12345 -net nic,model=virtio,macaddr=52:54:00:12:34:57 -net socket,listen=localhost:54321

    ip -6 addr add 2200:fe00::1/64 dev eth0 ip -6 addr add 4400:fe00::1/64 dev eth1

    ip6tables -I FORWARD -t filter -p ipv6-icmp -m icmp6 –icmpv6-type 1 -j ACCEPT ip6tables -I FORWARD -t filter -p ipv6-icmp -m icmp6 –icmpv6-type 2 -j ACCEPT ip6tables -I FORWARD -t filter -p ipv6-icmp -m icmp6 –icmpv6-type 3 -j ACCEPT ip6tables -I FORWARD -t filter -p ipv6-icmp -m icmp6 –icmpv6-type 128 -j ACCEPT ip6tables -I FORWARD -t filter -p ipv6-icmp -m icmp6 –icmpv6-type 129 -j ACCEPT

  2. machine B connect to A qemu-system-x86_64 -nographic -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append “console=ttyS0 nokaslr” -net nic,model=virtio,macaddr=52:54:00:12:34:58 -net socket,connect=localhost:12345 ip -6 addr add 2200:fe00::2/64 dev eth0 ip -6 route add default via 2200:fe00::1

  3. machine C connect to A qemu-system-x86_64 -nographic -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append “console=ttyS0” -net nic,model=virtio,macaddr=52:54:00:12:34:59 -net socket,connect=localhost:54321 ip -6 addr add 4400:fe00::2/64 dev eth0 ip -6 route add default via 4400:fe00::1

  4. enable ipv6 forward sysctl -w net.ipv6.conf.all.forwarding=1

  5. load ko symbol add-symbol-file drivers/mydrivers/mydriver.o 0xbf098000

  6. run

vlan config with qemu

two qemu virtual machine

  1. create bridge sudo brctl addbr br0 ifconfig br0 up
  2. config bridge helper so that tap device can be created without root previledge sudo chmod u+s /usr/local/libexec/qemu-bridge-helper sudo echo “allow br0” > /usr/local/etc/qemu/bridge.conf
  3. machine A with one nic connect to bridge br0 qemu-system-x86_64 -s -nographic -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append “console=ttyS0” -net nic,model=virtio,macaddr=52:54:00:12:34:56 -net bridge,br=br0
  4. machine B with one nic connect to bridge br0 qemu-system-x86_64 -nographic -kernel arch/x86/boot/bzImage -initrd ./initramfs.img -append “console=ttyS0” -net nic,model=virtio,macaddr=52:54:00:12:34:58 -net bridge,br=br0 ifconfig eth0 10.0.0.1
  5. ping from B to A

run qemu with bridge network which connected to gateway and an extented PC reachable

config on qemu

ifconfig eth0 192.168.3.101 route add 192.168.3.100 dev eth0 route add -net 0.0.0.0 gw 192.168.3.100 dev eth0

config on host

sudo brctl addbr br0 sudo ifconfig br0 up sudo ifconfig br0 192.168.3.100 sudo route add -net 200.200.200.0/24 dev eth1 gw 192.168.0.1 sudo echo 1 > /proc/sys/net/ipv4/ip_forward

config on gateway

route add -net 192.168.3.0 netmask 255.255.255.0 gw 192.168.0.100 dev br-lan