目录

1、ping的原理及实现

2、ipv6

2.1、ipv6报头

2.2、Linux中ipv6头部结构

3、icmpv6

3.1、icmpv6报头

3.2、Linux中icmpv6头部结构

4、代码分析

4.1、校验和

4.2、打包icmpv6报文

4.3、解包icmpv6报文

4.4、发送icmpv6报文

4.6、接收icmpv6报文

5、运行截图

6、工程文件

7、问题


1、ping的原理及实现

关于ping的原理,我在这篇博客里说明ping的原理及实现,可以参考

2、ipv6

2.1、ipv6报头

上图是ipv6携带icmpv6报文(图片来源网络)

MAC帧

从上图我们可以看出ipv6

  1. 数据链路层(L2)的type字段标识为0x86dd,表示承载的上层协议为ipv6(ipv4的表示符为0x0800)

  2. ipv6的固定报头结构(40字节)

ipv6报头结构
  • Version字段表示版本号,ipv6中该字段必须为6
  • Traffic Class字段含义类似IPv4中的TOS(Type Of Service)
  • Flow Label字段用于标识同一业务流的包
  • Payload Length字段用于标识有效的负载长度,只计算报头后面的扩展和数据部分的长度,不包括ipv6(40字节)的长度。该字段最多表示长度为64KB有效负载长度
  • Next Header字段用于标识当前报头(或者扩展报头)的下一个头部类型,每种扩展报头都有其对应的值。

扩展报头值对应的头部类型(来源网络)
  • Hop Limit字段与ipv4中的TTL字段,指定了报文可以有效转发的次数

2.2、Linux中ipv6头部结构

struct ip6_hdr
{union{struct ip6_hdrctl{uint32_t ip6_un1_flow;   /* 4 bits version, 8 bits TC, 20 bits flow-ID */uint16_t ip6_un1_plen;   /* payload length */uint8_t  ip6_un1_nxt;    /* next header */uint8_t  ip6_un1_hlim;   /* hop limit */} ip6_un1;uint8_t ip6_un2_vfc;       /* 4 bits version, top 4 bits tclass */} ip6_ctlun;struct in6_addr ip6_src;      /* source address */struct in6_addr ip6_dst;      /* destination address */
};
#define ip6_vfc   ip6_ctlun.ip6_un2_vfc//版本号以及通信类型
#define ip6_flow  ip6_ctlun.ip6_un1.ip6_un1_flow//流标签
#define ip6_plen  ip6_ctlun.ip6_un1.ip6_un1_plen//有效负载长度
#define ip6_nxt   ip6_ctlun.ip6_un1.ip6_un1_nxt//下一个报头
#define ip6_hlim  ip6_ctlun.ip6_un1.ip6_un1_hlim//跳数限制
#define ip6_hops  ip6_ctlun.ip6_un1.ip6_un1_hlim//跳数限制

3、icmpv6

3.1、icmpv6报头

icmp报头(来源网络)

类型说明在篇头博文中。

3.2、Linux中icmpv6头部结构

struct icmp6_hdr{uint8_t     icmp6_type;   /* type field */uint8_t     icmp6_code;   /* code field */uint16_t    icmp6_cksum;  /* checksum field */union{uint32_t  icmp6_un_data32[1]; /* type-specific field */uint16_t  icmp6_un_data16[2]; /* type-specific field */uint8_t   icmp6_un_data8[4];  /* type-specific field */} icmp6_dataun;};#define icmp6_data32    icmp6_dataun.icmp6_un_data32
#define icmp6_data16    icmp6_dataun.icmp6_un_data16
#define icmp6_data8     icmp6_dataun.icmp6_un_data8
#define icmp6_pptr      icmp6_data32[0]  /* parameter prob */
#define icmp6_mtu       icmp6_data32[0]  /* packet too big */
#define icmp6_id        icmp6_data16[0]  /* echo request/reply */
#define icmp6_seq       icmp6_data16[1]  /* echo request/reply */
#define icmp6_maxdelay  icmp6_data16[0]  /* mcast group membership */

4、代码分析

4.1、校验和

icmpv6的校验和有区别于icmpv4,icmpv6的校验和需要加入伪首部,先解释一下伪首部。

伪首部并非TCP&UDP数据报中实际的有效成分。伪首部是一个虚拟的数据结构,其中的信息是从数据报所在IP分组头的分组头中提取的,既不向下传送也不向上递交,而仅仅是为计算校验和。这样的校验和,既校验了TCP&UDP用户数据的源端口号和目的端口号以及TCP&UDP用户数据报的数据部分,又检验了IP数据报的源IP地址和目的地址。伪报头保证TCP&UDP数据单元到达正确的目的地址。因此,伪报头中包含IP地址并且作为计算校验和需要考虑的一部分。最终目的端根据伪报头和数据单元计算校验和以验证通信数据在传输过程中没有改变而且到达了正确的目的地址。伪首部更确切的说是校验和包含的—个96位的伪首标,是个理论上的值,只是理论上它位于TCP&UDP首标的前面。这个伪首标包含了源地址、目的地址、协议和TCP&UDP长度等字段,这使得TCP&UDP能够防止出现路由选择错误的数据段。这些信息由网际协议(IP)承载,通过TCP&UDP网络接口,在IP上运行的TCP&UDP调用参数或者结果中传递。

以上来自百度百科。简单来说,伪首部包括“源端地址”、“目的地址”、“报文长度(payload length)”、“下一报文值(next header)”

uint16_t checkSum(const struct in6_addr *src, const struct in6_addr *dst, const void *data, size_t len)
{union{uint32_t dword;uint16_t word[2];uint8_t byte[4];} temp;uint32_t sum = 0;for(int i = 0; i < 8; i++){sum += src->s6_addr16[i];sum += dst->s6_addr16[i];}temp.dword = htonl(len);sum += temp.word[0];sum += temp.word[1];temp.byte[0] = 0;temp.byte[1] = 0;temp.byte[2] = 0;temp.byte[3] = 58; // ICMPv6sum += temp.word[0];sum += temp.word[1];while (len > 1){sum += *((const uint16_t *)data);data = (const uint16_t *)data + 1;len -= 2;}if (len > 0)sum += *((const uint8_t *)data);while (sum >> 16 != 0)sum = (sum & 0xffff) + (sum >> 16);sum = ~sum;return (uint16_t)sum;
}

4.2、打包icmpv6报文

icmpv6可以使用让内核自动计算校验和,省去了自己获取主机ipv6地址的步骤(暂时不知道怎么获取发送接口的ipv6地址)

//显示的声明让内核自动计算校验和(虽然默认是打开的)
int sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);//获取校验和的位置
setsockopt(this->send_socket_fd, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));

打包icmp函数

int packIcmp(PingPacket *this, int length)
{PacketState *packet = this->stateArray->addPacket(this->stateArray, htons(this->send_seq));if(packet == NULL)return -1;struct icmp6_hdr *icmph = (struct icmp6_hdr *)this->send_buffer;icmph->icmp6_type = ICMP6_ECHO_REQUEST;icmph->icmp6_code  = 0;icmph->icmp6_cksum = 0;icmph->icmp6_id = this->pid;icmph->icmp6_seq = packet->seq;/*struct in6_addr host_addr;//ipv6的校验和需要目标地址和本机地址inet_pton(AF_INET6, "::1", &host_addr);icmph->icmp6_cksum = checksum(&host_addr, &this->dest.sin6_addr, icmph, length);//之前设置了让内核自动计算*/this->send_seq++;return 0;
}

4.3、解包icmpv6报文

直接使用icmpv6的原始套接字,我不知道如何获取ipv6报头,所以我直接获取数据链路层的MAC帧,绕开这个问题。

this->recv_socket_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));//创建数据链路层的原始套接字

解包icmp函数

PacketState *unpackIcmp(PingPacket *this, int length)
{static PacketState packet;struct ip6_hdr *ip6 = NULL;struct icmp6_hdr *icmp6 = NULL;struct timeval tv;gettimeofday(&tv, NULL);if(length == 118  && this->recv_buffer[12]==-122 && this->recv_buffer[13]==-35){ip6 = (struct ip6_hdr *)(this->recv_buffer + 14);if (ip6->ip6_nxt == 58){icmp6 = (struct icmp6_hdr *) (this->recv_buffer + 54);int icmp6_len = length - 54;if(this->checkSum(&ip6->ip6_src, &ip6->ip6_dst, icmp6, icmp6_len)) //计算校验和,检验包是否完整goto OUT;if (icmp6->icmp6_type == ICMP6_ECHO_REPLY && icmp6->icmp6_id == this->pid){int seq = icmp6->icmp6_seq;PacketState *have_packet = this->stateArray->findPacket(this->stateArray, seq);if(have_packet == NULL)goto OUT;have_packet->end_time = tv;have_packet->icmp_len = icmp6_len;have_packet->racv_icmp = icmp6;have_packet->recv_ip = ip6;memcpy(&packet, have_packet, sizeof(PacketState));this->stateArray->removePacket(this->stateArray, have_packet);return &packet;}}}
OUT:return NULL;
}

4.4、发送icmpv6报文

icmpv6报文发送类似于icmpv4

void sendIcmp(PingPacket *this)
{while(this->alive){if(pthread_mutex_lock(&this->mutex) == -1)continue;if(this->packIcmp(this, 64) != 0){printf("pack icmp error\n");sleep(1);continue;}if(pthread_mutex_unlock(&this->mutex) == -1){this->alive = false;continue;}if(sendto(this->send_socket_fd, this->send_buffer, 64, 0, (struct sockaddr *)&this->dest, sizeof(this->dest)) < 0){perror("sendto error");}sleep(1);}
}

4.6、接收icmpv6报文

void recvIcmp(PingPacket *this)
{while(this->alive){int size = recvfrom(this->recv_socket_fd, this->recv_buffer, sizeof(this->recv_buffer),0,NULL,NULL);if (errno == EINTR){perror("recvfrom error");continue;}//解包if(pthread_mutex_lock(&this->mutex) == -1)continue;PacketState *packet = this->unpackIcmp(this, size);if(pthread_mutex_unlock(&this->mutex) == -1){this->alive = false;continue;}if(packet != NULL){printInfo(packet);}}
}

5、运行截图

程序运行截图

6、工程文件

https://download.csdn.net/download/qq_33690566/12046459

7、问题

  1. 如何让程序选择发送接口,并获取本机ipv6地址
  2. 如何获取icmpv6原始套接字的ipv6头部

ping6简单代码实现相关推荐

  1. 机器学习简单代码示例

    机器学习简单代码示例 //在gcc-4.7.2下编译通过. //命令行:g++ -Wall -ansi -O2 test.cpp -o test #include <iostream> u ...

  2. tensorflow笔记:流程,概念和简单代码注释

    tensorflow是google在2015年开源的深度学习框架,可以很方便的检验算法效果.这两天看了看官方的tutorial,极客学院的文档,以及综合tensorflow的源码,把自己的心得整理了一 ...

  3. 使用vb获取网页源文件并保存的简单代码

    使用vb获取网页源文件并保存的简单代码 Private Sub Command1_Click() Inet1.URL = "HTTP://www.wangjianran.com/" ...

  4. python代码大全表解释-Python中顺序表的实现简单代码分享

    顺序表python版的实现(部分功能未实现) 结果展示: 代码示例: #!/usr/bin/env python # -*- coding:utf-8 -*- class SeqList(object ...

  5. python导入txt文件并绘图-Python实现读取txt文件并画三维图简单代码示例

    记忆力差的孩子得勤做笔记! 刚接触python,最近又需要画一个三维图,然后就找了一大堆资料,看的人头昏脑胀的,今天终于解决了!好了,废话不多说,直接上代码! #由三个一维坐标画三维散点 #codin ...

  6. python读取txt文件代码-Python实现读取txt文件并画三维图简单代码示例

    记忆力差的孩子得勤做笔记! 刚接触python,最近又需要画一个三维图,然后就找了一大堆资料,看的人头昏脑胀的,今天终于解决了!好了,废话不多说,直接上代码! #由三个一维坐标画三维散点 #codin ...

  7. CV之IS:利用pixellib库基于deeplabv3_xception模型对《庆余年》片段实现语义分割/图像分割简单代码全实现

    CV之IS:利用pixellib库基于deeplabv3_xception模型对<庆余年>片段实现语义分割/图像分割简单代码全实现 目录 利用pixellib库基于deeplabv3_xc ...

  8. CV之IS:利用pixellib库基于mask_rcnn_coco模型对《庆余年》片段实现实例分割简单代码全实现

    CV之IS:利用pixellib库基于mask_rcnn_coco模型对<庆余年>片段实现实例分割简单代码全实现 目录 利用pixellib库的instance_segmentation函 ...

  9. php简单代码大全,征集常用的PHP简单代码

    收集常用的PHP简单代码 对于日常工作中整理出来的某些功能做个简单梳理: ? 1. 短链生成算法 function code62($x) { $show = ''; while($x > 0) ...

最新文章

  1. Matlab图像处理教程
  2. Spring配置C3P0开源连接池
  3. SRS流媒体服务器——Forward集群搭建和源码分析
  4. 项目在云服务器上的绝对路径,服务器上的绝对路径怎么写
  5. WM有约(五):部署应用程序
  6. 【TWVRP】基于matlab遗传算法求解带时间窗的车辆路径问题【含Matlab源码 002期】
  7. [ openwrt ] 添加一个通过GPIO控制的LED
  8. 最新CISP模拟考试题库及答案(二)
  9. Java枚举类与常用方法
  10. Hanoi Tower Troubles Again! ZOJ - 1239
  11. (二)PUN 2基本教程
  12. 推荐算法:为什么浏览器总是知道我喜欢的内容?
  13. mysql escape 函数_mysql_escape_string()函数用法分析
  14. pandas取出符合条件的某单元格的值
  15. java抽取word,pdf的四种武器
  16. dede修改mysql,织梦教程:如何修改dedecms系统数据库表前缀?
  17. (java)使用createNewFile提示系统找不到指定路径
  18. html中dr标签的作用是什么,DR是什么意思?关于DR的意义
  19. Fedora 14 安装手记
  20. Excel 冻结指定行 / 列

热门文章

  1. 教你在Mac系统下双开微信的方法
  2. 第3章 细胞的结构和功能
  3. 三菱变频器MODBUS通讯设置哪些参数
  4. android 权限管理 主动防御,ROM刷机必备LBE安全大师杜绝病毒隐患
  5. 设计 VS 架构 VS 框架
  6. Java(if, else,else if)
  7. 红淘客APP排行榜之花卷云淘客app系统
  8. 电赛分几种_参加电赛需要具备哪些知识呢?
  9. 公司注册资金需要实缴吗?实缴和认缴有什么区别?
  10. java高级 上机模拟试题_2016计算机二级java考试上机模拟试题及答案