openr/nl/NetlinkMessageBase.h (63 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <memory> #include <queue> #include <limits.h> #include <linux/lwtunnel.h> #include <linux/mpls.h> #include <linux/rtnetlink.h> #include <sys/socket.h> #include <sys/types.h> #include <folly/futures/Future.h> #include <openr/nl/NetlinkTypes.h> namespace openr::fbnl { constexpr uint16_t kMaxNlPayloadSize{4096}; /* * Data structure representing a netlink message, either to be sent or received. * It wraps `struct nlmsghdr` and provides buffer for appending message payload. * Further message payload in turn can contain multiple attributes and * sub-attributes depending on the message type. * * Aim of the message is to faciliate serialization and deserialization of * C++ object (application) to/from bytes (kernel). * * Maximum size of message is limited by `kMaxNlPayloadSize` parameter. */ /* * For netlink reference: * * https://man7.org/linux/man-pages/man7/netlink.7.html * * Synopsis: * * netlink_socket = socket(AF_NETLINK, socket_type, netlink_family); * * Description: * Netlink is used to transfer information between the kernel and user-space * processes. It consists of a standard sockets-based interface for user space * processes and an internal kernel API for kernel modules. * * Netlink is a datagram-oriented service. Both `SOCK_RAW` and `SOCK_DGRAM` * are valid values for `socket_type`. However, the netlink protocol does not * distinguish between datagram and raw sockets. * * `netlink_family` selects the kernel module or netlink group to communicate * with. Especially for Open/R's interest: * * NETLINK_ROUTE * Receives routing and link updates and may be used to * modify the routing tables (IPv4, IPv6 and MPLS), IP * addresses, link parameters, neighbor setups, queueing * disciplines, traffic classes, and packet classifiers. */ /* * For rtnetlink reference: * * https://man7.org/linux/man-pages/man7/rtnetlink.7.html * * Synopsis: * * rtnetlink_socket = socket(AF_NETLINK, socket_type, NETLINK_ROUTE); * * Description: * Rtnetlink allows the kernel's routing tables to be read and altered. * * Message Types: * - RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK * - RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR * - RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE * - RTM_NEWNEIGH, RTM_DELNEIGH, RTM_GETNEIGH * - etc. */ class NetlinkMessageBase { public: NetlinkMessageBase(); virtual ~NetlinkMessageBase(); // construct message with type explicit NetlinkMessageBase(int type); // get pointer to NLMSG Header struct nlmsghdr* getMessagePtr(); // get underlying nlmsg_type uint16_t getMessageType() const; // get current length uint32_t getDataLength() const; // Buffer to create message std::array<char, kMaxNlPayloadSize> msg = {}; /** * APIs for accumulating objects of `GET_<>` request. These APIs are invoked * when an object is received from kernel in-response to this netlink-message. * Sub-classes must override them and define behavior for them depending on * the request type they make. * * e.g. GET_ROUTE request will invoke `rcvdRoute(..)` for each route received * from kernel. At the end `setReturnStatus(..)` will be invoked. */ virtual void rcvdRoute(Route&& /* route */) { CHECK(false) << "Must be implemented by subclass"; } virtual void rcvdLink(Link&& /* link */) { CHECK(false) << "Must be implemented by subclass"; } virtual void rcvdNeighbor(Neighbor&& /* neighbor */) { CHECK(false) << "Must be implemented by subclass"; } virtual void rcvdIfAddress(IfAddress&& /* ifAddr */) { CHECK(false) << "Must be implemented by subclass"; } virtual void rcvdRule(Rule&& /* rule */) { CHECK(false) << "Must be implemented by subclass"; } /** * Get SemiFuture associated with the the associated netlink request. Upon * receipt of the ack from kernel, the value will be set. */ folly::SemiFuture<int> getSemiFuture(); /** * Set the return value of the netlink request. Invoke this on receipt of the * ack. This must be invoked before class is destroyed. * * Sub-classes can override this method to define more specific behavior * on completion of the request. For e.g. `GET_<OBJ>` requests on completion * can fulfil the `Promise<vector<OBJ>>` */ virtual void setReturnStatus(int status); std::chrono::steady_clock::time_point getCreateTs() const { return createTs_; } // parse IP address static folly::Expected<folly::IPAddress, folly::IPAddressFormatError> parseIp( const struct rtattr* ipAttr, unsigned char family); protected: /* * Add TLV(Type-Length-Value) attributes and update the length field in * NLMSG header. * * @params: type => data type * data => data value * length => data length * * @return: * - EVNOBUFS: if enough buffer is not available * - 0: on success * - System error code: if failed to add attributes */ int addAttributes(int type, const char* const data, uint32_t len); /* * Add a sub RTA(Route Attribures) inside an RTA. The length of sub RTA will * not be added into the NLMSG header, but will be added to the parent RTA. * * @params: rta => route attribute struct ptr * type => data type * data => data value * length => data length * * @return: route attribute struct ptr */ struct rtattr* addSubAttributes( struct rtattr* rta, int type, const void* data, uint32_t len) const; // pointer to the netlink message header struct nlmsghdr* msghdr_{nullptr}; private: // disable copy, assign constructores NetlinkMessageBase(NetlinkMessageBase const&) = delete; NetlinkMessageBase& operator=(NetlinkMessageBase const&) = delete; // Promise to relay the status code received from kernel folly::Promise<int> promise_; // Timestamp when message object was created const std::chrono::steady_clock::time_point createTs_{ std::chrono::steady_clock::now()}; }; } // namespace openr::fbnl