blob: ea7bfe9eff7d21e21a71859e1aecc9f3804642ad [file] [log] [blame]
/*
* 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);
}