/*
* 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 <sstream>
#include <vector>
#include <linux/netfilter/nfnetlink.h>
#include <cutils/log.h>
#include <netdutils/Misc.h>
#include <netdutils/Syscalls.h>
#include "NetlinkListener.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)
: mEvent(std::move(event)), mSock(std::move(sock)), mWorker([this]() { run(); }) {
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));
}
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<std::mutex> guard(mMutex);
mDispatchMap[type] = fn;
return ok;
}
Status NetlinkListener::unsubscribe(uint16_t type) {
std::lock_guard<std::mutex> guard(mMutex);
mDispatchMap.erase(type);
return ok;
}
Status NetlinkListener::run() {
std::vector<char> rxbuf(4096);
const auto rxHandler = [this](const nlmsghdr& nlmsg, const Slice& buf) {
std::lock_guard<std::mutex> guard(mMutex);
const auto& fn = findWithDefault(mDispatchMap, nlmsg.nlmsg_type, kDefaultDispatchFn);
fn(nlmsg, buf);
};
const auto& sys = sSyscalls.get();
const std::array<Fd, 2> fds{{{mEvent}, {mSock}}};
const int events = POLLIN | POLLRDHUP | POLLERR | POLLHUP;
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) {
auto rx = sys.recvfrom(mSock, makeSlice(rxbuf), 0);
if (rx.status().code() == ENOBUFS) {
// Ignore ENOBUFS - the socket is still usable
// TODO: Users other than NFLOG may need to know about this
continue;
}
forEachNetlinkMessage(rx.value(), rxHandler);
}
}
return ok;
}
} // namespace net
} // namespace android