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