blob: a68e0ccc93aca3a43a12544d2b50dd411ee30af8 [file] [edit]
/*
* Copyright (C) 2024 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.
*/
#define LOG_TAG "resolv"
#include <linux/if_addrlabel.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include "addrlabel.h"
uint32_t resolv_getaddrlabel_simple(const sockaddr* addr) {
if (addr->sa_family == AF_INET) {
return 4;
} else if (addr->sa_family == AF_INET6) {
const sockaddr_in6* addr6 = (const sockaddr_in6*)addr;
if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) {
return 0;
} else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) {
return 4;
} else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
return 2;
} else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) {
return 5;
} else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) {
return ADDRLABEL_ULA;
} else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) {
return 3;
} else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) {
return 11;
} else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) {
return 12;
} else {
/* All other IPv6 addresses, including global unicast addresses. */
return 1;
}
} else {
/*
* This should never happen.
* Return a semi-random label as a last resort.
*/
return 1;
}
}
uint32_t resolv_getaddrlabel_netlink(const sockaddr* addr, int ifindex) {
struct getaddrlabel_req {
nlmsghdr nlh;
ifaddrlblmsg msg;
nlattr nla;
in6_addr addr;
} req = {
.nlh =
{
.nlmsg_len = sizeof(getaddrlabel_req),
.nlmsg_type = RTM_GETADDRLABEL,
.nlmsg_flags = NLM_F_REQUEST,
},
.msg =
{
.ifal_family = AF_INET6,
.ifal_prefixlen = 128,
.ifal_index = static_cast<__u32>(ifindex),
},
.nla =
{
.nla_len = RTA_SPACE(sizeof(*addr)),
.nla_type = IFAL_ADDRESS,
},
.addr = {},
};
if (addr->sa_family == AF_INET) {
in6_addr v4mapped = {
.in6_u.u6_addr32 = {0, 0, htonl(0xffff),
reinterpret_cast<const sockaddr_in*>(addr)->sin_addr.s_addr}};
req.addr = v4mapped;
} else {
req.addr = reinterpret_cast<const sockaddr_in6*>(addr)->sin6_addr;
}
// On error, return a semi-random label as a last resort.
constexpr uint32_t LABEL_UNKNOWN = 0;
uint32_t label = LABEL_UNKNOWN;
const sockaddr_nl kernel = {
.nl_family = AF_NETLINK,
};
android::base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE));
if (s == -1 || bind(s, reinterpret_cast<const sockaddr*>(&kernel), sizeof(kernel)) == -1 ||
connect(s, reinterpret_cast<const sockaddr*>(&kernel), sizeof(kernel)) == -1 ||
send(s, &req, sizeof(req), 0) < (ssize_t)sizeof(req)) {
LOG(ERROR) << __func__ << ": Error sending netlink request: " << strerror(errno);
return LABEL_UNKNOWN;
}
uint8_t response[256];
ssize_t bytesread = recv(s, response, sizeof(response), MSG_DONTWAIT | MSG_TRUNC);
if (bytesread < (ssize_t)(sizeof(nlmsghdr) + sizeof(nlmsgerr))) {
LOG(ERROR) << __func__ << ": short read: " << bytesread;
return LABEL_UNKNOWN;
}
nlmsghdr* nlh = reinterpret_cast<nlmsghdr*>(response);
if (nlh->nlmsg_type == NLMSG_ERROR) {
nlmsgerr* err = reinterpret_cast<nlmsgerr*>(nlh + 1);
LOG(ERROR) << __func__ << ": RTM_GETADDRLABEL: " << strerror(-err->error);
return LABEL_UNKNOWN;
}
if (nlh->nlmsg_len < (ssize_t)(sizeof(nlmsghdr) + sizeof(ifaddrlblmsg))) {
LOG(ERROR) << __func__ << ": short read: " << bytesread;
return LABEL_UNKNOWN;
}
ifaddrlblmsg* msg = reinterpret_cast<ifaddrlblmsg*>(nlh + 1);
rtattr* rta = reinterpret_cast<rtattr*>(msg + 1);
size_t rta_len = NLMSG_PAYLOAD(nlh, sizeof(*msg));
while (RTA_OK(rta, rta_len)) {
if (rta->rta_type == IFAL_LABEL) {
label = *static_cast<uint32_t*>(RTA_DATA(rta));
}
rta = RTA_NEXT(rta, rta_len);
}
return label;
}
uint32_t resolv_getaddrlabel(const struct sockaddr* addr) {
return resolv_getaddrlabel_simple(addr);
}