用raw socket实现一个ICMP_ECHO的功能,出错了!

zhoufanking 2008-08-27 11:17:02
下面这段代码是发送一个ICMP_ECHO然后等待对方的ICMP_REPLY,我在本机测试时可以ping通,能接收到ICMP_REPLY,但ping其他主机时,就会一直死等在recvfrom上,请大家帮忙看看是什么问题阿!另外我想问问,在自己构造ip头,icmp头时哪些字段需要进行字节顺序转换?
/*                 main.c                         */
#include "errexit.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <linux/icmp.h>
#include <linux/ip.h>
#include <unistd.h>

#define BUFFLENGTH 4096

void Usage()__attribute__((noreturn));
unsigned short in_cksum(unsigned short *addr, int len);

char *ProgName = "rping";
char *myip = "192.168.1.3";


int main( int argc, char * argv[])
{
if( argc != 2 )
Usage();

struct sockaddr_in to, from;
char *buffer[BUFFLENGTH];
char *recvbuffer[BUFFLENGTH];
struct icmphdr *icmphd;
struct iphdr *iphd;
int res;
int tosock;
int fromlen = sizeof(struct sockaddr);
int one = 1;
int *ptr_one = &one;

to.sin_family = AF_INET;
res = inet_aton(argv[1],&to.sin_addr);
if( res < 0 )
errexit("invalid address!\n%s\n",strerror(errno));

memset(buffer,0x00,BUFFLENGTH);
memset(recvbuffer,0x00,BUFFLENGTH);

/*
* create a socket to send out the icmp echo message
*/
tosock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if(tosock<0)
errexit("create socket failed!\n%s\n",strerror(errno));

res = setsockopt(tosock, IPPROTO_IP, IP_HDRINCL,ptr_one, sizeof(one));
if(res < 0)
errexit("setsockopt failed!\n%s\n",strerror(errno));
/*
* construct the ip header
*/
iphd = ( struct iphdr *)buffer;
iphd->version = 4;
iphd->ihl = 5;
iphd->tos = 0;
iphd->tot_len = 84;
iphd->id = getpid() & 0xffff;
iphd->frag_off =0;
iphd->ttl = 32;
iphd->protocol =IPPROTO_ICMP;
iphd->check = 0;
iphd->saddr = inet_addr(myip);
iphd->daddr = to.sin_addr.s_addr;

/*
* construct the icmp header
*/
icmphd = (struct icmphdr *)(iphd+1);
icmphd->type = ICMP_ECHO;
icmphd->code = 5;
icmphd->checksum = in_cksum((unsigned short *)icmphd,64);
icmphd->un.echo.id = (short)(getpid()&0xffff);
icmphd->un.echo.sequence = 1;

while(1)
{
res = sendto(tosock,(void *)buffer,84,0,(struct sockaddr*)&to,sizeof(struct sockaddr));
if(res == -1)
errexit("rping error occured!\n%s\n",strerror(errno));
else if(res == 84)
break;
else if (errno == EINTR)
continue;
}

printf("done,%d bytes sent.\n",res);

char *addr = (char *)&(iphd->saddr);
printf("src ip address is :%d.%d.%d.%d\n",
addr[0]&0xff,addr[1]&0xff,addr[2]&0xff,addr[3]&0xff);

while(1)
{
res = recvfrom(tosock,recvbuffer,BUFFLENGTH,0,(struct sockaddr*)&from,(socklen_t *)&fromlen);
if(res == -1)
errexit("rping recv failed!\n%s\n",strerror(errno));
else if( errno == EINTR)
continue;

iphd = (struct iphdr *)recvbuffer;

char *saddr = (char *)&(iphd->saddr);

icmphd = (struct icmphdr*)(iphd+1);
if(icmphd->type == ICMP_ECHO)
continue;
if(icmphd->type == ICMP_ECHOREPLY)
{
printf("ping %d bytes received,seq = %d,code = %d\n",
res,
icmphd->un.echo.sequence & 0xffff,
icmphd->code & 0xff);
printf("src ip address is :%d.%d.%d.%d\n",
saddr[0]&0xff,saddr[1]&0xff,saddr[2]&0xff,saddr[3]&0xff);
break;

}
}
return 0;
}

void Usage()
{
printf("%s dstIPaddress\n",ProgName);
exit(-1);
}

unsigned short in_cksum(unsigned short *addr, int len)
{
register int nleft = len;
register unsigned short *w = addr;
register unsigned short answer;
register int sum = 0;

while(nleft > 1)
{
sum+=*w++;
nleft -= 2;
}
/* mop up and odd byte, if necessary */
if(nleft == 1)
{
unsigned short u = 0;

*(unsigned short *)(&u) = *(unsigned short *)w;
sum +=u;
}

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

}
/* errexit.h */
#ifndef ERREXIT_H
#define ERREXIT_H

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

extern void errexit(const char *fmt,...)__attribute__((format(printf,1,2)));

#endif
/* errexit.c */
#include "errexit.h"

void errexit(const char * fmt,...){
va_list vp;
va_start(vp,fmt);
vfprintf(stderr,fmt,vp);
va_end(vp);

exit(-1);
}

...全文
563 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
blackbillow 2008-08-28
  • 打赏
  • 举报
回复

/*
* construct the icmp header
*/
icmphd = (struct icmphdr *)(iphd+1);
icmphd->type = ICMP_ECHO;
icmphd->code = 5;
icmphd->checksum = in_cksum((unsigned short *)icmphd,64);
icmphd->un.echo.id = (short)(getpid()&0xffff);
icmphd->un.echo.sequence = 1;


这里有两个错误:
1 icmphd->code = 5; 这里应该为0,echo request类型只有code 0

2 应该在最后计算checksum
正确的应该是这样:

/*
* construct the icmp header
*/
icmphd = (struct icmphdr *)(iphd+1);
icmphd->type = ICMP_ECHO;
icmphd->code = 0;
icmphd->un.echo.id = (short)(getpid()&0xffff);
icmphd->un.echo.sequence = 1;
icmphd->checksum = in_cksum((unsigned short *)icmphd,64);


还有
char *buffer[BUFFLENGTH];
char *recvbuffer[BUFFLENGTH];
应该是
char buffer[BUFFLENGTH];
char recvbuffer[BUFFLENGTH];
才对把
zhoufanking 2008-08-28
  • 打赏
  • 举报
回复
我把用自己写的一个程序将抓出来的包比较了一下,我发出的包,和系统自带的ping发出的包好像没什么区别阿。我等下再对比一下。感谢楼上,我试完了再把结果贴上。
快乐田伯光 2008-08-28
  • 打赏
  • 举报
回复
那就比较一下系统自带工具发的ping包跟你发的ping包有什么不同
zhoufanking 2008-08-28
  • 打赏
  • 举报
回复
我用系统自带的ping能ping通对方,但用我的就不行。我在对发机子上用tcpdump也抓到了我发的ECHO,为什么它不给我回呢?
快乐田伯光 2008-08-28
  • 打赏
  • 举报
回复
你直接用系统自带的ping工具测试一下是你程序的问题还是你程序的问题先
快乐田伯光 2008-08-28
  • 打赏
  • 举报
回复
你测试的另外一台机器是否允许ping?
zhoufanking 2008-08-28
  • 打赏
  • 举报
回复
终于通拉!感谢两位!尤其是blackbillow!
定义buffer时笔误了,checksum放错了地方!顺便再问一下,这两个错误未更正之前为什么也能ping通自己呢?
另:icmp->code不会被检查到,所以任何值都无所谓,这个字段可以用来做些非法的事:)
zhoufanking 2008-08-27
  • 打赏
  • 举报
回复
我用TCPDUMP看了一下,对方收到了我发送的echo request但没有发出echo reply.这是怎么回事呢,怎么解决阿?

23,126

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧