// // Copyright (C) 2012 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 "shill/net/netlink_socket.h" #include <string> #include <linux/if_packet.h> #include <linux/netlink.h> #include <sys/socket.h> #include <base/logging.h> #include "shill/net/netlink_message.h" #include "shill/net/sockets.h" // This is from a version of linux/socket.h that we don't have. #define SOL_NETLINK 270 namespace shill { // Keep this large enough to avoid overflows on IPv6 SNM routing update spikes const int NetlinkSocket::kReceiveBufferSize = 512 * 1024; NetlinkSocket::NetlinkSocket() : sequence_number_(0), file_descriptor_(-1) {} NetlinkSocket::~NetlinkSocket() { if (sockets_ && (file_descriptor_ >= 0)) { sockets_->Close(file_descriptor_); } } bool NetlinkSocket::Init() { // Allows for a test to set |sockets_| before calling |Init|. if (sockets_) { LOG(INFO) << "|sockets_| already has a value -- this must be a test."; } else { sockets_.reset(new Sockets); } // The following is stolen directly from RTNLHandler. // TODO(wdg): refactor this and RTNLHandler together to use common code. // crbug.com/221940 file_descriptor_ = sockets_->Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC); if (file_descriptor_ < 0) { LOG(ERROR) << "Failed to open netlink socket"; return false; } if (sockets_->SetReceiveBuffer(file_descriptor_, kReceiveBufferSize)) { LOG(ERROR) << "Failed to increase receive buffer size"; } struct sockaddr_nl addr; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; if (sockets_->Bind(file_descriptor_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) { sockets_->Close(file_descriptor_); file_descriptor_ = -1; LOG(ERROR) << "Netlink socket bind failed"; return false; } VLOG(2) << "Netlink socket started"; return true; } bool NetlinkSocket::RecvMessage(ByteString* message) { if (!message) { LOG(ERROR) << "Null |message|"; return false; } // Determine the amount of data currently waiting. const size_t kDummyReadByteCount = 1; ByteString dummy_read(kDummyReadByteCount); ssize_t result; result = sockets_->RecvFrom( file_descriptor_, dummy_read.GetData(), dummy_read.GetLength(), MSG_TRUNC | MSG_PEEK, nullptr, nullptr); if (result < 0) { PLOG(ERROR) << "Socket recvfrom failed."; return false; } // Read the data that was waiting when we did our previous read. message->Resize(result); result = sockets_->RecvFrom( file_descriptor_, message->GetData(), message->GetLength(), 0, nullptr, nullptr); if (result < 0) { PLOG(ERROR) << "Second socket recvfrom failed."; return false; } return true; } bool NetlinkSocket::SendMessage(const ByteString& out_msg) { ssize_t result = sockets_->Send(file_descriptor(), out_msg.GetConstData(), out_msg.GetLength(), 0); if (!result) { PLOG(ERROR) << "Send failed."; return false; } if (result != static_cast<ssize_t>(out_msg.GetLength())) { LOG(ERROR) << "Only sent " << result << " bytes out of " << out_msg.GetLength() << "."; return false; } return true; } bool NetlinkSocket::SubscribeToEvents(uint32_t group_id) { int err = setsockopt(file_descriptor_, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group_id, sizeof(group_id)); if (err < 0) { PLOG(ERROR) << "setsockopt didn't work."; return false; } return true; } uint32_t NetlinkSocket::GetSequenceNumber() { if (++sequence_number_ == NetlinkMessage::kBroadcastSequenceNumber) ++sequence_number_; return sequence_number_; } } // namespace shill.