30 #include <linux/genetlink.h> 31 #include <sys/socket.h> 33 #include <boost/asio/write.hpp> 36 #define SOL_NETLINK 270 38 #ifndef RTEXT_FILTER_SKIP_STATS 39 #define RTEXT_FILTER_SKIP_STATS (1 << 3) 48 : m_sock(make_shared<
boost::asio::posix::stream_descriptor>(io))
50 , m_seqNum(static_cast<uint32_t>(
time::system_clock::now().time_since_epoch().count()))
57 boost::system::error_code ec;
64 int fd = ::socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
66 BOOST_THROW_EXCEPTION(
Error(
"Cannot create netlink socket ("s + std::strerror(errno) +
")"));
71 const int bufsize = 1 * 1024 * 1024;
72 if (::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize,
sizeof(bufsize)) < 0) {
74 NDN_LOG_DEBUG(
"setting SO_RCVBUF failed: " << std::strerror(errno));
79 if (::setsockopt(fd,
SOL_NETLINK, NETLINK_PKTINFO, &one,
sizeof(one)) < 0) {
80 BOOST_THROW_EXCEPTION(
Error(
"Cannot enable NETLINK_PKTINFO ("s + std::strerror(errno) +
")"));
84 addr.nl_family = AF_NETLINK;
85 if (::bind(fd, reinterpret_cast<sockaddr*>(&addr),
sizeof(addr)) < 0) {
86 BOOST_THROW_EXCEPTION(
Error(
"Cannot bind netlink socket ("s + std::strerror(errno) +
")"));
90 socklen_t len =
sizeof(addr);
91 if (::getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &len) < 0) {
92 BOOST_THROW_EXCEPTION(
Error(
"Cannot obtain netlink socket address ("s + std::strerror(errno) +
")"));
94 if (len !=
sizeof(addr)) {
95 BOOST_THROW_EXCEPTION(
Error(
"Wrong address length (" +
to_string(len) +
")"));
97 if (addr.nl_family != AF_NETLINK) {
98 BOOST_THROW_EXCEPTION(
Error(
"Wrong address family (" +
to_string(addr.nl_family) +
")"));
103 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK 105 if (::setsockopt(fd,
SOL_NETLINK, NETLINK_EXT_ACK, &one,
sizeof(one)) < 0) {
107 NDN_LOG_DEBUG(
"setting NETLINK_EXT_ACK failed: " << std::strerror(errno));
109 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK 116 &group,
sizeof(group)) < 0) {
117 BOOST_THROW_EXCEPTION(
Error(
"Cannot join netlink group " +
to_string(group) +
118 " (" + std::strerror(errno) +
")"));
132 m_pendingRequests.erase(seq);
135 bool wasEmpty = m_pendingRequests.empty();
136 m_pendingRequests.emplace(seq, std::move(cb));
145 #define NLMSG_STRINGIFY(x) case NLMSG_##x: return to_string(type) + "<" #x ">" 154 #undef NLMSG_STRINGIFY 158 NetlinkSocket::asyncWait()
160 m_sock->async_read_some(boost::asio::null_buffers(),
162 [
this, sock =
m_sock] (
const boost::system::error_code& ec,
size_t) {
163 if (!sock->is_open() || ec == boost::asio::error::operation_aborted) {
169 BOOST_THROW_EXCEPTION(
Error(
"Netlink socket read error (" + ec.message() +
")"));
172 receiveAndValidate();
173 if (!m_pendingRequests.empty())
180 NetlinkSocket::receiveAndValidate()
182 sockaddr_nl sender{};
184 iov.iov_base = m_buffer.data();
185 iov.iov_len = m_buffer.size();
186 uint8_t cmsgBuffer[CMSG_SPACE(
sizeof(nl_pktinfo))];
188 msg.msg_name = &sender;
189 msg.msg_namelen =
sizeof(sender);
192 msg.msg_control = cmsgBuffer;
193 msg.msg_controllen =
sizeof(cmsgBuffer);
195 ssize_t nBytesRead = ::recvmsg(
m_sock->native_handle(), &msg, 0);
196 if (nBytesRead < 0) {
197 std::string errorString = std::strerror(errno);
198 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
204 BOOST_THROW_EXCEPTION(
Error(
"Netlink socket receive error (" + errorString +
")"));
207 NDN_LOG_TRACE(
"read " << nBytesRead <<
" bytes from netlink socket");
209 if (msg.msg_flags & MSG_TRUNC) {
211 BOOST_THROW_EXCEPTION(
Error(
"Received truncated netlink message"));
215 if (msg.msg_namelen >=
sizeof(sender) && sender.nl_pid != 0) {
216 NDN_LOG_TRACE(
"ignoring message from pid=" << sender.nl_pid);
220 uint32_t nlGroup = 0;
221 for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg !=
nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
223 cmsg->cmsg_type == NETLINK_PKTINFO &&
224 cmsg->cmsg_len == CMSG_LEN(
sizeof(nl_pktinfo))) {
225 const nl_pktinfo* pktinfo = reinterpret_cast<nl_pktinfo*>(CMSG_DATA(cmsg));
226 nlGroup = pktinfo->group;
230 NetlinkMessage nlmsg(m_buffer.data(), static_cast<size_t>(nBytesRead));
231 for (; nlmsg.isValid(); nlmsg = nlmsg.getNext()) {
232 NDN_LOG_TRACE(
"parsing " << (nlmsg->nlmsg_flags & NLM_F_MULTI ?
"multi-part " :
"") <<
234 " len=" << nlmsg->nlmsg_len <<
235 " seq=" << nlmsg->nlmsg_seq <<
236 " pid=" << nlmsg->nlmsg_pid <<
237 " group=" << nlGroup);
239 auto cbIt = m_pendingRequests.end();
242 cbIt = m_pendingRequests.find(0);
244 else if (nlmsg->nlmsg_pid ==
m_pid) {
246 cbIt = m_pendingRequests.find(nlmsg->nlmsg_seq);
253 if (cbIt == m_pendingRequests.end()) {
257 else if (nlmsg->nlmsg_flags & NLM_F_DUMP_INTR) {
259 BOOST_THROW_EXCEPTION(
Error(
"Inconsistency detected in netlink dump"));
264 BOOST_ASSERT(cbIt->second);
272 if (nlGroup == 0 && (!(nlmsg->nlmsg_flags & NLM_F_MULTI) || nlmsg->nlmsg_type == NLMSG_DONE)) {
273 NDN_LOG_TRACE(
"removing handler for seq=" << nlmsg->nlmsg_seq);
274 BOOST_ASSERT(cbIt != m_pendingRequests.end());
275 m_pendingRequests.erase(cbIt);
298 alignas(NLMSG_ALIGNTO) ifinfomsg ifi;
299 alignas(NLMSG_ALIGNTO) rtattr rta;
300 alignas(NLMSG_ALIGNTO) uint32_t rtext;
303 auto request = make_shared<RtnlRequest>();
304 request->nlh.nlmsg_type = nlmsgType;
305 request->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
306 request->nlh.nlmsg_seq = ++
m_seqNum;
307 request->nlh.nlmsg_pid =
m_pid;
308 request->ifi.ifi_family = AF_UNSPEC;
309 request->rta.rta_type = IFLA_EXT_MASK;
310 request->rta.rta_len = RTA_LENGTH(
sizeof(request->rtext));
312 request->nlh.nlmsg_len = NLMSG_SPACE(
sizeof(ifinfomsg)) + request->rta.rta_len;
316 boost::asio::async_write(*
m_sock, boost::asio::buffer(request.get(), request->nlh.nlmsg_len),
318 [
this, request] (
const boost::system::error_code& ec,
size_t) {
321 <<
" seq=" << request->nlh.nlmsg_seq);
323 else if (ec != boost::asio::error::operation_aborted) {
325 BOOST_THROW_EXCEPTION(
Error(
"Failed to send netlink request (" + ec.message() +
")"));
333 #define RTM_STRINGIFY(x) case RTM_##x: return to_string(type) + "<" #x ">" 353 m_cachedFamilyIds[
"nlctrl"] = GENL_ID_CTRL;
365 const void* payload,
size_t payloadLen,
368 auto it = m_cachedFamilyIds.find(familyName);
369 if (it != m_cachedFamilyIds.end()) {
370 if (it->second >= GENL_MIN_ID) {
371 sendRequest(it->second, command, payload, payloadLen, std::move(messageCb));
379 auto ret = m_familyResolvers.emplace(std::piecewise_construct,
380 std::forward_as_tuple(familyName),
381 std::forward_as_tuple(familyName, *
this));
382 auto& resolver = ret.first->second;
385 resolver.onResolved.connectSingleShot([=] (uint16_t familyId) {
386 m_cachedFamilyIds[familyName] = familyId;
388 resolver.onError.connectSingleShot([=] {
389 m_cachedFamilyIds[familyName] = 0;
392 resolver.onResolved.connectSingleShot([=, cb = std::move(messageCb)] (uint16_t familyId) {
393 sendRequest(familyId, command, payload, payloadLen, std::move(cb));
396 resolver.onError.connectSingleShot(std::move(errorCb));
404 struct GenlRequestHeader
406 alignas(NLMSG_ALIGNTO) nlmsghdr nlh;
407 alignas(NLMSG_ALIGNTO) genlmsghdr genlh;
409 static_assert(
sizeof(GenlRequestHeader) == NLMSG_SPACE(GENL_HDRLEN),
"");
411 auto hdr = make_shared<GenlRequestHeader>();
412 hdr->nlh.nlmsg_len =
sizeof(GenlRequestHeader) + payloadLen;
413 hdr->nlh.nlmsg_type = familyId;
414 hdr->nlh.nlmsg_flags = NLM_F_REQUEST;
416 hdr->nlh.nlmsg_pid =
m_pid;
417 hdr->genlh.cmd = command;
418 hdr->genlh.version = 1;
422 std::array<boost::asio::const_buffer, 2> bufs = {
423 boost::asio::buffer(hdr.get(),
sizeof(GenlRequestHeader)),
424 boost::asio::buffer(payload, payloadLen)
426 boost::asio::async_write(*
m_sock, bufs,
428 [
this, hdr] (
const boost::system::error_code& ec,
size_t) {
431 " cmd=" << static_cast<unsigned>(hdr->genlh.cmd) <<
432 " seq=" << hdr->nlh.nlmsg_seq);
434 else if (ec != boost::asio::error::operation_aborted) {
436 BOOST_THROW_EXCEPTION(
Error(
"Failed to send netlink request (" + ec.message() +
")"));
443 , m_family(std::move(familyName))
445 if (m_family.size() >= GENL_NAMSIZ) {
446 BOOST_THROW_EXCEPTION(std::invalid_argument(
"netlink family name '" + m_family +
"' too long"));
454 GenlFamilyResolver::asyncResolve()
456 struct FamilyNameAttribute
458 alignas(NLMSG_ALIGNTO) nlattr nla;
459 alignas(NLMSG_ALIGNTO)
char name[GENL_NAMSIZ];
462 auto attr = make_shared<FamilyNameAttribute>();
463 attr->nla.nla_type = CTRL_ATTR_FAMILY_NAME;
464 attr->nla.nla_len = NLA_HDRLEN + m_family.size() + 1;
465 ::strncpy(attr->name, m_family.data(), GENL_NAMSIZ);
467 m_sock.
sendRequest(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, attr.get(), attr->nla.nla_len,
469 [
this, attr] (
const auto& msg) { this->handleResolve(msg); });
473 GenlFamilyResolver::handleResolve(
const NetlinkMessage& nlmsg)
475 switch (nlmsg->nlmsg_type) {
477 const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
478 if (err ==
nullptr) {
481 else if (err->error != 0) {
482 NDN_LOG_DEBUG(
"failed to resolve netlink family " << m_family <<
": " 483 << std::strerror(
std::abs(err->error)));
490 const genlmsghdr* genlh = nlmsg.getPayload<genlmsghdr>();
491 if (genlh ==
nullptr) {
495 if (genlh->cmd != CTRL_CMD_NEWFAMILY) {
496 NDN_LOG_WARN(
"unexpected genl cmd=" << static_cast<unsigned>(genlh->cmd));
500 auto attrs = nlmsg.getAttributes<nlattr>(genlh);
501 auto familyName = attrs.getAttributeByType<std::string>(CTRL_ATTR_FAMILY_NAME);
502 if (familyName && *familyName != m_family) {
503 NDN_LOG_WARN(
"CTRL_ATTR_FAMILY_NAME mismatch: " << *familyName <<
" != " << m_family);
506 auto familyId = attrs.getAttributeByType<uint16_t>(CTRL_ATTR_FAMILY_ID);
511 if (*familyId < GENL_MIN_ID) {
512 NDN_LOG_WARN(
"invalid CTRL_ATTR_FAMILY_ID=" << *familyId);
516 NDN_LOG_TRACE(
"resolved netlink family name=" << m_family <<
" id=" << *familyId);
532 if (type >= GENL_MIN_ID) {
533 for (
const auto& p : m_cachedFamilyIds) {
534 if (p.second == type) {
535 return to_string(type) +
"<" + p.first +
">";
#define NLMSG_STRINGIFY(x)
Copyright (c) 2011-2015 Regents of the University of California.
util::Signal< GenlFamilyResolver > onError
#define NDN_LOG_WARN(expression)
#define NDN_LOG_DEBUG(expression)
RtnlSocket(boost::asio::io_service &io)
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
#define NDN_LOG_ERROR(expression)
std::string nlmsgTypeToString(uint16_t type) const final
void sendRequest(const std::string &familyName, uint8_t command, const void *payload, size_t payloadLen, MessageCallback messageCb, std::function< void()> errorCb)
uint32_t m_seqNum
sequence number of the last netlink request sent to the kernel
void sendDumpRequest(uint16_t nlmsgType, MessageCallback cb)
#define NDN_LOG_TRACE(expression)
std::string nlmsgTypeToString(uint16_t type) const final
void registerRequestCallback(uint32_t seq, MessageCallback cb)
NetworkMonitor::Error Error
GenlSocket(boost::asio::io_service &io)
shared_ptr< boost::asio::posix::stream_descriptor > m_sock
netlink socket descriptor
void registerNotificationCallback(MessageCallback cb)
void joinGroup(int group)
#define NDN_LOG_INIT(name)
declare a log module
NetlinkSocket(boost::asio::io_service &io)
std::string to_string(const V &v)
std::function< void(const NetlinkMessage &)> MessageCallback
virtual std::string nlmsgTypeToString(uint16_t type) const
uint32_t m_pid
port ID of this socket
util::Signal< GenlFamilyResolver, uint16_t > onResolved
GenlFamilyResolver(std::string familyName, GenlSocket &socket)
#define RTEXT_FILTER_SKIP_STATS