29 #include <linux/genetlink.h>
30 #include <sys/socket.h>
33 #define SOL_NETLINK 270
35 #ifndef NETLINK_CAP_ACK
36 #define NETLINK_CAP_ACK 10
38 #ifndef NETLINK_GET_STRICT_CHK
39 #define NETLINK_GET_STRICT_CHK 12
58 template<
typename Protocol>
65 template<
typename Protocol>
67 name(
const Protocol&)
const
72 template<
typename Protocol>
74 data(
const Protocol&)
const
79 template<
typename Protocol>
81 size(
const Protocol&)
const
83 return sizeof(m_value);
91 : m_sock(make_shared<
boost::asio::generic::raw_protocol::socket>(io))
93 , m_seqNum(static_cast<uint32_t>(
time::system_clock::now().time_since_epoch().count()))
100 boost::system::error_code ec;
107 boost::asio::generic::raw_protocol proto(AF_NETLINK, protocol);
109 int fd = ::socket(proto.family(), proto.type() | SOCK_CLOEXEC, proto.protocol());
114 boost::system::error_code ec;
115 m_sock->assign(proto, fd, ec);
121 m_sock->set_option(boost::asio::socket_base::receive_buffer_size(1 * 1024 * 1024), ec);
124 NDN_LOG_DEBUG(
"setting receive buffer size failed: " << ec.message());
130 NDN_THROW(
Error(
"Cannot enable NETLINK_PKTINFO: " + ec.message()));
134 addr.nl_family = AF_NETLINK;
135 if (::bind(
m_sock->native_handle(),
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) < 0) {
140 socklen_t len =
sizeof(addr);
141 if (::getsockname(
m_sock->native_handle(),
reinterpret_cast<sockaddr*
>(&addr), &len) < 0) {
144 if (len !=
sizeof(addr)) {
147 if (addr.nl_family != AF_NETLINK) {
157 NDN_LOG_DEBUG(
"setting NETLINK_CAP_ACK failed: " << ec.message());
160 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
165 NDN_LOG_DEBUG(
"setting NETLINK_EXT_ACK failed: " << ec.message());
167 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
173 NDN_LOG_DEBUG(
"setting NETLINK_GET_STRICT_CHK failed: " << ec.message());
180 boost::system::error_code ec;
197 m_pendingRequests.erase(seq);
200 bool wasEmpty = m_pendingRequests.empty();
201 m_pendingRequests.emplace(seq,
std::move(cb));
210 #define NLMSG_STRINGIFY(x) case NLMSG_##x: return to_string(type) + "<" #x ">"
219 #undef NLMSG_STRINGIFY
223 NetlinkSocket::asyncWait()
226 auto handler = [
this, sock =
m_sock] (
const boost::system::error_code& ec) {
227 if (!sock->is_open() || ec == boost::asio::error::operation_aborted) {
233 NDN_THROW(
Error(
"Netlink socket read error (" + ec.message() +
")"));
236 receiveAndValidate();
237 if (!m_pendingRequests.empty())
242 #if BOOST_VERSION >= 106600
243 m_sock->async_wait(boost::asio::socket_base::wait_read,
std::move(handler));
245 m_sock->async_receive(boost::asio::null_buffers(),
246 [h =
std::move(handler)] (
const boost::system::error_code& ec,
size_t) { h(ec); });
251 NetlinkSocket::receiveAndValidate()
253 sockaddr_nl sender{};
255 iov.iov_base = m_buffer.data();
256 iov.iov_len = m_buffer.size();
257 uint8_t cmsgBuffer[CMSG_SPACE(
sizeof(nl_pktinfo))];
259 msg.msg_name = &sender;
260 msg.msg_namelen =
sizeof(sender);
263 msg.msg_control = cmsgBuffer;
264 msg.msg_controllen =
sizeof(cmsgBuffer);
266 ssize_t nBytesRead = ::recvmsg(
m_sock->native_handle(), &msg, 0);
267 if (nBytesRead < 0) {
268 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
277 NDN_LOG_TRACE(
"read " << nBytesRead <<
" bytes from netlink socket");
279 if (msg.msg_flags & MSG_TRUNC) {
285 if (msg.msg_namelen >=
sizeof(sender) && sender.nl_pid != 0) {
286 NDN_LOG_TRACE(
"ignoring message from pid=" << sender.nl_pid);
290 uint32_t nlGroup = 0;
291 for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg !=
nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
293 cmsg->cmsg_type == NETLINK_PKTINFO &&
294 cmsg->cmsg_len == CMSG_LEN(
sizeof(nl_pktinfo))) {
295 const nl_pktinfo* pktinfo =
reinterpret_cast<nl_pktinfo*
>(CMSG_DATA(cmsg));
296 nlGroup = pktinfo->group;
300 NetlinkMessage nlmsg(m_buffer.data(),
static_cast<size_t>(nBytesRead));
301 for (; nlmsg.isValid(); nlmsg = nlmsg.getNext()) {
302 NDN_LOG_TRACE(
"parsing " << (nlmsg->nlmsg_flags & NLM_F_MULTI ?
"multi-part " :
"") <<
304 " len=" << nlmsg->nlmsg_len <<
305 " seq=" << nlmsg->nlmsg_seq <<
306 " pid=" << nlmsg->nlmsg_pid <<
307 " group=" << nlGroup);
309 auto cbIt = m_pendingRequests.end();
312 cbIt = m_pendingRequests.find(0);
314 else if (nlmsg->nlmsg_pid ==
m_pid) {
316 cbIt = m_pendingRequests.find(nlmsg->nlmsg_seq);
323 if (cbIt == m_pendingRequests.end()) {
327 else if (nlmsg->nlmsg_flags & NLM_F_DUMP_INTR) {
334 BOOST_ASSERT(cbIt->second);
342 if (nlGroup == 0 && (!(nlmsg->nlmsg_flags & NLM_F_MULTI) || nlmsg->nlmsg_type == NLMSG_DONE)) {
343 NDN_LOG_TRACE(
"removing handler for seq=" << nlmsg->nlmsg_seq);
344 BOOST_ASSERT(cbIt != m_pendingRequests.end());
345 m_pendingRequests.erase(cbIt);
366 struct RtnlMessageHeader
368 alignas(NLMSG_ALIGNTO) nlmsghdr nlh;
370 static_assert(
sizeof(RtnlMessageHeader) == NLMSG_HDRLEN,
"");
372 auto hdr = make_shared<RtnlMessageHeader>();
373 hdr->nlh.nlmsg_len =
sizeof(RtnlMessageHeader) + payloadLen;
374 hdr->nlh.nlmsg_type = nlmsgType;
375 hdr->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
377 hdr->nlh.nlmsg_pid =
m_pid;
381 std::array<boost::asio::const_buffer, 2> bufs = {
382 boost::asio::buffer(hdr.get(),
sizeof(RtnlMessageHeader)),
383 boost::asio::buffer(payload, payloadLen)
387 [
this, hdr] (
const boost::system::error_code& ec,
size_t) {
390 <<
" seq=" << hdr->nlh.nlmsg_seq);
392 else if (ec != boost::asio::error::operation_aborted) {
394 NDN_THROW(
Error(
"Failed to send netlink request (" + ec.message() +
")"));
402 #define RTM_STRINGIFY(x) case RTM_##x: return to_string(type) + "<" #x ">"
422 m_cachedFamilyIds[
"nlctrl"] = GENL_ID_CTRL;
434 const void* payload,
size_t payloadLen,
437 auto it = m_cachedFamilyIds.find(familyName);
438 if (it != m_cachedFamilyIds.end()) {
439 if (it->second >= GENL_MIN_ID) {
448 auto ret = m_familyResolvers.emplace(std::piecewise_construct,
449 std::forward_as_tuple(familyName),
450 std::forward_as_tuple(familyName, *
this));
451 auto& resolver = ret.first->second;
454 resolver.onResolved.connectSingleShot([=] (uint16_t familyId) {
455 m_cachedFamilyIds[familyName] = familyId;
457 resolver.onError.connectSingleShot([=] {
458 m_cachedFamilyIds[familyName] = 0;
461 resolver.onResolved.connectSingleShot([=, cb =
std::move(messageCb)] (uint16_t familyId) {
465 resolver.onError.connectSingleShot(
std::move(errorCb));
473 struct GenlMessageHeader
475 alignas(NLMSG_ALIGNTO) nlmsghdr nlh;
476 alignas(NLMSG_ALIGNTO) genlmsghdr genlh;
478 static_assert(
sizeof(GenlMessageHeader) == NLMSG_SPACE(GENL_HDRLEN),
"");
480 auto hdr = make_shared<GenlMessageHeader>();
481 hdr->nlh.nlmsg_len =
sizeof(GenlMessageHeader) + payloadLen;
482 hdr->nlh.nlmsg_type = familyId;
483 hdr->nlh.nlmsg_flags = NLM_F_REQUEST;
485 hdr->nlh.nlmsg_pid =
m_pid;
486 hdr->genlh.cmd = command;
487 hdr->genlh.version = 1;
491 std::array<boost::asio::const_buffer, 2> bufs = {
492 boost::asio::buffer(hdr.get(),
sizeof(GenlMessageHeader)),
493 boost::asio::buffer(payload, payloadLen)
497 [
this, hdr] (
const boost::system::error_code& ec,
size_t) {
500 " cmd=" <<
static_cast<unsigned>(hdr->genlh.cmd) <<
501 " seq=" << hdr->nlh.nlmsg_seq);
503 else if (ec != boost::asio::error::operation_aborted) {
505 NDN_THROW(
Error(
"Failed to send netlink request (" + ec.message() +
")"));
512 , m_family(std::
move(familyName))
514 if (m_family.size() >= GENL_NAMSIZ) {
515 NDN_THROW(std::invalid_argument(
"netlink family name '" + m_family +
"' too long"));
523 GenlFamilyResolver::asyncResolve()
525 struct FamilyNameAttribute
527 alignas(NLA_ALIGNTO) nlattr nla;
528 alignas(NLA_ALIGNTO)
char name[GENL_NAMSIZ];
531 auto attr = make_shared<FamilyNameAttribute>();
532 attr->nla.nla_type = CTRL_ATTR_FAMILY_NAME;
533 attr->nla.nla_len = NLA_HDRLEN + m_family.size() + 1;
534 ::strncpy(attr->name, m_family.data(), GENL_NAMSIZ);
536 m_sock.
sendRequest(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, attr.get(), attr->nla.nla_len,
538 [
this, attr] (
const auto& msg) { this->handleResolve(msg); });
542 GenlFamilyResolver::handleResolve(
const NetlinkMessage& nlmsg)
544 switch (nlmsg->nlmsg_type) {
546 const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
547 if (err ==
nullptr) {
550 else if (err->error != 0) {
551 NDN_LOG_DEBUG(
" failed to resolve netlink family " << m_family
552 <<
": " << std::strerror(
std::abs(err->error)));
559 const genlmsghdr* genlh = nlmsg.getPayload<genlmsghdr>();
560 if (genlh ==
nullptr) {
564 if (genlh->cmd != CTRL_CMD_NEWFAMILY) {
565 NDN_LOG_WARN(
"unexpected genl cmd=" <<
static_cast<unsigned>(genlh->cmd));
569 auto attrs = nlmsg.getAttributes<nlattr>(genlh);
570 auto familyName = attrs.getAttributeByType<std::string>(CTRL_ATTR_FAMILY_NAME);
571 if (familyName && *familyName != m_family) {
572 NDN_LOG_WARN(
"CTRL_ATTR_FAMILY_NAME mismatch: " << *familyName <<
" != " << m_family);
575 auto familyId = attrs.getAttributeByType<uint16_t>(CTRL_ATTR_FAMILY_ID);
580 if (*familyId < GENL_MIN_ID) {
581 NDN_LOG_WARN(
"invalid CTRL_ATTR_FAMILY_ID=" << *familyId);
585 NDN_LOG_TRACE(
" resolved netlink family name=" << m_family <<
" id=" << *familyId);
601 if (type >= GENL_MIN_ID) {
602 for (
const auto& p : m_cachedFamilyIds) {
603 if (p.second == type) {
604 return to_string(type) +
"<" + p.first +
">";