Bug 975813 - Support IPv6 in Network Manager. r=vicamo,fabrice
authorChuck Lee <chulee@mozilla.com>
Fri, 28 Feb 2014 20:24:35 +0800
changeset 171652 e9b4e110a6a7f36d52a7108cf2a7978037ff4726
parent 171651 153ddd7b25f060451d05bbbb36fef87eb3931707
child 171653 935970b1890847d8e0a00fc957a1dc16afd780bb
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersvicamo, fabrice
bugs975813
milestone30.0a1
Bug 975813 - Support IPv6 in Network Manager. r=vicamo,fabrice
dom/system/gonk/NetworkManager.js
dom/system/gonk/NetworkService.js
dom/system/gonk/NetworkUtils.cpp
dom/system/gonk/SystemWorkerManager.cpp
dom/system/gonk/nsINetworkService.idl
dom/webidl/NetworkOptions.webidl
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -218,17 +218,17 @@ NetworkManager.prototype = {
             // Dun type is a special case where we add the default route to a
             // secondary table.
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
               this.setSecondaryDefaultRoute(network);
             }
 #endif
             // Remove pre-created default route and let setAndConfigureActive()
             // to set default route only on preferred network
-            gNetworkService.removeDefaultRoute(network.name);
+            gNetworkService.removeDefaultRoute(network);
             this.setAndConfigureActive();
 #ifdef MOZ_B2G_RIL
             // Update data connection when Wifi connected/disconnected
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
               for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
                 this.mRil.getRadioInterface(i).updateRILNetworkInterface();
               }
             }
@@ -253,17 +253,17 @@ NetworkManager.prototype = {
               this.removeSecondaryDefaultRoute(network);
             }
 #endif
             // Remove routing table in /proc/net/route
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
               gNetworkService.resetRoutingTable(network);
 #ifdef MOZ_B2G_RIL
             } else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
-              gNetworkService.removeDefaultRoute(network.name);
+              gNetworkService.removeDefaultRoute(network);
 #endif
             }
 
             // Abort ongoing captive portal detection on the wifi interface
             CaptivePortalDetectionHelper
               .notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network);
             this.setAndConfigureActive();
 #ifdef MOZ_B2G_RIL
@@ -377,17 +377,17 @@ NetworkManager.prototype = {
 #ifdef MOZ_B2G_RIL
     // Add host route for data calls
     if (this.isNetworkTypeMobile(network.type)) {
       gNetworkService.addHostRoute(network);
     }
 #endif
     // Remove pre-created default route and let setAndConfigureActive()
     // to set default route only on preferred network
-    gNetworkService.removeDefaultRoute(network.name);
+    gNetworkService.removeDefaultRoute(network);
     this.setAndConfigureActive();
     Services.obs.notifyObservers(network, TOPIC_INTERFACE_REGISTERED, null);
     debug("Network '" + network.name + "' registered.");
   },
 
   unregisterNetworkInterface: function(network) {
     if (!(network instanceof Ci.nsINetworkInterface)) {
       throw Components.Exception("Argument must be nsINetworkInterface.",
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -300,21 +300,22 @@ NetworkService.prototype = {
       domain: "mozilla." + network.name + ".doman",
       dns1_str: network.dns1,
       dns2_str: network.dns2
     };
     this.controlMessage(options, function() {});
     this.setNetworkProxy(network);
   },
 
-  removeDefaultRoute: function(ifname) {
-    if(DEBUG) debug("Remove default route for " + ifname);
+  removeDefaultRoute: function(network) {
+    if(DEBUG) debug("Remove default route for " + network.name);
     let options = {
       cmd: "removeDefaultRoute",
-      ifname: ifname
+      ifname: network.name,
+      gateway: network.gateway
     };
     this.controlMessage(options, function() {});
   },
 
   addHostRoute: function(network) {
     if(DEBUG) debug("Going to add host route on " + network.name);
     let options = {
       cmd: "addHostRoute",
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -15,16 +15,21 @@
 
 #include "NetworkUtils.h"
 
 #include <android/log.h>
 #include <cutils/properties.h>
 #include <limits>
 #include "mozilla/dom/network/NetUtils.h"
 
+#include <sys/types.h>  // struct addrinfo
+#include <sys/socket.h> // getaddrinfo(), freeaddrinfo()
+#include <netdb.h>
+#include <arpa/inet.h>  // inet_ntop()
+
 #define _DEBUG 0
 
 #define WARN(args...)   __android_log_print(ANDROID_LOG_WARN,  "NetworkUtils", ## args)
 #define ERROR(args...)  __android_log_print(ANDROID_LOG_ERROR,  "NetworkUtils", ## args)
 
 #if _DEBUG
 #define DEBUG(args...)  __android_log_print(ANDROID_LOG_DEBUG, "NetworkUtils" , ## args)
 #else
@@ -298,16 +303,33 @@ static void getIFProperties(const char* 
   snprintf(key, PROPERTY_KEY_MAX - 1, "net.%s.gw", ifname);
   property_get(key, prop.gateway, "");
   snprintf(key, PROPERTY_KEY_MAX - 1, "net.%s.dns1", ifname);
   property_get(key, prop.dns1, "");
   snprintf(key, PROPERTY_KEY_MAX - 1, "net.%s.dns2", ifname);
   property_get(key, prop.dns2, "");
 }
 
+static int getIpType(const char *aIp) {
+  struct addrinfo hint, *ip_info = NULL;
+
+  memset(&hint, 0, sizeof(hint));
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_flags = AI_NUMERICHOST;
+
+  if (getaddrinfo(aIp, NULL, &hint, &ip_info)) {
+    return AF_UNSPEC;
+  }
+
+  int type = ip_info->ai_family;
+  freeaddrinfo(ip_info);
+
+  return type;
+}
+
 static void postMessage(NetworkResultOptions& aResult)
 {
   MOZ_ASSERT(gNetworkUtils);
   MOZ_ASSERT(gNetworkUtils->getMessageCallback());
 
   if (*(gNetworkUtils->getMessageCallback()))
     (*(gNetworkUtils->getMessageCallback()))(aResult);
 }
@@ -1148,88 +1170,186 @@ bool NetworkUtils::setDNS(NetworkParams&
   return true;
 }
 
 /**
  * Set default route and DNS servers for given network interface.
  */
 bool NetworkUtils::setDefaultRouteAndDNS(NetworkParams& aOptions)
 {
+  NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
+  char gateway[128];
+
+  if (aOptions.mGateway_str.IsEmpty()) {
+    char key[PROPERTY_KEY_MAX];
+    snprintf(key, sizeof key - 1, "net.%s.gw", autoIfname.get());
+    property_get(key, gateway, "");
+  } else {
+    MOZ_ASSERT(strlen(GET_CHAR(mGateway_str)) < sizeof gateway);
+    strncpy(gateway, GET_CHAR(mGateway_str), sizeof(gateway) - 1);
+  }
+
+  int type = getIpType(gateway);
+  if (type != AF_INET && type != AF_INET6) {
+    return false;
+  }
+
+  if (type == AF_INET6) {
+    if (!aOptions.mOldIfname.IsEmpty()) {
+      mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL);
+    }
+
+    mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, gateway);
+
+    setDNS(aOptions);
+    return true;
+  }
+
+  /* type == AF_INET */
   if (!aOptions.mOldIfname.IsEmpty()) {
     mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname));
   }
 
-  IFProperties ifprops;
-  getIFProperties(GET_CHAR(mIfname), ifprops);
-
-  if (aOptions.mGateway_str.IsEmpty()) {
-    mNetUtils->do_ifc_set_default_route(GET_CHAR(mIfname), inet_addr(ifprops.gateway));
-  } else {
-    mNetUtils->do_ifc_set_default_route(GET_CHAR(mIfname), inet_addr(GET_CHAR(mGateway_str)));
-  }
+  mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(gateway));
 
   setDNS(aOptions);
   return true;
 }
 
 /**
  * Remove default route for given network interface.
  */
 bool NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
 {
-  mNetUtils->do_ifc_remove_default_route(GET_CHAR(mIfname));
+  NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway);
+
+  int type = getIpType(autoGateway.get());
+  if (type != AF_INET && type != AF_INET6) {
+    return false;
+  }
+
+  mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
+                                 type == AF_INET ? "0.0.0.0" : "::",
+                                 0, autoGateway.get());
+
   return true;
 }
 
 /**
  * Add host route for given network interface.
  */
 bool NetworkUtils::addHostRoute(NetworkParams& aOptions)
 {
+  NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
+  NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway);
+  int type, prefix;
+
   uint32_t length = aOptions.mHostnames.Length();
   for (uint32_t i = 0; i < length; i++) {
-    mNetUtils->do_ifc_add_route(GET_CHAR(mIfname), GET_CHAR(mHostnames[i]), 32, GET_CHAR(mGateway));
+    NS_ConvertUTF16toUTF8 autoHostname(aOptions.mHostnames[i]);
+
+    type = getIpType(autoHostname.get());
+    if (type != AF_INET && type != AF_INET6) {
+      continue;
+    }
+
+    prefix = type == AF_INET ? 32 : 128;
+    mNetUtils->do_ifc_add_route(autoIfname.get(), autoHostname.get(), prefix,
+                                autoGateway.get());
   }
   return true;
 }
 
 /**
  * Remove host route for given network interface.
  */
 bool NetworkUtils::removeHostRoute(NetworkParams& aOptions)
 {
+  NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
+  NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway);
+  int type, prefix;
+
   uint32_t length = aOptions.mHostnames.Length();
   for (uint32_t i = 0; i < length; i++) {
-    mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname), GET_CHAR(mHostnames[i]), 32, GET_CHAR(mGateway));
+    NS_ConvertUTF16toUTF8 autoHostname(aOptions.mHostnames[i]);
+
+    type = getIpType(autoHostname.get());
+    if (type != AF_INET && type != AF_INET6) {
+      continue;
+    }
+
+    prefix = type == AF_INET ? 32 : 128;
+    mNetUtils->do_ifc_remove_route(autoIfname.get(), autoHostname.get(), prefix,
+                                   autoGateway.get());
   }
   return true;
 }
 
 /**
  * Remove the routes associated with the named interface.
  */
 bool NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
 {
   mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname));
   return true;
 }
 
 bool NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
 {
-  uint32_t ip = inet_addr(GET_CHAR(mIp));
+  NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
+  NS_ConvertUTF16toUTF8 autoIp(aOptions.mIp);
+
+  int type = getIpType(autoIp.get());
+  if (type != AF_INET && type != AF_INET6) {
+    return false;
+  }
+
   uint32_t prefixLength = GET_FIELD(mPrefixLength);
+
+  if (type == AF_INET6) {
+    // Calculate subnet.
+    struct in6_addr in6;
+    if (inet_pton(AF_INET6, autoIp.get(), &in6) != 1) {
+      return false;
+    }
+
+    uint32_t p, i, p1, mask;
+    p = prefixLength;
+    i = 0;
+    while (i < 4) {
+      p1 = p > 32 ? 32 : p;
+      p -= p1;
+      mask = p1 ? ~0x0 << (32 - p1) : 0;
+      in6.s6_addr32[i++] &= htonl(mask);
+    }
+
+    char subnetStr[INET6_ADDRSTRLEN];
+    if (!inet_ntop(AF_INET6, &in6, subnetStr, sizeof subnetStr)) {
+      return false;
+    }
+
+    // Remove default route.
+    mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL);
+
+    // Remove subnet route.
+    mNetUtils->do_ifc_remove_route(autoIfname.get(), subnetStr, prefixLength, NULL);
+    return true;
+  }
+
+  /* type == AF_INET */
+  uint32_t ip = inet_addr(autoIp.get());
   uint32_t netmask = makeMask(prefixLength);
   uint32_t subnet = ip & netmask;
   const char* gateway = "0.0.0.0";
   struct in_addr addr;
   addr.s_addr = subnet;
   const char* dst = inet_ntoa(addr);
 
-  mNetUtils->do_ifc_remove_default_route(GET_CHAR(mIfname));
-  mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname), dst, prefixLength, gateway);
+  mNetUtils->do_ifc_remove_default_route(autoIfname.get());
+  mNetUtils->do_ifc_remove_route(autoIfname.get(), dst, prefixLength, gateway);
   return true;
 }
 
 bool NetworkUtils::addSecondaryRoute(NetworkParams& aOptions)
 {
   char command[MAX_COMMAND_SIZE];
   snprintf(command, MAX_COMMAND_SIZE - 1,
            "interface route add %s secondary %s %s %s",
--- a/dom/system/gonk/SystemWorkerManager.cpp
+++ b/dom/system/gonk/SystemWorkerManager.cpp
@@ -46,17 +46,16 @@ USING_WORKERS_NAMESPACE
 
 using namespace mozilla::dom::gonk;
 using namespace mozilla::ipc;
 using namespace mozilla::system;
 
 namespace {
 
 NS_DEFINE_CID(kWifiWorkerCID, NS_WIFIWORKER_CID);
-NS_DEFINE_CID(kNetworkServiceCID, NS_INETWORKSERVICE_IID);
 
 // Doesn't carry a reference, we're owned by services.
 SystemWorkerManager *gInstance = nullptr;
 
 } // anonymous namespace
 
 SystemWorkerManager::SystemWorkerManager()
   : mShutdown(false)
@@ -237,17 +236,17 @@ SystemWorkerManager::RegisterNfcWorker(J
 
   return NfcConsumer::Register(wctd);
 #endif // MOZ_NFC
 }
 
 nsresult
 SystemWorkerManager::InitNetd(JSContext *cx)
 {
-  nsCOMPtr<nsIWorkerHolder> worker = do_GetService(kNetworkServiceCID);
+  nsCOMPtr<nsIWorkerHolder> worker = do_GetService("@mozilla.org/network/service;1");
   NS_ENSURE_TRUE(worker, NS_ERROR_FAILURE);
   mNetdWorker = worker;
   return NS_OK;
 }
 
 nsresult
 SystemWorkerManager::InitWifi(JSContext *cx)
 {
--- a/dom/system/gonk/nsINetworkService.idl
+++ b/dom/system/gonk/nsINetworkService.idl
@@ -99,17 +99,17 @@ interface nsIUpdateUpStreamCallback : ns
    *        The external interface name.
    */
   void updateUpStreamResult(in boolean success, in DOMString externalIfname);
 };
 
 /**
  * Provide network services.
  */
-[scriptable, uuid(baec696c-c78d-42db-8b44-603f8fbfafb4)]
+[scriptable, uuid(f96461fa-e844-45d2-a6c3-8cd23ab0916b)]
 interface nsINetworkService : nsISupports
 {
   /**
    * Enable or disable Wifi Tethering
    *
    * @param enabled
    *        Boolean that indicates whether tethering should be enabled (true) or disabled (false).
    * @param config
@@ -241,20 +241,20 @@ interface nsINetworkService : nsISupport
    *        The previous network interface.
    */
   void setDefaultRouteAndDNS(in nsINetworkInterface networkInterface,
                              in nsINetworkInterface oldInterface);
 
   /**
    * Remove default route.
    *
-   * @param interfaceName
+   * @param networkInterface
    *        The network interface we want remove from the default route.
    */
-  void removeDefaultRoute(in DOMString interfaceName);
+  void removeDefaultRoute(in nsINetworkInterface networkInterface);
 
   /**
    * Add host route.
    *
    * @param networkInterface
    *        The network interface we want to add to the host route.
    */
   void addHostRoute(in nsINetworkInterface networkInterface);
--- a/dom/webidl/NetworkOptions.webidl
+++ b/dom/webidl/NetworkOptions.webidl
@@ -15,17 +15,18 @@ dictionary NetworkCommandOptions
                                       //     "removeHostRoutes".
   DOMString ip;                       // for "removeNetworkRoute", "setWifiTethering".
   unsigned long prefixLength;         // for "removeNetworkRoute".
   DOMString domain;                   // for "setDNS"
   DOMString dns1_str;                 // for "setDNS", "setDefaultRouteAndDNS".
   DOMString dns2_str;                 // for "setDNS", "setDefaultRouteAndDNS".
   DOMString oldIfname;                // for "setDefaultRouteAndDNS".
   DOMString gateway_str;              // for "setDefaultRouteAndDNS".
-  DOMString gateway;                  // for "addHostRoute", "removeHostRoute".
+  DOMString gateway;                  // for "addHostRoute", "removeHostRoute",
+                                      //     "removeDefaultRoute".
   sequence<DOMString> hostnames;      // for "addHostRoute", "removeHostRoute".
   DOMString mode;                     // for "setWifiOperationMode".
   boolean report;                     // for "setWifiOperationMode".
   boolean isAsync;                    // for "setWifiOperationMode".
   boolean enabled;                    // for "setDhcpServer".
   DOMString wifictrlinterfacename;    // for "setWifiTethering".
   DOMString internalIfname;           // for "setWifiTethering".
   DOMString externalIfname;           // for "setWifiTethering".