为什么 echo ping 没有到达目的地? [英] Why is the echo ping not reaching the destination?

查看:91
本文介绍了为什么 echo ping 没有到达目的地?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用原始套接字自己实现 ICMP 回显 ping 数据包.在我的 Linux 机器上执行 tcpdump 时,我发现 Echo ping 请求已成功发送,但是当我使用 Wireshark 观察 Windows 机器中传入的数据包时,没有观察到传入的 ICMP 请求.

I'm trying to implement an ICMP echo ping packet myself using Raw Socket. When taking tcpdump on my Linux machine, I'm finding that the Echo ping request has been successfully sent but when I'm observing the incoming packets in my Windows machine using Wireshark, no incoming ICMP requests are observed.

我编写了以下代码来实现 ICMP 回显请求和回复.

I've written the following code to implement the ICMP echo request and reply.

#include <stdio.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 
#include <unistd.h> 
#include <string.h> 
#include <stdlib.h> 
#include <netinet/ip_icmp.h> 
#include <time.h> 
#include <fcntl.h> 
#include <signal.h> 
#include <time.h> 
#include <string.h>
#include <netinet/if_ether.h> 
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <sys/ioctl.h>


#define PING_PKT_S 64 
#define PORT_NO 0 
#define PING_SLEEP_RATE 1000000 
#define RECV_TIMEOUT 1 

int pingloop=1; 

struct ping_pkt 
{
   struct ethhdr eth; 
   struct ip ip;    
   struct icmphdr hdr; 
   char msg[PING_PKT_S-sizeof(struct icmphdr)]; 
}g_pckt; 


typedef struct ping_ctx{
   int tos;
   int ttl;
   char srcIP[200];
   char dstIP[200];
   int r_sock;
}ping_ctx;

unsigned short checksum(void *b, int len)  
{
 unsigned short *buf = b; 
 unsigned int sum=0; 
 unsigned short result; 

 for ( sum = 0; len > 1; len -= 2 ) 
     sum += *buf++; 
 if ( len == 1 ) 
     sum += *(unsigned char*)buf; 
 sum = (sum >> 16) + (sum & 0xFFFF); 
 sum += (sum >> 16); 
 result = ~sum; 
 return result; 
} 

void intHandler(int dummy) 
{ 
pingloop=0; 
}  

void fill_ip_h(struct ip *ip, ping_ctx* ctx)
{
    ip->ip_src.s_addr = inet_addr(ctx->srcIP);
    ip->ip_dst.s_addr = inet_addr(ctx->dstIP);
    ip->ip_v = 4;
    ip->ip_hl = sizeof*ip >> 2;
    ip->ip_tos = (unsigned char)(ctx->tos);
    ip->ip_len = htons(sizeof(g_pckt)-sizeof(struct ethhdr));
    ip->ip_id = htons(4321);
    ip->ip_off = htons(0);
    ip->ip_ttl = (unsigned char)(ctx->ttl);
    ip->ip_p = 1;
    ip->ip_sum = 0; /* Let kernel fills in */
 }

 void fill_icmp_h(struct icmphdr* hdr,int *msg_count)
 {
    hdr->type = ICMP_ECHO;
    hdr->un.echo.id = 1; 
    hdr->un.echo.sequence = (*msg_count)++; 
 }

 void fill_data(unsigned char * data)
 {
  memset(data, 'J', PING_PKT_S-sizeof(struct icmphdr));
 }

 void fill_eth(struct ethhdr* eth){
 eth->h_source[0] = 0x78;
 eth->h_source[1] = 0xe7;
 eth->h_source[2] = 0xd1;
 eth->h_source[3] = 0xe2;
 eth->h_source[4] = 0xc3;
 eth->h_source[5] = 0x48;
 eth->h_dest[0] = 0xc0;
 eth->h_dest[1] = 0x42;
 eth->h_dest[2] = 0xd0;
 eth->h_dest[3] = 0x6e;
 eth->h_dest[4] = 0xe1;
 eth->h_dest[5] = 0xd9;
 eth->h_proto = htons(ETH_P_IP);
}

int recv_ping(int ping_sockfd)
{ 
 int addr_len,msg_received_count=0,on =1; 
 struct sockaddr_ll r_addr; 
 /*struct timespec time_start, time_end, tfs, tfe; 
 long double rtt_msec=0, total_msec=0; 
 struct timeval tv_out;*/
char* buffer = (char*) malloc (65536);

 /*Timer Settings*/
 //tv_out.tv_sec = RECV_TIMEOUT; 
 //tv_out.tv_usec = 0; 
 //clock_gettime(CLOCK_MONOTONIC, &tfs);

 addr_len=sizeof(r_addr);
memset(buffer,0,65536);

 if ( recvfrom(ping_sockfd, buffer, 65536, 0, 
                (struct sockaddr*)&r_addr, &addr_len) <= 0 ) { 
        printf("\nPacket receive failed!\n"); 
 } 

 else{ 
    /*clock_gettime(CLOCK_MONOTONIC, &time_end); 

    double timeElapsed = ((double)(time_end.tv_nsec - 
                    time_start.tv_nsec))/1000000.0; 
    rtt_msec = (time_end.tv_sec - time_start.tv_sec) * 1000.0 + timeElapsed; 

    msg_received_count++;*/

    struct ethhdr *eth = (struct ethhdr*) (buffer);
    unsigned short iphdrlen;
    struct iphdr *ip = (struct iphdr*)(buffer + sizeof(struct ethhdr));
    if((unsigned int)ip->protocol==1){
        printf("\n Ethernet Header \n");
        printf("\t|Source Address : %.2X - %.2X - %.2X - %.2X - %.2X - %.2X",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]);
        printf("\t|Destination Address : %.2X - %.2X - %.2X - %.2X - %.2X - %.2X",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]);


        printf("\n Internet header \n");
        printf("\t|-Version : %d\n",(unsigned int)ip->version);
        printf("\t|-Internet Header Length : %d DWORDS or %d Bytes\n",(unsigned int)ip->ihl,((unsigned int)(ip->ihl))*4);
        printf("\t|-Type Of Service : %d\n",(unsigned int)ip->tos);
        printf("\t|-Total Length : %d Bytes\n",ntohs(ip->tot_len));
        printf("\t|-Identification : %d\n",ntohs(ip->id));
        printf("\t|-Time To Live : %d\n",(unsigned int)ip->ttl);
        printf("\t|-Protocol : %d\n",(unsigned int)ip->protocol);
        printf("\t|-Header Checksum : %d\n",ntohs(ip->check));
        printf("\t|-Source IP : %d\n", ip->saddr);
        printf("\t|-Destination IP : %d\n",ip->daddr);
        return 1;
    }
    else{
        return 0;
    }
}    
} 



void send_ping(ping_ctx* ctx,int r_sock)
{ 
int  msg_count=0, i, addr_len, pkt_sent=1, 
    msg_received_count=0,on =1; 
struct sockaddr_ll remote_addr; 
struct ip *ip = NULL;
struct icmphdr* icmph = NULL;
unsigned  char* data = NULL;
struct ethhdr* eth = NULL;
struct sockaddr_ll r_addr; 
struct timespec time_start, time_end, tfs, tfe; 
long double rtt_msec=0, total_msec=0; 
struct timeval tv_out;

/*Set params*/ 
char *to = ctx->dstIP;
char *from = ctx->srcIP;
int   ping_sockfd = ctx->r_sock;
int ttl = ctx->ttl;
int tos =ctx->tos;

/*Timer Settings*/
tv_out.tv_sec = RECV_TIMEOUT; 
tv_out.tv_usec = 0; 
//clock_gettime(CLOCK_MONOTONIC, &tfs); 

//GET INTERFACE INDEX FOR INTERFACE enp0s3
/*struct ifreq ifr;
size_t if_name_len = strlen(if_name);
if(if_name_len<sizeof(ifr.ifr_name)){
     memcpy(ifr.ifr_name,if_name,if_name_len);
     ifr.ifr_name[if_name_len]=0;
}
else{
     die("Interface name is too long");
}
memset(&ifr,0,sizeof(ifr));
strncpy(ifr.ifr_name,enp0s3,IFNAMSIZ-1);
int fd = socket(AF_UNIX,SOCK_DGRAM,0);
if(fd==-1)
     printf("Error opening socket");
if(ioctl(fd,SIOCGIFINDEX,&ifr)==-1){
     printf("Error getting index name");
}*/
//int ifindex = ifr.ifr_ifindex;
int ifindex = if_nametoindex("enp2s0f0");
printf("The interface number is : %d \n",ifindex);

remote_addr.sll_ifindex = ifindex;
remote_addr.sll_halen = ETH_ALEN;
remote_addr.sll_addr[0] = 0xc0;
remote_addr.sll_addr[1] = 0x42;
remote_addr.sll_addr[2] = 0xd0;
remote_addr.sll_addr[3] = 0x6e;
remote_addr.sll_addr[4] = 0xe1;
remote_addr.sll_addr[5] = 0xd9;

while(pingloop) 
{ 
    pkt_sent=1; 
    memset(&g_pckt, 0, sizeof(g_pckt));

    /*ETHERNET Header*/
    eth = (struct ethhdr *)&g_pckt;
    fill_eth(eth);

    /*IP Header*/        
    ip = (struct ip *)(eth + 1); 
    fill_ip_h(ip, ctx);

    /*ICMP Header*/
    icmph = (struct icmphdr*)(ip + 1);
    fill_icmp_h(icmph, &msg_count); 

    /*Data*/
    data = (unsigned char *)(icmph + 1);
    fill_data(data);

    /*ICMP Checksum*/
    icmph->checksum = checksum(icmph, PING_PKT_S);

    usleep(PING_SLEEP_RATE); 

    /*TX*/ 
    clock_gettime(CLOCK_MONOTONIC, &time_start); 
    if ( sendto(ping_sockfd, &g_pckt, sizeof(g_pckt), 0, 
                (struct sockaddr*) &remote_addr, 
                sizeof(remote_addr)) <= 0) 
    { 
        printf("\nPacket Sending Failed!\n"); 
        pkt_sent=0; 
    } 

    /*RX*/ 
    /*addr_len=sizeof(r_addr); 

    if ( recvfrom(ping_sockfd, icmph, PING_PKT_S, 0, 
                (struct sockaddr*)&r_addr, &addr_len) <= 0 
            && msg_count>1) 
    { 
        printf("\nPacket receive failed!\n"); 
    } */

    else
    { 
        /*clock_gettime(CLOCK_MONOTONIC, &time_end); 

        double timeElapsed = ((double)(time_end.tv_nsec - 
                    time_start.tv_nsec))/1000000.0; 
        rtt_msec = (time_end.tv_sec- 
                time_start.tv_sec) * 1000.0 
            + timeElapsed;*/ 

        // if packet was not sent, don't receive 
        if(pkt_sent) 
        { 
            msg_count++;
            if(recv_ping(r_sock)){
                msg_received_count++;
            }

            //printf("Total Messages received : %d\n", msg_received_count ); 
            /*if(!(g_pckt.hdr.type ==69 && g_pckt.hdr.code==0)) 
            { 
                printf("Error..Packet received with ICMP" 
                        "type %d code %d\n", 
                        g_pckt.hdr.type, g_pckt.hdr.code); 
            } 
            else
            { 
                printf("%d bytes Received reply from %s: icmp_seq=:%d ttl=%d time=%Lf ms\n",PING_PKT_S, ctx->dstIP, msg_count, ctx->ttl, rtt_msec); 
                msg_received_count++;
            } */
        } 
    }    
} 
/*clock_gettime(CLOCK_MONOTONIC, &tfe); 
double timeElapsed = ((double)(tfe.tv_nsec - 
            tfs.tv_nsec))/1000000.0; 

total_msec = (tfe.tv_sec-tfs.tv_sec)*1000.0+ 
    timeElapsed ;*/

printf("\n%d packets sent, %d packets received\n\n", 
        msg_count, msg_received_count); 
} 

int main(int argc, char *argv[]) 
{ 
ping_ctx ctx = {0};

if(argc!=3) 
{ 
    printf("sudo ./myping 10.117.157.6 10.39.51.117\n"); 
    return 0; 
} 

signal(SIGINT, intHandler); 
ctx.r_sock = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); 
int r_sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)); 
if(r_sock <0) 
{ 
    printf("\nSocket file descriptor not received\n"); 
    return 0; 
} 
if(ctx.r_sock <0) 
{ 
    printf("%d",ctx.r_sock);
    printf("\nSocket file descriptor not received\n"); 
    return 0; 
} 

ctx.tos = 0;
ctx.ttl = 64;
strncpy(ctx.dstIP, argv[1],strlen(argv[1]));
strncpy(ctx.srcIP, argv[2],strlen(argv[2]));

send_ping(&ctx,r_sock); 

return 0; 
} 

此外,调用的 sendto api 返回值 0,表示仅发送了 0 个字节,这不应该是理想情况,因为它应该返回发送的字节数,该数应等于数据包大小.

Also the sendto api as called is returning a value of 0 indicating only 0 bytes have been sent which shouldn't be the ideal case as it is supposed to return the number of bytes sent which should be equal to the packet size.

我知道发布的代码很长,但我不确定是哪个部分导致了错误.所以我要发布整个代码.任何快速帮助将不胜感激.

I know the code posted is long but I was unsure which part is causing the error. So I'd to post the whole code. Any quick help will be appreciated.

Linux 发送者的 tcpdump 捕获如下所示

The tcpdump capture for the Linux sender is shown below

在此处输入图片描述

推荐答案

好的,我想通了.显然 AF_PACKET 总是假设数据包将被完全填满.完全我的意思是完全.我试图忽略内核的 IPv4 校验和来填充,虽然对于 AF_INET 是成功的,但对于 AF_PACKET 却不是这种情况.因此,AF_PACKET 必须填写校验和,这样做一切正常.

Okay so I figured it out. Apparently AF_PACKET always assumes that the packet will be completely filled. By completely I MEAN COMPLETELY. And I was trying to leave out the IPv4 checksum for the kernel to fill in which though was being successful for AF_INET but is not the case for AF_PACKET. So it was compulsory for AF_PACKET to fill the checksum and doing so everything got fine.

这篇关于为什么 echo ping 没有到达目的地?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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