You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
12 KiB
291 lines
12 KiB
/*
|
|
* Copyright (C) 2008 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 <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define LOG_TAG "Netd"
|
|
|
|
#include <log/log.h>
|
|
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/strings.h>
|
|
#include <netutils/ifc.h>
|
|
#include <sysutils/NetlinkEvent.h>
|
|
#include "Controllers.h"
|
|
#include "NetlinkHandler.h"
|
|
#include "NetlinkManager.h"
|
|
#include "SockDiag.h"
|
|
|
|
#include <charconv>
|
|
|
|
#define BINDER_RETRY(exp) \
|
|
({ \
|
|
bool res = true; \
|
|
for (int attempt = 0; /*nop*/; ++attempt) { \
|
|
auto _rc = (exp); \
|
|
if (_rc.exceptionCode() == binder::Status::EX_TRANSACTION_FAILED && \
|
|
attempt < RETRY_ATTEMPTS) { \
|
|
usleep(RETRY_INTERVAL_MICRO_S); \
|
|
} else { \
|
|
res = _rc.isOk(); \
|
|
break; \
|
|
} \
|
|
} \
|
|
res; \
|
|
})
|
|
|
|
#define LOG_EVENT_FUNC(retry, func, ...) \
|
|
do { \
|
|
const auto listenerMap = gCtls->eventReporter.getNetdUnsolicitedEventListenerMap(); \
|
|
for (auto& listener : listenerMap) { \
|
|
auto entry = gUnsolicitedLog.newEntry().function(#func).args(__VA_ARGS__); \
|
|
if (retry(listener.first->func(__VA_ARGS__))) { \
|
|
gUnsolicitedLog.log(entry.withAutomaticDuration()); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
namespace android {
|
|
namespace net {
|
|
|
|
constexpr int RETRY_ATTEMPTS = 2;
|
|
constexpr int RETRY_INTERVAL_MICRO_S = 100000;
|
|
|
|
NetlinkHandler::NetlinkHandler(NetlinkManager *nm, int listenerSocket,
|
|
int format) :
|
|
NetlinkListener(listenerSocket, format) {
|
|
mNm = nm;
|
|
}
|
|
|
|
NetlinkHandler::~NetlinkHandler() {
|
|
}
|
|
|
|
int NetlinkHandler::start() {
|
|
return this->startListener();
|
|
}
|
|
|
|
int NetlinkHandler::stop() {
|
|
return this->stopListener();
|
|
}
|
|
|
|
static long parseIfIndex(const char* ifIndex) {
|
|
if (ifIndex == nullptr) {
|
|
return 0;
|
|
}
|
|
long ifaceIndex = strtol(ifIndex, nullptr, 10);
|
|
// strtol returns 0 on error, which is fine because 0 is not a valid ifindex.
|
|
if (errno == ERANGE && (ifaceIndex == LONG_MAX || ifaceIndex == LONG_MIN)) {
|
|
return 0;
|
|
}
|
|
return ifaceIndex;
|
|
}
|
|
|
|
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
|
|
const char *subsys = evt->getSubsystem();
|
|
if (!subsys) {
|
|
ALOGW("No subsystem found in netlink event");
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(subsys, "net")) {
|
|
NetlinkEvent::Action action = evt->getAction();
|
|
const char *iface = evt->findParam("INTERFACE");
|
|
if ((action == NetlinkEvent::Action::kAdd) ||
|
|
(action == NetlinkEvent::Action::kLinkUp) ||
|
|
(action == NetlinkEvent::Action::kLinkDown)) {
|
|
const char *ifIndex = evt->findParam("IFINDEX");
|
|
long ifaceIndex = parseIfIndex(ifIndex);
|
|
if (ifaceIndex) {
|
|
gCtls->trafficCtrl.addInterface(iface, ifaceIndex);
|
|
} else {
|
|
ALOGE("invalid interface index: %s(%s)", iface, ifIndex);
|
|
}
|
|
}
|
|
|
|
if (action == NetlinkEvent::Action::kAdd) {
|
|
notifyInterfaceAdded(iface);
|
|
} else if (action == NetlinkEvent::Action::kRemove) {
|
|
notifyInterfaceRemoved(iface);
|
|
} else if (action == NetlinkEvent::Action::kChange) {
|
|
evt->dump();
|
|
notifyInterfaceChanged("nana", true);
|
|
} else if (action == NetlinkEvent::Action::kLinkUp) {
|
|
notifyInterfaceLinkChanged(iface, true);
|
|
} else if (action == NetlinkEvent::Action::kLinkDown) {
|
|
notifyInterfaceLinkChanged(iface, false);
|
|
} else if (action == NetlinkEvent::Action::kAddressUpdated ||
|
|
action == NetlinkEvent::Action::kAddressRemoved) {
|
|
const char *address = evt->findParam("ADDRESS");
|
|
const char *flags = evt->findParam("FLAGS");
|
|
const char *scope = evt->findParam("SCOPE");
|
|
const char *ifIndex = evt->findParam("IFINDEX");
|
|
char addrstr[INET6_ADDRSTRLEN + strlen("/128")];
|
|
strlcpy(addrstr, address, sizeof(addrstr));
|
|
char *slash = strchr(addrstr, '/');
|
|
if (slash) {
|
|
*slash = '\0';
|
|
}
|
|
|
|
long ifaceIndex = parseIfIndex(ifIndex);
|
|
if (!ifaceIndex) {
|
|
ALOGE("invalid interface index: %s(%s)", iface, ifIndex);
|
|
}
|
|
const bool addrUpdated = (action == NetlinkEvent::Action::kAddressUpdated);
|
|
if (addrUpdated) {
|
|
gCtls->netCtrl.addInterfaceAddress(ifaceIndex, address);
|
|
} else { // action == NetlinkEvent::Action::kAddressRemoved
|
|
bool shouldDestroy = gCtls->netCtrl.removeInterfaceAddress(ifaceIndex, address);
|
|
if (shouldDestroy) {
|
|
SockDiag sd;
|
|
if (sd.open()) {
|
|
// Pass the interface index iff. destroying sockets on a link-local address.
|
|
// This cannot use an interface name as the interface might no longer exist.
|
|
int destroyIfaceIndex =
|
|
std::string_view(addrstr).starts_with("fe80:") ? ifaceIndex : 0;
|
|
int ret = sd.destroySockets(addrstr, destroyIfaceIndex);
|
|
if (ret < 0) {
|
|
ALOGE("Error destroying sockets: %s", strerror(-ret));
|
|
}
|
|
} else {
|
|
ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno));
|
|
}
|
|
}
|
|
}
|
|
// Note: if this interface was deleted, iface is "" and we don't notify.
|
|
if (iface && iface[0] && address && flags && scope) {
|
|
if (addrUpdated) {
|
|
notifyAddressUpdated(address, iface, std::stoi(flags), std::stoi(scope));
|
|
} else {
|
|
notifyAddressRemoved(address, iface, std::stoi(flags), std::stoi(scope));
|
|
}
|
|
}
|
|
} else if (action == NetlinkEvent::Action::kRdnss) {
|
|
const char *lifetime = evt->findParam("LIFETIME");
|
|
const char *servers = evt->findParam("SERVERS");
|
|
if (lifetime && servers) {
|
|
notifyInterfaceDnsServers(iface, strtol(lifetime, nullptr, 10),
|
|
android::base::Split(servers, ","));
|
|
}
|
|
} else if (action == NetlinkEvent::Action::kRouteUpdated ||
|
|
action == NetlinkEvent::Action::kRouteRemoved) {
|
|
const char *route = evt->findParam("ROUTE");
|
|
const char *gateway = evt->findParam("GATEWAY");
|
|
const char *iface = evt->findParam("INTERFACE");
|
|
if (route && (gateway || iface)) {
|
|
notifyRouteChange((action == NetlinkEvent::Action::kRouteUpdated) ? true : false,
|
|
route, (gateway == nullptr) ? "" : gateway,
|
|
(iface == nullptr) ? "" : iface);
|
|
}
|
|
}
|
|
|
|
} else if (!strcmp(subsys, "qlog") || !strcmp(subsys, "xt_quota2")) {
|
|
const char *alertName = evt->findParam("ALERT_NAME");
|
|
const char *iface = evt->findParam("INTERFACE");
|
|
if (alertName && iface) {
|
|
notifyQuotaLimitReached(alertName, iface);
|
|
}
|
|
|
|
} else if (!strcmp(subsys, "strict")) {
|
|
const char *uid = evt->findParam("UID");
|
|
const char *hex = evt->findParam("HEX");
|
|
if (uid && hex) {
|
|
notifyStrictCleartext(strtol(uid, nullptr, 10), hex);
|
|
}
|
|
|
|
} else if (!strcmp(subsys, "xt_idletimer")) {
|
|
const char *label = evt->findParam("INTERFACE");
|
|
const char *state = evt->findParam("STATE");
|
|
const char *timestamp = evt->findParam("TIME_NS");
|
|
const char *uid = evt->findParam("UID");
|
|
if (state) {
|
|
bool isActive = !strcmp("active", state);
|
|
int64_t processTimestamp = (timestamp == nullptr) ? 0 : strtoll(timestamp, nullptr, 10);
|
|
int intLabel;
|
|
// NMS only accepts interface class activity changes with integer labels, and only ever
|
|
// creates idletimers with integer labels.
|
|
if (android::base::ParseInt(label, &intLabel)) {
|
|
const long reportedUid =
|
|
(uid != nullptr && isActive) ? strtol(uid, nullptr, 10) : -1;
|
|
notifyInterfaceClassActivityChanged(intLabel, isActive, processTimestamp,
|
|
reportedUid);
|
|
}
|
|
}
|
|
|
|
#if !LOG_NDEBUG
|
|
} else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {
|
|
/* It is not a VSYNC or a backlight event */
|
|
ALOGV("unexpected event from subsystem %s", subsys);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void NetlinkHandler::notifyInterfaceAdded(const std::string& ifName) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceAdded, ifName);
|
|
}
|
|
|
|
void NetlinkHandler::notifyInterfaceRemoved(const std::string& ifName) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceRemoved, ifName);
|
|
}
|
|
|
|
void NetlinkHandler::notifyInterfaceChanged(const std::string& ifName, bool up) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceChanged, ifName, up);
|
|
}
|
|
|
|
void NetlinkHandler::notifyInterfaceLinkChanged(const std::string& ifName, bool up) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceLinkStateChanged, ifName, up);
|
|
}
|
|
|
|
void NetlinkHandler::notifyQuotaLimitReached(const std::string& labelName,
|
|
const std::string& ifName) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onQuotaLimitReached, labelName, ifName);
|
|
}
|
|
|
|
void NetlinkHandler::notifyInterfaceClassActivityChanged(int label, bool isActive,
|
|
int64_t timestamp, int uid) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceClassActivityChanged, isActive, label, timestamp, uid);
|
|
}
|
|
|
|
void NetlinkHandler::notifyAddressUpdated(const std::string& addr, const std::string& ifName,
|
|
int flags, int scope) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceAddressUpdated, addr, ifName, flags, scope);
|
|
}
|
|
|
|
void NetlinkHandler::notifyAddressRemoved(const std::string& addr, const std::string& ifName,
|
|
int flags, int scope) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceAddressRemoved, addr, ifName, flags, scope);
|
|
}
|
|
|
|
void NetlinkHandler::notifyInterfaceDnsServers(const std::string& ifName, int64_t lifetime,
|
|
const std::vector<std::string>& servers) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onInterfaceDnsServerInfo, ifName, lifetime, servers);
|
|
}
|
|
|
|
void NetlinkHandler::notifyRouteChange(bool updated, const std::string& route,
|
|
const std::string& gateway, const std::string& ifName) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onRouteChanged, updated, route, gateway, ifName);
|
|
}
|
|
|
|
void NetlinkHandler::notifyStrictCleartext(uid_t uid, const std::string& hex) {
|
|
LOG_EVENT_FUNC(BINDER_RETRY, onStrictCleartextDetected, uid, hex);
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace android
|