Skip to content

Commit

Permalink
[NetworkInformation] Support network IP/status events
Browse files Browse the repository at this point in the history
	Add support for the events in NetworkChanged (linux-only).
  • Loading branch information
gonzalop committed Mar 27, 2011
1 parent 0da944a commit 75d42f1
Show file tree
Hide file tree
Showing 6 changed files with 553 additions and 9 deletions.
4 changes: 4 additions & 0 deletions configure.in
Expand Up @@ -368,6 +368,10 @@ AM_CONDITIONAL(NO_VERSION_SCRIPT, test x$no_version_script = xyes)

AC_CHECK_HEADERS(sys/filio.h sys/sockio.h netdb.h utime.h sys/utime.h semaphore.h sys/un.h linux/rtc.h sys/syscall.h sys/mkdev.h sys/uio.h sys/param.h)
AC_CHECK_HEADERS(sys/param.h sys/socket.h sys/ipc.h sys/sem.h sys/utsname.h alloca.h ucontext.h pwd.h sys/select.h netinet/tcp.h netinet/in.h unistd.h sys/types.h link.h asm/sigcontext.h)
AC_CHECK_HEADERS([linux/netlink.h linux/rtnetlink.h],
[], [], [#include <stddef.h>
#include <sys/socket.h>
#include <linux/socket.h>])

AC_CHECK_HEADERS(sys/user.h, [], [],
[
Expand Down
174 changes: 166 additions & 8 deletions mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs
Expand Up @@ -4,7 +4,7 @@
// Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com)
//
// Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
// Copyright (c) 2006,2011 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
Expand All @@ -25,19 +25,177 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#if NET_2_0

using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.Net.NetworkInformation {
public sealed class NetworkChange {
[Flags]
enum EventType {
Availability = 1 << 0,
Address = 1 << 1,
}

static object _lock = new object ();
static Socket nl_sock;
static SocketAsyncEventArgs nl_args;
static EventType pending_events;
static Timer timer;

static NetworkAddressChangedEventHandler AddressChanged;
static NetworkAvailabilityChangedEventHandler AvailabilityChanged;

private NetworkChange ()
{
}

// Disable the warnings about the events not being used.
#pragma warning disable 67
public static event NetworkAddressChangedEventHandler NetworkAddressChanged;
public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged;
#pragma warning restore
public static event NetworkAddressChangedEventHandler NetworkAddressChanged {
add { Register (value); }
remove { Unregister (value); }
}

public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
add { Register (value); }
remove { Unregister (value); }
}

//internal Socket (AddressFamily family, SocketType type, ProtocolType proto, IntPtr sock)

static bool EnsureSocket ()
{
lock (_lock) {
if (nl_sock != null)
return true;
IntPtr fd = CreateNLSocket ();
if (fd.ToInt64 () == -1)
return false;

nl_sock = new Socket (0, SocketType.Raw, ProtocolType.Udp, fd);
nl_args = new SocketAsyncEventArgs ();
nl_args.SetBuffer (new byte [8192], 0, 8192);
nl_args.Completed += OnDataAvailable;
nl_sock.ReceiveAsync (nl_args);
}
return true;
}

// _lock is held by the caller
static void MaybeCloseSocket ()
{
if (nl_sock == null || AvailabilityChanged != null || AddressChanged != null)
return;

CloseNLSocket (nl_sock.Handle);
GC.SuppressFinalize (nl_sock);
nl_sock = null;
nl_args = null;
}

static bool GetAvailability ()
{
NetworkInterface [] adapters = NetworkInterface.GetAllNetworkInterfaces ();
foreach (NetworkInterface n in adapters) {
// TODO: also check for a default route present?
if (n.NetworkInterfaceType == NetworkInterfaceType.Loopback)
continue;
if (n.OperationalStatus == OperationalStatus.Up)
return true;
}
return false;
}

static void OnAvailabilityChanged (object unused)
{
NetworkAvailabilityChangedEventHandler d = AvailabilityChanged;
d (null, new NetworkAvailabilityEventArgs (GetAvailability ()));
}

static void OnAddressChanged (object unused)
{
NetworkAddressChangedEventHandler d = AddressChanged;
d (null, EventArgs.Empty);
}

static void OnEventDue (object unused)
{
EventType evts;
lock (_lock) {
evts = pending_events;
pending_events = 0;
timer.Change (-1, -1);
}
if ((evts & EventType.Availability) != 0)
ThreadPool.QueueUserWorkItem (OnAvailabilityChanged);
if ((evts & EventType.Address) != 0)
ThreadPool.QueueUserWorkItem (OnAddressChanged);
}

static void QueueEvent (EventType type)
{
lock (_lock) {
if (timer == null)
timer = new Timer (OnEventDue);
if (pending_events == 0)
timer.Change (150, -1);
pending_events |= type;
}
}

unsafe static void OnDataAvailable (object sender, SocketAsyncEventArgs args)
{
EventType type;
fixed (byte *ptr = args.Buffer) {
type = ReadEvents (nl_sock.Handle, new IntPtr (ptr), args.BytesTransferred, 8192);
}
nl_sock.ReceiveAsync (nl_args);
if (type != 0)
QueueEvent (type);
}

static void Register (NetworkAddressChangedEventHandler d)
{
EnsureSocket ();
AddressChanged += d;
}

static void Register (NetworkAvailabilityChangedEventHandler d)
{
EnsureSocket ();
AvailabilityChanged += d;
}

static void Unregister (NetworkAddressChangedEventHandler d)
{
lock (_lock) {
AddressChanged -= d;
MaybeCloseSocket ();
}
}

static void Unregister (NetworkAvailabilityChangedEventHandler d)
{
lock (_lock) {
AvailabilityChanged -= d;
MaybeCloseSocket ();
}
}

#if MONOTOUCH || MONODROID
const string LIBNAME = "__Internal";
#else
const string LIBNAME = "MonoPosixHelper";
#endif

[DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr CreateNLSocket ();

[DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
static extern EventType ReadEvents (IntPtr sock, IntPtr buffer, int count, int size);

[DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr CloseNLSocket (IntPtr sock);
}
}
#endif

Expand Up @@ -52,7 +52,6 @@ protected NetworkInterface ()
{
}

[MonoTODO("Only works on Linux and Windows")]
public static NetworkInterface [] GetAllNetworkInterfaces ()
{
if (runningOnUnix) {
Expand Down
2 changes: 2 additions & 0 deletions support/Makefile.am
Expand Up @@ -32,6 +32,8 @@ MPH_UNIX_SOURCE = \
fstab.c \
grp.c \
macros.c \
nl.c \
nl.h \
old-map.c \
old-map.h \
pwd.c \
Expand Down

0 comments on commit 75d42f1

Please sign in to comment.