Refactor the Mono.Security code to use more linker friendly ways to create hash algor...
[mono-project.git] / support / nl.c
blob60ad23d594eb20f8d6caf2e003aee5389c2fedf3
1 /*
2 * Network availability and change notifications for linux.
4 * Authors:
5 * Gonzalo Paniagua Javier (gonzalo@novell.com)
7 * Copyright (c) Novell, Inc. 2011
8 */
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
14 #include "nl.h"
16 #if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
18 #include <errno.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <arpa/inet.h>
24 #include <linux/netlink.h>
25 #include <linux/rtnetlink.h>
27 #undef NL_DEBUG
28 #define NL_DEBUG_STMT(a) do { } while (0)
29 #define NL_DEBUG_PRINT(...)
32 #define NL_DEBUG 1
33 #define NL_DEBUG_STMT(a) do { a } while (0)
34 #define NL_DEBUG_PRINT(...) g_message(__VA_ARGS__)
38 #ifdef AF_INET6
39 #define ADDR_BYTE_LENGTH 16
40 #define ADDR_STR_LENGTH INET6_ADDRSTRLEN
41 #else
42 #define ADDR_LENGTH 4
43 #define ADDR_STR_LENGTH INET_ADDRSTRLEN
44 #endif
46 enum event_type {
47 EVT_NONE = 0,
48 #define EVT_NONE 0
49 EVT_AVAILABILITY = 1 << 0,
50 #define EVT_AVAILABILITY (1 << 0)
51 EVT_ADDRESS = 1 << 1,
52 #define EVT_ADDRESS (1 << 1)
53 EVT_ALL = EVT_AVAILABILITY | EVT_ADDRESS
54 #define EVT_ALL (EVT_AVAILABILITY | EVT_ADDRESS)
57 #ifdef NL_DEBUG
58 typedef struct {
59 int value;
60 const char *name;
61 } value2name_t;
63 #define INIT(x) { x, #x }
64 #define FIND_NAME(a, b) value_to_name (a, b)
66 #define FIND_RT_TYPE_NAME(b) FIND_NAME (rt_types, b)
67 static value2name_t rt_types [] = {
68 INIT (RTM_NEWROUTE),
69 INIT (RTM_DELROUTE),
70 INIT (RTM_GETROUTE),
71 INIT (RTM_NEWADDR),
72 INIT (RTM_DELADDR),
73 INIT (RTM_GETADDR),
74 INIT (RTM_NEWLINK),
75 INIT (RTM_GETLINK),
76 INIT (RTM_DELLINK),
77 INIT (RTM_NEWNEIGH),
78 INIT (RTM_GETNEIGH),
79 INIT (RTM_DELNEIGH),
80 {0, NULL}
83 #define FIND_RTM_TYPE_NAME(b) FIND_NAME (rtm_types, b)
84 static value2name_t rtm_types [] = {
85 INIT (RTN_UNSPEC),
86 INIT (RTN_UNICAST),
87 INIT (RTN_LOCAL),
88 INIT (RTN_BROADCAST),
89 INIT (RTN_ANYCAST),
90 INIT (RTN_MULTICAST),
91 INIT (RTN_BLACKHOLE),
92 INIT (RTN_UNREACHABLE),
93 INIT (RTN_PROHIBIT),
94 INIT (RTN_THROW),
95 INIT (RTN_NAT),
96 INIT (RTN_XRESOLVE),
97 {0, NULL}
100 #define FIND_RTM_PROTO_NAME(b) FIND_NAME (rtm_protocols, b)
101 static value2name_t rtm_protocols[] = {
102 INIT (RTPROT_UNSPEC),
103 INIT (RTPROT_REDIRECT),
104 INIT (RTPROT_KERNEL),
105 INIT (RTPROT_BOOT),
106 INIT (RTPROT_STATIC),
107 {0, NULL}
110 #define FIND_RTM_SCOPE_NAME(b) FIND_NAME (rtm_scopes, b)
111 static value2name_t rtm_scopes [] = {
112 INIT (RT_SCOPE_UNIVERSE),
113 INIT (RT_SCOPE_SITE),
114 INIT (RT_SCOPE_LINK),
115 INIT (RT_SCOPE_HOST),
116 INIT (RT_SCOPE_NOWHERE),
117 {0, NULL}
120 #define FIND_RTM_ATTRS_NAME(b) FIND_NAME (rtm_attrs, b)
121 static value2name_t rtm_attrs [] = {
122 INIT (RTA_UNSPEC),
123 INIT (RTA_DST),
124 INIT (RTA_SRC),
125 INIT (RTA_IIF),
126 INIT (RTA_OIF),
127 INIT (RTA_GATEWAY),
128 INIT (RTA_PRIORITY),
129 INIT (RTA_PREFSRC),
130 INIT (RTA_METRICS),
131 INIT (RTA_MULTIPATH),
132 INIT (RTA_PROTOINFO),
133 INIT (RTA_FLOW),
134 INIT (RTA_CACHEINFO),
135 INIT (RTA_SESSION),
136 INIT (RTA_MP_ALGO),
137 INIT (RTA_TABLE),
138 {0, NULL}
141 #define FIND_RT_TABLE_NAME(b) FIND_NAME (rtm_tables, b)
142 static value2name_t rtm_tables [] = {
143 INIT (RT_TABLE_UNSPEC),
144 INIT (RT_TABLE_COMPAT),
145 INIT (RT_TABLE_DEFAULT),
146 INIT (RT_TABLE_MAIN),
147 INIT (RT_TABLE_LOCAL),
148 {0,0}
151 static const char *
152 value_to_name (value2name_t *tbl, int value)
154 static char auto_name [16];
156 while (tbl->name) {
157 if (tbl->value == value)
158 return tbl->name;
159 tbl++;
161 snprintf (auto_name, sizeof (auto_name), "#%d", value);
162 return auto_name;
164 #endif /* NL_DEBUG */
167 CreateNLSocket (void)
169 int sock;
170 struct sockaddr_nl sa;
171 int ret;
173 sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
175 ret = fcntl (sock, F_GETFL, 0);
176 if (ret != -1) {
177 ret |= O_NONBLOCK;
178 ret = fcntl (sock, F_SETFL, ret);
179 if (ret < 0)
180 return -1;
183 memset (&sa, 0, sizeof (sa));
184 if (sock < 0)
185 return -1;
186 sa.nl_family = AF_NETLINK;
187 sa.nl_pid = getpid ();
188 sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NOTIFY;
189 /* RTNLGRP_IPV4_IFADDR | RTNLGRP_IPV6_IFADDR
190 * RTMGRP_LINK */
192 if (bind (sock, (struct sockaddr *) &sa, sizeof (sa)) < 0)
193 return -1;
195 return sock;
199 ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
201 struct nlmsghdr *nlp;
202 struct rtmsg *rtp;
203 int rtl;
204 struct rtattr *rtap;
205 int result;
206 int s;
208 NL_DEBUG_PRINT ("ENTER ReadEvents()");
209 result = EVT_NONE;
210 s = GPOINTER_TO_INT (sock);
211 /* This socket is not found by IO layer, so we do everything here */
212 if (count == 0) {
213 while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
214 if (count <= 0) {
215 NL_DEBUG_PRINT ("EXIT ReadEvents()");
216 return result;
219 for (nlp = (struct nlmsghdr *) buffer; NLMSG_OK (nlp, count); nlp = NLMSG_NEXT (nlp, count)) {
220 int family;
221 int addr_length;
222 int msg_type;
223 int table;
224 int protocol;
225 int scope;
226 int rtm_type;
227 gboolean have_dst;
228 gboolean have_src;
229 gboolean have_pref_src;
230 gboolean have_gw;
231 char dst [ADDR_BYTE_LENGTH];
232 char src [ADDR_BYTE_LENGTH];
233 char pref_src [ADDR_BYTE_LENGTH];
234 char gw [ADDR_BYTE_LENGTH];
236 msg_type = nlp->nlmsg_type;
237 NL_DEBUG_PRINT ("TYPE: %d %s", msg_type, FIND_RT_TYPE_NAME (msg_type));
238 if (msg_type != RTM_NEWROUTE && msg_type != RTM_DELROUTE)
239 continue;
241 rtp = (struct rtmsg *) NLMSG_DATA (nlp);
242 family = rtp->rtm_family;
243 #ifdef AF_INET6
244 if (family != AF_INET && family != AF_INET6) {
245 #else
246 if (family != AF_INET) {
247 #endif
248 continue;
251 addr_length = (family == AF_INET) ? 4 : 16;
252 table = rtp->rtm_table;
253 protocol = rtp->rtm_protocol;
254 scope = rtp->rtm_scope;
255 rtm_type = rtp->rtm_type;
256 NL_DEBUG_PRINT ("\tRTMSG table: %d %s", table, FIND_RT_TABLE_NAME (table));
257 if (table != RT_TABLE_MAIN && table != RT_TABLE_LOCAL)
258 continue;
260 NL_DEBUG_PRINT ("\tRTMSG protocol: %d %s", protocol, FIND_RTM_PROTO_NAME (protocol));
261 NL_DEBUG_PRINT ("\tRTMSG scope: %d %s", scope, FIND_RTM_SCOPE_NAME (scope));
262 NL_DEBUG_PRINT ("\tRTMSG type: %d %s", rtm_type, FIND_RTM_TYPE_NAME (rtm_type));
264 rtap = (struct rtattr *) RTM_RTA (rtp);
265 rtl = RTM_PAYLOAD (nlp);
266 // loop & get every attribute
269 // NEW_ROUTE
270 // table = RT_TABLE_LOCAL, Scope = HOST + pref.src == src + type=LOCAL -> new if addr
271 // RT_TABLE_MAIN, Scope = Universe, unicast, gateway exists -> NEW default route
272 // DEL_ROUTE
273 // table = RT_TABLE_LOCAL, Scope = HOST, perfsrc = dst + type=LOCAL -> if addr deleted
274 // RT_TABLE_MAIN - DELROUTE + unicast -> event (gw down?)
275 have_dst = have_src = have_pref_src = have_gw = FALSE;
276 for(; RTA_OK (rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) {
277 char *data;
278 #ifdef NL_DEBUG
279 char ip [ADDR_STR_LENGTH];
280 #endif
282 NL_DEBUG_PRINT ("\tAttribute: %d %d (%s)", rtap->rta_len, rtap->rta_type, FIND_RTM_ATTRS_NAME (rtap->rta_type));
283 data = RTA_DATA (rtap);
284 switch (rtap->rta_type) {
285 case RTA_DST:
286 have_dst = TRUE;
287 memcpy (dst, data, addr_length);
288 NL_DEBUG_STMT (
289 *ip = 0;
290 inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
291 NL_DEBUG_PRINT ("\t\tDst: %s", ip);
293 break;
294 case RTA_PREFSRC:
295 have_pref_src = TRUE;
296 memcpy (pref_src, data, addr_length);
297 NL_DEBUG_STMT (
298 *ip = 0;
299 inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
300 NL_DEBUG_PRINT ("\t\tPref. Src.: %s", ip);
302 break;
303 case RTA_SRC:
304 have_src = TRUE;
305 memcpy (src, data, addr_length);
306 NL_DEBUG_STMT (
307 *ip = 0;
308 inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
309 NL_DEBUG_PRINT ("\tSrc: %s", ip);
311 break;
312 case RTA_GATEWAY:
313 have_gw = TRUE;
314 memcpy (gw, data, addr_length);
315 NL_DEBUG_STMT (
316 *ip = 0;
317 inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
318 NL_DEBUG_PRINT ("\t\tGateway: %s", ip);
320 break;
321 default:
322 break;
325 if (msg_type == RTM_NEWROUTE) {
326 if (table == RT_TABLE_MAIN) {
327 NL_DEBUG_PRINT ("NEWROUTE: Availability changed");
328 result |= EVT_AVAILABILITY;
329 } else if (table == RT_TABLE_LOCAL) {
330 NL_DEBUG_PRINT ("NEWROUTE: new IP");
331 if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0)
332 result |= EVT_ADDRESS;
334 } else if (msg_type == RTM_DELROUTE) {
335 if (table == RT_TABLE_MAIN) {
336 if (rtm_type == RTN_UNICAST && (have_dst || have_pref_src)) {
337 result |= EVT_AVAILABILITY;
338 NL_DEBUG_PRINT ("DELROUTE: Availability changed");
340 } else if (table == RT_TABLE_LOCAL) {
341 if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0) {
342 result |= EVT_ADDRESS;
343 NL_DEBUG_PRINT ("DELROUTE: deleted IP");
347 while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
348 if (count <= 0) {
349 NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
350 return result;
352 nlp = (struct nlmsghdr *) buffer;
354 NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
355 return result;
359 CloseNLSocket (gpointer sock)
361 return close (GPOINTER_TO_INT (sock));
363 #else
365 GetNLSocket (void)
367 return -1;
371 ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
373 return 0;
377 CreateNLSocket (void)
379 return -1;
383 CloseNLSocket (gpointer sock)
385 return -1;
387 #endif /* linux/netlink.h + linux/rtnetlink.h */