C++程序  |  266行  |  8.27 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.
 */

#include "netlink.h"

#include "log.h"
#include "netlinkmessage.h"

#include <errno.h>
#include <poll.h>
#include <string.h>
#include <linux/netlink.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

static const size_t kControlRead = 0;
static const size_t kControlWrite = 1;

static void closeIfOpen(int* fd) {
    if (*fd != -1) {
        ::close(*fd);
        *fd = -1;
    }
}

Netlink::Netlink()
    : mNextSequenceNumber(1)
    , mSocket(-1) {
    mControlPipe[kControlRead] = -1;
    mControlPipe[kControlWrite] = -1;
}

Netlink::~Netlink() {
    closeIfOpen(&mSocket);
    closeIfOpen(&mControlPipe[kControlRead]);
    closeIfOpen(&mControlPipe[kControlWrite]);
}

bool Netlink::init() {
    if (mSocket != -1) {
        ALOGE("Netlink already initialized");
        return false;
    }

    int status = ::pipe2(mControlPipe, O_CLOEXEC);
    if (status != 0) {
        ALOGE("Failed to create control pipe: %s", strerror(errno));
        return false;
    }

    mSocket = ::socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
    if (mSocket == -1) {
        ALOGE("Failed to create netlink socket: %s", strerror(errno));
        return false;
    }

    struct sockaddr_nl addr;
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    status = ::bind(mSocket,
                    reinterpret_cast<struct sockaddr*>(&addr),
                    sizeof(addr));
    if (status != 0) {
        ALOGE("Failed to bind netlink socket: %s", strerror(errno));
        return false;
    }

    return true;
}

void Netlink::stop(StopHandler handler) {
    char stop = 1;
    // Set the handler before writing so that it's guaranteed to be available
    // when the event loop reads from the control pipe.
    {
        // No need to keep the lock while writing so make it scoped
        std::unique_lock<std::mutex> lock(mStopHandlerMutex);
        mStopHandler = handler;
    }
    ::write(mControlPipe[kControlWrite], &stop, sizeof(stop));
}

bool Netlink::eventLoop() {
    struct pollfd fds[2];
    memset(fds, 0, sizeof(fds));
    fds[0].fd = mSocket;
    fds[0].events = POLLIN;
    fds[1].fd = mControlPipe[kControlRead];
    fds[1].events = POLLIN;

    for (;;) {
        int status = ::poll(fds, 2, -1);
        if (status == 0) {
            // Timeout, not really supposed to happen
            ALOGW("poll encountered a timeout despite infinite timeout");
            continue;
        } else if (status < 0) {
            if (errno == EINTR) {
                continue;
            }
            ALOGE("poll encountered an error: %s", strerror(errno));
            return false;
        }
        for (auto& fd : fds) {
            if ((fd.revents & POLLIN) == 0) {
                continue;
            }
            if (fd.fd == mSocket) {
                readNetlinkMessage(fd.fd);
            } else if (fd.fd == mControlPipe[kControlRead]) {
                if (readControlMessage()) {
                    // Make a copy of the stop handler while holding the lock
                    // and then call it after releasing the lock. This prevents
                    // the potential deadlock of someone calling stop from the
                    // stop callback. The drawback of this is that if someone
                    // calls stop again with a new stop handler that new stop
                    // handler might not be called if the timing is wrong.
                    // Both of these scenarios indicate highly questionable
                    // behavior on the callers part but at least this way the
                    // event loop will terminate which seems better than a
                    // total deadlock.
                    StopHandler handler;
                    {
                        std::unique_lock<std::mutex> lock(mStopHandlerMutex);
                        handler = mStopHandler;
                    }
                    if (handler) {
                        handler();
                    }
                    return true;
                }
            }
        }
    }
}

uint32_t Netlink::getSequenceNumber() {
    return mNextSequenceNumber++;
}

bool Netlink::sendMessage(const NetlinkMessage& message,
                          ReplyHandler handler) {
    // Keep lock the entire time so that we can safely erase the handler
    // without worrying about another call to sendAsync adding a handler that
    // shouldn't be deleted.
    std::unique_lock<std::mutex> lock(mHandlersMutex);
    // Register handler before sending in case the read thread picks up the
    // response between the send thread sending and registering the handler.
    mHandlers[message.sequence()] = handler;
    for (;;) {
        int bytesSent = ::send(mSocket, message.data(), message.size(), 0);
        if (bytesSent > 0 && static_cast<size_t>(bytesSent) == message.size()) {
            return true;
        }
        if (bytesSent < 0 && errno == EINTR) {
            // We need to try again, keep the mutex locked
            continue;
        }
        // It's a failure, remove the handler and unlock the mutex
        mHandlers.erase(message.sequence());
        lock.unlock();

        if (bytesSent < 0) {
            ALOGE("Failed to send netlink message: %s", strerror(errno));
        }
        return false;
    }
}

bool Netlink::readNetlinkMessage(int fd) {
    char buffer[8 * 1024];
    for (;;) {
        int bytesReceived = ::recv(fd, buffer, sizeof(buffer), 0);
        if (bytesReceived < 0) {
            if (errno == EINTR) {
                continue;
            }
            ALOGE("recv failed to receive on netlink socket: %s",
                  strerror(errno));
            return false;
        }
        char* data = buffer;
        char* end = data + bytesReceived;
        while (data < end) {
            if (data + sizeof(nlmsghdr) > end) {
                ALOGE("received invalid netlink message, too small for header");
                return false;
            }
            auto header = reinterpret_cast<nlmsghdr*>(data);
            if (data + header->nlmsg_len > end) {
                ALOGE("received invalid netlink message, too small for data");
                return false;
            }

            if (header->nlmsg_type == NLMSG_ERROR) {
                if (data + NLMSG_HDRLEN + sizeof(nlmsgerr) <= end) {
                    auto err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(header));
                    ALOGE("Receive netlink error message: %s, sequence %u",
                          strerror(-err->error), header->nlmsg_seq);
                } else {
                    ALOGE("Received netlink error code but no error message");
                }
                return false;
            }

            notifyHandler(data, header->nlmsg_len);

            data += header->nlmsg_len;
        }
        return true;
    }
}

bool Netlink::readControlMessage() {
    char buffer[32];

    for (;;) {
        int bytesReceived = ::recv(mControlPipe[kControlRead],
                                   buffer,
                                   sizeof(buffer),
                                   0);
        if (bytesReceived < 0) {
            if (errno == EINTR) {
                continue;
            }
        } else if (bytesReceived == 0) {
            return false;
        }
        return true;
    }
}


void Netlink::notifyHandler(const char* data, size_t size) {
    NetlinkMessage message(data, size);

    ReplyHandler replyHandler;
    {
        std::unique_lock<std::mutex> lock(mHandlersMutex);
        auto handler = mHandlers.find(message.sequence());
        if (handler == mHandlers.end()) {
            // No handler found, ignore message
            return;
        }
        replyHandler = handler->second;
        mHandlers.erase(handler);
    }

    replyHandler(message);
}