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