实现ping ipv6

前言

由于工作中有个需求需要判断ipv6网络是否连通,第一个想法就是通过ping一个ipv6的地址或者域名,来判断当前ipv6网络是否可用;通过阅读busybox的ping的源码,实现了一个简单的接口

ping的原理

ping的原理主要涉及ICMP协议,网上的资料很多,这里不过讲述,直接上代码

实现代码

1. 初始化socket

int ipv6_socket_init(void)
{
    int socket_fd = -1;
    int sockopt;

    socket_fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
    if (socket_fd < 0) {
        printf("Error, create socket failed, errmsg=[%s]\n", strerror(errno));
        return NT_FAILED;
    }

    /* 开启内核自动计算校验和 */
    sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
	setsockopt(socket_fd, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));

    return socket_fd;
}

2. 反初始化

void ipv6_socket_uninit(int fd)
{
    if (fd < 0) return ;
    
    close(fd);
}

3. 主要的ping接口

int ipv6_ping(int fd, const char *hostname, int timeout_ms)
{
    struct sockaddr_in6 dest;
    struct in6_addr inaddr;
    struct icmp6_hdr *pkt;
    int ret = -1;
    char ip_str[50] = {0};

    memset(&dest, 0, sizeof(struct sockaddr_in6));
    dest.sin6_family = AF_INET6;

    if (inet_pton(AF_INET6, hostname, &inaddr) <= 0) {
        struct addrinfo hints;
        struct addrinfo *result;

        memset(&hints, 0, sizeof(struct addrinfo));
        hints.ai_family = AF_INET6;
        hints.ai_socktype = SOCK_RAW;
        hints.ai_flags = AI_ALL;
        hints.ai_protocol = IPPROTO_ICMPV6;
        if(getaddrinfo(hostname, NULL, &hints, &result) != 0)
            return NT_FAILED;
        
        struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)result->ai_addr;
        memcpy(&dest.sin6_addr, &ipv6->sin6_addr, sizeof(ipv6->sin6_addr));
        freeaddrinfo(result);

        inet_ntop(AF_INET6, &ipv6->sin6_addr, ip_str, sizeof(ip_str));
        // printf("[%s] get ip [%s]\n", hostname, ip_str);
    }
    else {
        memcpy(ip_str, hostname, sizeof(ip_str)-1);
        memcpy(&dest.sin6_addr, &inaddr, sizeof(inaddr));
    }

    static unsigned short ipv6_seq = 0;
    unsigned short curr_pid = getpid();
    char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN] = {0};
    pkt = (struct icmp6_hdr *)packet;
    pkt->icmp6_type = ICMP6_ECHO_REQUEST;
	pkt->icmp6_id = curr_pid;
    pkt->icmp6_seq = ipv6_seq++;

    ret = sendto(fd, packet, DEFDATALEN + sizeof(struct icmp6_hdr), 0, (struct sockaddr*)&dest, sizeof(dest));
    if (ret < 0) {
        printf("Send icmp failed! ip=[%s] errmsg=[%s]\n", ip_str, strerror(errno));
        return NT_FAILED;
    }

    struct timeval interval = { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &interval, sizeof(struct timeval));

    time_t start_ts = time(NULL);
    /* listen for replies */
	while (1) {
        if (time(NULL) > start_ts + start_ts / 1000) {
            printf("recv icmp reply timeout!\n");
            return NT_TIMEOUT;
        }

		ret = recv(fd, packet, sizeof(packet), 0);
		if (ret < 0) {
			if (errno != EINTR)
				printf("recv error\n");
			continue;
		}
		if (ret >= ICMP_MINLEN) {	/* icmp6_hdr */
			if (pkt->icmp6_id != curr_pid)
				continue; /* not our ping */
			if (pkt->icmp6_type == ICMP6_ECHO_REPLY) {
                printf("ping [%s] success\n", ip_str);
				return NT_SUCCESS;
            }
		}
	}
}

结语

以上就是一个简单的ping ipv6的实现,如有错误,欢迎指出。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值