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);