Path: blob/master/src/java.base/unix/native/libnet/Inet6AddressImpl.c
41119 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24#include <ctype.h>25#include <errno.h>26#include <stdlib.h>27#include <string.h>28#include <sys/time.h>29#include <sys/types.h>30#include <netinet/in.h>31#include <netinet/icmp6.h>3233#if defined(_ALLBSD_SOURCE)34#include <ifaddrs.h>35#include <net/if.h>36#endif3738#include "net_util.h"3940#include "java_net_InetAddress.h"41#include "java_net_Inet4AddressImpl.h"42#include "java_net_Inet6AddressImpl.h"4344#define SET_NONBLOCKING(fd) { \45int flags = fcntl(fd, F_GETFL); \46flags |= O_NONBLOCK; \47fcntl(fd, F_SETFL, flags); \48}4950/*51* Inet6AddressImpl52*/5354/*55* Class: java_net_Inet6AddressImpl56* Method: getLocalHostName57* Signature: ()Ljava/lang/String;58*/59JNIEXPORT jstring JNICALL60Java_java_net_Inet6AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {61char hostname[NI_MAXHOST + 1];6263hostname[0] = '\0';64if (gethostname(hostname, sizeof(hostname)) != 0) {65strcpy(hostname, "localhost");66} else {67// make sure string is null-terminated68hostname[NI_MAXHOST] = '\0';69}70return (*env)->NewStringUTF(env, hostname);71}7273#if defined(MACOSX)74/* also called from Inet4AddressImpl.c */75__private_extern__ jobjectArray76lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6)77{78jobjectArray result = NULL;79char myhostname[NI_MAXHOST + 1];80struct ifaddrs *ifa = NULL;81int familyOrder = 0;82int count = 0, i, j;83int addrs4 = 0, addrs6 = 0, numV4Loopbacks = 0, numV6Loopbacks = 0;84jboolean includeLoopback = JNI_FALSE;85jobject name;8687initInetAddressIDs(env);88JNU_CHECK_EXCEPTION_RETURN(env, NULL);8990/* If the requested name matches this host's hostname, return IP addresses91* from all attached interfaces. (#2844683 et al) This prevents undesired92* PPP dialup, but may return addresses that don't actually correspond to93* the name (if the name actually matches something in DNS etc.94*/95myhostname[0] = '\0';96if (gethostname(myhostname, sizeof(myhostname)) == -1) {97/* Something went wrong, maybe networking is not setup? */98return NULL;99}100myhostname[NI_MAXHOST] = '\0';101102if (strcmp(myhostname, hostname) != 0) {103// Non-self lookup104return NULL;105}106107if (getifaddrs(&ifa) != 0) {108NET_ThrowNew(env, errno, "Can't get local interface addresses");109return NULL;110}111112name = (*env)->NewStringUTF(env, hostname);113if (name == NULL) {114freeifaddrs(ifa);115return NULL;116}117118/* Iterate over the interfaces, and total up the number of IPv4 and IPv6119* addresses we have. Also keep a count of loopback addresses. We need to120* exclude them in the normal case, but return them if we don't get an IP121* address.122*/123struct ifaddrs *iter = ifa;124while (iter) {125if (iter->ifa_addr != NULL) {126int family = iter->ifa_addr->sa_family;127if (iter->ifa_name[0] != '\0') {128jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK;129if (family == AF_INET) {130addrs4++;131if (isLoopback) numV4Loopbacks++;132} else if (family == AF_INET6 && includeV6) {133addrs6++;134if (isLoopback) numV6Loopbacks++;135} // else we don't care, e.g. AF_LINK136}137}138iter = iter->ifa_next;139}140141if (addrs4 == numV4Loopbacks && addrs6 == numV6Loopbacks) {142// We don't have a real IP address, just loopback. We need to include143// loopback in our results.144includeLoopback = JNI_TRUE;145}146147/* Create and fill the Java array. */148int arraySize = addrs4 + addrs6 -149(includeLoopback ? 0 : (numV4Loopbacks + numV6Loopbacks));150result = (*env)->NewObjectArray(env, arraySize, ia_class, NULL);151if (!result) goto done;152153if ((*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID)) {154i = includeLoopback ? addrs6 : (addrs6 - numV6Loopbacks);155j = 0;156} else {157i = 0;158j = includeLoopback ? addrs4 : (addrs4 - numV4Loopbacks);159}160161// Now loop around the ifaddrs162iter = ifa;163while (iter != NULL) {164if (iter->ifa_addr != NULL) {165jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK;166int family = iter->ifa_addr->sa_family;167168if (iter->ifa_name[0] != '\0' &&169(family == AF_INET || (family == AF_INET6 && includeV6)) &&170(!isLoopback || includeLoopback))171{172int port;173int index = (family == AF_INET) ? i++ : j++;174jobject o = NET_SockaddrToInetAddress(env,175(SOCKETADDRESS *)iter->ifa_addr, &port);176if (!o) {177freeifaddrs(ifa);178if (!(*env)->ExceptionCheck(env))179JNU_ThrowOutOfMemoryError(env, "Object allocation failed");180return NULL;181}182setInetAddress_hostName(env, o, name);183if ((*env)->ExceptionCheck(env))184goto done;185(*env)->SetObjectArrayElement(env, result, index, o);186(*env)->DeleteLocalRef(env, o);187}188}189iter = iter->ifa_next;190}191192done:193freeifaddrs(ifa);194195return result;196}197#endif198199/*200* Class: java_net_Inet6AddressImpl201* Method: lookupAllHostAddr202* Signature: (Ljava/lang/String;)[[B203*/204JNIEXPORT jobjectArray JNICALL205Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,206jstring host) {207jobjectArray ret = NULL;208const char *hostname;209int error = 0;210struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,211*iterator;212213initInetAddressIDs(env);214JNU_CHECK_EXCEPTION_RETURN(env, NULL);215216if (IS_NULL(host)) {217JNU_ThrowNullPointerException(env, "host argument is null");218return NULL;219}220hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);221CHECK_NULL_RETURN(hostname, NULL);222223// try once, with our static buffer224memset(&hints, 0, sizeof(hints));225hints.ai_flags = AI_CANONNAME;226hints.ai_family = AF_UNSPEC;227228error = getaddrinfo(hostname, NULL, &hints, &res);229230if (error) {231#if defined(MACOSX)232// if getaddrinfo fails try getifaddrs233ret = lookupIfLocalhost(env, hostname, JNI_TRUE);234if (ret != NULL || (*env)->ExceptionCheck(env)) {235goto cleanupAndReturn;236}237#endif238// report error239NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error);240goto cleanupAndReturn;241} else {242int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0,243inet6Index = 0, originalIndex = 0;244int addressPreference =245(*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);;246iterator = res;247while (iterator != NULL) {248// skip duplicates249int skip = 0;250struct addrinfo *iteratorNew = resNew;251while (iteratorNew != NULL) {252if (iterator->ai_family == iteratorNew->ai_family &&253iterator->ai_addrlen == iteratorNew->ai_addrlen) {254if (iteratorNew->ai_family == AF_INET) { /* AF_INET */255struct sockaddr_in *addr1, *addr2;256addr1 = (struct sockaddr_in *)iterator->ai_addr;257addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;258if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {259skip = 1;260break;261}262} else {263int t;264struct sockaddr_in6 *addr1, *addr2;265addr1 = (struct sockaddr_in6 *)iterator->ai_addr;266addr2 = (struct sockaddr_in6 *)iteratorNew->ai_addr;267268for (t = 0; t < 16; t++) {269if (addr1->sin6_addr.s6_addr[t] !=270addr2->sin6_addr.s6_addr[t]) {271break;272}273}274if (t < 16) {275iteratorNew = iteratorNew->ai_next;276continue;277} else {278skip = 1;279break;280}281}282} else if (iterator->ai_family != AF_INET &&283iterator->ai_family != AF_INET6) {284// we can't handle other family types285skip = 1;286break;287}288iteratorNew = iteratorNew->ai_next;289}290291if (!skip) {292struct addrinfo *next293= (struct addrinfo *)malloc(sizeof(struct addrinfo));294if (!next) {295JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");296ret = NULL;297goto cleanupAndReturn;298}299memcpy(next, iterator, sizeof(struct addrinfo));300next->ai_next = NULL;301if (resNew == NULL) {302resNew = next;303} else {304last->ai_next = next;305}306last = next;307i++;308if (iterator->ai_family == AF_INET) {309inetCount++;310} else if (iterator->ai_family == AF_INET6) {311inet6Count++;312}313}314iterator = iterator->ai_next;315}316317// allocate array - at this point i contains the number of addresses318ret = (*env)->NewObjectArray(env, i, ia_class, NULL);319if (IS_NULL(ret)) {320/* we may have memory to free at the end of this */321goto cleanupAndReturn;322}323324if (addressPreference == java_net_InetAddress_PREFER_IPV6_VALUE) {325inetIndex = inet6Count;326inet6Index = 0;327} else if (addressPreference == java_net_InetAddress_PREFER_IPV4_VALUE) {328inetIndex = 0;329inet6Index = inetCount;330} else if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {331inetIndex = inet6Index = originalIndex = 0;332}333334iterator = resNew;335while (iterator != NULL) {336if (iterator->ai_family == AF_INET) {337jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);338if (IS_NULL(iaObj)) {339ret = NULL;340goto cleanupAndReturn;341}342setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));343if ((*env)->ExceptionCheck(env))344goto cleanupAndReturn;345setInetAddress_hostName(env, iaObj, host);346if ((*env)->ExceptionCheck(env))347goto cleanupAndReturn;348(*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);349inetIndex++;350} else if (iterator->ai_family == AF_INET6) {351jint scope = 0;352jboolean ret1;353jobject iaObj = (*env)->NewObject(env, ia6_class, ia6_ctrID);354if (IS_NULL(iaObj)) {355ret = NULL;356goto cleanupAndReturn;357}358ret1 = setInet6Address_ipaddress(env, iaObj, (char *)&(((struct sockaddr_in6*)iterator->ai_addr)->sin6_addr));359if (ret1 == JNI_FALSE) {360ret = NULL;361goto cleanupAndReturn;362}363scope = ((struct sockaddr_in6 *)iterator->ai_addr)->sin6_scope_id;364if (scope != 0) { // zero is default value, no need to set365setInet6Address_scopeid(env, iaObj, scope);366}367setInetAddress_hostName(env, iaObj, host);368if ((*env)->ExceptionCheck(env))369goto cleanupAndReturn;370(*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);371inet6Index++;372}373if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {374originalIndex++;375inetIndex = inet6Index = 0;376}377iterator = iterator->ai_next;378}379}380cleanupAndReturn:381JNU_ReleaseStringPlatformChars(env, host, hostname);382while (resNew != NULL) {383last = resNew;384resNew = resNew->ai_next;385free(last);386}387if (res != NULL) {388freeaddrinfo(res);389}390return ret;391}392393/*394* Class: java_net_Inet6AddressImpl395* Method: getHostByAddr396* Signature: ([B)Ljava/lang/String;397*398* Theoretically the UnknownHostException could be enriched with gai error399* information. But as it is silently ignored anyway, there's no need for this.400* It's only important that either a valid hostname is returned or an401* UnknownHostException is thrown.402*/403JNIEXPORT jstring JNICALL404Java_java_net_Inet6AddressImpl_getHostByAddr(JNIEnv *env, jobject this,405jbyteArray addrArray) {406jstring ret = NULL;407char host[NI_MAXHOST + 1];408int len = 0;409jbyte caddr[16];410SOCKETADDRESS sa;411412memset((void *)&sa, 0, sizeof(SOCKETADDRESS));413414// construct a sockaddr_in structure (AF_INET or AF_INET6)415if ((*env)->GetArrayLength(env, addrArray) == 4) {416jint addr;417(*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);418addr = ((caddr[0] << 24) & 0xff000000);419addr |= ((caddr[1] << 16) & 0xff0000);420addr |= ((caddr[2] << 8) & 0xff00);421addr |= (caddr[3] & 0xff);422sa.sa4.sin_addr.s_addr = htonl(addr);423sa.sa4.sin_family = AF_INET;424len = sizeof(struct sockaddr_in);425} else {426(*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);427memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr));428sa.sa6.sin6_family = AF_INET6;429len = sizeof(struct sockaddr_in6);430}431432if (getnameinfo(&sa.sa, len, host, sizeof(host), NULL, 0, NI_NAMEREQD)) {433JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);434} else {435ret = (*env)->NewStringUTF(env, host);436if (ret == NULL) {437JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);438}439}440441return ret;442}443444/**445* ping implementation using tcp port 7 (echo)446*/447static jboolean448tcp_ping6(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout,449jint ttl)450{451jint fd;452int connect_rv = -1;453454// open a TCP socket455fd = socket(AF_INET6, SOCK_STREAM, 0);456if (fd == -1) {457// note: if you run out of fds, you may not be able to load458// the exception class, and get a NoClassDefFoundError instead.459NET_ThrowNew(env, errno, "Can't create socket");460return JNI_FALSE;461}462463// set TTL464if (ttl > 0) {465setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));466}467468// A network interface was specified, so let's bind to it.469if (netif != NULL) {470if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) {471NET_ThrowNew(env, errno, "Can't bind socket");472close(fd);473return JNI_FALSE;474}475}476477// Make the socket non blocking so we can use select/poll.478SET_NONBLOCKING(fd);479480sa->sa6.sin6_port = htons(7); // echo port481connect_rv = NET_Connect(fd, &sa->sa, sizeof(struct sockaddr_in6));482483// connection established or refused immediately, either way it means484// we were able to reach the host!485if (connect_rv == 0 || errno == ECONNREFUSED) {486close(fd);487return JNI_TRUE;488}489490switch (errno) {491case ENETUNREACH: // Network Unreachable492case EAFNOSUPPORT: // Address Family not supported493case EADDRNOTAVAIL: // address is not available on the remote machine494#if defined(__linux__) || defined(_AIX)495// On some Linux versions, when a socket is bound to the loopback496// interface, connect will fail and errno will be set to EINVAL497// or EHOSTUNREACH. When that happens, don't throw an exception,498// just return false.499case EINVAL:500case EHOSTUNREACH: // No route to host501#endif502close(fd);503return JNI_FALSE;504case EINPROGRESS: // this is expected as we'll probably have to wait505break;506default:507NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",508"connect failed");509close(fd);510return JNI_FALSE;511}512513timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);514if (timeout >= 0) {515// connection has been established, check for error condition516socklen_t optlen = (socklen_t)sizeof(connect_rv);517if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,518&optlen) <0)519{520connect_rv = errno;521}522if (connect_rv == 0 || connect_rv == ECONNREFUSED) {523close(fd);524return JNI_TRUE;525}526}527close(fd);528return JNI_FALSE;529}530531/**532* ping implementation.533* Send an ICMP_ECHO_REQUEST packet every second until either the timeout534* expires or an answer is received.535* Returns true if an ECHO_REPLY is received, false otherwise.536*/537static jboolean538ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,539jint timeout, jint ttl)540{541jint n, size = 60 * 1024, tmout2, seq = 1;542socklen_t len;543unsigned char sendbuf[1500], recvbuf[1500];544struct icmp6_hdr *icmp6;545struct sockaddr_in6 sa_recv;546jchar pid;547struct timeval tv;548size_t plen = sizeof(struct icmp6_hdr) + sizeof(tv);549550#if defined(__linux__)551/**552* For some strange reason, the linux kernel won't calculate the553* checksum of ICMPv6 packets unless you set this socket option554*/555int csum_offset = 2;556setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int));557#endif558559setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));560561// sets the ttl (max number of hops)562if (ttl > 0) {563setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));564}565566// a specific interface was specified, so let's bind the socket567// to that interface to ensure the requests are sent only through it.568if (netif != NULL) {569if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) {570NET_ThrowNew(env, errno, "Can't bind socket");571close(fd);572return JNI_FALSE;573}574}575576// icmp_id is a 16 bit data type, therefore down cast the pid577pid = (jchar)getpid();578579// Make the socket non blocking so we can use select580SET_NONBLOCKING(fd);581do {582// create the ICMP request583icmp6 = (struct icmp6_hdr *)sendbuf;584icmp6->icmp6_type = ICMP6_ECHO_REQUEST;585icmp6->icmp6_code = 0;586// let's tag the ECHO packet with our pid so we can identify it587icmp6->icmp6_id = htons(pid);588icmp6->icmp6_seq = htons(seq);589seq++;590gettimeofday(&tv, NULL);591memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv));592icmp6->icmp6_cksum = 0;593// send it594n = sendto(fd, sendbuf, plen, 0, &sa->sa, sizeof(struct sockaddr_in6));595if (n < 0 && errno != EINPROGRESS) {596#if defined(__linux__)597/*598* On some Linux versions, when a socket is bound to the loopback599* interface, sendto will fail and errno will be set to600* EINVAL or EHOSTUNREACH. When that happens, don't throw an601* exception, just return false.602*/603if (errno != EINVAL && errno != EHOSTUNREACH) {604NET_ThrowNew(env, errno, "Can't send ICMP packet");605}606#else607NET_ThrowNew(env, errno, "Can't send ICMP packet");608#endif609close(fd);610return JNI_FALSE;611}612613tmout2 = timeout > 1000 ? 1000 : timeout;614do {615tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);616if (tmout2 >= 0) {617len = sizeof(sa_recv);618n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0,619(struct sockaddr *)&sa_recv, &len);620// check if we received enough data621if (n < (jint)sizeof(struct icmp6_hdr)) {622continue;623}624icmp6 = (struct icmp6_hdr *)recvbuf;625// We did receive something, but is it what we were expecting?626// I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and627// from the host that we are trying to determine is reachable.628if (icmp6->icmp6_type == ICMP6_ECHO_REPLY &&629(ntohs(icmp6->icmp6_id) == pid))630{631if (NET_IsEqual((jbyte *)&sa->sa6.sin6_addr,632(jbyte *)&sa_recv.sin6_addr)) {633close(fd);634return JNI_TRUE;635} else if (NET_IsZeroAddr((jbyte *)&sa->sa6.sin6_addr)) {636close(fd);637return JNI_TRUE;638}639}640}641} while (tmout2 > 0);642timeout -= 1000;643} while (timeout > 0);644close(fd);645return JNI_FALSE;646}647648/*649* Class: java_net_Inet6AddressImpl650* Method: isReachable0651* Signature: ([BII[BII)Z652*/653JNIEXPORT jboolean JNICALL654Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,655jbyteArray addrArray, jint scope,656jint timeout, jbyteArray ifArray,657jint ttl, jint if_scope)658{659jbyte caddr[16];660jint sz, fd;661SOCKETADDRESS sa, inf, *netif = NULL;662663// If IPv6 is not enabled, then we can't reach an IPv6 address, can we?664// Actually, we probably shouldn't even get here.665if (!ipv6_available()) {666return JNI_FALSE;667}668669// If it's an IPv4 address, ICMP won't work with IPv4 mapped address,670// therefore, let's delegate to the Inet4Address method.671sz = (*env)->GetArrayLength(env, addrArray);672if (sz == 4) {673return Java_java_net_Inet4AddressImpl_isReachable0(env, this,674addrArray, timeout,675ifArray, ttl);676}677678// load address to SOCKETADDRESS679memset((char *)caddr, 0, 16);680(*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);681memset((char *)&sa, 0, sizeof(SOCKETADDRESS));682memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr));683sa.sa6.sin6_family = AF_INET6;684if (scope > 0) {685sa.sa6.sin6_scope_id = scope;686}687688// load network interface address to SOCKETADDRESS, if specified689if (!(IS_NULL(ifArray))) {690memset((char *)caddr, 0, 16);691(*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr);692memset((char *)&inf, 0, sizeof(SOCKETADDRESS));693memcpy((void *)&inf.sa6.sin6_addr, caddr, sizeof(struct in6_addr));694inf.sa6.sin6_family = AF_INET6;695inf.sa6.sin6_scope_id = if_scope;696netif = &inf;697}698699// Let's try to create a RAW socket to send ICMP packets.700// This usually requires "root" privileges, so it's likely to fail.701fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);702if (fd == -1) {703return tcp_ping6(env, &sa, netif, timeout, ttl);704} else {705// It didn't fail, so we can use ICMP_ECHO requests.706return ping6(env, fd, &sa, netif, timeout, ttl);707}708}709710711