Joel Scherpelz | 08b84cd | 2017-05-22 13:11:54 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #define LOG_TAG "WakeupController" |
| 18 | |
| 19 | #include <endian.h> |
| 20 | #include <linux/netfilter/nfnetlink.h> |
| 21 | #include <linux/netfilter/nfnetlink_log.h> |
| 22 | #include <iostream> |
| 23 | |
| 24 | #include <android-base/stringprintf.h> |
| 25 | #include <cutils/log.h> |
| 26 | #include <netdutils/Netfilter.h> |
| 27 | #include <netdutils/Netlink.h> |
| 28 | |
| 29 | #include "IptablesRestoreController.h" |
| 30 | #include "NetlinkManager.h" |
| 31 | #include "WakeupController.h" |
| 32 | |
| 33 | namespace android { |
| 34 | namespace net { |
| 35 | |
| 36 | using base::StringPrintf; |
| 37 | using netdutils::Slice; |
| 38 | using netdutils::Status; |
| 39 | |
| 40 | const char WakeupController::LOCAL_MANGLE_INPUT[] = "wakeupctrl_mangle_INPUT"; |
| 41 | |
| 42 | WakeupController::~WakeupController() { |
| 43 | expectOk(mListener->unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP)); |
| 44 | } |
| 45 | |
| 46 | netdutils::Status WakeupController::init(NFLogListenerInterface* listener) { |
| 47 | mListener = listener; |
| 48 | const auto msgHandler = [this](const nlmsghdr&, const nfgenmsg&, const Slice msg) { |
| 49 | std::string prefix; |
| 50 | uid_t uid = -1; |
| 51 | gid_t gid = -1; |
| 52 | uint64_t timestampNs = -1; |
| 53 | const auto attrHandler = [&prefix, &uid, &gid, ×tampNs](const nlattr attr, |
| 54 | const Slice payload) { |
| 55 | switch (attr.nla_type) { |
| 56 | case NFULA_TIMESTAMP: { |
| 57 | timespec timespec = {}; |
| 58 | extract(payload, timespec); |
| 59 | constexpr uint64_t kNsPerS = 1000000000ULL; |
| 60 | timestampNs = be32toh(timespec.tv_nsec) + (be32toh(timespec.tv_sec) * kNsPerS); |
| 61 | break; |
| 62 | } |
| 63 | case NFULA_PREFIX: |
| 64 | // Strip trailing '\0' |
| 65 | prefix = toString(take(payload, payload.size() - 1)); |
| 66 | break; |
| 67 | case NFULA_UID: |
| 68 | extract(payload, uid); |
| 69 | uid = be32toh(uid); |
| 70 | break; |
| 71 | case NFULA_GID: |
| 72 | extract(payload, gid); |
| 73 | gid = be32toh(gid); |
| 74 | break; |
| 75 | default: |
| 76 | break; |
| 77 | } |
| 78 | }; |
| 79 | forEachNetlinkAttribute(msg, attrHandler); |
| 80 | mReport(prefix, uid, gid, timestampNs); |
| 81 | }; |
| 82 | return mListener->subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP, msgHandler); |
| 83 | } |
| 84 | |
| 85 | Status WakeupController::addInterface(const std::string& ifName, const std::string& prefix, |
| 86 | uint32_t mark, uint32_t mask) { |
| 87 | return execIptables("-A", ifName, prefix, mark, mask); |
| 88 | } |
| 89 | |
| 90 | Status WakeupController::delInterface(const std::string& ifName, const std::string& prefix, |
| 91 | uint32_t mark, uint32_t mask) { |
| 92 | return execIptables("-D", ifName, prefix, mark, mask); |
| 93 | } |
| 94 | |
| 95 | Status WakeupController::execIptables(const std::string& action, const std::string& ifName, |
| 96 | const std::string& prefix, uint32_t mark, uint32_t mask) { |
| 97 | // NFLOG messages to batch before releasing to userspace |
| 98 | constexpr int kBatch = 8; |
| 99 | // Max log message rate in packets/second |
| 100 | constexpr int kRateLimit = 10; |
| 101 | const char kFormat[] = |
| 102 | "*mangle\n%s %s -i %s -j NFLOG --nflog-prefix %s --nflog-group %d --nflog-threshold %d" |
| 103 | " -m mark --mark 0x%08x/0x%08x -m limit --limit %d/s\nCOMMIT\n"; |
| 104 | const auto cmd = StringPrintf( |
| 105 | kFormat, action.c_str(), WakeupController::LOCAL_MANGLE_INPUT, ifName.c_str(), |
| 106 | prefix.c_str(), NetlinkManager::NFLOG_WAKEUP_GROUP, kBatch, mark, mask, kRateLimit); |
| 107 | |
| 108 | std::string out; |
| 109 | auto rv = mIptables->execute(V4V6, cmd, &out); |
| 110 | if (rv != 0) { |
| 111 | auto s = Status(rv, "Failed to execute iptables cmd: " + cmd + ", out: " + out); |
| 112 | ALOGE("%s", toString(s).c_str()); |
| 113 | return s; |
| 114 | } |
| 115 | return netdutils::status::ok; |
| 116 | } |
| 117 | |
| 118 | } // namespace net |
| 119 | } // namespace android |