Add WakeupController and NFLogListener
These classes work together to parse and dispatch NFLOG messages in
response to inbound packets annotated by the WiFi driver.
Test: as follows
- built
- flashed
- booted
- netd_unit_test passes
Change-Id: Id26d62858bf4bc4186ae66850f08077adf6fc2ac
diff --git a/server/WakeupControllerTest.cpp b/server/WakeupControllerTest.cpp
new file mode 100644
index 0000000..05e899c
--- /dev/null
+++ b/server/WakeupControllerTest.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 <linux/netfilter/nfnetlink_log.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "NetlinkManager.h"
+#include "WakeupController.h"
+
+using ::testing::StrictMock;
+using ::testing::Test;
+using ::testing::DoAll;
+using ::testing::SaveArg;
+using ::testing::Return;
+using ::testing::_;
+
+namespace android {
+namespace net {
+
+using netdutils::status::ok;
+
+class MockNetdEventListener {
+ public:
+ MOCK_METHOD4(onWakeupEvent,
+ void(const std::string& prefix, uid_t uid, gid_t gid, uint64_t timestampNs));
+};
+
+class MockIptablesRestore : public IptablesRestoreInterface {
+ public:
+ ~MockIptablesRestore() override = default;
+ MOCK_METHOD3(execute, int(const IptablesTarget target, const std::string& commands,
+ std::string* output));
+};
+
+class MockNFLogListener : public NFLogListenerInterface {
+ public:
+ ~MockNFLogListener() override = default;
+ MOCK_METHOD2(subscribe, netdutils::Status(uint16_t nfLogGroup, const DispatchFn& fn));
+ MOCK_METHOD1(unsubscribe, netdutils::Status(uint16_t nfLogGroup));
+};
+
+class WakeupControllerTest : public Test {
+ protected:
+ WakeupControllerTest() {
+ EXPECT_CALL(mListener, subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP, _))
+ .WillOnce(DoAll(SaveArg<1>(&mMessageHandler), Return(ok)));
+ EXPECT_CALL(mListener, unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP)).WillOnce(Return(ok));
+ mController.init(&mListener);
+ }
+
+ StrictMock<MockNetdEventListener> mEventListener;
+ StrictMock<MockIptablesRestore> mIptables;
+ StrictMock<MockNFLogListener> mListener;
+ WakeupController mController{
+ [this](const std::string& prefix, uid_t uid, gid_t gid, uint64_t timestampNs) {
+ mEventListener.onWakeupEvent(prefix, uid, gid, timestampNs);
+ },
+ &mIptables};
+ NFLogListenerInterface::DispatchFn mMessageHandler;
+};
+
+TEST_F(WakeupControllerTest, msgHandler) {
+ const char kPrefix[] = "test:prefix";
+ const uid_t kUid = 8734;
+ const gid_t kGid = 2222;
+ const uint64_t kNsPerS = 1000000000ULL;
+ const uint64_t kTsNs = 9999 + (34 * kNsPerS);
+
+ struct Msg {
+ nlmsghdr nlmsg;
+ nfgenmsg nfmsg;
+ nlattr uidAttr;
+ uid_t uid;
+ nlattr gidAttr;
+ gid_t gid;
+ nlattr tsAttr;
+ timespec ts;
+ nlattr prefixAttr;
+ char prefix[sizeof(kPrefix)];
+ } msg = {};
+
+ msg.uidAttr.nla_type = NFULA_UID;
+ msg.uidAttr.nla_len = sizeof(msg.uidAttr) + sizeof(msg.uid);
+ msg.uid = htobe32(kUid);
+
+ msg.gidAttr.nla_type = NFULA_GID;
+ msg.gidAttr.nla_len = sizeof(msg.gidAttr) + sizeof(msg.gid);
+ msg.gid = htobe32(kGid);
+
+ msg.tsAttr.nla_type = NFULA_TIMESTAMP;
+ msg.tsAttr.nla_len = sizeof(msg.tsAttr) + sizeof(msg.ts);
+ msg.ts.tv_sec = htobe32(kTsNs / kNsPerS);
+ msg.ts.tv_nsec = htobe32(kTsNs % kNsPerS);
+
+ msg.prefixAttr.nla_type = NFULA_PREFIX;
+ msg.prefixAttr.nla_len = sizeof(msg.prefixAttr) + sizeof(msg.prefix);
+ memcpy(msg.prefix, kPrefix, sizeof(kPrefix));
+
+ auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, uidAttr));
+ EXPECT_CALL(mEventListener, onWakeupEvent(kPrefix, kUid, kGid, kTsNs));
+ mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
+}
+
+TEST_F(WakeupControllerTest, badAttr) {
+ const char kPrefix[] = "test:prefix";
+ const uid_t kUid = 8734;
+ const gid_t kGid = 2222;
+ const uint64_t kNsPerS = 1000000000ULL;
+ const uint64_t kTsNs = 9999 + (34 * kNsPerS);
+
+ struct Msg {
+ nlmsghdr nlmsg;
+ nfgenmsg nfmsg;
+ nlattr uidAttr;
+ uid_t uid;
+ nlattr invalid0;
+ nlattr invalid1;
+ nlattr gidAttr;
+ gid_t gid;
+ nlattr tsAttr;
+ timespec ts;
+ nlattr prefixAttr;
+ char prefix[sizeof(kPrefix)];
+ } msg = {};
+
+ msg.uidAttr.nla_type = 999;
+ msg.uidAttr.nla_len = sizeof(msg.uidAttr) + sizeof(msg.uid);
+ msg.uid = htobe32(kUid);
+
+ msg.invalid0.nla_type = 0;
+ msg.invalid0.nla_len = 0;
+ msg.invalid1.nla_type = 0;
+ msg.invalid1.nla_len = 1;
+
+ msg.gidAttr.nla_type = NFULA_GID;
+ msg.gidAttr.nla_len = sizeof(msg.gidAttr) + sizeof(msg.gid);
+ msg.gid = htobe32(kGid);
+
+ msg.tsAttr.nla_type = NFULA_TIMESTAMP;
+ msg.tsAttr.nla_len = sizeof(msg.tsAttr) - 2;
+ msg.ts.tv_sec = htobe32(kTsNs / kNsPerS);
+ msg.ts.tv_nsec = htobe32(kTsNs % kNsPerS);
+
+ msg.prefixAttr.nla_type = NFULA_UID;
+ msg.prefixAttr.nla_len = sizeof(msg.prefixAttr) + sizeof(msg.prefix);
+ memcpy(msg.prefix, kPrefix, sizeof(kPrefix));
+
+ auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, uidAttr));
+ EXPECT_CALL(mEventListener, onWakeupEvent("", 1952805748, kGid, 0));
+ mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
+}
+
+TEST_F(WakeupControllerTest, unterminatedString) {
+ char ones[20] = {};
+ memset(ones, 1, sizeof(ones));
+
+ struct Msg {
+ nlmsghdr nlmsg;
+ nfgenmsg nfmsg;
+ nlattr prefixAttr;
+ char prefix[sizeof(ones)];
+ } msg = {};
+
+ msg.prefixAttr.nla_type = NFULA_PREFIX;
+ msg.prefixAttr.nla_len = sizeof(msg.prefixAttr) + sizeof(msg.prefix);
+ memcpy(msg.prefix, ones, sizeof(ones));
+
+ const auto expected = std::string(ones, sizeof(ones) - 1);
+ auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, prefixAttr));
+ EXPECT_CALL(mEventListener, onWakeupEvent(expected, -1, -1, -1));
+ mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
+}
+
+TEST_F(WakeupControllerTest, addInterface) {
+ const char kPrefix[] = "test:prefix";
+ const char kIfName[] = "wlan8";
+ const uint32_t kMark = 0x12345678;
+ const uint32_t kMask = 0x0F0F0F0F;
+ const char kExpected[] =
+ "*mangle\n-A wakeupctrl_mangle_INPUT -i test:prefix"
+ " -j NFLOG --nflog-prefix wlan8 --nflog-group 3 --nflog-threshold 8"
+ " -m mark --mark 0x12345678/0x0f0f0f0f -m limit --limit 10/s\nCOMMIT\n";
+ EXPECT_CALL(mIptables, execute(V4V6, kExpected, _)).WillOnce(Return(0));
+ mController.addInterface(kPrefix, kIfName, kMark, kMask);
+}
+
+TEST_F(WakeupControllerTest, delInterface) {
+ const char kPrefix[] = "test:prefix";
+ const char kIfName[] = "wlan8";
+ const uint32_t kMark = 0x12345678;
+ const uint32_t kMask = 0xF0F0F0F0;
+ const char kExpected[] =
+ "*mangle\n-D wakeupctrl_mangle_INPUT -i test:prefix"
+ " -j NFLOG --nflog-prefix wlan8 --nflog-group 3 --nflog-threshold 8"
+ " -m mark --mark 0x12345678/0xf0f0f0f0 -m limit --limit 10/s\nCOMMIT\n";
+ EXPECT_CALL(mIptables, execute(V4V6, kExpected, _)).WillOnce(Return(0));
+ mController.delInterface(kPrefix, kIfName, kMark, kMask);
+}
+
+} // namespace net
+} // namespace android