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 10, 2024
1 parent 579f9b8 commit eab7c9d
Showing 1 changed file with 123 additions and 14 deletions.
137 changes: 123 additions & 14 deletions third-party/rsutils/src/adapter-watcher.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2024 Intel Corporation. All Rights Reserved.

//#define _WINSOCKAPI_ // stops windows.h including winsock.h

#include <rsutils/os/adapter-watcher.h>
#include <rsutils/shared-ptr-singleton.h>
#include <rsutils/signal.h>
#include <rsutils/easylogging/easyloggingpp.h>

#ifdef WIN32
#ifdef _WIN32

#include <windows.h>
//#include <winsock2.h>
#include <iphlpapi.h>
#pragma comment( lib, "iphlpapi.lib" )

#else

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

#endif


Expand All @@ -28,29 +33,31 @@ class adapter_watcher_singleton
{
using public_signal = rsutils::public_signal< adapter_watcher_singleton >;

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

public:
public_signal callbacks;

adapter_watcher_singleton()
#ifdef WIN32
#ifdef _WIN32
: _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];
DWORD const n_handles = sizeof( handles ) / sizeof( handles[0] );
handles[0] = _done;
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-notifyaddrchange
OVERLAPPED overlap = {0};
//overlap.hEvent = WSACreateEvent();
OVERLAPPED overlap = { 0 };
// overlap.hEvent = WSACreateEvent();
overlap.hEvent = CreateEvent( nullptr /*security attrs*/,
FALSE /*auto reset*/,
FALSE /*init unset*/,
Expand All @@ -69,7 +76,7 @@ class adapter_watcher_singleton
== WaitForMultipleObjects( n_handles, handles, FALSE /*wait-for-all*/, INFINITE ) )
break; // we're done
LOG_DEBUG( "network adapter changes detected!" );
//WSAResetEvent( handles[1] );
// WSAResetEvent( handles[1] );
callbacks.raise();
}

Expand All @@ -78,26 +85,128 @@ 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" );
}

~adapter_watcher_singleton()
{
#ifdef WIN32
#ifdef _WIN32
if( _th.joinable() )
{
SetEvent( _done );
_th.join();
}
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 eab7c9d

Please sign in to comment.