ICMP recvfrom 函数始终接收数据,即使目标已关闭
ICMP recvfrom function always receive data even though the destination was shutdown
我用 C 写了一个程序来 ping 一台机器并接收来自那台机器的回声来检测这台机器是否还活着。我的代码如下:
#define PING_PKT_S 64
#define PORT_NO 0
#define PING_SLEEP_RATE 1000000
#define RECV_TIMEOUT 1
int pingloop=1;
struct ping_pkt {
struct icmphdr hdr;
char msg[PING_PKT_S-sizeof(struct icmphdr)];
};
unsigned short checksum(void *b, int len) {
unsigned short *buf = b;
unsigned int sum=0;
unsigned short result;
for ( sum = 0; len > 1; len -= 2 )
sum += *buf++;
if ( len == 1 )
sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
//to check if this machine is actively working
int isActiveNow(){
const char *pre = "161.";
char *prefixArr[] = {pre};
char myIp[64];
memset(myIp, 0, sizeof(myIp));
get_local_ip(myIp, prefixArr);
printf("---isActiveNow:%s\n",myIp);
if(myIp[0]=='[=10=]'){
printf("Not Active Now!\n");
return 0;
}
return 1;
}
void intHandler(int dummy) {
pingloop=0;
}
long current_timestamp() {
struct timeval te;
gettimeofday(&te, NULL);
long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000;
return milliseconds;
}
void display(void *buf, int bytes){
int i;
struct iphdr *ip = buf;
struct icmphdr *icmp = buf+ip->ihl*4;
printf("---------------------Start Display\n");
for ( i = 0; i < bytes; i++ ){
printf("%c", ((unsigned char*)buf)[i]);
}
printf("---------------------End Display\n");
}
int failedNum = 0;
// make a ping request
void send_ping(int ping_sockfd, char *ping_ip) {
int ttl_val=64, msg_count=0, i, addr_len, msg_received_count=0;
struct ping_pkt pckt;
struct sockaddr_in r_addr;
struct timespec time_start, time_end;
// set socket options at ip to TTL and value to 64,
if (setsockopt(ping_sockfd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) != 0) {
printf("Setting socket options to TTL failed!\n");
return;
}else{
printf("\nSocket set to TTL..\n");
}
// send icmp packet in an infinite loop
while(pingloop) {
sleep(2);
if(isActiveNow()){
printf("--current is active!");
continue;
}
//filling packet
bzero(&pckt, sizeof(pckt));
pckt.hdr.type = ICMP_ECHO;
pckt.hdr.un.echo.id = getpid();
for ( i = 0; i < 10; i++ ){
pckt.msg[i] = 'w';
}
pckt.msg[i] = 0;
pckt.hdr.un.echo.sequence = msg_count++;
pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
long s1 = current_timestamp();
struct hostent *hostentObj;
hostentObj = gethostbyname(ping_ip);
struct sockaddr_in ping_addr;
ping_addr.sin_port = 0;
ping_addr.sin_family = hostentObj->h_addrtype;
ping_addr.sin_addr.s_addr = *(long*) hostentObj->h_addr;
//Using synchronize
if ( sendto(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*) &ping_addr, sizeof(ping_addr)) <= 0) {
printf("\nPacket Sending Failed!\n");
}
struct timeval tv_out;
tv_out.tv_sec = 10;
tv_out.tv_usec = 0;
// setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv_out, sizeof tv_out);
struct sockaddr_in myAddr;
unsigned char buf[1024];
int len=sizeof(myAddr);
bzero(buf, sizeof(buf));
struct ping_pkt pckt2;
int bytes = recvfrom(ping_sockfd, &pckt2, sizeof(pckt2), 0, (struct sockaddr*)&myAddr, &len);
printf("---------------------bytes len:%d\n",bytes);
if ( bytes > 0 ){
display(&pckt2, bytes);
long s2 = current_timestamp();
printf("from %s msg_seq=%d ttl=%d rtt = %ld ms. pckt.hdr.type=%d \n", ping_ip, msg_count,ttl_val, (s2-s1), pckt.hdr.type);
}else{
printf("-------NO RESPONSE!!!!!!\n");
failedNum++;
//maximum detected failed number is 10
if(failedNum>3){
printf("--The other server dead!!!\n");
}
}
}
}
bool prefix(const char *pre, const char *str){
return strncmp(pre, str, strlen(pre)) == 0;
}
void get_local_ip(char *pubIp, char *prefixArr[]){
char *ip;
int fd, intrface, retn = 0;
struct ifreq buf[INET_ADDRSTRLEN];
struct ifconf ifc;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0){
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (caddr_t)buf;
if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc)){
intrface = ifc.ifc_len/sizeof(struct ifreq);
while (intrface-- > 0){
if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[intrface]))){
ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
int len = 0,i = 0;
len = sizeof(prefixArr) / sizeof(*prefixArr);
for (i = 0; i < len; i++) {
if(prefix(prefixArr[i],ip)){
strcpy(pubIp,ip);
return;
}
}
}
}
}
close(fd);
}
}
// Driver Code
int main(int argc, char *argv[]){
int sockfd;
char *pingIp;
char *prefixArr[] = { "11.","10.","172."};
char myIp[64];
memset(myIp, 0, sizeof(myIp));
get_local_ip(myIp, prefixArr);
//the other server's ip for detecting it's alive or dead
pingIp = argv[1];
printf("my ip:%s-----ping ip:%s\n", myIp, pingIp);
//socket()
sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
struct sockaddr_in r_addr;
r_addr.sin_family = AF_INET;
r_addr.sin_addr.s_addr = inet_addr(myIp);
r_addr.sin_port = 0;
bind(sockfd, (struct sockaddr *) &r_addr, sizeof(r_addr));
if(sockfd<0){
printf("\nSocket file descriptor not received!!\n");
return 0;
}else{
printf("\nSocket file descriptor %d received\n", sockfd);
}
//once error seen, it will stop the loop
signal(SIGINT, intHandler);//catching interrupt
//send pings continuously
send_ping(sockfd, pingIp);
return 0;
}
问题是 myReceiveBytes 的值始终为 64,即使目标服务器已经关闭。
您的代码实际上并未对接收到的数据包进行解码。很有可能,您收到的是一个 ICMP 目标无法到达的数据包 from your own host 或来自您的网关。
大致如下:
#include <netinet/ip.h> /* struct iphdr */
...
...
struct sockaddr_in peerAddr;
unsigned char buf[1024];
unsigned int len = sizeof(peerAddr);
int bytes_received = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *) &peerAddr, &len);
if (bytes_received > 0) {
printf("ICMP packet received from %s\n", inet_ntoa(peerAddr.sin_addr));
struct icmphdr *icmp = (struct icmphdr *) (buf + sizeof(struct iphdr));
switch (icmp->type) {
case ICMP_ECHOREPLY: printf("Type: ICMP_ECHOREPLY\n"); break;
case ICMP_DEST_UNREACH: printf("Type: ICMP_DEST_UNREACH\n"); break;
case ICMP_SOURCE_QUENCH: printf("Type: ICMP_SOURCE_QUENCH\n"); break;
case ICMP_REDIRECT: printf("Type: ICMP_REDIRECT\n"); break;
case ICMP_ECHO: printf("Type: ICMP_ECHO\n"); break;
case ICMP_TIME_EXCEEDED: printf("Type: ICMP_TIME_EXCEEDED\n"); break;
case ICMP_PARAMETERPROB: printf("Type: ICMP_PARAMETERPROB\n"); break;
case ICMP_TIMESTAMP: printf("Type: ICMP_TIMESTAMP\n"); break;
case ICMP_TIMESTAMPREPLY: printf("Type: ICMP_TIMESTAMPREPLY\n"); break;
case ICMP_INFO_REQUEST: printf("Type: ICMP_INFO_REQUEST\n"); break;
case ICMP_INFO_REPLY: printf("Type: ICMP_INFO_REPLY\n"); break;
case ICMP_ADDRESS: printf("Type: ICMP_ADDRESS\n"); break;
case ICMP_ADDRESSREPLY: printf("Type: ICMP_ADDRESSREPLY\n"); break;
default: printf("Type: <0x%02x>\n", icmp->type); break;
}
}
我用 C 写了一个程序来 ping 一台机器并接收来自那台机器的回声来检测这台机器是否还活着。我的代码如下:
#define PING_PKT_S 64
#define PORT_NO 0
#define PING_SLEEP_RATE 1000000
#define RECV_TIMEOUT 1
int pingloop=1;
struct ping_pkt {
struct icmphdr hdr;
char msg[PING_PKT_S-sizeof(struct icmphdr)];
};
unsigned short checksum(void *b, int len) {
unsigned short *buf = b;
unsigned int sum=0;
unsigned short result;
for ( sum = 0; len > 1; len -= 2 )
sum += *buf++;
if ( len == 1 )
sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
//to check if this machine is actively working
int isActiveNow(){
const char *pre = "161.";
char *prefixArr[] = {pre};
char myIp[64];
memset(myIp, 0, sizeof(myIp));
get_local_ip(myIp, prefixArr);
printf("---isActiveNow:%s\n",myIp);
if(myIp[0]=='[=10=]'){
printf("Not Active Now!\n");
return 0;
}
return 1;
}
void intHandler(int dummy) {
pingloop=0;
}
long current_timestamp() {
struct timeval te;
gettimeofday(&te, NULL);
long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000;
return milliseconds;
}
void display(void *buf, int bytes){
int i;
struct iphdr *ip = buf;
struct icmphdr *icmp = buf+ip->ihl*4;
printf("---------------------Start Display\n");
for ( i = 0; i < bytes; i++ ){
printf("%c", ((unsigned char*)buf)[i]);
}
printf("---------------------End Display\n");
}
int failedNum = 0;
// make a ping request
void send_ping(int ping_sockfd, char *ping_ip) {
int ttl_val=64, msg_count=0, i, addr_len, msg_received_count=0;
struct ping_pkt pckt;
struct sockaddr_in r_addr;
struct timespec time_start, time_end;
// set socket options at ip to TTL and value to 64,
if (setsockopt(ping_sockfd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) != 0) {
printf("Setting socket options to TTL failed!\n");
return;
}else{
printf("\nSocket set to TTL..\n");
}
// send icmp packet in an infinite loop
while(pingloop) {
sleep(2);
if(isActiveNow()){
printf("--current is active!");
continue;
}
//filling packet
bzero(&pckt, sizeof(pckt));
pckt.hdr.type = ICMP_ECHO;
pckt.hdr.un.echo.id = getpid();
for ( i = 0; i < 10; i++ ){
pckt.msg[i] = 'w';
}
pckt.msg[i] = 0;
pckt.hdr.un.echo.sequence = msg_count++;
pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
long s1 = current_timestamp();
struct hostent *hostentObj;
hostentObj = gethostbyname(ping_ip);
struct sockaddr_in ping_addr;
ping_addr.sin_port = 0;
ping_addr.sin_family = hostentObj->h_addrtype;
ping_addr.sin_addr.s_addr = *(long*) hostentObj->h_addr;
//Using synchronize
if ( sendto(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*) &ping_addr, sizeof(ping_addr)) <= 0) {
printf("\nPacket Sending Failed!\n");
}
struct timeval tv_out;
tv_out.tv_sec = 10;
tv_out.tv_usec = 0;
// setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv_out, sizeof tv_out);
struct sockaddr_in myAddr;
unsigned char buf[1024];
int len=sizeof(myAddr);
bzero(buf, sizeof(buf));
struct ping_pkt pckt2;
int bytes = recvfrom(ping_sockfd, &pckt2, sizeof(pckt2), 0, (struct sockaddr*)&myAddr, &len);
printf("---------------------bytes len:%d\n",bytes);
if ( bytes > 0 ){
display(&pckt2, bytes);
long s2 = current_timestamp();
printf("from %s msg_seq=%d ttl=%d rtt = %ld ms. pckt.hdr.type=%d \n", ping_ip, msg_count,ttl_val, (s2-s1), pckt.hdr.type);
}else{
printf("-------NO RESPONSE!!!!!!\n");
failedNum++;
//maximum detected failed number is 10
if(failedNum>3){
printf("--The other server dead!!!\n");
}
}
}
}
bool prefix(const char *pre, const char *str){
return strncmp(pre, str, strlen(pre)) == 0;
}
void get_local_ip(char *pubIp, char *prefixArr[]){
char *ip;
int fd, intrface, retn = 0;
struct ifreq buf[INET_ADDRSTRLEN];
struct ifconf ifc;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0){
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (caddr_t)buf;
if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc)){
intrface = ifc.ifc_len/sizeof(struct ifreq);
while (intrface-- > 0){
if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[intrface]))){
ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
int len = 0,i = 0;
len = sizeof(prefixArr) / sizeof(*prefixArr);
for (i = 0; i < len; i++) {
if(prefix(prefixArr[i],ip)){
strcpy(pubIp,ip);
return;
}
}
}
}
}
close(fd);
}
}
// Driver Code
int main(int argc, char *argv[]){
int sockfd;
char *pingIp;
char *prefixArr[] = { "11.","10.","172."};
char myIp[64];
memset(myIp, 0, sizeof(myIp));
get_local_ip(myIp, prefixArr);
//the other server's ip for detecting it's alive or dead
pingIp = argv[1];
printf("my ip:%s-----ping ip:%s\n", myIp, pingIp);
//socket()
sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
struct sockaddr_in r_addr;
r_addr.sin_family = AF_INET;
r_addr.sin_addr.s_addr = inet_addr(myIp);
r_addr.sin_port = 0;
bind(sockfd, (struct sockaddr *) &r_addr, sizeof(r_addr));
if(sockfd<0){
printf("\nSocket file descriptor not received!!\n");
return 0;
}else{
printf("\nSocket file descriptor %d received\n", sockfd);
}
//once error seen, it will stop the loop
signal(SIGINT, intHandler);//catching interrupt
//send pings continuously
send_ping(sockfd, pingIp);
return 0;
}
问题是 myReceiveBytes 的值始终为 64,即使目标服务器已经关闭。
您的代码实际上并未对接收到的数据包进行解码。很有可能,您收到的是一个 ICMP 目标无法到达的数据包 from your own host 或来自您的网关。
大致如下:
#include <netinet/ip.h> /* struct iphdr */
...
...
struct sockaddr_in peerAddr;
unsigned char buf[1024];
unsigned int len = sizeof(peerAddr);
int bytes_received = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *) &peerAddr, &len);
if (bytes_received > 0) {
printf("ICMP packet received from %s\n", inet_ntoa(peerAddr.sin_addr));
struct icmphdr *icmp = (struct icmphdr *) (buf + sizeof(struct iphdr));
switch (icmp->type) {
case ICMP_ECHOREPLY: printf("Type: ICMP_ECHOREPLY\n"); break;
case ICMP_DEST_UNREACH: printf("Type: ICMP_DEST_UNREACH\n"); break;
case ICMP_SOURCE_QUENCH: printf("Type: ICMP_SOURCE_QUENCH\n"); break;
case ICMP_REDIRECT: printf("Type: ICMP_REDIRECT\n"); break;
case ICMP_ECHO: printf("Type: ICMP_ECHO\n"); break;
case ICMP_TIME_EXCEEDED: printf("Type: ICMP_TIME_EXCEEDED\n"); break;
case ICMP_PARAMETERPROB: printf("Type: ICMP_PARAMETERPROB\n"); break;
case ICMP_TIMESTAMP: printf("Type: ICMP_TIMESTAMP\n"); break;
case ICMP_TIMESTAMPREPLY: printf("Type: ICMP_TIMESTAMPREPLY\n"); break;
case ICMP_INFO_REQUEST: printf("Type: ICMP_INFO_REQUEST\n"); break;
case ICMP_INFO_REPLY: printf("Type: ICMP_INFO_REPLY\n"); break;
case ICMP_ADDRESS: printf("Type: ICMP_ADDRESS\n"); break;
case ICMP_ADDRESSREPLY: printf("Type: ICMP_ADDRESSREPLY\n"); break;
default: printf("Type: <0x%02x>\n", icmp->type); break;
}
}