blob: d791702b876243797600e8cdd7668d7afe7cb708 [file] [log] [blame]
Wade Guthrie0d438532012-05-18 14:18:50 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Wade Guthrie0d438532012-05-18 14:18:50 -07004
5#include "shill/netlink_socket.h"
6
Wade Guthriecc53f232013-03-05 13:22:23 -08007#include <linux/if_packet.h>
Wade Guthrie0ae4b8e2013-04-10 16:49:15 -07008#include <linux/netlink.h>
Wade Guthriecc53f232013-03-05 13:22:23 -08009#include <sys/socket.h>
Wade Guthrie0d438532012-05-18 14:18:50 -070010
Christopher Wiley393b93f2012-11-08 17:30:58 -080011#include "shill/logging.h"
Wade Guthrie0ae4b8e2013-04-10 16:49:15 -070012#include "shill/netlink_message.h"
Wade Guthriecc53f232013-03-05 13:22:23 -080013#include "shill/sockets.h"
repo syncdc085c82012-12-28 08:54:41 -080014
Wade Guthriebee87c22013-03-06 11:00:46 -080015// This is from a version of linux/socket.h that we don't have.
16#define SOL_NETLINK 270
17
Wade Guthrie0d438532012-05-18 14:18:50 -070018namespace shill {
19
Wade Guthriecc53f232013-03-05 13:22:23 -080020// Keep this large enough to avoid overflows on IPv6 SNM routing update spikes
21const int NetlinkSocket::kReceiveBufferSize = 512 * 1024;
22
23NetlinkSocket::NetlinkSocket() : sequence_number_(0), file_descriptor_(-1) {}
24
Wade Guthrie0d438532012-05-18 14:18:50 -070025NetlinkSocket::~NetlinkSocket() {
Wade Guthriecc53f232013-03-05 13:22:23 -080026 if (sockets_ && (file_descriptor_ >= 0)) {
27 sockets_->Close(file_descriptor_);
Wade Guthrie0d438532012-05-18 14:18:50 -070028 }
29}
30
31bool NetlinkSocket::Init() {
Wade Guthriecc53f232013-03-05 13:22:23 -080032 // Allows for a test to set |sockets_| before calling |Init|.
33 if (sockets_) {
34 LOG(INFO) << "|sockets_| already has a value -- this must be a test.";
35 } else {
36 sockets_.reset(new Sockets);
37 }
38
39 // The following is stolen directly from RTNLHandler.
40 // TODO(wdg): refactor this and RTNLHandler together to use common code.
41 // crosbug.com/39842
42
43 file_descriptor_ = sockets_->Socket(PF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC);
44 if (file_descriptor_ < 0) {
45 LOG(ERROR) << "Failed to open netlink socket";
Wade Guthrie0d438532012-05-18 14:18:50 -070046 return false;
47 }
48
Wade Guthriecc53f232013-03-05 13:22:23 -080049 if (sockets_->SetReceiveBuffer(file_descriptor_, kReceiveBufferSize)) {
50 LOG(ERROR) << "Failed to increase receive buffer size";
51 }
52
53 struct sockaddr_nl addr;
54 memset(&addr, 0, sizeof(addr));
55 addr.nl_family = AF_NETLINK;
56
57 if (sockets_->Bind(file_descriptor_,
58 reinterpret_cast<struct sockaddr *>(&addr),
59 sizeof(addr)) < 0) {
60 sockets_->Close(file_descriptor_);
61 file_descriptor_ = -1;
62 LOG(ERROR) << "Netlink socket bind failed";
63 return false;
64 }
65 SLOG(WiFi, 2) << "Netlink socket started";
66
67 return true;
68}
69
70bool NetlinkSocket::RecvMessage(ByteString *message) {
71 if (!message) {
72 LOG(ERROR) << "Null |message|";
Wade Guthrie0d438532012-05-18 14:18:50 -070073 return false;
74 }
75
Wade Guthriecc53f232013-03-05 13:22:23 -080076 // Determine the amount of data currently waiting.
77 const size_t kDummyReadByteCount = 1;
78 ByteString dummy_read(kDummyReadByteCount);
79 ssize_t result;
80 result = sockets_->RecvFrom(
81 file_descriptor_,
82 dummy_read.GetData(),
83 dummy_read.GetLength(),
84 MSG_TRUNC | MSG_PEEK,
85 NULL,
86 NULL);
87 if (result < 0) {
88 PLOG(ERROR) << "Socket recvfrom failed.";
89 return false;
90 }
91
92 // Read the data that was waiting when we did our previous read.
93 message->Resize(result);
94 result = sockets_->RecvFrom(
95 file_descriptor_,
96 message->GetData(),
97 message->GetLength(),
98 0,
99 NULL,
100 NULL);
101 if (result < 0) {
102 PLOG(ERROR) << "Second socket recvfrom failed.";
103 return false;
104 }
Wade Guthrie0d438532012-05-18 14:18:50 -0700105 return true;
106}
107
Wade Guthriebdcdaa72013-03-04 12:47:12 -0800108bool NetlinkSocket::SendMessage(const ByteString &out_msg) {
Wade Guthriecc53f232013-03-05 13:22:23 -0800109 ssize_t result = sockets_->Send(file_descriptor(), out_msg.GetConstData(),
110 out_msg.GetLength(), 0);
repo syncdc085c82012-12-28 08:54:41 -0800111 if (!result) {
112 PLOG(ERROR) << "Send failed.";
113 return false;
Wade Guthrie5d53d492012-11-07 09:53:31 -0800114 }
Wade Guthriecc53f232013-03-05 13:22:23 -0800115 if (result != static_cast<ssize_t>(out_msg.GetLength())) {
116 LOG(ERROR) << "Only sent " << result << " bytes out of "
117 << out_msg.GetLength() << ".";
Wade Guthrie0d438532012-05-18 14:18:50 -0700118 return false;
119 }
120
121 return true;
122}
123
Wade Guthriebee87c22013-03-06 11:00:46 -0800124bool NetlinkSocket::SubscribeToEvents(uint32_t group_id) {
125 int err = setsockopt(file_descriptor_, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
126 &group_id, sizeof(group_id));
127 if (err < 0) {
128 PLOG(ERROR) << "setsockopt didn't work.";
129 return false;
130 }
131 return true;
132}
133
Wade Guthriecc53f232013-03-05 13:22:23 -0800134uint32_t NetlinkSocket::GetSequenceNumber() {
135 if (++sequence_number_ == NetlinkMessage::kBroadcastSequenceNumber)
136 ++sequence_number_;
137 return sequence_number_;
Wade Guthrie0d438532012-05-18 14:18:50 -0700138}
139
140} // namespace shill.