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 "NFLogListener"
     18 
     19 #include <sstream>
     20 #include <vector>
     21 
     22 #include <arpa/inet.h>
     23 #include <linux/netfilter/nfnetlink_log.h>
     24 
     25 #include <cutils/log.h>
     26 #include <netdutils/Misc.h>
     27 #include <netdutils/Netfilter.h>
     28 #include <netdutils/Syscalls.h>
     29 
     30 #include "NFLogListener.h"
     31 
     32 namespace android {
     33 namespace net {
     34 
     35 using netdutils::Slice;
     36 using netdutils::Status;
     37 using netdutils::StatusOr;
     38 using netdutils::UniqueFd;
     39 using netdutils::Status;
     40 using netdutils::makeSlice;
     41 using netdutils::sSyscalls;
     42 using netdutils::findWithDefault;
     43 using netdutils::status::ok;
     44 using netdutils::extract;
     45 
     46 constexpr int kNFLogConfigMsgType = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
     47 constexpr int kNFLogPacketMsgType = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET;
     48 constexpr int kNetlinkDoneMsgType = (NFNL_SUBSYS_NONE << 8) | NLMSG_DONE;
     49 constexpr size_t kDefaultPacketRange = 0;
     50 
     51 namespace {
     52 
     53 const NFLogListener::DispatchFn kDefaultDispatchFn = [](const nlmsghdr& nlmsg,
     54                                                         const nfgenmsg& nfmsg, const Slice msg) {
     55     std::stringstream ss;
     56     ss << nlmsg << " " << nfmsg << " " << msg << " " << netdutils::toHex(msg, 32);
     57     ALOGE("unhandled nflog message: %s", ss.str().c_str());
     58 };
     59 
     60 using SendFn = std::function<Status(const Slice msg)>;
     61 
     62 // Required incantation?
     63 Status cfgCmdPfUnbind(const SendFn& send) {
     64     struct {
     65         nlmsghdr nlhdr;
     66         nfgenmsg nfhdr;
     67         nfattr attr;
     68         nfulnl_msg_config_cmd cmd;
     69     } __attribute__((packed)) msg = {};
     70 
     71     msg.nlhdr.nlmsg_len = sizeof(msg);
     72     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
     73     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
     74     msg.nfhdr.nfgen_family = AF_UNSPEC;
     75     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.cmd);
     76     msg.attr.nfa_type = NFULA_CFG_CMD;
     77     msg.cmd.command = NFULNL_CFG_CMD_PF_UNBIND;
     78     return send(makeSlice(msg));
     79 }
     80 
     81 // Control delivery mode for NFLOG messages marked with nfLogGroup.
     82 // range controls maximum bytes to copy
     83 // mode must be one of: NFULNL_COPY_NONE, NFULNL_COPY_META, NFULNL_COPY_PACKET
     84 Status cfgMode(const SendFn& send, uint16_t nfLogGroup, uint32_t range, uint8_t mode) {
     85     struct {
     86         nlmsghdr nlhdr;
     87         nfgenmsg nfhdr;
     88         nfattr attr;
     89         nfulnl_msg_config_mode mode;
     90     } __attribute__((packed)) msg = {};
     91 
     92     msg.nlhdr.nlmsg_len = sizeof(msg);
     93     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
     94     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
     95     msg.nfhdr.nfgen_family = AF_UNSPEC;
     96     msg.nfhdr.res_id = htons(nfLogGroup);
     97     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.mode);
     98     msg.attr.nfa_type = NFULA_CFG_MODE;
     99     msg.mode.copy_mode = mode;
    100     msg.mode.copy_range = htonl(range);
    101     return send(makeSlice(msg));
    102 }
    103 
    104 // Request that NFLOG messages marked with nfLogGroup are delivered to this socket
    105 Status cfgCmdBind(const SendFn& send, uint16_t nfLogGroup) {
    106     struct {
    107         nlmsghdr nlhdr;
    108         nfgenmsg nfhdr;
    109         nfattr attr;
    110         nfulnl_msg_config_cmd cmd;
    111     } __attribute__((packed)) msg = {};
    112 
    113     msg.nlhdr.nlmsg_len = sizeof(msg);
    114     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
    115     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
    116     msg.nfhdr.nfgen_family = AF_UNSPEC;
    117     msg.nfhdr.res_id = htons(nfLogGroup);
    118     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.cmd);
    119     msg.attr.nfa_type = NFULA_CFG_CMD;
    120     msg.cmd.command = NFULNL_CFG_CMD_BIND;
    121     return send(makeSlice(msg));
    122 }
    123 
    124 // Request that NFLOG messages marked with nfLogGroup are not delivered to this socket
    125 Status cfgCmdUnbind(const SendFn& send, uint16_t nfLogGroup) {
    126     struct {
    127         nlmsghdr nlhdr;
    128         nfgenmsg nfhdr;
    129         nfattr attr;
    130         nfulnl_msg_config_cmd cmd;
    131     } __attribute__((packed)) msg = {};
    132 
    133     msg.nlhdr.nlmsg_len = sizeof(msg);
    134     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
    135     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
    136     msg.nfhdr.nfgen_family = AF_UNSPEC;
    137     msg.nfhdr.res_id = htons(nfLogGroup);
    138     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.cmd);
    139     msg.attr.nfa_type = NFULA_CFG_CMD;
    140     msg.cmd.command = NFULNL_CFG_CMD_UNBIND;
    141     return send(makeSlice(msg));
    142 }
    143 
    144 }  // namespace
    145 
    146 NFLogListener::NFLogListener(std::shared_ptr<NetlinkListenerInterface> listener)
    147     : mListener(std::move(listener)) {
    148     // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function.
    149     const auto rxHandler = [this](const nlmsghdr& nlmsg, const Slice msg) {
    150         nfgenmsg nfmsg = {};
    151         extract(msg, nfmsg);
    152         std::lock_guard<std::mutex> guard(mMutex);
    153         const auto& fn = findWithDefault(mDispatchMap, ntohs(nfmsg.res_id), kDefaultDispatchFn);
    154         fn(nlmsg, nfmsg, drop(msg, sizeof(nfmsg)));
    155     };
    156     expectOk(mListener->subscribe(kNFLogPacketMsgType, rxHandler));
    157 
    158     // Each batch of NFLOG messages is terminated with NLMSG_DONE which is useless to us
    159     const auto rxDoneHandler = [](const nlmsghdr&, const Slice msg) {
    160         // Ignore NLMSG_DONE  messages
    161         nfgenmsg nfmsg = {};
    162         extract(msg, nfmsg);
    163         // TODO: why is nfmsg filled with garbage?
    164     };
    165     expectOk(mListener->subscribe(kNetlinkDoneMsgType, rxDoneHandler));
    166 }
    167 
    168 NFLogListener::~NFLogListener() {
    169     expectOk(mListener->unsubscribe(kNFLogPacketMsgType));
    170     expectOk(mListener->unsubscribe(kNetlinkDoneMsgType));
    171     const auto sendFn = [this](const Slice msg) { return mListener->send(msg); };
    172     for (auto pair : mDispatchMap) {
    173         expectOk(cfgCmdUnbind(sendFn, pair.first));
    174     }
    175 }
    176 
    177 Status NFLogListener::subscribe(uint16_t nfLogGroup, const DispatchFn& fn) {
    178     return subscribe(nfLogGroup, kDefaultPacketRange, fn);
    179 }
    180 
    181 Status NFLogListener::subscribe(
    182         uint16_t nfLogGroup, uint32_t copyRange, const DispatchFn& fn) {
    183     const auto sendFn = [this](const Slice msg) { return mListener->send(msg); };
    184     // Install fn into the dispatch map BEFORE requesting delivery of messages
    185     {
    186         std::lock_guard<std::mutex> guard(mMutex);
    187         mDispatchMap[nfLogGroup] = fn;
    188     }
    189     RETURN_IF_NOT_OK(cfgCmdBind(sendFn, nfLogGroup));
    190 
    191     // Mode must be set for every nfLogGroup
    192     const uint8_t copyMode = copyRange > 0 ? NFULNL_COPY_PACKET : NFULNL_COPY_NONE;
    193     return cfgMode(sendFn, nfLogGroup, copyRange, copyMode);
    194 }
    195 
    196 Status NFLogListener::unsubscribe(uint16_t nfLogGroup) {
    197     const auto sendFn = [this](const Slice msg) { return mListener->send(msg); };
    198     RETURN_IF_NOT_OK(cfgCmdUnbind(sendFn, nfLogGroup));
    199     // Remove from the dispatch map AFTER stopping message delivery.
    200     {
    201         std::lock_guard<std::mutex> guard(mMutex);
    202         mDispatchMap.erase(nfLogGroup);
    203     }
    204     return ok;
    205 }
    206 
    207 StatusOr<std::unique_ptr<NFLogListener>> makeNFLogListener() {
    208     const auto& sys = sSyscalls.get();
    209     ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC));
    210     const auto domain = AF_NETLINK;
    211     const auto flags = SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
    212     const auto protocol = NETLINK_NETFILTER;
    213     ASSIGN_OR_RETURN(auto sock, sys.socket(domain, flags, protocol));
    214 
    215     // Timestamps are disabled by default. Request RX timestamping
    216     RETURN_IF_NOT_OK(sys.setsockopt<int32_t>(sock, SOL_SOCKET, SO_TIMESTAMP, 1));
    217 
    218     std::shared_ptr<NetlinkListenerInterface> listener =
    219         std::make_unique<NetlinkListener>(std::move(event), std::move(sock));
    220     const auto sendFn = [&listener](const Slice msg) { return listener->send(msg); };
    221     RETURN_IF_NOT_OK(cfgCmdPfUnbind(sendFn));
    222     return std::unique_ptr<NFLogListener>(new NFLogListener(std::move(listener)));
    223 }
    224 
    225 }  // namespace net
    226 }  // namespace android
    227