Написание OpenBSD Loadable Kernel Modules (LKM): различия между версиями
(Новая страница: « == Написание OpenBSD Loadable Kernel Modules (LKM) == Данная статья 100% копипаст с [http://www.openbsd.ru www.openbsd.ru] Явл…») |
|||
Строка 223: | Строка 223: | ||
bye! | bye! | ||
</source> | </source> | ||
+ | |||
+ | === Модуль Virtual File System === | ||
+ | |||
+ | Добавление виртуальной файловой системы очень простая задача. Необходимо лишь добавить точку входа. | ||
+ | |||
+ | Структура vfs модуля выглядит так: | ||
+ | <source lang=c> | ||
+ | struct lkm_vfs { | ||
+ | MODTYPE lkm_type; /* тип модуля, в данном случае LM_VFS */ | ||
+ | int lkm_ver; /* версия lkm интерфейса */ | ||
+ | char *lkm_name; /* имя модуля */ | ||
+ | u_long lkm_offset; /* положение vfs */ | ||
+ | struct vfsconf *lkm_vfsconf; /* указатель на vfs операции */ | ||
+ | }; | ||
+ | </source> | ||
+ | |||
+ | В этом случае положение (offset) не используется. | ||
+ | |||
+ | Структура инициализируется через макрос MOD_VFS: | ||
+ | <source lang=c> | ||
+ | MOD_VFS("nullfs", -1, &nullfs_vfsconf) | ||
+ | </source> | ||
+ | |||
+ | Первый аргумент это имя модуля, второй положение, в данном случае не имеет значения. Наконец инициализированная структура vfsconf файловой системы. | ||
+ | |||
+ | Рассмотрим все вышеописанное на примере [http://www.openbsd.ru/cgi-bin/cvsweb/src/lkm/nullfs/lkm nullfs] | ||
+ | |||
+ | код модуля (null_lkm.c): | ||
+ | <source lang=c> | ||
+ | #include <sys/param.h> | ||
+ | #include <sys/ioctl.h> | ||
+ | #include <sys/systm.h> | ||
+ | #include <sys/conf.h> | ||
+ | #include <sys/mount.h> | ||
+ | #include <sys/exec.h> | ||
+ | #include <sys/lkm.h> | ||
+ | #include <sys/file.h> | ||
+ | #include <sys/errno.h> | ||
+ | |||
+ | extern struct vfsops null_vfsops; | ||
+ | int nullfs_lkmentry(struct lkm_table *, int, int); | ||
+ | |||
+ | /* Определяем вектор vfs операций */ | ||
+ | |||
+ | #define MOUNT_NULLFS "nullfs" | ||
+ | #define FS_NULLFS 22 | ||
+ | |||
+ | struct vfsconf null_vfsconf = { | ||
+ | &null_vfsops, MOUNT_NULLFS, FS_NULLFS, 0, 0, NULL | ||
+ | }; | ||
+ | |||
+ | /* | ||
+ | * Инициализируем внутреннюю структуру нашего модуля | ||
+ | */ | ||
+ | MOD_VFS("nullfs", -1, &null_vfsconf) | ||
+ | |||
+ | /* | ||
+ | * Внешняя точка входа модуля, используется для загрузки, выгрузки и получения статистики через modload(8), | ||
+ | * modunload(8) и modstat(8). Устанавливаем lkm_nofunc, т.к. vfs подсистема сама занимается инициализацией и т.д. | ||
+ | */ | ||
+ | |||
+ | int | ||
+ | nullfs_lkmentry(struct lkm_table *lkmtp, int cmd, int ver) | ||
+ | { | ||
+ | DISPATCH(lkmtp, cmd, ver, lkm_nofunc, lkm_nofunc, lkm_nofunc); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Компиляция и загрузка: | ||
+ | <source lang=bash> | ||
+ | # cd lkm/ | ||
+ | # make | ||
+ | # make load | ||
+ | # modstat | ||
+ | Type Id Off Loadaddr Size Info Rev Module Name | ||
+ | VFS 0 -1 d80b7000 0002 d80b85e4 2 nullfs | ||
+ | </source> | ||
+ | Утилита монтирования nullfs системы находится bin/mount_nullfs Код модуля доступен через cvs, адрес: [http://www.openbsd.ru/cgi-bin/cvsweb/src/lkm/nullfs http://www.openbsd.ru/cgi-bin/cvsweb/src/lkm/nullfs] |
Версия 11:25, 25 мая 2013
Написание OpenBSD Loadable Kernel Modules (LKM)
Данная статья 100% копипаст с www.openbsd.ru
Является вольным переводом статьи OpenBSD Loadable Kernel Modules By Peter Werner peter_a_werner@yahoo.com оригинал
Введение
LKM позволяют динамически добавлять или удалять функциональность рабочей системы. Это также позволяет разработчикам тестировать изменения частей ядра без перезагрузки системы.
Недостаток LKM - потенциальная дыра в безопасности. Для загрузки модулей securelevelядра должен быть меньше 0. Если требуется загрузить модуль, то необходимо указать это в rc.securelevel. Если вы разрабатываете модуль, то необходимо установить securelevel = -1, таким образом можно загружать и выгружать модуль в любое время.
Взаимодействие с /dev/lkm выполняется через вызовы ioctl(2). В основном используется modload(8), modunload(8) и modstat(8) для загрузки, выгрузки и получения статистики.
Интерфейс lkm определяет пять типов модулей:
- System calls
- Virtual File System
- Device Driver
- Execution Interpreter
- Miscellaneous
System calls - заменяет системные вызовы. Все системные вызовы могут быть заменены, но стоит обратить внимания на ioctl, так как он используется для загрузки и выгрузки модуля. После выгрузки модуля замененный системный вызов возвращается на исходный. Virtual File System - добавляет виртуальные файловые системы. Device Driver - добавляет новые символьные или блочные устройства. Execution Interpreter - добавляет код для загрузки и выполнения двоичных файлов, которые не поддерживаются системой, примером могут быть эмуляции выполнения двоичных файлов различных операционных систем. Miscellaneous - в этот тип входит все что не описано выше, при этом следует учесть, что при замене системных вызовов, возвращение в исходное состояние должно осуществляться кодом модуля.
Обычно модуль состоит из трех частей:
- Обработчик загрузки, выгрузки модуля
- Внешняя точка входа, используемая modload
- Функциональный код модуля
Модуль System Call
Здесь мы добавим новый системный вызов который будет выводить аргументы вызова. Прототип функции:
int syscall(int, char *)
Внутреннее описание lkm структуры выглядит так:
struct lkm_syscall {
MODTYPE lkm_type; /* тип модуля, в данном случае LM_SYSCALL */
int lkm_ver; /* версия lkm интерфейса */
сhar *lkm_name; /* имя модуля */
u_long lkm_offset; /* положение системного вызова в таблице */
struct sysent *lkm_sysent; /* указатель на struct sysent системного вызова */
struct sysent lkm_oldent; /* место для копии содержимого таблицы вызовов до загрузки */
};
Инициализация происходит используя макрос MOD_SYSCALL:
MOD_SYSCALL("ourcall", -1, &newcallent); /* устанавливает имя "ourcall", положение в таблице системных вызовов,
* в данном случае -1 т.е. не имеет значение, положение определяется автоматически,
* newcallent - данные системного вызова
*/
Так же должны описать обработчик загрузки/выгрузки модуля. Обработчики устанавливают функции исполняемые при загрузке, выгрузке и получении статистики. Если обработчик не требуется просто указывается функция lkm_nofunc() для данного типа события.
Внешняя точка входа модуля ourcall использует макрос DISPATCH:
int
ourcall(lkmtp, cmd, ver)
struct lkm_table *lkmtp;
int cmd;
int ver;
{
DISPATCH(lkmtp, cmd, ver, ourcall_handler, ourcall_handler, lkm_nofunc);
}
где ourcall_handler - функция обработчик, а cmd - вид команды, который может принимать значение:
- LKM_E_LOAD - загрузка
- LKM_E_UNLOAD - выгрузка
- LKM_E_STAT - получение статистики
Собственно код модуля (ourcall.c):
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/cdefs.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/proc.h>
#include <sys/syscallargs.h>
/* прототип нашего системного вызова */
int newcall (struct proc *, void *, int *); /* Все системные вызовы имеют три аргумента: Указатель на
* struct proc - процесс который вызывает, указатель void на аргументы и
* указатель на возвращаемое значение.
*/
/* аргументы системного вызова */
struct newcall_args{
syscallarg(int) value;
syscallarg(char *) msg;
};
/*
* Определяем наш системный вызов. Первый аргумент кол-во аргументов, второй - размер аргументов,
* третий флаги - SY_NOLOCK или SY_MPSAFE, и четвертый - собственно функция системного вызова.
*/
static struct sysent newcallent = {2, sizeof(struct newcall_args), 0, newcall};
/*
* Инициализируем внутреннюю структуру нашего модуля
*/
MOD_SYSCALL("ourcall", -1, &newcallent);
/*
* Обработчик загрузки/выгрузки модуля
*/
static int
ourcall_handler(struct lkm_table *lkmtp, int cmd)
{
if (cmd == LKM_E_LOAD)
printf("hi!\n");
else if (cmd == LKM_E_UNLOAD)
printf("bye!\n");
return(0);
}
/*
* Внешняя точка входа модуля, используется для загрузки, выгрузки и получения статистики через modload(8),
* modunload(8) и modstat(8). Устанавливаем функцию обработчик ourcall_handler, вызываемую при загрузке и
* выгрузке модуля.
*/
int
ourcall(struct lkm_table *lkmtp, int cmd, int ver)
{
DISPATCH(lkmtp, cmd, ver, ourcall_handler, ourcall_handler, lkm_nofunc);
}
/*
* Собственно сам код системного вызова
*/
int
newcall(struct proc *p, void *v, int *retval)
{
struct newcall_args *uap = v;
printf("newcall called with msg %s and value %d\n", SCARG(uap, msg), SCARG(uap, value));
return(0);
}
Компиляция и установка:
# cc -D_KERNEL -I/sys -c ourcall.c -o syscall.o
# modload -o ourcall.o -eourcall syscall.o
Module loaded as ID 0
# modtstat
Type Id Off Loadaddr Size Info Rev Module Name
SYSCALL 0 210 d76cd000 0001 d76cd1a0 2 ourcall
# dmesg | tail -2
hi!
DDB symbols added: 372192 bytes
Протестировать модуль можно при помощи следующей программы (testourcall.c):
#include <stdio.h>
#include <stdlib.h>
static void usage(void);
int
main(int argc, char *argv[])
{
int err = 0;
int syscall_num = 0;
if (argc != 2)
usage();
if ((syscall_num = atoi(argv[1]))>0)
if (err = syscall(syscall_num, 10, "testourcall"))
errx(err,"syscall");
}
static void usage(void){
extern char *__progname;
(void)fprintf(stderr, "usage: %s value\n", __progname);
exit(1);
}
Соберем программу:
# make testcall
cc -O2 -pipe -o testourcall testourcall.c
Запустим и получим результат(210 - значение offset, полученное через modstat(8)):
# ./testourcall 210
# dmesg | tail -1
newcall called with msg testourcall and value 10
Чтобы выгрузить модуль:
# modunload -n ourcall
Получим результат:
# dmesg | tail -1
bye!
Модуль Virtual File System
Добавление виртуальной файловой системы очень простая задача. Необходимо лишь добавить точку входа.
Структура vfs модуля выглядит так:
struct lkm_vfs {
MODTYPE lkm_type; /* тип модуля, в данном случае LM_VFS */
int lkm_ver; /* версия lkm интерфейса */
char *lkm_name; /* имя модуля */
u_long lkm_offset; /* положение vfs */
struct vfsconf *lkm_vfsconf; /* указатель на vfs операции */
};
В этом случае положение (offset) не используется.
Структура инициализируется через макрос MOD_VFS:
MOD_VFS("nullfs", -1, &nullfs_vfsconf)
Первый аргумент это имя модуля, второй положение, в данном случае не имеет значения. Наконец инициализированная структура vfsconf файловой системы.
Рассмотрим все вышеописанное на примере nullfs
код модуля (null_lkm.c):
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/file.h>
#include <sys/errno.h>
extern struct vfsops null_vfsops;
int nullfs_lkmentry(struct lkm_table *, int, int);
/* Определяем вектор vfs операций */
#define MOUNT_NULLFS "nullfs"
#define FS_NULLFS 22
struct vfsconf null_vfsconf = {
&null_vfsops, MOUNT_NULLFS, FS_NULLFS, 0, 0, NULL
};
/*
* Инициализируем внутреннюю структуру нашего модуля
*/
MOD_VFS("nullfs", -1, &null_vfsconf)
/*
* Внешняя точка входа модуля, используется для загрузки, выгрузки и получения статистики через modload(8),
* modunload(8) и modstat(8). Устанавливаем lkm_nofunc, т.к. vfs подсистема сама занимается инициализацией и т.д.
*/
int
nullfs_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
{
DISPATCH(lkmtp, cmd, ver, lkm_nofunc, lkm_nofunc, lkm_nofunc);
}
Компиляция и загрузка:
# cd lkm/
# make
# make load
# modstat
Type Id Off Loadaddr Size Info Rev Module Name
VFS 0 -1 d80b7000 0002 d80b85e4 2 nullfs
Утилита монтирования nullfs системы находится bin/mount_nullfs Код модуля доступен через cvs, адрес: http://www.openbsd.ru/cgi-bin/cvsweb/src/lkm/nullfs