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