diff options
Diffstat (limited to 'dom/system/gonk/NetworkUtils.cpp')
-rw-r--r-- | dom/system/gonk/NetworkUtils.cpp | 2973 |
1 files changed, 2973 insertions, 0 deletions
diff --git a/dom/system/gonk/NetworkUtils.cpp b/dom/system/gonk/NetworkUtils.cpp new file mode 100644 index 000000000..d661368b8 --- /dev/null +++ b/dom/system/gonk/NetworkUtils.cpp @@ -0,0 +1,2973 @@ +/* Copyright 2012 Mozilla Foundation and Mozilla contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NetworkUtils.h" + +#include "mozilla/Sprintf.h" +#include "SystemProperty.h" + +#include <android/log.h> +#include <limits> +#include "mozilla/dom/network/NetUtils.h" +#include "mozilla/fallible.h" +#include "base/task.h" + +#include <errno.h> +#include <string.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 NU_DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "NetworkUtils" , ## args) +#else +#define NU_DBG(args...) +#endif + +using namespace mozilla::dom; +using namespace mozilla::ipc; +using mozilla::system::Property; + +static const char* PERSIST_SYS_USB_CONFIG_PROPERTY = "persist.sys.usb.config"; +static const char* SYS_USB_CONFIG_PROPERTY = "sys.usb.config"; +static const char* SYS_USB_STATE_PROPERTY = "sys.usb.state"; + +static const char* USB_FUNCTION_RNDIS = "rndis"; +static const char* USB_FUNCTION_ADB = "adb"; + +// Use this command to continue the function chain. +static const char* DUMMY_COMMAND = "tether status"; + +// IPV6 Tethering is not supported in AOSP, use the property to +// identify vendor specific support in IPV6. We can remove this flag +// once upstream Android support IPV6 in tethering. +static const char* IPV6_TETHERING = "ro.tethering.ipv6"; + +// Retry 20 times (2 seconds) for usb state transition. +static const uint32_t USB_FUNCTION_RETRY_TIMES = 20; +// Check "sys.usb.state" every 100ms. +static const uint32_t USB_FUNCTION_RETRY_INTERVAL = 100; + +// 1xx - Requested action is proceeding +static const uint32_t NETD_COMMAND_PROCEEDING = 100; +// 2xx - Requested action has been successfully completed +static const uint32_t NETD_COMMAND_OKAY = 200; +// 4xx - The command is accepted but the requested action didn't +// take place. +static const uint32_t NETD_COMMAND_FAIL = 400; +// 5xx - The command syntax or parameters error +static const uint32_t NETD_COMMAND_ERROR = 500; +// 6xx - Unsolicited broadcasts +static const uint32_t NETD_COMMAND_UNSOLICITED = 600; + +// Broadcast messages +static const uint32_t NETD_COMMAND_INTERFACE_CHANGE = 600; +static const uint32_t NETD_COMMAND_BANDWIDTH_CONTROLLER = 601; + +static const char* INTERFACE_DELIMIT = ","; +static const char* USB_CONFIG_DELIMIT = ","; +static const char* NETD_MESSAGE_DELIMIT = " "; + +static const uint32_t BUF_SIZE = 1024; + +static const int32_t SUCCESS = 0; + +static uint32_t SDK_VERSION; +static uint32_t SUPPORT_IPV6_TETHERING; + +struct IFProperties { + char gateway[Property::VALUE_MAX_LENGTH]; + char dns1[Property::VALUE_MAX_LENGTH]; + char dns2[Property::VALUE_MAX_LENGTH]; +}; + +struct CurrentCommand { + CommandChain* chain; + CommandCallback callback; + char command[MAX_COMMAND_SIZE]; +}; + +typedef Tuple3<NetdCommand*, CommandChain*, CommandCallback> QueueData; + +#define GET_CURRENT_NETD_COMMAND (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a) +#define GET_CURRENT_CHAIN (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].b) +#define GET_CURRENT_CALLBACK (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].c) +#define GET_CURRENT_COMMAND (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a->mData) + +// A macro for native function call return value check. +// For native function call, non-zero return value means failure. +#define RETURN_IF_FAILED(rv) do { \ + if (SUCCESS != rv) { \ + return rv; \ + } \ +} while (0); + +#define WARN_IF_FAILED(rv) do { \ + if (SUCCESS != rv) { \ + WARN("Error (%d) occurred in %s (%s:%d)", rv, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + } \ +} while (0); + +static NetworkUtils* gNetworkUtils; +static nsTArray<QueueData> gCommandQueue; +static CurrentCommand gCurrentCommand; +static bool gPending = false; +static nsTArray<nsCString> gReason; +static NetworkParams *gWifiTetheringParms = 0; + +static nsTArray<CommandChain*> gCommandChainQueue; + +const CommandFunc NetworkUtils::sWifiEnableChain[] = { + NetworkUtils::clearWifiTetherParms, + NetworkUtils::wifiFirmwareReload, + NetworkUtils::startAccessPointDriver, + NetworkUtils::setAccessPoint, + NetworkUtils::startSoftAP, + NetworkUtils::setConfig, + NetworkUtils::tetherInterface, + NetworkUtils::addInterfaceToLocalNetwork, + NetworkUtils::addRouteToLocalNetwork, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::tetheringStatus, + NetworkUtils::startTethering, + NetworkUtils::setDnsForwarders, + NetworkUtils::enableNat, + NetworkUtils::wifiTetheringSuccess +}; + +const CommandFunc NetworkUtils::sWifiDisableChain[] = { + NetworkUtils::clearWifiTetherParms, + NetworkUtils::stopSoftAP, + NetworkUtils::stopAccessPointDriver, + NetworkUtils::wifiFirmwareReload, + NetworkUtils::untetherInterface, + NetworkUtils::removeInterfaceFromLocalNetwork, + NetworkUtils::preTetherInterfaceList, + NetworkUtils::postTetherInterfaceList, + NetworkUtils::disableNat, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering, + NetworkUtils::wifiTetheringSuccess +}; + +const CommandFunc NetworkUtils::sWifiFailChain[] = { + NetworkUtils::clearWifiTetherParms, + NetworkUtils::stopSoftAP, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering +}; + +const CommandFunc NetworkUtils::sWifiRetryChain[] = { + NetworkUtils::clearWifiTetherParms, + NetworkUtils::stopSoftAP, + NetworkUtils::stopTethering, + + // sWifiEnableChain: + NetworkUtils::wifiFirmwareReload, + NetworkUtils::startAccessPointDriver, + NetworkUtils::setAccessPoint, + NetworkUtils::startSoftAP, + NetworkUtils::setConfig, + NetworkUtils::tetherInterface, + NetworkUtils::addInterfaceToLocalNetwork, + NetworkUtils::addRouteToLocalNetwork, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::tetheringStatus, + NetworkUtils::startTethering, + NetworkUtils::setDnsForwarders, + NetworkUtils::enableNat, + NetworkUtils::wifiTetheringSuccess +}; + +const CommandFunc NetworkUtils::sWifiOperationModeChain[] = { + NetworkUtils::wifiFirmwareReload, + NetworkUtils::wifiOperationModeSuccess +}; + +const CommandFunc NetworkUtils::sUSBEnableChain[] = { + NetworkUtils::setConfig, + NetworkUtils::enableNat, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::tetherInterface, + NetworkUtils::addInterfaceToLocalNetwork, + NetworkUtils::addRouteToLocalNetwork, + NetworkUtils::tetheringStatus, + NetworkUtils::startTethering, + NetworkUtils::setDnsForwarders, + NetworkUtils::addUpstreamInterface, + NetworkUtils::usbTetheringSuccess +}; + +const CommandFunc NetworkUtils::sUSBDisableChain[] = { + NetworkUtils::untetherInterface, + NetworkUtils::removeInterfaceFromLocalNetwork, + NetworkUtils::preTetherInterfaceList, + NetworkUtils::postTetherInterfaceList, + NetworkUtils::removeUpstreamInterface, + NetworkUtils::disableNat, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering, + NetworkUtils::usbTetheringSuccess +}; + +const CommandFunc NetworkUtils::sUSBFailChain[] = { + NetworkUtils::stopSoftAP, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering +}; + +const CommandFunc NetworkUtils::sUpdateUpStreamChain[] = { + NetworkUtils::cleanUpStream, + NetworkUtils::removeUpstreamInterface, + NetworkUtils::createUpStream, + NetworkUtils::addUpstreamInterface, + NetworkUtils::updateUpStreamSuccess +}; + +const CommandFunc NetworkUtils::sStartDhcpServerChain[] = { + NetworkUtils::setConfig, + NetworkUtils::startTethering, + NetworkUtils::setDhcpServerSuccess +}; + +const CommandFunc NetworkUtils::sStopDhcpServerChain[] = { + NetworkUtils::stopTethering, + NetworkUtils::setDhcpServerSuccess +}; + +const CommandFunc NetworkUtils::sNetworkInterfaceEnableAlarmChain[] = { + NetworkUtils::enableAlarm, + NetworkUtils::setQuota, + NetworkUtils::setAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +const CommandFunc NetworkUtils::sNetworkInterfaceDisableAlarmChain[] = { + NetworkUtils::removeQuota, + NetworkUtils::disableAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +const CommandFunc NetworkUtils::sNetworkInterfaceSetAlarmChain[] = { + NetworkUtils::setAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +const CommandFunc NetworkUtils::sGetInterfacesChain[] = { + NetworkUtils::getInterfaceList, + NetworkUtils::getInterfacesSuccess +}; + +const CommandFunc NetworkUtils::sGetInterfaceConfigChain[] = { + NetworkUtils::getConfig, + NetworkUtils::getInterfaceConfigSuccess +}; + +const CommandFunc NetworkUtils::sSetInterfaceConfigChain[] = { + NetworkUtils::setConfig, + NetworkUtils::setInterfaceConfigSuccess +}; + +const CommandFunc NetworkUtils::sTetheringInterfaceSetAlarmChain[] = { + NetworkUtils::setGlobalAlarm, + NetworkUtils::removeAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +const CommandFunc NetworkUtils::sTetheringInterfaceRemoveAlarmChain[] = { + NetworkUtils::removeGlobalAlarm, + NetworkUtils::setAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +const CommandFunc NetworkUtils::sTetheringGetStatusChain[] = { + NetworkUtils::tetheringStatus, + NetworkUtils::defaultAsyncSuccessHandler +}; + +/** + * Helper function to get the mask from given prefix length. + */ +static uint32_t makeMask(const uint32_t prefixLength) +{ + uint32_t mask = 0; + for (uint32_t i = 0; i < prefixLength; ++i) { + mask |= (0x80000000 >> i); + } + return ntohl(mask); +} + +/** + * Helper function to get the network part of an ip from prefix. + * param ip must be in network byte order. + */ +static char* getNetworkAddr(const uint32_t ip, const uint32_t prefix) +{ + uint32_t mask = 0, subnet = 0; + + mask = ~mask << (32 - prefix); + mask = htonl(mask); + subnet = ip & mask; + + struct in_addr addr; + addr.s_addr = subnet; + + return inet_ntoa(addr); +} + +/** + * Helper function to split string by seperator, store split result as an nsTArray. + */ +static void split(char* str, const char* sep, nsTArray<nsCString>& result) +{ + char *s = strtok(str, sep); + while (s != nullptr) { + result.AppendElement(s); + s = strtok(nullptr, sep); + } +} + +static void split(char* str, const char* sep, nsTArray<nsString>& result) +{ + char *s = strtok(str, sep); + while (s != nullptr) { + result.AppendElement(NS_ConvertUTF8toUTF16(s)); + s = strtok(nullptr, sep); + } +} + +/** + * Helper function that implement join function. + */ +static void join(nsTArray<nsCString>& array, + const char* sep, + const uint32_t maxlen, + char* result) +{ +#define CHECK_LENGTH(len, add, max) len += add; \ + if (len > max - 1) \ + return; \ + + uint32_t len = 0; + uint32_t seplen = strlen(sep); + + if (array.Length() > 0) { + CHECK_LENGTH(len, strlen(array[0].get()), maxlen) + strcpy(result, array[0].get()); + + for (uint32_t i = 1; i < array.Length(); i++) { + CHECK_LENGTH(len, seplen, maxlen) + strcat(result, sep); + + CHECK_LENGTH(len, strlen(array[i].get()), maxlen) + strcat(result, array[i].get()); + } + } + +#undef CHECK_LEN +} + +static void convertUTF8toUTF16(nsTArray<nsCString>& narrow, + nsTArray<nsString>& wide, + uint32_t length) +{ + for (uint32_t i = 0; i < length; i++) { + wide.AppendElement(NS_ConvertUTF8toUTF16(narrow[i].get())); + } +} + +/** + * Helper function to get network interface properties from the system property table. + */ +static void getIFProperties(const char* ifname, IFProperties& prop) +{ + char key[Property::KEY_MAX_LENGTH]; + snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.gw", ifname); + Property::Get(key, prop.gateway, ""); + snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.dns1", ifname); + Property::Get(key, prop.dns1, ""); + snprintf(key, Property::KEY_MAX_LENGTH - 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); +} + +static void postMessage(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + MOZ_ASSERT(gNetworkUtils); + MOZ_ASSERT(gNetworkUtils->getMessageCallback()); + + aResult.mId = aOptions.mId; + + if (*(gNetworkUtils->getMessageCallback())) + (*(gNetworkUtils->getMessageCallback()))(aResult); +} + +void NetworkUtils::runNextQueuedCommandChain() +{ + if (gCommandChainQueue.IsEmpty()) { + NU_DBG("No command chain left in the queue. Done!"); + return; + } + NU_DBG("Process the queued command chain."); + CommandChain* nextChain = gCommandChainQueue[0]; + NetworkResultOptions newResult; + next(nextChain, false, newResult); +} + +void NetworkUtils::next(CommandChain* aChain, bool aError, NetworkResultOptions& aResult) +{ + if (aError) { + ErrorCallback onError = aChain->getErrorCallback(); + if(onError) { + aResult.mError = true; + (*onError)(aChain->getParams(), aResult); + } + delete aChain; + gCommandChainQueue.RemoveElementAt(0); + runNextQueuedCommandChain(); + return; + } + CommandFunc f = aChain->getNextCommand(); + if (!f) { + delete aChain; + gCommandChainQueue.RemoveElementAt(0); + runNextQueuedCommandChain(); + return; + } + + (*f)(aChain, next, aResult); +} + +CommandResult::CommandResult(int32_t aResultCode) + : mIsPending(false) +{ + // This is usually not a netd command. We treat the return code + // typical linux convention, which uses 0 to indicate success. + mResult.mError = (aResultCode == SUCCESS ? false : true); + mResult.mResultCode = aResultCode; + if (aResultCode != SUCCESS) { + // The returned value is sometimes negative, make sure we pass a positive + // error number to strerror. + enum { STRERROR_R_BUF_SIZE = 1024, }; + char strerrorBuf[STRERROR_R_BUF_SIZE]; + strerror_r(abs(aResultCode), strerrorBuf, STRERROR_R_BUF_SIZE); + mResult.mReason = NS_ConvertUTF8toUTF16(strerrorBuf); + } +} + +CommandResult::CommandResult(const mozilla::dom::NetworkResultOptions& aResult) + : mResult(aResult) + , mIsPending(false) +{ +} + +CommandResult::CommandResult(const Pending&) + : mIsPending(true) +{ +} + +bool CommandResult::isPending() const +{ + return mIsPending; +} + +/** + * Send command to netd. + */ +void NetworkUtils::nextNetdCommand() +{ + if (gCommandQueue.IsEmpty() || gPending) { + return; + } + + gCurrentCommand.chain = GET_CURRENT_CHAIN; + gCurrentCommand.callback = GET_CURRENT_CALLBACK; + snprintf(gCurrentCommand.command, MAX_COMMAND_SIZE - 1, "%s", GET_CURRENT_COMMAND); + + NU_DBG("Sending \'%s\' command to netd.", gCurrentCommand.command); + SendNetdCommand(GET_CURRENT_NETD_COMMAND); + + gCommandQueue.RemoveElementAt(0); + gPending = true; +} + +/** + * Composite NetdCommand sent to netd + * + * @param aCommand Command sent to netd to execute. + * @param aChain Store command chain data, ex. command parameter. + * @param aCallback Callback function to be executed when the result of + * this command is returned from netd. + */ +void NetworkUtils::doCommand(const char* aCommand, CommandChain* aChain, CommandCallback aCallback) +{ + NU_DBG("Preparing to send \'%s\' command...", aCommand); + + NetdCommand* netdCommand = new NetdCommand(); + + // Android JB version adds sequence number to netd command. + if (SDK_VERSION >= 16) { + snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "0 %s", aCommand); + } else { + snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "%s", aCommand); + } + netdCommand->mSize = strlen((char*)netdCommand->mData) + 1; + + gCommandQueue.AppendElement(QueueData(netdCommand, aChain, aCallback)); + + nextNetdCommand(); +} + +/* + * Netd command function + */ +#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aChain->getParams().prop).get() +#define GET_FIELD(prop) aChain->getParams().prop + +void NetworkUtils::wifiFirmwareReload(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "softap fwreload %s %s", GET_CHAR(mIfname), GET_CHAR(mMode)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::startAccessPointDriver(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // Skip the command for sdk version >= 16. + if (SDK_VERSION >= 16) { + aResult.mResultCode = 0; + aResult.mResultReason = NS_ConvertUTF8toUTF16(""); + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "softap start %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::stopAccessPointDriver(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // Skip the command for sdk version >= 16. + if (SDK_VERSION >= 16) { + aResult.mResultCode = 0; + aResult.mResultReason = NS_ConvertUTF8toUTF16(""); + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "softap stop %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +/** + * Command format for sdk version < 16 + * Arguments: + * argv[2] - wlan interface + * argv[3] - SSID + * argv[4] - Security + * argv[5] - Key + * argv[6] - Channel + * argv[7] - Preamble + * argv[8] - Max SCB + * + * Command format for sdk version >= 16 + * Arguments: + * argv[2] - wlan interface + * argv[3] - SSID + * argv[4] - Security + * argv[5] - Key + * + * Command format for sdk version >= 18 + * Arguments: + * argv[2] - wlan interface + * argv[3] - SSID + * argv[4] - Broadcast/Hidden + * argv[5] - Channel + * argv[6] - Security + * argv[7] - Key + */ +void NetworkUtils::setAccessPoint(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + nsCString ssid(GET_CHAR(mSsid)); + nsCString key(GET_CHAR(mKey)); + + escapeQuote(ssid); + escapeQuote(key); + + if (SDK_VERSION >= 19) { + snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" broadcast 6 %s \"%s\"", + GET_CHAR(mIfname), + ssid.get(), + GET_CHAR(mSecurity), + key.get()); + } else if (SDK_VERSION >= 16) { + snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" %s \"%s\"", + GET_CHAR(mIfname), + ssid.get(), + GET_CHAR(mSecurity), + key.get()); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s %s \"%s\" %s \"%s\" 6 0 8", + GET_CHAR(mIfname), + GET_CHAR(mWifictrlinterfacename), + ssid.get(), + GET_CHAR(mSecurity), + key.get()); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::cleanUpStream(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0", GET_CHAR(mPreInternalIfname), GET_CHAR(mPreExternalIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::createUpStream(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0", GET_CHAR(mCurInternalIfname), GET_CHAR(mCurExternalIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::startSoftAP(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + const char* command= "softap startap"; + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::stopSoftAP(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + const char* command= "softap stopap"; + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::clearWifiTetherParms(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + delete gWifiTetheringParms; + gWifiTetheringParms = 0; + next(aChain, false, aResult); +} + +void NetworkUtils::enableAlarm(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + const char* command= "bandwidth enable"; + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::disableAlarm(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + const char* command= "bandwidth disable"; + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setQuota(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setiquota %s % " PRId64, GET_CHAR(mIfname), INT64_MAX); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeQuota(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeiquota %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setAlarm(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setinterfacealert %s %lld", + GET_CHAR(mIfname), GET_FIELD(mThreshold)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeAlarm(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeinterfacealert %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setGlobalAlarm(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setglobalalert %lld", GET_FIELD(mThreshold)); + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeGlobalAlarm(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeglobalalert"); + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::tetherInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::addInterfaceToLocalNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // Skip the command for sdk version < 20. + if (SDK_VERSION < 20) { + aResult.mResultCode = 0; + aResult.mResultReason = NS_ConvertUTF8toUTF16(""); + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add local %s", + GET_CHAR(mInternalIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::addRouteToLocalNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // Skip the command for sdk version < 20. + if (SDK_VERSION < 20) { + aResult.mResultCode = 0; + aResult.mResultReason = NS_ConvertUTF8toUTF16(""); + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + uint32_t prefix = atoi(GET_CHAR(mPrefix)); + uint32_t ip = inet_addr(GET_CHAR(mIp)); + char* networkAddr = getNetworkAddr(ip, prefix); + + snprintf(command, MAX_COMMAND_SIZE - 1, "network route add local %s %s/%s", + GET_CHAR(mInternalIfname), networkAddr, GET_CHAR(mPrefix)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::preTetherInterfaceList(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + if (SDK_VERSION >= 16) { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list"); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list 0"); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::postTetherInterfaceList(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // Send the dummy command to continue the function chain. + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND); + + char buf[BUF_SIZE]; + NS_ConvertUTF16toUTF8 reason(aResult.mResultReason); + + size_t length = reason.Length() + 1 < BUF_SIZE ? reason.Length() + 1 : BUF_SIZE; + memcpy(buf, reason.get(), length); + split(buf, INTERFACE_DELIMIT, GET_FIELD(mInterfaceList)); + + doCommand(command, aChain, aCallback); +} + +bool isCommandChainIPv6(CommandChain* aChain, const char *externalInterface) { + // Check by gateway address + if (getIpType(GET_CHAR(mGateway)) == AF_INET6) { + return true; + } + + uint32_t length = GET_FIELD(mGateways).Length(); + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoGateway(GET_FIELD(mGateways)[i]); + if(getIpType(autoGateway.get()) == AF_INET6) { + return true; + } + } + + // Check by external inteface address + FILE *file = fopen("/proc/net/if_inet6", "r"); + if (!file) { + return false; + } + + bool isIPv6 = false; + char interface[32]; + while(fscanf(file, "%*s %*s %*s %*s %*s %32s", interface)) { + if (strcmp(interface, externalInterface) == 0) { + isIPv6 = true; + break; + } + } + + fclose(file); + return isIPv6; +} + +void NetworkUtils::addUpstreamInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + nsCString interface(GET_CHAR(mExternalIfname)); + if (!interface.get()[0]) { + interface = GET_CHAR(mCurExternalIfname); + } + + if (SUPPORT_IPV6_TETHERING == 0 || !isCommandChainIPv6(aChain, interface.get())) { + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add_upstream %s", + interface.get()); + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeUpstreamInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + nsCString interface(GET_CHAR(mExternalIfname)); + if (!interface.get()[0]) { + interface = GET_CHAR(mPreExternalIfname); + } + + if (SUPPORT_IPV6_TETHERING == 0 || !isCommandChainIPv6(aChain, interface.get())) { + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove_upstream %s", + interface.get()); + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setIpForwardingEnabled(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + if (GET_FIELD(mEnable)) { + snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd enable"); + } else { + // Don't disable ip forwarding because others interface still need it. + // Send the dummy command to continue the function chain. + if (GET_FIELD(mInterfaceList).Length() > 1) { + snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd disable"); + } + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::tetheringStatus(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + const char* command= "tether status"; + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::stopTethering(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + // Don't stop tethering because others interface still need it. + // Send the dummy to continue the function chain. + if (GET_FIELD(mInterfaceList).Length() > 1) { + snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether stop"); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::startTethering(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + // We don't need to start tethering again. + // Send the dummy command to continue the function chain. + if (aResult.mResultReason.Find("started") != kNotFound) { + snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND); + } else { + // If usbStartIp/usbEndIp is not valid, don't append them since + // the trailing white spaces will be parsed to extra empty args + // See: http://androidxref.com/4.3_r2.1/xref/system/core/libsysutils/src/FrameworkListener.cpp#78 + if (!GET_FIELD(mUsbStartIp).IsEmpty() && !GET_FIELD(mUsbEndIp).IsEmpty()) { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s %s %s", + GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp), + GET_CHAR(mUsbStartIp), GET_CHAR(mUsbEndIp)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s", + GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp)); + } + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::untetherInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeInterfaceFromLocalNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // Skip the command for sdk version < 20. + if (SDK_VERSION < 20) { + aResult.mResultCode = 0; + aResult.mResultReason = NS_ConvertUTF8toUTF16(""); + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "network interface remove local %s", + GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setDnsForwarders(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + if (SDK_VERSION >= 20) { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %d %s %s", + GET_FIELD(mNetId), GET_CHAR(mDns1), GET_CHAR(mDns2)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s", + GET_CHAR(mDns1), GET_CHAR(mDns2)); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::enableNat(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + if (!GET_FIELD(mIp).IsEmpty() && !GET_FIELD(mPrefix).IsEmpty()) { + uint32_t prefix = atoi(GET_CHAR(mPrefix)); + uint32_t ip = inet_addr(GET_CHAR(mIp)); + char* networkAddr = getNetworkAddr(ip, prefix); + + // address/prefix will only take effect when secondary routing table exists. + snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 1 %s/%s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname), networkAddr, + GET_CHAR(mPrefix)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::disableNat(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + + if (!GET_FIELD(mIp).IsEmpty() && !GET_FIELD(mPrefix).IsEmpty()) { + uint32_t prefix = atoi(GET_CHAR(mPrefix)); + uint32_t ip = inet_addr(GET_CHAR(mIp)); + char* networkAddr = getNetworkAddr(ip, prefix); + + snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 1 %s/%s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname), networkAddr, + GET_CHAR(mPrefix)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setDefaultInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "resolver setdefaultif %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeDefaultRoute(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + if (GET_FIELD(mLoopIndex) >= GET_FIELD(mGateways).Length()) { + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + nsTArray<nsString>& gateways = GET_FIELD(mGateways); + NS_ConvertUTF16toUTF8 autoGateway(gateways[GET_FIELD(mLoopIndex)]); + + int type = getIpType(autoGateway.get()); + snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s %s/0 %s", + GET_FIELD(mNetId), GET_CHAR(mIfname), + type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get()); + + struct MyCallback { + static void callback(CommandCallback::CallbackType aOriginalCallback, + CommandChain* aChain, + bool aError, + mozilla::dom::NetworkResultOptions& aResult) + { + NS_ConvertUTF16toUTF8 reason(aResult.mResultReason); + NU_DBG("removeDefaultRoute's reason: %s", reason.get()); + if (aError && !reason.EqualsASCII("removeRoute() failed (No such process)")) { + return aOriginalCallback(aChain, aError, aResult); + } + + GET_FIELD(mLoopIndex)++; + return removeDefaultRoute(aChain, aOriginalCallback, aResult); + } + }; + + CommandCallback wrappedCallback(MyCallback::callback, aCallback); + doCommand(command, aChain, wrappedCallback); +} + +void NetworkUtils::setInterfaceDns(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + int written; + + if (SDK_VERSION >= 20) { + written = SprintfLiteral(command, "resolver setnetdns %d %s", + GET_FIELD(mNetId), GET_CHAR(mDomain)); + } else { + written = SprintfLiteral(command, "resolver setifdns %s %s", + GET_CHAR(mIfname), GET_CHAR(mDomain)); + } + + nsTArray<nsString>& dnses = GET_FIELD(mDnses); + uint32_t length = dnses.Length(); + + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoDns(dnses[i]); + + int ret = snprintf(command + written, sizeof(command) - written, " %s", autoDns.get()); + if (ret <= 1) { + command[written] = '\0'; + continue; + } + + if (((size_t)ret + written) >= sizeof(command)) { + command[written] = '\0'; + break; + } + + written += ret; + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::getInterfaceList(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface list"); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::getConfig(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface getcfg %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setConfig(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + if (SDK_VERSION >= 16) { + snprintf(command, MAX_COMMAND_SIZE - 1, "interface setcfg %s %s %s %s", + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mLink)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, "interface setcfg %s %s %s [%s]", + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mLink)); + } + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::clearAddrForInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface clearaddrs %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::createNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "network create %d", GET_FIELD(mNetId)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::destroyNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "network destroy %d", GET_FIELD(mNetId)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::addInterfaceToNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add %d %s", + GET_FIELD(mNetId), GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::addRouteToInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + struct MyCallback { + static void callback(CommandCallback::CallbackType aOriginalCallback, + CommandChain* aChain, + bool aError, + mozilla::dom::NetworkResultOptions& aResult) + { + NS_ConvertUTF16toUTF8 reason(aResult.mResultReason); + NU_DBG("addRouteToInterface's reason: %s", reason.get()); + if (aError && reason.EqualsASCII("addRoute() failed (File exists)")) { + NU_DBG("Ignore \"File exists\" error when adding host route."); + return aOriginalCallback(aChain, false, aResult); + } + aOriginalCallback(aChain, aError, aResult); + } + }; + + CommandCallback wrappedCallback(MyCallback::callback, aCallback); + modifyRouteOnInterface(aChain, wrappedCallback, aResult, true); +} + +void NetworkUtils::removeRouteFromInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + modifyRouteOnInterface(aChain, aCallback, aResult, false); +} + +void NetworkUtils::modifyRouteOnInterface(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult, + bool aDoAdd) +{ + char command[MAX_COMMAND_SIZE]; + + // AOSP adds host route to its interface table but it doesn't work for + // B2G because we cannot set fwmark per application. So, we add + // all host routes to legacy_system table except scope link route. + + nsCString ipOrSubnetIp = NS_ConvertUTF16toUTF8(GET_FIELD(mIp)); + nsCString gatewayOrEmpty; + const char* legacyOrEmpty = "legacy 0 "; // Add to legacy by default. + if (GET_FIELD(mGateway).IsEmpty()) { + ipOrSubnetIp = getSubnetIp(ipOrSubnetIp, GET_FIELD(mPrefixLength)); + legacyOrEmpty = ""; // Add to interface table for scope link route. + } else { + gatewayOrEmpty = nsCString(" ") + NS_ConvertUTF16toUTF8(GET_FIELD(mGateway)); + } + + const char* action = aDoAdd ? "add" : "remove"; + + snprintf(command, MAX_COMMAND_SIZE - 1, "network route %s%s %d %s %s/%d%s", + legacyOrEmpty, action, + GET_FIELD(mNetId), GET_CHAR(mIfname), ipOrSubnetIp.get(), + GET_FIELD(mPrefixLength), gatewayOrEmpty.get()); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::addDefaultRouteToNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + if (GET_FIELD(mLoopIndex) >= GET_FIELD(mGateways).Length()) { + aCallback(aChain, false, aResult); + return; + } + + char command[MAX_COMMAND_SIZE]; + nsTArray<nsString>& gateways = GET_FIELD(mGateways); + NS_ConvertUTF16toUTF8 autoGateway(gateways[GET_FIELD(mLoopIndex)]); + + int type = getIpType(autoGateway.get()); + snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s %s/0 %s", + GET_FIELD(mNetId), GET_CHAR(mIfname), + type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get()); + + struct MyCallback { + static void callback(CommandCallback::CallbackType aOriginalCallback, + CommandChain* aChain, + bool aError, + mozilla::dom::NetworkResultOptions& aResult) + { + NS_ConvertUTF16toUTF8 reason(aResult.mResultReason); + NU_DBG("addDefaultRouteToNetwork's reason: %s", reason.get()); + if (aError && !reason.EqualsASCII("addRoute() failed (File exists)")) { + return aOriginalCallback(aChain, aError, aResult); + } + + GET_FIELD(mLoopIndex)++; + return addDefaultRouteToNetwork(aChain, aOriginalCallback, aResult); + } + }; + + CommandCallback wrappedCallback(MyCallback::callback, aCallback); + doCommand(command, aChain, wrappedCallback); +} + +void NetworkUtils::setDefaultNetwork(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "network default set %d", GET_FIELD(mNetId)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::addRouteToSecondaryTable(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) { + + char command[MAX_COMMAND_SIZE]; + + if (SDK_VERSION >= 20) { + snprintf(command, MAX_COMMAND_SIZE - 1, + "network route add %d %s %s/%s %s", + GET_FIELD(mNetId), + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mGateway)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, + "interface route add %s secondary %s %s %s", + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mGateway)); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::removeRouteFromSecondaryTable(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) { + char command[MAX_COMMAND_SIZE]; + + if (SDK_VERSION >= 20) { + snprintf(command, MAX_COMMAND_SIZE - 1, + "network route remove %d %s %s/%s %s", + GET_FIELD(mNetId), + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mGateway)); + } else { + snprintf(command, MAX_COMMAND_SIZE - 1, + "interface route remove %s secondary %s %s %s", + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mGateway)); + } + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setIpv6Enabled(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult, + bool aEnabled) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface ipv6 %s %s", + GET_CHAR(mIfname), aEnabled ? "enable" : "disable"); + + struct MyCallback { + static void callback(CommandCallback::CallbackType aOriginalCallback, + CommandChain* aChain, + bool aError, + mozilla::dom::NetworkResultOptions& aResult) + { + aOriginalCallback(aChain, false, aResult); + } + }; + + CommandCallback wrappedCallback(MyCallback::callback, aCallback); + doCommand(command, aChain, wrappedCallback); +} + +void NetworkUtils::enableIpv6(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + setIpv6Enabled(aChain, aCallback, aResult, true); +} + +void NetworkUtils::disableIpv6(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + setIpv6Enabled(aChain, aCallback, aResult, false); +} + +void NetworkUtils::setMtu(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface setmtu %s %ld", + GET_CHAR(mIfname), GET_FIELD(mMtu)); + + doCommand(command, aChain, aCallback); +} + +#undef GET_CHAR +#undef GET_FIELD + +/* + * Netd command success/fail function + */ +#define ASSIGN_FIELD(prop) aResult.prop = aChain->getParams().prop; +#define ASSIGN_FIELD_VALUE(prop, value) aResult.prop = value; + +template<size_t N> +void NetworkUtils::runChain(const NetworkParams& aParams, + const CommandFunc (&aCmds)[N], + ErrorCallback aError) +{ + CommandChain* chain = new CommandChain(aParams, aCmds, N, aError); + gCommandChainQueue.AppendElement(chain); + + if (gCommandChainQueue.Length() > 1) { + NU_DBG("%d command chains are queued. Wait!", gCommandChainQueue.Length()); + return; + } + + NetworkResultOptions result; + NetworkUtils::next(gCommandChainQueue[0], false, result); +} + +// Called to clean up the command chain and process the queued command chain if any. +void NetworkUtils::finalizeSuccess(CommandChain* aChain, + NetworkResultOptions& aResult) +{ + next(aChain, false, aResult); +} + +void NetworkUtils::wifiTetheringFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + // Notify the main thread. + postMessage(aOptions, aResult); + + // If one of the stages fails, we try roll back to ensure + // we don't leave the network systems in limbo. + ASSIGN_FIELD_VALUE(mEnable, false) + runChain(aOptions, sWifiFailChain, nullptr); +} + +void NetworkUtils::wifiTetheringSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + ASSIGN_FIELD(mEnable) + + if (aChain->getParams().mEnable) { + MOZ_ASSERT(!gWifiTetheringParms); + gWifiTetheringParms = new NetworkParams(aChain->getParams()); + } + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::usbTetheringFail(NetworkParams& aOptions, + NetworkResultOptions& aResult) +{ + // Notify the main thread. + postMessage(aOptions, aResult); + // Try to roll back to ensure + // we don't leave the network systems in limbo. + // This parameter is used to disable ipforwarding. + { + aOptions.mEnable = false; + runChain(aOptions, sUSBFailChain, nullptr); + } + + // Disable usb rndis function. + { + NetworkParams options; + options.mEnable = false; + options.mReport = false; + gNetworkUtils->enableUsbRndis(options); + } +} + +void NetworkUtils::usbTetheringSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + ASSIGN_FIELD(mEnable) + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::networkInterfaceAlarmFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::networkInterfaceAlarmSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + // TODO : error is not used , and it is conflict with boolean type error. + // params.error = parseFloat(params.resultReason); + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::updateUpStreamFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::updateUpStreamSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + ASSIGN_FIELD(mCurExternalIfname) + ASSIGN_FIELD(mCurInternalIfname) + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::setDhcpServerFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + aResult.mSuccess = false; + postMessage(aOptions, aResult); +} + +void NetworkUtils::setDhcpServerSuccess(CommandChain* aChain, CommandCallback aCallback, NetworkResultOptions& aResult) +{ + aResult.mSuccess = true; + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::wifiOperationModeFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::wifiOperationModeSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::setDnsFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::defaultAsyncSuccessHandler(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + NU_DBG("defaultAsyncSuccessHandler"); + aResult.mRet = true; + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::defaultAsyncFailureHandler(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + aResult.mRet = false; + postMessage(aOptions, aResult); +} + +void NetworkUtils::getInterfacesFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::getInterfacesSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char buf[BUF_SIZE]; + NS_ConvertUTF16toUTF8 reason(aResult.mResultReason); + memcpy(buf, reason.get(), strlen(reason.get())); + + nsTArray<nsCString> result; + split(buf, INTERFACE_DELIMIT, result); + + nsTArray<nsString> interfaceList; + uint32_t length = result.Length(); + convertUTF8toUTF16(result, interfaceList, length); + + aResult.mInterfaceList.Construct(); + for (uint32_t i = 0; i < length; i++) { + aResult.mInterfaceList.Value().AppendElement(interfaceList[i], fallible_t()); + } + + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::getInterfaceConfigFail(NetworkParams& aOptions, + NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::getInterfaceConfigSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char buf[BUF_SIZE]; + NS_ConvertUTF16toUTF8 reason(aResult.mResultReason); + memcpy(buf, reason.get(), strlen(reason.get())); + + nsTArray<nsCString> result; + split(buf, NETD_MESSAGE_DELIMIT, result); + + ASSIGN_FIELD_VALUE(mMacAddr, NS_ConvertUTF8toUTF16(result[0])) + ASSIGN_FIELD_VALUE(mIpAddr, NS_ConvertUTF8toUTF16(result[1])) + ASSIGN_FIELD_VALUE(mPrefixLength, atol(result[2].get())) + + if (result[3].Find("up")) { + ASSIGN_FIELD_VALUE(mFlag, NS_ConvertUTF8toUTF16("up")) + } else { + ASSIGN_FIELD_VALUE(mFlag, NS_ConvertUTF8toUTF16("down")) + } + + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +void NetworkUtils::setInterfaceConfigFail(NetworkParams& aOptions, + NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::setInterfaceConfigSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + postMessage(aChain->getParams(), aResult); + finalizeSuccess(aChain, aResult); +} + +#undef ASSIGN_FIELD +#undef ASSIGN_FIELD_VALUE + +NetworkUtils::NetworkUtils(MessageCallback aCallback) + : mMessageCallback(aCallback) +{ + mNetUtils = new NetUtils(); + + char value[Property::VALUE_MAX_LENGTH]; + Property::Get("ro.build.version.sdk", value, nullptr); + SDK_VERSION = atoi(value); + + Property::Get(IPV6_TETHERING, value, "0"); + SUPPORT_IPV6_TETHERING = atoi(value); + + gNetworkUtils = this; +} + +NetworkUtils::~NetworkUtils() +{ +} + +#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get() +#define GET_FIELD(prop) aOptions.prop + +// Hoist this type definition to global to avoid template +// instantiation error on gcc 4.4 used by ICS emulator. +typedef CommandResult (NetworkUtils::*CommandHandler)(NetworkParams&); +struct CommandHandlerEntry +{ + const char* mCommandName; + CommandHandler mCommandHandler; +}; + +void NetworkUtils::ExecuteCommand(NetworkParams aOptions) +{ + const static CommandHandlerEntry + COMMAND_HANDLER_TABLE[] = { + + // For command 'testCommand', BUILD_ENTRY(testCommand) will generate + // {"testCommand", NetworkUtils::testCommand} + #define BUILD_ENTRY(c) {#c, &NetworkUtils::c} + + BUILD_ENTRY(removeNetworkRoute), + BUILD_ENTRY(setDNS), + BUILD_ENTRY(setDefaultRoute), + BUILD_ENTRY(removeDefaultRoute), + BUILD_ENTRY(addHostRoute), + BUILD_ENTRY(removeHostRoute), + BUILD_ENTRY(addSecondaryRoute), + BUILD_ENTRY(removeSecondaryRoute), + BUILD_ENTRY(setNetworkInterfaceAlarm), + BUILD_ENTRY(enableNetworkInterfaceAlarm), + BUILD_ENTRY(disableNetworkInterfaceAlarm), + BUILD_ENTRY(setTetheringAlarm), + BUILD_ENTRY(removeTetheringAlarm), + BUILD_ENTRY(getTetheringStatus), + BUILD_ENTRY(setWifiOperationMode), + BUILD_ENTRY(setDhcpServer), + BUILD_ENTRY(setWifiTethering), + BUILD_ENTRY(setUSBTethering), + BUILD_ENTRY(enableUsbRndis), + BUILD_ENTRY(updateUpStream), + BUILD_ENTRY(configureInterface), + BUILD_ENTRY(dhcpRequest), + BUILD_ENTRY(stopDhcp), + BUILD_ENTRY(enableInterface), + BUILD_ENTRY(disableInterface), + BUILD_ENTRY(resetConnections), + BUILD_ENTRY(createNetwork), + BUILD_ENTRY(destroyNetwork), + BUILD_ENTRY(getNetId), + BUILD_ENTRY(getInterfaces), + BUILD_ENTRY(getInterfaceConfig), + BUILD_ENTRY(setInterfaceConfig), + BUILD_ENTRY(setMtu), + + #undef BUILD_ENTRY + }; + + // Loop until we find the command name which matches aOptions.mCmd. + CommandHandler handler = nullptr; + for (size_t i = 0; i < mozilla::ArrayLength(COMMAND_HANDLER_TABLE); i++) { + if (aOptions.mCmd.EqualsASCII(COMMAND_HANDLER_TABLE[i].mCommandName)) { + handler = COMMAND_HANDLER_TABLE[i].mCommandHandler; + break; + } + } + + if (!handler) { + // Command not found in COMMAND_HANDLER_TABLE. + WARN("unknown message: %s", NS_ConvertUTF16toUTF8(aOptions.mCmd).get()); + return; + } + + // The handler would return one of the following 3 values + // to be wrapped to CommandResult: + // + // 1) |int32_t| for mostly synchronous native function calls. + // 2) |NetworkResultOptions| to populate additional results. (e.g. dhcpRequest) + // 3) |CommandResult::Pending| to indicate the result is not + // obtained yet. + // + // If the handler returns "Pending", the handler should take the + // responsibility for posting result to main thread. + CommandResult commandResult = (this->*handler)(aOptions); + if (!commandResult.isPending()) { + postMessage(aOptions, commandResult.mResult); + } +} + +/** + * Handle received data from netd. + */ +void NetworkUtils::onNetdMessage(NetdCommand* aCommand) +{ + char* data = (char*)aCommand->mData; + + // get code & reason. + char* result = strtok(data, NETD_MESSAGE_DELIMIT); + + if (!result) { + nextNetdCommand(); + return; + } + uint32_t code = atoi(result); + + if (!isBroadcastMessage(code) && SDK_VERSION >= 16) { + strtok(nullptr, NETD_MESSAGE_DELIMIT); + } + + char* reason = strtok(nullptr, "\0"); + + if (isBroadcastMessage(code)) { + NU_DBG("Receiving broadcast message from netd."); + NU_DBG(" ==> Code: %d Reason: %s", code, reason); + sendBroadcastMessage(code, reason); + + if (code == NETD_COMMAND_INTERFACE_CHANGE) { + if (gWifiTetheringParms) { + char linkdownReason[MAX_COMMAND_SIZE]; + snprintf(linkdownReason, MAX_COMMAND_SIZE - 1, + "Iface linkstate %s down", + NS_ConvertUTF16toUTF8(gWifiTetheringParms->mIfname).get()); + + if (!strcmp(reason, linkdownReason)) { + NU_DBG("Wifi link down, restarting tethering."); + runChain(*gWifiTetheringParms, sWifiRetryChain, wifiTetheringFail); + } + } + } + + nextNetdCommand(); + return; + } + + // Set pending to false before we handle next command. + NU_DBG("Receiving \"%s\" command response from netd.", gCurrentCommand.command); + NU_DBG(" ==> Code: %d Reason: %s", code, reason); + + gReason.AppendElement(nsCString(reason)); + + // 1xx response code regards as command is proceeding, we need to wait for + // final response code such as 2xx, 4xx and 5xx before sending next command. + if (isProceeding(code)) { + return; + } + + if (isComplete(code)) { + gPending = false; + } + + { + char buf[BUF_SIZE]; + join(gReason, INTERFACE_DELIMIT, BUF_SIZE, buf); + + NetworkResultOptions result; + result.mResultCode = code; + result.mResultReason = NS_ConvertUTF8toUTF16(buf); + (gCurrentCommand.callback)(gCurrentCommand.chain, isError(code), result); + gReason.Clear(); + } + + // Handling pending commands if any. + if (isComplete(code)) { + nextNetdCommand(); + } +} + +/** + * Start/Stop DHCP server. + */ +CommandResult NetworkUtils::setDhcpServer(NetworkParams& aOptions) +{ + if (aOptions.mEnabled) { + aOptions.mWifiStartIp = aOptions.mStartIp; + aOptions.mWifiEndIp = aOptions.mEndIp; + aOptions.mIp = aOptions.mServerIp; + aOptions.mPrefix = aOptions.mMaskLength; + aOptions.mLink = NS_ConvertUTF8toUTF16("up"); + + runChain(aOptions, sStartDhcpServerChain, setDhcpServerFail); + } else { + runChain(aOptions, sStopDhcpServerChain, setDhcpServerFail); + } + return CommandResult::Pending(); +} + +/** + * Set DNS servers for given network interface. + */ +CommandResult NetworkUtils::setDNS(NetworkParams& aOptions) +{ + uint32_t length = aOptions.mDnses.Length(); + + if (length > 0) { + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoDns(aOptions.mDnses[i]); + + char dns_prop_key[Property::VALUE_MAX_LENGTH]; + SprintfLiteral(dns_prop_key, "net.dns%d", i+1); + Property::Set(dns_prop_key, autoDns.get()); + } + } else { + // Set dnses from system properties. + IFProperties interfaceProperties; + getIFProperties(GET_CHAR(mIfname), interfaceProperties); + + Property::Set("net.dns1", interfaceProperties.dns1); + Property::Set("net.dns2", interfaceProperties.dns2); + } + + // Bump the DNS change property. + char dnschange[Property::VALUE_MAX_LENGTH]; + Property::Get("net.dnschange", dnschange, "0"); + + char num[Property::VALUE_MAX_LENGTH]; + snprintf(num, Property::VALUE_MAX_LENGTH - 1, "%d", atoi(dnschange) + 1); + Property::Set("net.dnschange", num); + + // DNS needs to be set through netd since JellyBean (4.3). + if (SDK_VERSION >= 20) { + // Lollipop. + static CommandFunc COMMAND_CHAIN[] = { + setInterfaceDns, + addDefaultRouteToNetwork, + defaultAsyncSuccessHandler + }; + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(aOptions.mIfname, &netIdInfo)) { + return -1; + } + aOptions.mNetId = netIdInfo.mNetId; + runChain(aOptions, COMMAND_CHAIN, setDnsFail); + return CommandResult::Pending(); + } + if (SDK_VERSION >= 18) { + // JB, KK. + static CommandFunc COMMAND_CHAIN[] = { + #if ANDROID_VERSION == 18 + // Since we don't use per-interface DNS lookup feature on JB, + // we need to set the default DNS interface whenever setting the + // DNS name server. + setDefaultInterface, + #endif + setInterfaceDns, + defaultAsyncSuccessHandler + }; + runChain(aOptions, COMMAND_CHAIN, setDnsFail); + return CommandResult::Pending(); + } + + return SUCCESS; +} + +CommandResult NetworkUtils::configureInterface(NetworkParams& aOptions) +{ + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + return mNetUtils->do_ifc_configure( + autoIfname.get(), + aOptions.mIpaddr, + aOptions.mMask, + aOptions.mGateway_long, + aOptions.mDns1_long, + aOptions.mDns2_long + ); +} + +CommandResult NetworkUtils::stopDhcp(NetworkParams& aOptions) +{ + return mNetUtils->do_dhcp_stop(GET_CHAR(mIfname)); +} + +CommandResult NetworkUtils::dhcpRequest(NetworkParams& aOptions) { + mozilla::dom::NetworkResultOptions result; + + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + char ipaddr[Property::VALUE_MAX_LENGTH]; + char gateway[Property::VALUE_MAX_LENGTH]; + uint32_t prefixLength; + char dns1[Property::VALUE_MAX_LENGTH]; + char dns2[Property::VALUE_MAX_LENGTH]; + char server[Property::VALUE_MAX_LENGTH]; + uint32_t lease; + char vendorinfo[Property::VALUE_MAX_LENGTH]; + int32_t ret = mNetUtils->do_dhcp_do_request(autoIfname.get(), + ipaddr, + gateway, + &prefixLength, + dns1, + dns2, + server, + &lease, + vendorinfo); + + RETURN_IF_FAILED(ret); + + result.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr); + result.mGateway_str = NS_ConvertUTF8toUTF16(gateway); + result.mDns1_str = NS_ConvertUTF8toUTF16(dns1); + result.mDns2_str = NS_ConvertUTF8toUTF16(dns2); + result.mServer_str = NS_ConvertUTF8toUTF16(server); + result.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo); + result.mLease = lease; + result.mPrefixLength = prefixLength; + result.mMask = makeMask(prefixLength); + + uint32_t inet4; // only support IPv4 for now. + +#define INET_PTON(var, field) \ + PR_BEGIN_MACRO \ + inet_pton(AF_INET, var, &inet4); \ + result.field = inet4; \ + PR_END_MACRO + + INET_PTON(ipaddr, mIpaddr); + INET_PTON(gateway, mGateway); + + if (dns1[0] != '\0') { + INET_PTON(dns1, mDns1); + } + + if (dns2[0] != '\0') { + INET_PTON(dns2, mDns2); + } + + INET_PTON(server, mServer); + + char inet_str[64]; + if (inet_ntop(AF_INET, &result.mMask, inet_str, sizeof(inet_str))) { + result.mMask_str = NS_ConvertUTF8toUTF16(inet_str); + } + + return result; +} + +CommandResult NetworkUtils::enableInterface(NetworkParams& aOptions) { + return mNetUtils->do_ifc_enable( + NS_ConvertUTF16toUTF8(aOptions.mIfname).get()); +} + +CommandResult NetworkUtils::disableInterface(NetworkParams& aOptions) { + return mNetUtils->do_ifc_disable( + NS_ConvertUTF16toUTF8(aOptions.mIfname).get()); +} + +CommandResult NetworkUtils::resetConnections(NetworkParams& aOptions) { + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + return mNetUtils->do_ifc_reset_connections( + NS_ConvertUTF16toUTF8(aOptions.mIfname).get(), + RESET_ALL_ADDRESSES); +} + +/** + * Set default route and DNS servers for given network interface. + */ +CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions) +{ + if (SDK_VERSION < 20) { + return setDefaultRouteLegacy(aOptions); + } + + static CommandFunc COMMAND_CHAIN[] = { + addDefaultRouteToNetwork, + setDefaultNetwork, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) { + ERROR("No such interface"); + return -1; + } + + aOptions.mNetId = netIdInfo.mNetId; + aOptions.mLoopIndex = 0; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + + return CommandResult::Pending(); +} + +/** + * Set default route and DNS servers for given network interface by obsoleted libnetutils. + */ +CommandResult NetworkUtils::setDefaultRouteLegacy(NetworkParams& aOptions) +{ + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + + uint32_t length = aOptions.mGateways.Length(); + if (length > 0) { + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[i]); + + int type = getIpType(autoGateway.get()); + if (type != AF_INET && type != AF_INET6) { + continue; + } + + if (type == AF_INET6) { + RETURN_IF_FAILED(mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, autoGateway.get())); + } else { /* type == AF_INET */ + RETURN_IF_FAILED(mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(autoGateway.get()))); + } + } + } else { + // Set default froute from system properties. + char key[Property::KEY_MAX_LENGTH]; + char gateway[Property::KEY_MAX_LENGTH]; + + snprintf(key, sizeof key - 1, "net.%s.gw", autoIfname.get()); + Property::Get(key, gateway, ""); + + int type = getIpType(gateway); + if (type != AF_INET && type != AF_INET6) { + return EAFNOSUPPORT; + } + + if (type == AF_INET6) { + RETURN_IF_FAILED(mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, gateway)); + } else { /* type == AF_INET */ + RETURN_IF_FAILED(mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(gateway))); + } + } + + // Set the default DNS interface. + if (SDK_VERSION >= 18) { + // For JB, KK only. + static CommandFunc COMMAND_CHAIN[] = { + setDefaultInterface, + defaultAsyncSuccessHandler + }; + runChain(aOptions, COMMAND_CHAIN, setDnsFail); + return CommandResult::Pending(); + } + + return SUCCESS; +} + +/** + * Remove default route for given network interface. + */ +CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions) +{ + NU_DBG("Calling NetworkUtils::removeDefaultRoute"); + + if (SDK_VERSION < 20) { + return removeDefaultRouteLegacy(aOptions); + } + + static CommandFunc COMMAND_CHAIN[] = { + removeDefaultRoute, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) { + ERROR("No such interface: %s", GET_CHAR(mIfname)); + return -1; + } + + NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname)); + + aOptions.mNetId = netIdInfo.mNetId; + aOptions.mLoopIndex = 0; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + + return CommandResult::Pending(); +} + +/** + * Remove default route for given network interface by obsoleted libnetutils. + */ +CommandResult NetworkUtils::removeDefaultRouteLegacy(NetworkParams& aOptions) +{ + // Legacy libnetutils calls before Lollipop. + uint32_t length = aOptions.mGateways.Length(); + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[i]); + + int type = getIpType(autoGateway.get()); + if (type != AF_INET && type != AF_INET6) { + return EAFNOSUPPORT; + } + + WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname), + type == AF_INET ? "0.0.0.0" : "::", + 0, autoGateway.get())); + } + + return SUCCESS; +} + +/** + * Add host route for given network interface. + */ +CommandResult NetworkUtils::addHostRoute(NetworkParams& aOptions) +{ + if (SDK_VERSION < 20) { + return addHostRouteLegacy(aOptions); + } + + static CommandFunc COMMAND_CHAIN[] = { + addRouteToInterface, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) { + ERROR("No such interface: %s", GET_CHAR(mIfname)); + return -1; + } + + NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname)); + + aOptions.mNetId = netIdInfo.mNetId; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + + return CommandResult::Pending(); +} + +/** + * Add host route for given network interface. + */ +CommandResult NetworkUtils::addHostRouteLegacy(NetworkParams& aOptions) +{ + if (aOptions.mGateway.IsEmpty()) { + ERROR("addHostRouteLegacy does not support empty gateway."); + return EINVAL; + } + + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp); + NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway); + int type, prefix; + + type = getIpType(autoHostname.get()); + if (type != AF_INET && type != AF_INET6) { + return EAFNOSUPPORT; + } + + if (type != getIpType(autoGateway.get())) { + return EINVAL; + } + + prefix = type == AF_INET ? 32 : 128; + return mNetUtils->do_ifc_add_route(autoIfname.get(), autoHostname.get(), + prefix, autoGateway.get()); +} + +/** + * Remove host route for given network interface. + */ +CommandResult NetworkUtils::removeHostRoute(NetworkParams& aOptions) +{ + if (SDK_VERSION < 20) { + return removeHostRouteLegacy(aOptions); + } + + static CommandFunc COMMAND_CHAIN[] = { + removeRouteFromInterface, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) { + ERROR("No such interface: %s", GET_CHAR(mIfname)); + return -1; + } + + NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname)); + + aOptions.mNetId = netIdInfo.mNetId; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + + return CommandResult::Pending(); +} + +/** + * Remove host route for given network interface. + */ +CommandResult NetworkUtils::removeHostRouteLegacy(NetworkParams& aOptions) +{ + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp); + NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway); + int type, prefix; + + type = getIpType(autoHostname.get()); + if (type != AF_INET && type != AF_INET6) { + return EAFNOSUPPORT; + } + + if (type != getIpType(autoGateway.get())) { + return EINVAL; + } + + prefix = type == AF_INET ? 32 : 128; + return mNetUtils->do_ifc_remove_route(autoIfname.get(), autoHostname.get(), + prefix, autoGateway.get()); +} + +CommandResult NetworkUtils::removeNetworkRoute(NetworkParams& aOptions) +{ + if (SDK_VERSION < 20) { + return removeNetworkRouteLegacy(aOptions); + } + + static CommandFunc COMMAND_CHAIN[] = { + clearAddrForInterface, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) { + ERROR("interface %s is not present in any network", GET_CHAR(mIfname)); + return -1; + } + + NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname)); + + aOptions.mNetId = netIdInfo.mNetId; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + + return CommandResult::Pending(); +} + +nsCString NetworkUtils::getSubnetIp(const nsCString& aIp, int aPrefixLength) +{ + int type = getIpType(aIp.get()); + + if (AF_INET6 == type) { + struct in6_addr in6; + if (inet_pton(AF_INET6, aIp.get(), &in6) != 1) { + return nsCString(); + } + + uint32_t p, i, p1, mask; + p = aPrefixLength; + 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 nsCString(); + } + + return nsCString(subnetStr); + } + + if (AF_INET == type) { + uint32_t ip = inet_addr(aIp.get()); + uint32_t netmask = makeMask(aPrefixLength); + uint32_t subnet = ip & netmask; + struct in_addr addr; + addr.s_addr = subnet; + return nsCString(inet_ntoa(addr)); + } + + return nsCString(); +} + +CommandResult NetworkUtils::removeNetworkRouteLegacy(NetworkParams& aOptions) +{ + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + NS_ConvertUTF16toUTF8 autoIp(aOptions.mIp); + + int type = getIpType(autoIp.get()); + if (type != AF_INET && type != AF_INET6) { + return EAFNOSUPPORT; + } + + 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 EINVAL; + } + + 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 EINVAL; + } + + // Remove default route. + WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL)); + + // Remove subnet route. + RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), subnetStr, prefixLength, NULL)); + return SUCCESS; + } + + /* 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); + + RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(autoIfname.get())); + RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), dst, prefixLength, gateway)); + return SUCCESS; +} + +CommandResult NetworkUtils::addSecondaryRoute(NetworkParams& aOptions) +{ + static CommandFunc COMMAND_CHAIN[] = { + addRouteToSecondaryTable, + defaultAsyncSuccessHandler + }; + + if (SDK_VERSION >= 20) { + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(aOptions.mIfname, &netIdInfo)) { + return -1; + } + aOptions.mNetId = netIdInfo.mNetId; + } + + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::removeSecondaryRoute(NetworkParams& aOptions) +{ + static CommandFunc COMMAND_CHAIN[] = { + removeRouteFromSecondaryTable, + defaultAsyncSuccessHandler + }; + + if (SDK_VERSION >= 20) { + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(aOptions.mIfname, &netIdInfo)) { + return -1; + } + aOptions.mNetId = netIdInfo.mNetId; + } + + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::setNetworkInterfaceAlarm(NetworkParams& aOptions) +{ + NU_DBG("setNetworkInterfaceAlarms: %s", GET_CHAR(mIfname)); + runChain(aOptions, sNetworkInterfaceSetAlarmChain, networkInterfaceAlarmFail); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::enableNetworkInterfaceAlarm(NetworkParams& aOptions) +{ + NU_DBG("enableNetworkInterfaceAlarm: %s", GET_CHAR(mIfname)); + runChain(aOptions, sNetworkInterfaceEnableAlarmChain, networkInterfaceAlarmFail); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::disableNetworkInterfaceAlarm(NetworkParams& aOptions) +{ + NU_DBG("disableNetworkInterfaceAlarms: %s", GET_CHAR(mIfname)); + runChain(aOptions, sNetworkInterfaceDisableAlarmChain, networkInterfaceAlarmFail); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::setTetheringAlarm(NetworkParams& aOptions) +{ + NU_DBG("setTetheringAlarm"); + runChain(aOptions, sTetheringInterfaceSetAlarmChain, networkInterfaceAlarmFail); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::removeTetheringAlarm(NetworkParams& aOptions) +{ + NU_DBG("removeTetheringAlarm"); + runChain(aOptions, sTetheringInterfaceRemoveAlarmChain, networkInterfaceAlarmFail); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::getTetheringStatus(NetworkParams& aOptions) +{ + NU_DBG("getTetheringStatus"); + runChain(aOptions, sTetheringGetStatusChain, networkInterfaceAlarmFail); + return CommandResult::Pending(); +} + +/** + * handling main thread's reload Wifi firmware request + */ +CommandResult NetworkUtils::setWifiOperationMode(NetworkParams& aOptions) +{ + NU_DBG("setWifiOperationMode: %s %s", GET_CHAR(mIfname), GET_CHAR(mMode)); + runChain(aOptions, sWifiOperationModeChain, wifiOperationModeFail); + return CommandResult::Pending(); +} + +/** + * handling main thread's enable/disable WiFi Tethering request + */ +CommandResult NetworkUtils::setWifiTethering(NetworkParams& aOptions) +{ + bool enable = aOptions.mEnable; + IFProperties interfaceProperties; + getIFProperties(GET_CHAR(mExternalIfname), interfaceProperties); + + if (strcmp(interfaceProperties.dns1, "")) { + int type = getIpType(interfaceProperties.dns1); + if (type != AF_INET6) { + aOptions.mDns1 = NS_ConvertUTF8toUTF16(interfaceProperties.dns1); + } + } + if (strcmp(interfaceProperties.dns2, "")) { + int type = getIpType(interfaceProperties.dns2); + if (type != AF_INET6) { + aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2); + } + } + dumpParams(aOptions, "WIFI"); + + if (SDK_VERSION >= 20) { + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(aOptions.mExternalIfname, &netIdInfo)) { + ERROR("No such interface: %s", GET_CHAR(mExternalIfname)); + return -1; + } + aOptions.mNetId = netIdInfo.mNetId; + } + + if (enable) { + NU_DBG("Starting Wifi Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + runChain(aOptions, sWifiEnableChain, wifiTetheringFail); + } else { + NU_DBG("Stopping Wifi Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + runChain(aOptions, sWifiDisableChain, wifiTetheringFail); + } + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::setUSBTethering(NetworkParams& aOptions) +{ + bool enable = aOptions.mEnable; + IFProperties interfaceProperties; + getIFProperties(GET_CHAR(mExternalIfname), interfaceProperties); + + if (strcmp(interfaceProperties.dns1, "")) { + int type = getIpType(interfaceProperties.dns1); + if (type != AF_INET6) { + aOptions.mDns1 = NS_ConvertUTF8toUTF16(interfaceProperties.dns1); + } + } + if (strcmp(interfaceProperties.dns2, "")) { + int type = getIpType(interfaceProperties.dns2); + if (type != AF_INET6) { + aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2); + } + } + dumpParams(aOptions, "USB"); + + if (SDK_VERSION >= 20) { + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.lookup(aOptions.mExternalIfname, &netIdInfo)) { + ERROR("No such interface: %s", GET_CHAR(mExternalIfname)); + return -1; + } + aOptions.mNetId = netIdInfo.mNetId; + } + + if (enable) { + NU_DBG("Starting USB Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + runChain(aOptions, sUSBEnableChain, usbTetheringFail); + } else { + NU_DBG("Stopping USB Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + runChain(aOptions, sUSBDisableChain, usbTetheringFail); + } + return CommandResult::Pending(); +} + +void NetworkUtils::escapeQuote(nsCString& aString) +{ + aString.ReplaceSubstring("\\", "\\\\"); + aString.ReplaceSubstring("\"", "\\\""); +} + +CommandResult NetworkUtils::checkUsbRndisState(NetworkParams& aOptions) +{ + static uint32_t retry = 0; + + char currentState[Property::VALUE_MAX_LENGTH]; + Property::Get(SYS_USB_STATE_PROPERTY, currentState, nullptr); + + nsTArray<nsCString> stateFuncs; + split(currentState, USB_CONFIG_DELIMIT, stateFuncs); + bool rndisPresent = stateFuncs.Contains(nsCString(USB_FUNCTION_RNDIS)); + + if (aOptions.mEnable == rndisPresent) { + NetworkResultOptions result; + result.mEnable = aOptions.mEnable; + result.mResult = true; + retry = 0; + return result; + } + if (retry < USB_FUNCTION_RETRY_TIMES) { + retry++; + usleep(USB_FUNCTION_RETRY_INTERVAL * 1000); + return checkUsbRndisState(aOptions); + } + + NetworkResultOptions result; + result.mResult = false; + retry = 0; + return result; +} + +/** + * Modify usb function's property to turn on USB RNDIS function + */ +CommandResult NetworkUtils::enableUsbRndis(NetworkParams& aOptions) +{ + bool report = aOptions.mReport; + + // For some reason, rndis doesn't play well with diag,modem,nmea. + // So when turning rndis on, we set sys.usb.config to either "rndis" + // or "rndis,adb". When turning rndis off, we go back to + // persist.sys.usb.config. + // + // On the otoro/unagi, persist.sys.usb.config should be one of: + // + // diag,modem,nmea,mass_storage + // diag,modem,nmea,mass_storage,adb + // + // When rndis is enabled, sys.usb.config should be one of: + // + // rdnis + // rndis,adb + // + // and when rndis is disabled, it should revert to persist.sys.usb.config + + char currentConfig[Property::VALUE_MAX_LENGTH]; + Property::Get(SYS_USB_CONFIG_PROPERTY, currentConfig, nullptr); + + nsTArray<nsCString> configFuncs; + split(currentConfig, USB_CONFIG_DELIMIT, configFuncs); + + char persistConfig[Property::VALUE_MAX_LENGTH]; + Property::Get(PERSIST_SYS_USB_CONFIG_PROPERTY, persistConfig, nullptr); + + nsTArray<nsCString> persistFuncs; + split(persistConfig, USB_CONFIG_DELIMIT, persistFuncs); + + if (aOptions.mEnable) { + configFuncs.Clear(); + configFuncs.AppendElement(nsCString(USB_FUNCTION_RNDIS)); + if (persistFuncs.Contains(nsCString(USB_FUNCTION_ADB))) { + configFuncs.AppendElement(nsCString(USB_FUNCTION_ADB)); + } + } else { + // We're turning rndis off, revert back to the persist setting. + // adb will already be correct there, so we don't need to do any + // further adjustments. + configFuncs = persistFuncs; + } + + char newConfig[Property::VALUE_MAX_LENGTH] = ""; + Property::Get(SYS_USB_CONFIG_PROPERTY, currentConfig, nullptr); + join(configFuncs, USB_CONFIG_DELIMIT, Property::VALUE_MAX_LENGTH, newConfig); + if (strcmp(currentConfig, newConfig)) { + Property::Set(SYS_USB_CONFIG_PROPERTY, newConfig); + } + + // Trigger the timer to check usb state and report the result to NetworkManager. + if (report) { + usleep(USB_FUNCTION_RETRY_INTERVAL * 1000); + return checkUsbRndisState(aOptions); + } + return SUCCESS; +} + +/** + * handling upstream interface change event. + */ +CommandResult NetworkUtils::updateUpStream(NetworkParams& aOptions) +{ + runChain(aOptions, sUpdateUpStreamChain, updateUpStreamFail); + return CommandResult::Pending(); +} + +/** + * handling upstream interface change event. + */ +CommandResult NetworkUtils::createNetwork(NetworkParams& aOptions) +{ + if (SDK_VERSION < 20) { + return SUCCESS; + } + + static CommandFunc COMMAND_CHAIN[] = { + createNetwork, + enableIpv6, + addInterfaceToNetwork, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + mNetIdManager.acquire(GET_FIELD(mIfname), &netIdInfo); + if (netIdInfo.mRefCnt > 1) { + // Already created. Just return. + NU_DBG("Interface %s (%d) has been created.", GET_CHAR(mIfname), + netIdInfo.mNetId); + return SUCCESS; + } + + NU_DBG("Request netd to create a network with netid %d", netIdInfo.mNetId); + // Newly created netid. Ask netd to create network. + aOptions.mNetId = netIdInfo.mNetId; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + + return CommandResult::Pending(); +} + +/** + * handling upstream interface change event. + */ +CommandResult NetworkUtils::destroyNetwork(NetworkParams& aOptions) +{ + if (SDK_VERSION < 20) { + return SUCCESS; + } + + static CommandFunc COMMAND_CHAIN[] = { + disableIpv6, + destroyNetwork, + defaultAsyncSuccessHandler, + }; + + NetIdManager::NetIdInfo netIdInfo; + if (!mNetIdManager.release(GET_FIELD(mIfname), &netIdInfo)) { + ERROR("No existing netid for %s", GET_CHAR(mIfname)); + return -1; + } + + if (netIdInfo.mRefCnt > 0) { + // Still be referenced. Just return. + NU_DBG("Someone is still using this interface."); + return SUCCESS; + } + + NU_DBG("Interface %s (%d) is no longer used. Tell netd to destroy.", + GET_CHAR(mIfname), netIdInfo.mNetId); + + aOptions.mNetId = netIdInfo.mNetId; + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + return CommandResult::Pending(); +} + +/** + * Query the netId associated with the given network interface name. + */ +CommandResult NetworkUtils::getNetId(NetworkParams& aOptions) +{ + NetworkResultOptions result; + + if (SDK_VERSION < 20) { + // For pre-Lollipop, use the interface name as the fallback. + result.mNetId = GET_FIELD(mIfname); + return result; + } + + NetIdManager::NetIdInfo netIdInfo; + if (-1 == mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) { + return ESRCH; + } + result.mNetId.AppendInt(netIdInfo.mNetId, 10); + return result; +} + +/** + * Get existing network interfaces. + */ +CommandResult NetworkUtils::getInterfaces(NetworkParams& aOptions) +{ + runChain(aOptions, sGetInterfacesChain, getInterfacesFail); + return CommandResult::Pending(); +} + +/** + * Get network config of a specified interface. + */ +CommandResult NetworkUtils::getInterfaceConfig(NetworkParams& aOptions) +{ + runChain(aOptions, sGetInterfaceConfigChain, getInterfaceConfigFail); + return CommandResult::Pending(); +} + +/** + * Set network config for a specified interface. + */ +CommandResult NetworkUtils::setInterfaceConfig(NetworkParams& aOptions) +{ + runChain(aOptions, sSetInterfaceConfigChain, setInterfaceConfigFail); + return CommandResult::Pending(); +} + +CommandResult NetworkUtils::setMtu(NetworkParams& aOptions) +{ + // Setting/getting mtu is supported since Kitkat. + if (SDK_VERSION < 19) { + ERROR("setMtu is not supported in current SDK_VERSION."); + return -1; + } + + static CommandFunc COMMAND_CHAIN[] = { + setMtu, + defaultAsyncSuccessHandler, + }; + + runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler); + return CommandResult::Pending(); +} + +void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason) +{ + NetworkResultOptions result; + switch(code) { + case NETD_COMMAND_INTERFACE_CHANGE: + result.mTopic = NS_ConvertUTF8toUTF16("netd-interface-change"); + break; + case NETD_COMMAND_BANDWIDTH_CONTROLLER: + result.mTopic = NS_ConvertUTF8toUTF16("netd-bandwidth-control"); + break; + default: + return; + } + + result.mBroadcast = true; + result.mReason = NS_ConvertUTF8toUTF16(reason); + postMessage(result); +} + +inline uint32_t NetworkUtils::netdResponseType(uint32_t code) +{ + return (code / 100) * 100; +} + +inline bool NetworkUtils::isBroadcastMessage(uint32_t code) +{ + uint32_t type = netdResponseType(code); + return type == NETD_COMMAND_UNSOLICITED; +} + +inline bool NetworkUtils::isError(uint32_t code) +{ + uint32_t type = netdResponseType(code); + return type != NETD_COMMAND_PROCEEDING && type != NETD_COMMAND_OKAY; +} + +inline bool NetworkUtils::isComplete(uint32_t code) +{ + uint32_t type = netdResponseType(code); + return type != NETD_COMMAND_PROCEEDING; +} + +inline bool NetworkUtils::isProceeding(uint32_t code) +{ + uint32_t type = netdResponseType(code); + return type == NETD_COMMAND_PROCEEDING; +} + +void NetworkUtils::dumpParams(NetworkParams& aOptions, const char* aType) +{ +#ifdef _DEBUG + NU_DBG("Dump params:"); + NU_DBG(" ifname: %s", GET_CHAR(mIfname)); + NU_DBG(" ip: %s", GET_CHAR(mIp)); + NU_DBG(" link: %s", GET_CHAR(mLink)); + NU_DBG(" prefix: %s", GET_CHAR(mPrefix)); + NU_DBG(" wifiStartIp: %s", GET_CHAR(mWifiStartIp)); + NU_DBG(" wifiEndIp: %s", GET_CHAR(mWifiEndIp)); + NU_DBG(" usbStartIp: %s", GET_CHAR(mUsbStartIp)); + NU_DBG(" usbEndIp: %s", GET_CHAR(mUsbEndIp)); + NU_DBG(" dnsserver1: %s", GET_CHAR(mDns1)); + NU_DBG(" dnsserver2: %s", GET_CHAR(mDns2)); + NU_DBG(" internalIfname: %s", GET_CHAR(mInternalIfname)); + NU_DBG(" externalIfname: %s", GET_CHAR(mExternalIfname)); + if (!strcmp(aType, "WIFI")) { + NU_DBG(" wifictrlinterfacename: %s", GET_CHAR(mWifictrlinterfacename)); + NU_DBG(" ssid: %s", GET_CHAR(mSsid)); + NU_DBG(" security: %s", GET_CHAR(mSecurity)); + NU_DBG(" key: %s", GET_CHAR(mKey)); + } +#endif +} + +#undef GET_CHAR +#undef GET_FIELD |