Skip to content

Commit

Permalink
add Linux code to adapter-watcher
Browse files Browse the repository at this point in the history
  • Loading branch information
maloel committed Sep 5, 2024
1 parent 1a322aa commit b2e5e7e
Showing 1 changed file with 116 additions and 4 deletions.
120 changes: 116 additions & 4 deletions third-party/rsutils/src/adapter-watcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
#pragma comment( lib, "iphlpapi.lib" )

#else

#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/eventfd.h>
#include <poll.h>

#endif


Expand All @@ -30,9 +37,11 @@ class adapter_watcher_singleton

#ifdef WIN32
HANDLE _done;
std::thread _th;
#else
int _socket;
int _done;
#endif
std::thread _th;

public:
public_signal callbacks;
Expand All @@ -42,7 +51,7 @@ class adapter_watcher_singleton
: _done( CreateEvent(
nullptr /*security attrs*/, TRUE /*manual reset*/, FALSE /*init unset*/, nullptr /*no name*/ ) )
, _th(
[this]()
[this]
{
// We're going to wait on the following handles:
HANDLE handles[2];
Expand Down Expand Up @@ -78,9 +87,81 @@ class adapter_watcher_singleton
CancelIPChangeNotify( &overlap );
} )
#else
// https://manpages.ubuntu.com/manpages/oracular/en/man7/netlink.7.html
// https://stackoverflow.com/questions/27008067/how-to-get-notified-about-network-interface-changes-with-netlist-and-rtmgrp-link
On Linux, it is done opening and reading on a special kind of socket.Documentation here.Some nice examples here.
: _socket( open_netlink() )
, _done( eventfd(0, 0) )
, _th(
[this]
{
// We're going to wait on the following handles:
LOG_DEBUG( "starting network adapter watcher" );

int const timeout = -1; // negative value = infinite
int const nfds = 2;
pollfd fds[nfds];
memset( fds, 0, sizeof(fds) );
fds[0].fd = _done;
fds[0].events = POLLIN;
fds[1].fd = _socket;
fds[1].events = POLLIN;

while( true )
{
fds[0].revents = 0;
fds[1].revents = 0;
auto rv = poll( fds, nfds, timeout );
if( rv <= 0 )
{
LOG_ERROR( "poll failed: " << rv );
break;
}

if( fds[0].revents != 0 )
break; // we're done

LOG_DEBUG( "network adapter changes detected!" );

char buf[4096];
struct sockaddr_nl snl;
iovec iov = { buf, sizeof buf };
msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0 };
int status = recvmsg( _socket, &msg, 0 );
if( status < 0 )
{
// Socket non-blocking so bail out once we have read everything
if( errno == EWOULDBLOCK || errno == EAGAIN )
continue;

// Anything else is an error
LOG_DEBUG( "read_netlink: Error recvmsg: " << status );
break;
}
if( status == 0 )
LOG_DEBUG( "read_netlink: EOF" );

// We need to handle more than one message per recvmsg
for( auto h = (struct nlmsghdr *) buf;
NLMSG_OK( h, (unsigned int)status );
h = NLMSG_NEXT( h, status ) )
{
// Finish reading
if( h->nlmsg_type == NLMSG_DONE )
continue;

// Message is some kind of error
if( h->nlmsg_type == NLMSG_ERROR )
{
LOG_DEBUG( "read_netlink: Message is an error - decode TBD" );
continue;
}
}

// Call our clients once for all the messages... outside the loop
callbacks.raise();
}

LOG_DEBUG( "exiting network adapter watcher" );
} )
#endif
{
LOG_DEBUG( "network adapter watcher singleton is up" );
Expand All @@ -96,8 +177,39 @@ class adapter_watcher_singleton
}
CloseHandle( _done );
#else
if( _th.joinable() )
{
if( write( _done, &_done, sizeof( &_done ) ) != sizeof( &_done ) )
;
_th.join();
}
close( _done );
#endif
}

#ifndef WIN32
static int open_netlink()
{
// NETLINK_ROUTE
// Receives routing and link updates and may be used to modify the routing tables
// (both IPv4 and IPv6), IP addresses, link parameters, neighbor setups, queueing
// disciplines, traffic classes, and packet classifiers.
// https://manpages.ubuntu.com/manpages/oracular/en/man7/netlink.7.html
int sock = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
if( sock < 0 )
return sock;

struct sockaddr_nl addr;
memset( (void *)&addr, 0, sizeof( addr ) );
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR;
if( bind( sock, (struct sockaddr *)&addr, sizeof( addr ) ) < 0 )
return -1;

return sock;
}
#endif
};

static rsutils::shared_ptr_singleton< detail::adapter_watcher_singleton > the_adapter_watcher;
Expand Down

0 comments on commit b2e5e7e

Please sign in to comment.