Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / support / nl.c
1 /*
2  * Network availability and change notifications for linux.
3  *
4  * Authors:
5  *   Gonzalo Paniagua Javier (gonzalo@novell.com)
6  *
7  * Copyright (c) Novell, Inc. 2011
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include "nl.h"
15
16 #if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
17
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>
26
27 #undef NL_DEBUG
28 #define NL_DEBUG_STMT(a) do { } while (0)
29 #define NL_DEBUG_PRINT(...)
30
31 /*
32 #define NL_DEBUG 1
33 #define NL_DEBUG_STMT(a) do { a } while (0)
34 #define NL_DEBUG_PRINT(...) g_message(__VA_ARGS__)
35 */
36
37
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
45
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)
55 };
56
57 #ifdef NL_DEBUG
58 typedef struct {
59         int value;
60         const char *name;
61 } value2name_t;
62
63 #define INIT(x) { x, #x }
64 #define FIND_NAME(a, b) value_to_name (a, b)
65
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}
81 };
82
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}
98 };
99
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}
108 };
109
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}
118 };
119
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}
139 };
140
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}
149 };
150
151 static const char *
152 value_to_name (value2name_t *tbl, int value)
153 {
154         static char auto_name [16];
155
156         while (tbl->name) {
157                 if (tbl->value == value)
158                         return tbl->name;
159                 tbl++;
160         }
161         snprintf (auto_name, sizeof (auto_name), "#%d", value);
162         return auto_name;
163 }
164 #endif /* NL_DEBUG */
165
166 int
167 CreateNLSocket (void)
168 {
169         int sock;
170         struct sockaddr_nl sa;
171         int ret;
172         
173         sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
174
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;
181         }
182
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 */
191
192         if (bind (sock, (struct sockaddr *) &sa, sizeof (sa)) < 0)
193                 return -1;
194
195         return sock;
196 }
197
198 int
199 ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
200 {
201         struct nlmsghdr *nlp;
202         struct rtmsg *rtp;
203         int rtl;
204         struct rtattr *rtap;
205         int result;
206         int s;
207
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;
217                 }
218         }
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 #ifdef NL_DEBUG
225                 int protocol;
226                 int scope;
227 #endif
228                 int rtm_type;
229                 gboolean have_dst;
230                 gboolean have_src;
231                 gboolean have_pref_src;
232                 gboolean have_gw;
233                 char dst [ADDR_BYTE_LENGTH];
234                 char src [ADDR_BYTE_LENGTH];
235                 char pref_src [ADDR_BYTE_LENGTH];
236                 char gw [ADDR_BYTE_LENGTH];
237
238                 msg_type = nlp->nlmsg_type;
239                 NL_DEBUG_PRINT ("TYPE: %d %s", msg_type, FIND_RT_TYPE_NAME (msg_type));
240                 if (msg_type != RTM_NEWROUTE && msg_type != RTM_DELROUTE)
241                         continue;
242
243                 rtp = (struct rtmsg *) NLMSG_DATA (nlp);
244                 family = rtp->rtm_family;
245 #ifdef AF_INET6
246                 if (family != AF_INET && family != AF_INET6) {
247 #else
248                 if (family != AF_INET) {
249 #endif
250                         continue;
251                 }
252
253                 addr_length = (family == AF_INET) ? 4 : 16;
254                 table = rtp->rtm_table;
255 #ifdef NL_DEBUG
256                 protocol = rtp->rtm_protocol;
257                 scope = rtp->rtm_scope;
258 #endif
259                 rtm_type = rtp->rtm_type;
260                 NL_DEBUG_PRINT ("\tRTMSG table: %d %s", table, FIND_RT_TABLE_NAME (table));
261                 if (table != RT_TABLE_MAIN && table != RT_TABLE_LOCAL)
262                         continue;
263
264                 NL_DEBUG_PRINT ("\tRTMSG protocol: %d %s", protocol, FIND_RTM_PROTO_NAME (protocol));
265                 NL_DEBUG_PRINT ("\tRTMSG scope: %d %s", scope, FIND_RTM_SCOPE_NAME (scope));
266                 NL_DEBUG_PRINT ("\tRTMSG type: %d %s", rtm_type, FIND_RTM_TYPE_NAME (rtm_type));
267
268                 rtap = (struct rtattr *) RTM_RTA (rtp);
269                 rtl = RTM_PAYLOAD (nlp);
270                 // loop & get every attribute
271                 //
272                 // 
273                 // NEW_ROUTE
274                 //      table = RT_TABLE_LOCAL, Scope = HOST + pref.src == src  + type=LOCAL -> new if addr
275                 //      RT_TABLE_MAIN, Scope = Universe, unicast, gateway exists -> NEW default route
276                 // DEL_ROUTE
277                 //      table = RT_TABLE_LOCAL, Scope = HOST, perfsrc = dst  + type=LOCAL -> if addr deleted
278                 //      RT_TABLE_MAIN - DELROUTE + unicast -> event (gw down?)
279                 have_dst = have_src = have_pref_src = have_gw = FALSE;
280                 for(; RTA_OK (rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) {
281                         char *data;
282 #ifdef NL_DEBUG
283                         char ip [ADDR_STR_LENGTH];
284 #endif
285
286                         NL_DEBUG_PRINT ("\tAttribute: %d %d (%s)", rtap->rta_len, rtap->rta_type, FIND_RTM_ATTRS_NAME (rtap->rta_type));
287                         data = RTA_DATA (rtap);
288                         switch (rtap->rta_type) {
289                         case RTA_DST:
290                                 have_dst = TRUE;
291                                 memcpy (dst, data, addr_length);
292                                 NL_DEBUG_STMT (
293                                         *ip = 0;
294                                         inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
295                                         NL_DEBUG_PRINT ("\t\tDst: %s", ip);
296                                 );
297                                 break;
298                         case RTA_PREFSRC:
299                                 have_pref_src = TRUE;
300                                 memcpy (pref_src, data, addr_length);
301                                 NL_DEBUG_STMT (
302                                         *ip = 0;
303                                         inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
304                                         NL_DEBUG_PRINT ("\t\tPref. Src.: %s", ip);
305                                 );
306                                 break;
307                         case RTA_SRC:
308                                 have_src = TRUE;
309                                 memcpy (src, data, addr_length);
310                                 NL_DEBUG_STMT (
311                                         *ip = 0;
312                                         inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
313                                         NL_DEBUG_PRINT ("\tSrc: %s", ip);
314                                 );
315                                 break;
316                         case RTA_GATEWAY:
317                                 have_gw = TRUE;
318                                 memcpy (gw, data, addr_length);
319                                 NL_DEBUG_STMT (
320                                         *ip = 0;
321                                         inet_ntop (family, RTA_DATA (rtap), ip, sizeof (ip));
322                                         NL_DEBUG_PRINT ("\t\tGateway: %s", ip);
323                                 );
324                                 break;
325                         default:
326                                 break;
327                         }
328                 }
329                 if (msg_type == RTM_NEWROUTE) {
330                         if (table == RT_TABLE_MAIN) {
331                                 NL_DEBUG_PRINT ("NEWROUTE: Availability changed");
332                                 result |= EVT_AVAILABILITY;
333                         } else if (table == RT_TABLE_LOCAL) {
334                                 NL_DEBUG_PRINT ("NEWROUTE: new IP");
335                                 if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0)
336                                         result |= EVT_ADDRESS;
337                         }
338                 } else if (msg_type == RTM_DELROUTE) {
339                         if (table == RT_TABLE_MAIN) {
340                                 if (rtm_type == RTN_UNICAST && (have_dst || have_pref_src)) {
341                                         result |= EVT_AVAILABILITY;
342                                         NL_DEBUG_PRINT ("DELROUTE: Availability changed");
343                                 }
344                         } else if (table == RT_TABLE_LOCAL) {
345                                 if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0) {
346                                         result |= EVT_ADDRESS;
347                                         NL_DEBUG_PRINT ("DELROUTE: deleted IP");
348                                 }
349                         }
350                 }
351                 while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
352                 if (count <= 0) {
353                         NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
354                         return result;
355                 }
356                 nlp = (struct nlmsghdr *) buffer;
357         }
358         NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
359         return result;
360 }
361
362 int
363 CloseNLSocket (gpointer sock)
364 {
365         return close (GPOINTER_TO_INT (sock));
366 }
367 #else
368 int
369 GetNLSocket (void)
370 {
371         return -1;
372 }
373
374 int
375 ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
376 {
377         return 0;
378 }
379
380 int
381 CreateNLSocket (void)
382 {
383         return -1;
384 }
385
386 int
387 CloseNLSocket (gpointer sock)
388 {
389         return -1;
390 }
391 #endif /* linux/netlink.h + linux/rtnetlink.h */
392