4 #if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
10 #include <sys/socket.h>
11 #include <arpa/inet.h>
12 #include <linux/netlink.h>
13 #include <linux/rtnetlink.h>
16 #define NL_DEBUG_PRINT(...)
19 #define NL_DEBUG_PRINT(...) g_message(__VA_ARGS__)
25 EVT_AVAILABILITY = 1 << 0,
26 #define EVT_AVAILABILITY (1 << 0)
28 #define EVT_ADDRESS (1 << 1)
29 EVT_ALL = EVT_AVAILABILITY | EVT_ADDRESS
30 #define EVT_ALL (EVT_AVAILABILITY | EVT_ADDRESS)
39 #define INIT(x) { x, #x }
40 #define FIND_NAME(a, b) value_to_name (a, b)
42 #define FIND_RT_TYPE_NAME(b) FIND_NAME (rt_types, b)
43 static value2name_t rt_types [] = {
59 #define FIND_RTM_TYPE_NAME(b) FIND_NAME (rtm_types, b)
60 static value2name_t rtm_types [] = {
68 INIT (RTN_UNREACHABLE),
76 #define FIND_RTM_PROTO_NAME(b) FIND_NAME (rtm_protocols, b)
77 static value2name_t rtm_protocols[] = {
79 INIT (RTPROT_REDIRECT),
86 #define FIND_RTM_SCOPE_NAME(b) FIND_NAME (rtm_scopes, b)
87 static value2name_t rtm_scopes [] = {
88 INIT (RT_SCOPE_UNIVERSE),
92 INIT (RT_SCOPE_NOWHERE),
96 #define FIND_RTM_ATTRS_NAME(b) FIND_NAME (rtm_attrs, b)
97 static value2name_t rtm_attrs [] = {
107 INIT (RTA_MULTIPATH),
108 INIT (RTA_PROTOINFO),
110 INIT (RTA_CACHEINFO),
117 #define FIND_RT_TABLE_NAME(b) FIND_NAME (rtm_tables, b)
118 static value2name_t rtm_tables [] = {
119 INIT (RT_TABLE_UNSPEC),
120 INIT (RT_TABLE_COMPAT),
121 INIT (RT_TABLE_DEFAULT),
122 INIT (RT_TABLE_MAIN),
123 INIT (RT_TABLE_LOCAL),
128 value_to_name (value2name_t *tbl, int value)
130 static char auto_name [16];
133 if (tbl->value == value)
137 snprintf (auto_name, sizeof (auto_name), "#%d", value);
140 #endif /* NL_DEBUG */
143 CreateNLSocket (void)
146 struct sockaddr_nl sa;
149 sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
151 ret = fcntl (sock, F_GETFL, 0);
155 ret = fcntl (sock, F_SETFL, ret);
161 memset (&sa, 0, sizeof (sa));
164 sa.nl_family = AF_NETLINK;
165 sa.nl_pid = getpid ();
166 sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NOTIFY;
167 /* RTNLGRP_IPV4_IFADDR | RTNLGRP_IPV6_IFADDR
170 if (bind (sock, (struct sockaddr *) &sa, sizeof (sa)) < 0)
177 ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
179 struct nlmsghdr *nlp;
186 NL_DEBUG_PRINT ("ENTER ReadEvents()");
188 s = GPOINTER_TO_INT (sock);
189 /* This socket is not found by IO layer, so we do everything here */
191 while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
193 NL_DEBUG_PRINT ("EXIT ReadEvents()");
197 for (nlp = (struct nlmsghdr *) buffer; NLMSG_OK (nlp, count); nlp = NLMSG_NEXT (nlp, count)) {
207 gboolean have_pref_src;
221 msg_type = nlp->nlmsg_type;
222 NL_DEBUG_PRINT ("TYPE: %d %s", msg_type, FIND_RT_TYPE_NAME (msg_type));
223 if (msg_type != RTM_NEWROUTE && msg_type != RTM_DELROUTE)
226 rtp = (struct rtmsg *) NLMSG_DATA (nlp);
227 family = rtp->rtm_family;
229 addr_length = (family == AF_INET) ? 4 : 16;
233 table = rtp->rtm_table;
234 protocol = rtp->rtm_protocol;
235 scope = rtp->rtm_scope;
236 rtm_type = rtp->rtm_type;
237 NL_DEBUG_PRINT ("\tRTMSG table: %d %s", table, FIND_RT_TABLE_NAME (table));
238 if (table != RT_TABLE_MAIN && table != RT_TABLE_LOCAL)
241 NL_DEBUG_PRINT ("\tRTMSG protocol: %d %s", protocol, FIND_RTM_PROTO_NAME (protocol));
242 NL_DEBUG_PRINT ("\tRTMSG scope: %d %s", scope, FIND_RTM_SCOPE_NAME (scope));
243 NL_DEBUG_PRINT ("\tRTMSG type: %d %s", rtm_type, FIND_RTM_TYPE_NAME (rtm_type));
245 rtap = (struct rtattr *) RTM_RTA (rtp);
246 rtl = RTM_PAYLOAD (nlp);
247 // loop & get every attribute
251 // table = RT_TABLE_LOCAL, Scope = HOST + pref.src == src + type=LOCAL -> new if addr
252 // RT_TABLE_MAIN, Scope = Universe, unicast, gateway exists -> NEW default route
254 // table = RT_TABLE_LOCAL, Scope = HOST, perfsrc = dst + type=LOCAL -> if addr deleted
255 // RT_TABLE_MAIN - DELROUTE + unicast -> event (gw down?)
256 have_dst = have_src = have_pref_src = have_gw = FALSE;
257 for(; RTA_OK (rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) {
261 char ip [INET6_ADDRSTRLEN];
262 int ip_length = INET6_ADDRSTRLEN;
264 char ip [INET_ADDRSTRLEN];
265 int ip_length = INET_ADDRSTRLEN;
269 NL_DEBUG_PRINT ("\tAttribute: %d %d (%s)", rtap->rta_len, rtap->rta_type, FIND_RTM_ATTRS_NAME (rtap->rta_type));
270 data = RTA_DATA (rtap);
271 switch (rtap->rta_type) {
274 memcpy (dst, data, addr_length);
277 inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
278 NL_DEBUG_PRINT ("\t\tDst: %s", ip);
282 have_pref_src = TRUE;
283 memcpy (pref_src, data, addr_length);
286 inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
287 NL_DEBUG_PRINT ("\t\tPref. Src.: %s", ip);
292 memcpy (src, data, addr_length);
295 inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
296 NL_DEBUG_PRINT ("\tSrc: %s", ip);
301 memcpy (gw, data, addr_length);
304 inet_ntop (family, RTA_DATA (rtap), ip, ip_length);
305 NL_DEBUG_PRINT ("\t\tGateway: %s", ip);
312 if (msg_type == RTM_NEWROUTE) {
313 if (table == RT_TABLE_MAIN) {
314 NL_DEBUG_PRINT ("NEWROUTE: Availability changed");
315 result |= EVT_AVAILABILITY;
316 } else if (table == RT_TABLE_LOCAL) {
317 NL_DEBUG_PRINT ("NEWROUTE: new IP");
318 if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0)
319 result |= EVT_ADDRESS;
321 } else if (msg_type == RTM_DELROUTE) {
322 if (table == RT_TABLE_MAIN) {
323 if (rtm_type == RTN_UNICAST && (have_dst || have_pref_src)) {
324 result |= EVT_AVAILABILITY;
325 NL_DEBUG_PRINT ("DELROUTE: Availability changed");
327 } else if (table == RT_TABLE_LOCAL) {
328 if (have_dst && have_pref_src && memcmp (dst, pref_src, addr_length) == 0) {
329 result |= EVT_ADDRESS;
330 NL_DEBUG_PRINT ("DELROUTE: deleted IP");
334 while ((count = recv (s, buffer, size, 0)) == -1 && errno == EINTR);
336 NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
339 nlp = (struct nlmsghdr *) buffer;
341 NL_DEBUG_PRINT ("EXIT ReadEvents() -> %d", result);
346 CloseNLSocket (gpointer sock)
348 return close (GPOINTER_TO_INT (sock));
358 ReadEvents (gpointer sock, gpointer buffer, gint32 count, gint32 size)
364 CloseNLSocket (gpointer sock)
368 #endif /* linux/netlink.h + linux/rtnetlink.h */