11bbe3d1eSWilliam A. Kennington III #include "netlink.hpp" 21bbe3d1eSWilliam A. Kennington III 3*f174bccfSWilliam A. Kennington III #include <fmt/format.h> 41bbe3d1eSWilliam A. Kennington III #include <linux/netlink.h> 51bbe3d1eSWilliam A. Kennington III #include <linux/rtnetlink.h> 61bbe3d1eSWilliam A. Kennington III 71bbe3d1eSWilliam A. Kennington III #include <array> 81bbe3d1eSWilliam A. Kennington III #include <stdexcept> 92ac2660cSWilliam A. Kennington III #include <stdplus/fd/create.hpp> 102ac2660cSWilliam A. Kennington III #include <stdplus/fd/ops.hpp> 111bbe3d1eSWilliam A. Kennington III #include <stdplus/raw.hpp> 121bbe3d1eSWilliam A. Kennington III #include <system_error> 131bbe3d1eSWilliam A. Kennington III 141bbe3d1eSWilliam A. Kennington III namespace phosphor 151bbe3d1eSWilliam A. Kennington III { 161bbe3d1eSWilliam A. Kennington III namespace network 171bbe3d1eSWilliam A. Kennington III { 181bbe3d1eSWilliam A. Kennington III namespace netlink 191bbe3d1eSWilliam A. Kennington III { 201bbe3d1eSWilliam A. Kennington III namespace detail 211bbe3d1eSWilliam A. Kennington III { 221bbe3d1eSWilliam A. Kennington III 23058f4cf7SWilliam A. Kennington III void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb) 241bbe3d1eSWilliam A. Kennington III { 251bbe3d1eSWilliam A. Kennington III // Parse and update the message buffer 261bbe3d1eSWilliam A. Kennington III auto hdr = stdplus::raw::copyFrom<nlmsghdr>(msgs); 271bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_len < sizeof(hdr)) 281bbe3d1eSWilliam A. Kennington III { 29*f174bccfSWilliam A. Kennington III throw std::runtime_error( 30*f174bccfSWilliam A. Kennington III fmt::format("nlmsg length shorter than header: {} < {}", 31*f174bccfSWilliam A. Kennington III hdr.nlmsg_len, sizeof(hdr))); 321bbe3d1eSWilliam A. Kennington III } 331bbe3d1eSWilliam A. Kennington III if (msgs.size() < hdr.nlmsg_len) 341bbe3d1eSWilliam A. Kennington III { 35*f174bccfSWilliam A. Kennington III throw std::runtime_error( 36*f174bccfSWilliam A. Kennington III fmt::format("not enough message for nlmsg: {} < {}", msgs.size(), 37*f174bccfSWilliam A. Kennington III hdr.nlmsg_len)); 381bbe3d1eSWilliam A. Kennington III } 391bbe3d1eSWilliam A. Kennington III auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN); 401bbe3d1eSWilliam A. Kennington III msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len)); 411bbe3d1eSWilliam A. Kennington III 421bbe3d1eSWilliam A. Kennington III // Figure out how to handle the individual message 431bbe3d1eSWilliam A. Kennington III bool doCallback = true; 441bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_flags & NLM_F_MULTI) 451bbe3d1eSWilliam A. Kennington III { 461bbe3d1eSWilliam A. Kennington III done = false; 471bbe3d1eSWilliam A. Kennington III } 481bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_type == NLMSG_NOOP) 491bbe3d1eSWilliam A. Kennington III { 501bbe3d1eSWilliam A. Kennington III doCallback = false; 511bbe3d1eSWilliam A. Kennington III } 521bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_DONE) 531bbe3d1eSWilliam A. Kennington III { 541bbe3d1eSWilliam A. Kennington III if (done) 551bbe3d1eSWilliam A. Kennington III { 561bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got done for non-multi msg"); 571bbe3d1eSWilliam A. Kennington III } 581bbe3d1eSWilliam A. Kennington III done = true; 591bbe3d1eSWilliam A. Kennington III doCallback = false; 601bbe3d1eSWilliam A. Kennington III } 611bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_ERROR) 621bbe3d1eSWilliam A. Kennington III { 631bbe3d1eSWilliam A. Kennington III auto err = stdplus::raw::copyFrom<nlmsgerr>(msg); 641bbe3d1eSWilliam A. Kennington III // This is just an ACK so don't do the callback 651bbe3d1eSWilliam A. Kennington III if (err.error <= 0) 661bbe3d1eSWilliam A. Kennington III { 671bbe3d1eSWilliam A. Kennington III doCallback = false; 681bbe3d1eSWilliam A. Kennington III } 691bbe3d1eSWilliam A. Kennington III } 701bbe3d1eSWilliam A. Kennington III // All multi-msg headers must have the multi flag 711bbe3d1eSWilliam A. Kennington III if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI)) 721bbe3d1eSWilliam A. Kennington III { 731bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got non-multi msg before done"); 741bbe3d1eSWilliam A. Kennington III } 751bbe3d1eSWilliam A. Kennington III if (doCallback) 761bbe3d1eSWilliam A. Kennington III { 771bbe3d1eSWilliam A. Kennington III cb(hdr, msg); 781bbe3d1eSWilliam A. Kennington III } 791bbe3d1eSWilliam A. Kennington III } 801bbe3d1eSWilliam A. Kennington III 81058f4cf7SWilliam A. Kennington III static void receive(int sock, ReceiveCallback cb) 821bbe3d1eSWilliam A. Kennington III { 831bbe3d1eSWilliam A. Kennington III // We need to make sure we have enough room for an entire packet otherwise 841bbe3d1eSWilliam A. Kennington III // it gets truncated. The netlink docs guarantee packets will not exceed 8K 851bbe3d1eSWilliam A. Kennington III std::array<char, 8192> buf; 861bbe3d1eSWilliam A. Kennington III 871bbe3d1eSWilliam A. Kennington III iovec iov{}; 881bbe3d1eSWilliam A. Kennington III iov.iov_base = buf.data(); 891bbe3d1eSWilliam A. Kennington III iov.iov_len = buf.size(); 901bbe3d1eSWilliam A. Kennington III 911bbe3d1eSWilliam A. Kennington III sockaddr_nl from{}; 921bbe3d1eSWilliam A. Kennington III from.nl_family = AF_NETLINK; 931bbe3d1eSWilliam A. Kennington III 941bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 951bbe3d1eSWilliam A. Kennington III hdr.msg_name = &from; 961bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(from); 971bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 981bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 991bbe3d1eSWilliam A. Kennington III 1001bbe3d1eSWilliam A. Kennington III // We only do multiple recvs if we have a MULTI type message 1011bbe3d1eSWilliam A. Kennington III bool done = true; 1021bbe3d1eSWilliam A. Kennington III do 1031bbe3d1eSWilliam A. Kennington III { 1041bbe3d1eSWilliam A. Kennington III ssize_t recvd = recvmsg(sock, &hdr, 0); 1051bbe3d1eSWilliam A. Kennington III if (recvd < 0) 1061bbe3d1eSWilliam A. Kennington III { 1071bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 1081bbe3d1eSWilliam A. Kennington III "netlink recvmsg"); 1091bbe3d1eSWilliam A. Kennington III } 1101bbe3d1eSWilliam A. Kennington III if (recvd == 0) 1111bbe3d1eSWilliam A. Kennington III { 1121bbe3d1eSWilliam A. Kennington III throw std::runtime_error("netlink recvmsg: Got empty payload"); 1131bbe3d1eSWilliam A. Kennington III } 1141bbe3d1eSWilliam A. Kennington III 1151bbe3d1eSWilliam A. Kennington III std::string_view msgs(buf.data(), recvd); 1161bbe3d1eSWilliam A. Kennington III do 1171bbe3d1eSWilliam A. Kennington III { 1181bbe3d1eSWilliam A. Kennington III processMsg(msgs, done, cb); 1191bbe3d1eSWilliam A. Kennington III } while (!done && !msgs.empty()); 1201bbe3d1eSWilliam A. Kennington III 1211bbe3d1eSWilliam A. Kennington III if (done && !msgs.empty()) 1221bbe3d1eSWilliam A. Kennington III { 1231bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Extra unprocessed netlink messages"); 1241bbe3d1eSWilliam A. Kennington III } 1251bbe3d1eSWilliam A. Kennington III } while (!done); 1261bbe3d1eSWilliam A. Kennington III } 1271bbe3d1eSWilliam A. Kennington III 1281bbe3d1eSWilliam A. Kennington III static void requestSend(int sock, void* data, size_t size) 1291bbe3d1eSWilliam A. Kennington III { 1301bbe3d1eSWilliam A. Kennington III sockaddr_nl dst{}; 1311bbe3d1eSWilliam A. Kennington III dst.nl_family = AF_NETLINK; 1321bbe3d1eSWilliam A. Kennington III 1331bbe3d1eSWilliam A. Kennington III iovec iov{}; 1341bbe3d1eSWilliam A. Kennington III iov.iov_base = data; 1351bbe3d1eSWilliam A. Kennington III iov.iov_len = size; 1361bbe3d1eSWilliam A. Kennington III 1371bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 1381bbe3d1eSWilliam A. Kennington III hdr.msg_name = reinterpret_cast<sockaddr*>(&dst); 1391bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(dst); 1401bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 1411bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 1421bbe3d1eSWilliam A. Kennington III 1431bbe3d1eSWilliam A. Kennington III if (sendmsg(sock, &hdr, 0) < 0) 1441bbe3d1eSWilliam A. Kennington III { 1451bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 1461bbe3d1eSWilliam A. Kennington III "netlink sendmsg"); 1471bbe3d1eSWilliam A. Kennington III } 1481bbe3d1eSWilliam A. Kennington III } 1491bbe3d1eSWilliam A. Kennington III 1502ac2660cSWilliam A. Kennington III static stdplus::ManagedFd makeSocket(int protocol) 1511bbe3d1eSWilliam A. Kennington III { 1522ac2660cSWilliam A. Kennington III using namespace stdplus::fd; 1532ac2660cSWilliam A. Kennington III 1542ac2660cSWilliam A. Kennington III auto sock = socket(SocketDomain::Netlink, SocketType::Raw, 1552ac2660cSWilliam A. Kennington III static_cast<stdplus::fd::SocketProto>(protocol)); 1561bbe3d1eSWilliam A. Kennington III 1571bbe3d1eSWilliam A. Kennington III sockaddr_nl local{}; 1581bbe3d1eSWilliam A. Kennington III local.nl_family = AF_NETLINK; 1592ac2660cSWilliam A. Kennington III bind(sock, local); 1601bbe3d1eSWilliam A. Kennington III 1611bbe3d1eSWilliam A. Kennington III return sock; 1621bbe3d1eSWilliam A. Kennington III } 1631bbe3d1eSWilliam A. Kennington III 164058f4cf7SWilliam A. Kennington III void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb) 1651bbe3d1eSWilliam A. Kennington III { 1662ac2660cSWilliam A. Kennington III auto sock = makeSocket(protocol); 1672ac2660cSWilliam A. Kennington III requestSend(sock.get(), data, size); 1682ac2660cSWilliam A. Kennington III receive(sock.get(), cb); 1691bbe3d1eSWilliam A. Kennington III } 1701bbe3d1eSWilliam A. Kennington III 1711bbe3d1eSWilliam A. Kennington III } // namespace detail 1721bbe3d1eSWilliam A. Kennington III 1731bbe3d1eSWilliam A. Kennington III std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data) 1741bbe3d1eSWilliam A. Kennington III { 1751bbe3d1eSWilliam A. Kennington III auto hdr = stdplus::raw::copyFrom<rtattr>(data); 1761bbe3d1eSWilliam A. Kennington III if (hdr.rta_len < RTA_LENGTH(0)) 1771bbe3d1eSWilliam A. Kennington III { 178*f174bccfSWilliam A. Kennington III throw std::runtime_error(fmt::format( 179*f174bccfSWilliam A. Kennington III "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0))); 1801bbe3d1eSWilliam A. Kennington III } 1811bbe3d1eSWilliam A. Kennington III if (data.size() < hdr.rta_len) 1821bbe3d1eSWilliam A. Kennington III { 183*f174bccfSWilliam A. Kennington III throw std::runtime_error( 184*f174bccfSWilliam A. Kennington III fmt::format("not enough message for rtattr: {} < {}", data.size(), 185*f174bccfSWilliam A. Kennington III hdr.rta_len)); 1861bbe3d1eSWilliam A. Kennington III } 1871bbe3d1eSWilliam A. Kennington III auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0)); 1881bbe3d1eSWilliam A. Kennington III data.remove_prefix(RTA_ALIGN(hdr.rta_len)); 1891bbe3d1eSWilliam A. Kennington III return {hdr, attr}; 1901bbe3d1eSWilliam A. Kennington III } 1911bbe3d1eSWilliam A. Kennington III 1921bbe3d1eSWilliam A. Kennington III } // namespace netlink 1931bbe3d1eSWilliam A. Kennington III } // namespace network 1941bbe3d1eSWilliam A. Kennington III } // namespace phosphor 195