Merge "Revert "Work around new clang compiler warning.""
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..89c8871
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+ek@google.com
+lorenzo@google.com
+jscherpelz@google.com
diff --git a/libnetdutils/Android.bp b/libnetdutils/Android.bp
index f4046a3..c2f97ab 100644
--- a/libnetdutils/Android.bp
+++ b/libnetdutils/Android.bp
@@ -1,13 +1,14 @@
 cc_library_shared {
     name: "libnetdutils",
     srcs: [
-        "Status.cpp",
         "Fd.cpp",
-        "UniqueFd.cpp",
-        "Syscalls.cpp",
         "Netfilter.cpp",
         "Netlink.cpp",
         "Slice.cpp",
+        "Socket.cpp",
+        "Status.cpp",
+        "Syscalls.cpp",
+        "UniqueFd.cpp",
     ],
     shared_libs: [
         "libbase",
diff --git a/libnetdutils/Socket.cpp b/libnetdutils/Socket.cpp
new file mode 100644
index 0000000..e962b6e
--- /dev/null
+++ b/libnetdutils/Socket.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 <arpa/inet.h>
+
+#include "netdutils/Slice.h"
+#include "netdutils/Socket.h"
+
+namespace android {
+namespace netdutils {
+
+StatusOr<std::string> toString(const in6_addr& addr) {
+    std::array<char, INET6_ADDRSTRLEN> out = {};
+    auto* rv = inet_ntop(AF_INET6, &addr, out.data(), out.size());
+    if (rv == nullptr) {
+        return statusFromErrno(errno, "inet_ntop() failed");
+    }
+    return std::string(out.data());
+}
+
+}  // namespace netdutils
+}  // namespace android
diff --git a/libnetdutils/Status.cpp b/libnetdutils/Status.cpp
index e36889b..b373c56 100644
--- a/libnetdutils/Status.cpp
+++ b/libnetdutils/Status.cpp
@@ -21,22 +21,26 @@
 namespace android {
 namespace netdutils {
 
-void expectOk(const Status) {
+void expectOk(const Status&) {
     // TODO: put something here, for now this function serves solely as documentation.
 }
 
 Status statusFromErrno(int err, const std::string& msg) {
-    return Status(err, base::StringPrintf("%d : [%s] : %s", err, strerror(err), msg.c_str()));
+    return Status(err, base::StringPrintf("[%s] : %s", strerror(err), msg.c_str()));
 }
 
-std::string toString(const Status status) {
+bool equalToErrno(const Status& status, int err) {
+    return status.code() == err;
+}
+
+std::string toString(const Status& status) {
     std::stringstream ss;
     ss << status;
     return ss.str();
 }
 
 std::ostream& operator<<(std::ostream& os, const Status& s) {
-    return os << "Status[code: " << s.code() << ", msg: " << s.msg() << "]";
+    return os << "Status[code: " << s.code() << ", msg: \"" << s.msg() << "\"]";
 }
 
 }  // namespace netdutils
diff --git a/libnetdutils/Syscalls.cpp b/libnetdutils/Syscalls.cpp
index 3b2c826..bad8631 100644
--- a/libnetdutils/Syscalls.cpp
+++ b/libnetdutils/Syscalls.cpp
@@ -41,6 +41,14 @@
   public:
     ~RealSyscalls() override = default;
 
+    StatusOr<UniqueFd> open(const std::string& pathname, int flags, mode_t mode) const override {
+        UniqueFd fd(::open(pathname.c_str(), flags, mode));
+        if (!isWellFormed(fd)) {
+            return statusFromErrno(errno, "open(\"" + pathname + "\"...) failed");
+        }
+        return fd;
+    }
+
     StatusOr<UniqueFd> socket(int domain, int type, int protocol) const override {
         UniqueFd sock(::socket(domain, type, protocol));
         if (!isWellFormed(sock)) {
diff --git a/libnetdutils/SyscallsTest.cpp b/libnetdutils/SyscallsTest.cpp
index 5dda9cc..a754d1c 100644
--- a/libnetdutils/SyscallsTest.cpp
+++ b/libnetdutils/SyscallsTest.cpp
@@ -30,9 +30,10 @@
 #include "netdutils/StatusOr.h"
 #include "netdutils/Syscalls.h"
 
+using testing::ByMove;
 using testing::DoAll;
-using testing::Mock;
 using testing::Invoke;
+using testing::Mock;
 using testing::Return;
 using testing::StrictMock;
 using testing::_;
@@ -54,6 +55,18 @@
     EXPECT_EQ(&old, &sSyscalls.get());
 }
 
+TEST_F(SyscallsTest, open) {
+    const char kPath[] = "/test/path/please/ignore";
+    constexpr Fd kFd(40);
+    constexpr int kFlags = 883;
+    constexpr mode_t kMode = 37373;
+    const auto& sys = sSyscalls.get();
+    EXPECT_CALL(mSyscalls, open(kPath, kFlags, kMode)).WillOnce(Return(ByMove(UniqueFd(kFd))));
+    auto result = sys.open(kPath, kFlags, kMode);
+    EXPECT_EQ(status::ok, result.status());
+    EXPECT_EQ(kFd, result.value());
+}
+
 TEST_F(SyscallsTest, getsockname) {
     constexpr Fd kFd(40);
     sockaddr_nl expected = {};
diff --git a/libnetdutils/include/netdutils/MockSyscalls.h b/libnetdutils/include/netdutils/MockSyscalls.h
index 701d4d5..d6a02b5 100644
--- a/libnetdutils/include/netdutils/MockSyscalls.h
+++ b/libnetdutils/include/netdutils/MockSyscalls.h
@@ -33,6 +33,8 @@
   public:
     virtual ~MockSyscalls() = default;
     // Use Return(ByMove(...)) to deal with movable return types.
+    MOCK_CONST_METHOD3(open,
+                       StatusOr<UniqueFd>(const std::string& pathname, int flags, mode_t mode));
     MOCK_CONST_METHOD3(socket, StatusOr<UniqueFd>(int domain, int type, int protocol));
     MOCK_CONST_METHOD3(getsockname, Status(Fd sock, sockaddr* addr, socklen_t* addrlen));
     MOCK_CONST_METHOD5(setsockopt, Status(Fd sock, int level, int optname, const void* optval,
diff --git a/libnetdutils/include/netdutils/Slice.h b/libnetdutils/include/netdutils/Slice.h
index 40f173e..85a0980 100644
--- a/libnetdutils/include/netdutils/Slice.h
+++ b/libnetdutils/include/netdutils/Slice.h
@@ -74,6 +74,12 @@
     return {const_cast<T*>(&ref), sizeof(ref)};
 }
 
+// Return slice representation of string data()
+inline const Slice makeSlice(const std::string& s) {
+    using ValueT = std::string::value_type;
+    return {const_cast<ValueT*>(s.data()), s.size() * sizeof(ValueT)};
+}
+
 // Return slice representation of vector data()
 template <typename T>
 inline const Slice makeSlice(const std::vector<T>& v) {
diff --git a/libnetdutils/include/netdutils/Socket.h b/libnetdutils/include/netdutils/Socket.h
index 0bb5682..e5aaab9 100644
--- a/libnetdutils/include/netdutils/Socket.h
+++ b/libnetdutils/include/netdutils/Socket.h
@@ -14,7 +14,14 @@
  * limitations under the License.
  */
 
+#ifndef NETDUTILS_SOCKET_H
+#define NETDUTILS_SOCKET_H
+
+#include <netinet/in.h>
 #include <sys/socket.h>
+#include <string>
+
+#include "netdutils/StatusOr.h"
 
 namespace android {
 namespace netdutils {
@@ -27,5 +34,11 @@
     return reinterpret_cast<const sockaddr*>(addr);
 }
 
+// Return a string representation of addr or Status if there was a
+// failure during conversion.
+StatusOr<std::string> toString(const in6_addr& addr);
+
 }  // namespace netdutils
 }  // namespace android
+
+#endif /* NETDUTILS_SOCKET_H */
diff --git a/libnetdutils/include/netdutils/Status.h b/libnetdutils/include/netdutils/Status.h
index 5a89289..a9209bf 100644
--- a/libnetdutils/include/netdutils/Status.h
+++ b/libnetdutils/include/netdutils/Status.h
@@ -41,9 +41,7 @@
 
     const std::string& msg() const { return mMsg; }
 
-    bool operator==(const Status& other) const {
-        return (code() == other.code()) && (msg() == other.msg());
-    }
+    bool operator==(const Status& other) const { return code() == other.code(); }
     bool operator!=(const Status& other) const { return !(*this == other); }
 
   private:
@@ -54,7 +52,9 @@
 namespace status {
 
 const Status ok{0};
-const Status eof{256, "end of file"};
+// EOF is not part of errno space, we'll place it far above the
+// highest existing value.
+const Status eof{0x10001, "end of file"};
 const Status undefined{std::numeric_limits<int>::max(), "undefined"};
 
 }  // namespace status
@@ -62,23 +62,27 @@
 // Return true if status is "OK". This is sometimes preferable to
 // status.ok() when we want to check the state of Status-like objects
 // that implicitly cast to Status.
-inline bool isOk(const Status status) {
+inline bool isOk(const Status& status) {
     return status.ok();
 }
 
 // Document that status is expected to be ok. This function may log
 // (or assert when running in debug mode) if status has an unexpected
 // value.
-void expectOk(const Status status);
+void expectOk(const Status& status);
 
 // Convert POSIX errno to a Status object.
 // If Status is extended to have more features, this mapping may
 // become more complex.
-//
-// TODO: msg is only a placeholder for now
 Status statusFromErrno(int err, const std::string& msg);
 
-std::string toString(const Status status);
+// Helper that checks Status-like object (notably StatusOr) against a
+// value in the errno space.
+bool equalToErrno(const Status& status, int err);
+
+// Helper that converts Status-like object (notably StatusOr) to a
+// message.
+std::string toString(const Status& status);
 
 std::ostream& operator<<(std::ostream& os, const Status& s);
 
diff --git a/libnetdutils/include/netdutils/Syscalls.h b/libnetdutils/include/netdutils/Syscalls.h
index 5d8ce68..48be5a2 100644
--- a/libnetdutils/include/netdutils/Syscalls.h
+++ b/libnetdutils/include/netdutils/Syscalls.h
@@ -35,6 +35,9 @@
   public:
     virtual ~Syscalls() = default;
 
+    virtual StatusOr<UniqueFd> open(const std::string& pathname, int flags,
+                                    mode_t mode = 0) const = 0;
+
     virtual StatusOr<UniqueFd> socket(int domain, int type, int protocol) const = 0;
 
     virtual Status getsockname(Fd sock, sockaddr* addr, socklen_t* addrlen) const = 0;
diff --git a/server/Android.mk b/server/Android.mk
index 5edbd20..c8ba20f 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -160,6 +160,7 @@
         system/core/logwrapper/include \
 
 LOCAL_SRC_FILES := \
+        InterfaceController.cpp InterfaceControllerTest.cpp \
         Controllers.cpp \
         NetdConstants.cpp IptablesBaseTest.cpp \
         IptablesRestoreController.cpp IptablesRestoreControllerTest.cpp \
@@ -182,6 +183,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_LIBRARIES := libgmock libpcap
 LOCAL_SHARED_LIBRARIES := \
+        libnetdaidl \
         libbase \
         libbinder \
         libcutils \
diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp
index cb1f0c2..a8b985a 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -23,18 +23,32 @@
 
 #define LOG_TAG "InterfaceController"
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <cutils/log.h>
 #include <logwrap/logwrap.h>
 #include <netutils/ifc.h>
 
+#include <android/net/INetd.h>
+#include <netdutils/Misc.h>
+#include <netdutils/Slice.h>
+#include <netdutils/Syscalls.h>
+
 #include "InterfaceController.h"
 #include "RouteController.h"
 
-using android::base::StringPrintf;
 using android::base::ReadFileToString;
+using android::base::StringPrintf;
 using android::base::WriteStringToFile;
+using android::net::INetd;
 using android::net::RouteController;
+using android::netdutils::Status;
+using android::netdutils::StatusOr;
+using android::netdutils::makeSlice;
+using android::netdutils::sSyscalls;
+using android::netdutils::status::ok;
+using android::netdutils::statusFromErrno;
+using android::netdutils::toString;
 
 namespace {
 
@@ -54,6 +68,21 @@
 // RFC 7421 prefix length.
 constexpr int kRouteInfoMaxPrefixLen = 64;
 
+// Property used to persist RFC 7217 stable secret. Protected by SELinux policy.
+const char kStableSecretProperty[] = "persist.netd.stable_secret";
+
+// RFC 7217 stable secret on linux is formatted as an IPv6 address.
+// This function uses 128 bits of high quality entropy to generate an
+// address for this purpose. This function should be not be called
+// frequently.
+StatusOr<std::string> randomIPv6Address() {
+    in6_addr addr = {};
+    const auto& sys = sSyscalls.get();
+    ASSIGN_OR_RETURN(auto fd, sys.open("/dev/random", O_RDONLY));
+    RETURN_IF_NOT_OK(sys.read(fd, makeSlice(addr)));
+    return toString(addr);
+}
+
 inline bool isNormalPathComponent(const char *component) {
     return (strcmp(component, ".") != 0) &&
            (strcmp(component, "..") != 0) &&
@@ -141,8 +170,62 @@
     forEachInterface(ipv6_proc_path, fn);
 }
 
+// Ideally this function would return StatusOr<std::string>, however
+// there is no safe value for dflt that will always differ from the
+// stored property. Bugs code could conceivably end up persisting the
+// reserved value resulting in surprising behavior.
+std::string getProperty(const std::string& key, const std::string& dflt) {
+    return android::base::GetProperty(key, dflt);
+};
+
+Status setProperty(const std::string& key, const std::string& val) {
+    // SetProperty tries to encode something useful in errno, however
+    // the value may get clobbered by async_safe_format_log() in
+    // __system_property_set(). Use with care.
+    return android::base::SetProperty(key, val)
+        ? ok
+        : statusFromErrno(errno, "SetProperty failed");
+};
+
+
 }  // namespace
 
+android::netdutils::Status InterfaceController::enableStablePrivacyAddresses(
+        const std::string& iface, GetPropertyFn getProperty, SetPropertyFn setProperty) {
+    const auto& sys = sSyscalls.get();
+    const std::string procTarget = std::string(ipv6_proc_path) + "/" + iface + "/stable_secret";
+    auto procFd = sys.open(procTarget, O_CLOEXEC | O_WRONLY);
+
+    // Devices with old kernels (typically < 4.4) don't support
+    // RFC 7217 stable privacy addresses.
+    if (equalToErrno(procFd, ENOENT)) {
+        return statusFromErrno(EOPNOTSUPP,
+                               "Failed to open stable_secret. Assuming unsupported kernel version");
+    }
+
+    // If stable_secret exists but we can't open it, something strange is going on.
+    RETURN_IF_NOT_OK(procFd);
+
+    const char kUninitialized[] = "uninitialized";
+    const auto oldSecret = getProperty(kStableSecretProperty, kUninitialized);
+    std::string secret = oldSecret;
+
+    // Generate a new secret if no persistent property existed.
+    if (oldSecret == kUninitialized) {
+        ASSIGN_OR_RETURN(secret, randomIPv6Address());
+    }
+
+    // Ask the OS to generate SLAAC addresses on iface using secret.
+    RETURN_IF_NOT_OK(sys.write(procFd.value(), makeSlice(secret)));
+
+    // Don't persist an existing secret.
+    if (oldSecret != kUninitialized) {
+        return ok;
+    }
+
+    return setProperty(kStableSecretProperty, secret);
+}
+
 void InterfaceController::initializeAll() {
     // Initial IPv6 settings.
     // By default, accept_ra is set to 1 (accept RAs unless forwarding is on) on all interfaces.
@@ -179,6 +262,31 @@
     return writeValueToPath(ipv6_proc_path, interface, "disable_ipv6", disable_ipv6);
 }
 
+// Changes to addrGenMode will not fully take effect until the next
+// time disable_ipv6 transitions from 1 to 0.
+Status InterfaceController::setIPv6AddrGenMode(const std::string& interface, int mode) {
+    if (!isIfaceName(interface)) {
+        return statusFromErrno(ENOENT, "invalid iface name: " + interface);
+    }
+
+    switch (mode) {
+        case INetd::IPV6_ADDR_GEN_MODE_EUI64:
+            // Ignore return value. If /proc/.../stable_secret is
+            // missing we're probably in EUI64 mode already.
+            writeValueToPath(ipv6_proc_path, interface.c_str(), "stable_secret", "");
+            break;
+        case INetd::IPV6_ADDR_GEN_MODE_STABLE_PRIVACY: {
+            return enableStablePrivacyAddresses(interface, getProperty, setProperty);
+        }
+        case INetd::IPV6_ADDR_GEN_MODE_NONE:
+        case INetd::IPV6_ADDR_GEN_MODE_RANDOM:
+        default:
+            return statusFromErrno(EOPNOTSUPP, "unsupported addrGenMode");
+    }
+
+    return ok;
+}
+
 int InterfaceController::setAcceptIPv6Ra(const char *interface, const int on) {
     if (!isIfaceName(interface)) {
         errno = ENOENT;
@@ -213,7 +321,7 @@
         return -1;
     }
     // 0: disable IPv6 privacy addresses
-    // 0: enable IPv6 privacy addresses and prefer them over non-privacy ones.
+    // 2: enable IPv6 privacy addresses and prefer them over non-privacy ones.
     return writeValueToPath(ipv6_proc_path, interface, "use_tempaddr", on ? "2" : "0");
 }
 
diff --git a/server/InterfaceController.h b/server/InterfaceController.h
index 80ebe5c..30898f2 100644
--- a/server/InterfaceController.h
+++ b/server/InterfaceController.h
@@ -17,13 +17,24 @@
 #ifndef _INTERFACE_CONTROLLER_H
 #define _INTERFACE_CONTROLLER_H
 
+#include <functional>
 #include <string>
 
+#include <netdutils/Status.h>
+
+// TODO: move InterfaceController into android::net namespace.
+namespace android {
+namespace net {
+class StablePrivacyTest;
+}  // namespace net
+}  // namespace android
+
 class InterfaceController {
 public:
     static void initializeAll();
 
     static int setEnableIPv6(const char *interface, const int on);
+    static android::netdutils::Status setIPv6AddrGenMode(const std::string& interface, int mode);
     static int setAcceptIPv6Ra(const char *interface, const int on);
     static int setAcceptIPv6Dad(const char *interface, const int on);
     static int setIPv6DadTransmits(const char *interface, const char *value);
@@ -43,13 +54,25 @@
             const char *value);
 
 private:
-    static void setAcceptRA(const char* value);
-    static void setAcceptRARouteTable(int tableOrOffset);
-    static void setBaseReachableTimeMs(unsigned int millis);
-    static void setIPv6OptimisticMode(const char *value);
+  friend class android::net::StablePrivacyTest;
 
-    InterfaceController() = delete;
-    ~InterfaceController() = delete;
+  using GetPropertyFn =
+      std::function<std::string(const std::string& key, const std::string& dflt)>;
+  using SetPropertyFn =
+      std::function<android::netdutils::Status(const std::string& key, const std::string& val)>;
+
+  // Helper function exported from this compilation unit for testing.
+  static android::netdutils::Status enableStablePrivacyAddresses(const std::string& iface,
+                                                                 GetPropertyFn getProperty,
+                                                                 SetPropertyFn setProperty);
+
+  static void setAcceptRA(const char* value);
+  static void setAcceptRARouteTable(int tableOrOffset);
+  static void setBaseReachableTimeMs(unsigned int millis);
+  static void setIPv6OptimisticMode(const char* value);
+
+  InterfaceController() = delete;
+  ~InterfaceController() = delete;
 };
 
 #endif
diff --git a/server/InterfaceControllerTest.cpp b/server/InterfaceControllerTest.cpp
new file mode 100644
index 0000000..014d05d
--- /dev/null
+++ b/server/InterfaceControllerTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <netdutils/MockSyscalls.h>
+
+#include "InterfaceController.h"
+
+using testing::ByMove;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace android {
+namespace net {
+namespace {
+
+using netdutils::Fd;
+using netdutils::ScopedMockSyscalls;
+using netdutils::Slice;
+using netdutils::Status;
+using netdutils::StatusOr;
+using netdutils::UniqueFd;
+using netdutils::makeSlice;
+using netdutils::status::ok;
+using netdutils::statusFromErrno;
+
+constexpr Fd kDevRandomFd(777);
+constexpr Fd kStableSecretFd(9999);
+const char kDevRandomPath[] = "/dev/random";
+const char kTestIface[] = "wlan5";
+const char kStableSecretProperty[] = "persist.netd.stable_secret";
+const char kStableSecretPath[] = "/proc/sys/net/ipv6/conf/wlan5/stable_secret";
+const char kTestIPv6Address[] = "\x20\x01\x0d\xb8\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10";
+const char kTestIPv6AddressString[] = "2001:db8:506:708:90a:b0c:d0e:f10";
+
+// getProperty() and setProperty() are forwarded to this mock
+class MockProperties {
+  public:
+    MOCK_CONST_METHOD2(get, std::string(const std::string& key, const std::string& dflt));
+    MOCK_CONST_METHOD2(set, Status(const std::string& key, const std::string& val));
+};
+
+}  // namespace
+
+class StablePrivacyTest : public testing::Test {
+  protected:
+    void expectOpenFile(const std::string& path, const Fd fd, int err) {
+        if (err == 0) {
+            EXPECT_CALL(mSyscalls, open(path, _, _)).WillOnce(Return(ByMove(UniqueFd(fd))));
+            EXPECT_CALL(mSyscalls, close(fd)).WillOnce(Return(ok));
+        } else {
+            EXPECT_CALL(mSyscalls, open(path, _, _))
+                .WillOnce(Return(ByMove(statusFromErrno(err, "open() failed"))));
+        }
+    }
+
+    void expectReadFromDevRandom(const std::string& data) {
+        expectOpenFile(kDevRandomPath, kDevRandomFd, 0);
+        EXPECT_CALL(mSyscalls, read(kDevRandomFd, _)).WillOnce(Invoke([data](Fd, const Slice buf) {
+            EXPECT_EQ(data.size(), buf.size());
+            return take(buf, copy(buf, makeSlice(data)));
+        }));
+    }
+
+    void expectGetPropertyDefault(const std::string& key) {
+        EXPECT_CALL(mProperties, get(key, _))
+            .WillOnce(Invoke([](const std::string&, const std::string& dflt) { return dflt; }));
+    }
+
+    void expectGetProperty(const std::string& key, const std::string& val) {
+        EXPECT_CALL(mProperties, get(key, _))
+            .WillOnce(Invoke([val](const std::string&, const std::string&) { return val; }));
+    }
+
+    void expectSetProperty(const std::string& key, const std::string& val, Status status) {
+        EXPECT_CALL(mProperties, set(key, val)).WillOnce(Return(status));
+    }
+
+    void expectWriteToFile(const Fd fd, const std::string& val, int err) {
+        EXPECT_CALL(mSyscalls, write(fd, _))
+            .WillOnce(Invoke([val, err](Fd, const Slice buf) -> StatusOr<size_t> {
+                EXPECT_EQ(val, toString(buf));
+                if (err) {
+                    return statusFromErrno(err, "write() failed");
+                }
+                return val.size();
+            }));
+    }
+
+    Status enableStablePrivacyAddresses(const std::string& iface) {
+        return InterfaceController::enableStablePrivacyAddresses(iface, mGet, mSet);
+    }
+
+    StrictMock<ScopedMockSyscalls> mSyscalls;
+    StrictMock<MockProperties> mProperties;
+
+    const std::function<std::string(const std::string&, const std::string&)> mGet =
+        [this](const std::string& key, const std::string& dflt) {
+            return mProperties.get(key, dflt);
+        };
+    const std::function<Status(const std::string&, const std::string&)> mSet =
+        [this](const std::string& key, const std::string& val) {
+            return mProperties.set(key, val);
+        };
+};
+
+TEST_F(StablePrivacyTest, PropertyOpenEnoent) {
+    expectOpenFile(kStableSecretPath, kStableSecretFd, ENOENT);
+    EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface));
+}
+
+TEST_F(StablePrivacyTest, PropertyOpenEaccess) {
+    expectOpenFile(kStableSecretPath, kStableSecretFd, EACCES);
+    EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface));
+}
+
+TEST_F(StablePrivacyTest, FirstBootWriteOkSetPropertyOk) {
+    expectOpenFile(kStableSecretPath, kStableSecretFd, 0);
+    expectGetPropertyDefault(kStableSecretProperty);
+    expectReadFromDevRandom(kTestIPv6Address);
+    expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, 0);
+    expectSetProperty(kStableSecretProperty, kTestIPv6AddressString, ok);
+    EXPECT_EQ(ok, enableStablePrivacyAddresses(kTestIface));
+}
+
+TEST_F(StablePrivacyTest, FirstBootWriteOkSetPropertyFail) {
+    const auto kError = statusFromErrno(EINVAL, "");
+    expectOpenFile(kStableSecretPath, kStableSecretFd, 0);
+    expectGetPropertyDefault(kStableSecretProperty);
+    expectReadFromDevRandom(kTestIPv6Address);
+    expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, 0);
+    expectSetProperty(kStableSecretProperty, kTestIPv6AddressString, kError);
+    EXPECT_EQ(kError, enableStablePrivacyAddresses(kTestIface));
+}
+
+TEST_F(StablePrivacyTest, FirstBootWriteFail) {
+    expectOpenFile(kStableSecretPath, kStableSecretFd, 0);
+    expectGetPropertyDefault(kStableSecretProperty);
+    expectReadFromDevRandom(kTestIPv6Address);
+    expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, ENOSPC);
+    EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface));
+}
+
+TEST_F(StablePrivacyTest, ExistingPropertyWriteOk) {
+    expectOpenFile(kStableSecretPath, kStableSecretFd, 0);
+    expectGetProperty(kStableSecretProperty, kTestIPv6AddressString);
+    expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, 0);
+    EXPECT_EQ(ok, enableStablePrivacyAddresses(kTestIface));
+}
+
+TEST_F(StablePrivacyTest, ExistingPropertyWriteFail) {
+    expectOpenFile(kStableSecretPath, kStableSecretFd, 0);
+    expectGetProperty(kStableSecretProperty, kTestIPv6AddressString);
+    expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, EACCES);
+    EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface));
+}
+
+}  // namespace net
+}  // namespace android
diff --git a/server/IptablesBaseTest.cpp b/server/IptablesBaseTest.cpp
index ce6008a..b5fd9a0 100644
--- a/server/IptablesBaseTest.cpp
+++ b/server/IptablesBaseTest.cpp
@@ -73,6 +73,7 @@
             cmd += arg;
         }
     } while (arg);
+    va_end(args);
 
     if (target == V4 || target == V4V6) {
         sCmds.push_back(IPTABLES_PATH + cmd);
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 7e555f5..67bf9c0 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -53,7 +53,7 @@
     if (isOk(s)) {
         return binder::Status::ok();
     }
-    return binder::Status::fromExceptionCode(s.code(), s.msg().c_str());
+    return binder::Status::fromServiceSpecificError(s.code(), s.msg().c_str());
 }
 
 binder::Status checkPermission(const char *permission) {
@@ -418,6 +418,12 @@
                     socket));
 }
 
+binder::Status NetdNativeService::setIPv6AddrGenMode(const std::string& ifName,
+                                                     int32_t mode) {
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    return toBinderStatus(InterfaceController::setIPv6AddrGenMode(ifName, mode));
+}
+
 binder::Status NetdNativeService::wakeupAddInterface(const std::string& ifName,
                                                      const std::string& prefix, int32_t mark,
                                                      int32_t mask) {
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index a95b483..407c563 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -48,6 +48,8 @@
             std::vector<std::string>* domains, std::vector<int32_t>* params,
             std::vector<int32_t>* stats) override;
 
+    binder::Status setIPv6AddrGenMode(const std::string& ifName, int32_t mode) override;
+
     // NFLOG-related commands
     binder::Status wakeupAddInterface(const std::string& ifName, const std::string& prefix,
                                       int32_t mark, int32_t mask) override;
diff --git a/server/NetlinkManager.cpp b/server/NetlinkManager.cpp
index 7a7627a..698de9f 100644
--- a/server/NetlinkManager.cpp
+++ b/server/NetlinkManager.cpp
@@ -77,7 +77,8 @@
 
     memset(&nladdr, 0, sizeof(nladdr));
     nladdr.nl_family = AF_NETLINK;
-    nladdr.nl_pid = getpid();
+    // Kernel will assign a unique nl_pid if set to zero.
+    nladdr.nl_pid = 0;
     nladdr.nl_groups = groups;
 
     if ((*sock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, netlinkFamily)) < 0) {
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index f1552b4..e5bf218 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -312,4 +312,17 @@
     * @param prefix arbitrary string used to identify wakeup sources in onWakeupEvent
     */
     void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+
+    const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+    const int IPV6_ADDR_GEN_MODE_NONE = 1;
+    const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+    const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+
+    const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+   /**
+    * Set IPv6 address generation mode. IPv6 should be disabled before changing mode.
+    *
+    * @param mode SLAAC address generation mechanism to use
+    */
+    void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
 }
diff --git a/server/main.cpp b/server/main.cpp
index 50570fd..27596f7 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -83,19 +83,8 @@
         exit(1);
     };
 
-    std::unique_ptr<NFLogListener> logListener;
-    {
-        auto result = makeNFLogListener();
-        if (!isOk(result)) {
-            ALOGE("Unable to create NFLogListener: %s", result.status().msg().c_str());
-            exit(1);
-        }
-        logListener = std::move(result.value());
-    }
-
     gCtls = new android::net::Controllers();
     gCtls->init();
-    gCtls->wakeupCtrl.init(logListener.get());
 
     CommandListener cl;
     nm->setBroadcaster((SocketListener *) &cl);
@@ -105,6 +94,21 @@
         exit(1);
     }
 
+    std::unique_ptr<NFLogListener> logListener;
+    {
+        auto result = makeNFLogListener();
+        if (!isOk(result)) {
+            ALOGE("Unable to create NFLogListener: %s", toString(result).c_str());
+            exit(1);
+        }
+        logListener = std::move(result.value());
+        auto status = gCtls->wakeupCtrl.init(logListener.get());
+        if (!isOk(result)) {
+            ALOGE("Unable to init WakeupController: %s", toString(result).c_str());
+            // We can still continue without wakeup packet logging.
+        }
+    }
+
     // Set local DNS mode, to prevent bionic from proxying
     // back to this service, recursively.
     setenv("ANDROID_DNS_MODE", "local", 1);