C/C++ 路由跟踪小程序实现

执行:

./traceroute 183.220.98.129

编译:

复制代码到新建 traceroute.c 文件之中

gcc -D_DEFAULT_SOURCE -o traceroute traceroute.c

效果:

./traceroute 183.220.98.129
traceroute to 183.220.98.129 (183.220.98.129), 30 hops max
 1  192.168.1.1     192.168.1.1     192.168.1.1     
 2  100.72.0.1      100.72.0.1      100.72.0.1      
 3  183.222.60.97   183.222.60.97   183.222.60.97   
 4  223.86.15.65    223.86.15.65    223.86.15.65    
 5  223.87.26.174   223.87.26.174   223.87.26.174   
 6   *  *  * 
 7  183.220.98.129  183.220.98.129  183.220.98.129

例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/time.h>

// 必须在包含系统头文件前定义特性宏
#define _DEFAULT_SOURCE  // 替代废弃的 _BSD_SOURCE
#include <netinet/ip_icmp.h>

#define MAX_HOPS 30
#define PROBES_PER_HOP 3
#define TIMEOUT_SEC 3

// ICMP 校验和计算
unsigned short checksum(void *b, int len) {
    unsigned short *buf = (unsigned short *)b;
    unsigned int sum = 0;
    for (sum = 0; len > 1; len -= 2) {
        sum += *buf++;
    }
    if (len == 1) {
        sum += *(unsigned char *)buf;
    }
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    return (unsigned short)~sum;
}

// 解析 ICMP 响应
void parse_reply(char *buf, int len, struct sockaddr_in *from) {
    struct iphdr *ip = (struct iphdr *)buf;
    int iphdr_len = ip->ihl * 4;
    struct icmphdr *icmp = (struct icmphdr *)(buf + iphdr_len);

    if (icmp->type == ICMP_TIME_EXCEEDED || icmp->type == ICMP_ECHOREPLY) {
        char *orig_pkt = buf + iphdr_len + sizeof(struct icmphdr);
        struct iphdr *orig_ip = (struct iphdr *)orig_pkt;
        int orig_iphdr_len = orig_ip->ihl * 4;
        struct icmphdr *orig_icmp = (struct icmphdr *)(orig_pkt + orig_iphdr_len);

        // 检查是否为我们的探测包
        if (orig_icmp->un.echo.id == htons(getpid())) {
            printf("%-15s ", inet_ntoa(from->sin_addr));
            fflush(stdout);
        }
    }
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <hostname>\n", argv[0]);
        return 1;
    }

    // 创建原始套接字
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sock < 0) {
        perror("socket");
        return 1;
    }

    // 设置接收超时
    struct timeval tv = {TIMEOUT_SEC, 0};
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    // 解析目标地址
    struct sockaddr_in dest;
    memset(&dest, 0, sizeof(dest));
    dest.sin_family = AF_INET;
    struct hostent *host = gethostbyname(argv[1]);
    if (!host) {
        herror("gethostbyname");
        close(sock);
        return 1;
    }
    memcpy(&dest.sin_addr, host->h_addr, host->h_length);

    printf("traceroute to %s (%s), %d hops max\n", argv[1], inet_ntoa(dest.sin_addr), MAX_HOPS);

    struct sockaddr_in from;
    socklen_t fromlen = sizeof(from);
    memset(&from, 0, sizeof(from));

    // 主循环:递增 TTL
    for (int ttl = 1; ttl <= MAX_HOPS; ttl++) {
        printf("%2d  ", ttl);

        // 设置 TTL
        setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));

        // 发送多个探测包
        for (int probe = 0; probe < PROBES_PER_HOP; probe++) {
            // 构造 ICMP Echo 请求
            struct icmphdr icmp;
            memset(&icmp, 0, sizeof(icmp));
            icmp.type = ICMP_ECHO;
            icmp.code = 0;
            icmp.un.echo.id = htons(getpid());
            icmp.un.echo.sequence = htons(ttl * PROBES_PER_HOP + probe);
            icmp.checksum = checksum(&icmp, sizeof(icmp));

            // 发送包
            if (sendto(sock, &icmp, sizeof(icmp), 0, (struct sockaddr *)&dest, sizeof(dest)) <= 0) {
                perror("sendto");
                continue;
            }

            // 接收响应
            char buf[1024];
            int ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
            if (ret < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    printf(" * ");
                } else {
                    perror("recvfrom");
                }
            } else {
                parse_reply(buf, ret, &from);
            }
            usleep(100000); // 避免 flood
        }
        printf("\n");

        // 到达目标则退出
        if (from.sin_addr.s_addr == dest.sin_addr.s_addr) {
            break;
        }
    }

    close(sock);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值