linuxping命令源代码
-
Linux平台下的ping命令的源代码可以在网络上找到并下载。以下是ping命令的基本代码实现:
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include#define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3struct timeval tvrecv;
struct sockaddr_in dest_addr;
struct sockaddr_in from;const char *icmp_type[] = {
“Echo Reply”,
“ICMP 1”,
“ICMP 2”,
“Destination Unreachable”,
“Source Quench”,
“Redirect”,
“Alternate Host Address”,
“Echo Request”,
“Router Advertisement”,
“Router Solicitation”,
“Time Exceeded”,
“Parameter Problem”,
“Timestamp Request”,
“Timestamp Reply”,
“Information Request”,
“Information Reply”,
“Address Mask Request”,
“Address Mask Reply”
};typedef struct {
struct timeval sendtime;
char msg[PACKET_SIZE];
} Packet;unsigned short cal_chksum(unsigned short *addr, int len);
int pack(int pack_no, Packet *packet);
int unpack(char *buf, int len);
void tv_sub(struct timeval *out, struct timeval *in);int main(int argc, char *argv[]) {
int sockfd, i, n;
char buf[PACKET_SIZE];
Packet packet;
struct hostent *host;
int packetsize;
int datalen = sizeof(Packet);memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;if (argc != 2) {
printf(“Usage: %s hostname/IP address\n”, argv[0]);
exit(1);
}if (inet_addr(argv[1]) == INADDR_NONE) {
host = gethostbyname(argv[1]);
if (host == NULL) {
printf(“IP address error\n”);
exit(1);
}
memcpy((char *)&dest_addr.sin_addr.s_addr, host->h_addr, host->h_length);
} else {
dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
}printf(“PING %s (%s): %d bytes of data.\n”, argv[1], inet_ntoa(dest_addr.sin_addr), datalen);
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) { perror("socket error"); exit(1); } setuid(getuid()); for (i = 0; i < MAX_NO_PACKETS; i++) { n = pack(i, &packet); if (n < 0) { printf("pack error\n"); continue; } sendto(sockfd, packet.msg, packetsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); memset(buf, 0, sizeof(buf)); n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&from, (socklen_t *)&packetsize); if (n < 0) { perror("recvfrom error"); continue; } n = unpack(buf, n); if (n == -1) { printf("unpack error\n"); } usleep(1000000); } return 0;}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;
}int pack(int pack_no, Packet *packet) {
int i, n;
struct icmp *icmp;
struct timeval *tval;icmp = (struct icmp *)packet->msg;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = pack_no;
icmp->icmp_id = getpid();tval = (struct timeval *)icmp->icmp_data;
gettimeofday(tval, NULL);n = sizeof(struct icmp) + sizeof(struct timeval);
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, n);
return n;
}int unpack(char *buf, int len) {
int i, iphdrlen;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;ip = (struct ip *)buf;
iphdrlen = ip->ip_hl << 2; icmp = (struct icmp *)(buf + iphdrlen); len -= iphdrlen; if (len < 8) { printf("ICMP packets\'s length is less than 8\n"); return -1; } if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == getpid())) {
tvsend = (struct timeval *)icmp->icmp_data;
tv_sub(&tvrecv, tvsend);
printf(“%d bytes from %s: icmp_seq=%u ttl=%d time=%.2fms\n”,
len,
inet_ntoa(from.sin_addr),
icmp->icmp_seq,
ip->ip_ttl,
tvrecv.tv_sec * 1000 + tvrecv.tv_usec / 1000.0);
} else {
printf(“Error: pack %d failed. Expected ICMP_ECHOREPLY, received ICMP %d\n”,
icmp->icmp_seq,
icmp->icmp_type);
return -1;
}return 0;
}void tv_sub(struct timeval *out, struct timeval *in) {
if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
“`这是一个简化版的ping命令的源代码,使用Socket和ICMP协议实现。该代码逐个编码了ping命令的各个步骤,包括构建ICMP数据包、发送数据包、接收响应包、解析响应包等等。可以通过编译该代码来生成一个可执行文件,并在终端中运行,以执行ping命令。
2年前 -
Linux中的”ping”命令用于测试与目标主机的网络连接。它发送ICMP回显请求到目标主机,并等待接收到ICMP回显应答。以下是”ping”命令的源代码示例:
“`c
/*
* ping.c
*
* Simple implementation of the ping command in Linux.
*
*/#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include#define PACKET_SIZE 64
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3int sockfd, datalen = 56;
int nsend = 0, nreceived = 0;
struct sockaddr_in dest_addr;
struct sockaddr_in from;
pid_t pid;
struct timeval tvrecv;char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];// 计算校验和
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;
}// 如果还剩下1个字节,将其视为一个16位值的高字节
if (nleft == 1)
{
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}// 设置ICMP报文
int pack(int pack_no)
{
int i, packsize;
struct icmp *icmp;
struct timeval *tval;icmp = (struct icmp *)sendpacket;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = pack_no;
icmp->icmp_id = pid;
packsize = 8 + datalen;
tval = (struct timeval *)icmp->icmp_data;
gettimeofday(tval, NULL); // 记录发送时间
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, packsize); // 计算校验和
return packsize;
}// 发送ICMP报文
void send_packet()
{
int packetsize;
while (nsend < MAX_NO_PACKETS) { nsend++; packetsize = pack(nsend); // 设置ICMP报文 // 发送ICMP报文 if (sendto(sockfd, sendpacket, packetsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) { perror("sendto error"); continue; } sleep(1); // 间隔1秒钟 }}// 接收ICMP回显应答void recv_packet(){ int n, fromlen; extern int errno; signal(SIGALRM, recv_packet); // 设置超时时间 struct timeval timeout = { 5, // seconds 0 // microseconds }; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); fromlen = sizeof(from); while (nreceived < nsend) { errno = 0; // 接收ICMP应答 if ((n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, &fromlen)) < 0) { if (errno == EINTR) // 被信号中断,继续等待 continue; perror("recvfrom error"); continue; } gettimeofday(&tvrecv, NULL); // 记录接收时间 if (icmp->icmp_type == ICMP_ECHOREPLY &&
icmp->icmp_id == pid)
{
nreceived++;
printf(“%d bytes from %s: icmp_seq=%u ttl=%d time=%.2f ms\n”,
n, inet_ntoa(from.sin_addr), icmp->icmp_seq,
ip->ip_ttl, rtt);
}
else
continue;
}
}// 结束进程
void finish(int signo)
{
close(sockfd);
exit(0);
}int main(int argc, char *argv[])
{
struct hostent *host;
struct protoent *protocol;
unsigned long inaddr = 0l;
int waittime = MAX_WAIT_TIME;if (argc < 2) { printf("usage: ping ip/hostname\n"); exit(1); } // 获取ICMP协议号 if ((protocol = getprotobyname("icmp")) == NULL) { perror("getprotobyname"); exit(1); } // 创建原始套接字 if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0) { perror("socket error"); exit(1); } // 设置接收超时时间 setuid(getuid()); setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &waittime, sizeof(waittime)); // 处理中断信号 signal(SIGINT, finish); // 获取目标主机IP地址 if ((inaddr = inet_addr(argv[1])) == INADDR_NONE) { if ((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname error"); exit(1); } memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
}
else
{
memcpy((char *)&dest_addr.sin_addr, &inaddr, sizeof(inaddr));
}// 获取进程ID作为ICMP标识
pid = getpid();printf(“PING %s(%s): %d bytes data in ICMP packets.\n”, argv[1],
inet_ntoa(dest_addr.sin_addr), datalen);// 发送和接收ICMP报文
send_packet();
recv_packet();return 0;
}
“`这段代码展示了如何使用C语言编写一个简单的”ping”命令,用于测试与目标主机的网络连接。该代码使用了套接字编程来创建原始套接字,并通过发送和接收ICMP报文来实现ping功能。该代码使用了一些Linux系统库函数和数据结构,如netinet/ip_icmp.h、netinet/in.h、arpa/inet.h等。它首先创建一个原始套接字,并设置一些必要的参数,如接收超时时间。然后,它通过发送ICMP报文来测试网络连接,并等待接收ICMP回显应答。最后,它输出与目标主机的连接状态。
这段代码涵盖了”ping”命令的主要功能,但仍然有一些细节和错误处理可以改进。它只发送了固定数量的ICMP报文,并仅输出收到的回显应答。在实际使用中,可能需要更灵活的发送和接收ICMP报文的逻辑,并对收到的应答进行更详细的处理和分析。同时,它也没有处理所有可能的错误情况,如网络不可达、目标主机不可达等。因此,在实际使用中,可能需要根据需求进行修改和改进。
最后,需要注意的是,”ping”命令的实现可以因操作系统和网络环境而异。以上代码是一个基本的示例,可以作为参考进行修改和扩展。在实际使用中,请务必了解所使用的操作系统和网络环境的特定要求和限制,并进行相应的适配。
2年前 -
Linux系统中的ping命令用于检测网络连接和确定目标主机是否可达。下面是ping命令的源代码解析。
1. 引用头文件
ping命令的源代码文件为/usr/src/linux/net/ipv4/icmp.c。在这个文件的开头,需要引用一些头文件,其中包括:
“`c
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include
#include
“`2. 定义函数
在源代码中定义了许多函数,这些函数包括一些辅助函数和主要函数,用于执行ping检测的不同任务。
其中一些重要的函数包括:
– icmp_ping_init():用于初始化ICMP ping请求;
– icmp_echo():用于发送ICMP echo请求;
– ping_sendmsg():用于发送ping消息;
– ping_rcv():用于接收ping消息;
– ping_queue_rcv_skb():用于在ping队列中接收skb数据包;
– ping_queue_rcv():用于在ping队列中接收icmp数据包。3. 主要函数
源代码中的主要函数是icmp_echo()和ping_sendmsg(),它们是执行ping检测的核心函数。
icmp_echo()函数用于发送ICMP echo请求,代码如下:
“`c
static void icmp_echo(struct icmp_bxm *data)
{
struct sk_buff *skb;
struct flowi4 fl4;
struct net *net = sock_net(&ping_v4_sock);
struct user_msghdr umsg;
struct sockaddr_in *sin;
int err;memset(&umsg, 0, sizeof(umsg));
memset(&fl4, 0, sizeof(fl4));
skb = ip_local_out(net, data->ttl, GFP_KERNEL, &fl4, NULL);
if (IS_ERR(skb))
return;skb->sk = NULL;
skb->mark = 0;
skb->priority = 0;
skb->queue_mapping = dev_net(ping_dev) ->queue_mapping;ip_setup_cork(net, skb, &fl4);
err = ip_append_data(skb, icmp_out_count(net, IPPROTO_ICMP, len – sizeof(struct icmphdr)),
((struct sockaddr *)&data->dest.u.in),
data->flags & ICMP6_ECHOREPLY_FLAG ? MSG_MORE : 0, 0,
htons(IPPROTO_ICMP));if (err)
goto tx_error;lock_sock(&ping_v4_sock);
err = kernel_sendmsg(ping_v4_sock, &umsg, &fl4.saddr, 0, skb);
unlock_sock(&ping_v4_sock);if (err <= 0) goto tx_error; return;tx_error: icmp_out_errors(net, ICMP_MIB_OUTERRORS); kfree_skb(skb);}```ping_sendmsg()函数用于发送ping消息,代码如下:```cstatic ssize_t ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len){ struct inet_sock *inet = inet_sk(sk); struct rtable *rt; struct icmp_bxm icmp_param; int err = -EHOSTUNREACH; struct dst_entry *dst; if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_MORE))
return -EINVAL;if (atomic_read(&ping_errors))
return -EHOSTUNREACH;if (msg->msg_name) {
err = ip_mc_join(sk, msg, &icmp_param);
if (err)
return err;
}icmp_param.flags = inet->pinet->icmp_echo_seq = 0;
if (msg->msg_flags & MSG_DONTWAIT)
icmp_param.flags |= PING_IOWAIT;inet->pinet->ping_queue_len = 0;
icmp_param.dest.u.sport = ntohs(inet->inet_sport);
icmp_param.dest.u.dport = ntohs(inet->inet_dport);rt = ip_route_output_key(sk, &icmp_param);
if (IS_ERR(rt))
goto out;dst = rt_dst(rt);
icmp_param.ttl = inet->opt.ttl;
icmp_param.tos = 0;
icmp_param.len = len;
icmp_param.rtt = 0;if (rt->rt_type == RTN_UNREACHABLE &&
(rt->rt_flags & RTCF_LOCAL) == RTCF_LOCAL) {
icmp_send(sk, &icmp_param, ICMP_DEST_UNREACH,
ICMP_FRAG_NEEDED);
err = -EMSGSIZE;
goto out_rt;
}if (uip_sol_outgoing(sock_net(sk), &rt->fl.u.ip4) &&
IN_DEV_CSUM(sock_net(sk)->ipv4.devin)->gso_fallback && gso_max_size(sk)) {
if (ping_gso_receive(sk, msg, len, 0))
goto out_rt;
icmp_param.flags |= PING_PLUSGSO;
err = ping_queue_rcv_skb(sk, msg, len, rt, dst, &icmp_param);
if (err && err != -EHOSTUNREACH)
goto out_rt;
return -EINPROGRESS;
}if (ping_gso_receive(sk, msg, len, 0))
goto out_rt;icmp_param.flags |= PING_NOCACHE;
err = icmp_queue_rcv_skb(sk, NULL, msg, len, ICMP_ECHO,
IFNAMSIZ, IPPROTO_ICMP, PING_TTL,
icmp_handler, &icmp_param);out_rt:
ip_rt_put(rt);
out:
if (icmp_param.dest.ops) {
inet->pinet->tos = 0;
if (icmp_param.dest.ops->bindtodev)
icmp_param.dest.ops->bindtodev(NULL, &icmp_param.dest.u);
}
return err;
}
“`以上的代码只是源代码的一部分,但是它们展示了ping命令的关键部分。整个源代码实现了基本的ping功能,包括发送ICMP echo请求和接收ICMP echo回复。
2年前