Raw Socket Linux отправка / получение пакета

есть некоторые проблемы при получении пакетов. Я могу принимать и читать входящие пакеты, но я думаю, что не получаю рукопожатия с любым хостом. Я только хочу отправить пакет на удаленный компьютер с открытым портом при получении ответа, чтобы увидеть TTL(время жизни) и размер окна. Кто-нибудь знает, где ошибки? (у меня нет очень глубоких знаний в программировании на C)

код:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

struct pseudohdr {
    u_int32_t src_addr;
    u_int32_t dst_addr;
    u_int8_t padding;
    u_int8_t proto;
    u_int16_t length;
};

struct data_4_checksum {
    struct pseudohdr pshd;
    struct tcphdr tcphdr;
    char payload[1024];
};
unsigned short comp_chksum(unsigned short *addr, int len) {
    long sum = 0;

    while (len > 1) {
        sum += *(addr++);
        len -= 2;
    }

    if (len > 0)
        sum += *addr;

    while (sum >> 16)
        sum = ((sum & 0xffff) + (sum >> 16));

    sum = ~sum;

    return ((u_short) sum);

}

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

    int sock, bytes, on = 1;
    char buffer[1024];
    struct iphdr *ip;
    struct tcphdr *tcp;
    struct sockaddr_in to;
    struct pseudohdr pseudoheader;
    struct data_4_checksum tcp_chk_construct;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s ", argv[0]);
        fprintf(stderr, "<dest-addr>n");
        return 1;
    }

    sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sock == -1) {
        perror("socket() failed");
        return 1;
    }else{
        printf("socket() okn");
    }

    if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) {
        perror("setsockopt() failed");
        return 2;
    }else{
        printf("setsockopt() okn");
    }

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

    int iphdrlen = sizeof(struct iphdr);
    int tcphdrlen = sizeof(struct tcphdr);
    int datalen = 0;
    printf("Typecasting okn");

    ip->frag_off = 0;
    ip->version = 4;
    ip->ihl = 5;
    ip->tot_len = htons(iphdrlen + tcphdrlen);
    ip->id = 0;
    ip->ttl = 40;
    ip->protocol = IPPROTO_TCP;
    ip->saddr = inet_addr("192.168.165.135");
    ip->daddr = inet_addr(argv[1]);
    ip->check = 0;

    tcp->source     = htons(12345);
    tcp->dest       = htons(80);
    tcp->seq        = random();
    tcp->doff       = 5;
    tcp->ack        = 0;
    tcp->psh        = 0;
    tcp->rst        = 0;
    tcp->urg        = 0;
    tcp->syn        = 1;
    tcp->fin        = 0;
    tcp->window     = htons(65535);

    pseudoheader.src_addr = ip->saddr;
    pseudoheader.dst_addr = ip->daddr;
    pseudoheader.padding = 0;
    pseudoheader.proto = ip->protocol;
    pseudoheader.length = htons(tcphdrlen + datalen);

    tcp_chk_construct.pshd = pseudoheader;
    tcp_chk_construct.tcphdr = *tcp;

    int checksum = comp_chksum((unsigned short*) &tcp_chk_construct,
            sizeof(struct pseudohdr) + tcphdrlen + datalen);

    tcp->check = checksum;

    printf("TCP Checksum: %in", checksum);
    printf("Destination : %in", ntohs(tcp->dest));
    printf("Source: %in", ntohs(tcp->source));

    to.sin_addr.s_addr = ip->daddr;
    to.sin_family = AF_INET;
    to.sin_port = tcp->dest;

    bytes = sendto(sock, buffer, ntohs(ip->tot_len), 0, (struct sockaddr*) &to,
            sizeof(to));

    if (bytes == -1) {
        perror("sendto() failed");
        return 1;
    }

    recv(sock, buffer, sizeof(buffer), 0);
    printf("TTL= %dn", ip->ttl);
    printf("Window= %dn", tcp->window);
    printf("ACK= %dn", tcp->ack);
    printf("%s:%dt --> t%s:%d tSeq: %d tAck: %dn",
                    inet_ntoa(*(struct in_addr*) &ip->saddr), ntohs(tcp->source),
                    inet_ntoa(*(struct in_addr *) &ip->daddr), ntohs(tcp->dest),
                    ntohl(tcp->seq), ntohl(tcp->ack_seq));

    return 0;
}

2 ответов


  1. вы получаете и храните пакеты в buffer, а вы печатаете данные из ip и tcp без разбора этого буфера. Вы должны разобрать пакет из buffer после получения, и перед печатью.
  2. ваш код предполагает, что все пакеты являются TCP, что не так. Необработанные сокеты поддерживают только протоколы уровня 3 (IP, ICMP и т. д.). Другими словами, используя IPPROTO_TCP вводит в заблуждение при создании сокетов. Придерживаться IPPROTO_IP, и добавьте необходимые условия к вашему коду для каждого протокола, о котором вы заботитесь (TCP, UDP и т. д.). Это работает, потому что ядро Linux проверяет номер протокола и резервирует IPPROTO_IP. Однако это может не сработать в других системах.
  3. Проверьте, использует ли ваша сетевая связь правильный порядок байтов. Порядок байтов сети является большим, в то время как порядок байтов хоста зависит от вашей архитектуры, поэтому вам может потребоваться преобразовать многобайтовые поля обратно и вперед.
  4. код tcp->seq может иметь недопустимое значение, потому что TCP принимает только значения до 65535, в то время как random() возвращает значения от 0 до RAND_MAX (0x7fffffff включительно). Попробуй!--10-->
  5. ваш расчет смещения для заголовка TCP неверен. Он должен быть!--11-->, а не sizeof(struct tcphdr).

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

здесь, чтобы получить индекс массива заголовка tcp в buffer, вам необходимо добавить sizeof(struct iphdr) to buffer, как указано ниже.

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