| // |
| // 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. |