C++程序  |  154行  |  4.86 KB

/*
 * 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