11bbe3d1eSWilliam A. Kennington III #include "netlink.hpp" 21bbe3d1eSWilliam A. Kennington III 3f174bccfSWilliam 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 72ac2660cSWilliam A. Kennington III #include <stdplus/fd/create.hpp> 82ac2660cSWilliam A. Kennington III #include <stdplus/fd/ops.hpp> 91bbe3d1eSWilliam A. Kennington III #include <stdplus/raw.hpp> 10*89d734b9SPatrick Williams 11*89d734b9SPatrick Williams #include <array> 12*89d734b9SPatrick Williams #include <stdexcept> 131bbe3d1eSWilliam A. Kennington III #include <system_error> 141bbe3d1eSWilliam A. Kennington III 150bc39da3SWilliam A. Kennington III using stdplus::raw::Aligned; 160bc39da3SWilliam A. Kennington III 171bbe3d1eSWilliam A. Kennington III namespace phosphor 181bbe3d1eSWilliam A. Kennington III { 191bbe3d1eSWilliam A. Kennington III namespace network 201bbe3d1eSWilliam A. Kennington III { 211bbe3d1eSWilliam A. Kennington III namespace netlink 221bbe3d1eSWilliam A. Kennington III { 231bbe3d1eSWilliam A. Kennington III namespace detail 241bbe3d1eSWilliam A. Kennington III { 251bbe3d1eSWilliam A. Kennington III 26058f4cf7SWilliam A. Kennington III void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb) 271bbe3d1eSWilliam A. Kennington III { 281bbe3d1eSWilliam A. Kennington III // Parse and update the message buffer 290bc39da3SWilliam A. Kennington III const auto& hdr = stdplus::raw::refFrom<nlmsghdr, Aligned>(msgs); 301bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_len < sizeof(hdr)) 311bbe3d1eSWilliam A. Kennington III { 32f174bccfSWilliam A. Kennington III throw std::runtime_error( 33f174bccfSWilliam A. Kennington III fmt::format("nlmsg length shorter than header: {} < {}", 34f174bccfSWilliam A. Kennington III hdr.nlmsg_len, sizeof(hdr))); 351bbe3d1eSWilliam A. Kennington III } 361bbe3d1eSWilliam A. Kennington III if (msgs.size() < hdr.nlmsg_len) 371bbe3d1eSWilliam A. Kennington III { 38f174bccfSWilliam A. Kennington III throw std::runtime_error( 39f174bccfSWilliam A. Kennington III fmt::format("not enough message for nlmsg: {} < {}", msgs.size(), 40f174bccfSWilliam A. Kennington III hdr.nlmsg_len)); 411bbe3d1eSWilliam A. Kennington III } 421bbe3d1eSWilliam A. Kennington III auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN); 431bbe3d1eSWilliam A. Kennington III msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len)); 441bbe3d1eSWilliam A. Kennington III 451bbe3d1eSWilliam A. Kennington III // Figure out how to handle the individual message 461bbe3d1eSWilliam A. Kennington III bool doCallback = true; 471bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_flags & NLM_F_MULTI) 481bbe3d1eSWilliam A. Kennington III { 491bbe3d1eSWilliam A. Kennington III done = false; 501bbe3d1eSWilliam A. Kennington III } 511bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_type == NLMSG_NOOP) 521bbe3d1eSWilliam A. Kennington III { 531bbe3d1eSWilliam A. Kennington III doCallback = false; 541bbe3d1eSWilliam A. Kennington III } 551bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_DONE) 561bbe3d1eSWilliam A. Kennington III { 571bbe3d1eSWilliam A. Kennington III if (done) 581bbe3d1eSWilliam A. Kennington III { 591bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got done for non-multi msg"); 601bbe3d1eSWilliam A. Kennington III } 611bbe3d1eSWilliam A. Kennington III done = true; 621bbe3d1eSWilliam A. Kennington III doCallback = false; 631bbe3d1eSWilliam A. Kennington III } 641bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_ERROR) 651bbe3d1eSWilliam A. Kennington III { 660bc39da3SWilliam A. Kennington III const auto& err = stdplus::raw::refFrom<nlmsgerr, Aligned>(msg); 671bbe3d1eSWilliam A. Kennington III // This is just an ACK so don't do the callback 681bbe3d1eSWilliam A. Kennington III if (err.error <= 0) 691bbe3d1eSWilliam A. Kennington III { 701bbe3d1eSWilliam A. Kennington III doCallback = false; 711bbe3d1eSWilliam A. Kennington III } 721bbe3d1eSWilliam A. Kennington III } 731bbe3d1eSWilliam A. Kennington III // All multi-msg headers must have the multi flag 741bbe3d1eSWilliam A. Kennington III if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI)) 751bbe3d1eSWilliam A. Kennington III { 761bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got non-multi msg before done"); 771bbe3d1eSWilliam A. Kennington III } 781bbe3d1eSWilliam A. Kennington III if (doCallback) 791bbe3d1eSWilliam A. Kennington III { 801bbe3d1eSWilliam A. Kennington III cb(hdr, msg); 811bbe3d1eSWilliam A. Kennington III } 821bbe3d1eSWilliam A. Kennington III } 831bbe3d1eSWilliam A. Kennington III 841bbe3d1eSWilliam A. Kennington III static void requestSend(int sock, void* data, size_t size) 851bbe3d1eSWilliam A. Kennington III { 861bbe3d1eSWilliam A. Kennington III sockaddr_nl dst{}; 871bbe3d1eSWilliam A. Kennington III dst.nl_family = AF_NETLINK; 881bbe3d1eSWilliam A. Kennington III 891bbe3d1eSWilliam A. Kennington III iovec iov{}; 901bbe3d1eSWilliam A. Kennington III iov.iov_base = data; 911bbe3d1eSWilliam A. Kennington III iov.iov_len = size; 921bbe3d1eSWilliam A. Kennington III 931bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 941bbe3d1eSWilliam A. Kennington III hdr.msg_name = reinterpret_cast<sockaddr*>(&dst); 951bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(dst); 961bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 971bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 981bbe3d1eSWilliam A. Kennington III 991bbe3d1eSWilliam A. Kennington III if (sendmsg(sock, &hdr, 0) < 0) 1001bbe3d1eSWilliam A. Kennington III { 1011bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 1021bbe3d1eSWilliam A. Kennington III "netlink sendmsg"); 1031bbe3d1eSWilliam A. Kennington III } 1041bbe3d1eSWilliam A. Kennington III } 1051bbe3d1eSWilliam A. Kennington III 1062ac2660cSWilliam A. Kennington III static stdplus::ManagedFd makeSocket(int protocol) 1071bbe3d1eSWilliam A. Kennington III { 1082ac2660cSWilliam A. Kennington III using namespace stdplus::fd; 1092ac2660cSWilliam A. Kennington III 1102ac2660cSWilliam A. Kennington III auto sock = socket(SocketDomain::Netlink, SocketType::Raw, 1112ac2660cSWilliam A. Kennington III static_cast<stdplus::fd::SocketProto>(protocol)); 1121bbe3d1eSWilliam A. Kennington III 1131bbe3d1eSWilliam A. Kennington III sockaddr_nl local{}; 1141bbe3d1eSWilliam A. Kennington III local.nl_family = AF_NETLINK; 1152ac2660cSWilliam A. Kennington III bind(sock, local); 1161bbe3d1eSWilliam A. Kennington III 1171bbe3d1eSWilliam A. Kennington III return sock; 1181bbe3d1eSWilliam A. Kennington III } 1191bbe3d1eSWilliam A. Kennington III 120058f4cf7SWilliam A. Kennington III void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb) 1211bbe3d1eSWilliam A. Kennington III { 1222ac2660cSWilliam A. Kennington III auto sock = makeSocket(protocol); 1232ac2660cSWilliam A. Kennington III requestSend(sock.get(), data, size); 1242ac2660cSWilliam A. Kennington III receive(sock.get(), cb); 1251bbe3d1eSWilliam A. Kennington III } 1261bbe3d1eSWilliam A. Kennington III 1271bbe3d1eSWilliam A. Kennington III } // namespace detail 1281bbe3d1eSWilliam A. Kennington III 12929418efcSWilliam A. Kennington III size_t receive(int sock, ReceiveCallback cb) 1305f165dccSWilliam A. Kennington III { 1315f165dccSWilliam A. Kennington III // We need to make sure we have enough room for an entire packet otherwise 1325f165dccSWilliam A. Kennington III // it gets truncated. The netlink docs guarantee packets will not exceed 8K 1335f165dccSWilliam A. Kennington III std::array<char, 8192> buf; 1345f165dccSWilliam A. Kennington III 1355f165dccSWilliam A. Kennington III iovec iov{}; 1365f165dccSWilliam A. Kennington III iov.iov_base = buf.data(); 1375f165dccSWilliam A. Kennington III iov.iov_len = buf.size(); 1385f165dccSWilliam A. Kennington III 1395f165dccSWilliam A. Kennington III sockaddr_nl from{}; 1405f165dccSWilliam A. Kennington III from.nl_family = AF_NETLINK; 1415f165dccSWilliam A. Kennington III 1425f165dccSWilliam A. Kennington III msghdr hdr{}; 1435f165dccSWilliam A. Kennington III hdr.msg_name = &from; 1445f165dccSWilliam A. Kennington III hdr.msg_namelen = sizeof(from); 1455f165dccSWilliam A. Kennington III hdr.msg_iov = &iov; 1465f165dccSWilliam A. Kennington III hdr.msg_iovlen = 1; 1475f165dccSWilliam A. Kennington III 1485f165dccSWilliam A. Kennington III // We only do multiple recvs if we have a MULTI type message 1495f165dccSWilliam A. Kennington III bool done = true; 15029418efcSWilliam A. Kennington III size_t num_msgs = 0; 1515f165dccSWilliam A. Kennington III do 1525f165dccSWilliam A. Kennington III { 1535f165dccSWilliam A. Kennington III ssize_t recvd = recvmsg(sock, &hdr, 0); 15429418efcSWilliam A. Kennington III if (recvd < 0 && errno != EAGAIN) 1555f165dccSWilliam A. Kennington III { 1565f165dccSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 1575f165dccSWilliam A. Kennington III "netlink recvmsg"); 1585f165dccSWilliam A. Kennington III } 15929418efcSWilliam A. Kennington III if (recvd <= 0) 1605f165dccSWilliam A. Kennington III { 1615f165dccSWilliam A. Kennington III if (!done) 1625f165dccSWilliam A. Kennington III { 1635f165dccSWilliam A. Kennington III throw std::runtime_error("netlink recvmsg: Got empty payload"); 1645f165dccSWilliam A. Kennington III } 16529418efcSWilliam A. Kennington III return num_msgs; 1665f165dccSWilliam A. Kennington III } 1675f165dccSWilliam A. Kennington III 1685f165dccSWilliam A. Kennington III std::string_view msgs(buf.data(), recvd); 1695f165dccSWilliam A. Kennington III do 1705f165dccSWilliam A. Kennington III { 1715f165dccSWilliam A. Kennington III detail::processMsg(msgs, done, cb); 17229418efcSWilliam A. Kennington III num_msgs++; 1735f165dccSWilliam A. Kennington III } while (!done && !msgs.empty()); 1745f165dccSWilliam A. Kennington III 1755f165dccSWilliam A. Kennington III if (done && !msgs.empty()) 1765f165dccSWilliam A. Kennington III { 1775f165dccSWilliam A. Kennington III throw std::runtime_error("Extra unprocessed netlink messages"); 1785f165dccSWilliam A. Kennington III } 1795f165dccSWilliam A. Kennington III } while (!done); 18029418efcSWilliam A. Kennington III return num_msgs; 1815f165dccSWilliam A. Kennington III } 1825f165dccSWilliam A. Kennington III 1831bbe3d1eSWilliam A. Kennington III std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data) 1841bbe3d1eSWilliam A. Kennington III { 1850bc39da3SWilliam A. Kennington III const auto& hdr = stdplus::raw::refFrom<rtattr, Aligned>(data); 1861bbe3d1eSWilliam A. Kennington III if (hdr.rta_len < RTA_LENGTH(0)) 1871bbe3d1eSWilliam A. Kennington III { 188f174bccfSWilliam A. Kennington III throw std::runtime_error(fmt::format( 189f174bccfSWilliam A. Kennington III "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0))); 1901bbe3d1eSWilliam A. Kennington III } 1911bbe3d1eSWilliam A. Kennington III if (data.size() < hdr.rta_len) 1921bbe3d1eSWilliam A. Kennington III { 193f174bccfSWilliam A. Kennington III throw std::runtime_error( 194f174bccfSWilliam A. Kennington III fmt::format("not enough message for rtattr: {} < {}", data.size(), 195f174bccfSWilliam A. Kennington III hdr.rta_len)); 1961bbe3d1eSWilliam A. Kennington III } 1971bbe3d1eSWilliam A. Kennington III auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0)); 1981bbe3d1eSWilliam A. Kennington III data.remove_prefix(RTA_ALIGN(hdr.rta_len)); 1991bbe3d1eSWilliam A. Kennington III return {hdr, attr}; 2001bbe3d1eSWilliam A. Kennington III } 2011bbe3d1eSWilliam A. Kennington III 2021bbe3d1eSWilliam A. Kennington III } // namespace netlink 2031bbe3d1eSWilliam A. Kennington III } // namespace network 2041bbe3d1eSWilliam A. Kennington III } // namespace phosphor 205