Модуль доступа к "сырым" пакетам 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
может принимать следующие значения:
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() вторым параметром можно задать следующие флаги:
Так, например, для исключения приёма собственных пакетов 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;} elseif ( 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 );}} elseif (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>.
Опции утилиты:
Пример: 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>.
Опции утилиты:
Пример: pktdump wm0.
Базовые подсистемы ЗОСРВ «Нейтрино», Драйверы
Предыдущий раздел: Драйверы