blob: 87681f57ba0df6a0061c502fb6e14cfa18019a4c [file] [log] [blame]
/*
* 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 <arpa/inet.h>
#include <errno.h>
#include <ifaddrs.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 <unordered_set>
#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;
}
}
static const void* getSockAddrData(const struct sockaddr* addr) {
switch (addr->sa_family) {
case AF_INET:
return &reinterpret_cast<const struct sockaddr_in*>(addr)->sin_addr;
case AF_INET6:
return
&reinterpret_cast<const struct sockaddr_in6*>(addr)->sin6_addr;
default:
return nullptr;
}
}
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 requestAddresses() {
struct ifaddrs* addresses = nullptr;
if (getifaddrs(&addresses) != 0) {
RLOGE("Unable to retrieve list of interfaces, cannot get initial "
"interface addresses: %s", strerror(errno));
return;
}
for (struct ifaddrs* cur = addresses; cur; cur = cur->ifa_next) {
if (cur->ifa_name == nullptr ||
cur->ifa_addr == nullptr ||
cur->ifa_netmask == nullptr) {
// Interface doesn't have all the information we need. Rely on
// the netlink notification to catch this interface later if it
// is configured correctly.
continue;
}
if (cur->ifa_flags & IFF_LOOPBACK) {
// Not interested in loopback devices, they will never be radio
// interfaces.
continue;
}
unsigned int ifIndex = if_nametoindex(cur->ifa_name);
if (ifIndex == 0) {
RLOGE("Encountered interface %s with no index: %s",
cur->ifa_name, strerror(errno));
continue;
}
ifAddress addr;
addr.family = cur->ifa_addr->sa_family;
addr.prefix = getPrefix(cur->ifa_netmask);
memcpy(addr.addr,
getSockAddrData(cur->ifa_addr),
addrLength(cur->ifa_addr->sa_family));
mAddresses[ifIndex].push_back(addr);
}
freeifaddrs(addresses);
if (mOnAddressChangeCallback) {
for (const auto& ifAddr : mAddresses) {
mOnAddressChangeCallback(ifAddr.first,
ifAddr.second.data(),
ifAddr.second.size());
}
}
}
int getPrefix(const struct sockaddr* addr) {
// This uses popcnt, a built-in instruction on some CPUs, to count
// the number of bits in a 32-bit word. The number of bits in a netmask
// equals the width of the prefix. For example a netmask of
// 255.255.255.0 has 24 bits set and that's also its width.
if (addr->sa_family == AF_INET) {
auto v4 = reinterpret_cast<const struct sockaddr_in*>(addr);
return __builtin_popcount(v4->sin_addr.s_addr);
} else if (addr->sa_family == AF_INET6) {
auto v6 = reinterpret_cast<const struct sockaddr_in6*>(addr);
// Copy to our own array to avoid aliasing
uint64_t words[2];
memcpy(words, v6->sin6_addr.s6_addr, sizeof(words));
return __builtin_popcountll(words[0]) +
__builtin_popcountll(words[1]);
}
return 0;
}
void run() {
requestAddresses();
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;
}
hdr = NLMSG_NEXT(hdr, length);
}
}
}
std::string getInterfaceName(unsigned int ifIndex) {
char buffer[IF_NAMESIZE] = { '\0' };
return if_indextoname(ifIndex, buffer);
}
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();
}