Skip to content

Instantly share code, notes, and snippets.

@KelviNosse
Created June 15, 2017 21:02
Show Gist options
  • Save KelviNosse/930988c7dda1966e164a712fa32dc567 to your computer and use it in GitHub Desktop.
Save KelviNosse/930988c7dda1966e164a712fa32dc567 to your computer and use it in GitHub Desktop.
A ping function implemented on c++
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#define DEFDATALEN (64-ICMP_MINLEN)
#define MAXIPLEN 60
#define MAXICMPLEN 76
#define MAXPACKET (65536 - 60 - ICMP_MINLEN)
using namespace std;
uint16_t in_cksum(uint16_t *addr, unsigned len);
int ping(string target)
{
int s, i, cc, packlen, datalen = DEFDATALEN;
struct hostent *hp;
struct sockaddr_in to, from;
struct ip *ip;
u_char *packet, outpack[MAXPACKET];
char hnamebuf[MAXHOSTNAMELEN];
string hostname;
struct icmp *icp;
int ret, fromlen, hlen;
fd_set rfds;
struct timeval tv;
int retval;
struct timeval start, end;
int end_t;
bool cont = true;
to.sin_family = AF_INET;
to.sin_addr.s_addr = inet_addr(target.c_str());
if (to.sin_addr.s_addr != (u_int)-1)
hostname = target;
else
{
hp = gethostbyname(target.c_str());
if (!hp)
{
cerr << "unknown host "<< target << endl;
return -1;
}
to.sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&to.sin_addr, hp->h_length);
strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hostname = hnamebuf;
}
packlen = datalen + MAXIPLEN + MAXICMPLEN;
if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL)
{
cerr << "malloc error\n";
return -1;
}
if ( (s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
return -1; /* Needs to run as superuser!! */
}
icp = (struct icmp *)outpack;
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = 12345;
icp->icmp_id = getpid();
cc = datalen + ICMP_MINLEN;
icp->icmp_cksum = in_cksum((unsigned short *)icp,cc);
gettimeofday(&start, NULL);
i = sendto(s, (char *)outpack, cc, 0, (struct sockaddr*)&to, (socklen_t)sizeof(struct sockaddr_in));
if (i < 0 || i != cc)
{
if (i < 0)
perror("sendto error");
cout << "wrote " << hostname << " " << cc << " chars, ret= " << i << endl;
}
FD_ZERO(&rfds);
FD_SET(s, &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
while(cont)
{
retval = select(s+1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
perror("select()");
return -1;
}
else if (retval)
{
fromlen = sizeof(sockaddr_in);
if ( (ret = recvfrom(s, (char *)packet, packlen, 0,(struct sockaddr *)&from, (socklen_t*)&fromlen)) < 0)
{
perror("recvfrom error");
return -1;
}
// Check the IP header
ip = (struct ip *)((char*)packet);
hlen = sizeof( struct ip );
if (ret < (hlen + ICMP_MINLEN))
{
cerr << "packet too short (" << ret << " bytes) from " << hostname << endl;;
return -1;
}
// Now the ICMP part
icp = (struct icmp *)(packet + hlen);
if (icp->icmp_type == ICMP_ECHOREPLY)
{
if (icp->icmp_seq != 12345)
{
cout << "received sequence # " << icp->icmp_seq << endl;
continue;
}
if (icp->icmp_id != getpid())
{
cout << "received id " << icp->icmp_id << endl;
continue;
}
cont = false;
}
else
{
cout << "Recv: not an echo reply" << endl;
continue;
}
gettimeofday(&end, NULL);
end_t = 1000000*(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);
if(end_t < 1)
end_t = 1;
cout << "Elapsed time = " << end_t << " usec" << endl;
return end_t;
}
else
{
cout << "No data within one seconds.\n";
return 0;
}
}
return 0;
}
uint16_t in_cksum(uint16_t *addr, unsigned len)
{
uint16_t answer = 0;
/*
* Algorithm is simple, using a 32 bit accumulator (sum), add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the t 16 bits into the lower 16 bits.
*/
uint32_t sum = 0;
while (len > 1) {
sum += *addr++;
len -= 2;
}
if (len == 1) {
*(unsigned char *)&answer = *(unsigned char *)addr ;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
@ZhangQR
Copy link

ZhangQR commented Jan 4, 2020

你好~ 我觉得 socket 应该要关闭,不然在运行很长一段时间之后,会出现 error:24 ,此时 lsof -p | wc -l 已经显示一千多了,而且大部分是 “00000000:0001->00000000:0000 st=07”, 我尝试在 ping 函数中除第一个 return 之外的每一个 return 前面加上 close(s),就不会出现这个问题。

@ieleja
Copy link

ieleja commented Jan 22, 2022

Debian Bullseye, 64-bit

strange behavior, when system is 64-bit and

cat /proc/sys/kernel/pid_max
4194304

line 140. produces error

because icp->icmp_id
is
unsigned short

which is definitely less than PID

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment