1*1bbe3d1eSWilliam A. Kennington III #include "netlink.hpp" 2*1bbe3d1eSWilliam A. Kennington III 3*1bbe3d1eSWilliam A. Kennington III #include "util.hpp" 4*1bbe3d1eSWilliam A. Kennington III 5*1bbe3d1eSWilliam A. Kennington III #include <linux/netlink.h> 6*1bbe3d1eSWilliam A. Kennington III #include <linux/rtnetlink.h> 7*1bbe3d1eSWilliam A. Kennington III #include <sys/socket.h> 8*1bbe3d1eSWilliam A. Kennington III #include <unistd.h> 9*1bbe3d1eSWilliam A. Kennington III 10*1bbe3d1eSWilliam A. Kennington III #include <array> 11*1bbe3d1eSWilliam A. Kennington III #include <stdexcept> 12*1bbe3d1eSWilliam A. Kennington III #include <stdplus/raw.hpp> 13*1bbe3d1eSWilliam A. Kennington III #include <system_error> 14*1bbe3d1eSWilliam A. Kennington III 15*1bbe3d1eSWilliam A. Kennington III namespace phosphor 16*1bbe3d1eSWilliam A. Kennington III { 17*1bbe3d1eSWilliam A. Kennington III namespace network 18*1bbe3d1eSWilliam A. Kennington III { 19*1bbe3d1eSWilliam A. Kennington III namespace netlink 20*1bbe3d1eSWilliam A. Kennington III { 21*1bbe3d1eSWilliam A. Kennington III namespace detail 22*1bbe3d1eSWilliam A. Kennington III { 23*1bbe3d1eSWilliam A. Kennington III 24*1bbe3d1eSWilliam A. Kennington III void processMsg(std::string_view& msgs, bool& done, const ReceiveCallback& cb) 25*1bbe3d1eSWilliam A. Kennington III { 26*1bbe3d1eSWilliam A. Kennington III // Parse and update the message buffer 27*1bbe3d1eSWilliam A. Kennington III auto hdr = stdplus::raw::copyFrom<nlmsghdr>(msgs); 28*1bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_len < sizeof(hdr)) 29*1bbe3d1eSWilliam A. Kennington III { 30*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Invalid nlmsg length"); 31*1bbe3d1eSWilliam A. Kennington III } 32*1bbe3d1eSWilliam A. Kennington III if (msgs.size() < hdr.nlmsg_len) 33*1bbe3d1eSWilliam A. Kennington III { 34*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Bad nlmsg payload"); 35*1bbe3d1eSWilliam A. Kennington III } 36*1bbe3d1eSWilliam A. Kennington III auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN); 37*1bbe3d1eSWilliam A. Kennington III msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len)); 38*1bbe3d1eSWilliam A. Kennington III 39*1bbe3d1eSWilliam A. Kennington III // Figure out how to handle the individual message 40*1bbe3d1eSWilliam A. Kennington III bool doCallback = true; 41*1bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_flags & NLM_F_MULTI) 42*1bbe3d1eSWilliam A. Kennington III { 43*1bbe3d1eSWilliam A. Kennington III done = false; 44*1bbe3d1eSWilliam A. Kennington III } 45*1bbe3d1eSWilliam A. Kennington III if (hdr.nlmsg_type == NLMSG_NOOP) 46*1bbe3d1eSWilliam A. Kennington III { 47*1bbe3d1eSWilliam A. Kennington III doCallback = false; 48*1bbe3d1eSWilliam A. Kennington III } 49*1bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_DONE) 50*1bbe3d1eSWilliam A. Kennington III { 51*1bbe3d1eSWilliam A. Kennington III if (done) 52*1bbe3d1eSWilliam A. Kennington III { 53*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got done for non-multi msg"); 54*1bbe3d1eSWilliam A. Kennington III } 55*1bbe3d1eSWilliam A. Kennington III done = true; 56*1bbe3d1eSWilliam A. Kennington III doCallback = false; 57*1bbe3d1eSWilliam A. Kennington III } 58*1bbe3d1eSWilliam A. Kennington III else if (hdr.nlmsg_type == NLMSG_ERROR) 59*1bbe3d1eSWilliam A. Kennington III { 60*1bbe3d1eSWilliam A. Kennington III auto err = stdplus::raw::copyFrom<nlmsgerr>(msg); 61*1bbe3d1eSWilliam A. Kennington III // This is just an ACK so don't do the callback 62*1bbe3d1eSWilliam A. Kennington III if (err.error <= 0) 63*1bbe3d1eSWilliam A. Kennington III { 64*1bbe3d1eSWilliam A. Kennington III doCallback = false; 65*1bbe3d1eSWilliam A. Kennington III } 66*1bbe3d1eSWilliam A. Kennington III } 67*1bbe3d1eSWilliam A. Kennington III // All multi-msg headers must have the multi flag 68*1bbe3d1eSWilliam A. Kennington III if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI)) 69*1bbe3d1eSWilliam A. Kennington III { 70*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Got non-multi msg before done"); 71*1bbe3d1eSWilliam A. Kennington III } 72*1bbe3d1eSWilliam A. Kennington III if (doCallback) 73*1bbe3d1eSWilliam A. Kennington III { 74*1bbe3d1eSWilliam A. Kennington III cb(hdr, msg); 75*1bbe3d1eSWilliam A. Kennington III } 76*1bbe3d1eSWilliam A. Kennington III } 77*1bbe3d1eSWilliam A. Kennington III 78*1bbe3d1eSWilliam A. Kennington III static void receive(int sock, const ReceiveCallback& cb) 79*1bbe3d1eSWilliam A. Kennington III { 80*1bbe3d1eSWilliam A. Kennington III // We need to make sure we have enough room for an entire packet otherwise 81*1bbe3d1eSWilliam A. Kennington III // it gets truncated. The netlink docs guarantee packets will not exceed 8K 82*1bbe3d1eSWilliam A. Kennington III std::array<char, 8192> buf; 83*1bbe3d1eSWilliam A. Kennington III 84*1bbe3d1eSWilliam A. Kennington III iovec iov{}; 85*1bbe3d1eSWilliam A. Kennington III iov.iov_base = buf.data(); 86*1bbe3d1eSWilliam A. Kennington III iov.iov_len = buf.size(); 87*1bbe3d1eSWilliam A. Kennington III 88*1bbe3d1eSWilliam A. Kennington III sockaddr_nl from{}; 89*1bbe3d1eSWilliam A. Kennington III from.nl_family = AF_NETLINK; 90*1bbe3d1eSWilliam A. Kennington III 91*1bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 92*1bbe3d1eSWilliam A. Kennington III hdr.msg_name = &from; 93*1bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(from); 94*1bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 95*1bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 96*1bbe3d1eSWilliam A. Kennington III 97*1bbe3d1eSWilliam A. Kennington III // We only do multiple recvs if we have a MULTI type message 98*1bbe3d1eSWilliam A. Kennington III bool done = true; 99*1bbe3d1eSWilliam A. Kennington III do 100*1bbe3d1eSWilliam A. Kennington III { 101*1bbe3d1eSWilliam A. Kennington III ssize_t recvd = recvmsg(sock, &hdr, 0); 102*1bbe3d1eSWilliam A. Kennington III if (recvd < 0) 103*1bbe3d1eSWilliam A. Kennington III { 104*1bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 105*1bbe3d1eSWilliam A. Kennington III "netlink recvmsg"); 106*1bbe3d1eSWilliam A. Kennington III } 107*1bbe3d1eSWilliam A. Kennington III if (recvd == 0) 108*1bbe3d1eSWilliam A. Kennington III { 109*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("netlink recvmsg: Got empty payload"); 110*1bbe3d1eSWilliam A. Kennington III } 111*1bbe3d1eSWilliam A. Kennington III 112*1bbe3d1eSWilliam A. Kennington III std::string_view msgs(buf.data(), recvd); 113*1bbe3d1eSWilliam A. Kennington III do 114*1bbe3d1eSWilliam A. Kennington III { 115*1bbe3d1eSWilliam A. Kennington III processMsg(msgs, done, cb); 116*1bbe3d1eSWilliam A. Kennington III } while (!done && !msgs.empty()); 117*1bbe3d1eSWilliam A. Kennington III 118*1bbe3d1eSWilliam A. Kennington III if (done && !msgs.empty()) 119*1bbe3d1eSWilliam A. Kennington III { 120*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Extra unprocessed netlink messages"); 121*1bbe3d1eSWilliam A. Kennington III } 122*1bbe3d1eSWilliam A. Kennington III } while (!done); 123*1bbe3d1eSWilliam A. Kennington III } 124*1bbe3d1eSWilliam A. Kennington III 125*1bbe3d1eSWilliam A. Kennington III static void requestSend(int sock, void* data, size_t size) 126*1bbe3d1eSWilliam A. Kennington III { 127*1bbe3d1eSWilliam A. Kennington III sockaddr_nl dst{}; 128*1bbe3d1eSWilliam A. Kennington III dst.nl_family = AF_NETLINK; 129*1bbe3d1eSWilliam A. Kennington III 130*1bbe3d1eSWilliam A. Kennington III iovec iov{}; 131*1bbe3d1eSWilliam A. Kennington III iov.iov_base = data; 132*1bbe3d1eSWilliam A. Kennington III iov.iov_len = size; 133*1bbe3d1eSWilliam A. Kennington III 134*1bbe3d1eSWilliam A. Kennington III msghdr hdr{}; 135*1bbe3d1eSWilliam A. Kennington III hdr.msg_name = reinterpret_cast<sockaddr*>(&dst); 136*1bbe3d1eSWilliam A. Kennington III hdr.msg_namelen = sizeof(dst); 137*1bbe3d1eSWilliam A. Kennington III hdr.msg_iov = &iov; 138*1bbe3d1eSWilliam A. Kennington III hdr.msg_iovlen = 1; 139*1bbe3d1eSWilliam A. Kennington III 140*1bbe3d1eSWilliam A. Kennington III if (sendmsg(sock, &hdr, 0) < 0) 141*1bbe3d1eSWilliam A. Kennington III { 142*1bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), 143*1bbe3d1eSWilliam A. Kennington III "netlink sendmsg"); 144*1bbe3d1eSWilliam A. Kennington III } 145*1bbe3d1eSWilliam A. Kennington III } 146*1bbe3d1eSWilliam A. Kennington III 147*1bbe3d1eSWilliam A. Kennington III static int newRequestSocket(int protocol) 148*1bbe3d1eSWilliam A. Kennington III { 149*1bbe3d1eSWilliam A. Kennington III int sock = socket(AF_NETLINK, SOCK_RAW, protocol); 150*1bbe3d1eSWilliam A. Kennington III if (sock < 0) 151*1bbe3d1eSWilliam A. Kennington III { 152*1bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), "netlink open"); 153*1bbe3d1eSWilliam A. Kennington III } 154*1bbe3d1eSWilliam A. Kennington III 155*1bbe3d1eSWilliam A. Kennington III sockaddr_nl local{}; 156*1bbe3d1eSWilliam A. Kennington III local.nl_family = AF_NETLINK; 157*1bbe3d1eSWilliam A. Kennington III int r = bind(sock, reinterpret_cast<sockaddr*>(&local), sizeof(local)); 158*1bbe3d1eSWilliam A. Kennington III if (r < 0) 159*1bbe3d1eSWilliam A. Kennington III { 160*1bbe3d1eSWilliam A. Kennington III close(sock); 161*1bbe3d1eSWilliam A. Kennington III throw std::system_error(errno, std::generic_category(), "netlink bind"); 162*1bbe3d1eSWilliam A. Kennington III } 163*1bbe3d1eSWilliam A. Kennington III 164*1bbe3d1eSWilliam A. Kennington III return sock; 165*1bbe3d1eSWilliam A. Kennington III } 166*1bbe3d1eSWilliam A. Kennington III 167*1bbe3d1eSWilliam A. Kennington III void performRequest(int protocol, void* data, size_t size, 168*1bbe3d1eSWilliam A. Kennington III const ReceiveCallback& cb) 169*1bbe3d1eSWilliam A. Kennington III { 170*1bbe3d1eSWilliam A. Kennington III Descriptor sock(newRequestSocket(protocol)); 171*1bbe3d1eSWilliam A. Kennington III requestSend(sock(), data, size); 172*1bbe3d1eSWilliam A. Kennington III receive(sock(), cb); 173*1bbe3d1eSWilliam A. Kennington III } 174*1bbe3d1eSWilliam A. Kennington III 175*1bbe3d1eSWilliam A. Kennington III } // namespace detail 176*1bbe3d1eSWilliam A. Kennington III 177*1bbe3d1eSWilliam A. Kennington III std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data) 178*1bbe3d1eSWilliam A. Kennington III { 179*1bbe3d1eSWilliam A. Kennington III auto hdr = stdplus::raw::copyFrom<rtattr>(data); 180*1bbe3d1eSWilliam A. Kennington III if (hdr.rta_len < RTA_LENGTH(0)) 181*1bbe3d1eSWilliam A. Kennington III { 182*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Invalid rtattr length"); 183*1bbe3d1eSWilliam A. Kennington III } 184*1bbe3d1eSWilliam A. Kennington III if (data.size() < hdr.rta_len) 185*1bbe3d1eSWilliam A. Kennington III { 186*1bbe3d1eSWilliam A. Kennington III throw std::runtime_error("Not enough data for rtattr"); 187*1bbe3d1eSWilliam A. Kennington III } 188*1bbe3d1eSWilliam A. Kennington III auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0)); 189*1bbe3d1eSWilliam A. Kennington III data.remove_prefix(RTA_ALIGN(hdr.rta_len)); 190*1bbe3d1eSWilliam A. Kennington III return {hdr, attr}; 191*1bbe3d1eSWilliam A. Kennington III } 192*1bbe3d1eSWilliam A. Kennington III 193*1bbe3d1eSWilliam A. Kennington III } // namespace netlink 194*1bbe3d1eSWilliam A. Kennington III } // namespace network 195*1bbe3d1eSWilliam A. Kennington III } // namespace phosphor 196