C中的简单icmp traceroute实现

时间:2015-03-30 11:03:58

标签: c sockets icmp traceroute

好的,所以我尝试使用讲师的材料编写一个简单的ICMP traceroute,这些材料是echo_request和icmp_receive程序,我想合并以实现我的目标,但它有点不起作用。

您可以在我的代码>here<

中查看这些程序的代码以及其他程序的代码

请记住,我是套接字编程的初学者。我就像今天一样开始,我不太了解一些事情。

所以我的代码看起来像这样(它不是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时,它看起来像是它的tracerouting,但是当我想检查一个我收到它的包时会有不同的{ {1}}和icmp_id来自我发送的内容。这对我来说似乎很奇怪,因为当我运行icmp_seqecho_request并发送包含icmp_receive 123和id 1的包时,我会收到完全相同的{{1} }和seq,但在我的程序中它有所不同(我在id检查的内容)。

如果你能指出我在我的代码中做蠢事的地方并解释一下,我将不胜感激。这项任务变得比我预期的更难。

1 个答案:

答案 0 :(得分:0)

您需要在致电re后查看Recvfrom。可能存在错误或不同的长度。

此外,我记得,使用此RAW套接字,您将获得发送到网络接口的所有 ICMP数据包;所以你需要接收所有数据包,只能使用包含相同序列号的数据包。

我看不到bind()。这不是必要的吗?