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
62ac2660cSWilliam A. Kennington III #include <stdplus/fd/create.hpp>
72ac2660cSWilliam A. Kennington III #include <stdplus/fd/ops.hpp>
81bbe3d1eSWilliam A. Kennington III #include <stdplus/raw.hpp>
989d734b9SPatrick Williams
1089d734b9SPatrick Williams #include <array>
11*cafc1512SWilliam A. Kennington III #include <format>
1289d734b9SPatrick 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
processMsg(std::string_view & msgs,bool & done,ReceiveCallback cb)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(
33*cafc1512SWilliam A. Kennington III std::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(
39*cafc1512SWilliam A. Kennington III std::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
requestSend(int sock,void * data,size_t size)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
makeSocket(int protocol)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
performRequest(int protocol,void * data,size_t size,ReceiveCallback cb)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
receive(int sock,ReceiveCallback cb)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
extractRtAttr(std::string_view & data)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 {
188*cafc1512SWilliam A. Kennington III throw std::runtime_error(std::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(
194*cafc1512SWilliam A. Kennington III std::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