Merge tag android-5.1.0_r1 into AOSP_5.1_MERGE
Change-Id: I0eca19ee2680183a7f393eac4bb7e109518cfe08
diff --git a/client/Android.mk b/client/Android.mk
old mode 100644
new mode 100755
index 0c5e7ea..6b329f4
--- a/client/Android.mk
+++ b/client/Android.mk
@@ -21,5 +21,6 @@
LOCAL_CPPFLAGS := -std=c++11 -Wall -Werror
LOCAL_MODULE := libnetd_client
LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp
+LOCAL_SHARED_LIBRARIES := libdl
include $(BUILD_SHARED_LIBRARY)
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index fce362e..c5e6929 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -24,6 +24,7 @@
#include <atomic>
#include <sys/socket.h>
#include <unistd.h>
+#include<dlfcn.h>
namespace {
@@ -34,12 +35,21 @@
typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
typedef int (*SocketFunctionType)(int, int, int);
typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
+typedef void (*SetConnectFunc) (ConnectFunctionType* func);
+typedef void (*SetSocketFunc) (SocketFunctionType* func);
// These variables are only modified at startup (when libc.so is loaded) and never afterwards, so
// it's okay that they are read later at runtime without a lock.
Accept4FunctionType libcAccept4 = 0;
ConnectFunctionType libcConnect = 0;
SocketFunctionType libcSocket = 0;
+ConnectFunctionType propConnect = 0;
+SocketFunctionType propSocket = 0;
+SetConnectFunc setConnect = 0;
+SetSocketFunc setSocket = 0;
+
+
+static void *propClientHandle = 0;
int closeFdAndSetErrno(int fd, int error) {
close(fd);
@@ -78,20 +88,34 @@
return -1;
}
}
+
+ if ((propConnect) && FwmarkClient::shouldSetFwmark(addr->sa_family)) {
+ return propConnect(sockfd, addr, addrlen);
+ }
+
+ // fallback to libc
return libcConnect(sockfd, addr, addrlen);
}
int netdClientSocket(int domain, int type, int protocol) {
- int socketFd = libcSocket(domain, type, protocol);
- if (socketFd == -1) {
- return -1;
+ int socketFd = -1;
+ if (propSocket) {
+ socketFd = propSocket(domain, type, protocol);
+ } else if (libcSocket) {
+ socketFd = libcSocket(domain, type, protocol);
}
+
+ if (-1 == socketFd) {
+ return -1;
+ }
+
unsigned netId = netIdForProcess;
if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
if (int error = setNetworkForSocket(netId, socketFd)) {
return closeFdAndSetErrno(socketFd, error);
}
}
+
return socketFd;
}
@@ -146,6 +170,21 @@
libcConnect = *function;
*function = netdClientConnect;
}
+
+ if (!propClientHandle) {
+ propClientHandle = dlopen("libvendorconn.so", RTLD_LAZY);
+ }
+ if (!propClientHandle) {
+ return;
+ }
+
+ propConnect = reinterpret_cast<ConnectFunctionType>(dlsym(propClientHandle, "vendorConnect"));
+
+ setConnect = reinterpret_cast<SetConnectFunc>(dlsym(propClientHandle, "setConnectFunc"));
+ // Pass saved libc handle so it can be called from lib
+ if (setConnect && libcConnect) {
+ setConnect(&libcConnect);
+ }
}
extern "C" void netdClientInitSocket(SocketFunctionType* function) {
@@ -153,6 +192,21 @@
libcSocket = *function;
*function = netdClientSocket;
}
+
+ if (!propClientHandle) {
+ propClientHandle = dlopen("libvendorconn.so", RTLD_LAZY);
+ }
+ if (!propClientHandle) {
+ return;
+ }
+
+ propSocket = reinterpret_cast<SocketFunctionType>(dlsym(propClientHandle, "vendorSocket"));
+ setSocket = reinterpret_cast<SetSocketFunc>(dlsym(propClientHandle, "setSocketFunc"));
+ // Pass saved libc handle so it can be called from lib
+ if (setSocket && libcSocket) {
+ setSocket(&libcSocket);
+ }
+
}
extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
diff --git a/server/Android.mk b/server/Android.mk
old mode 100644
new mode 100755
index 55aa87c..cb94bc6
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -67,6 +67,15 @@
VirtualNetwork.cpp \
main.cpp \
oem_iptables_hook.cpp \
+ QcRouteController.cpp \
+
+
+ifeq ($(BOARD_HAS_QCOM_WLAN), true)
+ LOCAL_SRC_FILES += QsoftapCmd.cpp
+ LOCAL_CFLAGS += -DQSAP_WLAN
+ LOCAL_SHARED_LIBRARIES += libqsap_sdk
+ LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/sdk/softap/include
+endif
include $(BUILD_EXECUTABLE)
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index e1db680..023008e 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -53,6 +53,7 @@
/* Alphabetical */
#define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s"
+#define MAX_EXPECTED_ARGS 5
const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
const char* BandwidthController::LOCAL_INPUT = "bw_INPUT";
const char* BandwidthController::LOCAL_FORWARD = "bw_FORWARD";
@@ -135,20 +136,20 @@
};
const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
- "-N bw_happy_box",
- "-N bw_penalty_box",
- "-N bw_costly_shared",
+ "-w -N bw_happy_box",
+ "-w -N bw_penalty_box",
+ "-w -N bw_costly_shared",
};
const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
- "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
+ "-w -A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
- "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
+ "-w -A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
- "-A bw_costly_shared --jump bw_penalty_box",
+ "-w -A bw_costly_shared --jump bw_penalty_box",
- "-t raw -A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
- "-t mangle -A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
+ "-w -t raw -A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
+ "-w -t mangle -A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
};
BandwidthController::BandwidthController(void) {
@@ -1117,6 +1118,121 @@
return res;
}
+
+/* ipv6 tethering stats collection
+ * Parse the ptks and bytes out of:
+ * Chain natctrl_tether_counters (4 references)
+ * pkts bytes target prot opt in out source destination
+ * 26 2373 RETURN all wlan0 rmnet0 ::/0 ::/0 counter wlan0_rmnet0: 0 bytes
+ * 27 2002 RETURN all rmnet0 wlan0 ::/0 ::/0 counter rmnet0_wlan0: 0 bytes
+ * 1040 107471 RETURN all bt-pan rmnet0 ::/0 ::/0 counter bt-pan_rmnet0:0 bytes
+ * 1450 1708806 RETURN all rmnet0 bt-pan ::/0 ::/0 counter rmnet0_bt-pan:0 bytes
+ */
+int BandwidthController::parseForwardChainStatsv6(SocketClient *cli, const TetherStats filter,
+ FILE *fp, std::string &extraProcessingInfo) {
+ int res;
+ char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
+ char iface0[MAX_IPT_OUTPUT_LINE_LEN];
+ char iface1[MAX_IPT_OUTPUT_LINE_LEN];
+ char rest[MAX_IPT_OUTPUT_LINE_LEN];
+
+ TetherStats stats;
+ char *buffPtr;
+ int64_t packets, bytes;
+
+ bool filterPair = filter.intIface[0] && filter.extIface[0];
+
+ char *filterMsg = filter.getStatsLine();
+ ALOGV(" parseForwardChainStatsv6 filter: %s", filterMsg);
+ free(filterMsg);
+
+ stats = filter;
+
+ while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
+ /* Clean up, so a failed parse can still print info */
+ iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
+
+ ALOGV( " lineBuffer ; %s ",lineBuffer);
+ res = sscanf(buffPtr, "%" PRId64" %" PRId64" RETURN all %s %s ::%s",
+ &packets, &bytes, iface0, iface1, rest);
+ ALOGE("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s>" \
+ "orig line=<%s>", res, iface0, iface1, packets, bytes, rest, buffPtr);
+ extraProcessingInfo += buffPtr;
+
+ if (res != MAX_EXPECTED_ARGS) {
+ continue;
+ }
+ /*
+ * The following assumes that the 1st rule has in:extIface out:intIface,
+ * which is what NatController sets up.
+ * If not filtering, the 1st match rx, and sets up the pair for the tx side.
+ */
+ if (filter.intIface[0] && filter.extIface[0]) {
+ if (filter.intIface == iface0 && filter.extIface == iface1) {
+ ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" " \
+ "rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+ stats.rxPackets = packets;
+ stats.rxBytes = bytes;
+ } else if (filter.intIface == iface1 && filter.extIface == iface0) {
+ ALOGE("2Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" " \
+ "rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+ stats.txPackets = packets;
+ stats.txBytes = bytes;
+ }
+ } else if (filter.intIface[0] || filter.extIface[0]) {
+ if (filter.intIface == iface0 || filter.extIface == iface1) {
+ ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" " \
+ "rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+ stats.intIface = iface0;
+ stats.extIface = iface1;
+ stats.rxPackets = packets;
+ stats.rxBytes = bytes;
+ } else if (filter.intIface == iface1 || filter.extIface == iface0) {
+ ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" " \
+ "rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+ stats.intIface = iface1;
+ stats.extIface = iface0;
+ stats.txPackets = packets;
+ stats.txBytes = bytes;
+ }
+ } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ {
+ if (!stats.intIface[0]) {
+ ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" " \
+ "rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+ stats.intIface = iface0;
+ stats.extIface = iface1;
+ stats.rxPackets = packets;
+ stats.rxBytes = bytes;
+ } else if (stats.intIface == iface1 && stats.extIface == iface0) {
+ ALOGE("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" " \
+ "rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+ stats.txPackets = packets;
+ stats.txBytes = bytes;
+ }
+ }
+ if (stats.rxBytes != -1 && stats.txBytes != -1) {
+ ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64" filterPair=%d",
+ stats.rxBytes, stats.txBytes, filterPair);
+ /* Send out stats, and prep for the next if needed. */
+ char *msg = stats.getStatsLine();
+ if (filterPair) {
+ cli->sendMsg(ResponseCode::TetheringStatsResult, msg, false);
+ return 0;
+ } else {
+ cli->sendMsg(ResponseCode::TetheringStatsListResult, msg, false);
+ stats = filter;
+ }
+ free(msg);
+ }
+ }
+ /* Successful if the last stats entry wasn't partial. */
+ if ((stats.rxBytes == -1) == (stats.txBytes == -1)) {
+ cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
+ return 0;
+ }
+ return -1;
+}
+
/*
* Parse the ptks and bytes out of:
* Chain natctrl_tether_counters (4 references)
@@ -1225,7 +1341,7 @@
(!statsFound && !filterPair)) {
return -1;
}
- cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
+ /* Tethering stats list completed will be sent from parseForwardChainStatsv6() */
return 0;
}
@@ -1237,9 +1353,9 @@
}
int BandwidthController::getTetherStats(SocketClient *cli, TetherStats &stats, std::string &extraProcessingInfo) {
- int res;
- std::string fullCmd;
- FILE *iptOutput;
+ int res, res6;
+ std::string fullCmd, fullCmd6;
+ FILE *iptOutput, *iptOutput6;
/*
* Why not use some kind of lib to talk to iptables?
@@ -1257,11 +1373,27 @@
extraProcessingInfo += "Failed to run iptables.";
return -1;
}
+
+ ALOGV(" getTetherStats iptOutput %s ", fullCmd.c_str());
res = parseForwardChainStats(cli, stats, iptOutput, extraProcessingInfo);
pclose(iptOutput);
- /* Currently NatController doesn't do ipv6 tethering, so we are done. */
- return res;
+ fullCmd6 = IP6TABLES_PATH;
+ fullCmd6 += " -nvx -L ";
+ fullCmd6 += NatController::LOCAL_TETHER_COUNTERS_CHAIN;
+ iptOutput6 = popen(fullCmd6.c_str(), "r");
+ if (!iptOutput6) {
+ ALOGE("Failed to run %s err=%s", fullCmd6.c_str(), strerror(errno));
+ extraProcessingInfo += "Failed to run ip6tables.";
+ return res;
+ }
+
+ ALOGV(" getTetherStats iptOutput6 %s ", fullCmd6.c_str());
+ res6 = parseForwardChainStatsv6(cli, stats, iptOutput6, extraProcessingInfo);
+ pclose(iptOutput6);
+
+ ALOGV(" ipv4: %d, ipv6 : %d ",res,res6);
+ return res + res6;
}
void BandwidthController::flushExistingCostlyTables(bool doClean) {
diff --git a/server/BandwidthController.h b/server/BandwidthController.h
index 2aca2cd..c6ae850 100644
--- a/server/BandwidthController.h
+++ b/server/BandwidthController.h
@@ -152,7 +152,8 @@
int setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes);
int removeCostlyAlert(const char *costName, int64_t *alertBytes);
-
+ int parseForwardChainStatsv6(SocketClient *cli, const TetherStats filter,
+ FILE *fp, std::string &extraProcessingInfo);
/*
* stats should never have only intIface initialized. Other 3 combos are ok.
* fp should be a file to the apropriate FORWARD chain of iptables rules.
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index b8bcf1a..5b21c50 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -1,5 +1,8 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,6 +48,11 @@
#include "FirewallController.h"
#include "RouteController.h"
#include "UidRanges.h"
+#include "QcRouteController.h"
+
+#ifdef QSAP_WLAN
+#include "qsap_api.h"
+#endif
#include <string>
#include <vector>
@@ -92,6 +100,9 @@
ResolverController *CommandListener::sResolverCtrl = NULL;
FirewallController *CommandListener::sFirewallCtrl = NULL;
ClatdController *CommandListener::sClatdCtrl = NULL;
+QcRouteController *CommandListener::sQcRouteCtrl = NULL;
+bool CommandListener::IpFwdCmd::iWlanFwdEnable = false;
+bool CommandListener::IpFwdCmd::fwIpFwdEnable = false;
/**
* List of module chains to be created, along with explicit ordering. ORDERING
@@ -159,14 +170,56 @@
// -N to create the chain
// -A to append the chain to parent
- execIptablesSilently(target, "-t", table, "-D", parentChain, "-j", *childChain, NULL);
- execIptablesSilently(target, "-t", table, "-F", *childChain, NULL);
- execIptablesSilently(target, "-t", table, "-X", *childChain, NULL);
- execIptables(target, "-t", table, "-N", *childChain, NULL);
- execIptables(target, "-t", table, "-A", parentChain, "-j", *childChain, NULL);
+ execIptablesSilently(target, "-w", "-t", table, "-D", parentChain, "-j", *childChain, NULL);
+ execIptablesSilently(target, "-w", "-t", table, "-F", *childChain, NULL);
+ execIptablesSilently(target, "-w", "-t", table, "-X", *childChain, NULL);
+ execIptables(target, "-w", "-t", table, "-N", *childChain, NULL);
+ execIptables(target, "-w", "-t", table, "-A", parentChain, "-j", *childChain, NULL);
} while (*(++childChain) != NULL);
}
+/**
+ * Check if string is a valid interface name.
+ * Utilize if_nametoindex, on success it returns ifindex, and on error 0.
+ */
+static bool isValidIface(const char* iface) {
+ return (0 != if_nametoindex(iface));
+}
+
+/**
+ * Check if string is a valid IPv4 or IPv6 address
+ * Utilize inet_pton, on success it returns 1, and on error it returns 0 or -1.
+ * Error -1 means af is not a valid address family, but since v4/v6 is used, this is not applicable.
+ * Error 0 means string is not a valid address, and only this is applicable.
+ */
+static bool isValidIp(const char* str, const char* af) {
+ int ret = -1;
+ unsigned char c;
+ if (!strcmp(af, "v4")) {
+ struct in_addr addr;
+ ret = inet_pton(AF_INET, str, &addr);
+ }
+ else if (!strcmp(af, "v6")) {
+ struct in6_addr addr6;
+ ret = inet_pton(AF_INET6, str, &addr6);
+ }
+ if ( ret == 1 )
+ {
+ /* There is limitation on inet_pton which on success case it allows trailing
+ character of '\0' or space. The '\0' is ensured not to be accepted by the caller,
+ but the spacing should be caught here.
+ */
+ c = *str;
+ while (c)
+ {
+ if ( isspace(c) ) return false;
+ c = *++str;
+ }
+ return true;
+ }
+ return false;
+}
+
CommandListener::CommandListener() :
FrameworkListener("netd", true) {
registerCmd(new InterfaceCmd());
@@ -175,13 +228,18 @@
registerCmd(new NatCmd());
registerCmd(new ListTtysCmd());
registerCmd(new PppdCmd());
+#ifdef QSAP_WLAN
+ registerCmd(new QsoftapCmd());
+#else
registerCmd(new SoftapCmd());
+#endif
registerCmd(new BandwidthControlCmd());
registerCmd(new IdletimerControlCmd());
registerCmd(new ResolverCmd());
registerCmd(new FirewallCmd());
registerCmd(new ClatdCmd());
registerCmd(new NetworkCommand());
+ registerCmd(new QcRouteCmd());
if (!sNetCtrl)
sNetCtrl = new NetworkController();
@@ -205,6 +263,8 @@
sInterfaceCtrl = new InterfaceController();
if (!sClatdCtrl)
sClatdCtrl = new ClatdController(sNetCtrl);
+ if (!sQcRouteCtrl)
+ sQcRouteCtrl = new QcRouteController();
/*
* This is the only time we touch top-level chains in iptables; controllers
@@ -519,9 +579,21 @@
free(tmp);
return 0;
} else if (!strcmp(argv[1], "enable")) {
+ if((argc > 2) && !strcmp(argv[2], "iwlan")) {
+ iWlanFwdEnable = true;
+ } else {
+ fwIpFwdEnable = true;
+ }
rc = sTetherCtrl->setIpFwdEnabled(true);
} else if (!strcmp(argv[1], "disable")) {
- rc = sTetherCtrl->setIpFwdEnabled(false);
+ if((argc > 2) && !strcmp(argv[2], "iwlan")) {
+ iWlanFwdEnable = false;
+ } else {
+ fwIpFwdEnable = false;
+ }
+ if( !iWlanFwdEnable && !fwIpFwdEnable ) {
+ rc = sTetherCtrl->setIpFwdEnabled(false);
+ }
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
return 0;
@@ -544,6 +616,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;
@@ -612,6 +685,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);
@@ -646,6 +724,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], INVALID_TABLE_NUMBER);
+ } 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") {
}
@@ -1627,4 +1773,313 @@
}
return syntaxError(client, "Unknown argument");
+
+}
+
+CommandListener::QcRouteCmd::QcRouteCmd() :
+ NetdCommand("route") {
+}
+
+int CommandListener::QcRouteCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+ if (argc < 5) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Missing argument", false);
+ return 0;
+ }
+
+ const char *ipVer = NULL;
+ int domain;
+
+ if (!strcmp(argv[3], "v4")) {
+ ipVer = "-4";
+ domain = AF_INET;
+ } else if (!strcmp(argv[3], "v6")) {
+ ipVer = "-6";
+ domain = AF_INET6;
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Supported family v4|v6",false);
+ return 0;
+ }
+
+ if (!strcmp(argv[2], "src")) {
+ /* source based routing */
+ if (!strcmp(argv[1], "replace")) {
+ if (argc != 7 && argc != 8) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: route replace src inet_family <interface>"
+ " <ipaddr> <routeId> [<gateway>]", false);
+ return 0;
+ }
+
+ char* end;
+ long int rid = strtol(argv[6], &end, 10);
+ if (*end != '\0')
+ {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "RouteID: invalid numerical value", false);
+ return 0;
+ }
+ if ((rid < 1) || (rid > 252)) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "0 < RouteID < 253", false);
+ return 0;
+ }
+
+ struct in_addr addr;
+ int prefix_length;
+ unsigned flags = 0;
+
+ ifc_init();
+ ifc_get_info(argv[4], &addr.s_addr, &prefix_length, &flags);
+ ifc_close();
+
+ char *iface = argv[4],
+ *srcPrefix = argv[5],
+ *routeId = argv[6],
+ *network = NULL,
+ *gateway = NULL;
+
+ if (false == isValidIface(iface)) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid interface", false);
+ return 0;
+ }
+
+ if (false == isValidIp(srcPrefix, argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid IP address", false);
+ return 0;
+ }
+
+ if (argc > 7) {
+ gateway = argv[7];
+ if (false == isValidIp(gateway, argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid gateway", false);
+ return 0;
+ }
+ }
+
+ // compute the network block in CIDR notation (for IPv4 only)
+ if (domain == AF_INET) {
+ struct in_addr net;
+ in_addr_t mask = prefixLengthToIpv4Netmask(prefix_length);
+ net.s_addr = (addr.s_addr & mask);
+
+
+ char net_s[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &(net.s_addr), net_s, INET_ADDRSTRLEN);
+ asprintf(&network, "%s/%d", net_s, prefix_length);
+ }
+
+ std::string res = sQcRouteCtrl->repSrcRoute( iface,
+ srcPrefix,
+ gateway,
+ routeId,
+ ipVer);
+ if (!res.empty()) {
+ cli->sendMsg(ResponseCode::OperationFailed, res.c_str(), false);
+ } else {
+ if (network != NULL) {
+ //gateway is null for link local route, metric is 0
+ res = sQcRouteCtrl->addDstRoute(iface,
+ network, NULL, 0, routeId);
+ if (res.empty()) {
+ res = "source route replace & local subnet "
+ "route add succeeded for rid: ";
+ res += routeId;
+ }
+ cli->sendMsg(ResponseCode::CommandOkay, res.c_str(), false);
+ } else {
+ res = "source route replace succeeded for rid:";
+ res += routeId;
+ cli->sendMsg(ResponseCode::CommandOkay, res.c_str(), false);
+ }
+ }
+ free(network);
+ } else if (!strcmp(argv[1], "del")) {
+ if (argc != 5) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: route del src v[4|6] <routeId>", false);
+ return 0;
+ }
+
+ char* end;
+ long int rid = strtol(argv[4], &end, 10);
+ if (*end != '\0')
+ {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "RouteID: invalid numerical value", false);
+ return 0;
+ }
+ if ((rid < 1) || (rid > 252)) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "RouteID: between 0 and 253", false);
+ return 0;
+ }
+
+ std::string res = sQcRouteCtrl->delSrcRoute(argv[4], ipVer);
+ if (!res.empty()) {
+ cli->sendMsg(ResponseCode::OperationFailed, res.c_str(), false);
+ } else {
+ res = "source route delete succeeded for rid:";
+ res += argv[4];
+ cli->sendMsg(ResponseCode::CommandOkay, res.c_str(), false);
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "permitted operation for src routes: <replace|del>",
+ false);
+ }
+ } else if (!strcmp(argv[2], "def")) {
+ /* default route configuration */
+ if (!strcmp(argv[1], "replace")) {
+ if ((argc != 5) && (argc != 6)) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: route replace def v[4|6]"
+ " <interface> [<gateway>]", false);
+ return 0;
+ }
+
+ char *iface = argv[4],
+ *gateway = NULL;
+
+ if (false == isValidIface(iface)) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid interface", false);
+ return 0;
+ }
+
+ if (argc > 5) {
+ gateway = argv[5];
+ if (false == isValidIp(gateway, argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid gateway", false);
+ return 0;
+ }
+ }
+
+ std::string res =
+ sQcRouteCtrl->replaceDefRoute(iface, gateway, ipVer);
+ if (!res.empty()) {
+ cli->sendMsg(ResponseCode::OperationFailed, res.c_str(), false);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay,
+ "default route replace succeeded", false);
+ }
+ } else if (!strcmp(argv[1], "add")) {
+ if ((argc !=6) && (argc != 7)) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: route add def v[4|6]"
+ " <interface> <metric> [<gateway>]", false);
+ return 0;
+ }
+
+ char *iface = argv[4],
+ *gateway = NULL;
+ int metric = atoi(argv[5]);
+
+ if (false == isValidIface(iface)) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid interface", false);
+ return 0;
+ }
+
+ if (argc > 6) {
+ gateway = argv[6];
+ if (false == isValidIp(gateway, argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid gateway", false);
+ return 0;
+ }
+ }
+
+ std::string res =
+ sQcRouteCtrl->addDefRoute(iface, gateway, ipVer, metric);
+ if (!res.empty()) {
+ cli->sendMsg(ResponseCode::OperationFailed, res.c_str(), false);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay,
+ "default route add with metric succeeded", false);
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Permitted action for def routes <replace|add>",
+ false);
+ }
+ } else if (!strcmp(argv[2], "dst")) {
+ /* destination based route configuration */
+ if (!strcmp(argv[1], "add")) {
+ if (argc != 7 && argc != 8) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: route add dst v[4|6]"
+ " <interface> <metric> <dstIpAddr> [<gateway>]", false);
+ return 0;
+ }
+
+ char *iface = argv[4],
+ *dstPrefix = argv[6],
+ *gateway = NULL;
+ int metric = atoi(argv[5]);
+
+ if (false == isValidIface(iface)) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid interface", false);
+ return 0;
+ }
+
+ if (false == isValidIp(dstPrefix, argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid IP address", false);
+ return 0;
+ }
+
+ if (argc > 7) {
+ gateway = argv[7];
+ if (false == isValidIp(gateway, argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid gateway", false);
+ return 0;
+ }
+ }
+
+ std::string res =
+ sQcRouteCtrl->addDstRoute(iface, dstPrefix, gateway, metric);
+ if (!res.empty()) {
+ cli->sendMsg(ResponseCode::OperationFailed, res.c_str(), false);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay,
+ "destination route add succeeded", false);
+ }
+ } else if (!strcmp(argv[1], "del")) {
+ if (argc != 5) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: route del dst v[4|6] <ipaddr>", false);
+ return 0;
+ }
+
+ if (false == isValidIp(argv[4], argv[3]) ) {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "invalid IP address", false);
+ return 0;
+ }
+
+ std::string res = sQcRouteCtrl->delDstRoute(argv[4]);
+ if (!res.empty()){
+ cli->sendMsg(ResponseCode::OperationFailed, res.c_str(), false);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay,
+ "destination route delete succeeded", false);
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "permitted operation for dst routes: <add|del>",
+ false);
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandParameterError,
+ "allowed route types: <src|dst|def>", false);
+ }
+ return 0;
}
diff --git a/server/CommandListener.h b/server/CommandListener.h
index f605647..76fe1b7 100644
--- a/server/CommandListener.h
+++ b/server/CommandListener.h
@@ -1,5 +1,8 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +34,7 @@
#include "ResolverController.h"
#include "FirewallController.h"
#include "ClatdController.h"
+#include "QcRouteController.h"
class CommandListener : public FrameworkListener {
static TetherController *sTetherCtrl;
@@ -43,6 +47,7 @@
static ResolverController *sResolverCtrl;
static FirewallController *sFirewallCtrl;
static ClatdController *sClatdCtrl;
+ static QcRouteController *sQcRouteCtrl;
public:
static NetworkController *sNetCtrl;
@@ -59,6 +64,15 @@
int runCommand(SocketClient *c, int argc, char ** argv);
};
+#ifdef QSAP_WLAN
+ class QsoftapCmd : public SoftapCmd {
+ public:
+ QsoftapCmd();
+ virtual ~QsoftapCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+#endif
+
class InterfaceCmd : public NetdCommand {
public:
InterfaceCmd();
@@ -71,6 +85,9 @@
IpFwdCmd();
virtual ~IpFwdCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
+ private:
+ static bool iWlanFwdEnable;
+ static bool fwIpFwdEnable;
};
class TetherCmd : public NetdCommand {
@@ -80,6 +97,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();
@@ -153,6 +177,13 @@
int operationError(SocketClient* cli, const char* message, int ret);
int success(SocketClient* cli);
};
+
+ class QcRouteCmd : public NetdCommand {
+ public:
+ QcRouteCmd();
+ virtual ~QcRouteCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
};
#endif
diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp
index 17c6da4..b3571d1 100644
--- a/server/FirewallController.cpp
+++ b/server/FirewallController.cpp
@@ -45,9 +45,9 @@
disableFirewall();
// create default rule to drop all traffic
- res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
- res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
- res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
+ res |= execIptables(V4V6, "-w", "-A", LOCAL_INPUT, "-j", "DROP", NULL);
+ res |= execIptables(V4V6, "-w", "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
+ res |= execIptables(V4V6, "-w", "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
return res;
}
@@ -56,9 +56,9 @@
int res = 0;
// flush any existing rules
- res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
- res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
- res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL);
+ res |= execIptables(V4V6, "-w", "-F", LOCAL_INPUT, NULL);
+ res |= execIptables(V4V6, "-w", "-F", LOCAL_OUTPUT, NULL);
+ res |= execIptables(V4V6, "-w", "-F", LOCAL_FORWARD, NULL);
return res;
}
@@ -82,8 +82,8 @@
}
int res = 0;
- res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
- res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
+ res |= execIptables(V4V6, "-w", op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL);
+ res |= execIptables(V4V6, "-w", op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL);
return res;
}
@@ -101,8 +101,8 @@
}
int res = 0;
- res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
- res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
+ res |= execIptables(target, "-w", op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL);
+ res |= execIptables(target, "-w", op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL);
return res;
}
@@ -127,9 +127,9 @@
}
int res = 0;
- res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
+ res |= execIptables(target, "-w", op, LOCAL_INPUT, "-s", addr, "-p", protocolStr,
"--sport", portStr, "-j", "RETURN", NULL);
- res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
+ res |= execIptables(target, "-w", op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr,
"--dport", portStr, "-j", "RETURN", NULL);
return res;
}
@@ -146,9 +146,9 @@
}
int res = 0;
- res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
+ res |= execIptables(V4V6, "-w", op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
"-j", "RETURN", NULL);
- res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
+ res |= execIptables(V4V6, "-w", op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
"-j", "RETURN", NULL);
return res;
}
diff --git a/server/QcRouteController.cpp b/server/QcRouteController.cpp
new file mode 100644
index 0000000..0dbb9b0
--- /dev/null
+++ b/server/QcRouteController.cpp
@@ -0,0 +1,303 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_NDDEBUG 0
+#define LOG_NIDEBUG 0
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+
+#include <cutils/log.h>
+#include "QcRouteController.h"
+
+const char *TAG = "QcRouteController";
+static char IP_PATH[] = "/system/bin/ip";
+const char *QcRouteController::MAIN_TABLE = "254";
+const char *QcRouteController::SOURCE_POLICY_RULE_PRIO = "150";
+const int MAXSIZE = 256;
+
+
+QcRouteController::QcRouteController() {
+}
+
+QcRouteController::~QcRouteController() {
+}
+
+std::string QcRouteController::_runIpCmd(const char * cmd) {
+ FILE *fp = NULL;
+ char line[MAXSIZE];
+ std::string res, buffer;
+
+ if (strlen(cmd) > 255) {
+ return std::string(strerror(E2BIG));
+ }
+
+ buffer = IP_PATH;
+ buffer += " ";
+ buffer += cmd;
+ buffer += " 2>&1"; //capture stderr
+
+ ALOGV(TAG,"%s", buffer.c_str());
+
+ if ((fp = popen(buffer.c_str(),"r")) == NULL) {
+ ALOGE(TAG, "failed to popen: %s", strerror(errno));
+ res = (strerror(errno));
+ } else if (fgets(line, sizeof line, fp)) {
+ ALOGV(TAG, "%s", line);
+ res = cmd;
+ res += ": ";
+ res += line;
+ }
+ pclose(fp);
+
+ return res;
+}
+
+std::string QcRouteController::repSrcRoute
+(
+ const char *iface,
+ const char *srcPrefix,
+ const char *gateway,
+ const char *table,
+ const char *ipver
+)
+{
+ std::string res = _repDefRoute(iface, gateway, table, ipver);
+ if (res.empty()) {
+ _delRule(table, ipver);
+ res = _addRule(srcPrefix, table, ipver);
+ if (res.empty())
+ res = _flushCache();
+ }
+
+ return res;
+}
+
+std::string QcRouteController::delSrcRoute
+(
+ const char *table,
+ const char *ipver
+)
+{
+ //if iface is down then route is probably purged; ignore the error.
+ _delDefRoute(table, ipver);
+ std::string res = _delRule(table, ipver);
+ if (res.empty())
+ res = _flushCache();
+
+ return res;
+}
+
+std::string QcRouteController::addDstRoute
+(
+ const char *iface,
+ const char *dstPrefix,
+ const char *gateway,
+ const int metric,
+ const char *table
+)
+{
+ char buffer[255];
+
+ if (gateway) {
+ snprintf(buffer, sizeof buffer,
+ "route add %s via %s dev %s table %s metric %d",
+ dstPrefix, gateway, iface, table, metric);
+ } else {
+ snprintf(buffer, sizeof buffer,
+ "route add %s dev %s table %s metric %d",
+ dstPrefix, iface, table, metric);
+ }
+
+ //blindly delete an indentical route if it exists.
+ _delHostRoute(dstPrefix, table);
+
+ std::string res = _runIpCmd(buffer);
+ if (res.empty() || (res.find("exists") != std::string::npos))
+ res = _flushCache();
+
+ return res;
+}
+
+std::string QcRouteController::delDstRoute
+(
+ const char *dstPrefix,
+ const char *table
+)
+{
+ std::string res = _delHostRoute(dstPrefix, table);
+ if (res.empty())
+ res = _flushCache();
+
+ return res;
+}
+
+std::string QcRouteController::_delHostRoute
+(
+ const char *dstPrefix,
+ const char *table
+)
+{
+ char buffer[255];
+ snprintf(buffer, sizeof buffer, "route del %s table %s",
+ dstPrefix, table);
+
+ return _runIpCmd(buffer);
+}
+
+std::string QcRouteController::replaceDefRoute
+(
+ const char *iface,
+ const char *gateway,
+ const char *ipver
+)
+{
+ std::string res = _repDefRoute(iface, gateway, MAIN_TABLE, ipver);
+ if (res.empty())
+ res = _flushCache();
+
+ return res;
+}
+
+std::string QcRouteController::_repDefRoute
+(
+ const char *iface,
+ const char *gateway,
+ const char *table,
+ const char *ipver
+)
+{
+ char buffer[255];
+
+ if (gateway) {
+ snprintf(buffer, sizeof buffer,
+ "%s route replace default via %s dev %s scope global table %s",
+ ipver, gateway, iface, table);
+ } else {
+ snprintf(buffer, sizeof buffer,
+ "%s route replace default dev %s table %s",
+ ipver, iface, table);
+ }
+
+ return _runIpCmd(buffer);
+}
+
+std::string QcRouteController::_delDefRoute
+(
+ const char *table,
+ const char *ipver,
+ const char *iface
+)
+{
+ char buffer[255];
+
+ if (iface) {
+ snprintf(buffer, sizeof buffer,
+ "%s route del default dev %s table %s",
+ ipver, iface, table);
+ } else {
+ snprintf(buffer, sizeof buffer,
+ "%s route del default table %s", ipver, table);
+ }
+
+ return _runIpCmd(buffer);
+}
+
+std::string QcRouteController::addDefRoute
+(
+ const char *iface,
+ const char *gateway,
+ const char *ipver,
+ const int metric,
+ const char *table
+)
+{
+ char buffer[255];
+
+ //remove existing def route for an iface before adding one with new metric
+ _delDefRoute(table, ipver, iface);
+
+ if (gateway) {
+ snprintf(buffer, sizeof buffer,
+ "%s route add default via %s dev %s table %s metric %d",
+ ipver, gateway, iface, table, metric);
+ } else {
+ snprintf(buffer, sizeof buffer,
+ "%s route add default dev %s table %s metric %d",
+ ipver, iface, table, metric);
+ }
+
+ std::string res = _runIpCmd(buffer);
+ if (res.empty())
+ res = _flushCache();
+
+ return res;
+}
+
+std::string QcRouteController::_flushCache() {
+ char buffer[255];
+
+ snprintf(buffer, sizeof buffer, "route flush cached");
+
+ return _runIpCmd(buffer);
+}
+
+std::string QcRouteController::_addRule
+(
+ const char *address,
+ const char *table,
+ const char *ipver
+)
+{
+ char buffer[255];
+
+ snprintf(buffer, sizeof buffer,
+ "%s rule add from %s lookup %s prio %s", ipver, address, table,
+ SOURCE_POLICY_RULE_PRIO);
+
+ return _runIpCmd(buffer);
+}
+
+std::string QcRouteController::_delRule
+(
+ const char *table,
+ const char *ipver
+)
+{
+ char buffer[255];
+
+ snprintf(buffer, sizeof buffer,
+ "%s rule del table %s", ipver, table);
+
+ return _runIpCmd(buffer);
+}
diff --git a/server/QcRouteController.h b/server/QcRouteController.h
new file mode 100644
index 0000000..75c7edb
--- /dev/null
+++ b/server/QcRouteController.h
@@ -0,0 +1,121 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _QCROUTE_CONTROLLER_H
+#define _QCROUTE_CONTROLLER_H
+
+#include <string.h>
+#include <string>
+
+class QcRouteController {
+public:
+ QcRouteController();
+ virtual ~QcRouteController();
+
+ std::string repSrcRoute
+ (
+ const char *iface,
+ const char *srcPrefix,
+ const char *gateway,
+ const char *table,
+ const char *ipver
+ );
+ std::string delSrcRoute
+ (
+ const char *table,
+ const char *ipver
+ );
+ std::string addDstRoute
+ (
+ const char *iface,
+ const char *dstPrefix,
+ const char *gateway,
+ const int metric,
+ const char *table = MAIN_TABLE
+ );
+ std::string delDstRoute
+ (
+ const char *dstPrefix,
+ const char *table = MAIN_TABLE
+ );
+ std::string replaceDefRoute
+ (
+ const char *iface,
+ const char *gateway,
+ const char *ipver
+ );
+ std::string addDefRoute
+ (
+ const char *iface,
+ const char *gateway,
+ const char *ipver,
+ const int metric,
+ const char *table = MAIN_TABLE
+ );
+
+private:
+ const static char *MAIN_TABLE;
+ const static char *SOURCE_POLICY_RULE_PRIO;
+ std::string _runIpCmd
+ (
+ const char *cmd
+ );
+ std::string _flushCache();
+ std::string _repDefRoute
+ (
+ const char *iface,
+ const char *gateway,
+ const char *table,
+ const char *ipver
+ );
+ std::string _delDefRoute
+ (
+ const char *table,
+ const char *ipver,
+ const char *iface = NULL
+ );
+ std::string _delHostRoute
+ (
+ const char *dstPrefix,
+ const char *table = MAIN_TABLE
+ );
+ std::string _addRule
+ (
+ const char *address,
+ const char *table,
+ const char *ipver
+ );
+ std::string _delRule
+ (
+ const char *table,
+ const char *ipver
+ );
+};
+
+#endif
diff --git a/server/QsoftapCmd.cpp b/server/QsoftapCmd.cpp
new file mode 100644
index 0000000..dab2820
--- /dev/null
+++ b/server/QsoftapCmd.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * 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 <string.h>
+
+#define LOG_TAG "QsoftapCmd"
+#include <cutils/log.h>
+
+#include "CommandListener.h"
+#include "ResponseCode.h"
+
+#include "qsap_api.h"
+
+#include <cutils/properties.h>
+static char ath6kl_supported[PROPERTY_VALUE_MAX];
+
+CommandListener::QsoftapCmd::QsoftapCmd() :
+ SoftapCmd::SoftapCmd() {
+}
+
+int CommandListener::QsoftapCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ int rc = 0;
+
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument", false);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "qccmd")) {
+#define MAX_CMD_SIZE 256
+ char qcCmdBuf[MAX_CMD_SIZE], *pCmdBuf;
+ u32 len = MAX_CMD_SIZE;
+ int i = 2, ret;
+
+ if ( argc < 4 ) {
+ cli->sendMsg(ResponseCode::OperationFailed, "failure: invalid arguments", true);
+ return 0;
+ }
+
+ argc -= 2;
+ pCmdBuf = qcCmdBuf;
+#ifdef QSAP_STA_CONCURRENCY
+ //SAP STA Concurrency Customization
+ // Cmd Format Example "set sap_sta_concurrency=6" where 6 is STA Mode channel
+ if (!strncmp(argv[3], "sap_sta_concurrency=",20) && !strcmp(argv[2], "set")) {
+ //Extract STA Mode channel number from cmd
+ int sta_channel = atoi(&argv[3][20]);
+ int sap_channel;
+ //Get SAP Mode channel from SoftAP SDK
+ ret = snprintf(pCmdBuf, len, " get channel");
+ len = MAX_CMD_SIZE;
+ //Send cmd to SoftAP SDK
+ qsap_hostd_exec_cmd(qcCmdBuf, qcCmdBuf, (u32*)&len);
+ cli->sendMsg(qcCmdBuf);
+
+ sap_channel = atoi(&qcCmdBuf[16]);
+ ALOGD("SAP STA Concurrency GET CHANNEL Rsp %s STA Channel %d SAP Channel %d",qcCmdBuf,sta_channel,sap_channel);
+
+ //StopSoftAP and exitAP if channels are different
+ if(sta_channel != sap_channel) {
+ rc = sSoftapCtrl->stopSoftap();
+ if (!rc) {
+ cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
+ }
+ //Send exitAP cmd to SoftAP SDK
+ len = MAX_CMD_SIZE;
+ ret = snprintf(pCmdBuf, len, " set reset_ap=5");
+ qsap_hostd_exec_cmd(qcCmdBuf, qcCmdBuf, (u32*)&len);
+ cli->sendMsg(qcCmdBuf);
+ ALOGD("SAP STA Concurrency result for exitAP %s",qcCmdBuf);
+ }
+
+ return 0;
+ }
+ // Cmd Format Example "set sta_assoc_complete_ind"
+ else if (!strcmp(argv[3], "sta_assoc_complete_ind") && !strcmp(argv[2], "set")) {
+ //StartSoftAP and initAP if SoftAP is down
+ if(!sSoftapCtrl->isSoftapStarted()) {
+ //Send initAP cmd to SoftAP SDK
+ len = MAX_CMD_SIZE;
+ ret = snprintf(pCmdBuf, len, " set reset_ap=4");
+ //Send cmd to SoftAP SDK
+ qsap_hostd_exec_cmd(qcCmdBuf, qcCmdBuf, (u32*)&len);
+ cli->sendMsg(qcCmdBuf);
+ ALOGD("SAP STA Concurrency result for initAP %s",qcCmdBuf);
+
+ rc = sSoftapCtrl->startSoftap();
+ if (!rc) {
+ cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
+ }
+ }
+ return 0;
+ } //SAP STA Concurrency Customization Ends
+ else
+#endif //QSAP_STA_CONCURRENCY
+ {
+
+ while (argc--) {
+ ret = snprintf(pCmdBuf, len, " %s", argv[i]);
+ if ((ret < 0) || (ret >= (int)len)) {
+ /* Error case */
+ /* TODO: Command too long send the error message */
+ *pCmdBuf = '\0';
+ break;
+ }
+ pCmdBuf += ret;
+ len -= ret;
+ i++;
+ }
+
+ len = MAX_CMD_SIZE;
+ qsap_hostd_exec_cmd(qcCmdBuf, qcCmdBuf, (u32*)&len);
+ cli->sendMsg(ResponseCode::CommandOkay, qcCmdBuf, false);
+ return 0;
+ }
+ } else if (!strcmp(argv[1], "set")) {
+ /* When the WLAN is AR6004, use the Android native
+ SoftapController command. */
+ property_get("wlan.driver.ath", ath6kl_supported, 0);
+ if (*ath6kl_supported == '2') {
+ return SoftapCmd::runCommand(cli, argc, argv);
+ }
+
+ /* override processing of the "softap set" command. The
+ default class will install a hostapd.conf which contains
+ just the settings supported by the Android framework, and
+ will do this every time Soft AP is enabled. This will
+ destroy the hostapd.conf used to store the settings used by
+ the QSoftAP SDK */
+ ALOGD("Got softap set command we are overriding");
+ rc = qsapsetSoftap(argc, argv);
+ } else {
+ /* all other commands will be handed off to the native handler */
+ ALOGD("Got softap %s command we are passing on", argv[1]);
+ return SoftapCmd::runCommand(cli, argc, argv);
+ }
+
+ if (!rc) {
+ cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
+ }
+
+ return 0;
+}
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/RouteController.cpp b/server/RouteController.cpp
index c3a600d..ba5da9d 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -437,7 +437,7 @@
char markString[UINT32_HEX_STRLEN];
snprintf(markString, sizeof(markString), "0x%x", fwmark.intValue);
- if (execIptables(V4V6, "-t", "mangle", add ? "-A" : "-D", "INPUT", "-i", interface, "-j",
+ if (execIptables(V4V6, "-w", "-t", "mangle", add ? "-A" : "-D", "INPUT", "-i", interface, "-j",
"MARK", "--set-mark", markString, NULL)) {
ALOGE("failed to change iptables rule that sets incoming packet mark");
return -EREMOTEIO;
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index fb51c06..a5a91b4 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,32 @@
#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
+#define RTRADVDAEMON_MIN_IFACES 2
+#define MAX_TABLE_LEN 11
+#define MIN_TABLE_NUMBER 0
+#define IP_ADDR "ip addr"
+#define BASE_TABLE_NUMBER 1000
+#define IF_INDEX_PATH "/sys/class/net/%s/ifindex"
+#define SYS_PATH_SIZE PROC_PATH_SIZE
+
+/* This is the number of arguments for RTRADVDAEMON which accounts for the
+ * location of the daemon, the table name option, the name of the table
+ * and a final empty string
+ */
+#define RTRADVDAEMON_ARGS_COUNT 4
+
TetherController::TetherController() {
mInterfaces = new InterfaceCollection();
- mDnsNetId = 0;
+ mUpstreamInterfaces = new InterfaceCollection();
mDnsForwarders = new NetAddressCollection();
mDaemonFd = -1;
mDaemonPid = 0;
@@ -52,9 +79,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 +128,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 +259,164 @@
return (mDaemonPid == 0 ? false : true);
}
+int TetherController::startV6RtrAdv(int num_ifaces, char **ifaces, int table_number) {
+ int pid;
+ int num_processed_args = 1;
+ gid_t groups [] = { AID_NET_ADMIN, AID_NET_RAW, AID_INET };
+
+ if (num_ifaces < RTRADVDAEMON_MIN_IFACES) {
+ 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 + RTRADVDAEMON_ARGS_COUNT, sizeof(char *));
+ if (!args) {
+ ALOGE("%s: failed to allocate memory", __func__);
+ return -1;
+ }
+
+ args[0] = strdup(RTRADVDAEMON);
+ int aidx = 0;
+ for (int i=0; i < num_ifaces; i++) {
+ aidx = 3 * i + num_processed_args;
+ args[aidx++] = (char *)"-i";
+ args[aidx++] = ifaces[i];
+ args[aidx++] = (char *)"-x";
+ }
+ if (table_number > MIN_TABLE_NUMBER) {
+ char table_name[MAX_TABLE_LEN];
+ unsigned int retval = 0;
+ table_number += BASE_TABLE_NUMBER;
+ retval = snprintf(table_name, sizeof(table_name), "%d", table_number);
+ if (retval >= sizeof(table_name)) {
+ ALOGE("%s: String truncation occured", __func__);
+ } else {
+ args[aidx++] = (char *)"-t";
+ args[aidx] = table_name;
+ }
+ }
+
+ 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::getIfaceIndexForIface(const char *iface)
+{
+ FILE *fp = NULL;
+ char res[MAX_TABLE_LEN];
+ int iface_num = -1;
+ char if_index[SYS_PATH_SIZE];
+ unsigned int retval = 0;
+ if (iface == NULL)
+ {
+ ALOGE("%s() Interface is NULL", __func__);
+ return iface_num;
+ }
+
+ memset(if_index, 0, sizeof(if_index));
+ retval = snprintf(if_index, sizeof(if_index), IF_INDEX_PATH, iface);
+ if (retval >= sizeof(if_index)) {
+ ALOGE("%s() String truncation occurred", __func__);
+ return iface_num;
+ }
+
+ ALOGD("%s() File path is %s", __func__, if_index);
+ fp = fopen(if_index, "r");
+ if (fp == NULL)
+ {
+ ALOGE("%s() Cannot read file : path %s, error %s", __func__, if_index, strerror(errno));
+ return iface_num;
+ }
+
+ memset(res, 0, sizeof(res));
+ while (fgets(res, sizeof(res)-1, fp) != NULL)
+ {
+ ALOGD("%s() %s", __func__, res);
+ iface_num = atoi(res);
+ ALOGD("%s() Interface index for interface %s is %d", __func__, iface, iface_num);
+ }
+
+ fclose(fp);
+ return iface_num;
+}
+
+/* Stop and start the ipv6 router advertisement daemon with the updated
+ * interfaces. Pass the table number as a command line argument when
+ * tethering is enabled.
+ */
+int TetherController::configureV6RtrAdv() {
+ char **args;
+ int i;
+ int len;
+ InterfaceCollection::iterator it;
+ int iface_index = -1;
+ /* For now, just stop and start the daemon with the new interface list */
+
+ len = mInterfaces->size() + mUpstreamInterfaces->size();
+ 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;
+ iface_index = getIfaceIndexForIface(args[i]);
+ ALOGD("%s: Upstream Iface: %s iface index: %d", __func__, args[i], iface_index);
+ }
+
+ stopV6RtrAdv();
+ startV6RtrAdv(i, args, iface_index);
+
+ free(args);
+
+ return 0;
+}
+
+bool TetherController::isV6RtrAdvStarted() {
+ return (mRtrAdvPid == 0 ? false : true);
+}
+
#define MAX_CMD_SIZE 1024
int TetherController::setDnsForwarders(unsigned netId, char **servers, int numServers) {
@@ -252,6 +471,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 configureV6RtrAdv();
+}
+
+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 configureV6RtrAdv();
+ }
+ }
+
+ ALOGW("Couldn't find interface %s to remove", iface);
+ return 0;
+}
+
NetAddressCollection *TetherController::getDnsForwarders() {
return mDnsForwarders;
}
@@ -294,6 +555,8 @@
}
mInterfaces->push_back(strdup(interface));
+ configureV6RtrAdv();
+
if (applyDnsInterfaces()) {
InterfaceCollection::iterator it;
for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) {
@@ -318,7 +581,7 @@
if (!strcmp(interface, *it)) {
free(*it);
mInterfaces->erase(it);
-
+ configureV6RtrAdv();
return applyDnsInterfaces();
}
}
diff --git a/server/TetherController.h b/server/TetherController.h
index 1c32627..85f7f11 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -21,6 +21,8 @@
#include "List.h"
+#define INVALID_TABLE_NUMBER -1
+
typedef android::netd::List<char *> InterfaceCollection;
typedef android::netd::List<struct in_addr> NetAddressCollection;
@@ -32,6 +34,8 @@
NetAddressCollection *mDnsForwarders;
pid_t mDaemonPid;
int mDaemonFd;
+ pid_t mRtrAdvPid; // IPv6 support
+ InterfaceCollection *mUpstreamInterfaces;
public:
TetherController();
@@ -52,9 +56,16 @@
int tetherInterface(const char *interface);
int untetherInterface(const char *interface);
InterfaceCollection *getTetheredInterfaceList();
+ int startV6RtrAdv(int num_ifaces, char **ifaces, int table_number);
+ int stopV6RtrAdv();
+ bool isV6RtrAdvStarted();
+ int configureV6RtrAdv();
+ int addUpstreamInterface(char *iface);
+ int removeUpstreamInterface(char *iface);
private:
int applyDnsInterfaces();
+ int getIfaceIndexForIface(const char *iface);
};
#endif