diff -r 000000000000 -r 6474c204b198 dom/system/gonk/NetworkUtils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/system/gonk/NetworkUtils.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1735 @@ +/* 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 +#include +#include +#include "mozilla/dom/network/NetUtils.h" + +#include // struct addrinfo +#include // getaddrinfo(), freeaddrinfo() +#include +#include // 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 +#define DEBUG(args...) +#endif + +using namespace mozilla::dom; +using namespace mozilla::ipc; + +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"; + +// 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 uint32_t SDK_VERSION; + +struct IFProperties { + char gateway[PROPERTY_VALUE_MAX]; + char dns1[PROPERTY_VALUE_MAX]; + char dns2[PROPERTY_VALUE_MAX]; +}; + +struct CurrentCommand { + CommandChain* chain; + CommandCallback callback; + char command[MAX_COMMAND_SIZE]; +}; + +typedef Tuple3 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) + +static NetworkUtils* gNetworkUtils; +static nsTArray gCommandQueue; +static CurrentCommand gCurrentCommand; +static bool gPending = false; +static nsTArray gReason; + +CommandFunc NetworkUtils::sWifiEnableChain[] = { + NetworkUtils::wifiFirmwareReload, + NetworkUtils::startAccessPointDriver, + NetworkUtils::setAccessPoint, + NetworkUtils::startSoftAP, + NetworkUtils::setInterfaceUp, + NetworkUtils::tetherInterface, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::tetheringStatus, + NetworkUtils::startTethering, + NetworkUtils::setDnsForwarders, + NetworkUtils::enableNat, + NetworkUtils::wifiTetheringSuccess +}; + +CommandFunc NetworkUtils::sWifiDisableChain[] = { + NetworkUtils::stopSoftAP, + NetworkUtils::stopAccessPointDriver, + NetworkUtils::wifiFirmwareReload, + NetworkUtils::untetherInterface, + NetworkUtils::preTetherInterfaceList, + NetworkUtils::postTetherInterfaceList, + NetworkUtils::disableNat, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering, + NetworkUtils::wifiTetheringSuccess +}; + +CommandFunc NetworkUtils::sWifiFailChain[] = { + NetworkUtils::stopSoftAP, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering +}; + +CommandFunc NetworkUtils::sWifiOperationModeChain[] = { + NetworkUtils::wifiFirmwareReload, + NetworkUtils::wifiOperationModeSuccess +}; + +CommandFunc NetworkUtils::sUSBEnableChain[] = { + NetworkUtils::setInterfaceUp, + NetworkUtils::enableNat, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::tetherInterface, + NetworkUtils::tetheringStatus, + NetworkUtils::startTethering, + NetworkUtils::setDnsForwarders, + NetworkUtils::usbTetheringSuccess +}; + +CommandFunc NetworkUtils::sUSBDisableChain[] = { + NetworkUtils::untetherInterface, + NetworkUtils::preTetherInterfaceList, + NetworkUtils::postTetherInterfaceList, + NetworkUtils::disableNat, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering, + NetworkUtils::usbTetheringSuccess +}; + +CommandFunc NetworkUtils::sUSBFailChain[] = { + NetworkUtils::stopSoftAP, + NetworkUtils::setIpForwardingEnabled, + NetworkUtils::stopTethering +}; + +CommandFunc NetworkUtils::sUpdateUpStreamChain[] = { + NetworkUtils::cleanUpStream, + NetworkUtils::createUpStream, + NetworkUtils::updateUpStreamSuccess +}; + +CommandFunc NetworkUtils::sStartDhcpServerChain[] = { + NetworkUtils::setInterfaceUp, + NetworkUtils::startTethering, + NetworkUtils::setDhcpServerSuccess +}; + +CommandFunc NetworkUtils::sStopDhcpServerChain[] = { + NetworkUtils::stopTethering, + NetworkUtils::setDhcpServerSuccess +}; + +CommandFunc NetworkUtils::sNetworkInterfaceStatsChain[] = { + NetworkUtils::getRxBytes, + NetworkUtils::getTxBytes, + NetworkUtils::networkInterfaceStatsSuccess +}; + +CommandFunc NetworkUtils::sNetworkInterfaceEnableAlarmChain[] = { + NetworkUtils::enableAlarm, + NetworkUtils::setQuota, + NetworkUtils::setAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +CommandFunc NetworkUtils::sNetworkInterfaceDisableAlarmChain[] = { + NetworkUtils::removeQuota, + NetworkUtils::disableAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +CommandFunc NetworkUtils::sNetworkInterfaceSetAlarmChain[] = { + NetworkUtils::setAlarm, + NetworkUtils::networkInterfaceAlarmSuccess +}; + +CommandFunc NetworkUtils::sSetDnsChain[] = { + NetworkUtils::setDefaultInterface, + NetworkUtils::setInterfaceDns +}; + +/** + * 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& 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& 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& 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 +} + +/** + * 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]; + 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; +} + +/** + * Helper function to find the best match gateway. For now, return + * the gateway that matches the address family passed. + */ +static uint32_t selectGateway(nsTArray& gateways, int addrFamily) +{ + uint32_t length = gateways.Length(); + + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoGateway(gateways[i]); + if ((getIpType(autoGateway.get()) == AF_INET && addrFamily == AF_INET) || + (getIpType(autoGateway.get()) == AF_INET6 && addrFamily == AF_INET6)) { + return i; + } + } + return length; // invalid index. +} + +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::next(CommandChain* aChain, bool aError, NetworkResultOptions& aResult) +{ + if (aError) { + ErrorCallback onError = aChain->getErrorCallback(); + if(onError) { + aResult.mError = true; + (*onError)(aChain->getParams(), aResult); + } + delete aChain; + return; + } + CommandFunc f = aChain->getNextCommand(); + if (!f) { + delete aChain; + return; + } + + (*f)(aChain, next, aResult); +} + +/** + * 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); + + DEBUG("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) +{ + DEBUG("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::getRxBytes(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface readrxcounter %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::getTxBytes(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + NetworkParams& options = aChain->getParams(); + options.mRxBytes = atof(NS_ConvertUTF16toUTF8(aResult.mResultReason).get()); + + char command[MAX_COMMAND_SIZE]; + snprintf(command, MAX_COMMAND_SIZE - 1, "interface readtxcounter %s", GET_CHAR(mIfname)); + + doCommand(command, aChain, aCallback); +} + +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 %lld", GET_CHAR(mIfname), LLONG_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 %ld", GET_CHAR(mIfname), GET_FIELD(mThreshold)); + + doCommand(command, aChain, aCallback); +} + +void NetworkUtils::setInterfaceUp(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::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::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]; + const char* reason = NS_ConvertUTF16toUTF8(aResult.mResultReason).get(); + memcpy(buf, reason, strlen(reason)); + split(buf, INTERFACE_DELIMIT, GET_FIELD(mInterfaceList)); + + 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 { + snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s", GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp)); + + // 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, "%s %s %s", command, GET_CHAR(mUsbStartIp), GET_CHAR(mUsbEndIp)); + } + } + + 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::setDnsForwarders(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + 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::setInterfaceDns(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + char command[MAX_COMMAND_SIZE]; + int written = snprintf(command, sizeof command, "resolver setifdns %s %s", + GET_CHAR(mIfname), GET_CHAR(mDomain)); + + nsTArray& 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 ((ret + written) >= sizeof(command)) { + command[written] = '\0'; + break; + } + + written += ret; + } + + 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; + +#define RUN_CHAIN(param, cmds, err) \ + uint32_t size = sizeof(cmds) / sizeof(CommandFunc); \ + CommandChain* chain = new CommandChain(param, cmds, size, err); \ + NetworkResultOptions result; \ + next(chain, false, result); + +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) + RUN_CHAIN(aOptions, sWifiFailChain, nullptr) +} + +void NetworkUtils::wifiTetheringSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + ASSIGN_FIELD(mEnable) + postMessage(aChain->getParams(), 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; + RUN_CHAIN(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); +} + +void NetworkUtils::networkInterfaceStatsFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::networkInterfaceStatsSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + ASSIGN_FIELD(mRxBytes) + ASSIGN_FIELD_VALUE(mTxBytes, atof(NS_ConvertUTF16toUTF8(aResult.mResultReason).get())) + postMessage(aChain->getParams(), 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); +} + +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); +} + +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); +} + +void NetworkUtils::wifiOperationModeFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +void NetworkUtils::wifiOperationModeSuccess(CommandChain* aChain, + CommandCallback aCallback, + NetworkResultOptions& aResult) +{ + postMessage(aChain->getParams(), aResult); +} + +void NetworkUtils::setDnsFail(NetworkParams& aOptions, NetworkResultOptions& aResult) +{ + postMessage(aOptions, aResult); +} + +#undef ASSIGN_FIELD +#undef ASSIGN_FIELD_VALUE + +NetworkUtils::NetworkUtils(MessageCallback aCallback) + : mMessageCallback(aCallback) +{ + mNetUtils = new NetUtils(); + + char value[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.sdk", value, nullptr); + SDK_VERSION = atoi(value); + + gNetworkUtils = this; +} + +NetworkUtils::~NetworkUtils() +{ +} + +#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get() +#define GET_FIELD(prop) aOptions.prop + +void NetworkUtils::ExecuteCommand(NetworkParams aOptions) +{ + bool ret = true; + + if (aOptions.mCmd.EqualsLiteral("removeNetworkRoute")) { + removeNetworkRoute(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setDNS")) { + setDNS(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setDefaultRouteAndDNS")) { + setDefaultRouteAndDNS(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("removeDefaultRoute")) { + removeDefaultRoute(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("addHostRoute")) { + addHostRoute(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("removeHostRoute")) { + removeHostRoute(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("removeHostRoutes")) { + removeHostRoutes(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("addSecondaryRoute")) { + addSecondaryRoute(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("removeSecondaryRoute")) { + removeSecondaryRoute(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("getNetworkInterfaceStats")) { + getNetworkInterfaceStats(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setNetworkInterfaceAlarm")) { + setNetworkInterfaceAlarm(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("enableNetworkInterfaceAlarm")) { + enableNetworkInterfaceAlarm(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("disableNetworkInterfaceAlarm")) { + disableNetworkInterfaceAlarm(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setWifiOperationMode")) { + setWifiOperationMode(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setDhcpServer")) { + setDhcpServer(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setWifiTethering")) { + setWifiTethering(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("setUSBTethering")) { + setUSBTethering(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("enableUsbRndis")) { + enableUsbRndis(aOptions); + } else if (aOptions.mCmd.EqualsLiteral("updateUpStream")) { + updateUpStream(aOptions); + } else { + WARN("unknon message"); + return; + } + + if (!aOptions.mIsAsync) { + NetworkResultOptions result; + result.mRet = ret; + postMessage(aOptions, result); + } +} + +/** + * 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)) { + DEBUG("Receiving broadcast message from netd."); + DEBUG(" ==> Code: %d Reason: %s", code, reason); + sendBroadcastMessage(code, reason); + nextNetdCommand(); + return; + } + + // Set pending to false before we handle next command. + DEBUG("Receiving \"%s\" command response from netd.", gCurrentCommand.command); + DEBUG(" ==> 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; + } + + if (gCurrentCommand.callback) { + 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. + */ +bool 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"); + + RUN_CHAIN(aOptions, sStartDhcpServerChain, setDhcpServerFail) + } else { + RUN_CHAIN(aOptions, sStopDhcpServerChain, setDhcpServerFail) + } + return true; +} + +/** + * Set DNS servers for given network interface. + */ +bool 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]; + snprintf(dns_prop_key, sizeof 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]; + property_get("net.dnschange", dnschange, "0"); + + char num[PROPERTY_VALUE_MAX]; + snprintf(num, PROPERTY_VALUE_MAX - 1, "%d", atoi(dnschange) + 1); + property_set("net.dnschange", num); + + // DNS needs to be set through netd since JellyBean (4.3). + if (SDK_VERSION >= 18) { + RUN_CHAIN(aOptions, sSetDnsChain, setDnsFail) + } + + return true; +} + +/** + * Set default route and DNS servers for given network interface. + */ +bool NetworkUtils::setDefaultRouteAndDNS(NetworkParams& aOptions) +{ + NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); + + if (!aOptions.mOldIfname.IsEmpty()) { + // Remove IPv4's default route. + mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname)); + // Remove IPv6's default route. + mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL); + } + + 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) { + mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, autoGateway.get()); + } else { /* type == AF_INET */ + mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(autoGateway.get())); + } + } + } else { + // Set default froute from system properties. + char key[PROPERTY_KEY_MAX]; + char gateway[PROPERTY_KEY_MAX]; + + 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 false; + } + + if (type == AF_INET6) { + mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, gateway); + } else { /* type == AF_INET */ + 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) +{ + 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 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); + int type, prefix; + + uint32_t length = aOptions.mHostnames.Length(); + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoHostname(aOptions.mHostnames[i]); + + type = getIpType(autoHostname.get()); + if (type != AF_INET && type != AF_INET6) { + continue; + } + + uint32_t index = selectGateway(aOptions.mGateways, type); + if (index >= aOptions.mGateways.Length()) { + continue; + } + + NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[index]); + 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); + int type, prefix; + + uint32_t length = aOptions.mHostnames.Length(); + for (uint32_t i = 0; i < length; i++) { + NS_ConvertUTF16toUTF8 autoHostname(aOptions.mHostnames[i]); + + type = getIpType(autoHostname.get()); + if (type != AF_INET && type != AF_INET6) { + continue; + } + + uint32_t index = selectGateway(aOptions.mGateways, type); + if (index >= aOptions.mGateways.Length()) { + continue; + } + + NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[index]); + 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) +{ + 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(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", + GET_CHAR(mIfname), + GET_CHAR(mIp), + GET_CHAR(mPrefix), + GET_CHAR(mGateway)); + + doCommand(command, nullptr, nullptr); + return true; +} + +bool NetworkUtils::removeSecondaryRoute(NetworkParams& aOptions) +{ + char command[MAX_COMMAND_SIZE]; + 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, nullptr, nullptr); + return true; +} + +bool NetworkUtils::getNetworkInterfaceStats(NetworkParams& aOptions) +{ + DEBUG("getNetworkInterfaceStats: %s", GET_CHAR(mIfname)); + aOptions.mRxBytes = -1; + aOptions.mTxBytes = -1; + + RUN_CHAIN(aOptions, sNetworkInterfaceStatsChain, networkInterfaceStatsFail); + return true; +} + +bool NetworkUtils::setNetworkInterfaceAlarm(NetworkParams& aOptions) +{ + DEBUG("setNetworkInterfaceAlarms: %s", GET_CHAR(mIfname)); + RUN_CHAIN(aOptions, sNetworkInterfaceSetAlarmChain, networkInterfaceAlarmFail); + return true; +} + +bool NetworkUtils::enableNetworkInterfaceAlarm(NetworkParams& aOptions) +{ + DEBUG("enableNetworkInterfaceAlarm: %s", GET_CHAR(mIfname)); + RUN_CHAIN(aOptions, sNetworkInterfaceEnableAlarmChain, networkInterfaceAlarmFail); + return true; +} + +bool NetworkUtils::disableNetworkInterfaceAlarm(NetworkParams& aOptions) +{ + DEBUG("disableNetworkInterfaceAlarms: %s", GET_CHAR(mIfname)); + RUN_CHAIN(aOptions, sNetworkInterfaceDisableAlarmChain, networkInterfaceAlarmFail); + return true; +} + +/** + * handling main thread's reload Wifi firmware request + */ +bool NetworkUtils::setWifiOperationMode(NetworkParams& aOptions) +{ + DEBUG("setWifiOperationMode: %s %s", GET_CHAR(mIfname), GET_CHAR(mMode)); + RUN_CHAIN(aOptions, sWifiOperationModeChain, wifiOperationModeFail); + return true; +} + +/** + * handling main thread's enable/disable WiFi Tethering request + */ +bool NetworkUtils::setWifiTethering(NetworkParams& aOptions) +{ + bool enable = aOptions.mEnable; + IFProperties interfaceProperties; + getIFProperties(GET_CHAR(mExternalIfname), interfaceProperties); + + if (strcmp(interfaceProperties.dns1, "")) { + aOptions.mDns1 = NS_ConvertUTF8toUTF16(interfaceProperties.dns1); + } + if (strcmp(interfaceProperties.dns2, "")) { + aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2); + } + dumpParams(aOptions, "WIFI"); + + if (enable) { + DEBUG("Starting Wifi Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + RUN_CHAIN(aOptions, sWifiEnableChain, wifiTetheringFail) + } else { + DEBUG("Stopping Wifi Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + RUN_CHAIN(aOptions, sWifiDisableChain, wifiTetheringFail) + } + return true; +} + +bool NetworkUtils::setUSBTethering(NetworkParams& aOptions) +{ + bool enable = aOptions.mEnable; + IFProperties interfaceProperties; + getIFProperties(GET_CHAR(mExternalIfname), interfaceProperties); + + if (strcmp(interfaceProperties.dns1, "")) { + aOptions.mDns1 = NS_ConvertUTF8toUTF16(interfaceProperties.dns1); + } + if (strcmp(interfaceProperties.dns2, "")) { + aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2); + } + dumpParams(aOptions, "USB"); + + if (enable) { + DEBUG("Starting USB Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + RUN_CHAIN(aOptions, sUSBEnableChain, usbTetheringFail) + } else { + DEBUG("Stopping USB Tethering on %s <-> %s", + GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname)); + RUN_CHAIN(aOptions, sUSBDisableChain, usbTetheringFail) + } + return true; +} + +void NetworkUtils::escapeQuote(nsCString& aString) +{ + aString.ReplaceSubstring("\\", "\\\\"); + aString.ReplaceSubstring("\"", "\\\""); +} + +void NetworkUtils::checkUsbRndisState(NetworkParams& aOptions) +{ + static uint32_t retry = 0; + + char currentState[PROPERTY_VALUE_MAX]; + property_get(SYS_USB_STATE_PROPERTY, currentState, nullptr); + + nsTArray 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; + postMessage(aOptions, result); + retry = 0; + return; + } + if (retry < USB_FUNCTION_RETRY_TIMES) { + retry++; + usleep(USB_FUNCTION_RETRY_INTERVAL * 1000); + checkUsbRndisState(aOptions); + return; + } + + NetworkResultOptions result; + result.mResult = false; + postMessage(aOptions, result); + retry = 0; +} + +/** + * Modify usb function's property to turn on USB RNDIS function + */ +bool 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]; + property_get(SYS_USB_CONFIG_PROPERTY, currentConfig, nullptr); + + nsTArray configFuncs; + split(currentConfig, USB_CONFIG_DELIMIT, configFuncs); + + char persistConfig[PROPERTY_VALUE_MAX]; + property_get(PERSIST_SYS_USB_CONFIG_PROPERTY, persistConfig, nullptr); + + nsTArray 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] = ""; + property_get(SYS_USB_CONFIG_PROPERTY, currentConfig, nullptr); + join(configFuncs, USB_CONFIG_DELIMIT, PROPERTY_VALUE_MAX, 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); + checkUsbRndisState(aOptions); + } + return true; +} + +/** + * handling upstream interface change event. + */ +bool NetworkUtils::updateUpStream(NetworkParams& aOptions) +{ + RUN_CHAIN(aOptions, sUpdateUpStreamChain, updateUpStreamFail) + return true; +} + +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 + DEBUG("Dump params:"); + DEBUG(" ifname: %s", GET_CHAR(mIfname)); + DEBUG(" ip: %s", GET_CHAR(mIp)); + DEBUG(" link: %s", GET_CHAR(mLink)); + DEBUG(" prefix: %s", GET_CHAR(mPrefix)); + DEBUG(" wifiStartIp: %s", GET_CHAR(mWifiStartIp)); + DEBUG(" wifiEndIp: %s", GET_CHAR(mWifiEndIp)); + DEBUG(" usbStartIp: %s", GET_CHAR(mUsbStartIp)); + DEBUG(" usbEndIp: %s", GET_CHAR(mUsbEndIp)); + DEBUG(" dnsserver1: %s", GET_CHAR(mDns1)); + DEBUG(" dnsserver2: %s", GET_CHAR(mDns2)); + DEBUG(" internalIfname: %s", GET_CHAR(mInternalIfname)); + DEBUG(" externalIfname: %s", GET_CHAR(mExternalIfname)); + if (!strcmp(aType, "WIFI")) { + DEBUG(" wifictrlinterfacename: %s", GET_CHAR(mWifictrlinterfacename)); + DEBUG(" ssid: %s", GET_CHAR(mSsid)); + DEBUG(" security: %s", GET_CHAR(mSecurity)); + DEBUG(" key: %s", GET_CHAR(mKey)); + } +#endif +} + +#undef GET_CHAR +#undef GET_FIELD