WakeupController.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. #define LOG_TAG "WakeupController"
  17. #include <arpa/inet.h>
  18. #include <iostream>
  19. #include <linux/netfilter/nfnetlink.h>
  20. #include <linux/netfilter/nfnetlink_log.h>
  21. #include <sys/socket.h>
  22. #include <netinet/if_ether.h>
  23. #include <netinet/in.h>
  24. #include <netinet/ip.h>
  25. #include <netinet/ip6.h>
  26. #include <netinet/tcp.h>
  27. #include <netinet/udp.h>
  28. #include <android-base/strings.h>
  29. #include <android-base/stringprintf.h>
  30. #include <log/log.h>
  31. #include <netdutils/Netfilter.h>
  32. #include <netdutils/Netlink.h>
  33. #include "IptablesRestoreController.h"
  34. #include "NetlinkManager.h"
  35. #include "WakeupController.h"
  36. namespace android {
  37. namespace net {
  38. using base::StringPrintf;
  39. using netdutils::Slice;
  40. using netdutils::Status;
  41. const char WakeupController::LOCAL_MANGLE_INPUT[] = "wakeupctrl_mangle_INPUT";
  42. const uint32_t WakeupController::kDefaultPacketCopyRange =
  43. sizeof(struct tcphdr) + sizeof(struct ip6_hdr);
  44. static void extractIpPorts(WakeupController::ReportArgs& args, Slice payload) {
  45. switch (args.ipNextHeader) {
  46. case IPPROTO_TCP: {
  47. struct tcphdr header;
  48. if (extract(payload, header) < sizeof(struct tcphdr)) {
  49. return;
  50. }
  51. args.srcPort = ntohs(header.th_sport);
  52. args.dstPort = ntohs(header.th_dport);
  53. break;
  54. }
  55. case IPPROTO_UDP: {
  56. struct udphdr header;
  57. if (extract(payload, header) < sizeof(struct udphdr)) {
  58. return;
  59. }
  60. args.srcPort = ntohs(header.uh_sport);
  61. args.dstPort = ntohs(header.uh_dport);
  62. break;
  63. }
  64. default:
  65. break;
  66. }
  67. }
  68. static void extractIpHeader(WakeupController::ReportArgs& args, Slice payload) {
  69. switch (args.ethertype) {
  70. case ETH_P_IP: {
  71. struct iphdr header;
  72. if (extract(payload, header) < sizeof(struct iphdr)) {
  73. return;
  74. }
  75. args.ipNextHeader = header.protocol;
  76. char addr[INET_ADDRSTRLEN] = {};
  77. inet_ntop(AF_INET, &header.saddr, addr, sizeof(addr));
  78. args.srcIp = addr;
  79. inet_ntop(AF_INET, &header.daddr, addr, sizeof(addr));
  80. args.dstIp = addr;
  81. extractIpPorts(args, drop(payload, header.ihl * 4)); // ipv4 IHL counts 32 bit words.
  82. break;
  83. }
  84. case ETH_P_IPV6: {
  85. struct ip6_hdr header;
  86. if (extract(payload, header) < sizeof(struct ip6_hdr)) {
  87. return;
  88. }
  89. args.ipNextHeader = header.ip6_nxt;
  90. char addr[INET6_ADDRSTRLEN] = {};
  91. inet_ntop(AF_INET6, &header.ip6_src, addr, sizeof(addr));
  92. args.srcIp = addr;
  93. inet_ntop(AF_INET6, &header.ip6_dst, addr, sizeof(addr));
  94. args.dstIp = addr;
  95. // TODO: also deal with extension headers
  96. if (args.ipNextHeader == IPPROTO_TCP || args.ipNextHeader == IPPROTO_UDP) {
  97. extractIpPorts(args, drop(payload, sizeof(header)));
  98. }
  99. break;
  100. }
  101. default:
  102. break;
  103. }
  104. }
  105. WakeupController::~WakeupController() {
  106. expectOk(mListener->unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP));
  107. }
  108. netdutils::Status WakeupController::init(NFLogListenerInterface* listener) {
  109. mListener = listener;
  110. const auto msgHandler = [this](const nlmsghdr&, const nfgenmsg&, const Slice msg) {
  111. struct WakeupController::ReportArgs args = {
  112. .uid = -1,
  113. .gid = -1,
  114. .ethertype = -1,
  115. .ipNextHeader = -1,
  116. .srcPort = -1,
  117. .dstPort = -1,
  118. // and all other fields set to 0 as the default
  119. };
  120. bool parseAgain = false;
  121. const auto attrHandler = [&args, &parseAgain](const nlattr attr, const Slice payload) {
  122. switch (attr.nla_type) {
  123. case NFULA_TIMESTAMP: {
  124. timespec ts = {};
  125. extract(payload, ts);
  126. constexpr uint64_t kNsPerS = 1000000000ULL;
  127. args.timestampNs = ntohl(ts.tv_nsec) + (ntohl(ts.tv_sec) * kNsPerS);
  128. break;
  129. }
  130. case NFULA_PREFIX:
  131. // Strip trailing '\0'
  132. args.prefix = toString(take(payload, payload.size() - 1));
  133. break;
  134. case NFULA_UID:
  135. extract(payload, args.uid);
  136. args.uid = ntohl(args.uid);
  137. break;
  138. case NFULA_GID:
  139. extract(payload, args.gid);
  140. args.gid = ntohl(args.gid);
  141. break;
  142. case NFULA_HWADDR: {
  143. struct nfulnl_msg_packet_hw hwaddr = {};
  144. extract(payload, hwaddr);
  145. size_t hwAddrLen = ntohs(hwaddr.hw_addrlen);
  146. hwAddrLen = std::min(hwAddrLen, sizeof(hwaddr.hw_addr));
  147. args.dstHw.assign(hwaddr.hw_addr, hwaddr.hw_addr + hwAddrLen);
  148. break;
  149. }
  150. case NFULA_PACKET_HDR: {
  151. struct nfulnl_msg_packet_hdr packetHdr = {};
  152. extract(payload, packetHdr);
  153. args.ethertype = ntohs(packetHdr.hw_protocol);
  154. break;
  155. }
  156. case NFULA_PAYLOAD:
  157. // The packet payload is expected to come last in the Netlink message.
  158. // At that point NFULA_PACKET_HDR has already been parsed and processed.
  159. // If this is not the case, set parseAgain to true.
  160. parseAgain = (args.ethertype == -1);
  161. extractIpHeader(args, payload);
  162. break;
  163. default:
  164. break;
  165. }
  166. };
  167. forEachNetlinkAttribute(msg, attrHandler);
  168. if (parseAgain) {
  169. // NFULA_PAYLOAD was parsed before NFULA_PACKET_HDR.
  170. // Now that the ethertype is known, reparse msg for correctly extracting the payload.
  171. forEachNetlinkAttribute(msg, attrHandler);
  172. }
  173. mReport(args);
  174. };
  175. return mListener->subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP,
  176. WakeupController::kDefaultPacketCopyRange, msgHandler);
  177. }
  178. Status WakeupController::addInterface(const std::string& ifName, const std::string& prefix,
  179. uint32_t mark, uint32_t mask) {
  180. return execIptables("-A", ifName, prefix, mark, mask);
  181. }
  182. Status WakeupController::delInterface(const std::string& ifName, const std::string& prefix,
  183. uint32_t mark, uint32_t mask) {
  184. return execIptables("-D", ifName, prefix, mark, mask);
  185. }
  186. Status WakeupController::execIptables(const std::string& action, const std::string& ifName,
  187. const std::string& prefix, uint32_t mark, uint32_t mask) {
  188. // NFLOG messages to batch before releasing to userspace
  189. constexpr int kBatch = 8;
  190. // Max log message rate in packets/second
  191. constexpr int kRateLimit = 10;
  192. const char kFormat[] =
  193. "*mangle\n%s %s -i %s -j NFLOG --nflog-prefix %s --nflog-group %d --nflog-threshold %d"
  194. " -m mark --mark 0x%08x/0x%08x -m limit --limit %d/s\nCOMMIT\n";
  195. const auto cmd = StringPrintf(
  196. kFormat, action.c_str(), WakeupController::LOCAL_MANGLE_INPUT, ifName.c_str(),
  197. prefix.c_str(), NetlinkManager::NFLOG_WAKEUP_GROUP, kBatch, mark, mask, kRateLimit);
  198. std::string out;
  199. auto rv = mIptables->execute(V4V6, cmd, &out);
  200. if (rv != 0) {
  201. auto s = Status(rv, "Failed to execute iptables cmd: " + cmd + ", out: " + out);
  202. ALOGE("%s", toString(s).c_str());
  203. return s;
  204. }
  205. return netdutils::status::ok;
  206. }
  207. } // namespace net
  208. } // namespace android