lsm-nraw.so

Модуль доступа к "сырым" пакетам Ethernet

Синтаксис:

mount -Ttcpip lsm-nraw.so

Опции:

Нет

Платформы:

ЗОСРВ «Нейтрино»

Целевые архитектуры:

aarch64, arm, armv7, e2k, mips, ppc, x86

Описание:

Разделяемый объект lsm-nraw.so предоставляет доступ по чтению и записи для необработанных ("сырых") пакетов Ethernet, что позволяет реализовывать собственные сетевые протоколы и снифферы.

Сведения для разработчиков:

Ключевые структуры и макросы, необходимые разработчику доступны через заголовочный файл dcmd_nraw.h.

Структура struct bpat описывает элемент списка фильтров, которым должны соответствовать входящие пакеты. Фильтры работают по принципу ИЛИ – если пакет соответствуют одному их них, то пакет принимается. Данный список пополняется вызовом

devctl( fd, DCMD_NRAW_BYTE_PAT_ADD, bp, pbp->combine_len, 0 );

Для удаления фильтра из списка нужно вызвать devctl() с командой DCMD_NRAW_BYTE_PAT_DEL.

Поле flag структуры struct bpat может принимать следующие значения:

BPAT_FLAG_BYTE_ALL
Принимать все пакеты.
BPAT_FLAG_BYTE_MULTICAST
Принимать мультикаст пакеты.
BPAT_FLAG_BYTE_PATTERN
Задать шаблоны, описываемые структурой struct byte_pattern, в количестве npat, которым должен соотвествовать входящий пакет. Эти шаблоны контролируются по принципу И – пакет должен соответствовать всем шаблонам, описанным структурами struct byte_pattern.

Поле combine_len структуры struct bpat содержит размер структуры bpat + размер всех данных, которые идут после структуры (для команды BPAT_FLAG_BYTE_PATTERN это размеры структур struct byte_pattern с данными).

В структуре struct byte_pattern поле offset задаёт смещение в принятом пакете, откуда будет происходить сравнение. Поле len этой структуры задаёт длину буфера (без выравнивания), следующего за структурой byte_pattern, который будет сравниваться с принятым пакетом. Буфер должен быть выровнен по 4 байта, для этого может использоваться макрос:

int new_len = ROUNDUP_INT( len );



Подключение к интерфейсу nraw

Активированный интерфейс nraw решистритует для каждого имеющегося сетевого интерфейса, который может быть определен с помощью утилиты ifconfig, файл устройства с именем вида /dev/socket/nraw/<interface>.

При открытии nraw в функцию open() вторым параметром можно задать следующие флаги:

O_RDWR
Получать как входящие так и исходящие пакеты.
O_RDONLY
Получать только входящие пакеты.
O_EXCL
Запретить передачу пакета дальше по стеку.

Так, например, для исключения приёма собственных пакетов nraw должен быть открыт следующим образом:

int fd = open( "/dev/socket/nraw/wm0", O_RDONLY | O_EXCL );



Другие devctl-команды

Включение режима полного формирования пакета:

unsigned hdrcmplt = 1;
if ( devctl( rfd, DCMD_NRAW_SET_HDRCMPLT, &hdrcmplt, sizeof( hdrcmplt ), 0 ) != 0 )
{
perror( "devctl( DCMD_NRAW_SET_HDRCMPLT )" );
return (-1);
}

Примеры



Добавление фильтра

Добавляется фильтр для приёма пакетов с полем Ether Type равным 0xbeef:

struct bpat *pbp = NULL;
struct byte_pattern *p = NULL;
uint8_t *cp = NULL;
uint8_t *buffer = NULL;
buffer = (uint8_t *)malloc( sizeof( *pbp ) + sizeof ( *p ) + 4 );
pbp = (struct bpat *)buffer;
memset( pbp, 0, sizeof( *pbp ) );
pbp->flag = BPAT_FLAG_BYTE_PATTERN;
pbp->npat = 1;
p = (struct byte_pattern *)(pbp + 1);
p->offset = 12; /* Пропуск mac адресов назначения и источника */
p->len = 2; /* 2 байта - поле ethernet тип */
cp = (unsigned char *)(p + 1);
cp[0] = 0xbe;
cp[1] = 0xef;
pbp->combine_len = sizeof( *pbp ) + sizeof( *p ) + 2;
if ( devctl( fd, DCMD_NRAW_BYTE_PAT_ADD, pbp, pbp->combine_len, 0 ) != 0 )
{
perror( "devctl( DCMD_NRAW_BYTE_PAT_ADD )" );
return (-1);
}



Утилита перехвата PING-запросов и ответа на них

/*
* (c) 2010-2015, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_arp.h>
#include <net/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "dcmd_nraw.h"
int pingd_arp( unsigned char *buffer, struct ether_addr *if_mac, struct in_addr *if_ip )
{
struct ether_header *eh = NULL;
struct arphdr *ah = NULL;
eh = (struct ether_header *)buffer;
ah = (struct arphdr *)(eh + 1);
if ( ah->ar_hrd != htons( ARPHRD_ETHER ) || ah->ar_op != htons( ARPOP_REQUEST ) )
return (-1);
/* Проверяем, предназначен ли запрос данному узлу? */
if ( memcmp( ar_tpa( ah ), &if_ip->s_addr, ah->ar_pln ) != 0 )
return (-1);
/* Подготовка ARP REPLY */
ah->ar_op = htons( ARPOP_REPLY );
memcpy( ar_tha( ah ), ar_sha( ah ), ah->ar_hln );
memcpy( ar_tpa( ah ), ar_spa( ah ), ah->ar_pln );
memcpy( ar_sha( ah ), if_mac->ether_addr_octet, ah->ar_hln );
memcpy( ar_spa( ah ), &if_ip->s_addr, ah->ar_pln );
/* Заголовок Ethernet */
memcpy( eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN );
memcpy( eh->ether_shost, if_mac->ether_addr_octet, ETHER_ADDR_LEN );
return (sizeof( *eh ) + sizeof( *ah ) + (ah->ar_hln << 1) + (ah->ar_pln << 1));
}
int cksum( uint8_t *cp, int len )
{
uint16_t *w = (uint16_t *)cp;
uint32_t sum;
for ( sum = 0; len > 1; len -= 2 )
{
sum += *w;
w++;
}
if ( len == 1 )
sum += *(uint8_t *)w;
sum = (sum >> 16) + (sum & 0xffff);
sum += sum >> 16;
return (~sum & 0xffff);
}
int pingd_ip( unsigned char *buffer, struct ether_addr *if_mac, struct in_addr *if_ip )
{
struct ether_header *eh = NULL;
struct ip *ip = NULL;
struct icmp *icmp = NULL;
eh = (struct ether_header *)buffer;
ip = (struct ip *)(eh + 1);
icmp = (struct icmp *)(ip + 1);
if ( memcmp(&ip->ip_dst, if_ip, sizeof(if_ip)) != 0 ||
ip->ip_p != IPPROTO_ICMP ||
icmp->icmp_type != ICMP_ECHO )
return (-1);
/* Подготовка ICMP ECHOREPLY */
icmp->icmp_type = ICMP_ECHOREPLY;
/* Заголовок IP */
memcpy( &ip->ip_dst, &ip->ip_src, sizeof( ip->ip_src ) );
memcpy( &ip->ip_src, if_ip, sizeof( ip->ip_src ) );
/* Контрольная сумма ICMP пакета */
icmp->icmp_cksum = 0;
icmp->icmp_cksum = cksum( (uint8_t *)icmp, (ntohs( ip->ip_len ) - (ip->ip_hl << 2)) );
/* Заголовок Ethernet */
memcpy( eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN );
memcpy( eh->ether_shost, if_mac->ether_addr_octet, ETHER_ADDR_LEN );
return (sizeof( *eh ) + ntohs( ip->ip_len ));
}
int getmac( char *ifname, struct ether_addr *e )
{
struct ifaddrs *ifap = NULL,
*ifa = NULL;
struct sockaddr_dl *sdl = NULL;
int s;
/* Создание сокета */
if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 )
{
perror( "socket" );
return (-1);
}
/* Получение всех адресов */
if ( getifaddrs( &ifap ) == 0 )
{
for ( ifa = ifap; ifa; ifa = ifa->ifa_next )
{
if ( ifa->ifa_addr->sa_family != AF_LINK ||
ifa->ifa_data == NULL ||
(ifa->ifa_flags & IFF_LOOPBACK) != 0 ||
strncmp( ifa->ifa_name, ifname, IFNAMSIZ ) != 0 )
continue;
sdl = satosdl( ifa->ifa_addr );
memcpy( e->ether_addr_octet, LLADDR( sdl ), 6 );
freeifaddrs( ifap );
close( s );
return (0);
}
fprintf( stderr, "Interface '%s' desn't have a MAC address\n", ifname );
freeifaddrs( ifap );
}
close( s );
return (-1);
}
static int rfd;
static unsigned hdrcmplt = 0;
void exit_cleanup( int signo )
{
if ( hdrcmplt )
{
hdrcmplt = 0;
if ( devctl( rfd, DCMD_NRAW_SET_HDRCMPLT, &hdrcmplt, sizeof( hdrcmplt ), 0 ) != 0 )
{
perror( "devctl() for HDRCMPLT" );
exit( -1 );
}
}
}
int main( int argc, char **argv )
{
struct ether_addr if_mac;
struct in_addr if_ip;
struct bpat *bp = NULL;
struct byte_pattern *p = NULL;
char pathname[80];
unsigned char *buffer = NULL,
*cp = NULL;
int len;
signal( SIGTERM, exit_cleanup );
signal( SIGQUIT, exit_cleanup );
signal( SIGINT, exit_cleanup );
signal( SIGPIPE, exit_cleanup );
if ( argc < 3 )
{
printf( "Usage: pingd <ifname> <ip> [mac]\n" );
return (-1);
}
if ( argc > 3 )
{
if_mac = *ether_aton( argv[3] );
hdrcmplt = 1;
} else
if ( getmac( argv[1], &if_mac ) != 0 )
{
printf( "Interface '%s' does not have a mac address\n", argv[1] );
return (-1);
}
if ( inet_aton( argv[2], &if_ip ) == 0 )
{
perror( "inet_aton" );
return (-1);
}
/* Буфер для хранения одного пакета */
if ( (buffer = malloc( 1520 )) == NULL )
{
perror( "malloc" );
return (-1);
}
/* Открытие устройства nraw с флагом O_EXCL */
sprintf( pathname, "/dev/socket/nraw/%s", argv[1] );
if ( (rfd = open( pathname, O_RDONLY/* | O_EXCL*/ )) == -1 )
{
perror( "open" );
return (-1);
}
/* 2 шаблона фильтрации: один для ARP (0806), второй для IP (0800) */
bp = (struct bpat *)buffer;
memset( bp, 0, sizeof( *bp ) );
bp->flag = BPAT_FLAG_BYTE_PATTERN;
bp->npat = 1;
p = (struct byte_pattern *)(bp + 1);
p->offset = 12;
p->len = 2;
cp = (unsigned char *)(p + 1);
cp[0] = 0x08;
cp[1] = 0x06;
bp->combine_len = sizeof( *bp ) + sizeof( *p ) + 2;
if ( devctl( rfd, DCMD_NRAW_BYTE_PAT_ADD, bp, bp->combine_len, 0 ) != 0 )
{
perror( "devctl() for ARP" );
return (-1);
}
cp[0] = 0x08;
cp[1] = 0x00;
if ( devctl( rfd, DCMD_NRAW_BYTE_PAT_ADD, bp, bp->combine_len, 0 ) != 0 )
{
perror( "devctl() for ARP" );
return (-1);
}
if ( hdrcmplt )
{
if ( devctl( rfd, DCMD_NRAW_SET_HDRCMPLT, &hdrcmplt, sizeof( hdrcmplt ), 0 ) != 0 )
{
perror( "devctl() for HDRCMPLT" );
return (-1);
}
}
/* Ожидание входящих пакетов и их обработка */
for ( ;; )
{
if ( (len = read( rfd, buffer, 1520 )) == -1 )
{
perror( "read" );
return (-1);
}
if ( buffer[12] == 0x08 && buffer[13] == 0x06 )
{
printf( "seen an ARP\n" );
if ( (len = pingd_arp( buffer, &if_mac, &if_ip )) > 0 )
{
printf( " write ARP reply\n" );
write( rfd, buffer, len );
}
} else
if (buffer[12] == 0x08 && buffer[13] == 0x00)
{
printf( "seen an IP\n" );
if ( (len = pingd_ip( buffer, &if_mac, &if_ip )) > 0 )
{
printf( " write IP reply\n" );
write( rfd, buffer, len );
}
}
}
}

Использовать ее можно следующим образом, синтаксис утилиты: pingd <interface> <ip-address>.

Опции утилиты:

interface
Имя сетевого интерфейса для ожидания PING-пакетов.
ip-address
IP адрес для ответа.

Пример: pingd wm0 10.0.0.1.



Печать всех поступающих пакетов

/*
* (c) 2010-2015, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "dcmd_nraw.h"
int main( int argc, char **argv )
{
char *e = NULL,
*path = NULL;
unsigned char *buf = NULL;
int fd,
len,
i;
struct bpat bp;
if ( argc < 2 )
{
fprintf( stderr, "pkgdump <ifname>" );
return (-1);
}
if ( (e = getenv( "SOCK" )) != NULL )
path = malloc( strlen( e ) + strlen( "/dev/socket/nraw/" ) + strlen( argv[1] ) + 1 );
else {
path = malloc( strlen( "/dev/socket/nraw/" ) + strlen( argv[1] ) + 1 );
e = "";
}
if ( !path )
{
perror( "malloc" );
return (-1);
}
sprintf( path, "%s/dev/socket/nraw/%s", e, argv[1] );
if ( (fd = open( path, O_RDONLY )) == -1 )
{
perror( "open" );
return (-1);
}
if ( (buf = malloc( 1520 )) == NULL )
{
perror( "malloc(1520)" );
return (-1);
}
memset( &bp, 0, sizeof( bp ) );
bp.flag = BPAT_FLAG_BYTE_ALL;
bp.combine_len = sizeof( bp );
if ( devctl( fd, DCMD_NRAW_BYTE_PAT_ADD, &bp, bp.combine_len, 0 ) != 0 )
{
perror( "devctl()" );
return (-1);
}
for ( ;; )
{
len = read( fd, buf, 1520 );
if ( len <= 0 )
break;
printf( "Packet length: %d", len );
for ( i = 0; i < len; i++ )
{
if ( (i % 16) == 0 )
printf( "\n%04x: ", i );
printf( "%02x ", buf[i] );
}
printf( "\n" );
}
close( fd );
return (0);
}

Использовать ее можно следующим образом, синтаксис утилиты: pktdump <interface>.

Опции утилиты:

interface
Имя сетевого интерфейса для ожидания PING-пакетов.

Пример: pktdump wm0.

Классификация:

Базовые подсистемы ЗОСРВ «Нейтрино», Драйверы

Тематические ссылки:

io-pkt-*, nicinfo




Предыдущий раздел: Драйверы