2017-02-02 4 views
0

Ich studiere Socket-Programmierung mit C/C++ und ich denke, der beste Weg ist es, in sie einzutauchen. Ich kann Daten an den Socket senden, indem ich socket.h send() verwende, und möchte daher tiefer gehen, indem ich Netzwerkpakete herstelle.Crafting TCP/IP Pakete

Ich habe versucht, konnte aber immer noch nicht herausfinden, welcher Teil meiner Daten ungültig ist, wie ich Invalid argument errno 22. Das ist meine IP-Header in hex bin immer:

45 00 28 00 
d4 31 00 00 
ff 06 3c 6e 
c0 a8 01 06 
c0 a8 01 01 

Und das ist mein TCP header:

00 50 00 50 
00 00 00 00 
00 00 00 00 
50 02 16 d0 
15 1b 00 00 

Ich schätze alle Tipps.

NB: Ich lese beej.us und here für meine Studien.

Edit: Das ist mein Code:

struct pseudo_header { 
    u_int32_t source_address; 
    u_int32_t dest_address; 
    u_int8_t placeholder; 
    u_int8_t protocol; 
    u_int16_t tcp_length; 
}; 

int main(int argc, char* argv[]) { 
    int sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP); 
    if (sockfd == -1) { 
     perror("Failed to create socket"); 
     exit(1); 
    } 

    // Datagram to represent the packet 
    char datagram[4096]; 
    memset(datagram, 0, 4096); // zero out the packet buffer 

    //Data part 
    char *data = datagram + sizeof(struct ip) + sizeof(struct tcphdr); 
    strcpy(data, ""); 

    // some address resolution 
    char source_ip[32]; 
    strcpy(source_ip, "192.168.1.6"); 
    struct sockaddr_in sai; 
    sai.sin_family = AF_INET; 
    sai.sin_port = htons(80); 

    sai.sin_addr.s_addr = inet_addr("192.168.1.1"); 
    cout << "sai.sin_addr.s_addr=" << sai.sin_addr.s_addr << endl; 

    //Fill in the IP Header 
    struct ip *iph = (struct ip *) datagram; 
    iph->ip_hl = 5; 
    iph->ip_v = 4; 
    iph->ip_tos = 0; 
    iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data); 
    iph->ip_id = htons(54321); 
    iph->ip_off = 0; 
    iph->ip_ttl = 255; 
    iph->ip_p = IPPROTO_TCP; 
    iph->ip_sum = 0; 
    iph->ip_src.s_addr = inet_addr(source_ip); 
    iph->ip_dst.s_addr = sai.sin_addr.s_addr; 

    //Ip checksum 
    unsigned short checksum = csum((unsigned short *) datagram, iph->ip_len); 
    iph->ip_sum = checksum; 
    cout << "iph->ip_sum=" << checksum << endl; 

    unsigned char *pIph = (unsigned char *) datagram; 
    for (int i = 0; i < 20; i++) { 
     cout << setfill('0') << setw(2) << hex << (int) pIph[i] << " "; 
     if (i + 1 >= 4 && (i + 1) % 4 == 0) { 
      cout << endl; 
     } 
    } 

    //TCP Header 
    struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof(struct ip)); 
    struct pseudo_header psh; 
    tcph->th_sport = htons(80); 
    tcph->th_dport = htons(80); 
    tcph->th_seq = 0; 
    tcph->th_ack = 0; 
    tcph->th_off = 5; 
    tcph->th_flags = TH_SYN; 
    tcph->th_win = htons(5840); /* maximum allowed window size */ 
    tcph->th_sum = 0; 
    tcph->th_urp = 0; 

    //Now the TCP checksum 
    psh.source_address = inet_addr(source_ip); 
    psh.dest_address = sai.sin_addr.s_addr; 
    psh.placeholder = 0; 
    psh.protocol = IPPROTO_TCP; 
    psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data)); 

    int psize = sizeof(struct pseudo_header) + 
       sizeof(struct tcphdr) + 
       strlen(data); 

    char *pseudogram = malloc(psize); 

    memcpy(pseudogram, (char*) &psh, sizeof(struct pseudo_header)); 
    memcpy(pseudogram + sizeof(struct pseudo_header), tcph, sizeof(struct tcphdr) + strlen(data)); 

    checksum = csum((unsigned short*) pseudogram, psize); 
    tcph->th_sum = checksum; 
    cout << "tcph->th_sum=" << checksum << endl; 

    unsigned char *pTcph = (unsigned char *) tcph; 
    for (int i = 0; i < 20; i++) { 
     cout << setfill('0') << setw(2) << hex << (int) pTcph[i] << " "; 
     if (i + 1 >= 4 && (i + 1) % 4 == 0) { 
      cout << endl; 
     } 
    } 

    //IP_HDRINCL to tell the kernel that headers are included in the packet 
    int one = 1; 
    const int *val = &one; 
    if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) { 
     perror("Error setting IP_HDRINCL"); 
     exit(0); 
    } 

    struct sockaddr *pSa = (struct sockaddr *) &sai; 

    // Send the packet 
    if (sendto(sockfd, datagram, iph->ip_len, 0, pSa, sizeof(sai)) < 0) { // failed here 
     perror("sendto failed"); 

    } else { //Data send successfully 
     printf("Packet Send. Length : %d \n", iph->ip_len); 
    } 

    return 1; 
} 
+0

Gibt es einen Grund, warum Sie erstellen möchten, und Handle deine eigenen rohen Pakete? Wenn Sie nur Netzwerk-Programmierung lernen wollen, dann ist das nicht wirklich ein guter Anfang IMO. Erfahren Sie stattdessen, wie Sie die Standardprotokolle TCP und UDP mit den normalen Sockeln 'SOCK_STREAM' und' SOCK_DGRAM' verwenden. Dann, wenn Sie Ihren eigenen Netzwerk-Stack (aus irgendeinem masochistischen Grund) implementieren möchten, können Sie das tun, aber nehmen Sie es langsam, Schritt für Schritt, und *** beginnen an der Spitze ***. –

+0

Wo * genau * findest du 'errno' auf 22 gesetzt? Bitte posten Sie einen Teil Ihres Codes, wo er fehlschlägt. –

+0

Ja, es gibt einen Grund, warum ich rohe Pakete herstellen will. Ich möchte ein ICMP-Tunneling-Programm schreiben. – anon

Antwort

2

In Ihrem IPv4-Header:

45 00[28 00] 
d4 31 00 00 
ff 06 3c 6e 
c0 a8 01 06 
c0 a8 01 01 

Ist Ihre Paketlänge 10240 (0x2800)?

Sind Sie sicher, dass es nicht 40 (0x0028) ist?

45 00[00 28] 
d4 31 00 00 
ff 06[64 46] // checksum updated 
c0 a8 01 06 
c0 a8 01 01 

EDIT: jetzt, dass Sie Ihren Code geschrieben ...

Sie ersetzen sollten:

iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data); 

von:

iph->ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data)); 
+0

Nur ein Hinweis, um Ausführlichkeit hinzuzufügen, die Felder in der Kopfzeile sind ** Big Endian **, das heißt, für Multibyte-Felder ist das höchstwertige Byte zuerst. –

+0

Um zu rephasieren, sind alle Ganzzahlen in den Paketen in ** Netzwerk-Byte-Reihenfolge **. Wenn Multibyte Daten in ein Netzwerk einfügt, möchten Sie immer die Byte-Reihenfolge von ** Host ** in ** Netzwerk ** umwandeln, das ist die Funktion von ** h ** bis ** n ** s(). –

+0

Ich habe vermutet ist Byte-Reihenfolge ist falsch, und ich versuche immer noch zu verstehen, den Unterschied in Wiki geschrieben, und meine Host-Maschine + Cygwin, die LE verwendet. Nur wundern, die Flags und Offset-Felder sind weniger als ein Byte (3 Bits und 13 Bits), wie sollte das strukturiert sein? Sollten diese 2 Bytes zusammen betrachtet werden? – anon