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