| /* |
| * 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); |
| } |
| |