Мы пытаемся связаться с сервером, слушающим на петлевом интерфейсе Linux через неструктурированный сокет, и кажется, что сервер не получает единственный пакет от нас. Пакеты, которые мы отправляем, видимы в Wireshark.
Возможен неструктурированный сокет на обратной петле вообще? (Не спрашивайте, почему нам нужен он: это слишком сложно для объяснения здесь),
Править: это - то, как мы открываем его
_I_RawSocket = socket( PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)))
memset( &ifr, 0, sizeof( ifr ) );
strcpy( ifr.ifr_ifrn.ifrn_name, _InterfaceName);
ioctl( _I_RawSocket, SIOCGIFINDEX, &ifr )
memset( &sll, 0, sizeof( sll ) );
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons( ETH_P_ALL );
bind( _I_RawSocket, (struct sockaddr *) &sll, sizeof( sll ))
Сервер является lighttpd, и это достижимо через нормальный сокет на localhost. netstat - сырые данные печатают пустую таблицу, но я абсолютно уверен, что у нас есть два функциональных неструктурированных сокета на нормальных eth устройствах.
Сырые сокеты ведут себя особенно плохо с bind () и connect (), но я не могу подтвердите, что ваша проблема связана с ними. Я предлагаю вам использовать более простой подход:
Отправитель
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define DEST "127.0.0.1"
int main(int argc, char **argv)
{
int s;
struct sockaddr_in dst_addr;
char packet[50];
struct iphdr *ip = (struct iphdr *)packet;
if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = 0; /* not needed in SOCK_RAW */
inet_pton(AF_INET, DEST, (struct in_addr *)&dst_addr.sin_addr.s_addr);
memset(dst_addr.sin_zero, 0, sizeof(dst_addr.sin_zero));
memset(packet, 'A', sizeof(packet)); /* payload will be all As */
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = htons(40);
ip->frag_off = 0; /* NF */
ip->ttl = 64;
ip->protocol = IPPROTO_RAW; /* this has to be IPPROTO_RAW */
ip->check = 0;
ip->saddr = dst_addr.sin_addr.s_addr;
ip->daddr = dst_addr.sin_addr.s_addr;
while(42) {
sleep(5);
if (sendto(s, packet, sizeof(packet), 0,
(struct sockaddr *)&dst_addr, (socklen_t)sizeof(dst_addr)) < 0)
perror("uh oh:");
}
return(0);
}
Получатель
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int s;
struct sockaddr_in src_addr;
char packet[50];
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
memset(packet, 0, sizeof(packet));
socklen_t *len = (socklen_t *)sizeof(src_addr);
int fromlen = sizeof(src_addr);
while(42) {
if (recvfrom(s, &packet, sizeof(packet), 0,
(struct sockaddr *)&src_addr, &fromlen) < 0)
perror("uh oh:");
int i = sizeof(struct iphdr); /* print the payload */
for(; i < sizeof(packet); i++) {
printf("%c", packet[i]);
}
printf("\n");
}
return(0);
}
Я надеюсь, что они будут вести себя именно так, как вы хотите. Прочтите man 7 raw
, чтобы узнать подробности того, почему это работает, и , что более важно, пакет man 7
, если вы хотите его расширить. Также обратите внимание, что IPPROTO_RAW подразумевает параметр сокета IP_HDRINCL, поэтому мы сами создаем заголовок ip - хотя контрольная сумма IP и общая длина все равно вычисляются и заполняются ядром.
edit: Кроме того, если вам нужен необработанный сокет для отправки действительных данных в приложение, такое как lighttpd, вам нужно будет сопоставить аргумент протокола
с аргументом socket ()
, а также укажите допустимые значения для полей IP-заголовка. Правильный заголовок Ethernet не является обязательным - единственное важное поле будет заполнено за вас стеком ядра.