/* 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 #include #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 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; 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 const int32_t SUCCESS = 0; 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) // 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 gCommandQueue; static CurrentCommand gCurrentCommand; static bool gPending = false; static nsTArray gReason; static NetworkParams *gWifiTetheringParms = 0; static nsTArray gCommandChainQueue; const CommandFunc NetworkUtils::sWifiEnableChain[] = { NetworkUtils::clearWifiTetherParms, NetworkUtils::wifiFirmwareReload, NetworkUtils::startAccessPointDriver, NetworkUtils::setAccessPoint, NetworkUtils::startSoftAP, NetworkUtils::setInterfaceUp, 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::setInterfaceUp, 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::setInterfaceUp, NetworkUtils::enableNat, NetworkUtils::setIpForwardingEnabled, NetworkUtils::tetherInterface, NetworkUtils::addInterfaceToLocalNetwork, NetworkUtils::addRouteToLocalNetwork, NetworkUtils::tetheringStatus, NetworkUtils::startTethering, NetworkUtils::setDnsForwarders, NetworkUtils::usbTetheringSuccess }; const CommandFunc NetworkUtils::sUSBDisableChain[] = { NetworkUtils::untetherInterface, NetworkUtils::removeInterfaceFromLocalNetwork, NetworkUtils::preTetherInterfaceList, NetworkUtils::postTetherInterfaceList, 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::createUpStream, NetworkUtils::updateUpStreamSuccess }; const CommandFunc NetworkUtils::sStartDhcpServerChain[] = { NetworkUtils::setInterfaceUp, 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 }; /** * 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; } 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); } mResult.mRet = true; } 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 %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::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); memcpy(buf, reason.get(), reason.Length() + 1); 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 { // 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) { char command[MAX_COMMAND_SIZE]; // FIXME: (Bug 1121795) We only remove the first gateway to the default route. // For dual stack (ipv4/ipv6) device, one of the gateway would // not be added to the default route. snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s 0.0.0.0/0 %s", GET_FIELD(mNetId), GET_CHAR(mIfname), GET_CHAR(mGateways[0])); doCommand(command, aChain, aCallback); } void NetworkUtils::setInterfaceDns(CommandChain* aChain, CommandCallback aCallback, NetworkResultOptions& aResult) { char command[MAX_COMMAND_SIZE]; int written; if (SDK_VERSION >= 20) { written = snprintf(command, sizeof command, "resolver setnetdns %d %s", GET_FIELD(mNetId), GET_CHAR(mDomain)); } else { 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 (((size_t)ret + written) >= sizeof(command)) { command[written] = '\0'; break; } written += ret; } 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) { char command[MAX_COMMAND_SIZE]; // FIXME: (Bug 1121795) We only add the first gateway to the default route. // For dual stack (ipv4/ipv6) device, one of the gateway would // not be added to the default route. snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s 0.0.0.0/0 %s", GET_FIELD(mNetId), GET_CHAR(mIfname), GET_CHAR(mGateways[0])); 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)")) { NU_DBG("Ignore \"File exists\" error when adding host route."); return aOriginalCallback(aChain, false, aResult); } aOriginalCallback(aChain, aError, 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); } #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 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); } #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 // 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(removeHostRoutes), BUILD_ENTRY(addSecondaryRoute), BUILD_ENTRY(removeSecondaryRoute), BUILD_ENTRY(setNetworkInterfaceAlarm), BUILD_ENTRY(enableNetworkInterfaceAlarm), BUILD_ENTRY(disableNetworkInterfaceAlarm), 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(enableInterface), BUILD_ENTRY(disableInterface), BUILD_ENTRY(resetConnections), BUILD_ENTRY(createNetwork), BUILD_ENTRY(destroyNetwork), #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]; 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 >= 20) { // Lollipop. static CommandFunc COMMAND_CHAIN[] = { setInterfaceDns, 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[] = { setDefaultInterface, 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::dhcpRequest(NetworkParams& aOptions) { mozilla::dom::NetworkResultOptions result; NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname); char ipaddr[PROPERTY_VALUE_MAX]; char gateway[PROPERTY_VALUE_MAX]; uint32_t prefixLength; char dns1[PROPERTY_VALUE_MAX]; char dns2[PROPERTY_VALUE_MAX]; char server[PROPERTY_VALUE_MAX]; uint32_t lease; char vendorinfo[PROPERTY_VALUE_MAX]; 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.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; 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); if (!aOptions.mOldIfname.IsEmpty()) { // Remove IPv4's default route. RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname))); // Remove IPv6's default route. WARN_IF_FAILED(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) { 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]; 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 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))); } } 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; 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) { 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()); } /** * Remove the routes associated with the named interface. */ CommandResult NetworkUtils::removeHostRoutes(NetworkParams& aOptions) { if (SDK_VERSION < 20) { return removeHostRoutesLegacy(aOptions); } NU_DBG("Don't know how to remove host routes on a interface"); return SUCCESS; } /** * Remove the routes associated with the named interface. */ CommandResult NetworkUtils::removeHostRoutesLegacy(NetworkParams& aOptions) { return mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname)); } 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(); } /** * 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]; 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; 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]; 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); 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(); } 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