- Kernel module (Русский)
- Contents
- Обзор
- Получение информации
- Автоматическое управление модулями
- Управление модулями вручную
- Настройка параметров модуля
- С помощью файлов в /etc/modprobe.d/
- С помощью командной строки ядра
- Создание псевдонимов
- Запрет загрузки
- С помощью файлов в /etc/modprobe.d/
- С помощью командной строки ядра
- Networking¶
- Lab objectives¶
- Overview¶
- Networking in user space¶
- Linux networking¶
- The struct socket structure¶
- Operations on the socket structure¶
- The struct socket fields¶
- The struct sock structure¶
- The struct sk_buff structure¶
- Conversions¶
- netfilter¶
Kernel module (Русский)
Модули ядра — это отдельные кусочки кода, которые могут быть загружены и выгружены из ядра по мере необходимости. Они расширяют функциональность ядра без необходимости перезагрузки системы.
Contents
Обзор
Чтобы создать модуль ядра, вы можете прочитать The Linux Kernel Module Programming Guide. Модуль можно сконфигурировать как вкомпилированный, а можно как загружаемый. Чтобы иметь возможность динамически загружать или выгружать модуль, его необходимо сконфигурировать как загружаемый модуль в настройке ядра (в этом случае строка, относящаяся к модулю должна быть отмечена буквой M ).
Модули хранятся в /usr/lib/modules/kernel_release . Чтобы узнать текущую версию вашего ядра, используйте команду uname -r .
Получение информации
Чтобы узнать, какие модули ядра загружены в настоящий момент:
Чтобы показать информацию о модуле:
Чтобы вывести список опций, с которыми загружен модуль:
Чтобы отобразить настройки для всех модулей:
Чтобы отобразить настройки для отдельного модуля:
Чтобы узнать зависимости модуля (или его псевдонима), включая сам модуль:
Автоматическое управление модулями
Сегодня все необходимые загрузки модулей делаются автоматически с помощью udev, поэтому если вам не нужно загружать какие-либо модули, не входящие в стандартное ядро, вам не придётся прописывать модули, требующиеся для загрузки в каком-либо конфигурационном файле. Однако, бывают случаи, когда вам необходимо загружать свой модуль в процессе загрузки или наоборот не загружать какой-то стандартный модуль, чтобы ваш компьютер правильно функционировал.
Чтобы дополнительные модули ядра загружались автоматически в процессе загрузки, создаются статические списки в конфигурационных файлах в директории /etc/modules-load.d/ . Каждый конфигурационный файл называется по схеме /etc/modules-load.d/
.conf . Эти конфигурационные файлы содержат список названий модулей ядра, которые необходимо грузить, разделённых переносом строки. Пустые строки и строки, в которых первым непробельным символом является # или ; , игнорируются.
Смотрите modules-load.d(5) для дополнительной информации.
Управление модулями вручную
Управление модулями ядра производится с помощью утилит, предоставляемых пакетом kmod . Вы можете использовать эти утилиты вручную.
Загрузка модуля из другого места (для тех модулей, которых нет в /lib/modules/$(uname -r)/ ):
Альтернативный вариант выгрузки модуля:
Настройка параметров модуля
Чтобы передать параметр модулю ядра, вы можете воспользоваться конфигурационным файлом в modprobe или использовать командную строку ядра.
С помощью файлов в /etc/modprobe.d/
Файлы в директории /etc/modprobe.d/ можно использовать для передачи настроек модуля в udev, который через modprobe управляет загрузкой модулей во время загрузки системы. Конфигурационные файлы в этой директории могут иметь любое имя, оканчивающееся расширением .conf . Синтаксис следующий:
С помощью командной строки ядра
Если модуль вкомпилирован в ядро, вы также можете передать параметры модулю с помощью командной строки ядра. Для всех стандартных загрузчиков, подойдёт следующий синтаксис:
Просто добавьте это в загрузчике в строку с ядром, как описано в параметрах ядра.
Создание псевдонимов
Псевдонимы (алиасы) — это альтернативные названия для модуля. Например: alias my-mod really_long_modulename означает, что вы можете использовать modprobe my-mod вместо modprobe really_long_modulename . Вы также можете использовать звёздочки в стиле shell, то есть alias my-mod* really_long_modulename будет иметь тот же эффект, что и modprobe my-mod-something . Создайте алиас:
У некоторых модулей есть алиасы, которые используются для их автоматической загрузки, когда они потребуются определённой программе. Отключение этих алиасов может предотвратить их автоматическую загрузку, при этом остаётся возможность из загрузки вручную.
Запрет загрузки
В терминах модулей ядра blacklisting означает механизм, предотвращающий загрузку какого-то модуля. Это может понадобиться, например если вам не нужна работа какого-то оборудования или если загрузка данного модуля вызывает проблемы: например, могут быть два модуля ядра, которые пытаются управлять одним и тем же оборудованием, и их совместная загрузка приводит к конфликту.
Некоторые модули загружаются как часть initramfs. Команда mkinitcpio -M напечатает все автоматически обнаруженные модули: для предотвращения initramfs от загрузки каких-то из этих модулей, занесите их в чёрный список в /etc/modprobe.d/modprobe.conf . Команда mkinitcpio -v отобразит все модули, которые необходимы некоторым хукам (например, filesystems хук, block хук и т.д.). Не забудьте добавить этот .conf файл в секцию FILES в /etc/mkinitcpio.conf , если вы этого ещё не сделали, пересоберите initramfs после того, как вы запретили загрузку модулей, а затем перезагрузитесь.
С помощью файлов в /etc/modprobe.d/
Создайте .conf файл в /etc/modprobe.d/ и добавьте строку для каждого модуля, который вы хотите запретить, используя ключевое слово blacklist . Например, если вы хотите запретить загружать модуль pcspkr :
Можно изменить такое поведение. Команда install заставляет modprobe запускать вашу собственную команду вместо вставки модуля в ядро как обычно. Поэтому вы можете насильно сделать так, чтобы модуль никогда не загружался:
Это запретит данный модуль и все модули, зависящие от него.
С помощью командной строки ядра
Вы также можете запрещать модули из загрузчика.
Источник
Networking¶
Lab objectives¶
- Understanding the Linux kernel networking architecture
- Acquiring practical IP packet management skills using a packet filter or firewall
- Familiarize yourself with how to use sockets at the Linux kernel level
Overview¶
The development of the Internet has led to an exponential increase in network applications and, as a consequence, to increasing the speed and productivity requirements of an operating system’s networking subsystem. The networking subsystem is not an essential component of an operating system kernel (the Linux kernel can be compiled without networking support). It is, however, quite unlikely for a computing system (or even an embedded device) to have a non-networked operating system due to the need for connectivity. Modern operating systems use the TCP/IP stack. Their kernel implements protocols up to the transport layer, while application layer protocols are tipically implemented in user space (HTTP, FTP, SSH, etc.).
Networking in user space¶
In user space the abstraction of network communication is the socket. The socket abstracts a communication channel and is the kernel-based TCP/IP stack interaction interface. An IP socket is associated with an IP address, the transport layer protocol used (TCP, UDP etc) and a port. Common function calls that use sockets are: creation ( socket ), initialization ( bind ), connecting ( connect ), waiting for a connection ( listen , accept ), closing a socket ( close ).
Network communication is accomplished via read / write or recv / send calls for TCP sockets and recvfrom / sendto for UDP sockets. Transmission and reception operations are transparent to the application, leaving encapsulation and transmission over network at the kernel’s discretion. However, it is possible to implement the TCP/IP stack in user space using raw sockets (the PF_PACKET option when creating a socket), or implementing an application layer protocol in kernel (TUX web server).
For more details about user space programming using sockets, see Beej’s Guide to Network Programming Using Internet Sockets.
Linux networking¶
The Linux kernel provides three basic structures for working with network packets: struct socket , struct sock and struct sk_buff .
The first two are abstractions of a socket:
- struct socket is an abstraction very close to user space, ie BSD sockets used to program network applications;
- struct sock or INET socket in Linux terminology is the network representation of a socket.
The two structures are related: the struct socket contains an INET socket field, and the struct sock has a BSD socket that holds it.
The struct sk_buff structure is the representation of a network packet and its status. The structure is created when a kernel packet is received, either from the user space or from the network interface.
The struct socket structure¶
The struct socket structure is the kernel representation of a BSD socket, the operations that can be executed on it are similar to those offered by the kernel (through system calls). Common operations with sockets (creation, initialization/bind, closing, etc.) result in specific system calls; they work with the struct socket structure.
The struct socket operations are described in net/socket.c and are independent of the protocol type. The struct socket structure is thus a generic interface over particular network operations implementations. Typically, the names of these operations begin with the sock_ prefix.
Operations on the socket structure¶
Socket operations are:
Creation¶
Creation is similar to calling the socket() function in user space, but the struct socket created will be stored in the res parameter:
- int sock_create(int family, int type, int protocol, struct socket **res) creates a socket after the socket() system call;
- int sock_create_kern(struct net *net, int family, int type, int protocol, struct socket **res) creates a kernel socket;
- int sock_create_lite(int family, int type, int protocol, struct socket **res) creates a kernel socket without parameter sanity checks.
The parameters of these calls are as follows:
- net , where it is present, used as reference to the network namespace used; we will usually initialize it with init_net ;
- family represents the family of protocols used in the transfer of information; they usually begin with the PF_ (Protocol Family) string; the constants representing the family of protocols used are found in linux/socket.h , of which the most commonly used is PF_INET , for TCP/IP protocols;
- type is the type of socket; the constants used for this parameter are found in linux/net.h , of which the most used are SOCK_STREAM for a connection based source-to-destination communication and SOCK_DGRAM for connectionless communication;
- protocol represents the protocol used and is closely related to the type parameter; the constants used for this parameter are found in linux/in.h , of which the most used are IPPROTO_TCP for TCP and IPPROTO_UDP for UDP.
To create a TCP socket in kernel space, you must call:
and for creating UDP sockets:
A usage sample is part of the sys_socket() system call handler:
Closing¶
Close connection (for sockets using connection) and release associated resources:
- void sock_release(struct socket *sock) calls the release function in the ops field of the socket structure:
Sending/receiving messages¶
The messages are sent/received using the following functions:
- int sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags);
- int kernel_recvmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t size, int flags);
- int sock_sendmsg(struct socket *sock, struct msghdr *msg);
- int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t size);
The message sending/receiving functions will then call the sendmsg / recvmsg function in the ops field of the socket. Functions containing kernel_ as a prefix are used when the socket is used in the kernel.
The parameters are:
- msg , a struct msghdr structure, containing the message to be sent/received. Among the important components of this structure are msg_name and msg_namelen , which, for UDP sockets, must be filled in with the address to which the message is sent ( struct sockaddr_in );
- vec , a struct kvec structure, containing a pointer to the buffer containing its data and size; as can be seen, it has a similar structure to the struct iovec structure (the struct iovec structure corresponds to the user space data, and the struct kvec structure corresponds to kernel space data).
A usage example can be seen in the sys_sendto() system call handler:
The struct socket fields¶
The noteworthy fields are:
- ops — the structure that stores pointers to protocol-specific functions;
- sk — The INET socket associated with it.
The struct proto_ops structure¶
The struct proto_ops structure contains the implementations of the specific operations implemented (TCP, UDP, etc.); these functions will be called from generic functions through struct socket ( sock_release() , sock_sendmsg() , etc.)
The struct proto_ops structure therefore contains a number of function pointers for specific protocol implementations:
The initialization of the ops field from struct socket is done in the __sock_create() function, by calling the create() function, specific to each protocol; an equivalent call is the implementation of the __sock_create() function:
This will instantiate the function pointers with calls specific to the protocol type associated with the socket. The sock_register() and sock_unregister() calls are used to fill the net_families vector.
For the rest of the socket operations (other than creating, closing, and sending/receiving a message as described above in the Operations on the socket structure section), the functions sent via pointers in this structure will be called. For example, for bind , which associates a socket with a socket on the local machine, we will have the following code sequence:
As you can see, for transmitting the address and port information that will be associated with the socket, a struct sockaddr_in is filled.
The struct sock structure¶
The struct sock describes an INET socket. Such a structure is associated with a user space socket and implicitly with a struct socket structure. The structure is used to store information about the status of a connection. The structure’s fields and associated operations usually begin with the sk_ string. Some fields are listed below:
- sk_protocol is the type of protocol used by the socket;
- sk_type is the socket type ( SOCK_STREAM , SOCK_DGRAM , etc.);
- sk_socket is the BSD socket that holds it;
- sk_send_head is the list of struct sk_buff structures for transmission;
- the function pointers at the end are callbacks for different situations.
Initializing the struct sock and attaching it to a BSD socket is done using the callback created from net_families (called __sock_create() ). Here’s how to initialize the struct sock structure for the IP protocol, in the inet_create() function:
The struct sk_buff structure¶
The struct sk_buff (socket buffer) describes a network packet. The structure fields contain information about both the header and packet contents, the protocols used, the network device used, and pointers to the other struct sk_buff . A summary description of the content of the structure is presented below:
- next and prev are pointers to the next, and previous element in the buffer list;
- dev is the device which sends or receives the buffer;
- sk is the socket associated with the buffer;
- destructor is the callback that deallocates the buffer;
- transport_header , network_header , and mac_header are offsets between the beginning of the packet and the beginning of the various headers in the packets. They are internally maintained by the various processing layers through which the packet passes. To get pointers to the headers, use one of the following functions: tcp_hdr() , udp_hdr() , ip_hdr() , etc. In principle, each protocol provides a function to get a reference to the header of that protocol within a received packet. Keep in mind that the network_header field is not set until the packet reaches the network layer and the transport_header field is not set until the packet reaches the transport layer.
The structure of an IP header ( struct iphdr ) has the following fields:
- protocol is the transport layer protocol used;
- saddr is the source IP address;
- daddr is the destination IP address.
The structure of a TCP header ( struct tcphdr ) has the following fields:
- source is the source port;
- dest is the destination port;
- syn , ack , fin are the TCP flags used; for a more detailed view, see this diagram.
The structure of a UDP header ( struct udphdr ) has the following fields:
- source is the source port;
- dest is the destination port.
An example of accessing the information present in the headers of a network packet is as follows:
Conversions¶
In different systems, there are several ways of ordering bytes in a word (Endianness), including: Big Endian (the most significant byte first) and Little Endian (the least significant byte first). Since a network interconnects systems with different platforms, the Internet has imposed a standard sequence for the storage of numerical data, called network byte-order. In contrast, the byte sequence for the representation of numerical data on the host computer is called host byte-order. Data received/sent from/to the network is in the network byte-order format and should be converted between this format and the host byte-order.
For converting we use the following macros:
- u16 htons(u16 x) converts a 16 bit integer from host byte-order to network byte-order (host to network short);
- u32 htonl(u32 x) converts a 32 bit integer from host byte-order to network byte-order (host to network long);
- u16 ntohs(u16 x) converts a 16 bit integer from network byte-order to host byte-order (network to host short);
- u32 ntohl(u32 x) converts a 32 bit integer from network byte-order to host byte-order (network to host long).
netfilter¶
Netfilter is the name of the kernel interface for capturing network packets for modifying/analyzing them (for filtering, NAT, etc.). The netfilter interface is used in user space by iptables.
In the Linux kernel, packet capture using netfilter is done by attaching hooks. Hooks can be specified in different locations in the path followed by a kernel network packet, as needed. An organization chart with the route followed by a package and the possible areas for a hook can be found here.
The header included when using netfilter is linux/netfilter.h .
A hook is defined through the struct nf_hook_ops structure:
Источник