Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGonzalo Paniagua Javier <gonzalo.mono@gmail.com>2011-03-28 02:49:17 +0400
committerGonzalo Paniagua Javier <gonzalo.mono@gmail.com>2011-03-28 02:50:35 +0400
commit75d42f19f901d60a1571d3fa1e2fd22c6fff5df3 (patch)
tree24a1b090bd244e2e20bda804826eccde3e7d7c7b /support
parent0da944a95c84990cbb1ea04414989810910b39c2 (diff)
[NetworkInformation] Support network IP/status events
Add support for the events in NetworkChanged (linux-only).
Diffstat (limited to 'support')
-rw-r--r--support/Makefile.am2
-rw-r--r--support/nl.c369
-rw-r--r--support/nl.h12
3 files changed, 383 insertions, 0 deletions
diff --git a/support/Makefile.am b/support/Makefile.am
index c64bfc827c1..95299f8fa9c 100644
--- a/support/Makefile.am
+++ b/support/Makefile.am
@@ -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 \
diff --git a/support/nl.c b/support/nl.c
new file mode 100644
index 00000000000..e36fe6eb1ab
--- /dev/null
+++ b/support/nl.c
@@ -0,0 +1,369 @@
+#include <config.h>
+#include "nl.h"
+
+#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#undef NL_DEBUG
+#define NL_DEBUG_PRINT(...)
+/*
+#define NL_DEBUG 1
+#define NL_DEBUG_PRINT(...) g_message(__VA_ARGS__)
+*/
+
+enum event_type {
+ EVT_NONE = 0,
+#define EVT_NONE 0
+ EVT_AVAILABILITY = 1 << 0,
+#define EVT_AVAILABILITY (1 << 0)
+ EVT_ADDRESS = 1 << 1,
+#define EVT_ADDRESS (1 << 1)
+ EVT_ALL = EVT_AVAILABILITY | EVT_ADDRESS
+#define EVT_ALL (EVT_AVAILABILITY | EVT_ADDRESS)
+};
+
+#ifdef NL_DEBUG
+typedef struct {
+ int value;
+ const char *name;
+} value2name_t;
+
+#define INIT(x) { x, #x }
+#define FIND_NAME(a, b) value_to_name (a, b)
+
+#define FIND_RT_TYPE_NAME(b) FIND_NAME (rt_types, b)
+static value2name_t rt_types [] = {
+ INIT (RTM_NEWROUTE),
+ INIT (RTM_DELROUTE),
+ INIT (RTM_GETROUTE),
+ INIT (RTM_NEWADDR),
+ INIT (RTM_DELADDR),
+ INIT (RTM_GETADDR),
+ INIT (RTM_NEWLINK),
+ INIT (RTM_GETLINK),
+ INIT (RTM_DELLINK),
+ INIT (RTM_NEWNEIGH),
+ INIT (RTM_GETNEIGH),
+ INIT (RTM_DELNEIGH),
+ {0, NULL}
+};
+
+#define FIND_RTM_TYPE_NAME(b) FIND_NAME (rtm_types, b)
+static value2name_t rtm_types [] = {
+ INIT (RTN_UNSPEC),
+ INIT (RTN_UNICAST),
+ INIT (RTN_LOCAL),
+ INIT (RTN_BROADCAST),
+ INIT (RTN_ANYCAST),
+ INIT (RTN_MULTICAST),
+ INIT (RTN_BLACKHOLE),
+ INIT (RTN_UNREACHABLE),
+ INIT (RTN_PROHIBIT),
+ INIT (RTN_THROW),
+ INIT (RTN_NAT),
+ INIT (RTN_XRESOLVE),
+ {0, NULL}
+};
+
+#define FIND_RTM_PROTO_NAME(b) FIND_NAME (rtm_protocols, b)
+static value2name_t rtm_protocols[] = {
+ INIT (RTPROT_UNSPEC),
+ INIT (RTPROT_REDIRECT),
+ INIT (RTPROT_KERNEL),
+ INIT (RTPROT_BOOT),
+ INIT (RTPROT_STATIC),
+ {0, NULL}
+};
+
+#define FIND_RTM_SCOPE_NAME(b) FIND_NAME (rtm_scopes, b)
+static value2name_t rtm_scopes [] = {
+ INIT (RT_SCOPE_UNIVERSE),
+ INIT (RT_SCOPE_SITE),
+ INIT (RT_SCOPE_LINK),
+ INIT (RT_SCOPE_HOST),
+ INIT (RT_SCOPE_NOWHERE),
+ {0, NULL}
+};
+
+#define FIND_RTM_ATTRS_NAME(b) FIND_NAME (rtm_attrs, b)
+static value2name_t rtm_attrs [] = {
+ INIT (RTA_UNSPEC),
+ INIT (RTA_DST),
+ INIT (RTA_SRC),
+ INIT (RTA_IIF),
+ INIT (RTA_OIF),
+ INIT (RTA_GATEWAY),
+ INIT (RTA_PRIORITY),
+ INIT (RTA_PREFSRC),
+ INIT (RTA_METRICS),
+ INIT (RTA_MULTIPATH),
+ INIT (RTA_PROTOINFO),
+ INIT (RTA_FLOW),
+ INIT (RTA_CACHEINFO),
+ INIT (RTA_SESSION),
+ INIT (RTA_MP_ALGO),
+ INIT (RTA_TABLE),
+ {0, NULL}
+};
+
+#define FIND_RT_TABLE_NAME(b) FIND_NAME (rtm_tables, b)
+static value2name_t rtm_tables [] = {
+ INIT (RT_TABLE_UNSPEC),
+ INIT (RT_TABLE_COMPAT),
+ INIT (RT_TABLE_DEFAULT),
+ INIT (RT_TABLE_MAIN),
+ INIT (RT_TABLE_LOCAL),
+ {0,0}
+};
+
+static const char *
+value_to_name (value2name_t *tbl, int value)
+{
+ static char auto_name [16];
+
+ while (tbl->name) {
+ if (tbl->value == value)
+ return tbl->name;
+ tbl++;
+ }
+ snprintf (auto_name, sizeof (auto_name), "#%d", value);
+ return auto_name;
+}
+#endif /* NL_DEBUG */
+
+int
+CreateNLSocket (void)
+{
+ int sock;
+ struct sockaddr_nl sa;
+ int ret;
+
+ sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+
+ ret = fcntl (sock, F_GETFL, 0);
+#ifdef O_NONBLOCK
+ if (ret != -1) {
+ ret |= O_NONBLOCK;
+ ret = fcntl (sock, F_SETFL, ret);
+ if (ret < 0)
+ return -1;
+ }
+#endif
+
+ memset (&sa, 0, sizeof (sa));
+ if (sock < 0)
+ return -1;
+ sa.nl_family = AF_NETLINK;
+ sa.nl_pid = getpid ();
+ sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NOTIFY;
+ /* RTNLGRP_IPV4_IFADDR | RTNLGRP_IPV6_IFADDR
+ * RTMGRP_LINK */
+
+ if (bind (sock, (struct sockaddr *) &sa, sizeof (sa)) < 0)
+ return -1;
+
+ return sock;
+}
+
+int
+ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
+{
+ struct nlmsghdr *nlp;
+ struct rtmsg *rtp;
+ int rtl;
+ struct rtattr *rtap;
+ int result;
+ int s;
+
+ NL_DEBUG_PRINT ("ENTER ReadEvents()");
+ result = EVT_NONE;
+ s = GPOINTER_TO_INT (sock);
+ /* This socket is not found by IO layer, so we do everything here */
+ if (count == 0) {
+ while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
+ if (count <= 0) {
+ NL_DEBUG_PRINT ("EXIT ReadEvents()");
+ return result;
+ }
+ }
+ for (nlp = (struct nlmsghdr *) buffer; NLMSG_OK (nlp, count); nlp = NLMSG_NEXT (nlp, count)) {
+ int family;
+ int addr_length;
+ int msg_type;
+ int table;
+ int protocol;
+ int scope;
+ int rtm_type;
+ gboolean have_dst;
+ gboolean have_src;
+ gboolean have_pref_src;
+ gboolean have_gw;
+#ifdef AF_INET6
+ char dst [16];
+ char src [16];
+ char pref_src [16];
+ char gw [16];
+#else
+ char dst [4];
+ char src [4];
+ char pref_src [4];
+ char gw [4];
+#endif
+
+ msg_type = nlp->nlmsg_type;
+ NL_DEBUG_PRINT ("TYPE: %d %s", msg_type, FIND_RT_TYPE_NAME (msg_type));
+ if (msg_type != RTM_NEWROUTE && msg_type != RTM_DELROUTE)
+ continue;
+
+ rtp = (struct rtmsg *) NLMSG_DATA (nlp);
+ family = rtp->rtm_family;
+#ifdef AF_INET6
+ addr_length = (family == AF_INET) ? 4 : 16;
+#else
+ addr_length = 4;
+#endif
+ table = rtp->rtm_table;
+ protocol = rtp->rtm_protocol;
+ scope = rtp->rtm_scope;
+ rtm_type = rtp->rtm_type;
+ NL_DEBUG_PRINT ("\tRTMSG table: %d %s", table, FIND_RT_TABLE_NAME (table));
+ if (table != RT_TABLE_MAIN && table != RT_TABLE_LOCAL)
+ continue;
+
+ NL_DEBUG_PRINT ("\tRTMSG protocol: %d %s", protocol, FIND_RTM_PROTO_NAME (protocol));
+ NL_DEBUG_PRINT ("\tRTMSG scope: %d %s", scope, FIND_RTM_SCOPE_NAME (scope));
+ NL_DEBUG_PRINT ("\tRTMSG type: %d %s", rtm_type, FIND_RTM_TYPE_NAME (rtm_type));
+
+ rtap = (struct rtattr *) RTM_RTA (rtp);
+ rtl = RTM_PAYLOAD (nlp);
+ // loop & get every attribute
+ //
+ //
+ // NEW_ROUTE
+ // table = RT_TABLE_LOCAL, Scope = HOST + pref.src == src + type=LOCAL -> new if addr
+ // RT_TABLE_MAIN, Scope = Universe, unicast, gateway exists -> NEW default route
+ // DEL_ROUTE
+ // table = RT_TABLE_LOCAL, Scope = HOST, perfsrc = dst + type=LOCAL -> if addr deleted
+ // RT_TABLE_MAIN - DELROUTE + unicast -> event (gw down?)
+ have_dst = have_src = have_pref_src = have_gw = FALSE;
+ for(; RTA_OK (rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) {
+ char *data;
+#ifdef NL_DEBUG
+#ifdef AF_INET6
+ char ip [INET6_ADDRSTRLEN];
+ int ip_length = INET6_ADDRSTRLEN;
+#else
+ char ip [INET_ADDRSTRLEN];
+ int ip_length = INET_ADDRSTRLEN;
+#endif
+#endif
+
+ NL_DEBUG_PRINT ("\tAttribute: %d %d (%s)", rtap->rta_len, rtap->rta_type, FIND_RTM_ATTRS_NAME (rtap->rta_type));
+ data = RTA_DATA (rtap);
+ switch (rtap->rta_type) {
+ case RTA_DST:
+ have_dst = TRUE;
+ memcpy (dst, data, addr_length);
+#ifdef NL_DEBUG
+ *ip = 0;
+ inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
+ NL_DEBUG_PRINT ("\t\tDst: %s", ip);
+#endif
+ break;
+ case RTA_PREFSRC:
+ have_pref_src = TRUE;
+ memcpy (pref_src, data, addr_length);
+#ifdef NL_DEBUG
+ *ip = 0;
+ inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
+ NL_DEBUG_PRINT ("\t\tPref. Src.: %s", ip);
+#endif
+ break;
+ case RTA_SRC:
+ have_src = TRUE;
+ memcpy (src, data, addr_length);
+#ifdef NL_DEBUG
+ *ip = 0;
+ inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
+ NL_DEBUG_PRINT ("\tSrc: %s", ip);
+#endif
+ break;
+ case RTA_GATEWAY:
+ have_gw = TRUE;
+ memcpy (gw, data, addr_length);
+#ifdef NL_DEBUG
+ *ip = 0;
+ inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
+ NL_DEBUG_PRINT ("\t\tGateway: %s", ip);
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+ if (msg_type == RTM_NEWROUTE) {
+ if (table == RT_TABLE_MAIN) {
+ NL_DEBUG_PRINT ("NEWROUTE: Availability changed");
+ result |= EVT_AVAILABILITY;
+ } else if (table == RT_TABLE_LOCAL) {
+ NL_DEBUG_PRINT ("NEWROUTE: new IP");
+ if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0)
+ result |= EVT_ADDRESS;
+ }
+ } else if (msg_type == RTM_DELROUTE) {
+ if (table == RT_TABLE_MAIN) {
+ if (rtm_type == RTN_UNICAST && (have_dst || have_pref_src)) {
+ result |= EVT_AVAILABILITY;
+ NL_DEBUG_PRINT ("DELROUTE: Availability changed");
+ }
+ } else if (table == RT_TABLE_LOCAL) {
+ if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0) {
+ result |= EVT_ADDRESS;
+ NL_DEBUG_PRINT ("DELROUTE: deleted IP");
+ }
+ }
+ }
+ while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
+ if (count <= 0) {
+ NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
+ return result;
+ }
+ nlp = (struct nlmsghdr *) buffer;
+ }
+ NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
+ return result;
+}
+
+int
+CloseNLSocket (gpointer sock)
+{
+ return close (GPOINTER_TO_INT (sock));
+}
+#else
+int
+GetNLSocket (void)
+{
+ return -1;
+}
+
+int
+ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
+{
+ return 0;
+}
+
+int
+CloseNLSocket (gpointer sock)
+{
+ return -1;
+}
+#endif /* linux/netlink.h + linux/rtnetlink.h */
+
diff --git a/support/nl.h b/support/nl.h
new file mode 100644
index 00000000000..4a464266382
--- /dev/null
+++ b/support/nl.h
@@ -0,0 +1,12 @@
+#ifndef NL_H
+#define NL_H
+#include <glib.h>
+
+G_BEGIN_DECLS
+int CreateNLSocket (void);
+int ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size);
+int CloseNLSocket (gpointer sock);
+G_END_DECLS
+
+#endif
+