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