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 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 14*0bc39da3SWilliam A. Kennington III using stdplus::raw::Aligned; 15*0bc39da3SWilliam A. Kennington III 161bbe3d1eSWilliam A. Kennington III namespace phosphor 171bbe3d1eSWilliam A. Kennington III { 181bbe3d1eSWilliam A. Kennington III namespace network 191bbe3d1eSWilliam A. Kennington III { 201bbe3d1eSWilliam A. Kennington III namespace netlink 211bbe3d1eSWilliam A. Kennington III { 221bbe3d1eSWilliam A. Kennington III namespace detail 231bbe3d1eSWilliam A. Kennington III { 241bbe3d1eSWilliam A. Kennington III 25058f4cf7SWilliam A. Kennington III void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb) 261bbe3d1eSWilliam A. Kennington III { 271bbe3d1eSWilliam A. Kennington III // Parse and update the message buffer 28*0bc39da3SWilliam A. Kennington III const auto& hdr = stdplus::raw::refFrom<nlmsghdr, Aligned>(msgs); 291bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_len < sizeof(hdr)) 301bbe3d1eSWilliam A. Kennington III { 31f174bccfSWilliam A. Kennington III throw std::runtime_error( 32f174bccfSWilliam A. Kennington III fmt::format("nlmsg length shorter than header: {} < {}", 33f174bccfSWilliam A. Kennington III hdr.nlmsg_len, sizeof(hdr))); 341bbe3d1eSWilliam A. Kennington III } 351bbe3d1eSWilliam A. Kennington III if (msgs.size() < hdr.nlmsg_len) 361bbe3d1eSWilliam A. Kennington III { 37f174bccfSWilliam A. Kennington III throw std::runtime_error( 38f174bccfSWilliam A. Kennington III fmt::format("not enough message for nlmsg: {} < {}", msgs.size(), 39f174bccfSWilliam A. Kennington III hdr.nlmsg_len)); 401bbe3d1eSWilliam A. Kennington III } 411bbe3d1eSWilliam A. Kennington III auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN); 421bbe3d1eSWilliam A. Kennington III msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len)); 431bbe3d1eSWilliam A. Kennington III 441bbe3d1eSWilliam A. Kennington III // Figure out how to handle the individual message 451bbe3d1eSWilliam A. Kennington III bool doCallback = true; 461bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_flags & NLM_F_MULTI) 471bbe3d1eSWilliam A. Kennington III { 481bbe3d1eSWilliam A. Kennington III done = false; 491bbe3d1eSWilliam A. Kennington III } 501bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_type == NLMSG_NOOP) 511bbe3d1eSWilliam A. Kennington III { 521bbe3d1eSWilliam A. Kennington III doCallback = false; 531bbe3d1eSWilliam A. Kennington III } 541bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_DONE) 551bbe3d1eSWilliam A. Kennington III { 561bbe3d1eSWilliam A. Kennington III if (done) 571bbe3d1eSWilliam A. Kennington III { 581bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got done for non-multi msg"); 591bbe3d1eSWilliam A. Kennington III } 601bbe3d1eSWilliam A. Kennington III done = true; 611bbe3d1eSWilliam A. Kennington III doCallback = false; 621bbe3d1eSWilliam A. Kennington III } 631bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_ERROR) 641bbe3d1eSWilliam A. Kennington III { 65*0bc39da3SWilliam A. Kennington III const auto& err = stdplus::raw::refFrom<nlmsgerr, Aligned>(msg); 661bbe3d1eSWilliam A. Kennington III // This is just an ACK so don't do the callback 671bbe3d1eSWilliam A. Kennington III if (err.error <= 0) 681bbe3d1eSWilliam A. Kennington III { 691bbe3d1eSWilliam A. Kennington III doCallback = false; 701bbe3d1eSWilliam A. Kennington III } 711bbe3d1eSWilliam A. Kennington III } 721bbe3d1eSWilliam A. Kennington III // All multi-msg headers must have the multi flag 731bbe3d1eSWilliam A. Kennington III if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI)) 741bbe3d1eSWilliam A. Kennington III { 751bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got non-multi msg before done"); 761bbe3d1eSWilliam A. Kennington III } 771bbe3d1eSWilliam A. Kennington III if (doCallback) 781bbe3d1eSWilliam A. Kennington III { 791bbe3d1eSWilliam A. Kennington III cb(hdr, msg); 801bbe3d1eSWilliam A. Kennington III } 811bbe3d1eSWilliam A. Kennington III } 821bbe3d1eSWilliam A. Kennington III 831bbe3d1eSWilliam A. Kennington III static void requestSend(int sock, void* data, size_t size) 841bbe3d1eSWilliam A. Kennington III { 851bbe3d1eSWilliam A. Kennington III sockaddr_nl dst{}; 861bbe3d1eSWilliam A. Kennington III dst.nl_family = AF_NETLINK; 871bbe3d1eSWilliam A. Kennington III 881bbe3d1eSWilliam A. Kennington III iovec iov{}; 891bbe3d1eSWilliam A. Kennington III iov.iov_base = data; 901bbe3d1eSWilliam A. Kennington III iov.iov_len = size; 911bbe3d1eSWilliam A. Kennington III 921bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 931bbe3d1eSWilliam A. Kennington III hdr.msg_name = reinterpret_cast<sockaddr*>(&dst); 941bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(dst); 951bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 961bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 971bbe3d1eSWilliam A. Kennington III 981bbe3d1eSWilliam A. Kennington III if (sendmsg(sock, &hdr, 0) < 0) 991bbe3d1eSWilliam A. Kennington III { 1001bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 1011bbe3d1eSWilliam A. Kennington III "netlink sendmsg"); 1021bbe3d1eSWilliam A. Kennington III } 1031bbe3d1eSWilliam A. Kennington III } 1041bbe3d1eSWilliam A. Kennington III 1052ac2660cSWilliam A. Kennington III static stdplus::ManagedFd makeSocket(int protocol) 1061bbe3d1eSWilliam A. Kennington III { 1072ac2660cSWilliam A. Kennington III using namespace stdplus::fd; 1082ac2660cSWilliam A. Kennington III 1092ac2660cSWilliam A. Kennington III auto sock = socket(SocketDomain::Netlink, SocketType::Raw, 1102ac2660cSWilliam A. Kennington III static_cast<stdplus::fd::SocketProto>(protocol)); 1111bbe3d1eSWilliam A. Kennington III 1121bbe3d1eSWilliam A. Kennington III sockaddr_nl local{}; 1131bbe3d1eSWilliam A. Kennington III local.nl_family = AF_NETLINK; 1142ac2660cSWilliam A. Kennington III bind(sock, local); 1151bbe3d1eSWilliam A. Kennington III 1161bbe3d1eSWilliam A. Kennington III return sock; 1171bbe3d1eSWilliam A. Kennington III } 1181bbe3d1eSWilliam A. Kennington III 119058f4cf7SWilliam A. Kennington III void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb) 1201bbe3d1eSWilliam A. Kennington III { 1212ac2660cSWilliam A. Kennington III auto sock = makeSocket(protocol); 1222ac2660cSWilliam A. Kennington III requestSend(sock.get(), data, size); 1232ac2660cSWilliam A. Kennington III receive(sock.get(), cb); 1241bbe3d1eSWilliam A. Kennington III } 1251bbe3d1eSWilliam A. Kennington III 1261bbe3d1eSWilliam A. Kennington III } // namespace detail 1271bbe3d1eSWilliam A. Kennington III 1285f165dccSWilliam A. Kennington III void receive(int sock, ReceiveCallback cb) 1295f165dccSWilliam A. Kennington III { 1305f165dccSWilliam A. Kennington III // We need to make sure we have enough room for an entire packet otherwise 1315f165dccSWilliam A. Kennington III // it gets truncated. The netlink docs guarantee packets will not exceed 8K 1325f165dccSWilliam A. Kennington III std::array<char, 8192> buf; 1335f165dccSWilliam A. Kennington III 1345f165dccSWilliam A. Kennington III iovec iov{}; 1355f165dccSWilliam A. Kennington III iov.iov_base = buf.data(); 1365f165dccSWilliam A. Kennington III iov.iov_len = buf.size(); 1375f165dccSWilliam A. Kennington III 1385f165dccSWilliam A. Kennington III sockaddr_nl from{}; 1395f165dccSWilliam A. Kennington III from.nl_family = AF_NETLINK; 1405f165dccSWilliam A. Kennington III 1415f165dccSWilliam A. Kennington III msghdr hdr{}; 1425f165dccSWilliam A. Kennington III hdr.msg_name = &from; 1435f165dccSWilliam A. Kennington III hdr.msg_namelen = sizeof(from); 1445f165dccSWilliam A. Kennington III hdr.msg_iov = &iov; 1455f165dccSWilliam A. Kennington III hdr.msg_iovlen = 1; 1465f165dccSWilliam A. Kennington III 1475f165dccSWilliam A. Kennington III // We only do multiple recvs if we have a MULTI type message 1485f165dccSWilliam A. Kennington III bool done = true; 1495f165dccSWilliam A. Kennington III do 1505f165dccSWilliam A. Kennington III { 1515f165dccSWilliam A. Kennington III ssize_t recvd = recvmsg(sock, &hdr, 0); 1525f165dccSWilliam A. Kennington III if (recvd < 0) 1535f165dccSWilliam A. Kennington III { 1545f165dccSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 1555f165dccSWilliam A. Kennington III "netlink recvmsg"); 1565f165dccSWilliam A. Kennington III } 1575f165dccSWilliam A. Kennington III if (recvd == 0) 1585f165dccSWilliam A. Kennington III { 1595f165dccSWilliam A. Kennington III if (!done) 1605f165dccSWilliam A. Kennington III { 1615f165dccSWilliam A. Kennington III throw std::runtime_error("netlink recvmsg: Got empty payload"); 1625f165dccSWilliam A. Kennington III } 1635f165dccSWilliam A. Kennington III return; 1645f165dccSWilliam A. Kennington III } 1655f165dccSWilliam A. Kennington III 1665f165dccSWilliam A. Kennington III std::string_view msgs(buf.data(), recvd); 1675f165dccSWilliam A. Kennington III do 1685f165dccSWilliam A. Kennington III { 1695f165dccSWilliam A. Kennington III detail::processMsg(msgs, done, cb); 1705f165dccSWilliam A. Kennington III } while (!done && !msgs.empty()); 1715f165dccSWilliam A. Kennington III 1725f165dccSWilliam A. Kennington III if (done && !msgs.empty()) 1735f165dccSWilliam A. Kennington III { 1745f165dccSWilliam A. Kennington III throw std::runtime_error("Extra unprocessed netlink messages"); 1755f165dccSWilliam A. Kennington III } 1765f165dccSWilliam A. Kennington III } while (!done); 1775f165dccSWilliam A. Kennington III } 1785f165dccSWilliam A. Kennington III 1791bbe3d1eSWilliam A. Kennington III std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data) 1801bbe3d1eSWilliam A. Kennington III { 181*0bc39da3SWilliam A. Kennington III const auto& hdr = stdplus::raw::refFrom<rtattr, Aligned>(data); 1821bbe3d1eSWilliam A. Kennington III if (hdr.rta_len < RTA_LENGTH(0)) 1831bbe3d1eSWilliam A. Kennington III { 184f174bccfSWilliam A. Kennington III throw std::runtime_error(fmt::format( 185f174bccfSWilliam A. Kennington III "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0))); 1861bbe3d1eSWilliam A. Kennington III } 1871bbe3d1eSWilliam A. Kennington III if (data.size() < hdr.rta_len) 1881bbe3d1eSWilliam A. Kennington III { 189f174bccfSWilliam A. Kennington III throw std::runtime_error( 190f174bccfSWilliam A. Kennington III fmt::format("not enough message for rtattr: {} < {}", data.size(), 191f174bccfSWilliam A. Kennington III hdr.rta_len)); 1921bbe3d1eSWilliam A. Kennington III } 1931bbe3d1eSWilliam A. Kennington III auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0)); 1941bbe3d1eSWilliam A. Kennington III data.remove_prefix(RTA_ALIGN(hdr.rta_len)); 1951bbe3d1eSWilliam A. Kennington III return {hdr, attr}; 1961bbe3d1eSWilliam A. Kennington III } 1971bbe3d1eSWilliam A. Kennington III 1981bbe3d1eSWilliam A. Kennington III } // namespace netlink 1991bbe3d1eSWilliam A. Kennington III } // namespace network 2001bbe3d1eSWilliam A. Kennington III } // namespace phosphor 201