blob: 87b200e042197ab8ead334c9543bdc373e64ede1 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This code is derived from the 'iw' source code. The copyright and license
// of that code is as follows:
//
// Copyright (c) 2007, 2008 Johannes Berg
// Copyright (c) 2007 Andy Lutomirski
// Copyright (c) 2007 Mike Kershaw
// Copyright (c) 2008-2009 Luis R. Rodriguez
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "shill/netlink_socket.h"
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <net/if.h>
#include <netlink/attr.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/genl.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <iomanip>
#include "shill/logging.h"
namespace shill {
//
// NetlinkSocket::Callback.
//
NetlinkSocket::Callback::~Callback() {
if (cb_) {
nl_cb_put(cb_);
cb_ = NULL;
}
}
bool NetlinkSocket::Callback::Init() {
cb_ = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb_) {
LOG(ERROR) << "NULL cb_";
return false;
}
return true;
}
bool NetlinkSocket::Callback::ErrHandler(enum nl_cb_kind kind,
nl_recvmsg_err_cb_t func,
void *arg) {
int result = nl_cb_err(cb_, kind, func, arg);
if (result) {
LOG(ERROR) << "nl_cb_err returned " << result;
return false;
}
return true;
}
bool NetlinkSocket::Callback::SetHandler(enum nl_cb_type type,
enum nl_cb_kind kind,
nl_recvmsg_msg_cb_t func,
void *arg) {
int result = nl_cb_set(cb_, type, kind, func, arg);
if (result) {
LOG(ERROR) << "nl_cb_set returned " << result;
return false;
}
return true;
}
//
// NetlinkSocket.
//
NetlinkSocket::~NetlinkSocket() {
if (nl_sock_) {
nl_socket_free(nl_sock_);
nl_sock_ = NULL;
}
}
bool NetlinkSocket::Init() {
nl_sock_ = nl_socket_alloc();
if (!nl_sock_) {
LOG(ERROR) << "Failed to allocate netlink socket.";
return false;
}
if (genl_connect(nl_sock_)) {
LOG(ERROR) << "Failed to connect to generic netlink.";
return false;
}
return true;
}
uint32 NetlinkSocket::Send(struct nl_msg *message,
uint8 command,
int32 family_id) {
if (!message) {
LOG(ERROR) << "NULL |message|.";
return 0;
}
if (!nl_sock_) {
LOG(ERROR) << "Need to initialize the socket first.";
return 0;
}
// Parameters to genlmsg_put:
// @message: a pointer to a struct nl_msg *message.
// @pid: netlink pid the message is addressed to.
// @seq: sequence number.
// @family: netlink socket family (NETLINK_GENERIC for us)
// @flags netlink message flags.
// @hdrlen: Length of a user header (which we don't use)
// @cmd: netlink command.
// @version: version of communication protocol.
// genlmsg_put returns a void * pointing to the user header but we don't
// want to encourage its use outside of this object.
uint32 sequence_number = GetSequenceNumber();
if (genlmsg_put(message, NL_AUTO_PID, sequence_number, family_id,
0, 0, command, 0) == NULL) {
LOG(ERROR) << "genlmsg_put returned a NULL pointer.";
return 0;
}
SLOG(WiFi, 6) << "NL Message " << sequence_number << " ===>";
int result = nl_send_auto_complete(nl_sock_, message);
if (result < 0) {
LOG(ERROR) << "Failed call to 'nl_send_auto_complete': " << result;
return 0;
}
return sequence_number;
}
bool NetlinkSocket::DisableSequenceChecking() {
if (!nl_sock_) {
LOG(ERROR) << "NULL socket";
return false;
}
// NOTE: can't use nl_socket_disable_seq_check(); it's not in this version
// of the library.
int result = nl_socket_modify_cb(nl_sock_, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
NetlinkSocket::IgnoreSequenceCheck, NULL);
if (result) {
LOG(ERROR) << "Failed call to nl_socket_modify_cb: " << result;
return false;
}
return true;
}
int NetlinkSocket::GetFd() const {
if (!nl_sock_) {
LOG(ERROR) << "NULL socket";
return -1;
}
return nl_socket_get_fd(nl_sock_);
}
bool NetlinkSocket::GetMessages() {
// TODO(wdg): make this non-blocking.
// Blocks until a message is available. When that happens, the message is
// read and passed to the default callback (i.e., the one set with
// NetlinkSocket::SetNetlinkCallback).
int result = nl_recvmsgs_default(nl_sock_);
if (result < 0) {
LOG(ERROR) << "Failed call to nl_recvmsgs_default: " << strerror(-result)
<< " (" << result << ")";
return false;
}
return true;
}
bool NetlinkSocket::GetMessagesUsingCallback(
NetlinkSocket::Callback *on_netlink_data) {
if (!on_netlink_data || !on_netlink_data->cb_)
return GetMessages(); // Default to generic callback.
int result = nl_recvmsgs(nl_sock_, on_netlink_data->cb_);
if (result < 0) {
LOG(ERROR) << "Failed call to nl_recvmsgs: " << strerror(-result)
<< " (" << result << ")";
return false;
}
return true;
}
unsigned int NetlinkSocket::GetSequenceNumber() {
unsigned int number = nl_socket_use_seq(nl_sock_);
if (number == 0) {
number = nl_socket_use_seq(nl_sock_);
}
if (number == 0) {
LOG(WARNING) << "Couldn't get non-zero sequence number";
number = 1;
}
return number;
}
bool NetlinkSocket::SetNetlinkCallback(nl_recvmsg_msg_cb_t on_netlink_data,
void *callback_parameter) {
if (!nl_sock_) {
LOG(ERROR) << "NULL socket";
return false;
}
int result = nl_socket_modify_cb(nl_sock_, NL_CB_VALID, NL_CB_CUSTOM,
on_netlink_data, callback_parameter);
if (result) {
LOG(ERROR) << "nl_socket_modify_cb returned " << strerror(-result)
<< " (" << result << ")";
return false;
}
return true;
}
int NetlinkSocket::IgnoreSequenceCheck(struct nl_msg *ignored_msg,
void *ignored_arg) {
return NL_OK; // Proceed.
}
} // namespace shill.