gio: fix build
[platform/upstream/glib.git] / gio / gnetworkmonitornetlink.c
index da60b8a..21a7ad5 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 
 #include <errno.h>
+#include <string.h>
 #include <unistd.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
 
 #include "gnetworkmonitornetlink.h"
 #include "gcredentials.h"
 #include "ginitable.h"
 #include "giomodule-priv.h"
 #include "glibintl.h"
+#include "glib/gstdio.h"
 #include "gnetworkingprivate.h"
 #include "gnetworkmonitor.h"
 #include "gsocket.h"
 #include "gunixcredentialsmessage.h"
 
+/* must come at the end to pick system includes from
+ * gnetworkingprivate.h */
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
 static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
 static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
 
-#define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
-G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
-                        G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
-                                               g_network_monitor_netlink_iface_init)
-                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
-                                               g_network_monitor_netlink_initable_iface_init)
-                        _g_io_modules_ensure_extension_points_registered ();
-                        g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
-                                                        g_define_type_id,
-                                                        "netlink",
-                                                        20))
-
 struct _GNetworkMonitorNetlinkPrivate
 {
   GSocket *sock;
@@ -60,39 +51,50 @@ struct _GNetworkMonitorNetlinkPrivate
 };
 
 static gboolean read_netlink_messages (GSocket             *socket,
-                                      GIOCondition         condition,
-                                      gpointer             user_data);
+                                       GIOCondition         condition,
+                                       gpointer             user_data);
 static gboolean request_dump (GNetworkMonitorNetlink  *nl,
-                             GError                 **error);
+                              GError                 **error);
+
+#define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
+G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
+                         G_ADD_PRIVATE (GNetworkMonitorNetlink)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
+                                                g_network_monitor_netlink_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_network_monitor_netlink_initable_iface_init)
+                         _g_io_modules_ensure_extension_points_registered ();
+                         g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         "netlink",
+                                                         20))
 
 static void
 g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
 {
-  nl->priv = G_TYPE_INSTANCE_GET_PRIVATE (nl,
-                                         G_TYPE_NETWORK_MONITOR_NETLINK,
-                                         GNetworkMonitorNetlinkPrivate);
+  nl->priv = g_network_monitor_netlink_get_instance_private (nl);
 }
 
 
 static gboolean
 g_network_monitor_netlink_initable_init (GInitable     *initable,
-                                        GCancellable  *cancellable,
-                                        GError       **error)
+                                         GCancellable  *cancellable,
+                                         GError       **error)
 {
   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
-  gint sockfd, val;
+  gint sockfd;
   struct sockaddr_nl snl;
 
   /* We create the socket the old-school way because sockaddr_netlink
    * can't be represented as a GSocketAddress
    */
-  sockfd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL);
   if (sockfd == -1)
     {
       int errsv = errno;
       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
-                  _("Could not create netlink socket: %s"),
-                  g_strerror (errno));
+                   _("Could not create network monitor: %s"),
+                   g_strerror (errno));
       return FALSE;
     }
 
@@ -103,28 +105,27 @@ g_network_monitor_netlink_initable_init (GInitable     *initable,
     {
       int errsv = errno;
       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
-                  _("Could not bind netlink socket: %s"),
-                  g_strerror (errno));
-      close (sockfd);
+                   _("Could not create network monitor: %s"),
+                   g_strerror (errno));
+      (void) g_close (sockfd, NULL);
       return FALSE;
     }
 
-  val = 1;
-  if (setsockopt (sockfd, SOL_SOCKET, SO_PASSCRED, &val, sizeof (val)) != 0)
+  nl->priv->sock = g_socket_new_from_fd (sockfd, error);
+  if (error)
     {
-      int errsv = errno;
-      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
-                  _("Could not set options on netlink socket: %s"),
-                  g_strerror (errno));
-      close (sockfd);
+      g_prefix_error (error, "%s", _("Could not create network monitor: "));
+      (void) g_close (sockfd, NULL);
       return FALSE;
     }
 
-  nl->priv->sock = g_socket_new_from_fd (sockfd, error);
-  if (error)
+  if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED,
+                           TRUE, NULL))
     {
-      g_prefix_error (error, "%s", _("Could not wrap netlink socket: "));
-      close (sockfd);
+      int errsv = errno;
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+                   _("Could not create network monitor: %s"),
+                   g_strerror (errno));
       return FALSE;
     }
 
@@ -138,22 +139,22 @@ g_network_monitor_netlink_initable_init (GInitable     *initable,
   while (nl->priv->dump_networks)
     {
       if (!read_netlink_messages (NULL, G_IO_IN, nl))
-       break;
+        break;
     }
 
   g_socket_set_blocking (nl->priv->sock, FALSE);
   nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
   g_source_set_callback (nl->priv->source,
-                        (GSourceFunc) read_netlink_messages, nl, NULL);
+                         (GSourceFunc) read_netlink_messages, nl, NULL);
   g_source_attach (nl->priv->source,
-                  g_main_context_get_thread_default ());
+                   g_main_context_get_thread_default ());
 
   return TRUE;
 }
 
 static gboolean
 request_dump (GNetworkMonitorNetlink  *nl,
-             GError                 **error)
+              GError                 **error)
 {
   struct nlmsghdr *n;
   struct rtgenmsg *gen;
@@ -169,9 +170,9 @@ request_dump (GNetworkMonitorNetlink  *nl,
   gen->rtgen_family = AF_UNSPEC;
 
   if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
-                    NULL, error) < 0)
+                     NULL, error) < 0)
     {
-      g_prefix_error (error, "%s", _("Could not send netlink request: "));
+      g_prefix_error (error, "%s", _("Could not get network status: "));
       return FALSE;
     }
 
@@ -207,17 +208,16 @@ queue_request_dump (GNetworkMonitorNetlink *nl)
 
   nl->priv->dump_source = g_timeout_source_new (1000);
   g_source_set_callback (nl->priv->dump_source,
-                        (GSourceFunc) timeout_request_dump, nl, NULL);
+                         (GSourceFunc) timeout_request_dump, nl, NULL);
   g_source_attach (nl->priv->dump_source,
-                  g_main_context_get_thread_default ());
+                   g_main_context_get_thread_default ());
 }
 
 static void
 add_network (GNetworkMonitorNetlink *nl,
-            GSocketFamily           family,
-            gint                    dest_len,
-            guint8                 *dest,
-            guint8                 *gateway)
+             GSocketFamily           family,
+             gint                    dest_len,
+             guint8                 *dest)
 {
   GInetAddress *dest_addr;
   GInetAddressMask *network;
@@ -241,10 +241,9 @@ add_network (GNetworkMonitorNetlink *nl,
 
 static void
 remove_network (GNetworkMonitorNetlink *nl,
-               GSocketFamily           family,
-               gint                    dest_len,
-               guint8                 *dest,
-               guint8                 *gateway)
+                GSocketFamily           family,
+                gint                    dest_len,
+                guint8                 *dest)
 {
   GInetAddress *dest_addr;
   GInetAddressMask *network;
@@ -263,14 +262,14 @@ remove_network (GNetworkMonitorNetlink *nl,
       int i;
 
       for (i = 0; i < nl->priv->dump_networks->len; i++)
-       {
-         if (g_inet_address_mask_equal (network, dump_networks[i]))
-           g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
-       }
+        {
+          if (g_inet_address_mask_equal (network, dump_networks[i]))
+            g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
+        }
       g_object_unref (network);
     }
   else
-    {      
+    {
       g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
       g_object_unref (network);
     }
@@ -280,20 +279,20 @@ static void
 finish_dump (GNetworkMonitorNetlink *nl)
 {
   g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
-                                      (GInetAddressMask **)nl->priv->dump_networks->pdata,
-                                      nl->priv->dump_networks->len);
-  g_ptr_array_free (nl->priv->dump_networks, FALSE);
+                                       (GInetAddressMask **)nl->priv->dump_networks->pdata,
+                                       nl->priv->dump_networks->len);
+  g_ptr_array_free (nl->priv->dump_networks, TRUE);
   nl->priv->dump_networks = NULL;
 }
 
 static gboolean
 read_netlink_messages (GSocket      *socket,
-                      GIOCondition  condition,
-                      gpointer      user_data)
+                       GIOCondition  condition,
+                       gpointer      user_data)
 {
   GNetworkMonitorNetlink *nl = user_data;
   GInputVector iv;
-  gsize len;
+  gssize len;
   GSocketControlMessage **cmsgs = NULL;
   gint num_cmsgs = 0, i, flags;
   GError *error = NULL;
@@ -303,7 +302,7 @@ read_netlink_messages (GSocket      *socket,
   struct rtmsg *rtmsg;
   struct rtattr *attr;
   gsize attrlen;
-  guint8 *dest, *gateway;
+  guint8 *dest, *gateway, *oif;
   gboolean retval = TRUE;
 
   iv.buffer = NULL;
@@ -311,26 +310,26 @@ read_netlink_messages (GSocket      *socket,
 
   flags = MSG_PEEK | MSG_TRUNC;
   len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
-                                 NULL, NULL, &flags, NULL, &error);
+                                  NULL, NULL, &flags, NULL, &error);
   if (len < 0)
     {
       g_warning ("Error on netlink socket: %s", error->message);
       g_error_free (error);
       if (nl->priv->dump_networks)
-       finish_dump (nl);
+        finish_dump (nl);
       return FALSE;
     }
 
   iv.buffer = g_malloc (len);
   iv.size = len;
   len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
-                                 &cmsgs, &num_cmsgs, NULL, NULL, &error);
+                                  &cmsgs, &num_cmsgs, NULL, NULL, &error);
   if (len < 0)
     {
       g_warning ("Error on netlink socket: %s", error->message);
       g_error_free (error);
       if (nl->priv->dump_networks)
-       finish_dump (nl);
+        finish_dump (nl);
       return FALSE;
     }
 
@@ -346,63 +345,78 @@ read_netlink_messages (GSocket      *socket,
   for (; len > 0; msg = NLMSG_NEXT (msg, len))
     {
       if (!NLMSG_OK (msg, (size_t) len))
-       {
-         g_warning ("netlink message was truncated; shouldn't happen...");
-         retval = FALSE;
-         goto done;
-       }
+        {
+          g_warning ("netlink message was truncated; shouldn't happen...");
+          retval = FALSE;
+          goto done;
+        }
 
       switch (msg->nlmsg_type)
-       {
-       case RTM_NEWROUTE:
-       case RTM_DELROUTE:
-         rtmsg = NLMSG_DATA (msg);
-
-         if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
-           continue;
-         if (rtmsg->rtm_type == RTN_UNREACHABLE)
-           continue;
-
-         attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
-         attr = RTM_RTA (rtmsg);
-         dest = gateway = NULL;
-         while (RTA_OK (attr, attrlen))
-           {
-             if (attr->rta_type == RTA_DST)
-               dest = RTA_DATA (attr);
-             else if (attr->rta_type == RTA_GATEWAY)
-               gateway = RTA_DATA (attr);
-             attr = RTA_NEXT (attr, attrlen);
-           }
-
-         if (dest || gateway)
-           {
-             if (msg->nlmsg_type == RTM_NEWROUTE)
-               add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
-             else
-               remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
-             queue_request_dump (nl);
-           }
-         break;
-
-       case NLMSG_DONE:
-         finish_dump (nl);
-         goto done;
-
-       case NLMSG_ERROR:
-         {
-           struct nlmsgerr *e = NLMSG_DATA (msg);
-
-           g_warning ("netlink error: %s", g_strerror (-e->error));
-         }
-         retval = FALSE;
-         goto done;
-
-       default:
-         g_warning ("unexpected netlink message %d", msg->nlmsg_type);
-         retval = FALSE;
-         goto done;
-       }
+        {
+        case RTM_NEWROUTE:
+        case RTM_DELROUTE:
+          rtmsg = NLMSG_DATA (msg);
+
+          if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
+            continue;
+          if (rtmsg->rtm_type == RTN_UNREACHABLE)
+            continue;
+
+          attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
+          attr = RTM_RTA (rtmsg);
+          dest = gateway = oif = NULL;
+          while (RTA_OK (attr, attrlen))
+            {
+              if (attr->rta_type == RTA_DST)
+                dest = RTA_DATA (attr);
+              else if (attr->rta_type == RTA_GATEWAY)
+                gateway = RTA_DATA (attr);
+              else if (attr->rta_type == RTA_OIF)
+                oif = RTA_DATA (attr);
+              attr = RTA_NEXT (attr, attrlen);
+            }
+
+          if (dest || gateway || oif)
+            {
+              /* Unless we're processing the results of a dump, ignore
+               * IPv6 link-local multicast routes, which are added and
+               * removed all the time for some reason.
+               */
+#define UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL(a)           \
+              ((a[0] == 0xff) && ((a[1] & 0xf) == 0x2))
+
+              if (!nl->priv->dump_networks &&
+                  rtmsg->rtm_family == AF_INET6 &&
+                  rtmsg->rtm_dst_len != 0 &&
+                  UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL (dest))
+                continue;
+
+              if (msg->nlmsg_type == RTM_NEWROUTE)
+                add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest);
+              else
+                remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest);
+              queue_request_dump (nl);
+            }
+          break;
+
+        case NLMSG_DONE:
+          finish_dump (nl);
+          goto done;
+
+        case NLMSG_ERROR:
+          {
+            struct nlmsgerr *e = NLMSG_DATA (msg);
+
+            g_warning ("netlink error: %s", g_strerror (-e->error));
+          }
+          retval = FALSE;
+          goto done;
+
+        default:
+          g_warning ("unexpected netlink message %d", msg->nlmsg_type);
+          retval = FALSE;
+          goto done;
+        }
     }
 
  done:
@@ -448,8 +462,6 @@ g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
 
-  g_type_class_add_private (nl_class, sizeof (GNetworkMonitorNetlinkPrivate));
-
   gobject_class->finalize  = g_network_monitor_netlink_finalize;
 }