diff options
Diffstat (limited to 'server/TetherController.cpp')
| -rw-r--r-- | server/TetherController.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/server/TetherController.cpp b/server/TetherController.cpp new file mode 100644 index 00000000..fb51c06c --- /dev/null +++ b/server/TetherController.cpp @@ -0,0 +1,331 @@ +/* + * 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" + +TetherController::TetherController() { + mInterfaces = new InterfaceCollection(); + mDnsNetId = 0; + mDnsForwarders = new NetAddressCollection(); + mDaemonFd = -1; + mDaemonPid = 0; +} + +TetherController::~TetherController() { + InterfaceCollection::iterator it; + + for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) { + free(*it); + } + mInterfaces->clear(); + + mDnsForwarders->clear(); +} + +int TetherController::setIpFwdEnabled(bool enable) { + + ALOGD("Setting IP forward enable = %d", enable); + + // In BP tools mode, do not disable IP forwarding + char bootmode[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.bootmode", bootmode, "unknown"); + if ((enable == false) && (0 == strcmp("bp-tools", bootmode))) { + return 0; + } + + int fd = open("/proc/sys/net/ipv4/ip_forward", O_WRONLY); + if (fd < 0) { + ALOGE("Failed to open ip_forward (%s)", strerror(errno)); + return -1; + } + + if (write(fd, (enable ? "1" : "0"), 1) != 1) { + ALOGE("Failed to write ip_forward (%s)", strerror(errno)); + close(fd); + return -1; + } + close(fd); + return 0; +} + +bool TetherController::getIpFwdEnabled() { + int fd = open("/proc/sys/net/ipv4/ip_forward", O_RDONLY); + + if (fd < 0) { + ALOGE("Failed to open ip_forward (%s)", strerror(errno)); + return false; + } + + char enabled; + if (read(fd, &enabled, 1) != 1) { + ALOGE("Failed to read ip_forward (%s)", strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return (enabled == '1' ? true : false); +} + +#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); + } + + 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; +} |
