266 lines
8.3 KiB
C++
266 lines
8.3 KiB
C++
/*
|
|
* 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);
|
|
}
|
|
|