< prev index next >

src/java.base/unix/native/libnet/NetworkInterface.c

Print this page
rev 14637 : Fix for incorrectly reported network mask and broadcast address on Linux,
when there are more then on IP address on an interface.
Fix is implemented using getifaddrs.

@@ -47,10 +47,11 @@
 #if defined(__linux__)
 #include <sys/ioctl.h>
 #include <bits/ioctls.h>
 #include <sys/utsname.h>
 #include <stdio.h>
+#include <ifaddrs.h>
 #endif
 
 #if defined(_AIX)
 #include <sys/ioctl.h>
 #include <netinet/in6_var.h>

@@ -134,19 +135,23 @@
 static netif  *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs);
 #endif
 
 static netif  *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
                      struct sockaddr *ifr_addrP, int family, short prefix);
+static netif  *addif2(JNIEnv *env, int sock, const char *if_name, netif *ifs, 
+                      struct sockaddr *ifr_addrP, int family, short prefix,
+                      struct sockaddr *brdcast);
 static void    freeif(netif *ifs);
 
 static int     openSocket(JNIEnv *env, int proto);
 static int     openSocketWithFallback(JNIEnv *env, const char *ifname);
 
 
 static struct  sockaddr *getBroadcast(JNIEnv *env, int sock, const char *name,
                                       struct sockaddr *brdcast_store);
 static short   getSubnet(JNIEnv *env, int sock, const char *ifname);
+static short   getSubnetFromMaskAddr(const struct sockaddr_in * netmask_addrP);
 static int     getIndex(int sock, const char *ifname);
 
 static int     getFlags(int sock, const char *ifname, int *flags);
 static int     getMacAddress(JNIEnv *env, int sock,  const char *ifname,
                              const struct in_addr *addr, unsigned char *buf);

@@ -861,12 +866,13 @@
         free(currif);
         currif = ifs;
     }
 }
 
-netif *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
-             struct sockaddr *ifr_addrP, int family, short prefix)
+netif *addif2(JNIEnv *env, int sock, const char *if_name, netif *ifs,
+             struct sockaddr *ifr_addrP, int family, short prefix,
+             struct sockaddr *brdcast)
 {
     netif *currif = ifs, *parent;
     netaddr *addrP;
 
 #ifdef LIFNAMSIZ

@@ -913,19 +919,25 @@
     addrP->next = 0;
     if (family == AF_INET) {
        // Deal with broadcast addr & subnet mask
        struct sockaddr *brdcast_to =
               (struct sockaddr *) ((char *)addrP + sizeof(netaddr) + addr_size);
+#if defined(__linux__)
+       if (brdcast != NULL) {
+           addrP->brdcast = memcpy(brdcast_to, brdcast, addr_size);
+       }
+#else
        addrP->brdcast = getBroadcast(env, sock, name, brdcast_to);
        if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
            return ifs;
        }
        if ((mask = getSubnet(env, sock, name)) != -1) {
            addrP->mask = mask;
        } else if((*env)->ExceptionCheck(env)) {
            return ifs;
        }
+#endif
      }
 
     // Deal with virtual interface with colon notation e.g. eth0:1
     name_colonP = strchr(name, ':');
     if (name_colonP != NULL) {

@@ -1021,10 +1033,16 @@
     }
 
     return ifs;
 }
 
+netif *addif(JNIEnv *env, int sock, const char * if_name,
+             netif *ifs, struct sockaddr* ifr_addrP, int family,
+             short prefix) {
+    return addif2(env, sock, if_name, ifs, ifr_addrP, family, prefix, NULL);
+}
+
 /*
  * Opens a socket for further ioct calls. proto is one of AF_INET or AF_INET6.
  */
 static int openSocket(JNIEnv *env, int proto) {
     int sock;

@@ -1040,10 +1058,69 @@
     }
 
     return sock;
 }
 
+/*
+ * Count the number of non-zero bits
+ */
+static inline short mask2subnet(uint32_t mask) {
+    short ret = 0;
+
+    for (; mask; mask <<= 1) {
+        ++ret;
+    }
+
+    return ret;
+}
+
+static inline short getSubnetFromMaskAddr(const struct sockaddr_in * netmask_addrP) {
+    return (netmask_addrP != NULL) ? mask2subnet(ntohl(netmask_addrP->sin_addr.s_addr)) : 0;
+}
+
+#if defined(__linux__) || defined(_ALLBSD_SOURCE)
+/*
+ * Enumerates and returns all IPv4 interfaces
+ */
+static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
+    struct ifaddrs *ifa, *origifa;
+    struct sockaddr *ba = NULL;
+    short subnet = 0;
+
+    if (getifaddrs(&origifa) != 0) {
+        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
+                         "getifaddrs() function failed");
+        return ifs;
+    }
+
+    for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
+        /* Skip non-AF_INET entries */
+        if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET) {
+            continue;
+        }
+
+#if defined(__linux__)
+        ba = (ifa->ifa_flags & IFF_BROADCAST) ? ifa->ifa_broadaddr : NULL;
+        subnet = getSubnetFromMaskAddr(((const struct sockaddr_in *)ifa->ifa_netmask));
+#endif
+
+        /* Add to the list */
+        ifs = addif2(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr, AF_INET, subnet, ba);
+
+        /* If an exception occurred then free the list */
+        if ((*env)->ExceptionOccurred(env)) {
+            freeifaddrs(origifa);
+            freeif(ifs);
+            return NULL;
+        }
+    }
+
+    /* Free socket and buffer */
+    freeifaddrs(origifa);
+    return ifs;
+}
+#endif /* __linux__ || _ALLBSD_SOURCE */
 
 /** Linux, AIX **/
 #if defined(__linux__) || defined(_AIX)
 
 #ifdef AF_INET6

@@ -1079,55 +1156,42 @@
 static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     return openSocket(env,AF_INET);
 }
 #endif
 
+#if defined(_AIX)
 static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
     struct ifconf ifc;
     struct ifreq *ifreqP;
     char *buf = NULL;
     int numifs;
     unsigned i;
     int siocgifconfRequest = SIOCGIFCONF;
 
-#if defined(__linux__)
-    // need to do a dummy SIOCGIFCONF to determine the buffer size.
-    // SIOCGIFCOUNT doesn't work
-    ifc.ifc_buf = NULL;
-    if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
-                                     "ioctl SIOCGIFCONF failed");
-        return ifs;
-    }
-#elif defined(_AIX)
     ifc.ifc_buf = NULL;
     if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
                                      "ioctl SIOCGSIZIFCONF failed");
         return ifs;
     }
-#endif /* __linux__ */
 
     CHECKED_MALLOC3(buf, char *, ifc.ifc_len);
 
     ifc.ifc_buf = buf;
-#if defined(_AIX)
     siocgifconfRequest = CSIOCGIFCONF;
-#endif
+
     if (ioctl(sock, siocgifconfRequest, (char *)&ifc) < 0) {
         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
                                      "ioctl SIOCGIFCONF failed");
         free(buf);
         return ifs;
     }
 
     // Iterate through each interface
     ifreqP = ifc.ifc_req;
     for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++, ifreqP++) {
-#if defined(_AIX)
         if (ifreqP->ifr_addr.sa_family != AF_INET) continue;
-#endif
         // Add to the list
         ifs = addif(env, sock, ifreqP->ifr_name, ifs,
                     (struct sockaddr *)&(ifreqP->ifr_addr), AF_INET, 0);
 
         // If an exception occurred then free the list

@@ -1140,10 +1204,11 @@
 
     // Free socket and buffer
     free(buf);
     return ifs;
 }
+#endif /* _AIX */
 
 
 #if defined(AF_INET6) && defined(__linux__)
 
 /*

@@ -1877,40 +1942,10 @@
 #endif
 
 /*
  * Enumerates and returns all IPv4 interfaces.
  */
-static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
-    struct ifaddrs *ifa, *origifa;
-
-    if (getifaddrs(&origifa) != 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
-                                     "getifaddrs() function failed");
-        return ifs;
-    }
-
-    for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
-
-        // Skip non-AF_INET entries.
-        if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET)
-            continue;
-
-        // Add to the list.
-        ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr, AF_INET, 0);
-
-        // If an exception occurred then free the list.
-        if ((*env)->ExceptionOccurred(env)) {
-            freeifaddrs(origifa);
-            freeif(ifs);
-            return NULL;
-        }
-    }
-
-    // Free socket and buffer
-    freeifaddrs(origifa);
-    return ifs;
-}
 
 #ifdef AF_INET6
 /*
  * Determines the prefix on BSD for IPv6 interfaces.
  */
< prev index next >