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 <log/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::extract;
36 using netdutils::findWithDefault;
37 using netdutils::makeSlice;
38 using netdutils::NetlinkListener;
39 using netdutils::NetlinkListenerInterface;
40 using netdutils::Slice;
41 using netdutils::sSyscalls;
42 using netdutils::Status;
43 using netdutils::StatusOr;
44 using netdutils::status::ok;
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,
__anone8b324600202(const nlmsghdr& nlmsg, const nfgenmsg& nfmsg, const Slice msg) 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?
cfgCmdPfUnbind(const SendFn & send)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
cfgMode(const SendFn & send,uint16_t nfLogGroup,uint32_t range,uint8_t mode)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
cfgCmdBind(const SendFn & send,uint16_t nfLogGroup)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
cfgCmdUnbind(const SendFn & send,uint16_t nfLogGroup)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 
NFLogListener(std::shared_ptr<NetlinkListenerInterface> listener)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 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 
~NFLogListener()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 (const auto& [key, value] : mDispatchMap) {
173         expectOk(cfgCmdUnbind(sendFn, key));
174     }
175 }
176 
subscribe(uint16_t nfLogGroup,const DispatchFn & fn)177 Status NFLogListener::subscribe(uint16_t nfLogGroup, const DispatchFn& fn) {
178     return subscribe(nfLogGroup, kDefaultPacketRange, fn);
179 }
180 
subscribe(uint16_t nfLogGroup,uint32_t copyRange,const DispatchFn & fn)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 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 
unsubscribe(uint16_t nfLogGroup)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 guard(mMutex);
202         mDispatchMap.erase(nfLogGroup);
203     }
204     return ok;
205 }
206 
makeNFLogListener()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), "NFLogListener");
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