From b48a1205be045c7efdce8504d3c2384aefef768f Mon Sep 17 00:00:00 2001 From: Wayne Ma Date: Tue, 18 Jan 2022 18:20:24 +0800 Subject: [PATCH] Move NetlinkListener relevant files to frameworks/libs/net NetlinkListener is used by Netd, so move it to frameworks/libs/net and then it can be used by platform code and mainline module. Bug: 209935649 Test: m; flash; boot Change-Id: I4cf0e64eee1dda1e452d76af77642416c9729d42 --- staticlibs/netd/libnetdutils/Android.bp | 1 + .../netd/libnetdutils/NetlinkListener.cpp | 152 ++++++++++++++++++ .../include/netdutils/NetlinkListener.h | 105 ++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 staticlibs/netd/libnetdutils/NetlinkListener.cpp create mode 100644 staticlibs/netd/libnetdutils/include/netdutils/NetlinkListener.h diff --git a/staticlibs/netd/libnetdutils/Android.bp b/staticlibs/netd/libnetdutils/Android.bp index 732e37d90a..08d5412158 100644 --- a/staticlibs/netd/libnetdutils/Android.bp +++ b/staticlibs/netd/libnetdutils/Android.bp @@ -11,6 +11,7 @@ cc_library { "Log.cpp", "Netfilter.cpp", "Netlink.cpp", + "NetlinkListener.cpp", "Slice.cpp", "Socket.cpp", "SocketOption.cpp", diff --git a/staticlibs/netd/libnetdutils/NetlinkListener.cpp b/staticlibs/netd/libnetdutils/NetlinkListener.cpp new file mode 100644 index 0000000000..decaa9c88b --- /dev/null +++ b/staticlibs/netd/libnetdutils/NetlinkListener.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "NetlinkListener" + +#include +#include + +#include + +#include +#include +#include +#include + +namespace android { +namespace netdutils { + +using netdutils::Fd; +using netdutils::Slice; +using netdutils::Status; +using netdutils::UniqueFd; +using netdutils::findWithDefault; +using netdutils::forEachNetlinkMessage; +using netdutils::makeSlice; +using netdutils::sSyscalls; +using netdutils::status::ok; +using netdutils::statusFromErrno; + +namespace { + +constexpr int kNetlinkMsgErrorType = (NFNL_SUBSYS_NONE << 8) | NLMSG_ERROR; + +constexpr sockaddr_nl kKernelAddr = { + .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = 0, +}; + +const NetlinkListener::DispatchFn kDefaultDispatchFn = [](const nlmsghdr& nlmsg, const Slice) { + std::stringstream ss; + ss << nlmsg; + ALOGE("unhandled netlink message: %s", ss.str().c_str()); +}; + +} // namespace + +NetlinkListener::NetlinkListener(UniqueFd event, UniqueFd sock, const std::string& name) + : mEvent(std::move(event)), mSock(std::move(sock)), mThreadName(name) { + const auto rxErrorHandler = [](const nlmsghdr& nlmsg, const Slice msg) { + std::stringstream ss; + ss << nlmsg << " " << msg << " " << netdutils::toHex(msg, 32); + ALOGE("unhandled netlink message: %s", ss.str().c_str()); + }; + expectOk(NetlinkListener::subscribe(kNetlinkMsgErrorType, rxErrorHandler)); + + mErrorHandler = [& name = mThreadName](const int fd, const int err) { + ALOGE("Error on NetlinkListener(%s) fd=%d: %s", name.c_str(), fd, strerror(err)); + }; + + // Start the thread + mWorker = std::thread([this]() { run().ignoreError(); }); +} + +NetlinkListener::~NetlinkListener() { + const auto& sys = sSyscalls.get(); + const uint64_t data = 1; + // eventfd should never enter an error state unexpectedly + expectOk(sys.write(mEvent, makeSlice(data)).status()); + mWorker.join(); +} + +Status NetlinkListener::send(const Slice msg) { + const auto& sys = sSyscalls.get(); + ASSIGN_OR_RETURN(auto sent, sys.sendto(mSock, msg, 0, kKernelAddr)); + if (sent != msg.size()) { + return statusFromErrno(EMSGSIZE, "unexpect message size"); + } + return ok; +} + +Status NetlinkListener::subscribe(uint16_t type, const DispatchFn& fn) { + std::lock_guard guard(mMutex); + mDispatchMap[type] = fn; + return ok; +} + +Status NetlinkListener::unsubscribe(uint16_t type) { + std::lock_guard guard(mMutex); + mDispatchMap.erase(type); + return ok; +} + +void NetlinkListener::registerSkErrorHandler(const SkErrorHandler& handler) { + mErrorHandler = handler; +} + +Status NetlinkListener::run() { + std::vector rxbuf(4096); + + const auto rxHandler = [this](const nlmsghdr& nlmsg, const Slice& buf) { + std::lock_guard guard(mMutex); + const auto& fn = findWithDefault(mDispatchMap, nlmsg.nlmsg_type, kDefaultDispatchFn); + fn(nlmsg, buf); + }; + + if (mThreadName.length() > 0) { + int ret = pthread_setname_np(pthread_self(), mThreadName.c_str()); + if (ret) { + ALOGE("thread name set failed, name: %s, ret: %s", mThreadName.c_str(), strerror(ret)); + } + } + const auto& sys = sSyscalls.get(); + const std::array fds{{{mEvent}, {mSock}}}; + const int events = POLLIN; + const double timeout = 3600; + while (true) { + ASSIGN_OR_RETURN(auto revents, sys.ppoll(fds, events, timeout)); + // After mEvent becomes readable, we should stop servicing mSock and return + if (revents[0] & POLLIN) { + break; + } + if (revents[1] & (POLLIN|POLLERR)) { + auto rx = sys.recvfrom(mSock, makeSlice(rxbuf), 0); + int err = rx.status().code(); + if (err) { + // Ignore errors. The only error we expect to see here is ENOBUFS, and there's + // nothing we can do about that. The recvfrom above will already have cleared the + // error indication and ensured we won't get EPOLLERR again. + // TODO: Consider using NETLINK_NO_ENOBUFS. + mErrorHandler(((Fd) mSock).get(), err); + continue; + } + forEachNetlinkMessage(rx.value(), rxHandler); + } + } + return ok; +} + +} // namespace netdutils +} // namespace android diff --git a/staticlibs/netd/libnetdutils/include/netdutils/NetlinkListener.h b/staticlibs/netd/libnetdutils/include/netdutils/NetlinkListener.h new file mode 100644 index 0000000000..97f7bb2357 --- /dev/null +++ b/staticlibs/netd/libnetdutils/include/netdutils/NetlinkListener.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETLINK_LISTENER_H +#define NETLINK_LISTENER_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace android { +namespace netdutils { + +class NetlinkListenerInterface { + public: + using DispatchFn = std::function; + + using SkErrorHandler = std::function; + + virtual ~NetlinkListenerInterface() = default; + + // Send message to the kernel using the underlying netlink socket + virtual netdutils::Status send(const netdutils::Slice msg) = 0; + + // Deliver future messages with nlmsghdr.nlmsg_type == type to fn. + // + // Threadsafe. + // All dispatch functions invoked on a single service thread. + // subscribe() and join() must not be called from the stack of fn(). + virtual netdutils::Status subscribe(uint16_t type, const DispatchFn& fn) = 0; + + // Halt delivery of future messages with nlmsghdr.nlmsg_type == type. + // Threadsafe. + virtual netdutils::Status unsubscribe(uint16_t type) = 0; + + virtual void registerSkErrorHandler(const SkErrorHandler& handler) = 0; +}; + +// NetlinkListener manages a netlink socket and associated blocking +// service thread. +// +// This class is written in a generic way to allow multiple different +// netlink subsystems to share this common infrastructure. If multiple +// subsystems share the same message delivery requirements (drops ok, +// no drops) they may share a single listener by calling subscribe() +// with multiple types. +// +// This class is suitable for moderate performance message +// processing. In particular it avoids extra copies of received +// message data and allows client code to control which message +// attributes are processed. +// +// Note that NetlinkListener is capable of processing multiple batched +// netlink messages in a single system call. This is useful to +// netfilter extensions that allow batching of events like NFLOG. +class NetlinkListener : public NetlinkListenerInterface { + public: + NetlinkListener(netdutils::UniqueFd event, netdutils::UniqueFd sock, const std::string& name); + + ~NetlinkListener() override; + + netdutils::Status send(const netdutils::Slice msg) override; + + netdutils::Status subscribe(uint16_t type, const DispatchFn& fn) override EXCLUDES(mMutex); + + netdutils::Status unsubscribe(uint16_t type) override EXCLUDES(mMutex); + + void registerSkErrorHandler(const SkErrorHandler& handler) override; + + private: + netdutils::Status run(); + + const netdutils::UniqueFd mEvent; + const netdutils::UniqueFd mSock; + const std::string mThreadName; + std::mutex mMutex; + std::map mDispatchMap GUARDED_BY(mMutex); + std::thread mWorker; + SkErrorHandler mErrorHandler; +}; + +} // namespace netdutils +} // namespace android + +#endif /* NETLINK_LISTENER_H */