发布时间:2025-12-10 19:31:29 浏览次数:16
ping6简单代码实现「建议收藏」目录1、ping的原理及实现2、ipv62.1、ipv6报头2.2、Linux中ipv6头部结构3、icmpv63.1、icmpv6报头3.2、Linux中icmpv6头部结构4、代码分析4.1、校验和4.2、打包icmpv6报文4.3、解包icmpv6报文4.4、发送icmpv6报文4.6、接收icmpv6报文5、运行截图6、工程文…
目录
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、问题
关于ping的原理,我在这篇博客里说明ping的原理及实现,可以参考
从上图我们可以看出ipv6
数据链路层(L2)的type字段标识为0x86dd,表示承载的上层协议为ipv6(ipv4的表示符为0x0800)
ipv6的固定报头结构(40字节)
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//跳数限制 是否还在为Ide开发工具频繁失效而烦恼,来吧关注以下公众号获取最新激活方式。亲测可用!
【正版授权,激活自己账号】:Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】:官方授权 正版激活 自己使用,支持Jetbrains家族下所有IDE…
类型说明在篇头博文中。
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 */ 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调用参数或者结果中传递。
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; // ICMPv6 sum += 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;} 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;} 直接使用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;} 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); }} 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); } }} https://download.csdn.net/download/qq_33690566/12046459