diff options
author | Gonzalo Paniagua Javier <gonzalo.mono@gmail.com> | 2011-03-28 02:49:17 +0400 |
---|---|---|
committer | Gonzalo Paniagua Javier <gonzalo.mono@gmail.com> | 2011-03-28 02:50:35 +0400 |
commit | 75d42f19f901d60a1571d3fa1e2fd22c6fff5df3 (patch) | |
tree | 24a1b090bd244e2e20bda804826eccde3e7d7c7b /support | |
parent | 0da944a95c84990cbb1ea04414989810910b39c2 (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.am | 2 | ||||
-rw-r--r-- | support/nl.c | 369 | ||||
-rw-r--r-- | support/nl.h | 12 |
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 + |