XazGreatXia 2024-08-14 17:13 采纳率: 31%
浏览 4
已结题

C语言icmp协议的实现


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
struct icmp_packet {
    struct icmphdr icmp_hdr;
    char payload[64];
};
u_int16_t in_cksum(u_int16_t *packet,int packlen) {

    unsigned long sum = 0;

    while(packlen > 1) {
        sum = sum + *(packet++);
        packlen = packlen - 2;
    }

    if(packlen > 0) {
        sum = sum + *(unsigned char *)packet;
    }

    while(sum >> 16) {
        sum = (sum & 0xffff) + (sum >> 16);
    }

    return (u_int16_t)~sum;
}
int main(int argc, char *argv[]) {
    int sockfd;
    struct sockaddr_in dest_addr;
    struct hostent *host;
    char buffer[1024];
    struct icmp_packet i_packet;
    struct icmp_packet *recvp;
    int dest_len = sizeof(dest_addr);
    int i, cc;
 
    if (argc != 2) {
        fprintf(stderr, "Usage: %s [The hostname you want to test]\n", argv[0]);
        exit(1);
    }
 
    // 创建原始套接字
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 
    // 检查是否创建成功
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }
 
    // 解析目标主机名
    bzero(&dest_addr, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = 0;
    dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
    //if ((host = gethostbyname(argv[1])) == NULL) {
    //    fprintf(stderr, "Unknown host: %s\n", argv[1]);
    //    exit(1);
    //}
    //memcpy((char *)&dest_addr.sin_addr, (char *)host->h_addr_list, host->h_length);
 
    // 填充ICMP头部和数据
    memset(&i_packet,0,sizeof(i_packet));
    i_packet.icmp_hdr.type = ICMP_ECHO; // 回显请求类型
    i_packet.icmp_hdr.code = 0;
    i_packet.icmp_hdr.checksum = 0;
    i_packet.icmp_hdr.un.echo.sequence = 1;
    i_packet.icmp_hdr.un.echo.id = getpid();
 
    for (i = 0; i < 256; i++) {
        buffer[i] = (char)i;
    }
 
    // 计算ICMP校验和
    i_packet.icmp_hdr.checksum = in_cksum((unsigned short *)&i_packet, sizeof(struct icmp_packet));
 
    // 发送ICMP请求
    if (sendto(sockfd, buffer, sizeof(struct icmp_packet) + 256, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) {
        perror("sendto");
        exit(1);
    }
 
    // 接收ICMP回复
    cc = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest_addr, &dest_len);
    if (cc < 0) {
        perror("recvfrom");
        exit(1);
    } else {
        printf("Host is alive.");
    }
    //recvp = (struct icmp_packet *)&buffer;
    // 检查回复的ICMP数据
    //if (recvp->icmp_hdr.type == ICMP_ECHOREPLY) {
        //printf("Received ICMP Echo Reply from %s\n", inet_ntoa(dest_addr.sin_addr));
    //} else {
        //printf("Received ICMP Type: %d\n", recvp->icmp_hdr.type);
    //}
 
    close(sockfd);
    return 0;
}

请问我这段实现icmp的代码是不是哪里少了什么,或者出错了,执行了以后什么都不回应,就是一直卡着。

  • 写回答

1条回答 默认 最新

  • GISer Liu 2024-08-14 17:16
    关注

    该回答引用自GPT-deepseek, 由博主 GISer Liu 编写:

    问题分析

    您的代码实现了一个简单的ICMP Echo请求,但在执行时没有得到预期的回应,而是卡住了。这可能是由于以下几个原因:

    1. 目标主机的IP地址解析问题:您在代码中注释掉了通过域名解析IP地址的部分,直接使用了inet_addr函数,这可能导致无法正确解析目标主机的IP地址。
    2. ICMP数据包的发送和接收问题:发送和接收ICMP数据包的逻辑可能存在问题。
    3. 权限问题:使用原始套接字需要超级用户权限。

    解决方案

    1. 目标主机的IP地址解析

    首先,确保能够正确解析目标主机的IP地址。取消注释通过域名解析IP地址的部分,并进行相应的错误处理。

    // 解析目标主机名
    if ((host = gethostbyname(argv[1])) == NULL) {
        fprintf(stderr, "Unknown host: %s\n", argv[1]);
        exit(1);
    }
    memcpy((char *)&dest_addr.sin_addr, (char *)host->h_addr_list[0], host->h_length);
    

    2. ICMP数据包的发送和接收

    确保发送和接收ICMP数据包的逻辑正确。发送时应该使用i_packet而不是buffer,接收时应该正确解析接收到的数据。

    // 发送ICMP请求
    if (sendto(sockfd, &i_packet, sizeof(struct icmp_packet), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) {
        perror("sendto");
        exit(1);
    }
    
    // 接收ICMP回复
    cc = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest_addr, &dest_len);
    if (cc < 0) {
        perror("recvfrom");
        exit(1);
    } else {
        recvp = (struct icmp_packet *)&buffer[20]; // 跳过IP头部
        if (recvp->icmp_hdr.type == ICMP_ECHOREPLY) {
            printf("Received ICMP Echo Reply from %s\n", inet_ntoa(dest_addr.sin_addr));
        } else {
            printf("Received ICMP Type: %d\n", recvp->icmp_hdr.type);
        }
    }
    

    3. 权限问题

    确保程序以超级用户权限运行,因为使用原始套接字需要超级用户权限。

    完整代码

    以下是修改后的完整代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/in_systm.h>
    #include <netinet/ip.h>
    #include <netinet/ip_icmp.h>
    #include <arpa/inet.h>
    
    struct icmp_packet {
        struct icmphdr icmp_hdr;
        char payload[64];
    };
    
    u_int16_t in_cksum(u_int16_t *packet, int packlen) {
        unsigned long sum = 0;
        while (packlen > 1) {
            sum = sum + *(packet++);
            packlen = packlen - 2;
        }
        if (packlen > 0) {
            sum = sum + *(unsigned char *)packet;
        }
        while (sum >> 16) {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        return (u_int16_t)~sum;
    }
    
    int main(int argc, char *argv[]) {
        int sockfd;
        struct sockaddr_in dest_addr;
        struct hostent *host;
        char buffer[1024];
        struct icmp_packet i_packet;
        struct icmp_packet *recvp;
        int dest_len = sizeof(dest_addr);
        int i, cc;
    
        if (argc != 2) {
            fprintf(stderr, "Usage: %s [The hostname you want to test]\n", argv[0]);
            exit(1);
        }
    
        // 创建原始套接字
        sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    
        // 检查是否创建成功
        if (sockfd < 0) {
            perror("socket");
            exit(1);
        }
    
        // 解析目标主机名
        bzero(&dest_addr, sizeof(dest_addr));
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = 0;
        if ((host = gethostbyname(argv[1])) == NULL) {
            fprintf(stderr, "Unknown host: %s\n", argv[1]);
            exit(1);
        }
        memcpy((char *)&dest_addr.sin_addr, (char *)host->h_addr_list[0], host->h_length);
    
        // 填充ICMP头部和数据
        memset(&i_packet, 0, sizeof(i_packet));
        i_packet.icmp_hdr.type = ICMP_ECHO; // 回显请求类型
        i_packet.icmp_hdr.code = 0;
        i_packet.icmp_hdr.checksum = 0;
        i_packet.icmp_hdr.un.echo.sequence = 1;
        i_packet.icmp_hdr.un.echo.id = getpid();
    
        for (i = 0; i < 64; i++) {
            i_packet.payload[i] = (char)i;
        }
    
        // 计算ICMP校验和
        i_packet.icmp_hdr.checksum = in_cksum((unsigned short *)&i_packet, sizeof(struct icmp_packet));
    
        // 发送ICMP请求
        if (sendto(sockfd, &i_packet, sizeof(struct icmp_packet), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) {
            perror("sendto");
            exit(1);
        }
    
        // 接收ICMP回复
        cc = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest_addr, &dest_len);
        if (cc < 0) {
            perror("recvfrom");
            exit(1);
        } else {
            recvp = (struct icmp_packet *)&buffer[20]; // 跳过IP头部
            if (recvp->icmp_hdr.type == ICMP_ECHOREPLY) {
                printf("Received ICMP Echo Reply from %s\n", inet_ntoa(dest_addr.sin_addr));
            } else {
                printf("Received ICMP Type: %d\n", recvp->icmp_hdr.type);
            }
        }
    
        close(sockfd);
        return 0;
    }
    

    总结

    通过以上修改,您的代码应该能够正确解析目标主机的IP地址,并正确发送和接收ICMP数据包。确保以超级用户权限运行程序,以避免权限问题。希望这些修改能够解决您的问题。

    如果该回答解决了您的问题,请采纳!如果没有,请私信联系或评论您的疑惑

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 8月14日
  • 已采纳回答 8月14日
  • 创建了问题 8月14日

悬赏问题

  • ¥15 前端传参时,后端接收不到参数
  • ¥15 这是有什么问题吗,我检查许可证了但是显示有呢
  • ¥15 机器学习预测遇到的目标函数问题
  • ¥15 python的EOFError该怎么解决?
  • ¥15 Fluent,液体进入旋转区域体积分数不连续
  • ¥15 java linux下将docx文件转pdf
  • ¥15 maven无法下载依赖包
  • ¥15 关于pycharm, Callable[[str],bool]作为方法参数使用时, lambda 类型不提示问题
  • ¥15 layui数据重载无效
  • ¥15 寻找了解qq家园纵横四海的程序猿。