/*
* Copyright 2018, 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 "if_monitor.h"
#include <errno.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#define LOG_TAG "RIL-IFMON"
#include <utils/Log.h>
static const size_t kReadBufferSize = 32768;
static const size_t kControlServer = 0;
static const size_t kControlClient = 1;
// A list of commands that can be sent to the monitor. These should be one
// character long as that is all that the monitor will read and process.
static const char kMonitorStopCommand[] = "\1";
static const char kMonitorAckCommand[] = "\2";
static size_t addrLength(int addrFamily) {
switch (addrFamily) {
case AF_INET:
return 4;
case AF_INET6:
return 16;
default:
return 0;
}
}
bool operator==(const struct ifAddress& left, const struct ifAddress& right) {
// The prefix length does not factor in to whether two addresses are the
// same or not. Only the family and the address data. This matches the
// kernel behavior when attempting to add the same address with different
// prefix lengths, those changes are rejected because the address already
// exists.
return left.family == right.family &&
memcmp(&left.addr, &right.addr, addrLength(left.family)) == 0;
}
class InterfaceMonitor {
public:
InterfaceMonitor() : mSocketFd(-1) {
mControlSocket[kControlServer] = -1;
mControlSocket[kControlClient] = -1;
}
~InterfaceMonitor() {
if (mControlSocket[kControlClient] != -1) {
::close(mControlSocket[kControlClient]);
mControlSocket[kControlClient] = -1;
}
if (mControlSocket[kControlServer] != -1) {
::close(mControlSocket[kControlServer]);
mControlSocket[kControlServer] = -1;
}
if (mSocketFd != -1) {
::close(mSocketFd);
mSocketFd = -1;
}
}
bool init() {
if (mSocketFd != -1) {
RLOGE("InterfaceMonitor already initialized");
return false;
}
mSocketFd = ::socket(AF_NETLINK,
SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_ROUTE);
if (mSocketFd == -1) {
RLOGE("InterfaceMonitor failed to open socket: %s", strerror(errno));
return false;
}
if (::socketpair(AF_UNIX, SOCK_DGRAM, 0, mControlSocket) != 0) {
RLOGE("Unable to create control socket pair: %s", strerror(errno));
return false;
}
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = (1 << (RTNLGRP_IPV4_IFADDR - 1)) |
(1 << (RTNLGRP_IPV6_IFADDR - 1));
struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(&addr);
if (::bind(mSocketFd, sa, sizeof(addr)) != 0) {
RLOGE("InterfaceMonitor failed to bind socket: %s",
strerror(errno));
return false;
}
return true;
}
void setCallback(ifMonitorCallback callback) {
mOnAddressChangeCallback = callback;
}
void runAsync() {
std::unique_lock<std::mutex> lock(mThreadMutex);
mThread = std::make_unique<std::thread>([this]() { run(); });
}
void requestAddress() {
struct {
struct nlmsghdr hdr;
struct ifaddrmsg msg;
char padding[16];
} request;
memset(&request, 0, sizeof(request));
request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
request.hdr.nlmsg_type = RTM_GETADDR;
int status = ::send(mSocketFd, &request, request.hdr.nlmsg_len, 0);
if (status < 0 ||
static_cast<unsigned int>(status) != request.hdr.nlmsg_len) {
if (status < 0) {
RLOGE("Failed to send netlink request: %s", strerror(errno));
} else {
RLOGE("Short send only sent %d out of %d bytes",
status, (int)request.hdr.nlmsg_len);
}
}
}
void run() {
requestAddress();
std::vector<struct pollfd> fds(2);
fds[0].events = POLLIN;
fds[0].fd = mControlSocket[kControlServer];
fds[1].events = POLLIN;
fds[1].fd = mSocketFd;
while (true) {
int status = ::poll(fds.data(), fds.size(), -1);
if (status < 0) {
if (errno == EINTR) {
// Interrupted, just keep going
continue;
}
// Actual error, time to quit
RLOGE("Polling failed: %s", strerror(errno));
break;
} else if (status == 0) {
// Timeout
continue;
}
if (fds[0].revents & POLLIN) {
// Control message received
char command = -1;
if (::read(mControlSocket[kControlServer],
&command,
sizeof(command)) == 1) {
if (command == kMonitorStopCommand[0]) {
break;
}
}
} else if (fds[1].revents & POLLIN) {
onReadAvailable();
}
}
::write(mControlSocket[kControlServer], kMonitorAckCommand, 1);
}
void stop() {
std::unique_lock<std::mutex> lock(mThreadMutex);
if (mThread) {
::write(mControlSocket[kControlClient], kMonitorStopCommand, 1);
char ack = -1;
while (ack != kMonitorAckCommand[0]) {
::read(mControlSocket[kControlClient], &ack, sizeof(ack));
}
mThread->join();
mThread.reset();
}
}
private:
void onReadAvailable() {
char buffer[kReadBufferSize];
struct sockaddr_storage storage;
while (true) {
socklen_t addrSize = sizeof(storage);
int status = ::recvfrom(mSocketFd,
buffer,
sizeof(buffer),
MSG_DONTWAIT,
reinterpret_cast<struct sockaddr*>(&storage),
&addrSize);
if (status < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
// Nothing to receive, everything is fine
return;
} else if (status < 0 && errno == EINTR) {
// Caught interrupt, try again
continue;
} else if (status < 0) {
RLOGE("InterfaceMonitor receive failed: %s", strerror(errno));
return;
} else if (addrSize < 0 ||
static_cast<size_t>(addrSize) != sizeof(struct sockaddr_nl)) {
RLOGE("InterfaceMonitor received invalid address size");
return;
}
size_t length = static_cast<size_t>(status);
auto hdr = reinterpret_cast<struct nlmsghdr*>(buffer);
while (NLMSG_OK(hdr, length) && hdr->nlmsg_type != NLMSG_DONE) {
switch (hdr->nlmsg_type) {
case RTM_NEWADDR:
case RTM_DELADDR:
handleAddressChange(hdr);
break;
default:
RLOGE("Received message type %d", (int)hdr->nlmsg_type);
break;
}
NLMSG_NEXT(hdr, length);
}
}
}
void handleAddressChange(const struct nlmsghdr* hdr) {
if (!mOnAddressChangeCallback) {
return;
}
auto msg = reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(hdr));
std::vector<ifAddress>& ifAddrs = mAddresses[msg->ifa_index];
auto attr = reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
int attrLen = IFA_PAYLOAD(hdr);
bool somethingChanged = false;
for (;attr && RTA_OK(attr, attrLen); attr = RTA_NEXT(attr, attrLen)) {
if (attr->rta_type != IFA_LOCAL && attr->rta_type != IFA_ADDRESS) {
continue;
}
ifAddress addr;
memset(&addr, 0, sizeof(addr));
// Ensure that the payload matches the expected address length
if (RTA_PAYLOAD(attr) >= addrLength(msg->ifa_family)) {
addr.family = msg->ifa_family;
addr.prefix = msg->ifa_prefixlen;
memcpy(&addr.addr, RTA_DATA(attr), addrLength(addr.family));
} else {
RLOGE("Invalid address family (%d) and size (%d) combination",
int(msg->ifa_family), int(RTA_PAYLOAD(attr)));
continue;
}
auto it = std::find(ifAddrs.begin(), ifAddrs.end(), addr);
if (hdr->nlmsg_type == RTM_NEWADDR && it == ifAddrs.end()) {
// New address does not exist, add it
ifAddrs.push_back(addr);
somethingChanged = true;
} else if (hdr->nlmsg_type == RTM_DELADDR && it != ifAddrs.end()) {
// Address was removed and it exists, remove it
ifAddrs.erase(it);
somethingChanged = true;
}
}
if (somethingChanged) {
mOnAddressChangeCallback(msg->ifa_index,
ifAddrs.data(),
ifAddrs.size());
}
}
ifMonitorCallback mOnAddressChangeCallback;
std::unordered_map<unsigned int, std::vector<ifAddress>> mAddresses;
std::unique_ptr<std::thread> mThread;
std::mutex mThreadMutex;
int mSocketFd;
int mControlSocket[2];
};
extern "C"
struct ifMonitor* ifMonitorCreate() {
auto monitor = std::make_unique<InterfaceMonitor>();
if (!monitor || !monitor->init()) {
return nullptr;
}
return reinterpret_cast<struct ifMonitor*>(monitor.release());
}
extern "C"
void ifMonitorFree(struct ifMonitor* ifMonitor) {
InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
delete monitor;
}
extern "C"
void ifMonitorSetCallback(struct ifMonitor* ifMonitor,
ifMonitorCallback callback) {
InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
monitor->setCallback(callback);
}
extern "C"
void ifMonitorRunAsync(struct ifMonitor* ifMonitor) {
InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
monitor->runAsync();
}
extern "C"
void ifMonitorStop(struct ifMonitor* ifMonitor) {
InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
monitor->stop();
}