2012-12-30 26 views
8

Masz pewne problemy z odbieraniem pakietów. Mogę odbierać i czytać przychodzące pakiety, ale myślę, że nie dostaję uścisku dłoni z jakimkolwiek hostem. Chcę tylko wysłać pakiet do zdalnego komputera z otwartym portem po odebraniu odpowiedzi, aby zobaczyć TTL (czas życia) i rozmiar okna. Czy ktoś ma pomysł, gdzie są błędy? (nie mam bardzo głęboką wiedzę w programowaniu C) Raw Socket Linux wysyła/odbiera pakiet

KOD:

#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() ok\n"); 
    } 

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

    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 ok\n"); 

    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: %i\n", checksum); 
    printf("Destination : %i\n", ntohs(tcp->dest)); 
    printf("Source: %i\n", 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= %d\n", ip->ttl); 
    printf("Window= %d\n", tcp->window); 
    printf("ACK= %d\n", tcp->ack); 
    printf("%s:%d\t --> \t%s:%d \tSeq: %d \tAck: %d\n", 
        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; 
} 
+0

z przychodzących pakietów Znaczy, istnieją inne przychodzące od wszędzie, ale nie od muszę – x4k3p

+0

dlaczego do analizowania pakietu? Próbowałem uzyskać odpowiedź na czystej maszynie unix pod vmware. kiedy wysyłam pakiet ręcznie, mam to, czego potrzebuję. ale jeśli po prostu czekam, nic się nie stanie. w prawdziwym systemie pojawiają się pakiety, ale są one z innego miejsca. – x4k3p

+0

Właśnie przeredagowałem moje komentarze jako rzeczywistą odpowiedź. Mam nadzieję, że nie masz nic przeciwko. – jweyrich

Odpowiedz

10
  1. Wysłaliśmy i przechowywania pakietów w buffer, ale jesteś drukowania danych z ip i tcp bez analizowania tego bufora. Powinieneś sparsować pakiet od bufferpo otrzymaniu go i przed wydrukowaniem.
  2. Twój kod zakłada, że ​​wszystkie pakiety to TCP, co nie jest prawdą. Gniazda RAW obsługują tylko protokoły Layer 3 (IP, ICMP, itp.). Innymi słowy, używanie IPPROTO_TCP wprowadza w błąd podczas tworzenia gniazda RAW. Przykleić do IPPROTO_IP i dodać niezbędne warunki do kodu dla każdego protokołu, na którym Ci zależy (TCP, UDP, itp.). Zdarza się, że działa, ponieważ jądro Linux sprawdza poprawność numeru protokołu i awarie do IPPROTO_IP. Może to jednak nie działać w innych systemach.
  3. Sprawdź, czy w komunikacji sieciowej używana jest poprawna kolejność bajtów. Kolejność sieć-bajt to Big-Endian, natomiast kolejność-hosta zależy od architektury, więc może zajść potrzeba konwersji pól wielobajtowych tam iz powrotem.
  4. Twoja tcp->seq może mieć nieprawidłową wartość, ponieważ TCP akceptuje tylko wartości do 65535, a random() zwraca wartości od 0 do RAND_MAX (0x7fffffff). Wypróbuj tcp->seq = htonl(random() % 65535);
  5. Twoje obliczenie przesunięcia dla nagłówka TCP jest nieprawidłowe. Powinien to być sizeof(struct iphdr), a nie sizeof(struct tcphdr).
+0

Czy można odbierać pakiety TCP lub UDP z gniazdem raw? Myślałem, że te przychodzące pakiety zawsze były przekazywane do sterowników TCP i UDP. – Barmar

+0

@Barmar to możliwe. Jądro dostarcza kopię każdego datagramu do każdego procesu, który utworzył gniazdo RAW pasujące do protokołu datagramowego. – jweyrich

+0

Znaleziono błąd! Jest to bufor, ponieważ wysyłałem śmieci do zdalnego hosta. Dlatego nie mogłem otrzymać odpowiedzi. Korekta: char buffer [1024] = {0}; – x4k3p

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

tutaj, aby uzyskać indeks tablicy nagłówku TCP w buffer, trzeba dodać sizeof(struct iphdr) do buffer jak wymienione poniżej.

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