《Linux实现ICMP PING代码》

《C语言实现ICMP协议,并进行PING测试》

《本文源代码下载》

目录

源代码

icmpping.c

icmpping.h

main.c

makefile

测试结果


源代码

icmpping.c

/***  实现 ICMP ping功能,检测 目的 IP地址通路,并计算时延*  *  作者: RToax*  日期: 2020年10月12日*/
#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/timerfd.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <errno.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <pthread.h>#include "icmpping.h"#ifndef offsetof
#define offsetof(type, member)    ( (int) & ((type*)0) -> member )
#endif#define ICMP_BUF_SIZE           256
#define ICMP_RECVBUF_SIZE       (50 * 1024)
#define ICMP_MAGIC_ID           0xf1f2
#define ICMP_PROTO_NAME         "icmp"
#define ICMP_DATA               "Rong Tao's ICMP Ping"#define ICMP_PING_SUCC          __ICMP_PING_SUCC
#define ICMP_PING_FAIL          __ICMP_PING_FAIL#define ICMP_LOG(fmt...)        //printf(fmt)#define ICMP_LOG_ERR(fmt...)    fprintf(stderr, fmt);typedef enum {__ICMP_PING_FAIL = ICMPPING_FAIL,__ICMP_PING_SUCC = ICMPPING_SUCC,
}icmp_ping_rlst_t;/* type of icmp ping */
typedef struct __icmp_ping_s {int sockfd;struct sockaddr_in dst_addr, from_addr;int ntx, nrx, ntry;   //number of send pkg and recv pkgint tx, rx;int timeout_s;  //time out secondint (*log)(const char *fmt, ...);int rslt; //__ICMP_PING_XXXX
}icmp_ping_t;/* functions */
static icmp_ping_t* icmp_ping_init(const char *ipv4, int timeout_sec, int ntry, int (*log)(const char *fmt, ...));
int icmp_ping(const char *ipv4, unsigned int timeout, int ntry, int (*log)(const char *fmt, ...), long int *latency);
static void icmp_ping_finish (icmp_ping_t *ping);
static int icmp_ping_destroy(icmp_ping_t* ping);static int icmp_socket();
static int icmp_dst_addr(const char *addrHost, struct sockaddr_in * dst_addr);
static unsigned short icmp_gen_chksum(unsigned short * data, int len);
static int icmp_pkg_pack(void *buffer, int pack_no, const void *data, int data_size);
static int icmp_send_pkg(icmp_ping_t *ping, const void *data, int size);
static int icmp_recv_pkg(icmp_ping_t *ping, void *recvbuf, int size);
static int icmp_pkg_unpack(icmp_ping_t *ping, char * buf, int len);static void __icmp_timeval_sub(struct timeval * out, struct timeval * in);/* alogrithm of checksum */
static unsigned short icmp_gen_chksum(unsigned short * data, int len)
{int             nleft   = len;int             sum     = 0;unsigned short  *w      = data;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;
}/* create a socke to icmp */
static int icmp_socket()
{int sockfd;int size = ICMP_RECVBUF_SIZE;struct protoent * protocol  = NULL;if ((protocol = getprotobyname(ICMP_PROTO_NAME)) == NULL){ICMP_LOG_ERR("getprotobyname error.\n\r");return -1;}if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0){ICMP_LOG_ERR("socket error.\n\r");return -1;}if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size))!=0){ICMP_LOG_ERR("setsockopt SO_RCVBUF error.\n\r");close(sockfd);return -1;}
#if 1    struct timeval timeout = {1,0}; //secif(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(struct timeval))!=0){ICMP_LOG_ERR("setsockopt SO_RCVTIMEO error.\n\r");close(sockfd);return -1;}
#endif    return sockfd;
}/* get dst address */
static int icmp_dst_addr(const char *addrHost, struct sockaddr_in * dst_addr)
{struct hostent  * host      = NULL;unsigned long   inaddr      = 0;bzero(dst_addr, sizeof(struct sockaddr_in));dst_addr->sin_family = AF_INET;if ((inaddr = inet_addr(addrHost)) == INADDR_NONE){if ((host = gethostbyname(addrHost)) == NULL){ICMP_LOG_ERR("gethostbyname error.\n\r");return (-1);}memcpy((char *) &dst_addr->sin_addr, host->h_addr, host->h_length);}else {memcpy((char *) &dst_addr->sin_addr, (char *) &inaddr, sizeof(dst_addr->sin_addr));}return 0;
}/* set icmp hdr */
static int icmp_pkg_pack(void *buffer, int pack_no, const void *data, int data_size)
{int i, packsize = 0;struct icmp * icmp  = malloc(sizeof(struct icmp));icmp->icmp_type     = ICMP_ECHO;icmp->icmp_code     = 0;icmp->icmp_cksum    = 0;icmp->icmp_seq      = htons(pack_no);icmp->icmp_id       = htons(ICMP_MAGIC_ID);gettimeofday((struct timeval *) &icmp->icmp_data, NULL);memcpy(buffer, icmp, sizeof(struct icmp));packsize += sizeof(struct icmp);if(data && data_size){memcpy(buffer+packsize, data, data_size);packsize += data_size;}return packsize;
}/* send icmp package */
static int icmp_send_pkg(icmp_ping_t *ping, const void *data, int size)
{int             packetsize;unsigned short  checksum = 0;int             n = 0;char pkg_buffer[ICMP_BUF_SIZE];packetsize  = icmp_pkg_pack(pkg_buffer, ping->ntx, data, size);checksum    = icmp_gen_chksum((unsigned short *)pkg_buffer, packetsize);#define ICMP_PKG_CHKSUM_OFFSET  offsetof(struct icmp, icmp_cksum)
#define ICMP_PKG_CHKSUM_SIZE    2memcpy(pkg_buffer + ICMP_PKG_CHKSUM_OFFSET, &checksum, ICMP_PKG_CHKSUM_SIZE);if ((n = sendto(ping->sockfd, pkg_buffer, packetsize, 0, (struct sockaddr *) &ping->dst_addr, sizeof(struct sockaddr_in)))< 0){ICMP_LOG_ERR("sendto error. n = %d\n\r", n);return 0;}ping->ntx++;return n;
}/* send icmp package */
static int icmp_recv_pkg(icmp_ping_t *ping, void *recvbuf, int size)
{int n, fromlen;fromlen = sizeof(struct sockaddr_in);if ((n = recvfrom(ping->sockfd, recvbuf, size, 0, (struct sockaddr *) &ping->from_addr, &fromlen)) < 0){ICMP_LOG_ERR("recvfrom error.n = %d\n\r", n);return 0;}ping->nrx++;return n;
}/* unpack icmp pkg */
static int icmp_pkg_unpack(icmp_ping_t *ping, char * buf, int len)
{int     i, iphdrlen;struct  ip * ip = NULL;struct  icmp * icmp = NULL;struct  timeval * tvsend = NULL;double  rtt;ip          = (struct ip *)buf;iphdrlen    = ip->ip_hl << 2;icmp        = (struct icmp *) (buf + iphdrlen);len        -= iphdrlen;if (len < 8){ICMP_LOG_ERR("ICMP packet\'s length is less than 8\n\r");return - 1;}/* ensure icmp reply is mine response msg */if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id = ntohs(ICMP_MAGIC_ID))){tvsend  = (struct timeval *)icmp->icmp_data;struct timeval tvrecv;gettimeofday(&tvrecv, NULL);__icmp_timeval_sub(&tvrecv, tvsend);rtt = tvrecv.tv_sec * 1000 + tvrecv.tv_usec / 1000;if(ping->log)ping->log("%d byte from %s: icmp_req=%u ttl=%d rtt=%.3f ms\n\r", len, inet_ntoa(ping->from_addr.sin_addr), ntohs(icmp->icmp_seq), ip->ip_ttl, rtt);}else {return - 1;}return 0;
}static void __icmp_timeval_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;
}/**********************************************************************************************************************/
/* icmp ping with timeout */
static void icmp_ping_finish (icmp_ping_t *ping)
{if(ping->log){ping->log("\n--- ping statistics ---\n\r");ping->log("%d packets transmitted, %d received, %d%% lost\n\r", ping->ntx, ping->nrx, (ping->ntx - ping->nrx) / ping->ntx * 100);}if(ping->ntx == ping->ntry)ping->rslt = __ICMP_PING_SUCC;return;
}static icmp_ping_t* icmp_ping_init(const char *ipv4, int timeout_sec, int ntry, int (*log)(const char *fmt, ...))
{icmp_ping_t *ping = (icmp_ping_t *)malloc(sizeof(icmp_ping_t));if(!ping) return NULL;memset(ping, 0x00, sizeof(icmp_ping_t));ping->sockfd    = icmp_socket();ping->timeout_s = timeout_sec;ping->ntry      = ntry;ping->log       = log;icmp_dst_addr(ipv4, &ping->dst_addr);if(ping->log)ping->log("PING %s(%s): %d bytes data in ICMP packets.\n\r", ipv4, inet_ntoa(ping->dst_addr.sin_addr), sizeof(ICMP_DATA));return ping;
}static int icmp_ping_destroy(icmp_ping_t* ping)
{if(ping){close(ping->sockfd);free(ping);}ping = NULL;
}
static long int timeval_sub_usec(const struct timeval *before, const struct timeval *after)
{long int usec_diff = 0;usec_diff = (after->tv_sec-before->tv_sec)*1000000 + after->tv_usec-before->tv_usec;return usec_diff;
}/* PING */
int icmp_ping(const char *ipv4, unsigned int timeout, int ntry, int (*log)(const char *fmt, ...), long int *latency)
{icmp_ping_t *ping = icmp_ping_init(ipv4, timeout, ntry, log);struct timeval starttime, endtime;long int usec_diff = 0;char pkg_buffer[ICMP_BUF_SIZE];ping->rslt = __ICMP_PING_FAIL;while(ping->ntx < ping->ntry){gettimeofday(&starttime, NULL);ping->tx = icmp_send_pkg(ping, ICMP_DATA, sizeof(ICMP_DATA));if(ping->tx < 8) {ping->rslt = __ICMP_PING_FAIL;break;}ping->rx = icmp_recv_pkg(ping, pkg_buffer, ping->tx);if(ping->rx<8) {ping->rslt = __ICMP_PING_FAIL;break;}gettimeofday(&endtime, NULL);usec_diff += timeval_sub_usec(&starttime, &endtime);
//        printf("usec_diff = %ld\n", usec_diff);if (icmp_pkg_unpack(ping, pkg_buffer, ping->rx) == -1)continue;//sleep(1);}if(latency) *latency = (long int)(usec_diff / ping->ntry);icmp_ping_finish(ping);icmp_ping_destroy(ping);return ping->rslt;
}

icmpping.h

/***  实现 ICMP ping功能,检测 目的 IP地址通路,并计算时延*  *  作者: RToax*  日期: 2020年10月12日*/
#ifndef __ICMP_PING_H
#define __ICMP_PING_H 1/* icmp_ping 返回值 */
#define ICMPPING_SUCC   0   /* 成功 */
#define ICMPPING_FAIL   1   /* 失败 *//***  icmp_ping   - icmp ping*  *  param[in]   ipv4:       目的IPv4地址*  param[in]   timeout:    超时设置,若超出设定值,且ping不通情况下,函数自动返回*  param[in]   ntry:       ping的次数,发 req包数量*  param[in]   log:        打印调试信息,若为 NULL则不打印*  param[in]   latency:    计算平均时延,单位 微妙 micro second(参见结构 struct timeval)*                          该参数 填 NULL 时,不计算时延**  return  成功返回 ICMPPING_SUCC, 失败返回 ICMPPING_FAIL*/
int icmp_ping(const char *ipv4, unsigned int timeout, int ntry, int (*log)(const char *fmt, ...), long int *latency);#endif /*<__ICMP_PING_H>*/

main.c

#include <stdio.h>
#include "icmpping.h"int main(int argc, char * argv[])
{if (argc < 2){printf("Usage: %s hostname/IP address\n\r", argv[0]);return (-1);}long int latency;int rslt = icmp_ping(argv[1], 1, 3, NULL, &latency);if(rslt == ICMPPING_SUCC)printf("ping \033[1;32m%s\033[m is OK. latency = %ld microseconds\n", argv[1], latency);elseprintf("ping \033[1;31m%s\033[m is not OK.\n", argv[1]);return 0;
}

makefile

# 编译makefile
# RToax 2020年10月12日ICMP_INC := -I ./
ICMP_SRC := icmpping.cICMP_DEP := icmpping.oICMP_TARGET=icmppingall:$(ICMP_TARGET)$(ICMP_DEP): $(ICMP_SRC)gcc -c $(ICMP_SRC) $(ICMP_INC) -o $(ICMP_DEP)$(ICMP_TARGET): $(ICMP_DEP)gcc $(ICMP_DEP) main.c $(ICMP_INC) -o $(ICMP_TARGET)clean:rm -f $(ICMP_DEP) $(ICMP_TARGET)

测试结果

[root@localhost icmpping]# ./icmpping 0
ping 0 is OK. latency = 18 microseconds
[root@localhost icmpping]# ./icmpping 127.0.0.1
ping 127.0.0.1 is OK. latency = 15 microseconds
[root@localhost icmpping]# ./icmpping 10.170.6.24
ping 10.170.6.24 is OK. latency = 231 microseconds
[root@localhost icmpping]# ./icmpping 10.37.8.37
ping 10.37.8.37 is OK. latency = 20744 microseconds

Linux系统实现ICMP ping功能,并计算时延相关推荐

  1. linux中bash的功能主要有,Linux系统中的Bash功能的介绍

    今天小编要跟大家分享的文章是关于Linux系统中的Bash功能的介绍.一个完整计算机的体系结构包括:硬件与软件,而软件又分为系统软件与应用软件,负责对硬件仅需管理与操作的是系统软件的内核部分,用户是无 ...

  2. linux系统怎么ping命令,Linux系统下的ping命令的使用

    学习计算机网络的时候,常用的网络诊断工具比如ping.ipconfig命令这些都是我们经常要使用的,主要是用来监测网络是否通顺,相信你在Windows中已经是有经常使用过,并且也可以在dos系统下面可 ...

  3. Linux系统Bash的常用功能(9)

    了解了基本的Linux文件文件系统的概念后,我们将更深入的了解一下Linux的其他方面的内容,那就是我们所使用的用户接口,也就是大家常听到的 『Shell』 ,『这个shell并不是黑客反弹的shel ...

  4. 在Linux系统中使用蓝牙功能的基本方法

    首先确定硬件上有支持蓝牙的设备,然后运行如下命令,就可以开到我们的蓝牙设备了: lsusb 运行hciconfig可以看到: 从上图可以看出,我们的蓝牙设备是hci0 运行hcitool dev可以看 ...

  5. VB纯API实现强化Ping功能,替代系统自带Ping功能!

    本程序代码引用借鉴了两位前辈的代码: https://www.cnblogs.com/lichmama/p/3826565.html https://blog.csdn.net/jessezappy/ ...

  6. 类linux系统/proc/sysrq-trigger文件功能作用

    立即重启计算机      echo "b" > /proc/sysrq-trigger 立即关闭计算机      echo "o" > /proc/ ...

  7. Linux 系统中的dvfs功能

    前言 最近硬件的同事需要我们提供的版本能动态调频,何为动态调频呢?对于CPU来讲,功耗和性能是一对不可调和的矛盾,通过调整CPU的电压和频率,可以在功耗和性能之间找一个平衡点.由于调整是在系统运行的过 ...

  8. linux系统 设置网卡ping通主机连上外网

    上节https://blog.csdn.net/weixin_39816740/article/details/80255563 1.登陆系统 2.说明网卡没启动 3.按照我的做不会错 设置ip   ...

  9. linux系统更新失败处理功能,Proxmox VE升级apt-get update失败处理 | linux运维小站–linux系统架构_服务器运维_Linux运维工程师工作手札...

    安装每一个系统之后第一件事习惯性地升级系统,比如Centos.Redhat执行yum update,ubuntu.Debian执行apt-get update,但是昨天在安装完Proxmox VE执行 ...

最新文章

  1. [ linq2db ] 使用LoadWith()对外键进行查询
  2. UIPasteboard
  3. ESP8266固件的下载
  4. Java 重定位 —— redirect:
  5. python第三方工具箱_我的Python笔记——标准库、第三方工具包
  6. c++如何定义一个只能在堆上(栈上)生成对象的类?
  7. Linux网络编程 之 IO复用epoll(十)
  8. axure 画小程序效果图_APP详情页如何用Axure画出来
  9. java quartz spring_JavaLib-quartz | 基于Spring Boot Quartz开发的定时任务
  10. docker 容器的常用命令及配置
  11. 从 阿西莫夫机器人三大定律 谈起
  12. 数字城市厦门智慧防汛平台测试计划【软件测试与工程】
  13. python发短信sim800_sim800l 发短信
  14. 云计算采用的各种虚拟化技术比较
  15. 北京办理互联网经营许可证(ICP证)的要求
  16. 定时任务:springboot集成Quartz实现多任务多触发的动态管理
  17. android 最新 九宫格,Android开发中怎么显示一个九宫格图片
  18. 【OpenCV3图像处理】颜色空间转换(一)颜色空间分类总结
  19. php商品在最少购买,ecshop每个商品添加最少购买量最小订购量
  20. 浅谈现在完成时被动语态

热门文章

  1. css带占位符的搜索框,superplaceholder.js-功能强大的超级输入框占位符插件
  2. Spring框架----自动按照类型注入的Autowired注解
  3. # 2017-2018-1 20155336《信息安全技术》实验二——Windows口令破解
  4. OpenCV图像处理篇之边缘检測算子
  5. 动态规划uva1347
  6. 第二百六十三天 how can I 坚持
  7. Struts2返回Json数据(使用Struts2插件)
  8. MVC 中 注册不成功 或其他操作不成功 提示办法
  9. 图片类关于实现图片剪切功能的相关类的学习心得
  10. 6-MyBatis基础