在C程序中发送ICMP数据包 [英] Sending ICMP packets in a C program

查看:144
本文介绍了在C程序中发送ICMP数据包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在C语言中创建ICMP ping测试程序,但是在成功发送数据包方面遇到困难. sendto函数返回字节数和所有内容,但实际上未发送任何数据包.我已经在目标计算机上使用WireShark对此进行了验证.定期对主机执行ping操作可以正常工作,并且可以在WireShark中显示.

I'm trying to create an ICMP ping test program in C but am having difficulties with successfully sending the packets. The sendto function returns the # of bytes and everything but no packets are actually sent. I've verified this with WireShark on the destination computer. A regular ping on the host works fine and shows up in WireShark though.

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>

unsigned short cksum(unsigned short *addr, int len);

int main(int argc, char *argv[])
{
    int sock;
    char send_buf[400], recv_buf[400], src_name[256], src_ip[15], dst_ip[15];
    struct ip *ip = (struct ip *)send_buf;
    struct icmp *icmp = (struct icmp *)(ip + 1);
    struct hostent *src_hp, *dst_hp;
    struct sockaddr_in src, dst;
    struct timeval t;
    int on;
    int num = 10;
    int failed_count = 0;
    int bytes_sent, bytes_recv;
    int dst_addr_len;
    int result;
    fd_set socks;

    /* Initialize variables */
    on = 1;
    memset(send_buf, 0, sizeof(send_buf));

    /* Check for valid args */
    if(argc < 2)
    {
        printf("\nUsage: %s <dst_server>\n", argv[0]);
        printf("- dst_server is the target\n");
        exit(EXIT_FAILURE);
    }

    /* Check for root access */
    if (getuid() != 0)
    {
        fprintf(stderr, "%s: This program requires root privileges!\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Get source IP address */
    if(gethostname(src_name, sizeof(src_name)) < 0)
    {
        perror("gethostname() error");
        exit(EXIT_FAILURE);
    }
    else
    {
        if((src_hp = gethostbyname(src_name)) == NULL)
        {
            fprintf(stderr, "%s: Can't resolve, unknown source.\n", src_name);
            exit(EXIT_FAILURE);
        }
        else
            ip->ip_src = (*(struct in_addr *)src_hp->h_addr);
    }

    /* Get destination IP address */
    if((dst_hp = gethostbyname(argv[1])) == NULL)
    {
        if((ip->ip_dst.s_addr = inet_addr(argv[1])) == -1)
        {
            fprintf(stderr, "%s: Can't resolve, unknown destination.\n", argv[1]);
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        ip->ip_dst = (*(struct in_addr *)dst_hp->h_addr);
        dst.sin_addr = (*(struct in_addr *)dst_hp->h_addr);
    }

    sprintf(src_ip, "%s", inet_ntoa(ip->ip_src));
    sprintf(dst_ip, "%s", inet_ntoa(ip->ip_dst));
    printf("Source IP: '%s' -- Destination IP: '%s'\n", src_ip, dst_ip);

    /* Create RAW socket */
    if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
    {
        perror("socket() error");

        /* If something wrong, just exit */
        exit(EXIT_FAILURE);
    }

    /* Socket options, tell the kernel we provide the IP structure */
    if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
    {
        perror("setsockopt() for IP_HDRINCL error");
        exit(EXIT_FAILURE);
    }

    /* IP structure, check the ip.h */
    ip->ip_v = 4;
    ip->ip_hl = 5;
    ip->ip_tos = 0;
    ip->ip_len = htons(sizeof(send_buf));
    ip->ip_id = htons(321);
    ip->ip_off = htons(0);
    ip->ip_ttl = 255;
    ip->ip_p = IPPROTO_ICMP;
    ip->ip_sum = 0;

    /* ICMP structure, check ip_icmp.h */
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_id = 123;
    icmp->icmp_seq = 0;

    /* Set up destination address family */
    dst.sin_family = AF_INET;

    /* Loop based on the packet number */
    for(int i = 1; i <= num; i++)
    {
        /* Header checksums */
        icmp->icmp_cksum = 0;
        ip->ip_sum = cksum((unsigned short *)send_buf, ip->ip_hl);
        icmp->icmp_cksum = cksum((unsigned short *)icmp, sizeof(send_buf) - sizeof(struct icmp));

        /* Get destination address length */
        dst_addr_len = sizeof(dst);

        /* Set listening timeout */
        t.tv_sec = 5;
        t.tv_usec = 0;

        /* Set socket listening descriptors */
        FD_ZERO(&socks);
        FD_SET(sock, &socks);

        /* Send packet */
        if((bytes_sent = sendto(sock, send_buf, sizeof(send_buf), 0, (struct sockaddr *)&dst, dst_addr_len)) < 0)
        {
            perror("sendto() error");
            failed_count++;
            printf("Failed to send packet.\n");
            fflush(stdout);
        }
        else
        {
            printf("Sent %d byte packet... ", bytes_sent);

            fflush(stdout);

            /* Listen for the response or timeout */
            if((result = select(sock + 1, &socks, NULL, NULL, &t)) < 0)
            {
                perror("select() error");
                failed_count++;
                printf("Error receiving packet!\n");
            }
            else if (result > 0)
            {
                printf("Waiting for packet... ");
                fflush(stdout);

                if((bytes_recv = recvfrom(sock, recv_buf, sizeof(ip) + sizeof(icmp) + sizeof(recv_buf), 0, (struct sockaddr *)&dst, (socklen_t *)&dst_addr_len)) < 0)
                {
                    perror("recvfrom() error");
                    failed_count++;
                    fflush(stdout);
                }
                else
                    printf("Received %d byte packet!\n", bytes_recv);
            }
            else
            {
                printf("Failed to receive packet!\n");
                failed_count++;
            }

            fflush(stdout);

            icmp->icmp_seq++;
        }
    }

    /* Display success rate */
    printf("Ping test completed with %d%% success rate.\n", (((num - failed_count) / num) * 100));

    /* close socket */
    close(sock);

    return 0;
}

/* One's Complement checksum algorithm */
unsigned short cksum(unsigned short *addr, int len)
{
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1)
    {
      sum += *w++;
      nleft -= 2;
    }

    if (nleft == 1)
    {
      *(unsigned char *)(&answer) = *(unsigned char *)w;
      sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;

    return (answer);
}

对我在做什么错有任何想法吗?另外,校验和还好吗?两者都使用0可以吗?

Any thoughts at what I'm doing wrong? Also, are the checksums alright? Is it fine to just use 0 for both?

编辑:好的,感谢以下人员,我已经设法正确发送了数据包!并且目标计算机将发回回显答复.但是,现在,我的程序没有收到答复. select()函数始终超时.我已经更新了上面的代码.

OK, so I've managed to get the packets sent properly thanks to the folks below! And the destination computer will send back an echo reply. However, now, my program doesn't pick up the reply. The select() function always times out. I've updated my code above.

好,我已经开始工作了!我需要将套接字协议设置为IPPROTO_ICMP而不是IPPROTO_RAW,而且效果很好!再次感谢你们发表了评论!真的帮了忙!看来我只能将一个答案标记为正确,但是大家都帮助解决了这个问题. :)

EDIT 2: Alright, I've got it working! I needed to set the socket protocol to IPPROTO_ICMP instead of IPPROTO_RAW and it worked great! Thanks again you guys that commented! Really helped out! Looks like I can only mark one answer correct, but you all aided in fixing this. :)

推荐答案

我注意到的一件事...

Just one thing that I've noticed...

您已经拥有了:

 struct ip *ip = (struct ip *)send_buf;

然后,您要分配目标字段:

Then, you are assigning destination field:

ip->ip_dst = (*(struct in_addr *)dst_hp->h_addr)

然后使用memset擦除它(因为send_buff指向同一件事):

And then you are erasing it with memset (since send_buff points to the same thing):

memset(send_buf, 0, sizeof(send_buf));

因此,当您尝试在此处获取ip_dst时:

So, when you are trying to get ip_dst here:

dst.sin_addr = ip->ip_dst;

您将获得0,而不是先前设置的值.

you are getting 0 instead of what you set earlier.

这篇关于在C程序中发送ICMP数据包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆