простая реализация icmp traceroute на C

Хорошо, поэтому я попытался написать простую трассировку ICMP, используя материалы моего лектора, которые были программами echo_request и icmp_receive, которые я хотел объединить для достижения своей цели, но это вроде не работает.

Вы можете увидеть код этих и других программ, которые я включаю в свой код > здесь ‹

Имейте в виду, что я очень новичок в программировании сокетов. Я начал как сегодня и все еще кое-что не понимаю.

Итак, мой код выглядит так (это не последняя версия traceroute! Это просто фрагмент, который, как я думал, должен дать некоторые результаты):

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netinet/ip_icmp.h>
#include "sockwrap.h"
#include "icmp.h"
#include <time.h>

unsigned char   buffer[IP_MAXPACKET+1];
unsigned char*  buffer_ptr;
int             re;

int main(int argc, char **argv){
    if(argc != 2){
        printf("Usage: ./traceroute <ip>\n");
        return 1;
    }

    int sockfd = Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

    struct sockaddr_in remote_address;
    bzero (&remote_address, sizeof(remote_address));
    remote_address.sin_family = AF_INET;
    inet_pton(AF_INET, argv[1], &remote_address.sin_addr);

    int ttl = 1;

    while(1){
        struct icmp icmp_packet;
        icmp_packet.icmp_type = ICMP_ECHO;
        icmp_packet.icmp_code = 0;
        icmp_packet.icmp_id = 123;      
        icmp_packet.icmp_seq = ttl;     
        icmp_packet.icmp_cksum = 0;
        icmp_packet.icmp_cksum = in_cksum((u_short*)&icmp_packet, 8, 0);

        Setsockopt (sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(int));

        Sendto(sockfd, &icmp_packet, ICMP_HEADER_LEN, 0, &remote_address, sizeof(remote_address));
        Sendto(sockfd, &icmp_packet, ICMP_HEADER_LEN, 0, &remote_address, sizeof(remote_address));
        Sendto(sockfd, &icmp_packet, ICMP_HEADER_LEN, 0, &remote_address, sizeof(remote_address));

        clock_t start = clock();
        float seconds = 0;
        while(seconds < 1){
            clock_t c = clock();
            seconds = (float)(c - start)/CLOCKS_PER_SEC;
        }

        struct sockaddr_in sender;  
        socklen_t sender_len = sizeof(sender);
        buffer_ptr = buffer;
        re = Recvfrom (sockfd, buffer_ptr, IP_MAXPACKET, 
                                            0, &sender, &sender_len);

        char str[20]; 
        inet_ntop(AF_INET, &(sender.sin_addr), str, sizeof(str));
        // this printf shows me ip adresses of routers on route to 
        // destination(probably?), but anything below it doesn't seem to work
        // properly :(
        //printf ("%s\n", str);

        struct icmp* icmp_packet_recv = (struct icmp*) buffer_ptr;

        printf("%d %d\n", icmp_packet_recv->icmp_id, icmp_packet_recv->icmp_seq);

        if (icmp_packet_recv->icmp_type == ICMP_TIME_EXCEEDED && 
            icmp_packet_recv->icmp_code == ICMP_EXC_TTL) {

            struct ip* packet_orig = (struct ip*) buffer_ptr;

            if (packet_orig->ip_p == IPPROTO_ICMP) {
                if(icmp_packet_recv->icmp_seq == ttl && icmp_packet_recv->icmp_id == 123)
                    printf ("%s\n", str);
            }
        }
        if (icmp_packet_recv->icmp_type == ICMP_ECHOREPLY) {
            printf ("%s\n", str);
            return 0;
        }

        ttl++;
    }

    return 0;
}

Это вроде как работает, потому что когда я printf("%s\n", str);, где str - это IP-адрес отправителя, это выглядит так, как будто это трассировка, но когда я хочу проверить пакет, который я получаю, он содержит icmp_id и icmp_seq отличные от того, что я отправил. Мне это кажется странным, потому что когда я запускаю echo_request и icmp_receive и отправляю пакет с id 123 и seq 1, я получаю точно такие же id и seq, но в моей программе все по-другому (то, что я проверял этим printf("%d %d\n", icmp_packet_recv->icmp_id, icmp_packet_recv->icmp_seq);).

Если бы вы могли указать, где я делаю глупости в своем коде, и объяснить это, я был бы признателен. Эта задача оказалась сложнее, чем я ожидал.


person Octothorp    schedule 30.03.2015    source источник


Ответы (1)


Вам нужно проверить re после звонка на Recvfrom. Может быть ошибка или другая длина того, что вы ожидаете.

Кроме того, насколько я помню, с этим сокетом RAW вы будете получать все пакеты ICMP, отправленные на ваш сетевой интерфейс; поэтому вам нужно получать все пакеты и работать только с пакетами, которые содержат одинаковый порядковый номер.

И я не мог видеть bind(). Разве это не нужно?

person cls    schedule 30.03.2015
comment
Recvfrom - это оболочка для recvfrom, в которой я проверяю ошибки. Насколько я знаю, для пакетов ICMP нет необходимости в bind (). Что меня действительно беспокоит, так это то, что моя программа никогда не достигает той части, где я проверяю превышение времени и эхо-ответ. - person Octothorp; 30.03.2015
comment
Попытайтесь получить пакеты во внутреннем цикле и распечатать данные для каждого пакета. - person cls; 30.03.2015
comment
Хм, не могли бы вы сказать мне, что мне нужно проверить, чтобы знать, что полученный пакет с превышением времени на самом деле является ответом на мой эхо-запрос? icmp_id и icmp_seq одинаковы, только когда я получаю ICMP_ECHOREPLY пакет, но когда я получаю ICMP_TIME_EXCEEDED, они кажутся случайными. - person Octothorp; 30.03.2015