куда делись сетевые syscall-ы ?

Вопросы программного кода и архитектуры Linux

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

куда делись сетевые syscall-ы ?

Непрочитанное сообщение Olej » 23 июн 2015, 18:19

Копаюсь с вот этим вот интересным проектом: Implementing a Distributed Firewall - распределённый вот такой брандмауэр ...

Смотрю для начала что там (в Linux) происходит с системными вызовами connect() & accept()...
То, что они системные (а не библиотечные) убеждаемся для начала:

Код: Выделить всё

olej@ubuntu:~/Рабочий стол$ man 2 write

Код: Выделить всё

olej@ubuntu:~/Рабочий стол$ man 3 printf

Код: Выделить всё

olej@ubuntu:~/Рабочий стол$ man 2 connect
Т.е. man по таким вызовам находится в секции 2, а не 3 ... где находится, например, вызов printf(), очень похожий на системный, но отнесённый к библиотечным (потому что он является только ретранслятором к системному write(2)).

Смотрю /usr/include/asm-generic/unistd.h

Код: Выделить всё

...
/* net/socket.c */
#define __NR_socket 198
__SYSCALL(__NR_socket, sys_socket)
#define __NR_socketpair 199
__SYSCALL(__NR_socketpair, sys_socketpair)
#define __NR_bind 200
__SYSCALL(__NR_bind, sys_bind)
#define __NR_listen 201
__SYSCALL(__NR_listen, sys_listen)
#define __NR_accept 202
__SYSCALL(__NR_accept, sys_accept)
#define __NR_connect 203
__SYSCALL(__NR_connect, sys_connect)
...
Это определения для userspace, и относится это (составная часть) стандартной C-библиотеки GCC, libc.so.

ОК. Перехожу в kernelspace.

Код: Выделить всё

olej@ubuntu:~/Рабочий стол$ cd /lib/modules/`uname -r`/build/include/asm-generic
olej@ubuntu:/lib/modules/3.2.0-25-generic-pae/build/include/asm-generic$ pwd
/lib/modules/3.2.0-25-generic-pae/build/include/asm-generic
Смотрю unistd.h:

Код: Выделить всё

...
/* net/socket.c */
#define __NR_socket 198
__SYSCALL(__NR_socket, sys_socket)
#define __NR_socketpair 199
__SYSCALL(__NR_socketpair, sys_socketpair)
#define __NR_bind 200
__SYSCALL(__NR_bind, sys_bind)
#define __NR_listen 201
__SYSCALL(__NR_listen, sys_listen)
#define __NR_accept 202
__SYSCALL(__NR_accept, sys_accept)
#define __NR_connect 203
__SYSCALL(__NR_connect, sys_connect)
...

Только если этот файл подключить непосредственно по #include - он орёт что константы переопределены.
А с другой стороны, ругается в коде, что __NR_connect неизвестное имя.
И это вполне может быть, т.к.
- эти константы отличаются от архитектуры к архитектуре (ARM, MIPS ... даже i686 и x86_64 ) ...
- они могут быть определены только как препроцессорные константы периода компиляции и нигде больше ... как общий размер, например, sys_call_table для разных архитектур ... константа __NR_syscalls;

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: куда делись сетевые syscall-ы ?

Непрочитанное сообщение Olej » 23 июн 2015, 22:31

Olej писал(а): Это определения для userspace, и относится это (составная часть) стандартной C-библиотеки GCC, libc.so.
Нашёл!
По крайней мере зацепка, с какого конца его ухватить ;-) ...

Код: Выделить всё

olej@ubuntu:~/Рабочий стол$ man socketcall
NAME
       socketcall - socket system calls

SYNOPSIS
       int socketcall(int call, unsigned long *args);

DESCRIPTION
       socketcall()  is  a common kernel entry point for the socket system calls.  call determines which socket function to invoke.  args points to a
       block containing the actual arguments, which are passed through to the appropriate call.

       User programs should call the appropriate functions by their usual names.  Only standard library implementors and kernel hackers need to  know
       about socketcall().

CONFORMING TO
       This call is specific to Linux, and should not be used in programs intended to be portable.

NOTES
       On  a  few  architectures, for example ia64, there is no socketcall() system call; instead socket(2), accept(2), bind(2), and so on really are
       implemented as separate system calls.

SEE ALSO
       accept(2),  bind(2),  connect(2),  getpeername(2),  getsockname(2),  getsockopt(2),  listen(2),  recv(2),  recvfrom(2),  recvmsg(2),  send(2),
       sendmsg(2), sendto(2), setsockopt(2), shutdown(2), socket(2), socketpair(2)
Примечательно здесь то, что:
- 17 перечисленных системных вызовов идут через 1 системный вызов - /usr/include/i386-linux-gnu/asm/unistd_32.h :

Код: Выделить всё

#define __NR_socketcall         102
- /usr/include/asm-generic/unistd.h :

Код: Выделить всё

/* net/socket.c */
#define __NR_socket 198
__SYSCALL(__NR_socket, sys_socket)
#define __NR_socketpair 199
__SYSCALL(__NR_socketpair, sys_socketpair)
#define __NR_bind 200
__SYSCALL(__NR_bind, sys_bind)
#define __NR_listen 201
__SYSCALL(__NR_listen, sys_listen)
#define __NR_accept 202
__SYSCALL(__NR_accept, sys_accept)
#define __NR_connect 203
__SYSCALL(__NR_connect, sys_connect)
#define __NR_getsockname 204
__SYSCALL(__NR_getsockname, sys_getsockname)
#define __NR_getpeername 205
__SYSCALL(__NR_getpeername, sys_getpeername)
#define __NR_sendto 206
__SYSCALL(__NR_sendto, sys_sendto)
#define __NR_recvfrom 207
__SC_COMP(__NR_recvfrom, sys_recvfrom, compat_sys_recvfrom)
#define __NR_setsockopt 208
__SC_COMP(__NR_setsockopt, sys_setsockopt, compat_sys_setsockopt)
#define __NR_getsockopt 209
__SC_COMP(__NR_getsockopt, sys_getsockopt, compat_sys_getsockopt)
#define __NR_shutdown 210
__SYSCALL(__NR_shutdown, sys_shutdown)
#define __NR_sendmsg 211
__SC_COMP(__NR_sendmsg, sys_sendmsg, compat_sys_sendmsg)
#define __NR_recvmsg 212
__SC_COMP(__NR_recvmsg, sys_recvmsg, compat_sys_recvmsg)
- это сугубо Linux специфика ... только здесь это сделано так через задницу...
- на некоторых архитектурах (здесь же в Linux!) вообще нет такого вызова socketcall()
- и про эту "тонкость", как они пишут, нет необходимости знать никому, кроме разработчиков C-библиотеки и хакеров ядра...

Маладца! :-o :lol:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: куда делись сетевые syscall-ы ?

Непрочитанное сообщение Olej » 23 июн 2015, 23:18

Olej писал(а):Маладца! :-o :lol:
Только теперь становится более-менее внятными результаты теста ... который в исправленном виде имеет вид:

Код: Выделить всё

#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
#include <linux/unistd.h>
#include "find.c"

#define ERR(...) printk( KERN_ERR "! "__VA_ARGS__ )

asmlinkage long (*old_sys_write) ( unsigned int fd, const char __user *buf, size_t count );

static char buf[160 ];                       // messages for output

static int write( void ) {                     // put message in tty
   int n;
   mm_segment_t fs = get_fs();
   set_fs( get_ds() );
   n = old_sys_write( 1, buf, strlen( buf ) );
   set_fs( fs );
   return n;
}

#define printf( format, ... )  sprintf( buf, format, __VA_ARGS__ ); write();

static void **taddr;                         // table sys_call_table address
const int last = __NR_process_vm_writev;     // last syscall in i386;

static void show_in_table( const char *symbol ) {
   int n;
   void *waddr = find_sym( symbol );
   for( n = 0; n <= last; n++ )
      if( taddr[ n ] == waddr ) break;
   if( n <= last )
      sprintf( buf, "symbol: %s address=%p found in position %d\n", symbol, waddr, n );
   else
      sprintf( buf, "symbol: %s address=%p not found\n", symbol, waddr );
   write();
}

static int __init init( void ) {
   void *waddr;
   if( NULL == ( taddr = find_sym( "sys_call_table" ) ) ) {
      ERR( "sys_call_table not found\n" ); return -EINVAL;
   }
   old_sys_write = (void*)taddr[ __NR_write ];
   if( NULL == ( waddr = find_sym( "sys_write" ) ) ) {
      ERR( "sys_write not found\n" ); return -EINVAL;
   }
   if( old_sys_write != waddr ) {
      ERR( "Oooops! : addresses not equal\n" ); return -EINVAL;
   }
   printf( "address of sys_call_table  = %p\n", taddr );
   printf( "size of sys_call_table  = %d\n", last + 1 );
   printf( "__NR_socketcall = %d\n", __NR_socketcall );
   show_in_table( "sys_write" );
   show_in_table( "sys_accept" );
   show_in_table( "sys_connect" );
   show_in_table( "sys_socketcall" );
   return -EPERM;    // do not need installation
}

module_init( init );

MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );
Это модуль ядра Linux, динамически подгружаемый (правда он тут же и завершается)...
Тем, кто не имеет навыков в работе с модулями ядра Linux, я бы сильно не советовал экспериментировать с этим кодом ... да и разбираться с ним.
Главным действующим лицом является функция во включаемом файле find.c:

Код: Выделить всё

static void* find_sym( const char *sym ) {  // find address kernel symbol sym
   static unsigned long faddr = 0;          // static !!!
   // ----------- nested functions are a GCC extension ---------
   int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) {
      if( 0 == strcmp( (char*)data, sym ) ) {
         faddr = addr;
         return 1;
      }
      else return 0;
   };
   // --------------------------------------------------------
   kallsyms_on_each_symbol( symb_fn, (void*)sym );
   return (void*)faddr;
}

Это поиск адресов символов ядра (те, которые в /proc/kallsyms мы видим).
Это позволяет использовать не только экспортируемые символы ядра, но любые! ... в том числе и таблицу системных вызовов :lol:

Важен результат:

Код: Выделить всё

olej@ubuntu:~/WORK_2015/FWall/drivers/fwtest$ sudo insmod fwtest.ko
address of sys_call_table  = c15b4000
size of sys_call_table  = 349
__NR_socketcall = 102
symbol: sys_write address=c1144d20 found in position 4
symbol: sys_accept address=c149a070 not found
symbol: sys_connect address=c149a0a0 not found
symbol: sys_socketcall address=c149acd0 found in position 102
insmod: error inserting 'fwtest.ko': -1 Operation not permitted
Вложения
fwtest.tgz
(2.96 КБ) 340 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: куда делись сетевые syscall-ы ?

Непрочитанное сообщение Olej » 25 июн 2015, 23:45

Ну вот, собственно, и весть перехват ;-)
- находим таблицу системных вызовов sys_call_table
- в позиции __NR_socketcall = 102 таблицы лежит адрес обработчика sys_socketcall (sys_call_table[ __NR_socketcall ])
- сохраняем его как old_sys_socketcall
- а в sys_call_table[ __NR_socketcall ] записываем адрес old_sys_socketcall() :

Код: Выделить всё

asmlinkage long new_sys_socketcall( int call, unsigned long __user *args ) {
   long ret;
   if( SYS_CONNECT == call || SYS_ACCEPT == call )
      LOG( "new_sys_socketcall before cod=%d\n", call );
   ret = old_sys_socketcall( call, args );
   if( SYS_CONNECT == call || SYS_ACCEPT == call )
      LOG( "new_sys_socketcall after cod=%d\n", call );
   // sys_accept & sys_connect` are exported
   // можно вызывать: long aret = sys_accept( 1, (struct sockaddr __user*)args, 0 );
   return ret;
}
EXPORT_SYMBOL( new_sys_socketcall );
- при записи в sys_call_table нужно снять read-only через MMU с страницы RAM размещения таблицы ... это делается аппаратно через скрытый системный регистр процессора CR0.
- интересующие нас виды syscall-ов определяются селектором call: SYS_CONNECT = 3, SYS_ACCEPT = 5 и т.д.

Вот, собственно, и всё - вот они все наши сетевые syscall-ы в кулаке ;-) .
Работает это (очень важно!) и на реальном железе, и в виртуальной машине под VirtualBox ... несмотря на манипулирования со скрытыми системными регистрами процессора.
Вложения
fwnet.tgz
(8.28 КБ) 330 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: куда делись сетевые syscall-ы ?

Непрочитанное сообщение Olej » 26 июн 2015, 00:19

Почти случайно, в дебрях интернета ;), разыскалась вот такая заметка: sys socketcall: Network systems calls on Linux.
Daniel No´e
April 9, 2008
Насколько я помню, автор - это один из активных разработчиков сетевой подсистемы Linux ... это имя часто мелькает в комментариях исходного кода ядра.

Там есть весьма любопытные вещи:

1. Ну, про то, что а одних аппаратных архитектурах сделанно вот так, через socketcall (через задницу), а в других нормально, просто как все системные вызовы - это и в man написано. Но автор здесь утверждает, что в i386 сделано через задницу, а в X86_64 - нормально, через sys_call_table (man пишет обратное). Но и в X86_64 сохранён i386. Зачем и это сделано автор (как один из разработчиков) выказывает только догадки ... что, наверное, из-за совместимости с 32-бит приложениями.

2. Но он подробно описывает технику происходящего:
- все параметры любого сетевого ситемного вызова плотно пакуются друг за другом в один массив (не взирая, что для accepr, например, 1-й это целый дескриптор, 2-й - указатель struct sockaddr*, а 3-й - указатель на целую длину ... по барабану, всех пакуем вместе ... "доктор строгий - сказал в морг, значит в морг");
- потом всё это уродство одним параметром указателя на этот массив заталкивается в параметр (2-й) socketcall и делается системный вызов...
- в ядре хранится массив (для каждой сетевой операции) длины, которую нужно тупо скопировать из userspace ... байт за байтом не взирая указатель это или целое ...
- а отом каждый сетевой вызов сам разберётся, по порядку, какой элемент этого массива является его очередным параметром вызова...
И эти люди запрещают мне ковыряться в носу!
:lol:

3. Наконец сам автор-разработчик говорит, что это уродливая, мол, схема в свете современных представлений объектно-ориентированного и т.д. :oops: . Но так, мол, получилось.

Эта публикация многое проясняет.
И будет просто необходима каждому, кто как-то свяжется с программированием вокруг сетевой подсистемы Linux.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: куда делись сетевые syscall-ы ?

Непрочитанное сообщение Olej » 26 июн 2015, 23:40

Olej писал(а):Эта публикация многое проясняет.
И будет просто необходима каждому, кто как-то свяжется с программированием вокруг сетевой подсистемы Linux.
Эта задача, с подсказкой этой публикации, разрешена.
Мини файервол ... или его простейший прототип - перехватывает accept() и connect() и контролирует TCP доступ.
Но поскольку для этого проекта есть отдельная тема, то всё продолжение об этом коде будет там: Implementing a Distributed Firewall.

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 5 гостей