| /* |
| * 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 "NetlinkListener.h" |
| |
| #include <sstream> |
| #include <vector> |
| |
| #include <linux/netfilter/nfnetlink.h> |
| |
| #include <log/log.h> |
| #include <netdutils/Misc.h> |
| #include <netdutils/Syscalls.h> |
| |
| namespace android { |
| namespace net { |
| |
| 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<char> 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<Fd, 2> 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 net |
| } // namespace android |