Differences

This shows you the differences between two versions of the page.

Link to this comparison view

isc:labs:kernel:tasks:01 [2021/11/24 15:48]
radu.mantu
isc:labs:kernel:tasks:01 [2021/12/02 14:09] (current)
radu.mantu
Line 1: Line 1:
 ==== 01. [??p] Prerequisites ==== ==== 01. [??p] Prerequisites ====
  
-=== [??p] Task A - Dependencies ​installation ​===+=== [??p] Task A - Dependencies ===
  
-<note important+Before we get down to it, there are a few packages that need to be installed. The following commands //should// work on Ubuntu 20.04 Desktop. While most of these probably came with the base system, we'll enumerate them here for the sake of completeness. If you're using other desktop environments,​ most of these should have a correspondent (probably with an identical name). 
-TODOUbuntu + 
-</note>+<code bash
 +[student@host]$ sudo apt update 
 +[student@host]$ sudo apt install curl make gcc git iptables dnsutils qemu-system debootstrap libxtables-dev 
 +</​code>​ 
 + 
 +In case you're wandering what any of these are: 
 +  * ''​curl'':​ CLI tool for fetching (HTTP) data from servers 
 +  * ''​make'':​ project build automation tool 
 +  * ''​gcc'':​ GNU C compiler 
 +  * ''​git'':​ CLI tool for content tracking & versioning 
 +  * ''​iptables'':​ user space interface to the kernel'​s packet filter 
 +  * ''​dnsutils'':​ contains **dig** which we'll need to send out DNS queries 
 +  * ''​qemu-system'':​ PC system emulator; if you don't have space for all off them, install only **qemu-system-x86** or **-arm** 
 +  * ''​debootstrap'':​ user space environment bootstrapping tool 
 +  * ''​libxtables-dev'':​ development files for the packet filtering framework; needed for //"​xtables.h"//​ 
 + 
 +Moving forward, we prepared a code skeleton for you: {{:​isc:​labs:​kernel:​tasks:​skeleton.zip|}}. It's structure is as follows: 
 + 
 +<code bash> 
 +[student@host]$ tree -L 2 skeleton  
 +skeleton                    --root of workspace 
 +├── 02/                        --> task 2: basically a "Hello World"​ 
 +│   ├── Kbuild 
 +│   ├── Makefile 
 +│   ├── my_first_module.c 
 +│   └── patches/ 
 +├── 03/                        --> task 3: an iptables extension 
 +│   ├── include/ 
 +│   ├── module/ 
 +│   └── plugin/ 
 +└── images/ ​                   --> task 1: VM hard disk images (empty) 
 + 
 +7 directories,​ 3 files 
 +</​code>​ 
 + 
 +At this time, there'​s no point in taking a more detailed look at each file. In stead, let us focus on the task at hand and prepare our testing environment.
  
 === [??p] Task B - Development environment === === [??p] Task B - Development environment ===
  
-When developing new features for the kernel, chances are that you will screw up. Often. Depending on the severity, the kernel may or may not recover. So to avoid restarting your PC over and over, it's better to work in a minimal virtualized environment. As such, we will first bootstrap a loopback disk image with a basic Ubuntu system, but without the kernel. Eventually, we will boot a virtual ​macine ​with **qemu-system-x86_64** from this disk image, with a custom kernel that we will build ourselves.+When developing new features for the kernel, chances are that you will screw up. Often. Depending on the severity, the kernel may or may not recover. So to avoid restarting your PC over and over, it's better to work in a minimal virtualized environment. As such, we will first bootstrap a loopback disk image with a basic Ubuntu system, but without the kernel. Eventually, we will boot a virtual ​machine ​with **qemu-system-x86_64** from this disk image, with a custom kernel that we will build ourselves.
  
 <​note>​ <​note>​
-The bootstrapping and kernel building process may take a few (~15) minutes. Feel free to jump to Exercise 2 and come back once in a while to see if any progress was made. While Task A is doable on your live kernel, make sure to stop there. Task B is meant to crash your system ​:p+The bootstrapping and kernel building process may take a few (~15) minutes. Feel free to jump to Exercise 2 and come back once in a while to see if any progress was made. While Task A is doable on your live kernel, make sure to stop there. Task B is meant to generate errors and should be solved in the VM. For your sake :p
 </​note>​ </​note>​
  
Line 30: Line 65:
  
 # bootstrat the Ubuntu system # bootstrat the Ubuntu system
-[student@host]$ sudo debootstrap --arch amd64 focal /mnt https://mirrors.kernel.org/ubuntu+[student@host]$ sudo debootstrap --arch amd64 focal /mnt http://archive.ubuntu.com/ubuntu
 </​code>​ </​code>​
  
Line 45: Line 80:
 passwd: password updated successfully passwd: password updated successfully
  
-# exit from this bash instance and escape from chroot+# while we're here, set Google DNS as primary DNS 
 +# qemu has a bug where it refuses to fall back to other resolvers 
 +# so it will be hard stuck on 127.0.0.53 ==> can't resolve domain names 
 +[   ​root@jail]$ echo '​nameserver 8.8.8.8'​ > /​etc/​resolv.conf 
 + 
 +# exit from this bash instance and escape from the chroot ​jail
 [   ​root@jail]$ exit [   ​root@jail]$ exit
  
-# finally, unmount our disk -- were done with it for now+# finally, unmount our disk -- we'​re ​done with it for now
 [student@host]$ sudo umount /mnt [student@host]$ sudo umount /mnt
 </​code>​ </​code>​
Line 54: Line 94:
 === Kernel building === === Kernel building ===
  
-Next step is to get the kernel source code and compile it. By separating the kernel from the disk image, we are able to checkout to other branches / commits and test out different versions without installing them anywhere. Normally, you would have to select what options you want included in the compilation process (e.g.: memory allocators, cryptographic systems, etc.) by running ''​make menuconfig''​. After finishing your selection and saving the configuration,​ a //.config// file would be created. Because we haven'​t the faintest idea what most of the things enumerated in that menu even are, we will rely on default configurations.+Next step is to get the kernel source code and compile it. By separating the kernel from the disk image, we are able to checkout to other branches / commits and test out different versions without installing them anywhere. Normally, you would have to select what options you want included in the compilation process (e.g.: memory allocators, cryptographic systems, etc.) by running ''​make menuconfig''​. After finishing your selection and saving the configuration,​ a //.config// file would be created. Because we haven'​t the faintest idea what most of the things enumerated in that menu even are, we will rely on default configurations. One thing that is not part of the default configuration and will be useful later are debug symbols. Go through the following commands and refer to the GIF below when you'll be required to navigate the config menu.
  
 <code bash> <code bash>
Line 65: Line 105:
 # create a default configuration file (.config) # create a default configuration file (.config)
 [student@host]$ make x86_64_defconfig kvm_guest.config [student@host]$ make x86_64_defconfig kvm_guest.config
 +
 +# manually enable debug symbols on top of current .config
 +# NOTE: refer to the GIF below
 +[student@host]$ make menuconfig
  
 # optional: check out the generated .config file # optional: check out the generated .config file
Line 70: Line 114:
  
 # compile the kernel using all cores # compile the kernel using all cores
-[student@host]$ make -j $(nproc) ​bzImage+[student@host]$ make -j $(nproc)
  
 # return to the previous direcotry # return to the previous direcotry
 [student@host]$ popd [student@host]$ popd
 </​code>​ </​code>​
 +
 +[[https://​ocw.cs.pub.ro/​courses/​_media/​isc/​labs/​kernel/​tasks/​menuconfig-demo.gif|{{ :​isc:​labs:​kernel:​tasks:​menuconfig-demo.gif?​700 |}}]]
 +<​html><​center><​i>​ Click GIF to maximize. </​i></​center></​html>​
  
 === Booting up the virtual machine === === Booting up the virtual machine ===
Line 81: Line 128:
  
 <code bash> <code bash>
-$ sudo qemu-system-x86_64 ​                           \ +[student@host]$ sudo qemu-system-x86_64 ​                           \ 
-    -m 4G                                            \ +                  -m 4G                                            \ 
-    -smp 1                                           \ +                  -smp 1                                           \ 
-    -enable-kvm ​                                     \ +                  -enable-kvm ​                                     \ 
-    -kernel linux/​arch/​x86/​boot/​bzImage ​             \ +                  -kernel linux/​arch/​x86/​boot/​bzImage ​             \ 
-    -drive file=images/​ubuntu.raw,​format=raw,​index=0 \ +                  -drive file=images/​ubuntu.raw,​format=raw,​index=0 \ 
-    -append '​root=/​dev/​sda rw console=ttyS0' ​        ​+                  -append '​root=/​dev/​sda rw console=ttyS0 ​nokaslr' \ 
-    -nographic+                  -nographic
 </​code>​ </​code>​
  
Line 96: Line 143:
   - ''​-enable-kvm'':​ [[https://​www.redhat.com/​en/​topics/​virtualization/​what-is-KVM|KVM]] is a Linux kernel module that transforms your operating system intro a bare-metal hypervisor. This is what allows you to run __actual virtual machines__ on Linux. Without it, **qemu** would try to __emulate__ the system, resulting in worse performance.   - ''​-enable-kvm'':​ [[https://​www.redhat.com/​en/​topics/​virtualization/​what-is-KVM|KVM]] is a Linux kernel module that transforms your operating system intro a bare-metal hypervisor. This is what allows you to run __actual virtual machines__ on Linux. Without it, **qemu** would try to __emulate__ the system, resulting in worse performance.
   - ''​-kernel .../​bzImage'':​ this specifies the compiled & compressed kernel image to use when booting the virtual machine   - ''​-kernel .../​bzImage'':​ this specifies the compiled & compressed kernel image to use when booting the virtual machine
-  - ''​-drive ...''​ : specifies the disk image to load; note that ''​intex=0''​ will make the VM consider this to be //​%%/​dev/​sda%%//​. Adding another drive with ''​index=1''​ will cause it to be regarded as //​%%/​dev/​sdb%%//​. +  - ''​-drive ...''​ : specifies the disk image to load; note that ''​index=0''​ will make the VM consider this to be //​%%/​dev/​sda%%//​. Adding another drive with ''​index=1''​ will cause it to be regarded as //​%%/​dev/​sdb%%//​. 
-  - ''​-append ...'':​ these are command line arguments for the kernel (yes, even it has those). ''​root=/​dev/​sda rw''​ marks //​%%/​dev/​sda%%//​ (i.e.: our //​Ubuntu.raw//​ disk image) as the root device to be mounted onto the root directory (i.e.: //%%/%%//) in read-write mode. ''​console=ttyS0''​ exposes an [[https://​en.wikipedia.org/​wiki/​Universal_asynchronous_receiver-transmitter|UART]] serial interface to the VM and tells Linux to use it for I/O.+  - ''​-append ...'':​ these are command line arguments for the kernel (yes, even it has those). ''​root=/​dev/​sda rw''​ marks //​%%/​dev/​sda%%//​ (i.e.: our //​Ubuntu.raw//​ disk image) as the root device to be mounted onto the root directory (i.e.: //%%/%%//) in read-write mode. ''​console=ttyS0''​ exposes an [[https://​en.wikipedia.org/​wiki/​Universal_asynchronous_receiver-transmitter|UART]] serial interface to the VM and tells Linux to use it for I/O. ''​nokaslr''​ tells the kernel to disable address space layout randomization.
   - ''​-nographic'':​ tells **qemu:** not to open a separate window for the GUI. In stead, it will take the virtual serial device (which the VM will recognize as //ttyS0//) and link it to the terminal. So whatever the VM sends via the serial to be printed will end out in your //stdout//. Whatever you type into //stdin// will be forwarded to the VM as input.   - ''​-nographic'':​ tells **qemu:** not to open a separate window for the GUI. In stead, it will take the virtual serial device (which the VM will recognize as //ttyS0//) and link it to the terminal. So whatever the VM sends via the serial to be printed will end out in your //stdout//. Whatever you type into //stdin// will be forwarded to the VM as input.
  
Line 106: Line 153:
 </​note>​ </​note>​
  
-After starting the VM and logging in as //root// (with the password set earlier), try finding out the kernel version in both the host and guest operating systems:+After starting the VM and logging in as //root// (with the password ​that was set earlier), try finding out the kernel version in both the host and guest operating systems:
  
 <code bash> <code bash>
 # host has the latest Arch Linux kernel (you may have Ubuntu, etc.) # host has the latest Arch Linux kernel (you may have Ubuntu, etc.)
-[ host]$ uname -r+[student@host]$ uname -r
 5.15.2-arch1-1 5.15.2-arch1-1
  
 # guest has the newest Linux kernel release candidate # guest has the newest Linux kernel release candidate
-[guest]$ uname -r+ root@guest]$ uname -r
 5.16.0-rc2+ 5.16.0-rc2+
 </​code>​ </​code>​
 +
 +<note important>​
 +Note how we did not specify a network device to **qemu**. By default, SLiRP is used to provide network connectivity. If you've never heard of SLiRP, don't feel bad. It's a program that emulates Point-to-Protocol (PPP) using shell accounts and has become largely obsolete with the advent of dial-up modems (I kid you not). While it does provide TCP and UDP connectivity,​ note that ICMP packets will be dropped and your VM will not be discoverable;​ not even from your host.
 +
 +The correct way of doing things would be creating a bridge (i.e.: a software layer-2 switch) with **brctl**, adding a network device to your VM via the ''​-netdev''​ flag, and attaching it to the newly created bridge. This is a bit overkill for our purpose today. If you ever need to create such a setup, there are plenty of [[https://​www.linux-kvm.org/​page/​Networking|resources]] available.
 +
 +----
 +
 +Although we said that you //should// have network access in your VM, there //may// be a chance that you don't have an IP address assigned. You may need to do this manually:
 +
 +<code bash>
 +# list available interfaces (in a colorful fashion)
 +[root@guest]$ ip -c addr show
 +1: lo: <​LOOPBACK,​UP,​LOWER_UP>​ mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
 +    link/​loopback 00:​00:​00:​00:​00:​00 brd 00:​00:​00:​00:​00:​00
 +    inet 127.0.0.1/8 scope host lo
 +       ​valid_lft forever preferred_lft forever
 +    inet6 ::1/128 scope host 
 +       ​valid_lft forever preferred_lft forever
 +2: enp0s3: <​BROADCAST,​MULTICAST>​ mtu 1500 qdisc noop state DOWN group default qlen 1000
 +    link/ether 52:​54:​00:​12:​34:​56 brd ff:​ff:​ff:​ff:​ff:​ff
 +3: sit0@NONE: <​NOARP>​ mtu 1480 qdisc noop state DOWN group default qlen 1000
 +    link/sit 0.0.0.0 brd 0.0.0.0
 +
 +# send a DHCP request on your Ethernet interface
 +[root@guest]$ dhclient enp0s3
 +
 +# check if an IP address was allocated
 +[root@guest]$ ip -c addr show enp0s3
 +2: enp0s3: <​BROADCAST,​MULTICAST,​UP,​LOWER_UP>​ mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
 +    link/ether 52:​54:​00:​12:​34:​56 brd ff:​ff:​ff:​ff:​ff:​ff
 +    inet 10.0.2.15/​24 brd 10.0.2.255 scope global dynamic enp0s3
 +       ​valid_lft 86313sec preferred_lft 86313sec
 +    inet6 fec0::​5054:​ff:​fe12:​3456/​64 scope site dynamic mngtmpaddr ​
 +       ​valid_lft 86318sec preferred_lft 14318sec
 +    inet6 fe80::​5054:​ff:​fe12:​3456/​64 scope link 
 +       ​valid_lft forever preferred_lft forever
 +</​code>​
 +</​note>​
isc/labs/kernel/tasks/01.1637761737.txt.gz · Last modified: 2021/11/24 15:48 by radu.mantu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0