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