| // Copyright 2018 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net-base/netlink_socket.h" | |
| #include <linux/if_packet.h> | |
| #include <linux/netlink.h> | |
| #include <sys/select.h> | |
| #include <sys/socket.h> | |
| #include <memory> | |
| #include <utility> | |
| #include <base/logging.h> | |
| #include <base/posix/eintr_wrapper.h> | |
| #include "net-base/netlink_message.h" | |
| // This is from a version of linux/socket.h that we don't have. | |
| #define SOL_NETLINK 270 | |
| namespace net_base { | |
| std::unique_ptr<NetlinkSocket> NetlinkSocket::Create() { | |
| return CreateWithSocketFactory(std::make_unique<SocketFactory>()); | |
| } | |
| std::unique_ptr<NetlinkSocket> NetlinkSocket::CreateWithSocketFactory( | |
| std::unique_ptr<SocketFactory> socket_factory) { | |
| std::unique_ptr<Socket> socket = | |
| socket_factory->CreateNetlink(NETLINK_GENERIC, 0); | |
| if (socket == nullptr) { | |
| PLOG(ERROR) << "Failed to create AF_NETLINK socket"; | |
| return nullptr; | |
| } | |
| return std::unique_ptr<NetlinkSocket>(new NetlinkSocket(std::move(socket))); | |
| } | |
| NetlinkSocket::NetlinkSocket(std::unique_ptr<Socket> socket) | |
| : socket_(std::move(socket)) {} | |
| NetlinkSocket::~NetlinkSocket() = default; | |
| bool NetlinkSocket::RecvMessage(std::vector<uint8_t>* message) { | |
| return socket_->RecvMessage(message); | |
| } | |
| bool NetlinkSocket::SendMessage(base::span<const uint8_t> out_msg) { | |
| const std::optional<size_t> result = socket_->Send(out_msg, 0); | |
| if (!result) { | |
| PLOG(ERROR) << "Send failed."; | |
| return false; | |
| } | |
| if (*result != out_msg.size()) { | |
| LOG(ERROR) << "Only sent " << *result << " bytes out of " << out_msg.size() | |
| << "."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool NetlinkSocket::SubscribeToEvents(uint32_t group_id) { | |
| int err = setsockopt(socket_->Get(), SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, | |
| &group_id, sizeof(group_id)); | |
| if (err < 0) { | |
| PLOG(ERROR) << "setsockopt didn't work."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| int NetlinkSocket::WaitForRead(base::TimeDelta timeout) const { | |
| fd_set read_fds; | |
| FD_ZERO(&read_fds); | |
| if (socket_->Get() >= FD_SETSIZE) { | |
| LOG(ERROR) << "Invalid file_descriptor: " << socket_->Get(); | |
| return -1; | |
| } | |
| FD_SET(socket_->Get(), &read_fds); | |
| DCHECK(!timeout.is_negative()); | |
| struct timeval time = { | |
| .tv_sec = static_cast<time_t>(timeout.InSeconds()), | |
| .tv_usec = static_cast<suseconds_t>( | |
| (timeout - base::Seconds(timeout.InSeconds())).InMicroseconds()), | |
| }; | |
| return HANDLE_EINTR( | |
| select(socket_->Get() + 1, &read_fds, nullptr, nullptr, &time)); | |
| } | |
| uint32_t NetlinkSocket::GetSequenceNumber() { | |
| if (++sequence_number_ == NetlinkMessage::kBroadcastSequenceNumber) { | |
| ++sequence_number_; | |
| } | |
| return sequence_number_; | |
| } | |
| } // namespace net_base. |