linuxでC言語でpingコマンドを実現
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ファイル:
ping.cファイル:
実行結果:
# ./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
プログラム内の主な関数
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