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