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 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 { 29f174bccfSWilliam A. Kennington III throw std::runtime_error( 30f174bccfSWilliam A. Kennington III fmt::format("nlmsg length shorter than header: {} < {}", 31f174bccfSWilliam 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 { 35f174bccfSWilliam A. Kennington III throw std::runtime_error( 36f174bccfSWilliam A. Kennington III fmt::format("not enough message for nlmsg: {} < {}", msgs.size(), 37f174bccfSWilliam 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 811bbe3d1eSWilliam A. Kennington III static void requestSend(int sock, void* data, size_t size) 821bbe3d1eSWilliam A. Kennington III { 831bbe3d1eSWilliam A. Kennington III sockaddr_nl dst{}; 841bbe3d1eSWilliam A. Kennington III dst.nl_family = AF_NETLINK; 851bbe3d1eSWilliam A. Kennington III 861bbe3d1eSWilliam A. Kennington III iovec iov{}; 871bbe3d1eSWilliam A. Kennington III iov.iov_base = data; 881bbe3d1eSWilliam A. Kennington III iov.iov_len = size; 891bbe3d1eSWilliam A. Kennington III 901bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 911bbe3d1eSWilliam A. Kennington III hdr.msg_name = reinterpret_cast<sockaddr*>(&dst); 921bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(dst); 931bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 941bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 951bbe3d1eSWilliam A. Kennington III 961bbe3d1eSWilliam A. Kennington III if (sendmsg(sock, &hdr, 0) < 0) 971bbe3d1eSWilliam A. Kennington III { 981bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 991bbe3d1eSWilliam A. Kennington III "netlink sendmsg"); 1001bbe3d1eSWilliam A. Kennington III } 1011bbe3d1eSWilliam A. Kennington III } 1021bbe3d1eSWilliam A. Kennington III 1032ac2660cSWilliam A. Kennington III static stdplus::ManagedFd makeSocket(int protocol) 1041bbe3d1eSWilliam A. Kennington III { 1052ac2660cSWilliam A. Kennington III using namespace stdplus::fd; 1062ac2660cSWilliam A. Kennington III 1072ac2660cSWilliam A. Kennington III auto sock = socket(SocketDomain::Netlink, SocketType::Raw, 1082ac2660cSWilliam A. Kennington III static_cast<stdplus::fd::SocketProto>(protocol)); 1091bbe3d1eSWilliam A. Kennington III 1101bbe3d1eSWilliam A. Kennington III sockaddr_nl local{}; 1111bbe3d1eSWilliam A. Kennington III local.nl_family = AF_NETLINK; 1122ac2660cSWilliam A. Kennington III bind(sock, local); 1131bbe3d1eSWilliam A. Kennington III 1141bbe3d1eSWilliam A. Kennington III return sock; 1151bbe3d1eSWilliam A. Kennington III } 1161bbe3d1eSWilliam A. Kennington III 117058f4cf7SWilliam A. Kennington III void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb) 1181bbe3d1eSWilliam A. Kennington III { 1192ac2660cSWilliam A. Kennington III auto sock = makeSocket(protocol); 1202ac2660cSWilliam A. Kennington III requestSend(sock.get(), data, size); 1212ac2660cSWilliam A. Kennington III receive(sock.get(), cb); 1221bbe3d1eSWilliam A. Kennington III } 1231bbe3d1eSWilliam A. Kennington III 1241bbe3d1eSWilliam A. Kennington III } // namespace detail 1251bbe3d1eSWilliam A. Kennington III 126*5f165dccSWilliam A. Kennington III void receive(int sock, ReceiveCallback cb) 127*5f165dccSWilliam A. Kennington III { 128*5f165dccSWilliam A. Kennington III // We need to make sure we have enough room for an entire packet otherwise 129*5f165dccSWilliam A. Kennington III // it gets truncated. The netlink docs guarantee packets will not exceed 8K 130*5f165dccSWilliam A. Kennington III std::array<char, 8192> buf; 131*5f165dccSWilliam A. Kennington III 132*5f165dccSWilliam A. Kennington III iovec iov{}; 133*5f165dccSWilliam A. Kennington III iov.iov_base = buf.data(); 134*5f165dccSWilliam A. Kennington III iov.iov_len = buf.size(); 135*5f165dccSWilliam A. Kennington III 136*5f165dccSWilliam A. Kennington III sockaddr_nl from{}; 137*5f165dccSWilliam A. Kennington III from.nl_family = AF_NETLINK; 138*5f165dccSWilliam A. Kennington III 139*5f165dccSWilliam A. Kennington III msghdr hdr{}; 140*5f165dccSWilliam A. Kennington III hdr.msg_name = &from; 141*5f165dccSWilliam A. Kennington III hdr.msg_namelen = sizeof(from); 142*5f165dccSWilliam A. Kennington III hdr.msg_iov = &iov; 143*5f165dccSWilliam A. Kennington III hdr.msg_iovlen = 1; 144*5f165dccSWilliam A. Kennington III 145*5f165dccSWilliam A. Kennington III // We only do multiple recvs if we have a MULTI type message 146*5f165dccSWilliam A. Kennington III bool done = true; 147*5f165dccSWilliam A. Kennington III do 148*5f165dccSWilliam A. Kennington III { 149*5f165dccSWilliam A. Kennington III ssize_t recvd = recvmsg(sock, &hdr, 0); 150*5f165dccSWilliam A. Kennington III if (recvd < 0) 151*5f165dccSWilliam A. Kennington III { 152*5f165dccSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 153*5f165dccSWilliam A. Kennington III "netlink recvmsg"); 154*5f165dccSWilliam A. Kennington III } 155*5f165dccSWilliam A. Kennington III if (recvd == 0) 156*5f165dccSWilliam A. Kennington III { 157*5f165dccSWilliam A. Kennington III if (!done) 158*5f165dccSWilliam A. Kennington III { 159*5f165dccSWilliam A. Kennington III throw std::runtime_error("netlink recvmsg: Got empty payload"); 160*5f165dccSWilliam A. Kennington III } 161*5f165dccSWilliam A. Kennington III return; 162*5f165dccSWilliam A. Kennington III } 163*5f165dccSWilliam A. Kennington III 164*5f165dccSWilliam A. Kennington III std::string_view msgs(buf.data(), recvd); 165*5f165dccSWilliam A. Kennington III do 166*5f165dccSWilliam A. Kennington III { 167*5f165dccSWilliam A. Kennington III detail::processMsg(msgs, done, cb); 168*5f165dccSWilliam A. Kennington III } while (!done && !msgs.empty()); 169*5f165dccSWilliam A. Kennington III 170*5f165dccSWilliam A. Kennington III if (done && !msgs.empty()) 171*5f165dccSWilliam A. Kennington III { 172*5f165dccSWilliam A. Kennington III throw std::runtime_error("Extra unprocessed netlink messages"); 173*5f165dccSWilliam A. Kennington III } 174*5f165dccSWilliam A. Kennington III } while (!done); 175*5f165dccSWilliam A. Kennington III } 176*5f165dccSWilliam A. Kennington III 1771bbe3d1eSWilliam A. Kennington III std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data) 1781bbe3d1eSWilliam A. Kennington III { 1791bbe3d1eSWilliam A. Kennington III auto hdr = stdplus::raw::copyFrom<rtattr>(data); 1801bbe3d1eSWilliam A. Kennington III if (hdr.rta_len < RTA_LENGTH(0)) 1811bbe3d1eSWilliam A. Kennington III { 182f174bccfSWilliam A. Kennington III throw std::runtime_error(fmt::format( 183f174bccfSWilliam A. Kennington III "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0))); 1841bbe3d1eSWilliam A. Kennington III } 1851bbe3d1eSWilliam A. Kennington III if (data.size() < hdr.rta_len) 1861bbe3d1eSWilliam A. Kennington III { 187f174bccfSWilliam A. Kennington III throw std::runtime_error( 188f174bccfSWilliam A. Kennington III fmt::format("not enough message for rtattr: {} < {}", data.size(), 189f174bccfSWilliam A. Kennington III hdr.rta_len)); 1901bbe3d1eSWilliam A. Kennington III } 1911bbe3d1eSWilliam A. Kennington III auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0)); 1921bbe3d1eSWilliam A. Kennington III data.remove_prefix(RTA_ALIGN(hdr.rta_len)); 1931bbe3d1eSWilliam A. Kennington III return {hdr, attr}; 1941bbe3d1eSWilliam A. Kennington III } 1951bbe3d1eSWilliam A. Kennington III 1961bbe3d1eSWilliam A. Kennington III } // namespace netlink 1971bbe3d1eSWilliam A. Kennington III } // namespace network 1981bbe3d1eSWilliam A. Kennington III } // namespace phosphor 199