# CSE791 IoT: Notes for Mar 27, 2020

Why are we stuyding Linux drivers?

• To write TUN/TAP equivalence for arbitrary protocol.
• Allows tunneling for custom protocol.

## Three types of devices in Linux:

• char devices
• A device that can be accessed as a stream of bytes.
• Implements open, close, read, write system calls.
• Accessed by means of filesystem nodes (e.g. /dev/ttyS0, /dev/tun).
• Usually only allows sequantial access, not random access
• block devices
• A device that can hold a filesystem.
• Operates on blocks (e.g. 512 KB).
• Provides similar API as char devices to the user.
• network interface
• Can be hardware or pure software device.
• Network drivers know nothing about the connections. They only handle packets.

## Kernel programming

• Kernel programs do not link against libc libraries. It only links against the kernel. Therefore, nearly all common headers we use are not allowed in kernel programs. (That is, no malloc, no memset, no printf, etc.) This also makes C++ totally unfeasible in the kernel.
• Kernel programs must be concurrent. Data structures must be designed to keep multiple threads of execution separate.
• Kernel programs share kernel’s stack, which is very small (usually 4096B). Large structures needs to be dynamically allocated.
• Kernel programs do not support floating point arithmetic. Only integer types are allowed.

## A “correct” kernel build file

ifneq ($(KERNELRELEASE),) obj-m := hello.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd) default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

• This Makefile is actually called twice in the building process.
• In the first pass, the $(MAKE) command executes the kernel build system. -C changes directory to the kernel source directory, M moves back into module source directory before building modules target. • In the second pass, the kernel build system examines the Makefile to determine build targets and dependencies. ## A kernel module example #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_int(void){ printk(KERN_ALERT "Hello\n"); } static void hello_exit(void){ printk(KERN_ALERT "Goodbye\n"); } static void module_function(void){ // ... } module_init("hello_init"); module_exit("hello_exit");  • Race condition: race condition is everywhere in the kernel. It is possible that during the halfway of your initialization function, after you register a module function, the function is called right away before the initialization function finishes. Therefore, a lot of work needs to be done to ensure a kernel module works properly. • Sometimes, there will be tokens before kernel function declarations: #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int __init hello_int(void){ printk(KERN_ALERT "Hello\n"); } static void __exit hello_exit(void){ printk(KERN_ALERT "Goodbye\n"); } module_init("hello_init"); module_exit("hello_exit");  • __init token: the module loader will drop the function after initialization to save memory space. • __exit token: specify that this function is to be called when unloading. If this kernel module is built directly into the kernel, the function body will be discarded. • If you do not define a exit function, the kernel will not allow the module to be unloaded. • Module parameters: a module can take the following types of parameters when loaded: bool, charp, and integer types. It is registered in the kernel module code. This allows the user to change the behavior of the kernel module when loading it. static char *whom; static int howmany; module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO);  For example, when we are loading the virtual WPAN interface, we can specify number of interfaces with module parameters. modprobe fakelb numlbs=2  ## Metadata A kernel module usually contains some descriptive metadata. We can show the metadata of a kernel module using modinfo. Showing kernel module metadata: (base) user@machine:~$ modinfo /lib/modules/4.15.0-88-generic/kernel/drivers/acpi/video.ko
filename:       /lib/modules/4.15.0-88-generic/kernel/drivers/acpi/video.ko
description:    ACPI Video Driver
author:         Bruno Ducrot
srcversion:     1E58EF1A1E4284895F3AF07
alias:          acpi*:LNXVIDEO:*
depends:
retpoline:      Y
intree:         Y
name:           video
signat:         PKCS#7
signer:
sig_key:
sig_hashalgo:   md4
parm:           brightness_switch_enabled:bool
parm:           allow_duplicates:bool
parm:           disable_backlight_sysfs_if:int
parm:           report_key_events:0: none, 1: output changes, 2: brightness changes, 3: all (int)
parm:           hw_changes_brightness:Set this to 1 on buggy hw which changes the brightness itself when a hotkey is pressed: -1: auto, 0: normal 1: hw-changes-brightness (int)
parm:           device_id_scheme:bool
parm:           only_lcd:int


A well-known one is MODULE_LICENSE. If MODULE_LICENSE is not called with a open source license, the kernel module will be considered proprietary. This would put restrictions on the kernel module.

## Kernel symbol table

The table contains global kernel items—functions and variables—that are needed to implement modularized drivers. The public symbol table can be read in text form from the file /proc/kallsyms.

(base) user@machine:/proc\$ sudo more /proc/kallsyms
0000000000000000 A irq_stack_union
0000000000000000 A __per_cpu_start
0000000000004000 A cpu_debug_store
0000000000005000 A cpu_tss_rw
0000000000008000 A gdt_page
0000000000009000 A exception_stacks
000000000000e000 A entry_stack_storage
000000000000f008 A espfix_stack
000000000000f010 A cpu_llc_id


A kernel module can put new symbols into the symbol table with EXPORT_SYMBOL or EXPORT_SYMBOL_GPL. the latter call will make the symbol visible to GPL-licensed modules only.

EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);


Once a symbol is put into the table, it is available across all kernel modules. For example, if the kernel module is a USB driver, it will add an entry for read_usb function into the symbol table for other drivers to use.

• There are two utilities for loading kernel modules: insmod and modprobe. Kernel module loaders act like ld (the dynamic linker) for user-space programs. They would link the compiled kernel module against the functions provided by the kernel. No user-space library is involved.
• The difference between insmod and modprobe is: insmod will only load the current module. If the module has dependencies, it will stop and report an unresolved reference error. modprobe however, will automatically load dependencies.

## Difference between network interface and char/block device

• Unix philosophy: everything is a file. However, network interfaces cannot be designed as simple files.
• Multiple read, write operations on sockets can happen on one network interface.
• Block drivers operate only in response to the kernel. Network drivers receives packets asynchronously from outside, and asks the kernel to act.
• Network drivers also have to support administrative tasks, such as setting addresses, modifying transmission parameters and maintaining statistics.
• The Linux network subsystem is designed to be protocol independent. Therefore, it should be easy to write a driver for custom protocol.

## Analyzing loopback.c

loopback.c

• skb_orphan: If a buffer currently has an owner then we call the owner’s destructor function and make the skb unowned. The buffer continues to exist but is no longer charged to its former owner.
• skb_dst_force: it’s used for “refcounting” , krefs or refcounting is a kernel design pattern where a counter is incremented by 1 when some method references it , and decremented when he reference is released , to then free() the object.
• eth_type_trans: Used to determine the packet protocol id and insert it back in skb-> protocol