ping6(ping6简单代码实现「建议收藏」)

发布时间:2025-12-10 19:31:29 浏览次数:16

ping6简单代码实现「建议收藏」-平6路公交车路线

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、问题


1、ping的原理及实现

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

2、ipv6

2.1、ipv6报头

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

从上图我们可以看出ipv6

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

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//跳数限制

是否还在为Ide开发工具频繁失效而烦恼,来吧关注以下公众号获取最新激活方式。亲测可用!

为防止网络爬虫,请关注公众号回复”口令”

激活idea 激活CLion DataGrip DataSpell dotCover dotMemory dotTrace GoLand PhpStorm PyCharm ReSharper ReShaC++ Rider RubyMine WebStorm 全家桶 刷新

【正版授权,激活自己账号】:Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】:官方授权 正版激活 自己使用,支持Jetbrains家族下所有IDE…

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调用参数或者结果中传递。

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;}

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、问题

如何让程序选择发送接口,并获取本机ipv6地址 如何获取icmpv6原始套接字的ipv6头部
需要做网站?需要网络推广?欢迎咨询客户经理 13272073477