/* * 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 <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 "TetherController.h" namespace { static const char BP_TOOLS_MODE[] = "bp-tools"; static const char IPV4_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv4/ip_forward"; static const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding"; bool writeToFile(const char* filename, const char* value) { int fd = open(filename, O_WRONLY); 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 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 TetherController::TetherController() { mInterfaces = new InterfaceCollection(); mDnsNetId = 0; mDnsForwarders = new NetAddressCollection(); mDaemonFd = -1; mDaemonPid = 0; if (inBpToolsMode()) { enableForwarding(BP_TOOLS_MODE); } else { setIpFwdEnabled(); } } TetherController::~TetherController() { InterfaceCollection::iterator it; for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) { free(*it); } 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 8 int TetherController::startTethering(int num_addrs, struct in_addr* addrs) { 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]); } 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 *)""; int nextArg = TETHER_START_CONST_ARG; for (int addrIndex=0; addrIndex < num_addrs;) { char *start = strdup(inet_ntoa(addrs[addrIndex++])); char *end = strdup(inet_ntoa(addrs[addrIndex++])); asprintf(&(args[nextArg++]),"--dhcp-range=%s,%s,1h", start, end); free(start); free(end); } 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:0x%x", 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]); struct in_addr a; if (!inet_aton(servers[i], &a)) { ALOGE("Failed to parse DNS server '%s'", servers[i]); mDnsForwarders->clear(); return -1; } cmdLen += (strlen(servers[i]) + 1); if (cmdLen + 1 >= MAX_CMD_SIZE) { ALOGD("Too many DNS servers listed"); break; } strcat(daemonCmd, ":"); strcat(daemonCmd, servers[i]); mDnsForwarders->push_back(a); } 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(); return -1; } } return 0; } unsigned TetherController::getDnsNetId() { return mDnsNetId; } NetAddressCollection *TetherController::getDnsForwarders() { return mDnsForwarders; } int TetherController::applyDnsInterfaces() { char daemonCmd[MAX_CMD_SIZE]; strcpy(daemonCmd, "update_ifaces"); int cmdLen = strlen(daemonCmd); InterfaceCollection::iterator it; bool haveInterfaces = false; for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) { cmdLen += (strlen(*it) + 1); if (cmdLen + 1 >= MAX_CMD_SIZE) { ALOGD("Too many DNS ifaces listed"); break; } strcat(daemonCmd, ":"); strcat(daemonCmd, *it); 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 -1; } } return 0; } int TetherController::tetherInterface(const char *interface) { ALOGD("tetherInterface(%s)", interface); if (!isIfaceName(interface)) { errno = ENOENT; return -1; } mInterfaces->push_back(strdup(interface)); if (applyDnsInterfaces()) { InterfaceCollection::iterator it; for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) { if (!strcmp(interface, *it)) { free(*it); mInterfaces->erase(it); break; } } return -1; } else { return 0; } } int TetherController::untetherInterface(const char *interface) { InterfaceCollection::iterator it; ALOGD("untetherInterface(%s)", interface); for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) { if (!strcmp(interface, *it)) { free(*it); mInterfaces->erase(it); return applyDnsInterfaces(); } } errno = ENOENT; return -1; } InterfaceCollection *TetherController::getTetheredInterfaceList() { return mInterfaces; }