ping.h
#ifndef __PING_H_
#define __PING_H_
#include <stdio.h>
#include <stdlib.h>
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/time.h>
#define MAX_BUFF_SIZE (1024)
#define MAX_PACKET_SIZE (1024)
#define DEF_PACKET_SIZE (32)
#define DEBUG (0)
int CodePingReq(const struct icmphdr hdr, int datasize, unsigned char *output);
int DecodeIPHeader(unsigned char* buff, int size, struct iphdr &hdr);
int DecodePingResp(unsigned char* buff, int size);
unsigned short GetCheckSum(unsigned short *data, int size);
long getCurrentTimeUs();
void SendPingMsg(int sockfd,struct sockaddr_in DstAddr,int datasize, int times);
#endif
main.cpp
#include "ping.h"
void PrintHexStr(unsigned char* buff, int size)
{
for(int i=0;i<size;i++)
{
printf("%02X ", buff[i]);
}
printf("\n");
}
unsigned short GetCheckSum(unsigned short *data, int size)
{
unsigned long checksum = 0;
while(size > 1)
{
checksum += *data++;
size -= sizeof(unsigned short);
}
if(size > 0)
{
checksum += *(unsigned char*)data;
}
// 高16位与低16位相加
checksum = (checksum >> 16) + (checksum & 0xffff);
checksum += (checksum >> 16);
return (unsigned short)(~checksum);
}
long getCurrentTimeUs()
{
long timems = 0;
struct timeval tv = { 0 };
if(0 != gettimeofday(&tv, NULL))
{
printf("gettimeofday() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
}
timems = tv.tv_sec*1000000 + tv.tv_usec;
return timems;
}
int CodePingReq(const struct icmphdr hdr, int datasize ,unsigned char *output)
{
int nOffset = 0;
unsigned char *pos = output;
memcpy(pos + nOffset, &(hdr.type), sizeof(hdr.type));
nOffset += sizeof(hdr.type);
memcpy(pos + nOffset, &(hdr.code), sizeof(hdr.code));
nOffset += sizeof(hdr.code);
int nCheckSumPos = nOffset;
memcpy(pos + nOffset, &(hdr.checksum), sizeof(hdr.checksum));
nOffset += sizeof(hdr.checksum);
memcpy(pos + nOffset, &(hdr.un.echo.id), sizeof(hdr.un.echo.id));
nOffset += sizeof(hdr.un.echo.id);
unsigned short seq =hdr.un.echo.sequence;
memcpy(pos + nOffset, &seq, sizeof(seq));
nOffset += sizeof(seq);
unsigned long lTimeStamp = getCurrentTimeUs();
memcpy(pos + nOffset, &lTimeStamp, sizeof(lTimeStamp));
nOffset += sizeof(lTimeStamp);
char *pDataPart = (char*)malloc(datasize);
memset(pDataPart, 'E', datasize);
memcpy(pos + nOffset, pDataPart, datasize);
nOffset += datasize;
// 更新校验和
unsigned short checksum = GetCheckSum((unsigned short*)pos, nOffset);
memcpy(pos + nCheckSumPos, &checksum, sizeof(checksum));
#if DEBUG
printf("Code Ping Message:\n");
printf("type:%d,code:%d,checksum:%d,id:%d,sequence:%d,timestamp:%ld\n",
hdr.type,
hdr.code,
checksum,
hdr.un.echo.id,
seq),
lTimeStamp);
#endif
/*
struct icmphdr {
__u8 type;
__u8 code;
__sum16 checksum;
union {
struct {
__be16 id;
__be16 sequence;
} echo;
__be32 gateway;
struct {
__be16 __unused;
__be16 mtu;
} frag;
__u8 reserved[4];
} un;
};
*/
printf("Code Ping Header ==>type:%d,code:%d,checksum:%d,id:%d,sequence:%d,timestamp:%ld\n",
hdr.type,
hdr.code,
checksum,
hdr.un.echo.id,
seq,
lTimeStamp);
return nOffset;
}
int DecodePingResp(unsigned char* buff, int size, struct icmphdr &hdr, unsigned long &lRecvTime)
{
int nOffset = 0;
if(size < sizeof(struct icmphdr))
{
printf("Ping resp message too short!\n");
return 0;
}
memcpy(&hdr.type, buff + nOffset, sizeof(hdr.type));
nOffset += sizeof(hdr.type);
memcpy(&hdr.code, buff + nOffset, sizeof(hdr.code));
nOffset += sizeof(hdr.code);
memcpy(&hdr.checksum, buff + nOffset, sizeof(hdr.checksum));
nOffset += sizeof(hdr.checksum);
memcpy(&hdr.un.echo.id, buff + nOffset, sizeof(hdr.un.echo.id));
nOffset += sizeof(hdr.un.echo.id);
memcpy(&hdr.un.echo.sequence, buff + nOffset, sizeof(hdr.un.echo.sequence));
//hdr.un.echo.sequence = ntohs(hdr.un.echo.sequence);
nOffset += sizeof(hdr.un.echo.sequence);
memcpy(&lRecvTime, buff + nOffset, sizeof(lRecvTime));
nOffset += sizeof(lRecvTime);
#if DEBUG
printf("Decode Ping Message:\n");
printf("type:%d, code:%d, checksum:%d, id:%d, seq:%d, recvtime:%ld\n",
hdr.type,
hdr.code,
hdr.checksum,
hdr.un.echo.id,
hdr.un.echo.sequence,
lRecvTime);
for(int i=0;i<size-nOffset;i++)
{
printf("%02X ", buff[i]);
}
printf("\n");
#endif
return size;
}
int DecodeIPHeader(unsigned char* buff, int size, struct iphdr &hdr)
{
int nOffset = 0;
unsigned char version = 0;
memcpy(&version, buff + nOffset, sizeof(version));
nOffset += sizeof(version);
hdr.ihl = (version >> 4)& 0xf;
hdr.version = (version) & 0xf;
memcpy(&hdr.tos, buff + nOffset, sizeof(hdr.tos));
nOffset += sizeof(hdr.tos);
memcpy(&hdr.tot_len, buff + nOffset, sizeof(hdr.tot_len));
nOffset += sizeof(hdr.tot_len);
hdr.tot_len = ntohs(hdr.tot_len);
memcpy(&hdr.id, buff + nOffset, sizeof(hdr.id));
nOffset += sizeof(hdr.id);
//hdr.id = ntohs(hdr.id);
memcpy(&hdr.frag_off, buff + nOffset, sizeof(hdr.frag_off));
nOffset += sizeof(hdr.frag_off);
hdr.frag_off = ntohs(hdr.frag_off);
memcpy(&hdr.ttl, buff + nOffset, sizeof(hdr.ttl));
nOffset += sizeof(hdr.ttl);
memcpy(&hdr.protocol, buff + nOffset, sizeof(hdr.protocol));
nOffset += sizeof(hdr.protocol);
memcpy(&hdr.check, buff + nOffset, sizeof(hdr.check));
nOffset += sizeof(hdr.check);
hdr.check = ntohs(hdr.check);
memcpy(&hdr.saddr, buff + nOffset, sizeof(hdr.saddr));
nOffset += sizeof(hdr.saddr);
memcpy(&hdr.daddr, buff + nOffset, sizeof(hdr.daddr));
nOffset += sizeof(hdr.daddr);
return nOffset;
}
void SendPingMsg(int sockfd, struct sockaddr_in DstAddr, int datasize, int times)
{
unsigned char szSendBuffer[MAX_BUFF_SIZE] = { 0 };
unsigned char szRecvBuffer[MAX_BUFF_SIZE] = { 0 };
struct icmphdr hdr = { 0 };
hdr.type = ICMP_ECHO;
hdr.code = 0;
hdr.checksum = 0;
hdr.un.echo.id = (unsigned short)getpid();
hdr.un.echo.sequence = 0;
long statics = 0;
long seqno = 0;
for(int i=0;i<times;i++)
{
hdr.un.echo.sequence = seqno++;
hdr.checksum = 0;
int nDataSize = CodePingReq(hdr, datasize, szSendBuffer);
int nSend = sendto(sockfd, szSendBuffer, nDataSize, 0, (struct sockaddr*)&DstAddr, sizeof(DstAddr));
if(nSend < 0)
{
printf("sendto() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
break;
}
struct sockaddr_in fromAddr = { 0 };
socklen_t addrlen = sizeof(fromAddr);
int nRecv = recvfrom(sockfd, szRecvBuffer, MAX_BUFF_SIZE, 0, (struct sockaddr*)&fromAddr, &addrlen);
if(nRecv < 0)
{
printf("recvfrom() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
break;
}
unsigned long lRecvTime = getCurrentTimeUs();
char szFromIp[64] = { 0 };
if(inet_ntop(AF_INET, &fromAddr.sin_addr, szFromIp, sizeof(szFromIp)) < 0)
{
printf("inet_ntop() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
}
printf("recv %d bytes from %s.\n", nRecv, szFromIp);
struct iphdr ip_hdr = { 0 };
struct icmphdr icmp_hdr = { 0 };
unsigned long lSendTime = 0;
int nIPOffset = DecodeIPHeader(szRecvBuffer, nRecv, ip_hdr);
char szSrcIp[64] = { 0 }, szDstIp[64] = { 0 };
if(inet_ntop(AF_INET, &ip_hdr.saddr, szSrcIp, sizeof(szSrcIp)) < 0)
{
printf("inet_ntop() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
}
if(inet_ntop(AF_INET, &ip_hdr.daddr, szDstIp, sizeof(szDstIp)) < 0)
{
printf("inet_ntop() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
}
printf("Decode IP Header (%d bytes) <== ihl:%x, version:%x, tos:%x, tot_len:%x, id:%x, frag_off:%x, ttl:%x, protocol:%x, check:%x, saddr:%x(%s), daddr:%x(%s)\n",
nIPOffset,
ip_hdr.ihl,
ip_hdr.version,
ip_hdr.tos,
ip_hdr.tot_len,
ip_hdr.id,
ip_hdr.frag_off,
ip_hdr.ttl,
ip_hdr.protocol,
ip_hdr.check,
ip_hdr.saddr,
szSrcIp,
ip_hdr.daddr,
szDstIp);
int nICMPOffset = DecodePingResp(szRecvBuffer + nIPOffset, nRecv - nIPOffset, icmp_hdr, lSendTime);
printf("Decode Ping Header (%d bytes) <== type:%d, code:%d, checksum:%d, id:%d, seq:%d, sendtime:%.3fms\n",
nICMPOffset,
icmp_hdr.type,
icmp_hdr.code,
icmp_hdr.checksum,
icmp_hdr.un.echo.id,
icmp_hdr.un.echo.sequence,
(lRecvTime - lSendTime)*0.1/1000
);
memset(szSendBuffer,0,sizeof(szSendBuffer));
memset(szRecvBuffer,0,sizeof(szRecvBuffer));
sleep(1);
}
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Usage:%s <ADDR>\n", argv[0]);
return 0;
}
char* destination = argv[1];
int nDataSize = 48;
int nTimes = 4;
struct protoent *protocol = getprotobyname("icmp");
int sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto);
if(sockfd < 0)
{
printf("socket failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
return -1;
}
if(nDataSize > MAX_PACKET_SIZE)
{
nDataSize = DEF_PACKET_SIZE;
printf("data size too large, set to def packet size(%d).", DEF_PACKET_SIZE);
}
struct hostent *hDestHost = gethostbyname2(destination, AF_INET);
unsigned long lDstAddr = 0;
if(hDestHost == NULL)
{
printf("hostent is null\n");
if(inet_pton(AF_INET, destination, &lDstAddr) < 0)
{
printf("inet_pton failed\n");
lDstAddr = 0;
}
}
if(hDestHost == NULL && lDstAddr == INADDR_NONE)
{
printf("Unable to resovle %s\n", destination);
close(sockfd);
return -1;
}
char destAddrIp[64] = { 0 };
int destAddrType = 0;
if(hDestHost)
{
destAddrType = hDestHost->h_addrtype;
if(inet_ntop(AF_INET, (struct in_addr*)*hDestHost->h_addr_list, destAddrIp, sizeof(destAddrIp)) < 0)
{
printf("inet_ntop() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
}
}
else
{
destAddrType = AF_INET;
struct in_addr addr = { 0 };
addr.s_addr = lDstAddr;
if(inet_ntop(AF_INET, &addr, destAddrIp, sizeof(destAddrIp)) < 0)
{
printf("inet_ntop() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
close(sockfd);
return -1;
}
}
struct sockaddr_in dstaddr = { 0 };
dstaddr.sin_family = destAddrType;
if(inet_pton(AF_INET, destAddrIp, &dstaddr.sin_addr) < 0)
{
printf("inet_pton() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
close(sockfd);
return -1;
}
// 设置超时
struct timeval tv = {1000, 0};
// 接收超时
if(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0)
{
printf("setsockopt() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
close(sockfd);
return -1;
}
// 发送超时
if(setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
{
printf("setsockopt() failed, errno=%d, errmsg=%s\n", errno, strerror(errno));
close(sockfd);
return -1;
}
printf("Pinging %s ...\n", destAddrIp);
SendPingMsg(sockfd, dstaddr, nDataSize, nTimes);
close(sockfd);
printf("main quit\n");
return 0;
}
Makefile
CC=g++
SRC:=main.cpp
OBJS :=main.o
TARGET=ping
CFLAGS=-Werror
all:
${CC} -c ${SRC}
${CC} -o ${TARGET} ${OBJS}
clean:
rm -f ${OBJS} ${TARGET}
输出
root@ubuntu:~/code/imcp# ./ping 10.107.11.1
Pinging 10.107.11.1 ...
Code Ping Header ==>type:8,code:0,checksum:36444,id:9945,sequence:0,timestamp:1614393999141661
recv 84 bytes from 10.107.11.1.
Decode IP Header (20 bytes) <== ihl:4, version:5, tos:0, tot_len:54, id:c6f, frag_off:0, ttl:40, protocol:1, check:d9ba, saddr:10b6b0a(10.107.11.1), daddr:c126b0a(10.107.18.12)
Decode Ping Header (64 bytes) <== type:0, code:0, checksum:36452, id:9945, seq:0, sendtime:0.023ms
Code Ping Header ==>type:8,code:0,checksum:18549,id:9945,sequence:1,timestamp:1614394000142580
recv 84 bytes from 10.107.11.1.
Decode IP Header (20 bytes) <== ihl:4, version:5, tos:0, tot_len:54, id:d6f, frag_off:0, ttl:40, protocol:1, check:d9b9, saddr:10b6b0a(10.107.11.1), daddr:c126b0a(10.107.18.12)
Decode Ping Header (64 bytes) <== type:0, code:0, checksum:18557, id:9945, seq:1, sendtime:0.039ms
Code Ping Header ==>type:8,code:0,checksum:1051,id:9945,sequence:2,timestamp:1614394001143102
recv 84 bytes from 10.107.11.1.
Decode IP Header (20 bytes) <== ihl:4, version:5, tos:0, tot_len:54, id:e6f, frag_off:0, ttl:40, protocol:1, check:d9b8, saddr:10b6b0a(10.107.11.1), daddr:c126b0a(10.107.18.12)
Decode Ping Header (64 bytes) <== type:0, code:0, checksum:1059, id:9945, seq:2, sendtime:0.042ms
Code Ping Header ==>type:8,code:0,checksum:49076,id:9945,sequence:3,timestamp:1614394002143636
recv 84 bytes from 10.107.11.1.
Decode IP Header (20 bytes) <== ihl:4, version:5, tos:0, tot_len:54, id:f6f, frag_off:0, ttl:40, protocol:1, check:d9b7, saddr:10b6b0a(10.107.11.1), daddr:c126b0a(10.107.18.12)
Decode Ping Header (64 bytes) <== type:0, code:0, checksum:49084, id:9945, seq:3, sendtime:0.044ms
main quit