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