2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-12-10 22:41:40 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2019-11-21 19:29:49 +00:00
|
|
|
#include "test/syscalls/linux/socket_netlink_util.h"
|
2018-12-22 01:12:28 +00:00
|
|
|
|
2018-12-10 22:41:40 +00:00
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/netlink.h>
|
2020-02-05 02:04:26 +00:00
|
|
|
#include <linux/rtnetlink.h>
|
2019-11-21 19:29:49 +00:00
|
|
|
#include <sys/socket.h>
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "absl/strings/str_cat.h"
|
|
|
|
#include "test/syscalls/linux/socket_test_util.h"
|
|
|
|
|
|
|
|
namespace gvisor {
|
|
|
|
namespace testing {
|
|
|
|
|
2019-11-04 18:06:00 +00:00
|
|
|
PosixErrorOr<FileDescriptor> NetlinkBoundSocket(int protocol) {
|
2018-12-10 22:41:40 +00:00
|
|
|
FileDescriptor fd;
|
2019-11-04 18:06:00 +00:00
|
|
|
ASSIGN_OR_RETURN_ERRNO(fd, Socket(AF_NETLINK, SOCK_RAW, protocol));
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
struct sockaddr_nl addr = {};
|
|
|
|
addr.nl_family = AF_NETLINK;
|
|
|
|
|
|
|
|
RETURN_ERROR_IF_SYSCALL_FAIL(
|
|
|
|
bind(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)));
|
|
|
|
MaybeSave();
|
|
|
|
|
|
|
|
return std::move(fd);
|
|
|
|
}
|
|
|
|
|
2020-01-22 00:16:51 +00:00
|
|
|
PosixErrorOr<uint32_t> NetlinkPortID(int fd) {
|
2018-12-10 22:41:40 +00:00
|
|
|
struct sockaddr_nl addr;
|
|
|
|
socklen_t addrlen = sizeof(addr);
|
|
|
|
|
|
|
|
RETURN_ERROR_IF_SYSCALL_FAIL(
|
|
|
|
getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &addrlen));
|
|
|
|
MaybeSave();
|
|
|
|
|
2020-01-22 00:16:51 +00:00
|
|
|
return static_cast<uint32_t>(addr.nl_pid);
|
2018-12-10 22:41:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PosixError NetlinkRequestResponse(
|
|
|
|
const FileDescriptor& fd, void* request, size_t len,
|
2019-08-10 05:33:40 +00:00
|
|
|
const std::function<void(const struct nlmsghdr* hdr)>& fn,
|
|
|
|
bool expect_nlmsgerr) {
|
2018-12-10 22:41:40 +00:00
|
|
|
struct iovec iov = {};
|
|
|
|
iov.iov_base = request;
|
|
|
|
iov.iov_len = len;
|
|
|
|
|
|
|
|
struct msghdr msg = {};
|
|
|
|
msg.msg_iov = &iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
// No destination required; it defaults to pid 0, the kernel.
|
|
|
|
|
|
|
|
RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0));
|
|
|
|
|
|
|
|
constexpr size_t kBufferSize = 4096;
|
|
|
|
std::vector<char> buf(kBufferSize);
|
|
|
|
iov.iov_base = buf.data();
|
|
|
|
iov.iov_len = buf.size();
|
|
|
|
|
2020-02-05 02:04:26 +00:00
|
|
|
// If NLM_F_MULTI is set, response is a series of messages that ends with a
|
|
|
|
// NLMSG_DONE message.
|
2018-12-10 22:41:40 +00:00
|
|
|
int type = -1;
|
2020-02-05 02:04:26 +00:00
|
|
|
int flags = 0;
|
2018-12-10 22:41:40 +00:00
|
|
|
do {
|
|
|
|
int len;
|
|
|
|
RETURN_ERROR_IF_SYSCALL_FAIL(len = RetryEINTR(recvmsg)(fd.get(), &msg, 0));
|
|
|
|
|
|
|
|
// We don't bother with the complexity of dealing with truncated messages.
|
|
|
|
// We must allocate a large enough buffer up front.
|
|
|
|
if ((msg.msg_flags & MSG_TRUNC) == MSG_TRUNC) {
|
|
|
|
return PosixError(EIO,
|
|
|
|
absl::StrCat("Received truncated message with flags: ",
|
|
|
|
msg.msg_flags));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data());
|
|
|
|
NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
|
|
|
|
fn(hdr);
|
2020-02-05 02:04:26 +00:00
|
|
|
flags = hdr->nlmsg_flags;
|
2018-12-10 22:41:40 +00:00
|
|
|
type = hdr->nlmsg_type;
|
2019-10-10 23:54:30 +00:00
|
|
|
// Done should include an integer payload for dump_done_errno.
|
|
|
|
// See net/netlink/af_netlink.c:netlink_dump
|
|
|
|
// Some tools like the 'ip' tool check the minimum length of the
|
|
|
|
// NLMSG_DONE message.
|
|
|
|
if (type == NLMSG_DONE) {
|
|
|
|
EXPECT_GE(hdr->nlmsg_len, NLMSG_LENGTH(sizeof(int)));
|
|
|
|
}
|
2018-12-10 22:41:40 +00:00
|
|
|
}
|
2020-02-05 02:04:26 +00:00
|
|
|
} while ((flags & NLM_F_MULTI) && type != NLMSG_DONE && type != NLMSG_ERROR);
|
2018-12-10 22:41:40 +00:00
|
|
|
|
2019-08-10 05:33:40 +00:00
|
|
|
if (expect_nlmsgerr) {
|
|
|
|
EXPECT_EQ(type, NLMSG_ERROR);
|
2020-02-05 02:04:26 +00:00
|
|
|
} else if (flags & NLM_F_MULTI) {
|
2019-08-10 05:33:40 +00:00
|
|
|
EXPECT_EQ(type, NLMSG_DONE);
|
|
|
|
}
|
2018-12-10 22:41:40 +00:00
|
|
|
return NoError();
|
|
|
|
}
|
|
|
|
|
2020-01-28 20:31:58 +00:00
|
|
|
PosixError NetlinkRequestResponseSingle(
|
|
|
|
const FileDescriptor& fd, void* request, size_t len,
|
|
|
|
const std::function<void(const struct nlmsghdr* hdr)>& fn) {
|
|
|
|
struct iovec iov = {};
|
|
|
|
iov.iov_base = request;
|
|
|
|
iov.iov_len = len;
|
|
|
|
|
|
|
|
struct msghdr msg = {};
|
|
|
|
msg.msg_iov = &iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
// No destination required; it defaults to pid 0, the kernel.
|
|
|
|
|
|
|
|
RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0));
|
|
|
|
|
|
|
|
constexpr size_t kBufferSize = 4096;
|
|
|
|
std::vector<char> buf(kBufferSize);
|
|
|
|
iov.iov_base = buf.data();
|
|
|
|
iov.iov_len = buf.size();
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
RETURN_ERROR_IF_SYSCALL_FAIL(ret = RetryEINTR(recvmsg)(fd.get(), &msg, 0));
|
|
|
|
|
|
|
|
// We don't bother with the complexity of dealing with truncated messages.
|
|
|
|
// We must allocate a large enough buffer up front.
|
|
|
|
if ((msg.msg_flags & MSG_TRUNC) == MSG_TRUNC) {
|
|
|
|
return PosixError(
|
|
|
|
EIO,
|
|
|
|
absl::StrCat("Received truncated message with flags: ", msg.msg_flags));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data());
|
|
|
|
NLMSG_OK(hdr, ret); hdr = NLMSG_NEXT(hdr, ret)) {
|
|
|
|
fn(hdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NoError();
|
|
|
|
}
|
|
|
|
|
2020-02-05 02:04:26 +00:00
|
|
|
PosixError NetlinkRequestAckOrError(const FileDescriptor& fd, uint32_t seq,
|
|
|
|
void* request, size_t len) {
|
|
|
|
// Dummy negative number for no error message received.
|
|
|
|
// We won't get a negative error number so there will be no confusion.
|
|
|
|
int err = -42;
|
|
|
|
RETURN_IF_ERRNO(NetlinkRequestResponse(
|
|
|
|
fd, request, len,
|
|
|
|
[&](const struct nlmsghdr* hdr) {
|
|
|
|
EXPECT_EQ(NLMSG_ERROR, hdr->nlmsg_type);
|
|
|
|
EXPECT_EQ(hdr->nlmsg_seq, seq);
|
|
|
|
EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct nlmsgerr));
|
|
|
|
|
|
|
|
const struct nlmsgerr* msg =
|
|
|
|
reinterpret_cast<const struct nlmsgerr*>(NLMSG_DATA(hdr));
|
|
|
|
err = -msg->error;
|
|
|
|
},
|
|
|
|
true));
|
|
|
|
return PosixError(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct rtattr* FindRtAttr(const struct nlmsghdr* hdr,
|
|
|
|
const struct ifinfomsg* msg, int16_t attr) {
|
|
|
|
const int ifi_space = NLMSG_SPACE(sizeof(*msg));
|
|
|
|
int attrlen = hdr->nlmsg_len - ifi_space;
|
|
|
|
const struct rtattr* rta = reinterpret_cast<const struct rtattr*>(
|
|
|
|
reinterpret_cast<const uint8_t*>(hdr) + NLMSG_ALIGN(ifi_space));
|
|
|
|
for (; RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) {
|
|
|
|
if (rta->rta_type == attr) {
|
|
|
|
return rta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-12-10 22:41:40 +00:00
|
|
|
} // namespace testing
|
|
|
|
} // namespace gvisor
|