linux ping源码
时间: 2023-10-03 22:09:58 浏览: 46
以下是 Linux 中 ping 命令的源代码(基于 Ubuntu 20.04 LTS 版本):
```
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#define PACKET_SIZE 4096
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int sockfd, datalen = 56;
int nsend = 0, nreceived = 0;
struct sockaddr_in dest_addr;
pid_t pid;
struct timeval tvrecv;
void statistics(int signo)
{
printf("\n%d packets transmitted, %d received , %%%d lost\n",
nsend, nreceived, (nsend - nreceived) / nsend * 100);
close(sockfd);
exit(1);
}
unsigned short cal_chksum(unsigned short *addr, int len)
{
int nleft = len;
int sum = 0;
unsigned short *w = addr;
unsigned short 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;
}
void send_packet(void)
{
nsend++;
struct icmp *icmp = (struct icmp *)sendpacket;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = nsend;
icmp->icmp_id = pid;
memset(icmp->icmp_data, 0xa5, datalen);
gettimeofday((struct timeval *)icmp->icmp_data, NULL);
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, datalen + 8);
sendto(sockfd, sendpacket, datalen + 8, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
}
void recv_packet(void)
{
int n;
extern int errno;
signal(SIGALRM, statistics);
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
fd_set readfd;
while (nreceived < nsend) {
FD_ZERO(&readfd);
FD_SET(sockfd, &readfd);
int ret = select(sockfd+1, &readfd, NULL, NULL, &timeout);
if (ret == -1) {
if (errno == EINTR) {
continue;
}
perror("select error");
return;
} else if (ret == 0) {
printf("request timeout\n");
continue;
}
if (FD_ISSET(sockfd, &readfd)) {
n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, NULL, NULL);
if (n < 0) {
if (errno == EINTR) {
continue;
}
perror("recvfrom error");
return;
}
gettimeofday(&tvrecv, NULL);
struct ip *ip = (struct ip *)recvpacket;
struct icmp *icmp = (struct icmp *)(recvpacket + (ip->ip_hl << 2));
int len = n - (ip->ip_hl << 2);
if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid) {
nreceived++;
printf("%d bytes from %s: icmp_seq=%u ttl=%d time=%.1f ms\n",
len, inet_ntoa(ip->ip_src), icmp->icmp_seq, ip->ip_ttl,
(tvrecv.tv_sec - ((struct timeval *)icmp->icmp_data)->tv_sec) * 1000.0 +
(tvrecv.tv_usec - ((struct timeval *)icmp->icmp_data)->tv_usec) / 1000.0);
} else {
printf("received icmp packet with type %d code %d\n", icmp->icmp_type, icmp->icmp_code);
}
}
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: ping <ip address>\n");
return 1;
}
struct hostent *host = gethostbyname(argv[1]);
if (host == NULL) {
perror("gethostbyname error");
return 1;
}
pid = getpid();
printf("PING %s (%s) %d bytes of data.\n", host->h_name, inet_ntoa(*(struct in_addr *)host->h_addr), datalen);
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) {
perror("socket error");
return 1;
}
setuid(getuid());
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
memcpy(&dest_addr.sin_addr, host->h_addr, host->h_length);
signal(SIGINT, statistics);
while (1) {
send_packet();
recv_packet();
sleep(1);
}
return 0;
}
```
这段代码实现了 ping 命令的功能。它使用 ICMP 协议发送和接收数据包,计算往返时间并输出结果。其中 `send_packet()` 函数用于发送 ICMP 请求数据包,`recv_packet()` 函数用于接收 ICMP 响应数据包,`statistics()` 函数用于统计传输结果。