1//
2// Copyright (C) 2013 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "shill/net/generic_netlink_message.h"
18
19#include <base/bind.h>
20#include <base/logging.h>
21#include <base/strings/stringprintf.h>
22
23#include "shill/net/netlink_attribute.h"
24#include "shill/net/netlink_packet.h"
25
26using base::Bind;
27using base::StringPrintf;
28
29namespace shill {
30
31ByteString GenericNetlinkMessage::EncodeHeader(uint32_t sequence_number) {
32  // Build nlmsghdr.
33  ByteString result(NetlinkMessage::EncodeHeader(sequence_number));
34  if (result.GetLength() == 0) {
35    LOG(ERROR) << "Couldn't encode message header.";
36    return result;
37  }
38
39  // Build and append the genl message header.
40  genlmsghdr genl_header;
41  genl_header.cmd = command();
42  genl_header.version = 1;
43  genl_header.reserved = 0;
44
45  ByteString genl_header_string(
46      reinterpret_cast<unsigned char*>(&genl_header), sizeof(genl_header));
47  size_t genlmsghdr_with_pad = NLMSG_ALIGN(sizeof(genl_header));
48  genl_header_string.Resize(genlmsghdr_with_pad);  // Zero-fill.
49
50  nlmsghdr* pheader = reinterpret_cast<nlmsghdr*>(result.GetData());
51  pheader->nlmsg_len += genlmsghdr_with_pad;
52  result.Append(genl_header_string);
53  return result;
54}
55
56ByteString GenericNetlinkMessage::Encode(uint32_t sequence_number) {
57  ByteString result(EncodeHeader(sequence_number));
58  if (result.GetLength() == 0) {
59    LOG(ERROR) << "Couldn't encode message header.";
60    return result;
61  }
62
63  // Build and append attributes (padding is included by
64  // AttributeList::Encode).
65  ByteString attribute_string = attributes_->Encode();
66
67  // Need to re-calculate |header| since |Append|, above, moves the data.
68  nlmsghdr* pheader = reinterpret_cast<nlmsghdr*>(result.GetData());
69  pheader->nlmsg_len += attribute_string.GetLength();
70  result.Append(attribute_string);
71
72  return result;
73}
74
75bool GenericNetlinkMessage::InitAndStripHeader(NetlinkPacket* packet) {
76  if (!packet) {
77    LOG(ERROR) << "NULL packet";
78    return false;
79  }
80  if (!NetlinkMessage::InitAndStripHeader(packet)) {
81    return false;
82  }
83
84  genlmsghdr gnlh;
85  if (!packet->ConsumeData(sizeof(gnlh), &gnlh)) {
86    return false;
87  }
88
89  if (command_ != gnlh.cmd) {
90    LOG(WARNING) << "This object thinks it's a " << command_
91                 << " but the message thinks it's a " << gnlh.cmd;
92  }
93
94  return true;
95}
96
97void GenericNetlinkMessage::Print(int header_log_level,
98                                  int detail_log_level) const {
99  VLOG(header_log_level) << StringPrintf("Message %s (%d)",
100                                         command_string(),
101                                         command());
102  attributes_->Print(detail_log_level, 1);
103}
104
105// Control Message
106
107const uint16_t ControlNetlinkMessage::kMessageType = GENL_ID_CTRL;
108
109bool ControlNetlinkMessage::InitFromPacket(
110    NetlinkPacket* packet, NetlinkMessage::MessageContext context) {
111  if (!packet) {
112    LOG(ERROR) << "Null |packet| parameter";
113    return false;
114  }
115
116  if (!InitAndStripHeader(packet)) {
117    return false;
118  }
119
120  return packet->ConsumeAttributes(
121      Bind(&NetlinkAttribute::NewControlAttributeFromId), attributes_);
122}
123
124// Specific Control types.
125
126const uint8_t NewFamilyMessage::kCommand = CTRL_CMD_NEWFAMILY;
127const char NewFamilyMessage::kCommandString[] = "CTRL_CMD_NEWFAMILY";
128
129const uint8_t GetFamilyMessage::kCommand = CTRL_CMD_GETFAMILY;
130const char GetFamilyMessage::kCommandString[] = "CTRL_CMD_GETFAMILY";
131
132GetFamilyMessage::GetFamilyMessage()
133    : ControlNetlinkMessage(kCommand, kCommandString) {
134  attributes()->CreateStringAttribute(CTRL_ATTR_FAMILY_NAME,
135                                      "CTRL_ATTR_FAMILY_NAME");
136}
137
138// static
139NetlinkMessage* ControlNetlinkMessage::CreateMessage(
140    const NetlinkPacket& packet) {
141  genlmsghdr header;
142  if (!packet.GetGenlMsgHdr(&header)) {
143    LOG(ERROR) << "Could not read genl header.";
144    return nullptr;
145  }
146
147  switch (header.cmd) {
148    case NewFamilyMessage::kCommand:
149      return new NewFamilyMessage();
150    case GetFamilyMessage::kCommand:
151      return new GetFamilyMessage();
152    default:
153      LOG(WARNING) << "Unknown/unhandled netlink control message "
154                   << header.cmd;
155      return new UnknownControlMessage(header.cmd);
156      break;
157  }
158  return nullptr;
159}
160
161}  // namespace shill.
162