xref: /openbmc/phosphor-networkd/src/netlink.cpp (revision 2ac2660c63734e8a8cc8cb4dd29afc40eb99a4dc)
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