1.什么是ping程序
PING程序是测试网络连接,返回网络信息的一个工具,能返回往返时间、途径路由器数量等参数
2.PING使用的协议
PING用ICMP报文协议
3.什么是原始套接字
原始套接字能直接对ip数据包进行操作,能支持icmp,igmp等协议,还可以在开启IP_HDRINCL套接字选项下对IP首部进行构造
在创建套接字是套接字类型指定SOCK_RAW即可创建原始套接字。
4.PING程序的网络模型
PING程序网络模型想过当于客户/服务器网络模型,ping程序相当于客户程序,不停发送请求,循环等待接受回复消息,然后处理输出到客户端,
ping的地址相当于服务器,接受消息发回icmp应答报文。
5.PING程序运行流程
1.创建原始套接字
2.设置时钟信号处理程序,每次接受到信号就发送ICMP报文,时钟程序每过1秒,发出SIGALARM信号
3.等待接受ICMP应答报文,处理报文,输出TTL,RTT等信息
6.总结思考
是否可以利用其他协议实现PING程序,一些服务器对ping请求直接忽略,可否利用TCP/UDP协议实现类似功能
参考程序hping。
附上linux下源码
#include "unp.h"
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#define BUFSIZE 1500
char *host;
pid_t pid;
int sockfd;
int nsent;
int datalen=56;
char sendbuf[BUFSIZE];
struct sockaddr *sasend;
struct addrinfo *ai;
uint16_t
in_cksum(uint16_t *add, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = add;
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);
err_quit("cksum");
}
void
send_a()
{
int m;
int len;
struct icmp *icmp;
icmp = (struct icmp *) sendbuf;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_id = pid;
icmp->icmp_seq = nsent++;
memset(icmp->icmp_data, 0xa5, datalen);
gettimeofday((struct timeval *) icmp->icmp_data, NULL);
len = 8 + datalen;
icmp->icmp_cksum = 0;
icmp->icmp_cksum = in_cksum((u_short *) icmp, len);
if((m=sendto(sockfd, sendbuf, len, 0, ai->ai_addr, ai->ai_addrlen))== -1)
err_quit("sendto error");
}
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;
}
void
proc(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
int hlenl, icmplen;
double rtt;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;
ip = (struct ip *) ptr;
hlenl = (ip->ip_hl << 2);
if (ip->ip_p != IPPROTO_ICMP)
return;
icmp = (struct icmp *)(ptr + hlenl);
if ( (icmplen =len -hlenl) < 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, host,icmp->icmp_seq, ip->ip_ttl,rtt);
}
}
void
sig_alrm(int signo)
{
send_a();
alarm(1);
return;
}
void
readloop()
{
int size;
char recvbuf[BUFSIZE];
char controlbuf[BUFSIZE];
struct iovec iov;
struct msghdr msg;
struct timeval tval;
ssize_t nn;
sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
setuid(getuid());
size=60 * 1024;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size,sizeof(size));
sig_alrm(SIGALRM);
iov.iov_base = recvbuf;
iov.iov_len = sizeof(recvbuf);
msg.msg_name = calloc(1,ai->ai_addrlen);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = controlbuf;
for ( ; ; ){
msg.msg_namelen = ai->ai_addrlen;
msg.msg_controllen = sizeof(controlbuf);
nn = recvmsg (sockfd, &msg, 0);
if (nn < 0){
if (errno == EINTR)
continue;
else
err_sys("recvmsg error");
}
gettimeofday(&tval, NULL);
proc(recvbuf, nn, &msg, &tval);
}
}
int
main(int argc, char **argv)
{
ssize_t n;
struct addrinfo *res;
if (argc!=2)
err_quit("argument not enougth");
host = argv[1];
pid = getpid() & 0xffff;
signal(SIGALRM,sig_alrm);
if (n = getaddrinfo(host,NULL,0,&res) != 0)
return 0;
ai = host_serv(host,NULL,0,0);
readloop();
exit(0);
}