| #include "netlink.hpp" | 
 |  | 
 | #include <fmt/format.h> | 
 | #include <linux/netlink.h> | 
 | #include <linux/rtnetlink.h> | 
 |  | 
 | #include <stdplus/fd/create.hpp> | 
 | #include <stdplus/fd/ops.hpp> | 
 | #include <stdplus/raw.hpp> | 
 |  | 
 | #include <array> | 
 | #include <stdexcept> | 
 | #include <system_error> | 
 |  | 
 | using stdplus::raw::Aligned; | 
 |  | 
 | namespace phosphor | 
 | { | 
 | namespace network | 
 | { | 
 | namespace netlink | 
 | { | 
 | namespace detail | 
 | { | 
 |  | 
 | void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb) | 
 | { | 
 |     // Parse and update the message buffer | 
 |     const auto& hdr = stdplus::raw::refFrom<nlmsghdr, Aligned>(msgs); | 
 |     if (hdr.nlmsg_len < sizeof(hdr)) | 
 |     { | 
 |         throw std::runtime_error( | 
 |             fmt::format("nlmsg length shorter than header: {} < {}", | 
 |                         hdr.nlmsg_len, sizeof(hdr))); | 
 |     } | 
 |     if (msgs.size() < hdr.nlmsg_len) | 
 |     { | 
 |         throw std::runtime_error( | 
 |             fmt::format("not enough message for nlmsg: {} < {}", msgs.size(), | 
 |                         hdr.nlmsg_len)); | 
 |     } | 
 |     auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN); | 
 |     msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len)); | 
 |  | 
 |     // Figure out how to handle the individual message | 
 |     bool doCallback = true; | 
 |     if (hdr.nlmsg_flags & NLM_F_MULTI) | 
 |     { | 
 |         done = false; | 
 |     } | 
 |     if (hdr.nlmsg_type == NLMSG_NOOP) | 
 |     { | 
 |         doCallback = false; | 
 |     } | 
 |     else if (hdr.nlmsg_type == NLMSG_DONE) | 
 |     { | 
 |         if (done) | 
 |         { | 
 |             throw std::runtime_error("Got done for non-multi msg"); | 
 |         } | 
 |         done = true; | 
 |         doCallback = false; | 
 |     } | 
 |     else if (hdr.nlmsg_type == NLMSG_ERROR) | 
 |     { | 
 |         const auto& err = stdplus::raw::refFrom<nlmsgerr, Aligned>(msg); | 
 |         // This is just an ACK so don't do the callback | 
 |         if (err.error <= 0) | 
 |         { | 
 |             doCallback = false; | 
 |         } | 
 |     } | 
 |     // All multi-msg headers must have the multi flag | 
 |     if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI)) | 
 |     { | 
 |         throw std::runtime_error("Got non-multi msg before done"); | 
 |     } | 
 |     if (doCallback) | 
 |     { | 
 |         cb(hdr, msg); | 
 |     } | 
 | } | 
 |  | 
 | static void requestSend(int sock, void* data, size_t size) | 
 | { | 
 |     sockaddr_nl dst{}; | 
 |     dst.nl_family = AF_NETLINK; | 
 |  | 
 |     iovec iov{}; | 
 |     iov.iov_base = data; | 
 |     iov.iov_len = size; | 
 |  | 
 |     msghdr hdr{}; | 
 |     hdr.msg_name = reinterpret_cast<sockaddr*>(&dst); | 
 |     hdr.msg_namelen = sizeof(dst); | 
 |     hdr.msg_iov = &iov; | 
 |     hdr.msg_iovlen = 1; | 
 |  | 
 |     if (sendmsg(sock, &hdr, 0) < 0) | 
 |     { | 
 |         throw std::system_error(errno, std::generic_category(), | 
 |                                 "netlink sendmsg"); | 
 |     } | 
 | } | 
 |  | 
 | static stdplus::ManagedFd makeSocket(int protocol) | 
 | { | 
 |     using namespace stdplus::fd; | 
 |  | 
 |     auto sock = socket(SocketDomain::Netlink, SocketType::Raw, | 
 |                        static_cast<stdplus::fd::SocketProto>(protocol)); | 
 |  | 
 |     sockaddr_nl local{}; | 
 |     local.nl_family = AF_NETLINK; | 
 |     bind(sock, local); | 
 |  | 
 |     return sock; | 
 | } | 
 |  | 
 | void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb) | 
 | { | 
 |     auto sock = makeSocket(protocol); | 
 |     requestSend(sock.get(), data, size); | 
 |     receive(sock.get(), cb); | 
 | } | 
 |  | 
 | } // namespace detail | 
 |  | 
 | size_t receive(int sock, ReceiveCallback cb) | 
 | { | 
 |     // We need to make sure we have enough room for an entire packet otherwise | 
 |     // it gets truncated. The netlink docs guarantee packets will not exceed 8K | 
 |     std::array<char, 8192> buf; | 
 |  | 
 |     iovec iov{}; | 
 |     iov.iov_base = buf.data(); | 
 |     iov.iov_len = buf.size(); | 
 |  | 
 |     sockaddr_nl from{}; | 
 |     from.nl_family = AF_NETLINK; | 
 |  | 
 |     msghdr hdr{}; | 
 |     hdr.msg_name = &from; | 
 |     hdr.msg_namelen = sizeof(from); | 
 |     hdr.msg_iov = &iov; | 
 |     hdr.msg_iovlen = 1; | 
 |  | 
 |     // We only do multiple recvs if we have a MULTI type message | 
 |     bool done = true; | 
 |     size_t num_msgs = 0; | 
 |     do | 
 |     { | 
 |         ssize_t recvd = recvmsg(sock, &hdr, 0); | 
 |         if (recvd < 0 && errno != EAGAIN) | 
 |         { | 
 |             throw std::system_error(errno, std::generic_category(), | 
 |                                     "netlink recvmsg"); | 
 |         } | 
 |         if (recvd <= 0) | 
 |         { | 
 |             if (!done) | 
 |             { | 
 |                 throw std::runtime_error("netlink recvmsg: Got empty payload"); | 
 |             } | 
 |             return num_msgs; | 
 |         } | 
 |  | 
 |         std::string_view msgs(buf.data(), recvd); | 
 |         do | 
 |         { | 
 |             detail::processMsg(msgs, done, cb); | 
 |             num_msgs++; | 
 |         } while (!done && !msgs.empty()); | 
 |  | 
 |         if (done && !msgs.empty()) | 
 |         { | 
 |             throw std::runtime_error("Extra unprocessed netlink messages"); | 
 |         } | 
 |     } while (!done); | 
 |     return num_msgs; | 
 | } | 
 |  | 
 | std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data) | 
 | { | 
 |     const auto& hdr = stdplus::raw::refFrom<rtattr, Aligned>(data); | 
 |     if (hdr.rta_len < RTA_LENGTH(0)) | 
 |     { | 
 |         throw std::runtime_error(fmt::format( | 
 |             "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0))); | 
 |     } | 
 |     if (data.size() < hdr.rta_len) | 
 |     { | 
 |         throw std::runtime_error( | 
 |             fmt::format("not enough message for rtattr: {} < {}", data.size(), | 
 |                         hdr.rta_len)); | 
 |     } | 
 |     auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0)); | 
 |     data.remove_prefix(RTA_ALIGN(hdr.rta_len)); | 
 |     return {hdr, attr}; | 
 | } | 
 |  | 
 | } // namespace netlink | 
 | } // namespace network | 
 | } // namespace phosphor |