linuxでC言語でpingコマンドを実現

12122 ワード

C言語を用いてアナログ常用ネットワークコマンドpingコマンドを記述し、linux元のソケットとICMPプロトコルに基づくpingプログラムを実現する.このプログラムは、ホストまたはルータが正常に動作しているかどうかを検出するために使用できます.
プログラム内の主な関数
void alarm_handler(int);/*SIGALRMハンドラ*/
void int_handler(int);/*SIGINTプロセッサ*/
void set_sighandler();/*信号ハンドラの設定*/
void send_ping();/*pingメッセージを送信*/
void recv_reply();/*ping応答受信*/
u16 checksum(u8 *buf, int len);/*計算チェックサム*/
int handle_pkt();/*ICMP応答メッセージ処理*/
void get_statistics(int, int);/*統計pingコマンドの検出結果*/
void bail(const char *);/*エラーレポート*/
ping.hファイル:
/********************************************************************************
 *      Copyright:  (C) 2016 Yang Zheng<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  ping.h
 *    Description:  This head file 
 *
 *        Version:  1.0.0(01/22/2016~)
 *         Author:  Yang Zheng <[email protected]>
 *      ChangeLog:  1, Release initial version on "01/22/2016 04:51:41 PM"
 *                 
 ********************************************************************************/</span>


#define ICMP_ECHOREPLY 0 /* Echo  */  
#define ICMP_ECHO   /*Echo  */  
  
#define BUFSIZE 1500    /*       */  
#define DEFAULT_LEN 56  /**ping        /  
  
/*      */  
typedef unsigned char u8;  
typedef unsigned short u16;  
typedef unsigned int u32;  
  
/*ICMP    */  
struct icmphdr {  
    u8 type;     /*      */  
    u8 code;    /*      */  
    u16 checksum;   /*    */  
    union{  
        struct{  
        u16 id;  
        u16 sequence;  
    }echo;  
    u32 gateway;  
    struct{  
        u16 unsed;  
        u16 mtu;  
    }frag; /*pmtu  */  
    }un;  
  /*ICMP     */  
    u8 data[0];  
#define icmp_id un.echo.id  
#define icmp_seq un.echo.sequence  
};  
#define ICMP_HSIZE sizeof(struct icmphdr)  
/*    IP       */  
struct iphdr {  
    u8 hlen:4, ver:4;   /*  4     , IP    IPV4*/  
    u8 tos;				/*8     TOS*/  
    u16 tot_len;		/*16    */  
    u16 id;				/*16    */  
    u16 frag_off;		/*3    */  
    u8 ttl;				/*8     */  
    u8 protocol;		/*8   */  
    u16 check;			/*16 IP     */  
    u32 saddr;			/*32  IP  */  
    u32 daddr;			/*32   IP  */  
};  
  
char *hostname;				/* ping    */  
int datalen = DEFAULT_LEN;  /*ICMP         */  
char sendbuf[BUFSIZE];      /*       */   
char recvbuf[BUFSIZE];      /*       */  
int nsent;					/*   ICMP    */  
int nrecv;					/*   ICMP    */  
pid_t pid;					/*ping     PID*/  
struct timeval recvtime;    /*  ICMP      */  
int sockfd;					/*          */  
struct sockaddr_in dest;    /* ping   IP*/  
struct sockaddr_in from;    /*  ping       IP*/  
struct sigaction act_alarm;  
struct sigaction act_int;  
  
/*    */  
void alarm_handler(int);		/*SIGALRM    */  
void int_handler(int);			/*SIGINT    */  
void set_sighandler();			/*        */  
void send_ping();				/*  ping  */  
void recv_reply();				/*  ping  */  
u16 checksum(u8 *buf, int len); /*     */  
int handle_pkt();				/*ICMP      */  
void get_statistics(int, int);  /*  ping       */  
void bail(const char *);		/*    */

ping.cファイル:
/*********************************************************************************
 *      Copyright:  (C) 2016 Yang Zheng<[email protected]>  
 *                  All rights reserved.
 *
 *       Filename:  ping.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(01/22/2016~)
 *         Author:  Yang Zheng <[email protected]>
 *      ChangeLog:  1, Release initial version on "01/22/2016 04:51:12 PM"
 *                 
 ********************************************************************************/</span>
#include<stdio.h>  
#include<stdlib.h>  
#include<sys/time.h>  /* Linux          */  
#include<unistd.h>    /*  POSIX     unix             ,     UNIX         ,  read  、write   getpid  */  
#include<string.h>  
#include<sys/socket.h>    /*    socket    */  
#include<sys/types.h>  
#include<netdb.h> /*           ,    , ,  。  gethostbyname() */  
#include<errno.h> /*sys/types.h             */  
#include<arpa/inet.h> /*inet_ntoa() inet_addr()     ,    arpa/inet.h*/  
#include<signal.h>    /*         */  
#include<netinet/in.h>    /*      */  
  
#include"ping.h"  
#define IP_HSIZE sizeof(struct iphdr)   /*  IP_HSIZE ip    */  
#define IPVERSION  4   /*  IPVERSION 4,   ipv4*/  

/*           ,     ,    ,     1 */  
struct itimerval val_alarm = {
	.it_interval.tv_sec = 1,      
	.it_interval.tv_usec = 0,  
	.it_value.tv_sec = 0,  
	.it_value.tv_usec = 1  
};  

/*argc               ,argv            ,             */
int main(int argc,char **argv)  
{  
	struct hostent		*host; /*      include<netdb.h>*/   
    int					on = 1;  
  
    if( argc < 2)/*         */ 
	{       
		printf("Usage: %s hostname
",argv[0]); exit(1); } /*gethostbyname() ,*/ //if((host = getaddrinfo(argv[1])) == NULL) if((host = gethostbyname(argv[1])) == NULL) { printf("usage:%s hostname/IP address
", argv[0]); exit(1); } hostname = argv[1]; /* */ memset(&dest,0,sizeof dest); /* dest sizeof(dest) 0 s, , */ dest.sin_family=PF_INET; /*PF_INET IPV4,internet , <netinet/in.h> , */ dest.sin_port=ntohs(0); /* ,ntohs() 。*/ dest.sin_addr=*(struct in_addr *)host->h_addr_list[0];/*host->h_addr_list[0] . IP , */ /*PF_INEI ,SOCK_RAW ,IPPROTO_ICMP , socket 。 */ if((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("RAW socket created error"); exit(1); } /* ,sockfd ,IPPROTO_IP IP , IP_HDRINCL , ,sizeof(on) */ setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); /*getuid() ID,setuid() root root 。*/ setuid(getuid()); pid = getpid(); /*getpid */ set_sighandler();/* */ printf("Ping %s(%s): %d bytes data in ICMP packets.
", argv[1], inet_ntoa(dest.sin_addr), datalen); if((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -1) /* */ { bail("setitimer fails."); } recv_reply(); /* ping */ return 0; } /* ping */ void send_ping(void) { struct iphdr *ip_hdr; /*iphdr IP */ struct icmphdr *icmp_hdr; /*icmphdr ICMP */ int len; int len1; /*ip */ ip_hdr=(struct iphdr *)sendbuf; /* */ ip_hdr->hlen=sizeof(struct iphdr)>>2; /* */ ip_hdr->ver=IPVERSION; /* */ ip_hdr->tos=0; /* */ ip_hdr->tot_len=IP_HSIZE+ICMP_HSIZE+datalen; /* */ ip_hdr->id=0; /* */ ip_hdr->frag_off=0; /* flag 0*/ ip_hdr->protocol=IPPROTO_ICMP;/* ICMP */ ip_hdr->ttl=255; /* */ ip_hdr->daddr=dest.sin_addr.s_addr; /* */ len1=ip_hdr->hlen<<2; /*ip */ /*ICMP */ icmp_hdr=(struct icmphdr *)(sendbuf+len1); /* */ icmp_hdr->type=8; /* ICMP type*/ icmp_hdr->code=0; /* code*/ icmp_hdr->icmp_id=pid; /* icmp_id*/ icmp_hdr->icmp_seq=nsent++; /* ICMP icmp */ memset(icmp_hdr->data,0xff,datalen); /* datalen datalen 0xff icmp_hdr-dat*/ gettimeofday((struct timeval *)icmp_hdr->data,NULL); /* */ len=ip_hdr->tot_len; /* len */ icmp_hdr->checksum=0; /* */ icmp_hdr->checksum=checksum((u8 *)icmp_hdr,len); /* */ sendto(sockfd,sendbuf,len,0,(struct sockaddr *)&dest,sizeof (dest)); /* socket */ } /* ping */ void recv_reply() { int n; int len; int errno; n = 0; nrecv = 0; len = sizeof(from); /* ping IP*/ while(nrecv < 4) { /* socket , , 0.*/ if((n=recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&from, &len))<0) { if(errno==EINTR) /*EINTR */ continue; bail("recvfrom error"); } gettimeofday(&recvtime, NULL); /* */ if(handle_pkt()) /* ICMP */ continue; nrecv++; } get_statistics(nsent, nrecv); /* ping */ } /* */ u16 checksum(u8 *buf,int len) { u32 sum = 0; u16 *cbuf; cbuf = (u16 *)buf; while(len > 1) { sum += *cbuf++; len -= 2; } if(len) { sum += *(u8 *)cbuf; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return ~sum; } /*ICMP */ int handle_pkt() { struct iphdr *ip; struct icmphdr *icmp; int ip_hlen; u16 ip_datalen; /*ip */ double rtt; /* */ struct timeval *sendtime; ip = (struct iphdr *)recvbuf; ip_hlen = ip->hlen << 2; ip_datalen = ntohs(ip->tot_len) - ip_hlen; icmp = (struct icmphdr *)(recvbuf + ip_hlen); if(checksum((u8 *)icmp, ip_datalen)) /* */ return -1; if(icmp->icmp_id != pid) return -1; sendtime = (struct timeval *)icmp->data; /* */ rtt = ((&recvtime)->tv_sec - sendtime->tv_sec) * 1000 + ((&recvtime)->tv_usec - sendtime->tv_usec)/1000.0; /* */ /* */ printf("%d bytes from %s:icmp_seq=%u ttl=%d rtt=%.3f ms
", \ ip_datalen, /*IP */ inet_ntoa(from.sin_addr), /* ip */ icmp->icmp_seq, /*icmp */ ip->ttl, /* */ rtt); /* */ return 0; } /* */ void set_sighandler() { act_alarm.sa_handler = alarm_handler; /*sigaction() signum 。 signum , &act ,NULL 。 。*/ if(sigaction(SIGALRM, &act_alarm, NULL) == -1) { bail("SIGALRM handler setting fails."); } act_int.sa_handler = int_handler; if(sigaction(SIGINT, &act_int, NULL) == -1) { bail("SIGALRM handler setting fails."); } } /* ping */ void get_statistics(int nsent,int nrecv) { printf("--- %s ping statistics ---
",inet_ntoa(dest.sin_addr)); /* “.” 。*/ printf("%d packets transmitted, %d received, %0.0f%% ""packet loss
", \ nsent,nrecv,1.0*(nsent-nrecv)/nsent*100); } /* */ void bail(const char * on_what) { /*: ( ‘\0’)。 , , 0; EOR( , -1)。*/ fputs(strerror(errno),stderr); fputs(":",stderr); fputs(on_what,stderr); fputc('
',stderr); /* */ exit(1); } /*SIGINT( ) */ void int_handler(int sig) { get_statistics(nsent,nrecv); /* ping */ close(sockfd); /* */ exit(1); } /*SIGALRM( ) */ void alarm_handler(int signo) { send_ping(); /* ping */ }<strong> </strong>

実行結果:
# ./ping 192.168.1.1 Ping 192.168.1.1(192.168.1.1): 56 bytes data in ICMP packets. 64 bytes from 192.168.1.1:icmp_seq=0 ttl=64 rtt=113.510 ms 64 bytes from 192.168.1.1:icmp_seq=1 ttl=64 rtt=19.854 ms --- 192.168.1.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss   # ./ping baidu.com Ping baidu.com(220.181.57.217): 56 bytes data in ICMP packets. 64 bytes from 220.181.57.217:icmp_seq=0 ttl=50 rtt=44.679 ms 64 bytes from 220.181.57.217:icmp_seq=1 ttl=50 rtt=58.446 ms 64 bytes from 220.181.57.217:icmp_seq=2 ttl=50 rtt=47.933 ms 64 bytes from 220.181.57.217:icmp_seq=3 ttl=50 rtt=44.946 ms --- 220.181.57.217 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss