netd: IPV6 tethering support
- Start NDP proxy daemon when tethering is enabled
- Add new interface commands "add_upstream" and "remove_upstream".
Used to provide the complete set of interfaces to be proxied to
the NDP proxy daemon
- Enable ipv6 forwarding
- After repeatedly toggling tethering/Wifi hotspot on and off, hotspot
may not turn on anymore. It appears untethering is unsuccessful and
this eventually leads to a timeout and a framework reboot. The NDP
proxy daemon appears to be started multiple times even though there
is no ipv6 upstream interface present. There appears to be a signifcant
number of calls to fork and exec this daemon even though this
functionality is not needed in this case. Fix this overhead by invoking
the daemon only if there are atleast 2 interfaces to establish a bridge
and proxy the NDP messages.
Conflicts:
server/TetherController.cpp
CRs-fixed: 665103
Change-Id: I89170f8acd60b949ede749f353acd27363ff5798
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index b57ec75..ededddb 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -587,6 +587,7 @@
int argc, char **argv) {
int rc = 0;
+ ALOGD("TetherCmd::runCommand. argc: %d. argv[0]: %s", argc, argv[0]);
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
return 0;
@@ -655,6 +656,11 @@
} else if (!strcmp(argv[2], "remove")) {
rc = sTetherCtrl->untetherInterface(argv[3]);
/* else if (!strcmp(argv[2], "list")) handled above */
+ } else if (!strcmp(argv[2], "add_upstream")) {
+ ALOGD("command %s %s %s %s", argv[0], argv[1], argv[2], argv[3]);
+ rc = sTetherCtrl->addUpstreamInterface(argv[3]);
+ } else if (!strcmp(argv[2], "remove_upstream")) {
+ rc = sTetherCtrl->removeUpstreamInterface(argv[3]);
} else {
cli->sendMsg(ResponseCode::CommandParameterError,
"Unknown tether interface operation", false);
@@ -689,6 +695,74 @@
return 0;
}
+CommandListener::V6RtrAdvCmd::V6RtrAdvCmd() :
+ NetdCommand("v6rtradv") {
+}
+
+int CommandListener::V6RtrAdvCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ int rc = 0;
+
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "stop")) {
+ rc = sTetherCtrl->stopV6RtrAdv();
+ } else if (!strcmp(argv[1], "status")) {
+ char *tmp = NULL;
+
+ asprintf(&tmp, "IPv6 Router Advertisement service %s",
+ (sTetherCtrl->isV6RtrAdvStarted() ? "started" : "stopped"));
+ cli->sendMsg(ResponseCode::V6RtrAdvResult, tmp, false);
+ free(tmp);
+ return 0;
+ } else {
+ /*
+ * These commands take a minimum of 4 arguments
+ */
+ if (argc < 4) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "start")) {
+ int num_ifaces = argc - 2;
+ int arg_index = 2;
+ rc = sTetherCtrl->startV6RtrAdv(num_ifaces, &argv[arg_index]);
+ } else if (!strcmp(argv[1], "interface")) {
+ if (!strcmp(argv[2], "add")) {
+ rc = sTetherCtrl->tetherInterface(argv[3]);
+ } else if (!strcmp(argv[2], "remove")) {
+ rc = sTetherCtrl->untetherInterface(argv[3]);
+ } else if (!strcmp(argv[2], "list")) {
+ InterfaceCollection *ilist = sTetherCtrl->getTetheredInterfaceList();
+ InterfaceCollection::iterator it;
+
+ for (it = ilist->begin(); it != ilist->end(); ++it) {
+ cli->sendMsg(ResponseCode::TetherInterfaceListResult, *it, false);
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "Unknown tether interface operation", false);
+ return 0;
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown v6rtradv cmd", false);
+ return 0;
+ }
+ }
+
+ if (!rc) {
+ cli->sendMsg(ResponseCode::CommandOkay, "V6RtrAdv operation succeeded", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed, "V6RtrAdv operation failed", true);
+ }
+
+ return 0;
+}
+
CommandListener::NatCmd::NatCmd() :
NetdCommand("nat") {
}
diff --git a/server/CommandListener.h b/server/CommandListener.h
index 7819db6..c1f7867 100644
--- a/server/CommandListener.h
+++ b/server/CommandListener.h
@@ -94,6 +94,13 @@
int runCommand(SocketClient *c, int argc, char ** argv);
};
+ class V6RtrAdvCmd: public NetdCommand {
+ public:
+ V6RtrAdvCmd();
+ virtual ~V6RtrAdvCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
class NatCmd : public NetdCommand {
public:
NatCmd();
diff --git a/server/ResponseCode.h b/server/ResponseCode.h
index 6a0c22c..594d997 100644
--- a/server/ResponseCode.h
+++ b/server/ResponseCode.h
@@ -46,6 +46,7 @@
static const int TetheringStatsResult = 221;
static const int DnsProxyQueryResult = 222;
static const int ClatdStatusResult = 223;
+ static const int V6RtrAdvResult = 227;
// 400 series - The command was accepted but the requested action
// did not take place.
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index fb51c06..f710876 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -23,11 +23,15 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <linux/capability.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LOG_TAG "TetherController"
+#define LOG_NDEBUG 0
+#define LOG_NDDEBUG 0
+#define LOG_NIDEBUG 0
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -36,9 +40,20 @@
#include "Permission.h"
#include "TetherController.h"
+#include <private/android_filesystem_config.h>
+#include <unistd.h>
+
+#define RTRADVDAEMON "/system/bin/radish"
+#define IP4_CFG_IP_FORWARD "/proc/sys/net/ipv4/ip_forward"
+#define IP6_CFG_ALL_PROXY_NDP "/proc/sys/net/ipv6/conf/all/proxy_ndp"
+#define IP6_CFG_ALL_FORWARDING "/proc/sys/net/ipv6/conf/all/forwarding"
+#define IP6_IFACE_CFG_ACCEPT_RA "/proc/sys/net/ipv6/conf/%s/accept_ra"
+#define PROC_PATH_SIZE 255
+
+
TetherController::TetherController() {
mInterfaces = new InterfaceCollection();
- mDnsNetId = 0;
+ mUpstreamInterfaces = new InterfaceCollection();
mDnsForwarders = new NetAddressCollection();
mDaemonFd = -1;
mDaemonPid = 0;
@@ -52,9 +67,32 @@
}
mInterfaces->clear();
+ for (it = mUpstreamInterfaces->begin(); it != mUpstreamInterfaces->end(); ++it) {
+ free(*it);
+ }
+ mUpstreamInterfaces->clear();
+
mDnsForwarders->clear();
}
+static int config_write_setting(const char *path, const char *value)
+{
+ int fd = open(path, O_WRONLY);
+
+ ALOGD("config_write_setting(%s, %s)", path, value);
+ if (fd < 0) {
+ ALOGE("Failed to open %s (%s)", path, strerror(errno));
+ return -1;
+ }
+ if (write(fd, value, strlen(value)) != (int)strlen(value)) {
+ ALOGE("Failed to write to %s (%s)", path, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
int TetherController::setIpFwdEnabled(bool enable) {
ALOGD("Setting IP forward enable = %d", enable);
@@ -78,6 +116,17 @@
return -1;
}
close(fd);
+ if (config_write_setting(
+ IP6_CFG_ALL_PROXY_NDP, enable ? "2" : "0")) {
+ ALOGE("Failed to write proxy_ndp (%s)", strerror(errno));
+ return -1;
+ }
+ if (config_write_setting(
+ IP6_CFG_ALL_FORWARDING, enable ? "2" : "0")) {
+ ALOGE("Failed to write ip6 forwarding (%s)", strerror(errno));
+ return -1;
+ }
+
return 0;
}
@@ -198,6 +247,107 @@
return (mDaemonPid == 0 ? false : true);
}
+int TetherController::startV6RtrAdv(int num_ifaces, char **ifaces) {
+ int pid;
+ int num_processed_args = 1;
+ gid_t groups [] = { AID_NET_ADMIN, AID_NET_RAW, AID_INET };
+
+ if (num_ifaces < 2) {
+ ALOGD("Need atleast two interfaces to start Router advertisement daemon");
+ return 0;
+ }
+
+ if ((pid = fork()) < 0) {
+ ALOGE("%s: fork failed (%s)", __func__, strerror(errno));
+ return -1;
+ }
+ if (!pid) {
+ char **args;
+ const char *cmd = RTRADVDAEMON;
+
+ args = (char **)calloc(num_ifaces * 3 + 2, sizeof(char *));
+
+ args[0] = strdup(RTRADVDAEMON);
+ for (int i=0; i < num_ifaces; i++) {
+ int aidx = 3 * i + num_processed_args;
+ args[aidx] = (char *)"-i";
+ args[aidx + 1] = ifaces[i];
+ args[aidx + 2] = (char *)"-x";
+ }
+
+
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+ setresgid(AID_RADIO, AID_RADIO, AID_RADIO);
+ setresuid(AID_RADIO, AID_RADIO, AID_RADIO);
+
+ if (execv(cmd, args)) {
+ ALOGE("Unable to exec %s: (%s)" , cmd, strerror(errno));
+ }
+ free(args[0]);
+ free(args);
+ exit(0);
+ } else {
+ mRtrAdvPid = pid;
+ ALOGD("Router advertisement daemon running");
+ }
+ return 0;
+}
+
+int TetherController::stopV6RtrAdv() {
+ if (!mRtrAdvPid) {
+ ALOGD("Router advertisement daemon already stopped");
+ return 0;
+ }
+
+ kill(mRtrAdvPid, SIGTERM);
+ waitpid(mRtrAdvPid, NULL, 0);
+ mRtrAdvPid = 0;
+ ALOGD("Router advertisement daemon stopped");
+ return 0;
+}
+
+int TetherController::addV6RtrAdvIface(const char *iface) {
+ char **args;
+ int i;
+ int len;
+ InterfaceCollection::iterator it;
+ /* For now, just stop and start the daemon with the new interface list */
+
+ len = mInterfaces->size() + mUpstreamInterfaces->size();
+ ALOGD("addV6RtrAdvIface: len = %d. Iface: %s\n", len, iface);
+ args = (char **)calloc(len, sizeof(char *));
+
+ if (!args) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ for (i = 0, it = mInterfaces->begin(); it != mInterfaces->end(); it++, i++) {
+ args[i] = *it;
+ }
+
+ for (it = mUpstreamInterfaces->begin(); i < len && it != mUpstreamInterfaces->end(); it++, i++) {
+ args[i] = *it;
+ }
+
+ stopV6RtrAdv();
+ startV6RtrAdv(i, args);
+
+ free(args);
+
+ return 0;
+}
+
+int TetherController::removeV6RtrAdvIface(const char *iface) {
+ /* For now, just call addV6RtrAdvIface, since that will stop and
+ * start the daemon with the updated interfaces
+ */
+ return addV6RtrAdvIface(iface);
+}
+bool TetherController::isV6RtrAdvStarted() {
+ return (mRtrAdvPid == 0 ? false : true);
+}
+
#define MAX_CMD_SIZE 1024
int TetherController::setDnsForwarders(unsigned netId, char **servers, int numServers) {
@@ -252,6 +402,48 @@
return mDnsNetId;
}
+int TetherController::addUpstreamInterface(char *iface)
+{
+ InterfaceCollection::iterator it;
+
+ ALOGD("addUpstreamInterface(%s)\n", iface);
+
+ if (!iface) {
+ ALOGE("addUpstreamInterface: received null interface");
+ return 0;
+ }
+ for (it = mUpstreamInterfaces->begin(); it != mUpstreamInterfaces->end(); ++it) {
+ ALOGD(".");
+ if (*it && !strcmp(iface, *it)) {
+ ALOGD("addUpstreamInterface: interface %s already present", iface);
+ return 0;
+ }
+ }
+ mUpstreamInterfaces->push_back(strdup(iface));
+
+ return addV6RtrAdvIface(iface);
+}
+
+int TetherController::removeUpstreamInterface(char *iface)
+{
+ InterfaceCollection::iterator it;
+
+ if (!iface) {
+ ALOGE("removeUpstreamInterface: Null interface name received");
+ return 0;
+ }
+ for (it = mUpstreamInterfaces->begin(); it != mUpstreamInterfaces->end(); ++it) {
+ if (*it && !strcmp(iface, *it)) {
+ free(*it);
+ mUpstreamInterfaces->erase(it);
+ return removeV6RtrAdvIface(iface);
+ }
+ }
+
+ ALOGW("Couldn't find interface %s to remove", iface);
+ return 0;
+}
+
NetAddressCollection *TetherController::getDnsForwarders() {
return mDnsForwarders;
}
@@ -294,6 +486,8 @@
}
mInterfaces->push_back(strdup(interface));
+ addV6RtrAdvIface(interface);
+
if (applyDnsInterfaces()) {
InterfaceCollection::iterator it;
for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) {
@@ -318,7 +512,7 @@
if (!strcmp(interface, *it)) {
free(*it);
mInterfaces->erase(it);
-
+ removeV6RtrAdvIface(NULL);
return applyDnsInterfaces();
}
}
diff --git a/server/TetherController.h b/server/TetherController.h
index 1c32627..64c3e94 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -32,6 +32,8 @@
NetAddressCollection *mDnsForwarders;
pid_t mDaemonPid;
int mDaemonFd;
+ pid_t mRtrAdvPid; // IPv6 support
+ InterfaceCollection *mUpstreamInterfaces;
public:
TetherController();
@@ -52,6 +54,13 @@
int tetherInterface(const char *interface);
int untetherInterface(const char *interface);
InterfaceCollection *getTetheredInterfaceList();
+ int startV6RtrAdv(int num_ifaces, char **ifaces);
+ int stopV6RtrAdv();
+ bool isV6RtrAdvStarted();
+ int addV6RtrAdvIface(const char *iface);
+ int removeV6RtrAdvIface(const char *iface);
+ int addUpstreamInterface(char *iface);
+ int removeUpstreamInterface(char *iface);
private:
int applyDnsInterfaces();