Розробка мережевого системного програмного забезпечення на базі ядра ОС Linux

Структура мережевої підсистеми Linux. Створення мережевого інтерфейсу. Передача пакетів та аналіз поведінки інтерфейсу. Протокол транспортного рівня. Використання модулів ядра. Вплив маршрутизації на процес розробки і налагодження мережевих модулів.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык украинский
Дата добавления 23.05.2013
Размер файла 56,2 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

DBG( "%s: %s", dev->name, sdebg );

}

return 0;

}

static int stop( struct net_device *dev ) {

LOG( "%s: device closed", dev->name );

netif_stop_queue( dev );

return 0;

}

static struct net_device_stats *get_stats( struct net_device *dev ) {

return &stats;

}

// передача кадру

static netdev_tx_t start_xmit( struct sk_buff *skb, struct net_device *dev ) {

struct priv *priv = netdev_priv( dev );

stats.tx_packets++;

stats.tx_bytes += skb->len;

skb->dev = priv->parent; // передача в батьківський (фізичний) інтерфейс

skb->priority = 1;

dev_queue_xmit( skb );

DBG( "tx: injecting frame from %s to %s with length: %u",

dev->name, skb->dev->name, skb->len );

return 0;

}

static struct net_device_ops net_device_ops = {

.ndo_open = open,

.ndo_stop = stop,

.ndo_get_stats = get_stats,

.ndo_start_xmit = start_xmit,

};

// прийом кадру

int pack_parent( struct sk_buff *skb, struct net_device *dev,

struct packet_type *pt, struct net_device *odev ) {

skb->dev = child; // передача кадру у віртуальний інтерфейс

stats.rx_packets++;

stats.rx_bytes += skb->len;

DBG( "tx: injecting frame from %s to %s with length: %u",

dev->name, skb->dev->name, skb->len );

return skb->len;

};

static struct packet_type proto_parent = {

__constant_htons( ETH_P_ALL ), // Перехоплювати всі пакети: ETH_P_ARP & ETH_P_IP

NULL,

pack_parent,

(void*)1,

NULL

};

int __init init( void ) {

void setup( struct net_device *dev ) { // вкладена функція (GCC)

int j;

ether_setup( dev );

memset( netdev_priv( dev ), 0, sizeof( struct priv ) );

dev->netdev_ops = &net_device_ops;

for( j = 0; j < ETH_ALEN; ++j ) // встановити фіктивний MAC-адрес

dev->dev_addr[ j ] = (char)j;

}

int err = 0;

struct priv *priv;

char ifstr[ 40 ];

sprintf( ifstr, "%s%s", ifname, "%d" );

child = alloc_netdev( sizeof( struct priv ), ifstr, setup );

if( child == NULL ) {

ERR( "%s: allocate error", THIS_MODULE->name ); return -ENOMEM;

}

priv = netdev_priv( child );

priv->parent = __dev_get_by_name( &init_net, link ); // батьківський інтерфейс

if( !priv->parent ) {

ERR( "%s: no such net: %s", THIS_MODULE->name, link );

err = -ENODEV; goto err;

}

if( priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK ) {

ERR( "%s: illegal net type", THIS_MODULE->name );

err = -EINVAL; goto err;

}

memcpy( child->dev_addr, priv->parent->dev_addr, ETH_ALEN );

memcpy( child->broadcast, priv->parent->broadcast, ETH_ALEN );

if( ( err = dev_alloc_name( child, child->name ) ) ) {

ERR( "%s: allocate name, error %i", THIS_MODULE->name, err );

err = -EIO; goto err;

}

register_netdev( child ); // зареєструвати новий інтерфейс

proto_parent.dev = priv->parent;

dev_add_pack( &proto_parent ); // встановити обробник фреймів для батька

LOG( "module %s loaded", THIS_MODULE->name );

LOG( "%s: create link %s", THIS_MODULE->name, child->name );

return 0;

err:

free_netdev( child );

return err;

}

void __exit exit( void ) {

dev_remove_pack( &proto_parent ); // видалити обробник фреймів

unregister_netdev( child );

free_netdev( child );

LOG( "module %s unloaded", THIS_MODULE->name );

LOG( "=============================================" );

}

module_init( init );

module_exit( exit );

MODULE_AUTHOR( "Oleg Tsiliuric" );

MODULE_LICENSE( "GPL v2" );

MODULE_VERSION( "3.6" );

Від розглянутих раніше прикладів код відрізняється тим, що після реєстрації нового мережевого інтерфейсу virt0 він виконує виклик dev_add_pack (), попередньо встановивши в структурі packet_type поле dev на покажчик батьківського інтерфейсу: вхідний трафік з цього інтерфейсу і буде перехоплюватися визначеної у структурі функцією pack_parent (). Ця функція фіксує статистику інтерфейсу і, найголовніше, підміняє в сокетних буфері покажчик батьківського інтерфейсу на віртуальний. Зворотний підміна (віртуального на фізичний) відбувається у функції відправки кадру start_xmit ().

Перевіримо наш інтерфейс, виконавши завантаження і конфігурацію модуля:

$ ip address

...

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

link/ether 08:00:27:52:b9:e0 brd ff:ff:ff:ff:ff:ff

...

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

link/ether 08:00:27:0f:13:6d brd ff:ff:ff:ff:ff:ff

...

$ sudo insmod ./virt.ko link=eth1 debug=1

$ sudo ifconfig virt0 192.168.50.19

$ sudo ifconfig virt0

virt0 Link encap:Ethernet HWaddr 08:00:27:0f:13:6d

inet addr:192.168.50.19 Bcast:192.168.50.255 Mask:255.255.255.0

inet6 addr: fe80::a00:27ff:fe0f:136d/64 Scope:Link

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:0 errors:0 dropped:0 overruns:0 frame:0

Як видно, наш інтерфейс поки не отримав жодної інформації, так як значення параметра RX packets (число прийнятих байт) дорівнює нулю.

Створимо на іншому комп'ютері аліасний IP для тестової підмережі (192.168.50.0/24) і організуємо відправку трафіку на створений інтерфейс.

$ sudo ifconfig vboxnet0:1 192.168.50.1

$ ping 192.168.50.19

PING 192.168.50.19 (192.168.50.19) 56(84) bytes of data.

64 bytes from 192.168.50.19: icmp_req=1 ttl=64 time=0.627 ms

...

^C

--- 192.168.50.19 ping statistics ---

3 packets transmitted, 3 received, 0% packet loss, time 2000ms

rtt min/avg/max/mdev = 0.305/0.419/0.627/0.148 ms

На цьому ж комп'ютері в окремому терміналі можна спостерігати за трафіком (в окремому терміналі), фіксуємо утилітою tcpdump.

$ sudo tcpdump -i vboxnet0

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on vboxnet0, link-type EN10MB (Ethernet), capture size 65535 bytes

...

18:41:01.740607 ARP, Request who-has 192.168.50.19 tell 192.168.50.1, length 28

18:41:01.741104 ARP, Reply 192.168.50.19 is-at 08:00:27:0f:13:6d (oui Unknown), length 28

18:41:01.741116 IP 192.168.50.1 > 192.168.50.19: ICMP echo request,

id 8402, seq 1, length 64

...

18:41:03.741471 IP 192.168.50.19 > 192.168.50.1: ICMP echo reply,

id 8402, seq 3, length 64

18:41:06.747701 ARP, Request who-has 192.168.50.1 tell 192.168.50.19, length 28

Тепер повернемося до питання, як створити повноцінний віртуальний інтерфейс, що працює тільки зі своїм трафіком і не порушує роботу батьківського інтерфейсу.

Для цього необхідно оголосити два незалежних обробника протоколів і зареєструвати їх у функції ініціалізації модуля, як показано в лістингу 2.

Лістинг 2. Створення повноцінного віртуального інтерфейсу.

// обробник фреймів ETH_P_ARP

int arp_pack_rcv( struct sk_buff *skb, struct net_device *dev,

struct packet_type *pt, struct net_device *odev ) {

...

return skb->len;

};

static struct packet_type arp_proto = {

__constant_htons( ETH_P_ARP ),

NULL,

arp_pack_rcv, // фильтр пртокола ETH_P_ARP

(void*)1,

NULL

};

// обробник феймів ETH_P_IP

int ip4_pack_rcv( struct sk_buff *skb, struct net_device *dev,

struct packet_type *pt, struct net_device *odev ) {

...

return skb->len;

};

static struct packet_type ip4_proto = {

__constant_htons( ETH_P_IP ),

NULL,

ip4_pack_rcv, // фильтр пртокола ETH_P_IP

(void*)1,

NULL

};

// Реєстрація обробників

// Перехоплення тільки з батьківського інтерфейсу arp_proto.dev = ip4_proto.dev = priv->parent;

dev_add_pack( &arp_proto );

dev_add_pack( &ip4_proto );

Кожен з обробників повинен здійснювати підміну інтерфейсу тільки для тих фреймів, IP-адресу одержувача яких збігається з IP-адресом самого інтерфейсу. Два роздільних обробника зручні тим, що заголовки фреймів ARP і IP мають різний формат, і тому виділяти IP-адреса призначення в них доводиться по-різному.

Використовуючи такий повноцінний модуль, можна відкрити з хостом дві паралельні SSH-сесії на різні інтерфейси (що використовують різні IP), які будуть одночасно використовувати єдиний загальний фізичний інтерфейс:

$ ssh olej@192.168.50.17

olej@192.168.50.17's password:

Last login: Mon Jul 16 15:52:16 2012 from 192.168.1.9

...

$ ssh olej@192.168.56.101

olej@192.168.56.101's password:

Last login: Mon Jul 16 17:29:57 2012 from 192.168.50.1

...

$ who

olej tty1 2012-07-16 09:29 (:0)

olej pts/0 2012-07-16 09:33 (:0.0)

olej pts/1 2012-07-16 12:22 (192.168.1.9)

olej pts/4 2012-07-16 15:52 (192.168.1.9)

olej pts/6 2012-07-16 17:29 (192.168.50.1)

olej pts/7 2012-07-16 17:31 (192.168.56.1)

Команда who виконується вже в SSH-сесії на самому віддаленому хості, до якого були здійснені два незалежних підключення з двох різних підмереж (останні два рядки виводу), насправді представляє один і той же хост.

3.2 Вплив маршрутизації на процес розробки і налагодження мережевих модулів

Успішний результат при розробці мережевих модулів залежить не тільки від якісного коду і продуманого плану тестування. Важливим елементом успіху стає грамотне управління роутингом в мережевій системі і додавання відповідних записів у таблиці маршрутизації. Вміст таблиці маршрутизації можна переглянути за допомогою команди route.

$ route

Kernel IP routing table

Destination Gateway Genmask Flags Metric Ref Use Iface

192.168.1.0 * 255.255.255.0 U 2 0 0 wlan0

default 192.168.1.1 0.0.0.0 UG 0 0 0 wlan0

До тих пір, поки в цій таблиці не з'явиться рядок (рядки), що визначають поведінку розроблюваного інтерфейсу, ніякого позитивного результату в поведінці модуля домогтися неможливо. Щоб цей рядок з'явилася в таблиці, її необхідно додати туди за допомогою команди route. Надалі подібні дії можуть виконуватися синхронно з інсталяцією модуля при установці пакета. Але на етапі розробки гнучке управління роутингом стає запорукою успіху. Ця особливість суттєво відрізняє процес розробки мережевих модулів ядра від процесу створення драйверів потокових пристроїв в / dev.

І, звичайно ж, при налагодженні мережевих модулів ядра незамінним інструментом стають утиліти для аналізу трафіку, наприклад, tcpdump.

3.3 Допоміжні API ядра

Крім безлічі викликів API ядра, призначених для виконання чітко ядерних функцій, існує обмежений ряд операцій, які допомагають виконувати деякі дії з боку ядра, аналогічно таким же операціям в просторі користувача. Саме тому в літературі і обговореннях по ядру, про такі API говорять, як про загальну групу під назвою «хелпери» (або «хелпери простору користувача»). Велика частина подібних можливостей, обговорюваних далі, вкрай рідко зустрічається в публікаціях або згадується в обговореннях. Але вони, крім того, що можуть виявитися вкрай корисні в деяких випадках, дають дуже важливе розуміння перебігу процесів в ядрі, і того, що можна, а що не можна зробити в рамках ядра.

3.4 Операції з файлами даних

Операції з даними в іменованих файлах (регулярних файлах, FIFO, і т.д.) не відносяться до тих можливостей, якими програміст активно користується всередині коду ядра (модуля). Але, по-перше, такі операції цілком можливі, а по-друге, існує, як мінімум, одна ситуація, коли така можливість необхідна: це читання конфігураційних даних модуля при його запуску з конфігураційних файлів. Як буде показано, таке завдання не тільки можна виконати з коду ядра, але навіть існує кілька способів його вирішення. Розглянемо детальний приклад, наведений у файлі mod_file.c.

Лістинг 1. Читання файлу з модуля

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/sched.h>

static char* file = NULL;

module_param( file, charp, 0 );

#define BUF_LEN 255

#define DEFNAME "/etc/yumex.profiles.conf"; // довільний текстовий файл

static char buff[ BUF_LEN + 1 ] = DEFNAME;

static int __init kread_init( void ) {

struct file *f;

size_t n;

if( file != NULL ) strcpy( buff, file );

printk( "*** openning file: %s\n", buff );

f = filp_open( buff, O_RDONLY, 0 );

if( IS_ERR( f ) ) {

printk( "*** file open failed: %s\n", buff );

return -ENOENT;

}

n = kernel_read( f, 0, buff, BUF_LEN );

if( n ) {

printk( "*** read first %d bytes:\n", n );

buff[ n ] = '\0';

printk( "%s\n", buff );

} else {

printk( "*** kernel_read failed\n" );

return -EIO;

}

printk( "*** close file\n" );

filp_close( f, NULL );

return -EPERM;

}

module_init( kread_init );

Перевіримо роботу нашого прикладу (використовуваний в тесті конфігураційний файл ./Yumex.profiles.conf був підготовлений заздалегідь у каталозі /etc і містить кілька рядків тексту, а файл з ім'ям ./Yyy відсутній, і зазначений у прикладі як неприпустиме ім'я файлу):

$ sudo insmod mod_file.ko file=/etc/yumex.profiles.conf

insmod: error inserting 'mod_file.ko': -1 Operation not permitted

$ cat /etc/yumex.profiles.conf

[main]

lastprofile = yum-enabled$

$ dmesg | tail -n 40

**** openning file: /etc/yumex.profiles.conf

**** read first 32 bytes:

[main]

lastprofile = yum-enabled

*** close file

Спробуємо зчитати неіснуючий файл, зверніть увагу, як змінився код помилки завантаження модуля:

$ sudo insmod mod_file.ko file=./yyy

insmod: error inserting 'mod_file1.ko': -1 Unknown symbol in module

$ dmesg | tail -n20 | grep '^\*'

*** openning file: ./yyy

*** file open failed: ./yyy

У попередньому прикладі використовувався спеціальний виклик ядра kernel_read (), призначений тільки для цієї мети. Але в наступному прикладі (файл mod_vfs.c) використовується зовсім інший набір API ядра: виклики імен, експортованих віртуальною файловою системою (VFS, виклики виду vfs_ * ()).

Лістинг 2. Читання файлу засобами віртуальної файлової системи

include <linux/module.h>

#include <linux/fs.h>

#include <linux/sched.h>

#include <linux/uaccess.h>

static char* file = NULL;

module_param( file, charp, 0 );

#define BUF_LEN 255

#define DEFNAME "/etc/yumex.profiles.conf";

static char buff[ BUF_LEN + 1 ] = DEFNAME;

static int __init kread_init( void ) {

struct file *f;

size_t n;

long l;

loff_t file_offset = 0;

mm_segment_t fs = get_fs();

set_fs( get_ds() );

if( file != NULL ) strcpy( buff, file );

printk( "*** openning file: %s\n", buff );

f = filp_open( buff, O_RDONLY, 0 );

if( IS_ERR( f ) ) {

printk( "*** file open failed: %s\n", buff );

l = -ENOENT;

goto fail_oupen;

}

l = vfs_llseek( f, 0L, 2 ); // 2 means SEEK_END

if( l <= 0 ) {

printk( "*** failed to lseek %s\n", buff );

l = -EINVAL;

goto failure;

}

printk( "*** file size = %d bytes\n", (int)l );

vfs_llseek( f, 0L, 0 ); // 0 means SEEK_SET

if( ( n = vfs_read( f, buff, l, &file_offset ) ) != l ) {

printk( "*** failed to read\n" );

l = -EIO;

goto failure;

}

buff[ n ] = '\0';

printk( "%s\n", buff );

printk( KERN_ALERT "**** close file\n" );

l = -EPERM;

failure:

filp_close( f, NULL );

fail_oupen:

set_fs( fs );

return (int)l;

}

module_init( kread_init );

Хоча цей варіант і складніший, але він має більшу гнучкість, яка полягає в можливості використання всього набору функцій файлових операцій (таких, як показано в прикладі vfs_llseek ()), а не лише вузьких підмножин викликів. А складність полягає в тому, що операції віртуальної системи «заточені» на роботу з буферами в просторі користувача, і перевіряють приналежність адрес-параметрів на приналежність цьому простору. Для виконання тих же операцій з даними простору ядра, потрібно зняти цю перевірку на час операції і відновити її після. Що й досягається використанням макровикликів: get_fs (), set_fs (), get_ds (). Але за цим треба ретельно стежити, інакше операція завершиться з кодом: Bad address. Перевіримо, як змінений модуль може працювати з тим же файлом даних:

$ sudo insmod mod_vfs.ko file=./xxx

insmod: error inserting 'mod_vfs.ko': -1 Operation not permitted

$ dmesg | tail -n30 | grep '^\*'

*** openning file: ./xxx

*** file size = 39 bytes

*1 .........

*2 .........

**** close file

Можливість запису у файл з модуля ядра виявляється потрібнішою ще рідше, ніж читання. Але і подібна операція, по-перше, можлива, а по-друге, буває корисна, наприклад, для протоколювання подій, наприклад, з метою налагодження (можливості якої вкрай звужені у випадку з модулями ядра). Наступний приклад з файлу mdw.c демонструє таку можливість.

Лістинг 3. Запис у файл з коду модуля

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/uaccess.h>

static char* log = NULL;

module_param( log, charp, 0 );

#define BUF_LEN 255

#define DEFLOG "./module.log"

#define TEXT "...............\n"

static struct file *f;

static int __init init( void ) {

ssize_t n = 0;

loff_t offset = 0;

mm_segment_t fs;

char buff[ BUF_LEN + 1 ] = DEFLOG;

if( log != NULL ) strcpy( buff, log );

f = filp_open( buff,

O_CREAT | O_RDWR | O_TRUNC,

S_IRUSR | S_IWUSR );

if( IS_ERR( f ) ) {

printk( "! file open failed: %s\n", buff );

return -ENOENT;

}

printk( "! file open %s\n", buff );

fs = get_fs();

set_fs( get_ds() );

strcpy( buff, TEXT );

if( ( n = vfs_write( f, buff, strlen( buff ), &offset ) ) != strlen( buff ) ) {

printk( "! failed to write: %d\n", n );

return -EIO;

}

printk( "! write %d bytes\n", n );

printk( "! %s", buff );

set_fs( fs );

filp_close( f, NULL );

printk( "! file close\n" );

return -1;

}

module_init( init );

Нижче наведено приклад запуску даного модуля:

$ sudo insmod mdw.ko

insmod: error inserting 'mdw.ko': -1 Operation not permitted

$ dmesg | tail -n40 | grep !

! file open ./module.log

! write 16 bytes

! ...............

! file close

$ ls -l *.log

-rw------- 1 root root 16 Дек 31 21:23 module.log

$ sudo cat module.log

Зверніть увагу, що файл module.log, в який ведеться запис, створюється від імені root (сам модуль теж можна виконати командою insmod тільки від імені root), тому для подальшої роботи з таким файлом, можливо, доведеться поміняти його власника.

3.5 Запуск нових процесів з ядра

Як звичайно, нам належить відповісти на питання, що не дуже часто зустрічається в публікаціях і обговорюється на форумах. Ми повинні встановити, чи можна запустити новий користувальницький процес з коду модуля? Інтуїтивна відповідь очевидна відразу - так, тому що всі процеси, що виконуються в системі, запущені саме з коду ядра (одним із системних викликів групи exec * ()).

Нові процеси користувальницького простору можуть запускатися кодом ядра точно також, як вони запускаються з користувальницького (POSIX) коду викликами групи exec*(). Але хоча форма викликів в обох випадках і збігається, за змістом це дія (запуску процесу з ядра) має зовсім інший зміст. У просторі користувача процеси створюються в два етапи. Спочатку виконується виклик fork () для створення нового адресного простору, який є абсолютним дублікатом первинного процесу. Це адресний простір пізніше стає простором нового процесу, коли вже в ньому виробляється виклик одній з функцій сімейства exec * (). Або, в окремих випадках, при виклику типу fexecve (), коли джерело коду нового процесу визначається не ім'ям файлу програми, а зазначенням дескриптора такого файлу, але сенс у будь-якому випадку залишається тим же. У просторі ядра створення процесу відбувається по-іншому, тому що тут не можна створити дублікат ядерного простору (так як відсутній еквівалент fork ()). Для запуску нового процесу з коду ядра в API ядра присутній спеціальний виклик call_usermodehelper ().

У лістингу 1 представлений найпростіший приклад породження нових процесів у системі з ініціативи ядра.

Лістинг 1. Запуск нового процесу з коду модуля ядра.

#include <linux/module.h>

static char *str;

module_param( str, charp, S_IRUGO );

Kit __Kit exec_Kit( Load ) {

Kit On;

char *argv[] = { "wall", "\nthis is wall message ", NULL },

*envp[] = { NULL },

msg[ 80 ];

if( str ) {

sprintf( msg, "\n%s", str );

argv[ 1 ] = msg;

}

rc = call_usermodehelper( "/usr/bin/wall", argv, envp, 0 );

if( rc ) {

printk( KERN_INFO "failed to execute : %s\n", argv[ 0 ] );

return rc;

}

printk( KERN_INFO "execute : %s %s\n", argv[ 0 ], argv[ 1 ] );

msleep( 100 );

return -1;

}

module_init( exec_init );

Виклик call_usermodehelper () отримує параметри точно також, як і системний виклик користувальницького простору execve() (через який виконуються всі інші бібліотечні виклики сімейства exec*()). З особливостями роботи з execve () можна познайомитися в довіднику man.

$ man 2 execve

Шляхове ім'я файлу виконуваної програми має бути зазначено в повній абсолютній формі (відлічуване від кореня файлової системи /), так як ніякий пошук в рамках змінної $ PATH не проводиться. Перевіримо роботу нашого модуля, не зазначаючи параметрів.

$ sudo insmod mod_exec.ko

Broadcast message from root@notebook.localdomain (Mon Jan 30 18:13:10 2012):

this is wall message

insmod: error inserting 'mod_exec.ko': -1 Operation not permitted

Тепер подивимося, що буде, якщо при завантаженні модуля вказати параметр (передаючу рядок).

$ sudo insmod mod_exec.ko str="new_string"

Broadcast message from root@notebook.localdomain (Mon Jan 30 18:22:59 2012):

new_string

insmod: error inserting 'mod_exec.ko': -1 Operation not permitted

$ dmesg | tail -n30 | grep -v ^audit

execute : wall

new_string

Як видно, модуль був успішно завантажений, а потім був проведений запуск автономного користувальницького додатка, яке вивело широкомовне повідомлення на всі термінали системи. Якщо вказану програму не вдається запустити, а це найчастіше відбувається через неправильні вказівки повного шляху імені файлу з виконуваною програмою (в даному випадку було вказано /bin/wall - внесені зміни в код і виконана компіляція), то код завершення завантаження модуля буде іншим (ненульовим). Це важливо, якщо врахувати, що в ході виконання модулів ядра практично відсутній вивід якої-небудь відлагоджувальної інформації.

$ sudo insmod mod_exec.ko

insmod: error inserting 'mod_exec.ko': -1 Unknown symbol in module

$ dmesg | tail -n30 | grep -v ^audit

failed to execute : /bin/wall

$ which wall

/usr/bin/wall

Обмеження даного способу запуску додатків шляхом виклику call_usermodehelper () з коду ядра полягає в тому, що процес запускається без керуючого терміналу і з нестандартним для нього оточенням! Для стандартних користувальницьких додатків термінал і оточення спочатку створюються ініціалізуючим процесом init, і наступні процеси успадковують їх від нього (в останніх версіях багатьох дистрибутивів ці операції проводяться не init, а systemd). У цьому можна легко переконатися, якщо в якості користувача процесу використовувати утиліту echo, а рядки запуску відповідним чином змінити.

char *argv[] = { "/bin/echo", "this is wall message", NULL };

rc = call_usermodehelper( "/bin/echo", argv, envp, 0 );

Якщо запустити такий модуль, то ми отримаємо такий висновок.

$ sudo insmod mod_execho.ko

insmod: error inserting 'mod_execho.ko': -1 Operation not permitted

$ dmesg | tail -n30 | grep -v ^audit

execute : /bin/echo echo message

Тут має місце нормальне виконання утиліти echo, проте ми не побачимо результату її роботи (виведення на термінал), оскільки цей керуючий термінал просто не існує (дескриптор 1, який в традиційному процесі відкритий на SYSOUT в даному випадку взагалі не був відкритий).

Тим не менш, такі фонові процеси, ініційовані з модуля ядра (і з коду ядра) можуть цілком успішно використовуватися для виконання найрізноманітніших внутрішніх дій: відправка сигналів UNIX, посилка мережевих повідомлень, і т.д.

Вся основна робота зі створення та запуску процесу, як легко побачити, зосереджена в єдиному виклику call_usermodehelper(), сигнатура якого наведена нижче, а повний код можна знайти у файлі <linux/kmod.h>.

static inline int call_usermodehelper( char *path, char **argv,

char **envp, enum umh_wait wait );

enum umh_wait {

UMH_NO_WAIT = -1, /* don't wait at all */

UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */

UMH_WAIT_PROC = 1, /* wait for the process to complete */

};

Наведені константи мають наступний зміст:

· UMH_WAIT_PROC - очікувати завершення запущеного процесу;

· UMH_WAIT_EXEC - очікувати завершення дій по запуску дочірнього процесу, але не самого процесу;

· UMH_NO_WAIT - не чекати взагалі нічого (в цьому випадку неможливо буде зафіксувати помилку запуску процесу, наприклад, внаслідок вказівки помилкового імені файлу).

ВИСНОВКИ

Після знайомства з архітектурою мережевої підсистеми платформи Linux та інструментами, використовуваними для спостереження та управління мережевими інтерфейсами, можна перейти до більш складних питань, пов'язаним зі спільним використанням мережевих інтерфейсів і драйверів ядра.

Розглянуті основні структури, використовувані для роботи з мережевими пристроями та безпосередньої передачі даних, детально вивченні принципи роботи мережевої підсистеми.

Досліджено питання, де у цьому процесі знаходиться місце, де створюється інформація, що поміщається в буфер, або споживається інформація з прийнятих буферів? Це місце знаходиться за межами мережевого стека ядра, так як будь-яка інформація, що відправляється в мережу або споживана з мережі, стає видимою тільки на прикладних рівнях, у додатках просторі користувача. Інтерфейс з цього прикладного рівня в стек протоколів ядра забезпечується відомим POSIX API сокетів прикладного рівня. В подальшому було ще раз підтверджено початкове твердження, що з коду модуля ядра програмісту доступні всі ті можливості, якими він звично оперує в просторі користувача.

Вище в розділах на реальних прикладах показані деякі нестандартні можливості по роботі з файловою системою, які зазвичай викликають сумніви і питання в публічних обговореннях. Основна суть їх полягає в тому, що з коду модуля ядра доступні всі операції, використовувані процесами користувацького простору. Першим таким прикладом і є читання або запис будь-яких файлів деревовидної файлової системи Linux з коду модуля ядра.

ПЕРЕЛІК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ

1. Цилюрик О. І. Розробка модулів ядра Linux: Частина 30. Знайомство з мережевою підсистемою Linux [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_30. - Назва з екрану.

2. Цилюрик О. І. Розробка модулів ядра Linux: Частина 31. Структури даних, що використовуються при роботі з мережевою підсистемою [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_31/. - Назва з екрану.

3. Цилюрик О. І. Розробка модулів ядра Linux: Частина 32. Принципи роботи з мережевою підсистемою [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_32/. - Назва з екрану.

4. Цилюрик О. І. Розробка модулів ядра Linux: Частина 33. Віртуальний мережевий інтерфейс [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_33/. - Назва з екрану.

5. Цилюрик О. І. Розробка модулів ядра Linux: Частина 34. Протоколи мережевого і транспортного рівнів [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_34/. - Назва з екрану.

6. Цилюрик О. І. Розробка модулів ядра Linux: Частина 35. Додаткові аспекти використання модулів ядра для створення мережевих інтерфейсів [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_35/. - Назва з екрану.

7. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 36. Операції c файлами [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_36/. - Назва з екрану.

8. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 37. Створення нових процесів [Електронний ресурс]. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_37/. - Назва з екрану.

Размещено на Allbest.ru


Подобные документы

  • Неекспортовані символи ядра. Оптимальний підхід до реалізації пошуку символів у ядрі. Виконання, підміна, додавання та приховання системних викликів. Завантаження модуля ядра із програмного коду та з коду іншого модуля. Робота з UNIX-сигналами.

    курсовая работа [84,0 K], добавлен 23.05.2013

  • МАС-адреса як унікальний ідентифікатор мережевого інтерфейсу (зазвичай мережевої карти) для реалізації комунікації пристроїв в мережі на фізичному рівні. IP-адреса: поняття та призначення, принципи та етапи настройки. Основи роботи з утилітами TCP/IP.

    лабораторная работа [161,6 K], добавлен 15.10.2013

  • История создания, архитектура операционной системы и перечень возможностей, реализуемых в Linux. Инструментальные средства и цикл разработки новой версии ядра. Жизненный цикл патча. Структура принятия решений при добавлении новых функций (патчей) в ядро.

    лекция [303,8 K], добавлен 29.07.2012

  • Аналіз системи збору первинної інформації та розробка структури керуючої ЕОМ АСУ ТП. Розробка апаратного забезпечення інформаційних каналів, структури програмного забезпечення. Алгоритми системного програмного забезпечення. Опис програмних модулів.

    дипломная работа [1,9 M], добавлен 19.08.2012

  • Аналіз технічного забезпечення, вибір інструментального програмного забезпечення та середовища розробки програм. Створення класів для реалізації необхідних функцій для роботи програмного засобу. Розробка інтерфейсу для користувача та лістинг програми.

    курсовая работа [343,9 K], добавлен 24.08.2012

  • Створення навчальної програми для вирішення системи лінійних рівнянь різними методами. Детальне покрокове рішення та довідкова теоретична інформація. Структура і функціональне призначення модулів програмного продукту, основні елементи його інтерфейсу.

    курсовая работа [1,9 M], добавлен 20.05.2015

  • Етапи розробки проекту. Вимоги до апаратного і програмного забезпечення, до користувача. Специфікація та структура даних, які мають бути розміщеними в системі. Вигляд інтерфейсу системи програмного забезпечення. Розробка бази даних косметичного салону.

    дипломная работа [1,8 M], добавлен 21.02.2015

  • Структура та побудова модулів для системи віддаленого адміністрування серверів Ajenti. Огляд веб-орієнтованих систем віддаленого адміністрування для linux. Процес розробки та реалізації програмного модуля "Менеджер процесів", системні вимоги до нього.

    дипломная работа [3,0 M], добавлен 09.06.2012

  • Требования к операционной системе Linux, встраиваемые приложения. Предсказуемость поведения, свойства ОС реального времени. Структура ядра; системные вызовы; работа с файлами. Стандартные устройства; обзор программирования; компилирование и линковка.

    лекция [112,2 K], добавлен 29.07.2012

  • UNIX - одна з найпопулярніших в світі операційних систем. Ключеві риси Linux. Порівняльні характеристики 32-розрядних операційних систем. Поверхневий огляд характеристик ядра Linux. Програмні характеристики: базові команди і утиліти, мови програмування.

    курсовая работа [33,3 K], добавлен 07.12.2010

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.