nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
||
3 | * Copyright 2011 Red Hat, Inc. |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU Lesser General Public |
||
7 | * License as published by the Free Software Foundation; either |
||
8 | * version 2 of the License, or (at your option) any later version. |
||
9 | * |
||
10 | * This library is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * Lesser General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Lesser General |
||
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
17 | */ |
||
18 | |||
19 | #include "config.h" |
||
20 | |||
21 | #include <errno.h> |
||
22 | #include <string.h> |
||
23 | #include <unistd.h> |
||
24 | |||
25 | #include "gnetworkmonitornetlink.h" |
||
26 | #include "gcredentials.h" |
||
27 | #include "ginetaddressmask.h" |
||
28 | #include "ginitable.h" |
||
29 | #include "giomodule-priv.h" |
||
30 | #include "glibintl.h" |
||
31 | #include "glib/gstdio.h" |
||
32 | #include "gnetworkingprivate.h" |
||
33 | #include "gnetworkmonitor.h" |
||
34 | #include "gsocket.h" |
||
35 | #include "gunixcredentialsmessage.h" |
||
36 | |||
37 | /* must come at the end to pick system includes from |
||
38 | * gnetworkingprivate.h */ |
||
39 | #include <linux/netlink.h> |
||
40 | #include <linux/rtnetlink.h> |
||
41 | |||
42 | static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface); |
||
43 | static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface); |
||
44 | |||
45 | struct _GNetworkMonitorNetlinkPrivate |
||
46 | { |
||
47 | GSocket *sock; |
||
48 | GSource *source, *dump_source; |
||
49 | |||
50 | GPtrArray *dump_networks; |
||
51 | }; |
||
52 | |||
53 | static gboolean read_netlink_messages (GSocket *socket, |
||
54 | GIOCondition condition, |
||
55 | gpointer user_data); |
||
56 | static gboolean request_dump (GNetworkMonitorNetlink *nl, |
||
57 | GError **error); |
||
58 | |||
59 | #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type |
||
60 | G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE, |
||
61 | G_ADD_PRIVATE (GNetworkMonitorNetlink) |
||
62 | G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR, |
||
63 | g_network_monitor_netlink_iface_init) |
||
64 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, |
||
65 | g_network_monitor_netlink_initable_iface_init) |
||
66 | _g_io_modules_ensure_extension_points_registered (); |
||
67 | g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME, |
||
68 | g_define_type_id, |
||
69 | "netlink", |
||
70 | 20)) |
||
71 | |||
72 | static void |
||
73 | g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl) |
||
74 | { |
||
75 | nl->priv = g_network_monitor_netlink_get_instance_private (nl); |
||
76 | } |
||
77 | |||
78 | |||
79 | static gboolean |
||
80 | g_network_monitor_netlink_initable_init (GInitable *initable, |
||
81 | GCancellable *cancellable, |
||
82 | GError **error) |
||
83 | { |
||
84 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable); |
||
85 | gint sockfd; |
||
86 | struct sockaddr_nl snl; |
||
87 | |||
88 | /* We create the socket the old-school way because sockaddr_netlink |
||
89 | * can't be represented as a GSocketAddress |
||
90 | */ |
||
91 | sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL); |
||
92 | if (sockfd == -1) |
||
93 | { |
||
94 | int errsv = errno; |
||
95 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
||
96 | _("Could not create network monitor: %s"), |
||
97 | g_strerror (errno)); |
||
98 | return FALSE; |
||
99 | } |
||
100 | |||
101 | snl.nl_family = AF_NETLINK; |
||
102 | snl.nl_pid = snl.nl_pad = 0; |
||
103 | snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE; |
||
104 | if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0) |
||
105 | { |
||
106 | int errsv = errno; |
||
107 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
||
108 | _("Could not create network monitor: %s"), |
||
109 | g_strerror (errno)); |
||
110 | (void) g_close (sockfd, NULL); |
||
111 | return FALSE; |
||
112 | } |
||
113 | |||
114 | nl->priv->sock = g_socket_new_from_fd (sockfd, error); |
||
115 | if (error) |
||
116 | { |
||
117 | g_prefix_error (error, "%s", _("Could not create network monitor: ")); |
||
118 | (void) g_close (sockfd, NULL); |
||
119 | return FALSE; |
||
120 | } |
||
121 | |||
122 | if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED, |
||
123 | TRUE, NULL)) |
||
124 | { |
||
125 | int errsv = errno; |
||
126 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
||
127 | _("Could not create network monitor: %s"), |
||
128 | g_strerror (errno)); |
||
129 | return FALSE; |
||
130 | } |
||
131 | |||
132 | /* Request the current state */ |
||
133 | if (!request_dump (nl, error)) |
||
134 | return FALSE; |
||
135 | |||
136 | /* And read responses; since we haven't yet marked the socket |
||
137 | * non-blocking, each call will block until a message is received. |
||
138 | */ |
||
139 | while (nl->priv->dump_networks) |
||
140 | { |
||
141 | if (!read_netlink_messages (NULL, G_IO_IN, nl)) |
||
142 | break; |
||
143 | } |
||
144 | |||
145 | g_socket_set_blocking (nl->priv->sock, FALSE); |
||
146 | nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL); |
||
147 | g_source_set_callback (nl->priv->source, |
||
148 | (GSourceFunc) read_netlink_messages, nl, NULL); |
||
149 | g_source_attach (nl->priv->source, |
||
150 | g_main_context_get_thread_default ()); |
||
151 | |||
152 | return TRUE; |
||
153 | } |
||
154 | |||
155 | static gboolean |
||
156 | request_dump (GNetworkMonitorNetlink *nl, |
||
157 | GError **error) |
||
158 | { |
||
159 | struct nlmsghdr *n; |
||
160 | struct rtgenmsg *gen; |
||
161 | gchar buf[NLMSG_SPACE (sizeof (*gen))]; |
||
162 | |||
163 | memset (buf, 0, sizeof (buf)); |
||
164 | n = (struct nlmsghdr*) buf; |
||
165 | n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen)); |
||
166 | n->nlmsg_type = RTM_GETROUTE; |
||
167 | n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; |
||
168 | n->nlmsg_pid = 0; |
||
169 | gen = NLMSG_DATA (n); |
||
170 | gen->rtgen_family = AF_UNSPEC; |
||
171 | |||
172 | if (g_socket_send (nl->priv->sock, buf, sizeof (buf), |
||
173 | NULL, error) < 0) |
||
174 | { |
||
175 | g_prefix_error (error, "%s", _("Could not get network status: ")); |
||
176 | return FALSE; |
||
177 | } |
||
178 | |||
179 | nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref); |
||
180 | return TRUE; |
||
181 | } |
||
182 | |||
183 | static gboolean |
||
184 | timeout_request_dump (gpointer user_data) |
||
185 | { |
||
186 | GNetworkMonitorNetlink *nl = user_data; |
||
187 | |||
188 | g_source_destroy (nl->priv->dump_source); |
||
189 | g_source_unref (nl->priv->dump_source); |
||
190 | nl->priv->dump_source = NULL; |
||
191 | |||
192 | request_dump (nl, NULL); |
||
193 | |||
194 | return FALSE; |
||
195 | } |
||
196 | |||
197 | static void |
||
198 | queue_request_dump (GNetworkMonitorNetlink *nl) |
||
199 | { |
||
200 | if (nl->priv->dump_networks) |
||
201 | return; |
||
202 | |||
203 | if (nl->priv->dump_source) |
||
204 | { |
||
205 | g_source_destroy (nl->priv->dump_source); |
||
206 | g_source_unref (nl->priv->dump_source); |
||
207 | } |
||
208 | |||
209 | nl->priv->dump_source = g_timeout_source_new (1000); |
||
210 | g_source_set_callback (nl->priv->dump_source, |
||
211 | (GSourceFunc) timeout_request_dump, nl, NULL); |
||
212 | g_source_attach (nl->priv->dump_source, |
||
213 | g_main_context_get_thread_default ()); |
||
214 | } |
||
215 | |||
216 | static void |
||
217 | add_network (GNetworkMonitorNetlink *nl, |
||
218 | GSocketFamily family, |
||
219 | gint dest_len, |
||
220 | guint8 *dest) |
||
221 | { |
||
222 | GInetAddress *dest_addr; |
||
223 | GInetAddressMask *network; |
||
224 | |||
225 | if (dest) |
||
226 | dest_addr = g_inet_address_new_from_bytes (dest, family); |
||
227 | else |
||
228 | dest_addr = g_inet_address_new_any (family); |
||
229 | network = g_inet_address_mask_new (dest_addr, dest_len, NULL); |
||
230 | g_object_unref (dest_addr); |
||
231 | g_return_if_fail (network != NULL); |
||
232 | |||
233 | if (nl->priv->dump_networks) |
||
234 | g_ptr_array_add (nl->priv->dump_networks, network); |
||
235 | else |
||
236 | { |
||
237 | g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network); |
||
238 | g_object_unref (network); |
||
239 | } |
||
240 | } |
||
241 | |||
242 | static void |
||
243 | remove_network (GNetworkMonitorNetlink *nl, |
||
244 | GSocketFamily family, |
||
245 | gint dest_len, |
||
246 | guint8 *dest) |
||
247 | { |
||
248 | GInetAddress *dest_addr; |
||
249 | GInetAddressMask *network; |
||
250 | |||
251 | if (dest) |
||
252 | dest_addr = g_inet_address_new_from_bytes (dest, family); |
||
253 | else |
||
254 | dest_addr = g_inet_address_new_any (family); |
||
255 | network = g_inet_address_mask_new (dest_addr, dest_len, NULL); |
||
256 | g_object_unref (dest_addr); |
||
257 | g_return_if_fail (network != NULL); |
||
258 | |||
259 | if (nl->priv->dump_networks) |
||
260 | { |
||
261 | GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata; |
||
262 | int i; |
||
263 | |||
264 | for (i = 0; i < nl->priv->dump_networks->len; i++) |
||
265 | { |
||
266 | if (g_inet_address_mask_equal (network, dump_networks[i])) |
||
267 | g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--); |
||
268 | } |
||
269 | g_object_unref (network); |
||
270 | } |
||
271 | else |
||
272 | { |
||
273 | g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network); |
||
274 | g_object_unref (network); |
||
275 | } |
||
276 | } |
||
277 | |||
278 | static void |
||
279 | finish_dump (GNetworkMonitorNetlink *nl) |
||
280 | { |
||
281 | g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl), |
||
282 | (GInetAddressMask **)nl->priv->dump_networks->pdata, |
||
283 | nl->priv->dump_networks->len); |
||
284 | g_ptr_array_free (nl->priv->dump_networks, TRUE); |
||
285 | nl->priv->dump_networks = NULL; |
||
286 | } |
||
287 | |||
288 | static gboolean |
||
289 | read_netlink_messages (GSocket *socket, |
||
290 | GIOCondition condition, |
||
291 | gpointer user_data) |
||
292 | { |
||
293 | GNetworkMonitorNetlink *nl = user_data; |
||
294 | GInputVector iv; |
||
295 | gssize len; |
||
296 | gint flags; |
||
297 | GError *error = NULL; |
||
298 | GSocketAddress *addr; |
||
299 | struct nlmsghdr *msg; |
||
300 | struct rtmsg *rtmsg; |
||
301 | struct rtattr *attr; |
||
302 | struct sockaddr_nl source_sockaddr; |
||
303 | gsize attrlen; |
||
304 | guint8 *dest, *gateway, *oif; |
||
305 | gboolean retval = TRUE; |
||
306 | |||
307 | iv.buffer = NULL; |
||
308 | iv.size = 0; |
||
309 | |||
310 | flags = MSG_PEEK | MSG_TRUNC; |
||
311 | len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1, |
||
312 | NULL, NULL, &flags, NULL, &error); |
||
313 | if (len < 0) |
||
314 | { |
||
315 | g_warning ("Error on netlink socket: %s", error->message); |
||
316 | g_error_free (error); |
||
317 | if (nl->priv->dump_networks) |
||
318 | finish_dump (nl); |
||
319 | return FALSE; |
||
320 | } |
||
321 | |||
322 | iv.buffer = g_malloc (len); |
||
323 | iv.size = len; |
||
324 | len = g_socket_receive_message (nl->priv->sock, &addr, &iv, 1, |
||
325 | NULL, NULL, NULL, NULL, &error); |
||
326 | if (len < 0) |
||
327 | { |
||
328 | g_warning ("Error on netlink socket: %s", error->message); |
||
329 | g_error_free (error); |
||
330 | if (nl->priv->dump_networks) |
||
331 | finish_dump (nl); |
||
332 | return FALSE; |
||
333 | } |
||
334 | |||
335 | if (!g_socket_address_to_native (addr, &source_sockaddr, sizeof (source_sockaddr), &error)) |
||
336 | { |
||
337 | g_warning ("Error on netlink socket: %s", error->message); |
||
338 | g_error_free (error); |
||
339 | if (nl->priv->dump_networks) |
||
340 | finish_dump (nl); |
||
341 | return FALSE; |
||
342 | } |
||
343 | |||
344 | /* If the sender port id is 0 (not fakeable) then the message is from the kernel */ |
||
345 | if (source_sockaddr.nl_pid != 0) |
||
346 | goto done; |
||
347 | |||
348 | msg = (struct nlmsghdr *) iv.buffer; |
||
349 | for (; len > 0; msg = NLMSG_NEXT (msg, len)) |
||
350 | { |
||
351 | if (!NLMSG_OK (msg, (size_t) len)) |
||
352 | { |
||
353 | g_warning ("netlink message was truncated; shouldn't happen..."); |
||
354 | retval = FALSE; |
||
355 | goto done; |
||
356 | } |
||
357 | |||
358 | switch (msg->nlmsg_type) |
||
359 | { |
||
360 | case RTM_NEWROUTE: |
||
361 | case RTM_DELROUTE: |
||
362 | rtmsg = NLMSG_DATA (msg); |
||
363 | |||
364 | if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6) |
||
365 | continue; |
||
366 | if (rtmsg->rtm_type == RTN_UNREACHABLE) |
||
367 | continue; |
||
368 | |||
369 | attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg)); |
||
370 | attr = RTM_RTA (rtmsg); |
||
371 | dest = gateway = oif = NULL; |
||
372 | while (RTA_OK (attr, attrlen)) |
||
373 | { |
||
374 | if (attr->rta_type == RTA_DST) |
||
375 | dest = RTA_DATA (attr); |
||
376 | else if (attr->rta_type == RTA_GATEWAY) |
||
377 | gateway = RTA_DATA (attr); |
||
378 | else if (attr->rta_type == RTA_OIF) |
||
379 | oif = RTA_DATA (attr); |
||
380 | attr = RTA_NEXT (attr, attrlen); |
||
381 | } |
||
382 | |||
383 | if (dest || gateway || oif) |
||
384 | { |
||
385 | /* Unless we're processing the results of a dump, ignore |
||
386 | * IPv6 link-local multicast routes, which are added and |
||
387 | * removed all the time for some reason. |
||
388 | */ |
||
389 | #define UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL(a) \ |
||
390 | ((a[0] == 0xff) && ((a[1] & 0xf) == 0x2)) |
||
391 | |||
392 | if (!nl->priv->dump_networks && |
||
393 | rtmsg->rtm_family == AF_INET6 && |
||
394 | rtmsg->rtm_dst_len != 0 && |
||
395 | UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL (dest)) |
||
396 | continue; |
||
397 | |||
398 | if (msg->nlmsg_type == RTM_NEWROUTE) |
||
399 | add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest); |
||
400 | else |
||
401 | remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest); |
||
402 | queue_request_dump (nl); |
||
403 | } |
||
404 | break; |
||
405 | |||
406 | case NLMSG_DONE: |
||
407 | finish_dump (nl); |
||
408 | goto done; |
||
409 | |||
410 | case NLMSG_ERROR: |
||
411 | { |
||
412 | struct nlmsgerr *e = NLMSG_DATA (msg); |
||
413 | |||
414 | g_warning ("netlink error: %s", g_strerror (-e->error)); |
||
415 | } |
||
416 | retval = FALSE; |
||
417 | goto done; |
||
418 | |||
419 | default: |
||
420 | g_warning ("unexpected netlink message %d", msg->nlmsg_type); |
||
421 | retval = FALSE; |
||
422 | goto done; |
||
423 | } |
||
424 | } |
||
425 | |||
426 | done: |
||
427 | g_free (iv.buffer); |
||
428 | |||
429 | if (!retval && nl->priv->dump_networks) |
||
430 | finish_dump (nl); |
||
431 | return retval; |
||
432 | } |
||
433 | |||
434 | static void |
||
435 | g_network_monitor_netlink_finalize (GObject *object) |
||
436 | { |
||
437 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object); |
||
438 | |||
439 | if (nl->priv->sock) |
||
440 | { |
||
441 | g_socket_close (nl->priv->sock, NULL); |
||
442 | g_object_unref (nl->priv->sock); |
||
443 | } |
||
444 | |||
445 | if (nl->priv->source) |
||
446 | { |
||
447 | g_source_destroy (nl->priv->source); |
||
448 | g_source_unref (nl->priv->source); |
||
449 | } |
||
450 | |||
451 | if (nl->priv->dump_source) |
||
452 | { |
||
453 | g_source_destroy (nl->priv->dump_source); |
||
454 | g_source_unref (nl->priv->dump_source); |
||
455 | } |
||
456 | |||
457 | G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object); |
||
458 | } |
||
459 | |||
460 | static void |
||
461 | g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class) |
||
462 | { |
||
463 | GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); |
||
464 | |||
465 | gobject_class->finalize = g_network_monitor_netlink_finalize; |
||
466 | } |
||
467 | |||
468 | static void |
||
469 | g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface) |
||
470 | { |
||
471 | } |
||
472 | |||
473 | static void |
||
474 | g_network_monitor_netlink_initable_iface_init (GInitableIface *iface) |
||
475 | { |
||
476 | iface->init = g_network_monitor_netlink_initable_init; |
||
477 | } |