您的位置:首页 > 运维架构 > Linux

UNP1:linux下实现ping程序

2011-09-03 15:11 543 查看
UNP1里面给出了一个ping程序的实现,里面包含了ipv4和ipv6两个版本。

经过学习,对里面的代码做了一点点小得修改(还原了基本的API),再加了一点注释,测试可以通过。

经过手敲了这段代码,收获还是很大的。对raw socket的编程有了基本的概念,同时也对icmp包和ip包有了更深入的了解。

修改后的代码如下,总共分为三个文件:

ping.h

#include<stdio.h>
#include<time.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>
#include<netdb.h>
#include<string.h>
#include<strings.h>
#include<netinet/in.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in_systm.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#include<signal.h>
#define BUFSIZE 1500

char sendbuf[BUFSIZE];

int datalen;
char *host;
int nsent;
pid_t pid;
int sockfd;
int verbose;

void proc_v4(char *,ssize_t,struct msghdr*,struct timeval *);
void send_v4(void);
void readloop(void);
void sig_alarm(int);
void tv_sub(struct timeval *,struct timeval *);

struct proto{
void (*fproc)(char *,ssize_t,struct msghdr *,struct timeval *);
void (*fsend)(void);
void (*finit)(void);
struct sockaddr *sasend;
struct sockaddr *sarecv;
socklen_t salen;
int icmpproto;
}*pr;


main.c:获取目标ip

#include"ping.h"
struct proto proto_v4={proc_v4,send_v4,NULL,NULL,NULL,0,IPPROTO_ICMP};

int datalen=56;
struct addrinfo *host_serv(const char *host,const char *serv,int family,int socktype)
{
int n;
struct addrinfo hints,*res;

bzero(&hints,sizeof(hints));
hints.ai_flags=AI_CANONNAME;
hints.ai_family=family;
hints.ai_socktype=socktype;
if((n=getaddrinfo(host,serv,&hints,&res))!=0){
return NULL;
}
return (res);
}

int main(int argc,char *argv[])
{
int c;
struct addrinfo *ai;
char h[20]={0};
opterr=0;
while((c=getopt(argc,argv,"v"))!=-1){
switch(c){
case 'v':
verbose++;
break;
case '?':
printf("unrecognized option: %c\n",c);
return 0;
}

}
if(optind!=argc-1)
printf("usage: ping [-v] <hostname>\n");
host=argv[optind];
pid=getpid() & 0xffff;
signal(SIGALRM,sig_alarm);
ai=host_serv(host,NULL,0,0);
inet_ntop(AF_INET,&((struct sockaddr_in*)(ai->ai_addr))->sin_addr,h,sizeof(h));
printf("PING %s (%s):%d data bytes\n",
ai->ai_canonname?ai->ai_canonname:h,h,datalen);

if(ai->ai_family==AF_INET){
pr=&proto_v4;
}else{
printf("unknown address family %d\n",ai->ai_family);
}

pr->sasend=ai->ai_addr;
pr->sarecv=(struct sockaddr*)calloc(1,ai->ai_addrlen);
pr->salen=ai->ai_addrlen;
readloop();
exit(0);
}


readloop.c:发送和接收icmp包

#include"ping.h"

//get rtt
void tv_sub(
struct timeval *out,    //time,tv_sev is microseconds
struct timeval *in)
{
if((out->tv_usec-=in->tv_usec)<0){
--out->tv_sec;
out->tv_sec+=1000000;
}
out->tv_sec-=in->tv_sec;
}

void proc_v4(char *ptr,ssize_t len,struct msghdr *msg,struct timeval * tvrecv)
{
int hlen1,icmplen;
char host[20];
double rtt;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;

ip=(struct ip*)ptr;
hlen1=ip->ip_hl<<2;//get ipdatagram length,include option
if(ip->ip_p!=IPPROTO_ICMP){
return;
}
icmp=(struct icmp*)(ptr+hlen1);
if((icmplen=len-hlen1)<8)
return;
if(icmp->icmp_type==ICMP_ECHOREPLY){
if(icmp->icmp_id!=pid)
return;
if(icmplen<16)
return;
tvsend=(struct timeval*)icmp->icmp_data;
tv_sub(tvrecv,tvsend);
rtt=tvrecv->tv_sec*1000.0+tvrecv->tv_usec/1000.0;
printf("%d bytes from %s:seq=%u,ttl=%d,rtt=%.3f ms\n",icmplen,inet_ntop(AF_INET,&((struct sockaddr_in*)(pr->sarecv))->sin_addr,host,sizeof(host)),icmp->icmp_seq,ip->ip_ttl,rtt);
}
}

//call send data func
void sig_alarm(int signo)
{
(*pr->fsend)();
alarm(1);
return;
}

//create checksum
uint16_t in_cksum(uint16_t *addr,int len)
{
int nleft=len;
uint32_t sum=0;
uint16_t *w=addr;
uint16_t 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;
}

//send icmp data
void send_v4(void){
int len;
struct icmp *icmp;

//init icmp datagram
icmp=(struct icmp*)sendbuf;
icmp->icmp_type=ICMP_ECHO;  //type
icmp->icmp_code=0;          //code
icmp->icmp_id=pid;
icmp->icmp_seq=nsent++;
gettimeofday((struct timeval *)icmp->icmp_data,NULL);//get send time
len=8+datalen;
icmp->icmp_cksum=0;
icmp->icmp_cksum=in_cksum((u_short *)icmp,len);

sendto(sockfd,sendbuf,len,0,pr->sasend,pr->salen);//send data
}

void readloop(void){
int i=0;
int size;
char recvbuf[BUFSIZE];  //get the response
char controlbuf[BUFSIZE];
struct msghdr msg;
struct iovec iov;       //send data
ssize_t n;
struct timeval tval;

sockfd=socket(pr->sasend->sa_family,SOCK_RAW,pr->icmpproto);

//set effective uid to real uid
setuid(getuid());
if(pr->finit)
(*pr->finit)();

size=60*1024;
setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size));//set recvbuf size

sig_alarm(SIGALRM);//cal fun

//init buffer
iov.iov_base=recvbuf;   //set recvbuf
iov.iov_len=sizeof(recvbuf);//recvbuf len
msg.msg_name=pr->sarecv;//sockaddr
msg.msg_iov=&iov;
msg.msg_iovlen=1;
msg.msg_control=controlbuf;

//loop ping
for(;;){
msg.msg_namelen=pr->salen;
msg.msg_controllen=sizeof(controlbuf);
n=recvmsg(sockfd,&msg,0);   //receive data
if(n<0){
if(errno=EINTR)
continue;
else
printf("recvmsg error\n");
}
gettimeofday(&tval,NULL);//receive time
(*pr->fproc)(recvbuf,n,&msg,&tval);//handle receive data
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: