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