blob: 87b200e042197ab8ead334c9543bdc373e64ede1 [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.
4//
5// This code is derived from the 'iw' source code. The copyright and license
6// of that code is as follows:
7//
8// Copyright (c) 2007, 2008 Johannes Berg
9// Copyright (c) 2007 Andy Lutomirski
10// Copyright (c) 2007 Mike Kershaw
11// Copyright (c) 2008-2009 Luis R. Rodriguez
12//
13// Permission to use, copy, modify, and/or distribute this software for any
14// purpose with or without fee is hereby granted, provided that the above
15// copyright notice and this permission notice appear in all copies.
16//
17// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25#include "shill/netlink_socket.h"
26
27#include <ctype.h>
28#include <errno.h>
Wade Guthrieb1ec8602012-10-18 17:26:14 -070029#include <string.h>
Wade Guthrie0d438532012-05-18 14:18:50 -070030
31#include <net/if.h>
32#include <netlink/attr.h>
33#include <netlink/genl/ctrl.h>
34#include <netlink/genl/family.h>
35#include <netlink/genl/genl.h>
36#include <netlink/msg.h>
37#include <netlink/netlink.h>
38
39#include <iomanip>
40
Christopher Wiley393b93f2012-11-08 17:30:58 -080041#include "shill/logging.h"
Wade Guthrie0d438532012-05-18 14:18:50 -070042
43namespace shill {
44
45//
46// NetlinkSocket::Callback.
47//
48
49NetlinkSocket::Callback::~Callback() {
50 if (cb_) {
51 nl_cb_put(cb_);
52 cb_ = NULL;
53 }
54}
55
56bool NetlinkSocket::Callback::Init() {
57 cb_ = nl_cb_alloc(NL_CB_DEFAULT);
58 if (!cb_) {
59 LOG(ERROR) << "NULL cb_";
60 return false;
61 }
62 return true;
63}
64
65bool NetlinkSocket::Callback::ErrHandler(enum nl_cb_kind kind,
66 nl_recvmsg_err_cb_t func,
67 void *arg) {
68 int result = nl_cb_err(cb_, kind, func, arg);
69 if (result) {
70 LOG(ERROR) << "nl_cb_err returned " << result;
71 return false;
72 }
73 return true;
74}
75
76bool NetlinkSocket::Callback::SetHandler(enum nl_cb_type type,
77 enum nl_cb_kind kind,
78 nl_recvmsg_msg_cb_t func,
79 void *arg) {
80 int result = nl_cb_set(cb_, type, kind, func, arg);
81 if (result) {
82 LOG(ERROR) << "nl_cb_set returned " << result;
83 return false;
84 }
85 return true;
86}
87
88//
89// NetlinkSocket.
90//
91
92NetlinkSocket::~NetlinkSocket() {
93 if (nl_sock_) {
94 nl_socket_free(nl_sock_);
95 nl_sock_ = NULL;
96 }
97}
98
99bool NetlinkSocket::Init() {
100 nl_sock_ = nl_socket_alloc();
101 if (!nl_sock_) {
102 LOG(ERROR) << "Failed to allocate netlink socket.";
103 return false;
104 }
105
106 if (genl_connect(nl_sock_)) {
107 LOG(ERROR) << "Failed to connect to generic netlink.";
108 return false;
109 }
110
111 return true;
112}
113
Christopher Wiley393b93f2012-11-08 17:30:58 -0800114uint32 NetlinkSocket::Send(struct nl_msg *message,
115 uint8 command,
116 int32 family_id) {
Wade Guthrie5d53d492012-11-07 09:53:31 -0800117 if (!message) {
118 LOG(ERROR) << "NULL |message|.";
Christopher Wiley393b93f2012-11-08 17:30:58 -0800119 return 0;
Wade Guthrie5d53d492012-11-07 09:53:31 -0800120 }
121
122 if (!nl_sock_) {
123 LOG(ERROR) << "Need to initialize the socket first.";
Christopher Wiley393b93f2012-11-08 17:30:58 -0800124 return 0;
Wade Guthrie5d53d492012-11-07 09:53:31 -0800125 }
126
Christopher Wiley393b93f2012-11-08 17:30:58 -0800127 // Parameters to genlmsg_put:
128 // @message: a pointer to a struct nl_msg *message.
129 // @pid: netlink pid the message is addressed to.
130 // @seq: sequence number.
131 // @family: netlink socket family (NETLINK_GENERIC for us)
132 // @flags netlink message flags.
133 // @hdrlen: Length of a user header (which we don't use)
134 // @cmd: netlink command.
135 // @version: version of communication protocol.
136 // genlmsg_put returns a void * pointing to the user header but we don't
137 // want to encourage its use outside of this object.
138
139 uint32 sequence_number = GetSequenceNumber();
140 if (genlmsg_put(message, NL_AUTO_PID, sequence_number, family_id,
141 0, 0, command, 0) == NULL) {
142 LOG(ERROR) << "genlmsg_put returned a NULL pointer.";
143 return 0;
144 }
145
146 SLOG(WiFi, 6) << "NL Message " << sequence_number << " ===>";
147
Wade Guthrie5d53d492012-11-07 09:53:31 -0800148 int result = nl_send_auto_complete(nl_sock_, message);
149 if (result < 0) {
150 LOG(ERROR) << "Failed call to 'nl_send_auto_complete': " << result;
Christopher Wiley393b93f2012-11-08 17:30:58 -0800151 return 0;
Wade Guthrie5d53d492012-11-07 09:53:31 -0800152 }
Christopher Wiley393b93f2012-11-08 17:30:58 -0800153 return sequence_number;
Wade Guthrie5d53d492012-11-07 09:53:31 -0800154}
155
156
Wade Guthrie0d438532012-05-18 14:18:50 -0700157bool NetlinkSocket::DisableSequenceChecking() {
158 if (!nl_sock_) {
159 LOG(ERROR) << "NULL socket";
160 return false;
161 }
162
163 // NOTE: can't use nl_socket_disable_seq_check(); it's not in this version
164 // of the library.
165 int result = nl_socket_modify_cb(nl_sock_, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
166 NetlinkSocket::IgnoreSequenceCheck, NULL);
167 if (result) {
168 LOG(ERROR) << "Failed call to nl_socket_modify_cb: " << result;
169 return false;
170 }
171
172 return true;
173}
174
175int NetlinkSocket::GetFd() const {
176 if (!nl_sock_) {
177 LOG(ERROR) << "NULL socket";
178 return -1;
179 }
180 return nl_socket_get_fd(nl_sock_);
181}
182
183bool NetlinkSocket::GetMessages() {
184 // TODO(wdg): make this non-blocking.
185 // Blocks until a message is available. When that happens, the message is
186 // read and passed to the default callback (i.e., the one set with
187 // NetlinkSocket::SetNetlinkCallback).
188 int result = nl_recvmsgs_default(nl_sock_);
189 if (result < 0) {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700190 LOG(ERROR) << "Failed call to nl_recvmsgs_default: " << strerror(-result)
191 << " (" << result << ")";
Wade Guthrie0d438532012-05-18 14:18:50 -0700192 return false;
193 }
194 return true;
195}
196
197bool NetlinkSocket::GetMessagesUsingCallback(
198 NetlinkSocket::Callback *on_netlink_data) {
199 if (!on_netlink_data || !on_netlink_data->cb_)
200 return GetMessages(); // Default to generic callback.
201
202 int result = nl_recvmsgs(nl_sock_, on_netlink_data->cb_);
203 if (result < 0) {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700204 LOG(ERROR) << "Failed call to nl_recvmsgs: " << strerror(-result)
205 << " (" << result << ")";
Wade Guthrie0d438532012-05-18 14:18:50 -0700206 return false;
207 }
208 return true;
209}
210
Wade Guthrie5d3d6de2012-11-02 11:08:34 -0700211unsigned int NetlinkSocket::GetSequenceNumber() {
212 unsigned int number = nl_socket_use_seq(nl_sock_);
213 if (number == 0) {
214 number = nl_socket_use_seq(nl_sock_);
215 }
216 if (number == 0) {
217 LOG(WARNING) << "Couldn't get non-zero sequence number";
218 number = 1;
219 }
220 return number;
221}
222
Wade Guthrie0d438532012-05-18 14:18:50 -0700223bool NetlinkSocket::SetNetlinkCallback(nl_recvmsg_msg_cb_t on_netlink_data,
224 void *callback_parameter) {
225 if (!nl_sock_) {
226 LOG(ERROR) << "NULL socket";
227 return false;
228 }
229
230 int result = nl_socket_modify_cb(nl_sock_, NL_CB_VALID, NL_CB_CUSTOM,
231 on_netlink_data, callback_parameter);
232 if (result) {
Wade Guthrieb1ec8602012-10-18 17:26:14 -0700233 LOG(ERROR) << "nl_socket_modify_cb returned " << strerror(-result)
234 << " (" << result << ")";
Wade Guthrie0d438532012-05-18 14:18:50 -0700235 return false;
236 }
237 return true;
238}
239
240int NetlinkSocket::IgnoreSequenceCheck(struct nl_msg *ignored_msg,
241 void *ignored_arg) {
242 return NL_OK; // Proceed.
243}
244
245} // namespace shill.