164 lines
5.0 KiB
C++
164 lines
5.0 KiB
C++
// Copyright 2020 The gVisor Authors.
|
|
//
|
|
// 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 "test/syscalls/linux/socket_netlink_route_util.h"
|
|
|
|
#include <linux/if.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "absl/types/optional.h"
|
|
#include "test/syscalls/linux/socket_netlink_util.h"
|
|
|
|
namespace gvisor {
|
|
namespace testing {
|
|
namespace {
|
|
|
|
constexpr uint32_t kSeq = 12345;
|
|
|
|
} // namespace
|
|
|
|
PosixError DumpLinks(
|
|
const FileDescriptor& fd, uint32_t seq,
|
|
const std::function<void(const struct nlmsghdr* hdr)>& fn) {
|
|
struct request {
|
|
struct nlmsghdr hdr;
|
|
struct ifinfomsg ifm;
|
|
};
|
|
|
|
struct request req = {};
|
|
req.hdr.nlmsg_len = sizeof(req);
|
|
req.hdr.nlmsg_type = RTM_GETLINK;
|
|
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
|
req.hdr.nlmsg_seq = seq;
|
|
req.ifm.ifi_family = AF_UNSPEC;
|
|
|
|
return NetlinkRequestResponse(fd, &req, sizeof(req), fn, false);
|
|
}
|
|
|
|
PosixErrorOr<std::vector<Link>> DumpLinks() {
|
|
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE));
|
|
|
|
std::vector<Link> links;
|
|
RETURN_IF_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) {
|
|
if (hdr->nlmsg_type != RTM_NEWLINK ||
|
|
hdr->nlmsg_len < NLMSG_SPACE(sizeof(struct ifinfomsg))) {
|
|
return;
|
|
}
|
|
const struct ifinfomsg* msg =
|
|
reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr));
|
|
const auto* rta = FindRtAttr(hdr, msg, IFLA_IFNAME);
|
|
if (rta == nullptr) {
|
|
// Ignore links that do not have a name.
|
|
return;
|
|
}
|
|
|
|
links.emplace_back();
|
|
links.back().index = msg->ifi_index;
|
|
links.back().type = msg->ifi_type;
|
|
links.back().name =
|
|
std::string(reinterpret_cast<const char*>(RTA_DATA(rta)));
|
|
}));
|
|
return links;
|
|
}
|
|
|
|
PosixErrorOr<absl::optional<Link>> FindLoopbackLink() {
|
|
ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks());
|
|
for (const auto& link : links) {
|
|
if (link.type == ARPHRD_LOOPBACK) {
|
|
return absl::optional<Link>(link);
|
|
}
|
|
}
|
|
return absl::optional<Link>();
|
|
}
|
|
|
|
PosixError LinkAddLocalAddr(int index, int family, int prefixlen,
|
|
const void* addr, int addrlen) {
|
|
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE));
|
|
|
|
struct request {
|
|
struct nlmsghdr hdr;
|
|
struct ifaddrmsg ifaddr;
|
|
char attrbuf[512];
|
|
};
|
|
|
|
struct request req = {};
|
|
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifaddr));
|
|
req.hdr.nlmsg_type = RTM_NEWADDR;
|
|
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
|
req.hdr.nlmsg_seq = kSeq;
|
|
req.ifaddr.ifa_index = index;
|
|
req.ifaddr.ifa_family = family;
|
|
req.ifaddr.ifa_prefixlen = prefixlen;
|
|
|
|
struct rtattr* rta = reinterpret_cast<struct rtattr*>(
|
|
reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len));
|
|
rta->rta_type = IFA_LOCAL;
|
|
rta->rta_len = RTA_LENGTH(addrlen);
|
|
req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen);
|
|
memcpy(RTA_DATA(rta), addr, addrlen);
|
|
|
|
return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len);
|
|
}
|
|
|
|
PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change) {
|
|
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE));
|
|
|
|
struct request {
|
|
struct nlmsghdr hdr;
|
|
struct ifinfomsg ifinfo;
|
|
char pad[NLMSG_ALIGNTO];
|
|
};
|
|
|
|
struct request req = {};
|
|
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo));
|
|
req.hdr.nlmsg_type = RTM_NEWLINK;
|
|
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
|
req.hdr.nlmsg_seq = kSeq;
|
|
req.ifinfo.ifi_index = index;
|
|
req.ifinfo.ifi_flags = flags;
|
|
req.ifinfo.ifi_change = change;
|
|
|
|
return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len);
|
|
}
|
|
|
|
PosixError LinkSetMacAddr(int index, const void* addr, int addrlen) {
|
|
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE));
|
|
|
|
struct request {
|
|
struct nlmsghdr hdr;
|
|
struct ifinfomsg ifinfo;
|
|
char attrbuf[512];
|
|
};
|
|
|
|
struct request req = {};
|
|
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo));
|
|
req.hdr.nlmsg_type = RTM_NEWLINK;
|
|
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
|
req.hdr.nlmsg_seq = kSeq;
|
|
req.ifinfo.ifi_index = index;
|
|
|
|
struct rtattr* rta = reinterpret_cast<struct rtattr*>(
|
|
reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len));
|
|
rta->rta_type = IFLA_ADDRESS;
|
|
rta->rta_len = RTA_LENGTH(addrlen);
|
|
req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen);
|
|
memcpy(RTA_DATA(rta), addr, addrlen);
|
|
|
|
return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len);
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace gvisor
|