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