blob: c3d72c54e23f71c18361c158672158af453901a8 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/macros.h>
#include <libnl++/Buffer.h>
#include <libnl++/types.h>
#include <linux/netlink.h>
#include <string>
namespace android::nl {
class MessageFactoryBase {
protected:
static nlattr* add(nlmsghdr* msg, size_t maxLen, nlattrtype_t type, const void* data,
size_t dataLen);
static void closeNested(nlmsghdr* msg, nlattr* nested);
};
/**
* Wrapper around NETLINK_ROUTE messages, to build them in C++ style.
*
* \param T Message payload type (such as ifinfomsg).
* \param BUFSIZE how much space to reserve for attributes.
*/
template <class T, unsigned int BUFSIZE = 128>
class MessageFactory : private MessageFactoryBase {
struct alignas(NLMSG_ALIGNTO) Message {
nlmsghdr header;
T data;
uint8_t attributesBuffer[BUFSIZE];
};
public:
/**
* Create empty message.
*
* \param type Message type (such as RTM_NEWLINK).
* \param flags Message flags (such as NLM_F_REQUEST).
*/
MessageFactory(nlmsgtype_t type, uint16_t flags)
: header(mMessage.header), data(mMessage.data) {
mMessage.header.nlmsg_len = offsetof(Message, attributesBuffer);
mMessage.header.nlmsg_type = type;
mMessage.header.nlmsg_flags = flags;
}
/**
* Netlink message header.
*
* This is a generic Netlink header containing information such as message flags.
*/
nlmsghdr& header;
/**
* Netlink message data.
*
* This is a payload specific to a given message type.
*/
T& data;
T* operator->() { return &mMessage.data; }
/**
* Build netlink message.
*
* In fact, this operation is almost a no-op, since the factory builds the message in a single
* buffer, using native data structures.
*
* A likely failure case is when the BUFSIZE template parameter is too small to acommodate
* added attributes. In such a case, please increase this parameter.
*
* \return Netlink message or std::nullopt in case of failure.
*/
std::optional<Buffer<nlmsghdr>> build() const {
if (!mIsGood) return std::nullopt;
return {{&mMessage.header, mMessage.header.nlmsg_len}};
}
/**
* Adds an attribute of a trivially copyable type.
*
* Template specializations may extend this function for other types, such as std::string.
*
* If this method fails (i.e. due to insufficient space), a warning will be printed to the log
* and the message will be marked as bad, causing later \see build call to fail.
*
* \param type attribute type (such as IFLA_IFNAME)
* \param attr attribute data
*/
template <class A>
void add(nlattrtype_t type, const A& attr) {
add(type, &attr, sizeof(attr));
}
template <>
void add(nlattrtype_t type, const std::string& s) {
add(type, s.c_str(), s.size() + 1);
}
/** Guard class to frame nested attributes. \see addNested(nlattrtype_t). */
class [[nodiscard]] NestedGuard {
public:
NestedGuard(MessageFactory & req, nlattrtype_t type) : mReq(req), mAttr(req.add(type)) {}
~NestedGuard() { closeNested(&mReq.mMessage.header, mAttr); }
private:
MessageFactory& mReq;
nlattr* mAttr;
DISALLOW_COPY_AND_ASSIGN(NestedGuard);
};
/**
* Add nested attribute.
*
* The returned object is a guard for auto-nesting children inside the argument attribute.
* When the guard object goes out of scope, the nesting attribute is closed.
*
* Example usage nesting IFLA_CAN_BITTIMING inside IFLA_INFO_DATA, which is nested
* inside IFLA_LINKINFO:
* MessageFactory<ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
* {
* auto linkinfo = req.addNested(IFLA_LINKINFO);
* req.add(IFLA_INFO_KIND, "can");
* {
* auto infodata = req.addNested(IFLA_INFO_DATA);
* req.add(IFLA_CAN_BITTIMING, bitTimingStruct);
* }
* }
* // use req
*
* \param type attribute type (such as IFLA_LINKINFO)
*/
NestedGuard addNested(nlattrtype_t type) { return {*this, type}; }
private:
Message mMessage = {};
bool mIsGood = true;
nlattr* add(nlattrtype_t type, const void* data = nullptr, size_t len = 0) {
if (!mIsGood) return nullptr;
auto attr = MessageFactoryBase::add(&mMessage.header, sizeof(mMessage), type, data, len);
if (attr == nullptr) mIsGood = false;
return attr;
}
};
} // namespace android::nl