/* * Copyright (C) 2008 The Android Open Source Project * * 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 <stdlib.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> #include <string.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #define LOG_TAG "TetherController" #include <cutils/log.h> #include <cutils/properties.h> #include "Fwmark.h" #include "NetdConstants.h" #include "Permission.h" #include "InterfaceController.h" #include "NetworkController.h" #include "TetherController.h" namespace { const char BP_TOOLS_MODE[] = "bp-tools"; const char IPV4_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv4/ip_forward"; const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding"; const char SEPARATOR[] = "|"; bool writeToFile(const char* filename, const char* value) { int fd = open(filename, O_WRONLY | O_CLOEXEC); if (fd < 0) { ALOGE("Failed to open %s: %s", filename, strerror(errno)); return false; } const ssize_t len = strlen(value); if (write(fd, value, len) != len) { ALOGE("Failed to write %s to %s: %s", value, filename, strerror(errno)); close(fd); return false; } close(fd); return true; } bool configureForIPv6Router(const char *interface) { return (InterfaceController::setEnableIPv6(interface, 0) == 0) && (InterfaceController::setAcceptIPv6Ra(interface, 0) == 0) && (InterfaceController::setAcceptIPv6Dad(interface, 0) == 0) && (InterfaceController::setIPv6DadTransmits(interface, "0") == 0) && (InterfaceController::setEnableIPv6(interface, 1) == 0); } void configureForIPv6Client(const char *interface) { InterfaceController::setAcceptIPv6Ra(interface, 1); InterfaceController::setAcceptIPv6Dad(interface, 1); InterfaceController::setIPv6DadTransmits(interface, "1"); InterfaceController::setEnableIPv6(interface, 0); } bool inBpToolsMode() { // In BP tools mode, do not disable IP forwarding char bootmode[PROPERTY_VALUE_MAX] = {0}; property_get("ro.bootmode", bootmode, "unknown"); return !strcmp(BP_TOOLS_MODE, bootmode); } } // namespace namespace android { namespace net { TetherController::TetherController() { mDnsNetId = 0; mDaemonFd = -1; mDaemonPid = 0; if (inBpToolsMode()) { enableForwarding(BP_TOOLS_MODE); } else { setIpFwdEnabled(); } } TetherController::~TetherController() { mInterfaces.clear(); mDnsForwarders.clear(); mForwardingRequests.clear(); } bool TetherController::setIpFwdEnabled() { bool success = true; const char* value = mForwardingRequests.empty() ? "0" : "1"; ALOGD("Setting IP forward enable = %s", value); success &= writeToFile(IPV4_FORWARDING_PROC_FILE, value); success &= writeToFile(IPV6_FORWARDING_PROC_FILE, value); return success; } bool TetherController::enableForwarding(const char* requester) { // Don't return an error if this requester already requested forwarding. Only return errors for // things that the caller caller needs to care about, such as "couldn't write to the file to // enable forwarding". mForwardingRequests.insert(requester); return setIpFwdEnabled(); } bool TetherController::disableForwarding(const char* requester) { mForwardingRequests.erase(requester); return setIpFwdEnabled(); } size_t TetherController::forwardingRequestCount() { return mForwardingRequests.size(); } #define TETHER_START_CONST_ARG 10 int TetherController::startTethering(int num_addrs, char **dhcp_ranges) { if (mDaemonPid != 0) { ALOGE("Tethering already started"); errno = EBUSY; return -1; } ALOGD("Starting tethering services"); pid_t pid; int pipefd[2]; if (pipe(pipefd) < 0) { ALOGE("pipe failed (%s)", strerror(errno)); return -1; } /* * TODO: Create a monitoring thread to handle and restart * the daemon if it exits prematurely */ if ((pid = fork()) < 0) { ALOGE("fork failed (%s)", strerror(errno)); close(pipefd[0]); close(pipefd[1]); return -1; } if (!pid) { close(pipefd[1]); if (pipefd[0] != STDIN_FILENO) { if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { ALOGE("dup2 failed (%s)", strerror(errno)); return -1; } close(pipefd[0]); } Fwmark fwmark; fwmark.netId = NetworkController::LOCAL_NET_ID; fwmark.explicitlySelected = true; fwmark.protectedFromVpn = true; fwmark.permission = PERMISSION_SYSTEM; char markStr[UINT32_HEX_STRLEN]; snprintf(markStr, sizeof(markStr), "0x%x", fwmark.intValue); int num_processed_args = TETHER_START_CONST_ARG + (num_addrs/2) + 1; char **args = (char **)malloc(sizeof(char *) * num_processed_args); args[num_processed_args - 1] = NULL; args[0] = (char *)"/system/bin/dnsmasq"; args[1] = (char *)"--keep-in-foreground"; args[2] = (char *)"--no-resolv"; args[3] = (char *)"--no-poll"; args[4] = (char *)"--dhcp-authoritative"; // TODO: pipe through metered status from ConnService args[5] = (char *)"--dhcp-option-force=43,ANDROID_METERED"; args[6] = (char *)"--pid-file"; args[7] = (char *)"--listen-mark"; args[8] = (char *)markStr; args[9] = (char *)""; int nextArg = TETHER_START_CONST_ARG; for (int addrIndex = 0; addrIndex < num_addrs; addrIndex += 2) { asprintf(&(args[nextArg++]),"--dhcp-range=%s,%s,1h", dhcp_ranges[addrIndex], dhcp_ranges[addrIndex+1]); } if (execv(args[0], args)) { ALOGE("execl failed (%s)", strerror(errno)); } ALOGE("Should never get here!"); _exit(-1); } else { close(pipefd[0]); mDaemonPid = pid; mDaemonFd = pipefd[1]; applyDnsInterfaces(); ALOGD("Tethering services running"); } return 0; } int TetherController::stopTethering() { if (mDaemonPid == 0) { ALOGE("Tethering already stopped"); return 0; } ALOGD("Stopping tethering services"); kill(mDaemonPid, SIGTERM); waitpid(mDaemonPid, NULL, 0); mDaemonPid = 0; close(mDaemonFd); mDaemonFd = -1; ALOGD("Tethering services stopped"); return 0; } bool TetherController::isTetheringStarted() { return (mDaemonPid == 0 ? false : true); } #define MAX_CMD_SIZE 1024 int TetherController::setDnsForwarders(unsigned netId, char **servers, int numServers) { int i; char daemonCmd[MAX_CMD_SIZE]; Fwmark fwmark; fwmark.netId = netId; fwmark.explicitlySelected = true; fwmark.protectedFromVpn = true; fwmark.permission = PERMISSION_SYSTEM; snprintf(daemonCmd, sizeof(daemonCmd), "update_dns%s0x%x", SEPARATOR, fwmark.intValue); int cmdLen = strlen(daemonCmd); mDnsForwarders.clear(); for (i = 0; i < numServers; i++) { ALOGD("setDnsForwarders(0x%x %d = '%s')", fwmark.intValue, i, servers[i]); addrinfo *res, hints = { .ai_flags = AI_NUMERICHOST }; int ret = getaddrinfo(servers[i], NULL, &hints, &res); freeaddrinfo(res); if (ret) { ALOGE("Failed to parse DNS server '%s'", servers[i]); mDnsForwarders.clear(); errno = EINVAL; return -1; } cmdLen += (strlen(servers[i]) + 1); if (cmdLen + 1 >= MAX_CMD_SIZE) { ALOGD("Too many DNS servers listed"); break; } strcat(daemonCmd, SEPARATOR); strcat(daemonCmd, servers[i]); mDnsForwarders.push_back(servers[i]); } mDnsNetId = netId; if (mDaemonFd != -1) { ALOGD("Sending update msg to dnsmasq [%s]", daemonCmd); if (write(mDaemonFd, daemonCmd, strlen(daemonCmd) +1) < 0) { ALOGE("Failed to send update command to dnsmasq (%s)", strerror(errno)); mDnsForwarders.clear(); errno = EREMOTEIO; return -1; } } return 0; } unsigned TetherController::getDnsNetId() { return mDnsNetId; } const std::list<std::string> &TetherController::getDnsForwarders() const { return mDnsForwarders; } bool TetherController::applyDnsInterfaces() { char daemonCmd[MAX_CMD_SIZE]; strcpy(daemonCmd, "update_ifaces"); int cmdLen = strlen(daemonCmd); bool haveInterfaces = false; for (const auto &ifname : mInterfaces) { cmdLen += (ifname.size() + 1); if (cmdLen + 1 >= MAX_CMD_SIZE) { ALOGD("Too many DNS ifaces listed"); break; } strcat(daemonCmd, SEPARATOR); strcat(daemonCmd, ifname.c_str()); haveInterfaces = true; } if ((mDaemonFd != -1) && haveInterfaces) { ALOGD("Sending update msg to dnsmasq [%s]", daemonCmd); if (write(mDaemonFd, daemonCmd, strlen(daemonCmd) +1) < 0) { ALOGE("Failed to send update command to dnsmasq (%s)", strerror(errno)); return false; } } return true; } int TetherController::tetherInterface(const char *interface) { ALOGD("tetherInterface(%s)", interface); if (!isIfaceName(interface)) { errno = ENOENT; return -1; } if (!configureForIPv6Router(interface)) { configureForIPv6Client(interface); return -1; } mInterfaces.push_back(interface); if (!applyDnsInterfaces()) { mInterfaces.pop_back(); configureForIPv6Client(interface); return -1; } else { return 0; } } int TetherController::untetherInterface(const char *interface) { ALOGD("untetherInterface(%s)", interface); for (auto it = mInterfaces.cbegin(); it != mInterfaces.cend(); ++it) { if (!strcmp(interface, it->c_str())) { mInterfaces.erase(it); configureForIPv6Client(interface); return applyDnsInterfaces() ? 0 : -1; } } errno = ENOENT; return -1; } const std::list<std::string> &TetherController::getTetheredInterfaceList() const { return mInterfaces; } } // namespace net } // namespace android