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