Home | History | Annotate | Download | only in server
      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 <cutils/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 
     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 
     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 
    116 WakeupController::~WakeupController() {
    117     expectOk(mListener->unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP));
    118 }
    119 
    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 
    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 
    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 
    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