2 * Network availability and change notifications for linux.
5 * Gonzalo Paniagua Javier (gonzalo@novell.com)
7 * Copyright (c) Novell, Inc. 2011
16 #if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_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>
28 #define NL_DEBUG_STMT(a) do { } while (0)
29 #define NL_DEBUG_PRINT(...)
33 #define NL_DEBUG_STMT(a) do { a } while (0)
34 #define NL_DEBUG_PRINT(...) g_message(__VA_ARGS__)
39 #define ADDR_BYTE_LENGTH 16
40 #define ADDR_STR_LENGTH INET6_ADDRSTRLEN
43 #define ADDR_STR_LENGTH INET_ADDRSTRLEN
49 EVT_AVAILABILITY
= 1 << 0,
50 #define EVT_AVAILABILITY (1 << 0)
52 #define EVT_ADDRESS (1 << 1)
53 EVT_ALL
= EVT_AVAILABILITY
| EVT_ADDRESS
54 #define EVT_ALL (EVT_AVAILABILITY | EVT_ADDRESS)
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
[] = {
83 #define FIND_RTM_TYPE_NAME(b) FIND_NAME (rtm_types, b)
84 static value2name_t rtm_types
[] = {
92 INIT (RTN_UNREACHABLE
),
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
),
106 INIT (RTPROT_STATIC
),
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
),
120 #define FIND_RTM_ATTRS_NAME(b) FIND_NAME (rtm_attrs, b)
121 static value2name_t rtm_attrs
[] = {
131 INIT (RTA_MULTIPATH
),
132 INIT (RTA_PROTOINFO
),
134 INIT (RTA_CACHEINFO
),
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
),
152 value_to_name (value2name_t
*tbl
, int value
)
154 static char auto_name
[16];
157 if (tbl
->value
== value
)
161 snprintf (auto_name
, sizeof (auto_name
), "#%d", value
);
164 #endif /* NL_DEBUG */
167 CreateNLSocket (void)
170 struct sockaddr_nl sa
;
173 sock
= socket (AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
175 ret
= fcntl (sock
, F_GETFL
, 0);
178 ret
= fcntl (sock
, F_SETFL
, ret
);
183 memset (&sa
, 0, sizeof (sa
));
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
192 if (bind (sock
, (struct sockaddr
*) &sa
, sizeof (sa
)) < 0)
199 ReadEvents (gpointer sock
, gpointer buffer
, gint32 count
, gint32 size
)
201 struct nlmsghdr
*nlp
;
208 NL_DEBUG_PRINT ("ENTER ReadEvents()");
210 s
= GPOINTER_TO_INT (sock
);
211 /* This socket is not found by IO layer, so we do everything here */
213 while ((count
= recv (s
, buffer
, size
, 0)) == -1 && errno
== EINTR
);
215 NL_DEBUG_PRINT ("EXIT ReadEvents()");
219 for (nlp
= (struct nlmsghdr
*) buffer
; NLMSG_OK (nlp
, count
); nlp
= NLMSG_NEXT (nlp
, count
)) {
229 gboolean have_pref_src
;
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
)
241 rtp
= (struct rtmsg
*) NLMSG_DATA (nlp
);
242 family
= rtp
->rtm_family
;
244 if (family
!= AF_INET
&& family
!= AF_INET6
) {
246 if (family
!= AF_INET
) {
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
)
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
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
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
)) {
279 char ip
[ADDR_STR_LENGTH
];
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
) {
287 memcpy (dst
, data
, addr_length
);
290 inet_ntop (family
, RTA_DATA (rtap
), ip
, sizeof (ip
));
291 NL_DEBUG_PRINT ("\t\tDst: %s", ip
);
295 have_pref_src
= TRUE
;
296 memcpy (pref_src
, data
, addr_length
);
299 inet_ntop (family
, RTA_DATA (rtap
), ip
, sizeof (ip
));
300 NL_DEBUG_PRINT ("\t\tPref. Src.: %s", ip
);
305 memcpy (src
, data
, addr_length
);
308 inet_ntop (family
, RTA_DATA (rtap
), ip
, sizeof (ip
));
309 NL_DEBUG_PRINT ("\tSrc: %s", ip
);
314 memcpy (gw
, data
, addr_length
);
317 inet_ntop (family
, RTA_DATA (rtap
), ip
, sizeof (ip
));
318 NL_DEBUG_PRINT ("\t\tGateway: %s", ip
);
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
);
349 NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result
);
352 nlp
= (struct nlmsghdr
*) buffer
;
354 NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result
);
359 CloseNLSocket (gpointer sock
)
361 return close (GPOINTER_TO_INT (sock
));
371 ReadEvents (gpointer sock
, gpointer buffer
, gint32 count
, gint32 size
)
377 CreateNLSocket (void)
383 CloseNLSocket (gpointer sock
)
387 #endif /* linux/netlink.h + linux/rtnetlink.h */