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