Отправка необработанного tcp-пакета с установленным флагом syn просто проходит через интерфейс lo, а не через eth0, как я хочу.

Я хотел бы отправить пакет синхронизации на мой сервер httpd и получить ответный пакет синхронизации. Но когда я отслеживаю с помощью Wireshark, пакет отправляется моим локальным интерфейсом, lo, а не eth0.

Я пытался установить несколько разных значений в setsockopt, как вы можете видеть в коде ниже, но ни один из них не работает, он всегда использует интерфейс lo, а не eth0. Я не знаю, что-то не так в tcp-пакете, из-за которого он проходит через локальный интерфейс, или что-то еще.

#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>

#define PCKT_LEN 8192

unsigned short csum(unsigned short *buf, int len) {
    unsigned long sum;
    for(sum=0; len>0; len--)
        sum += *buf++;
    sum = (sum >> 16) + (sum &0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}

int main(int argc, char** argv) {

    char *buffer = new char[PCKT_LEN]();

    class iphdr *ip = (struct iphdr *) buffer;
    class tcphdr *tcp = (struct tcphdr *) (buffer + sizeof(struct iphdr));

    class sockaddr_in sin;

    int sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    if(sd < 0) {
       perror("socket() error");
       exit(-1);
    } else {
        printf("socket()-SOCK_RAW and tcp protocol is OK.\n");
    }

    sin.sin_family = AF_INET;           // Address family
    sin.sin_port = htons(atoi("2345")); // Source port
    inet_pton(AF_INET, "192.168.1.11", &(sin.sin_addr.s_addr)); // Dest IP - ERROR WAS WRONG IP

    ip->ihl = 5;
    ip->version = 4;
    ip->tos = 16;
    ip->tot_len = sizeof(class iphdr) + sizeof(class tcphdr);
    ip->id = htons(54321);
    ip->frag_off = 0;
    ip->ttl = 32;
    ip->protocol = 6; // TCP
    ip->check = 0; // Done by kernel
    inet_pton(AF_INET, "192.168.1.10", &(ip->saddr)); // Source IP
    inet_pton(AF_INET, "192.168.1.11", &(ip->daddr)); // Destination IP

    // The TCP structure
    tcp->source = htons(atoi("2345"));
    tcp->dest = htons(atoi("80"));      // Destination port
    tcp->seq = htonl(1);
    tcp->ack_seq = random();
    tcp->doff = 5;
    tcp->syn = 1;
    tcp->ack = 0;
    tcp->window = htons(32767);
    tcp->check = 0; // Done by kernel
    tcp->rst = 0;
    tcp->urg_ptr = 0;

    ip->check = csum((unsigned short *) buffer, (sizeof(class iphdr) + sizeof(class tcphdr)));

    // Bind socket to interface
    int iface = 1;
    const int *val = &iface;
    char *opt = "eth0";
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(iface)) < 0) {
    //if(setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, 4) < 0) {
        perror("setsockopt() error");
        exit(-1);
    }
    else
        printf("setsockopt() is OK\n");

    if(sendto(sd, buffer, ip->tot_len, 0, (sockaddr*)&sin, sizeof(class sockaddr_in)) < 0) {
       perror("sendto() error");
       exit(-1);
    }
    else
        printf("Send OK!");

    close(sd);
    return 0;
}

Мои интерфейсы:

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 00:0c:29:6e:82:29 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.10/24 brd 192.168.1.255 scope global eth0
    inet6 fe80::20c:29ff:fe6e:8229/64 scope link 
       valid_lft forever preferred_lft forever

ИЗМЕНИТЬ

...
#include <sys/ioctl.h>
#include <net/if.h>
...

struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth0");
if(ioctl(sd, SIOCGIFINDEX, &ifr) < 0) {
    perror("ioctl failed!");
    return EXIT_FAILURE;
}
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &ifr, sizeof(ifr)) < 0) {
    perror("setsockopt() error");
    exit(-1);
}
    printf("setsockopt() is OK\n");

Но все равно идет через lo интерфейс. Это что-то с мостовым сетевым интерфейсом на моей виртуальной машине?

ИЗМЕНИТЬ 2

Теперь я сравнил свои необработанные IP-пакеты с теми, которые отправляет hping2, и единственное, что отличается, — это идентификатор интерфейса (указанный во фрейме) и то, что уровень Ethernet не содержит никакой информации о MAC-адресе. hping2 отправляет через интерфейс eth0 и содержит всю информацию о MAC-адресе. Моя программа отправляет его через lo и НЕ содержит никакой информации о MAC-адресе (возможно, он отправляется через интерфейс lo, потому что он не содержит никакой информации о MAC-адресе в пакете???). Посмотрите на это изображение: Кадр Ethernet не содержит информации о MAC-адресе

Я также сравнил исходный код hping2 с моим кодом создания необработанного IP-пакета, и я не вижу ничего, что заставило бы пакеты проходить через интерфейс lo, как в моем случае. И я вообще не понимаю, почему, черт возьми, моя программа не будет включать MAC-адреса в мои пакеты. Все остальное в моем пакете равно содержимому пакетов hping2, за исключением порядкового номера, который рандомизирован.

Любые другие идеи?


person Rox    schedule 31.12.2013    source источник
comment
http-сервер находится на другой машине или на виртуальной машине?   -  person Willem van Doesburg    schedule 31.12.2013
comment
Это на виртуальной машине. И клиент (Kali Linux), и сервер (CentOS) виртуализированы, и я использую мостовую сеть на обеих машинах.   -  person Rox    schedule 31.12.2013


Ответы (2)


Сам решил. Это был sin.sin_addr.s_addr, который указывал на IP-адрес отправителя, но это должен был быть IP-адрес сервера! Будьте осторожны, потому что не всегда легко увидеть такие ошибки в коде! :-)

Теперь пакеты содержат правильную информацию о MAC-адресе.

Следующая проблема заключается в том, почему я не получаю никаких syn-acks с сервера, но я создам новый вопрос для этой проблемы.

person Rox    schedule 03.01.2014
comment
это яркий пример для некоторых std::cerr - person ldgorman; 07.01.2014

Чтобы выбрать определенный сетевой интерфейс (в Linux) для исходящего трафика, вы можете использовать:

setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device)); 

Ссылки с примером фрагмента кода

Дополнительная информация

person rakeshdn    schedule 31.12.2013
comment
Я предполагаю, что когда вы используете необработанные сокеты, он передает ARP и просто отправляет его на первый интерфейс в списке. - person rakeshdn; 31.12.2013
comment
Посмотрите на мою правку. Я попробовал фрагмент в вашей первой ссылке, но он дает мне некоторые ошибки. - person Rox; 31.12.2013
comment
Хорошо, первая ошибка из-за назначения. Вы должны сделать strcpy/sprintf/memcpy. ifreq — это структура, и при выполнении поиска в Google я нашел сообщения, в которых люди сталкивались с проблемами, когда и linux/if.h, и net/if.h включаются (иногда косвенно) в один и тот же исходный файл. Можете ли вы попробовать переупорядочить включения, чтобы net/xxx стояло первым. - person rakeshdn; 31.12.2013
comment
austinmarton.wordpress.com/2011/09/14/ - person rakeshdn; 31.12.2013
comment
Спасибо за ссылку. Я очень внимательно прочитал информацию и попробовал то, что в ней указано, но безуспешно. Посмотрите на мою правку 2 в моем посте выше. - person Rox; 01.01.2014
comment
Неинициализированный MAC-адрес также является моим главным подозреваемым. Если вы сделали то, что написано в блоге выше в заголовке Construct the Ethernet: тогда я понятия не имею, почему mac-адрес не инициализирован. - person rakeshdn; 01.01.2014