blob: 4e8adc754a0359df7e86c997a7b690b8e56c8b63 [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.
#include "shill/generic_netlink_message.h"
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <base/bind.h>
#include <base/stringprintf.h>
#include "shill/logging.h"
#include "shill/netlink_attribute.h"
using base::Bind;
using base::StringPrintf;
namespace shill {
ByteString GenericNetlinkMessage::EncodeHeader(uint32_t sequence_number) {
// Build nlmsghdr.
ByteString result(NetlinkMessage::EncodeHeader(sequence_number));
if (result.GetLength() == 0) {
LOG(ERROR) << "Couldn't encode message header.";
return result;
}
// Build and append the genl message header.
genlmsghdr genl_header;
genl_header.cmd = command();
genl_header.version = 1;
genl_header.reserved = 0;
ByteString genl_header_string(
reinterpret_cast<unsigned char *>(&genl_header), sizeof(genl_header));
size_t genlmsghdr_with_pad = NLMSG_ALIGN(sizeof(genl_header));
genl_header_string.Resize(genlmsghdr_with_pad); // Zero-fill.
nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
pheader->nlmsg_len += genlmsghdr_with_pad;
result.Append(genl_header_string);
return result;
}
ByteString GenericNetlinkMessage::Encode(uint32_t sequence_number) {
ByteString result(EncodeHeader(sequence_number));
if (result.GetLength() == 0) {
LOG(ERROR) << "Couldn't encode message header.";
return result;
}
// Build and append attributes (padding is included by
// AttributeList::Encode).
ByteString attribute_string = attributes_->Encode();
// Need to re-calculate |header| since |Append|, above, moves the data.
nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
pheader->nlmsg_len += attribute_string.GetLength();
result.Append(attribute_string);
return result;
}
bool GenericNetlinkMessage::InitAndStripHeader(ByteString *input) {
if (!input) {
LOG(ERROR) << "NULL input";
return false;
}
if (!NetlinkMessage::InitAndStripHeader(input)) {
return false;
}
// Read the genlmsghdr.
genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(input->GetData());
if (command_ != gnlh->cmd) {
LOG(WARNING) << "This object thinks it's a " << command_
<< " but the message thinks it's a " << gnlh->cmd;
}
// Strip the genlmsghdr.
input->RemovePrefix(NLMSG_ALIGN(sizeof(struct genlmsghdr)));
return true;
}
void GenericNetlinkMessage::Print(int header_log_level,
int detail_log_level) const {
SLOG(WiFi, header_log_level) << StringPrintf("Message %s (%d)",
command_string(),
command());
attributes_->Print(detail_log_level, 1);
}
// Control Message
const uint16_t ControlNetlinkMessage::kMessageType = GENL_ID_CTRL;
bool ControlNetlinkMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
if (!const_msg) {
LOG(ERROR) << "Null |msg| parameter";
return false;
}
ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
const_msg->nlmsg_len);
if (!InitAndStripHeader(&message)) {
return false;
}
// Attributes.
// Parse the attributes from the nl message payload into the 'tb' array.
nlattr *tb[CTRL_ATTR_MAX + 1];
nla_parse(tb, CTRL_ATTR_MAX,
reinterpret_cast<nlattr *>(message.GetData()), message.GetLength(),
NULL);
for (int i = 0; i < CTRL_ATTR_MAX + 1; ++i) {
if (tb[i]) {
attributes_->CreateAndInitAttribute(
i, tb[i], Bind(&NetlinkAttribute::NewControlAttributeFromId));
}
}
return true;
}
// Specific Control types.
const uint8_t NewFamilyMessage::kCommand = CTRL_CMD_NEWFAMILY;
const char NewFamilyMessage::kCommandString[] = "CTRL_CMD_NEWFAMILY";
const uint8_t GetFamilyMessage::kCommand = CTRL_CMD_GETFAMILY;
const char GetFamilyMessage::kCommandString[] = "CTRL_CMD_GETFAMILY";
GetFamilyMessage::GetFamilyMessage()
: ControlNetlinkMessage(kCommand, kCommandString) {
attributes()->CreateStringAttribute(CTRL_ATTR_FAMILY_NAME,
"CTRL_ATTR_FAMILY_NAME");
}
// static
NetlinkMessage *ControlNetlinkMessage::CreateMessage(
const nlmsghdr *const_msg) {
if (!const_msg) {
LOG(ERROR) << "NULL |const_msg| parameter";
return NULL;
}
// Casting away constness since, while nlmsg_data doesn't change its
// parameter, it also doesn't declare its paramenter as const.
nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
void *payload = nlmsg_data(msg);
genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
switch (gnlh->cmd) {
case NewFamilyMessage::kCommand:
return new NewFamilyMessage();
case GetFamilyMessage::kCommand:
return new GetFamilyMessage();
default:
LOG(WARNING) << "Unknown/unhandled netlink control message " << gnlh->cmd;
return new UnknownControlMessage(gnlh->cmd);
break;
}
return NULL;
}
} // namespace shill.