blob: b2e21e5fefbc93c80d5bd5c5d35c3528524e87bb [file] [log] [blame]
// Copyright (c) 2013 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.
#ifndef SHILL_NET_NETLINK_MESSAGE_H_
#define SHILL_NET_NETLINK_MESSAGE_H_
#include <linux/netlink.h>
#include <map>
#include <string>
#include <base/bind.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST.
#include "shill/net/byte_string.h"
#include "shill/shill_export.h"
struct nlmsghdr;
namespace shill {
// Netlink messages are sent over netlink sockets to talk between user-space
// programs (like shill) and kernel modules (like the cfg80211 module). Each
// kernel module that talks netlink potentially adds its own family header to
// the nlmsghdr (the top-level netlink message header) and, potentially, uses a
// different payload format. The NetlinkMessage class represents that which
// is common between the different types of netlink message.
//
// The common portions of Netlink Messages start with a |nlmsghdr|. Those
// messages look something like the following (the functions, macros, and
// datatypes described are provided by libnl -- see also
// http://www.infradead.org/~tgr/libnl/doc/core.html):
//
// |<--------------nlmsg_total_size()----------->|
// | |<------nlmsg_datalen()-------------->|
// | | |
// -----+-----+-+-----------------------------------+-+----
// ... | | | netlink payload | |
// | | +------------+-+--------------------+ |
// | nl | | | | | | nl
// | msg |p| (optional) |p| |p| msg ...
// | hdr |a| family |a| family payload |a| hdr
// | |d| header |d| |d|
// | | | | | | |
// -----+-----+-+------------+-+--------------------+-+----
// ^
// |
// +-- nlmsg_data()
//
// All NetlinkMessages sent to the kernel need a valid message type (which is
// found in the nlmsghdr structure) and all NetlinkMessages received from the
// kernel have a valid message type. Some message types (NLMSG_NOOP,
// NLMSG_ERROR, and GENL_ID_CTRL, for example) are allocated statically; for
// those, the |message_type_| is assigned directly.
//
// Other message types ("nl80211", for example), are assigned by the kernel
// dynamically. To get the message type, pass a closure to assign the
// message_type along with the sting to NetlinkManager::GetFamily:
//
// nl80211_type = netlink_manager->GetFamily(Nl80211Message::kMessageType);
//
// Do all of this before you start to create NetlinkMessages so that
// NetlinkMessage can be instantiated with a valid |message_type_|.
class SHILL_EXPORT NetlinkMessage {
public:
static const uint32_t kBroadcastSequenceNumber;
static const uint16_t kIllegalMessageType;
explicit NetlinkMessage(uint16_t message_type) :
flags_(0), message_type_(message_type),
sequence_number_(kBroadcastSequenceNumber) {}
virtual ~NetlinkMessage() {}
// Returns a string of bytes representing the message (with it headers) and
// any necessary padding. These bytes are appropriately formatted to be
// written to a netlink socket.
virtual ByteString Encode(uint32_t sequence_number) = 0;
// Initializes the |NetlinkMessage| from a complete and legal message
// (potentially received from the kernel via a netlink socket).
virtual bool InitFromNlmsg(const nlmsghdr *msg);
uint16_t message_type() const { return message_type_; }
void AddFlag(uint16_t new_flag) { flags_ |= new_flag; }
void AddAckFlag() { flags_ |= NLM_F_ACK; }
uint16_t flags() const { return flags_; }
uint32_t sequence_number() const { return sequence_number_; }
// Logs the message. Allows a different log level (presumably more
// stringent) for the body of the message than the header.
virtual void Print(int header_log_level, int detail_log_level) const = 0;
// Logs the message's raw bytes (with minimal interpretation).
static void PrintBytes(int log_level, const unsigned char *buf,
size_t num_bytes);
protected:
friend class NetlinkManagerTest;
FRIEND_TEST(NetlinkManagerTest, NL80211_CMD_NOTIFY_CQM);
// Returns a string of bytes representing an |nlmsghdr|, filled-in, and its
// padding.
virtual ByteString EncodeHeader(uint32_t sequence_number);
// Reads the |nlmsghdr| and removes it from |input|.
virtual bool InitAndStripHeader(ByteString *input);
uint16_t flags_;
uint16_t message_type_;
uint32_t sequence_number_;
private:
DISALLOW_COPY_AND_ASSIGN(NetlinkMessage);
};
// The Error and Ack messages are received from the kernel and are combined,
// here, because they look so much alike (the only difference is that the
// error code is 0 for the Ack messages). Error messages are received from
// the kernel in response to a sent message when there's a problem (such as
// a malformed message or a busy kernel module). Ack messages are received
// from the kernel when a sent message has the NLM_F_ACK flag set, indicating
// that an Ack is requested.
class SHILL_EXPORT ErrorAckMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
ErrorAckMessage() : NetlinkMessage(kMessageType), error_(0) {}
explicit ErrorAckMessage(uint32_t err)
: NetlinkMessage(kMessageType), error_(err) {}
static uint16_t GetMessageType() { return kMessageType; }
virtual bool InitFromNlmsg(const nlmsghdr *const_msg);
virtual ByteString Encode(uint32_t sequence_number);
virtual void Print(int header_log_level, int detail_log_level) const;
std::string ToString() const;
uint32_t error() const { return -error_; }
private:
uint32_t error_;
DISALLOW_COPY_AND_ASSIGN(ErrorAckMessage);
};
class SHILL_EXPORT NoopMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
NoopMessage() : NetlinkMessage(kMessageType) {}
static uint16_t GetMessageType() { return kMessageType; }
virtual ByteString Encode(uint32_t sequence_number);
virtual void Print(int header_log_level, int detail_log_level) const;
std::string ToString() const { return "<NOOP>"; }
private:
DISALLOW_COPY_AND_ASSIGN(NoopMessage);
};
class SHILL_EXPORT DoneMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
DoneMessage() : NetlinkMessage(kMessageType) {}
static uint16_t GetMessageType() { return kMessageType; }
virtual ByteString Encode(uint32_t sequence_number);
virtual void Print(int header_log_level, int detail_log_level) const;
std::string ToString() const { return "<DONE with multipart message>"; }
private:
DISALLOW_COPY_AND_ASSIGN(DoneMessage);
};
class SHILL_EXPORT OverrunMessage : public NetlinkMessage {
public:
static const uint16_t kMessageType;
OverrunMessage() : NetlinkMessage(kMessageType) {}
static uint16_t GetMessageType() { return kMessageType; }
virtual ByteString Encode(uint32_t sequence_number);
virtual void Print(int header_log_level, int detail_log_level) const;
std::string ToString() const { return "<OVERRUN - data lost>"; }
private:
DISALLOW_COPY_AND_ASSIGN(OverrunMessage);
};
class SHILL_EXPORT UnknownMessage : public NetlinkMessage {
public:
UnknownMessage(uint16_t message_type, ByteString message_body) :
NetlinkMessage(message_type), message_body_(message_body) {}
virtual ByteString Encode(uint32_t sequence_number);
virtual void Print(int header_log_level, int detail_log_level) const;
private:
ByteString message_body_;
DISALLOW_COPY_AND_ASSIGN(UnknownMessage);
};
//
// Factory class.
//
class SHILL_EXPORT NetlinkMessageFactory {
public:
typedef base::Callback<NetlinkMessage *(const nlmsghdr *msg)> FactoryMethod;
NetlinkMessageFactory() {}
// Adds a message factory for a specific message_type. Intended to be used
// at initialization.
bool AddFactoryMethod(uint16_t message_type, FactoryMethod factory);
// Ownership of the message is passed to the caller and, as such, he should
// delete it.
NetlinkMessage *CreateMessage(const nlmsghdr *msg) const;
private:
std::map<uint16_t, FactoryMethod> factories_;
DISALLOW_COPY_AND_ASSIGN(NetlinkMessageFactory);
};
} // namespace shill
#endif // SHILL_NET_NETLINK_MESSAGE_H_