1 /*
2  * Copyright (C) 2003 Sun Microsystems, Inc.
3  * Copyright (C) 2004 Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18  * 02111-1307, USA.
19  *
20  * Authors:
21  *    Erwann Chenede  <erwann.chenede@sun.com>
22  *    Mark McLoughlin  <mark@skynet.ie>
23  *
24  * Support for the various hardware types is adapted from the
25  * net-tools package (version 1.60) which is also distributed
26  * under the GNU General Public License. The code used below
27  * is Copyright (C) 1993 MicroWalt Corporation.
28  */
29 
30 #include <config.h>
31 
32 #include "netstatus-iface.h"
33 
34 #include <glib/gi18n.h>
35 
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #ifdef HAVE_SYS_SOCKIO_H
39 #include <sys/sockio.h>
40 #endif /* HAVE_SYS_SOCKIO_H */
41 #include <sys/param.h>
42 #include <sys/socket.h>
43 #include <net/if.h>
44 #include <net/if_arp.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <errno.h>
48 #include <unistd.h>
49 #include <string.h>
50 
51 #include "netstatus-sysdeps.h"
52 #include "netstatus-enums.h"
53 
54 #define NETSTATUS_IFACE_POLL_DELAY       500  /* milliseconds between polls */
55 #define NETSTATUS_IFACE_POLLS_IN_ERROR   10   /* no. of polls in error before increasing delay */
56 #define NETSTATUS_IFACE_ERROR_POLL_DELAY 5000 /* delay to use when in error state */
57 
58 enum
59 {
60   PROP_0,
61   PROP_NAME,
62   PROP_STATE,
63   PROP_STATS,
64   PROP_WIRELESS,
65   PROP_SIGNAL_STRENGTH,
66   PROP_ERROR
67 };
68 
69 struct _NetstatusIfacePrivate
70 {
71   char           *name;
72 
73   NetstatusState  state;
74   NetstatusStats  stats;
75   int             signal_strength;
76   GError         *error;
77 
78   int             sockfd;
79   guint           monitor_id;
80 
81   guint           error_polling : 1;
82   guint           is_wireless : 1;
83 };
84 
85 static void     netstatus_iface_instance_init   (NetstatusIface      *iface,
86 						 NetstatusIfaceClass *klass);
87 static void     netstatus_iface_class_init      (NetstatusIfaceClass *klass);
88 static void     netstatus_iface_finalize        (GObject             *object);
89 static void     netstatus_iface_set_property    (GObject             *object,
90 						 guint                property_id,
91 						 const GValue        *value,
92 						 GParamSpec          *pspec);
93 static void     netstatus_iface_get_property    (GObject             *object,
94 						 guint                property_id,
95 						 GValue              *value,
96 						 GParamSpec          *pspec);
97 static gboolean netstatus_iface_monitor_timeout (NetstatusIface      *iface);
98 static void     netstatus_iface_init_monitor    (NetstatusIface      *iface);
99 
100 static GObjectClass *parent_class;
101 
102 GType
netstatus_iface_get_type(void)103 netstatus_iface_get_type (void)
104 {
105   static GType type = 0;
106   if (!type)
107     {
108       static const GTypeInfo info =
109       {
110         sizeof (NetstatusIfaceClass),
111         NULL,
112         NULL,
113         (GClassInitFunc) netstatus_iface_class_init,
114         NULL,
115         NULL,
116         sizeof (NetstatusIface),
117         0,
118         (GInstanceInitFunc) netstatus_iface_instance_init,
119         NULL
120       };
121       type = g_type_register_static (G_TYPE_OBJECT, "NetstatusIface", &info, 0);
122     }
123   return type;
124 }
125 
126 static void
netstatus_iface_instance_init(NetstatusIface * iface,NetstatusIfaceClass * klass)127 netstatus_iface_instance_init (NetstatusIface      *iface,
128 			       NetstatusIfaceClass *klass __attribute__((unused)))
129 {
130   iface->priv = g_new0 (NetstatusIfacePrivate, 1);
131   iface->priv->state = NETSTATUS_STATE_DISCONNECTED;
132 }
133 
134 static void
netstatus_iface_class_init(NetstatusIfaceClass * klass)135 netstatus_iface_class_init (NetstatusIfaceClass *klass)
136 {
137   GObjectClass *gobject_class = (GObjectClass *) klass;
138 
139   parent_class = g_type_class_peek_parent (klass);
140 
141   gobject_class->finalize     = netstatus_iface_finalize;
142   gobject_class->get_property = netstatus_iface_get_property;
143   gobject_class->set_property = netstatus_iface_set_property;
144 
145   g_object_class_install_property (gobject_class,
146 				   PROP_NAME,
147 				   g_param_spec_string ("name",
148 							_("Name"),
149 							_("The interface name"),
150 							NULL,
151 							G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
152 
153   g_object_class_install_property (gobject_class,
154 				   PROP_STATE,
155 				   g_param_spec_enum ("state",
156 						      _("State"),
157 						      _("The interface state"),
158 						      NETSTATUS_TYPE_STATE,
159 						      NETSTATUS_STATE_DISCONNECTED,
160 						      G_PARAM_READABLE));
161 
162   g_object_class_install_property (gobject_class,
163 				   PROP_STATS,
164 				   g_param_spec_boxed ("stats",
165 						       _("Stats"),
166 						       _("The interface packets/bytes statistics"),
167 						       NETSTATUS_TYPE_STATS,
168 						       G_PARAM_READABLE));
169 
170   g_object_class_install_property (gobject_class,
171 				   PROP_WIRELESS,
172 				   g_param_spec_boolean ("wireless",
173 							 _("Wireless"),
174 							 _("Whether the interface is a wireless interface"),
175 							 FALSE,
176 							 G_PARAM_READABLE));
177 
178   g_object_class_install_property (gobject_class,
179 				   PROP_SIGNAL_STRENGTH,
180 				   g_param_spec_int ("signal-strength",
181 						     _("Signal"),
182 						     _("Wireless signal strength percentage"),
183 						     0,
184 						     100,
185 						     0,
186 						     G_PARAM_READABLE));
187 
188   g_object_class_install_property (gobject_class,
189 				   PROP_ERROR,
190 				   g_param_spec_boxed ("error",
191 						       _("Error"),
192 						       _("The current error condition"),
193 						       NETSTATUS_TYPE_G_ERROR,
194 						       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
195 }
196 
197 static void
netstatus_iface_finalize(GObject * object)198 netstatus_iface_finalize (GObject *object)
199 {
200   NetstatusIface *iface = (NetstatusIface *) object;
201 
202   if (iface->priv->error)
203     g_error_free (iface->priv->error);
204   iface->priv->error = NULL;
205 
206   if (iface->priv->monitor_id)
207     g_source_remove (iface->priv->monitor_id);
208   iface->priv->monitor_id = 0;
209 
210   if (iface->priv->sockfd)
211     close (iface->priv->sockfd);
212   iface->priv->sockfd = 0;
213 
214   g_free (iface->priv->name);
215   iface->priv->name = NULL;
216 
217   g_free (iface->priv);
218   iface->priv = NULL;
219 
220   parent_class->finalize (object);
221 }
222 
223 static void
netstatus_iface_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)224 netstatus_iface_set_property (GObject      *object,
225 			      guint         property_id,
226 			      const GValue *value,
227 			      GParamSpec   *pspec)
228 {
229   NetstatusIface *iface = (NetstatusIface *) object;
230 
231   switch (property_id)
232     {
233     case PROP_NAME:
234       netstatus_iface_set_name (iface, g_value_get_string (value));
235       break;
236     case PROP_ERROR:
237       netstatus_iface_set_error (iface, g_value_get_boxed (value));
238       break;
239     default:
240       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
241       break;
242     }
243 }
244 
245 static void
netstatus_iface_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)246 netstatus_iface_get_property (GObject    *object,
247 			      guint       property_id,
248 			      GValue     *value,
249 			      GParamSpec *pspec)
250 {
251   NetstatusIface *iface = (NetstatusIface *) object;
252 
253   switch (property_id)
254     {
255     case PROP_NAME:
256       g_value_set_string (value, iface->priv->name);
257       break;
258     case PROP_STATE:
259       g_value_set_enum (value, iface->priv->state);
260       break;
261     case PROP_STATS:
262       g_value_set_boxed (value, &iface->priv->stats);
263       break;
264     case PROP_WIRELESS:
265       g_value_set_boolean (value, iface->priv->is_wireless);
266       break;
267     case PROP_SIGNAL_STRENGTH:
268       g_value_set_int (value, iface->priv->signal_strength);
269       break;
270     case PROP_ERROR:
271       g_value_set_boxed (value, iface->priv->error);
272     default:
273       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
274       break;
275     }
276 }
277 
278 NetstatusIface *
netstatus_iface_new(const char * name)279 netstatus_iface_new (const char *name)
280 {
281   return g_object_new (NETSTATUS_TYPE_IFACE,
282 		       "name", name,
283 		       NULL);
284 }
285 
286 void
netstatus_iface_set_name(NetstatusIface * iface,const char * name)287 netstatus_iface_set_name (NetstatusIface *iface,
288 			  const char     *name)
289 {
290   g_return_if_fail (NETSTATUS_IS_IFACE (iface));
291 
292   if (iface->priv->name && name &&
293       !strcmp (iface->priv->name, name))
294     return;
295 
296   if (name && strlen (name) >= IF_NAMESIZE)
297     {
298       g_warning (G_STRLOC ": interface name '%s' is too long\n", name);
299       return;
300     }
301 
302   if (iface->priv->name)
303     g_free (iface->priv->name);
304   iface->priv->name = g_strdup (name);
305 
306   netstatus_iface_init_monitor (iface);
307 
308   g_object_notify (G_OBJECT (iface), "name");
309 }
310 
311 const char *
netstatus_iface_get_name(NetstatusIface * iface)312 netstatus_iface_get_name (NetstatusIface *iface)
313 {
314   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), NULL);
315 
316   return iface->priv->name;
317 }
318 
319 NetstatusState
netstatus_iface_get_state(NetstatusIface * iface)320 netstatus_iface_get_state (NetstatusIface *iface)
321 {
322   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), NETSTATUS_STATE_DISCONNECTED);
323 
324   return iface->priv->state;
325 }
326 
327 void
netstatus_iface_get_statistics(NetstatusIface * iface,NetstatusStats * stats)328 netstatus_iface_get_statistics (NetstatusIface *iface,
329 				NetstatusStats *stats)
330 {
331   g_return_if_fail (NETSTATUS_IS_IFACE (iface));
332 
333   if (stats)
334     *stats  = iface->priv->stats;
335 }
336 
337 gboolean
netstatus_iface_get_is_wireless(NetstatusIface * iface)338 netstatus_iface_get_is_wireless (NetstatusIface *iface)
339 {
340   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), FALSE);
341 
342   return iface->priv->is_wireless;
343 }
344 
345 int
netstatus_iface_get_signal_strength(NetstatusIface * iface)346 netstatus_iface_get_signal_strength (NetstatusIface *iface)
347 {
348   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), 0);
349 
350   return iface->priv->signal_strength;
351 }
352 
353 void
netstatus_iface_set_error(NetstatusIface * iface,const GError * error)354 netstatus_iface_set_error (NetstatusIface *iface,
355 			   const GError   *error)
356 {
357   g_return_if_fail (NETSTATUS_IS_IFACE (iface));
358 
359   if (iface->priv->state != NETSTATUS_STATE_ERROR && error)
360     {
361       g_assert (iface->priv->error == NULL);
362 
363       iface->priv->state = NETSTATUS_STATE_ERROR;
364       iface->priv->error = g_error_copy (error);
365 
366       g_object_notify (G_OBJECT (iface), "state");
367       g_object_notify (G_OBJECT (iface), "error");
368     }
369 }
370 
371 const GError *
netstatus_iface_get_error(NetstatusIface * iface)372 netstatus_iface_get_error (NetstatusIface *iface)
373 {
374   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), NULL);
375 
376   return iface->priv->error;
377 }
378 
379 void
netstatus_iface_clear_error(NetstatusIface * iface,NetstatusError code)380 netstatus_iface_clear_error (NetstatusIface *iface,
381 			     NetstatusError  code)
382 {
383   g_return_if_fail (NETSTATUS_IS_IFACE (iface));
384 
385   if (iface->priv->state == NETSTATUS_STATE_ERROR &&
386       g_error_matches (iface->priv->error, NETSTATUS_ERROR, code))
387     {
388       iface->priv->state = NETSTATUS_STATE_DISCONNECTED;
389 
390       g_error_free (iface->priv->error);
391       iface->priv->error = NULL;
392 
393       g_object_notify (G_OBJECT (iface), "state");
394       g_object_notify (G_OBJECT (iface), "error");
395     }
396 }
397 
398 static void
netstatus_iface_set_polling_error(NetstatusIface * iface,NetstatusError code,const char * format,...)399 netstatus_iface_set_polling_error (NetstatusIface *iface,
400 				   NetstatusError  code,
401 				   const char     *format,
402 				   ...)
403 {
404   GError  *error;
405   va_list  args;
406 #if !GLIB_CHECK_VERSION(2, 22, 0)
407   char    *error_message;
408 #endif
409 
410   va_start (args, format);
411 
412 #if GLIB_CHECK_VERSION(2, 22, 0)
413   error = g_error_new_valist (NETSTATUS_ERROR, code, format, args);
414 #else
415   error_message = g_strdup_vprintf (format, args);
416   error = g_error_new (NETSTATUS_ERROR, code, "%s", error_message);
417   g_free (error_message);
418 #endif
419 
420   dprintf (POLLING, "ERROR: %s\n", error->message);
421   netstatus_iface_set_error (iface, error);
422 
423   g_error_free (error);
424 
425   va_end (args);
426 }
427 
428 static int
netstatus_iface_get_sockfd(NetstatusIface * iface)429 netstatus_iface_get_sockfd (NetstatusIface *iface)
430 {
431   int fd;
432 
433   if (iface->priv->sockfd)
434     return iface->priv->sockfd;
435 
436   if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
437     {
438       netstatus_iface_set_polling_error (iface,
439 					 NETSTATUS_ERROR_SOCKET,
440 					 _("Unable to open socket: %s"),
441 					 g_strerror (errno));
442       return 0;
443     }
444 
445   dprintf (POLLING, "Successfully opened socket for polling\n");
446   netstatus_iface_clear_error (iface, NETSTATUS_ERROR_SOCKET);
447 
448   iface->priv->sockfd = fd;
449 
450   return iface->priv->sockfd;
451 }
452 
453 static gboolean
netstatus_iface_poll_iface_statistics(NetstatusIface * iface,gulong * in_packets,gulong * out_packets,gulong * in_bytes,gulong * out_bytes)454 netstatus_iface_poll_iface_statistics (NetstatusIface *iface,
455 				       gulong         *in_packets,
456 				       gulong         *out_packets,
457 				       gulong         *in_bytes,
458 				       gulong         *out_bytes)
459 {
460   char *error_message;
461 
462   error_message = netstatus_sysdeps_read_iface_statistics (iface->priv->name,
463 							   in_packets,
464 							   out_packets,
465 							   in_bytes,
466 							   out_bytes);
467   if (error_message)
468     {
469       netstatus_iface_set_polling_error (iface,
470 					 NETSTATUS_ERROR_STATISTICS,
471 					 error_message);
472       g_free (error_message);
473 
474       return FALSE;
475     }
476 
477   netstatus_iface_clear_error (iface, NETSTATUS_ERROR_STATISTICS);
478 
479   return TRUE;
480 }
481 
482 static NetstatusState
netstatus_iface_poll_state(NetstatusIface * iface)483 netstatus_iface_poll_state (NetstatusIface *iface)
484 {
485   NetstatusState state;
486   struct ifreq   if_req;
487   gboolean       tx, rx;
488   int            fd;
489   gulong         in_packets, out_packets;
490   gulong         in_bytes, out_bytes;
491 
492   if (!(fd = netstatus_iface_get_sockfd (iface)))
493     return NETSTATUS_STATE_DISCONNECTED;
494 
495   memset (&if_req, 0, sizeof (struct ifreq));
496   strcpy (if_req.ifr_name, iface->priv->name);
497 
498   if (ioctl (fd, SIOCGIFFLAGS, &if_req) < 0)
499     {
500       netstatus_iface_set_polling_error (iface,
501 					 NETSTATUS_ERROR_IOCTL_IFFLAGS,
502 					 _("SIOCGIFFLAGS error: %s"),
503 					 g_strerror (errno));
504       return NETSTATUS_STATE_DISCONNECTED;
505     }
506 
507   netstatus_iface_clear_error (iface, NETSTATUS_ERROR_IOCTL_IFFLAGS);
508 
509   dprintf (POLLING, "Interface is %sup and %srunning\n",
510 	   if_req.ifr_flags & IFF_UP ? "" : "not ",
511 	   if_req.ifr_flags & IFF_RUNNING ? "" : "not ");
512 
513   if (!(if_req.ifr_flags & IFF_UP) || !(if_req.ifr_flags & IFF_RUNNING))
514     return NETSTATUS_STATE_DISCONNECTED;
515 
516   if (!netstatus_iface_poll_iface_statistics (iface, &in_packets, &out_packets, &in_bytes, &out_bytes))
517     return NETSTATUS_STATE_IDLE;
518 
519   dprintf (POLLING, "Packets in: %ld out: %ld. Prev in: %ld out: %ld\n",
520 	   in_packets, out_packets,
521 	   iface->priv->stats.in_packets, iface->priv->stats.out_packets);
522   dprintf (POLLING, "Bytes in: %ld out: %ld. Prev in: %ld out: %ld\n",
523 	   in_bytes, out_bytes,
524 	   iface->priv->stats.in_bytes, iface->priv->stats.out_bytes);
525 
526   rx = in_packets  > iface->priv->stats.in_packets;
527   tx = out_packets > iface->priv->stats.out_packets;
528 
529   if (!tx && !rx)
530     state = NETSTATUS_STATE_IDLE;
531   else if (tx && rx)
532     state = NETSTATUS_STATE_TX_RX;
533   else if (tx)
534     state = NETSTATUS_STATE_TX;
535   else /* if (rx) */
536     state = NETSTATUS_STATE_RX;
537 
538   dprintf (POLLING, "State: %s\n", netstatus_get_state_string (state));
539 
540   if (tx || rx)
541     {
542       iface->priv->stats.in_packets  = in_packets;
543       iface->priv->stats.out_packets = out_packets;
544       iface->priv->stats.in_bytes    = in_bytes;
545       iface->priv->stats.out_bytes   = out_bytes;
546 
547       g_object_notify (G_OBJECT (iface), "stats");
548     }
549 
550   return state;
551 }
552 
553 static gboolean
netstatus_iface_poll_wireless_details(NetstatusIface * iface,int * signal_strength)554 netstatus_iface_poll_wireless_details (NetstatusIface *iface,
555 				       int            *signal_strength)
556 {
557   char     *error_message;
558   gboolean  is_wireless;
559 
560   error_message = netstatus_sysdeps_read_iface_wireless_details (iface->priv->name,
561 								 &is_wireless,
562 								 signal_strength);
563 
564   if (error_message)
565     {
566       netstatus_iface_set_polling_error (iface,
567 					 NETSTATUS_ERROR_WIRELESS_DETAILS,
568 					 error_message);
569       g_free (error_message);
570 
571       return FALSE;
572     }
573 
574   netstatus_iface_clear_error (iface, NETSTATUS_ERROR_WIRELESS_DETAILS);
575 
576   return is_wireless;
577 }
578 
579 static void
netstatus_iface_increase_poll_delay_in_error(NetstatusIface * iface)580 netstatus_iface_increase_poll_delay_in_error (NetstatusIface *iface)
581 {
582   static int polls_in_error = 0;
583 
584   if (iface->priv->state == NETSTATUS_STATE_ERROR)
585     {
586       dprintf (POLLING, "Interface in error state\n");
587 
588       if (!iface->priv->error_polling &&
589 	  ++polls_in_error >= NETSTATUS_IFACE_POLLS_IN_ERROR)
590 	{
591 	  dprintf (POLLING, "Increasing polling delay after too many errors\n");
592 	  iface->priv->error_polling = TRUE;
593 	  g_source_remove (iface->priv->monitor_id);
594 	  iface->priv->monitor_id = g_timeout_add (NETSTATUS_IFACE_ERROR_POLL_DELAY,
595 						   (GSourceFunc) netstatus_iface_monitor_timeout,
596 						   iface);
597 	}
598     }
599   else if (iface->priv->error_polling)
600     {
601       dprintf (POLLING, "Recovered from errors. Increasing polling delay again\n");
602 
603       iface->priv->error_polling = FALSE;
604       polls_in_error = 0;
605 
606       g_source_remove (iface->priv->monitor_id);
607       iface->priv->monitor_id = g_timeout_add (NETSTATUS_IFACE_POLL_DELAY,
608 					       (GSourceFunc) netstatus_iface_monitor_timeout,
609 					       iface);
610     }
611 }
612 
613 static gboolean
netstatus_iface_monitor_timeout(NetstatusIface * iface)614 netstatus_iface_monitor_timeout (NetstatusIface *iface)
615 {
616   NetstatusState state;
617   int            signal_strength;
618   gboolean       is_wireless;
619 
620   if (g_source_is_destroyed(g_main_current_source()))
621     return FALSE;
622 
623   state = netstatus_iface_poll_state (iface);
624 
625   if (iface->priv->state != state &&
626       iface->priv->state != NETSTATUS_STATE_ERROR)
627     {
628       iface->priv->state = state;
629       g_object_notify (G_OBJECT (iface), "state");
630     }
631 
632   is_wireless = netstatus_iface_poll_wireless_details (iface, &signal_strength);
633   if (iface->priv->is_wireless != is_wireless)
634     {
635       iface->priv->is_wireless = is_wireless;
636       g_object_notify (G_OBJECT (iface), "wireless");
637     }
638 
639   if (iface->priv->signal_strength != signal_strength)
640     {
641       iface->priv->signal_strength = signal_strength;
642       g_object_notify (G_OBJECT (iface), "signal-strength");
643     }
644 
645   netstatus_iface_increase_poll_delay_in_error (iface);
646 
647   return TRUE;
648 }
649 
650 static void
netstatus_iface_init_monitor(NetstatusIface * iface)651 netstatus_iface_init_monitor (NetstatusIface *iface)
652 {
653   iface->priv->stats.in_packets  = 0;
654   iface->priv->stats.out_packets = 0;
655   iface->priv->stats.in_bytes    = 0;
656   iface->priv->stats.out_bytes   = 0;
657   iface->priv->signal_strength   = 0;
658   iface->priv->is_wireless       = FALSE;
659 
660   g_object_freeze_notify (G_OBJECT (iface));
661   g_object_notify (G_OBJECT (iface), "state");
662   g_object_notify (G_OBJECT (iface), "wireless");
663   g_object_notify (G_OBJECT (iface), "signal-strength");
664   g_object_thaw_notify (G_OBJECT (iface));
665 
666   if (iface->priv->monitor_id)
667     {
668       dprintf (POLLING, "Removing existing monitor\n");
669       g_source_remove (iface->priv->monitor_id);
670       iface->priv->monitor_id = 0;
671     }
672 
673   if (iface->priv->name)
674     {
675       dprintf (POLLING, "Initialising monitor with delay of %d\n", NETSTATUS_IFACE_POLL_DELAY);
676       iface->priv->monitor_id = g_timeout_add (NETSTATUS_IFACE_POLL_DELAY,
677 					       (GSourceFunc) netstatus_iface_monitor_timeout,
678 					       iface);
679 
680       /* netstatus_iface_monitor_timeout (iface); */
681     }
682 }
683 
684 gboolean
netstatus_iface_get_inet4_details(NetstatusIface * iface,char ** addr,char ** dest,char ** bcast,char ** mask)685 netstatus_iface_get_inet4_details (NetstatusIface  *iface,
686 				   char           **addr,
687 				   char           **dest,
688 				   char           **bcast,
689 				   char           **mask)
690 {
691   struct ifreq if_req;
692   int          fd;
693   int          flags;
694 
695   if (addr)
696     *addr = NULL;
697   if (dest)
698     *dest = NULL;
699   if (mask)
700     *mask = NULL;
701 
702   if (!iface->priv->name)
703     return FALSE;
704 
705   if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
706     {
707       g_warning (G_STRLOC ": unable to open AF_INET socket: %s\n",
708 		 g_strerror (errno));
709       return FALSE;
710     }
711 
712   if_req.ifr_addr.sa_family = AF_INET;
713 
714   strncpy (if_req.ifr_name, iface->priv->name, IF_NAMESIZE - 1);
715   if_req.ifr_name [IF_NAMESIZE - 1] = '\0';
716   if (addr && ioctl (fd, SIOCGIFADDR, &if_req) == 0)
717     *addr = g_strdup (inet_ntoa (((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr));
718 
719   if (addr && !*addr)
720     {
721       close (fd);
722       return FALSE;
723     }
724 
725   strncpy (if_req.ifr_name, iface->priv->name, IF_NAMESIZE - 1);
726   if_req.ifr_name [IF_NAMESIZE - 1] = '\0';
727   if (ioctl (fd, SIOCGIFFLAGS, &if_req) < 0)
728     {
729       close (fd);
730       return TRUE;
731     }
732 
733   flags = if_req.ifr_flags;
734 
735   strncpy (if_req.ifr_name, iface->priv->name, IF_NAMESIZE - 1);
736   if_req.ifr_name [IF_NAMESIZE - 1] = '\0';
737   if (dest && flags & IFF_POINTOPOINT &&
738       ioctl (fd, SIOCGIFDSTADDR, &if_req) == 0)
739     *dest = g_strdup (inet_ntoa (((struct sockaddr_in *) &if_req.ifr_dstaddr)->sin_addr));
740 
741   strncpy (if_req.ifr_name, iface->priv->name, IF_NAMESIZE - 1);
742   if_req.ifr_name [IF_NAMESIZE - 1] = '\0';
743   if (bcast && flags & IFF_BROADCAST &&
744       ioctl (fd, SIOCGIFBRDADDR, &if_req) == 0)
745     *bcast = g_strdup (inet_ntoa (((struct sockaddr_in *) &if_req.ifr_broadaddr)->sin_addr));
746 
747   strncpy (if_req.ifr_name, iface->priv->name, IF_NAMESIZE - 1);
748   if_req.ifr_name [IF_NAMESIZE - 1] = '\0';
749   if (mask && ioctl (fd, SIOCGIFNETMASK, &if_req) == 0)
750     *mask = g_strdup (inet_ntoa (((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr));
751 
752   close (fd);
753 
754   return TRUE;
755 }
756 
757 static char *
print_mac_addr(guchar * p)758 print_mac_addr (guchar *p)
759 {
760   return g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
761 			  p [0] & 0377,
762 			  p [1] & 0377,
763 			  p [2] & 0377,
764 			  p [3] & 0377,
765 			  p [4] & 0377,
766 			  p [5] & 0377);
767 }
768 
769 static char *
print_ash_addr(guchar * p)770 print_ash_addr (guchar *p)
771 {
772 #define ASH_ALEN 64
773 
774   GString *str;
775   char    *retval;
776   int      i = 0;
777 
778   str = g_string_new ("[");
779 
780   while (p [i] != 0xc9 && p [i] != 0xff && (i < ASH_ALEN))
781     g_string_append_printf (str, "%1x", p [i++]);
782 
783   g_string_append_c (str, ']');
784 
785   retval = str->str;
786   g_string_free (str, FALSE);
787 
788   return retval;
789 
790 #undef ASH_ALEN
791 }
792 
793 static char *
print_ax25_addr(guchar * p)794 print_ax25_addr (guchar *p)
795 {
796   GString *str;
797   char    *retval;
798   int      i;
799 
800   str = g_string_new (NULL);
801 
802   for (i = 0; i < 6; i++)
803     {
804       char c = (p [i] & 0377) >> 1;
805 
806       if (c == ' ')
807 	{
808 	  retval = str->str;
809 	  g_string_free (str, FALSE);
810 
811 	  return retval;
812 	}
813 
814       g_string_append_c (str, c);
815     }
816 
817   i = (p [6] & 0x1E) >> 1;
818   if (i != 0)
819     g_string_append_printf (str, "-%d", i);
820 
821   retval = str->str;
822   g_string_free (str, FALSE);
823 
824   return retval;
825 }
826 
827 static char *
print_rose_addr(guchar * p)828 print_rose_addr (guchar *p)
829 {
830   return g_strdup_printf ("%02x%02x%02x%02x%02x", p [0], p [1], p [2], p [3], p [4]);
831 }
832 
833 static char *
print_x25_addr(guchar * p)834 print_x25_addr (guchar *p)
835 {
836   return g_strdup ((const gchar *) p);
837 }
838 
839 static char *
print_arcnet_addr(guchar * p)840 print_arcnet_addr (guchar *p)
841 {
842   return g_strdup_printf ("%02X", p [0] & 0377);
843 }
844 
845 static char *
print_dlci_addr(guchar * p)846 print_dlci_addr (guchar *p)
847 {
848   return g_strdup_printf ("%i", *(short *) p);
849 }
850 
851 static char *
print_irda_addr(guchar * p)852 print_irda_addr (guchar *p)
853 {
854   return g_strdup_printf ("%02x:%02x:%02x:%02x", p [3], p [2], p [1], p [0]);
855 }
856 
857 static char *
print_econet_addr(guchar * p)858 print_econet_addr (guchar *p)
859 {
860   /* This should really be:
861    *   #include <neteconet/ec.h>
862    *
863    *   struct ec_addr *ec = (struct ec_addr *) p;
864    *   return g_strdup_printf ("%d.%d, ec->net, ec->station);
865    *
866    * But I think the hack is safe enough.
867    */
868   return g_strdup_printf ("%d.%d", p [0], p [1]);
869 }
870 
871 static struct HwType
872 {
873   int   hw_type;
874   char *hw_name;
875   char *(*print_hw_addr) (guchar *p);
876 } hw_types [] =
877   {
878 #ifdef ARPHRD_NETROM
879     { ARPHRD_NETROM, N_("AMPR NET/ROM"), print_ax25_addr },
880 #endif
881 #ifdef ARPHRD_ETHER
882     { ARPHRD_ETHER, N_("Ethernet"), print_mac_addr },
883 #endif
884 #ifdef ARPHRD_EETHER
885     { ARPHRD_EETHER, NULL, NULL },
886 #endif
887 #ifdef ARPHRD_AX25
888     { ARPHRD_AX25, N_("AMPR AX.25"), NULL },
889 #endif
890 #ifdef ARPHRD_PRONET
891     { ARPHRD_PRONET, NULL, NULL },
892 #endif
893 #ifdef ARPHRD_CHAOS
894     { ARPHRD_CHAOS, NULL, NULL },
895 #endif
896 #ifdef ARPHRD_IEEE802
897     { ARPHRD_IEEE802, N_("16/4 Mbps Token Ring"), print_mac_addr },
898 #endif
899 #ifdef ARPHRD_ARCNET
900     { ARPHRD_ARCNET, N_("ARCnet"), print_arcnet_addr },
901 #endif
902 #ifdef ARPHRD_APPLETLK
903     { ARPHRD_APPLETLK, NULL, NULL },
904 #endif
905 #ifdef ARPHRD_DLCI
906     { ARPHRD_DLCI, N_("Frame Relay DLCI"), print_dlci_addr },
907 #endif
908 #ifdef ARPHRD_ATM
909     { ARPHRD_ATM, NULL, NULL },
910 #endif
911 #ifdef ARPHRD_METRICOM
912     { ARPHRD_METRICOM, N_("Metricom Starmode IP"), NULL },
913 #endif
914 #ifdef ARPHRD_SLIP
915     { ARPHRD_SLIP, N_("Serial Line IP"), NULL },
916 #endif
917 #ifdef ARPHRD_CSLIP
918     { ARPHRD_CSLIP, N_("VJ Serial Line IP"), NULL },
919 #endif
920 #ifdef ARPHRD_SLIP6
921     { ARPHRD_SLIP6, N_("6-bit Serial Line IP"), NULL },
922 #endif
923 #ifdef ARPHRD_CSLIP6
924     { ARPHRD_CSLIP6, N_("VJ 6-bit Serial Line IP"), NULL },
925 #endif
926 #ifdef ARPHRD_RSRVD
927     { ARPHRD_RSRVD, NULL, NULL },
928 #endif
929 #ifdef ARPHRD_ADAPT
930     { ARPHRD_ADAPT, N_("Adaptive Serial Line IP"), NULL },
931 #endif
932 #ifdef ARPHRD_ROSE
933     { ARPHRD_ROSE, N_("AMPR ROSE"), print_rose_addr },
934 #endif
935 #ifdef ARPHRD_X25
936     { ARPHRD_X25, N_("Generic X.25"), print_x25_addr },
937 #endif
938 #ifdef ARPHRD_PPP
939     { ARPHRD_PPP, N_("Point-to-Point Protocol"), NULL },
940 #endif
941 #ifdef ARPHRD_CISCO
942     { ARPHRD_CISCO, NULL, NULL },
943 #endif
944 #ifdef ARPHRD_HDLC
945     { ARPHRD_HDLC, N_("(Cisco)-HDLC"), NULL },
946 #endif
947 #ifdef ARPHRD_LAPB
948     { ARPHRD_LAPB, N_("LAPB"), NULL },
949 #endif
950 #ifdef ARPHRD_DDCMP
951     { ARPHRD_DDCMP, NULL, NULL },
952 #endif
953 #ifdef ARPHRD_RAWHDLC
954     { ARPHRD_RAWHDLC, NULL, NULL },
955 #endif
956 #ifdef ARPHRD_TUNNEL
957     { ARPHRD_TUNNEL, N_("IPIP Tunnel"), NULL },
958 #endif
959 #ifdef ARPHRD_TUNNEL6
960     { ARPHRD_TUNNEL6, NULL, NULL },
961 #endif
962 #ifdef ARPHRD_FRAD
963     { ARPHRD_FRAD, N_("Frame Relay Access Device"), NULL },
964 #endif
965 #ifdef ARPHRD_SKIP
966     { ARPHRD_SKIP, NULL, NULL },
967 #endif
968 #ifdef ARPHRD_LOOPBACK
969     { ARPHRD_LOOPBACK, N_("Local Loopback"), print_mac_addr },
970 #endif
971 #ifdef ARPHRD_LOCALTLK
972     { ARPHRD_LOCALTLK, NULL, NULL },
973 #endif
974 #ifdef ARPHRD_FDDI
975     { ARPHRD_FDDI, N_("Fiber Distributed Data Interface"), print_mac_addr },
976 #endif
977 #ifdef ARPHRD_BIF
978     { ARPHRD_BIF, NULL, NULL },
979 #endif
980 #ifdef ARPHRD_SIT
981     { ARPHRD_SIT, N_("IPv6-in-IPv4"), NULL },
982 #endif
983 #ifdef ARPHRD_IPDDP
984     { ARPHRD_IPDDP, NULL, NULL },
985 #endif
986 #ifdef ARPHRD_IPGRE
987     { ARPHRD_IPGRE, NULL, NULL },
988 #endif
989 #ifdef ARPHRD_PIMREG
990     { ARPHRD_PIMREG, NULL, NULL },
991 #endif
992 #ifdef ARPHRD_HIPPI
993     { ARPHRD_HIPPI, N_("HIPPI"), print_mac_addr },
994 #endif
995 #ifdef ARPHRD_ASH
996     { ARPHRD_ASH, N_("Ash"), print_ash_addr },
997 #endif
998 #ifdef ARPHRD_ECONET
999     { ARPHRD_ECONET, N_("Econet"), print_econet_addr },
1000 #endif
1001 #ifdef ARPHRD_IRDA
1002     { ARPHRD_IRDA, N_("IrLAP"), print_irda_addr },
1003 #endif
1004 #ifdef ARPHRD_FCPP
1005     { ARPHRD_FCPP, NULL, NULL },
1006 #endif
1007 #ifdef ARPHRD_FCAL
1008     { ARPHRD_FCAL, NULL, NULL },
1009 #endif
1010 #ifdef ARPHRD_FCPL
1011     { ARPHRD_FCPL, NULL, NULL },
1012 #endif
1013 #ifdef ARPHRD_FCPFABRIC
1014     { ARPHRD_FCPFABRIC, NULL, NULL },
1015 #endif
1016 #ifdef ARPHRD_IEEE802_TR
1017     { ARPHRD_IEEE802_TR, N_("16/4 Mbps Token Ring"), print_mac_addr },
1018 #endif
1019 #ifdef ARPHRD_IEEE80211
1020     { ARPHRD_IEEE80211, NULL, NULL },
1021 #endif
1022   };
1023 
1024 static struct HwType *
netstatus_iface_get_hw_details(NetstatusIface * iface,char ** hw_addr)1025 netstatus_iface_get_hw_details (NetstatusIface  *iface,
1026 				char           **hw_addr)
1027 {
1028 #ifdef SIOCGIFHWADDR
1029   static struct HwType *hw_type = NULL;
1030   struct ifreq          if_req;
1031   int                   fd;
1032   unsigned int                   i;
1033 
1034   if (hw_addr)
1035     *hw_addr = NULL;
1036 
1037   if (!iface->priv->name)
1038     return NULL;
1039 
1040   if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
1041     {
1042       g_warning (G_STRLOC ": unable to open AF_INET socket: %s\n",
1043 		 g_strerror (errno));
1044       return NULL;
1045     }
1046 
1047   strncpy (if_req.ifr_name, iface->priv->name, IF_NAMESIZE - 1);
1048   if_req.ifr_name [IF_NAMESIZE - 1] = '\0';
1049   if (ioctl (fd, SIOCGIFHWADDR, &if_req) < 0)
1050     {
1051       g_warning (G_STRLOC ": unable to obtain hardware address: %s\n",
1052 		 g_strerror (errno));
1053       close (fd);
1054       return NULL;
1055     }
1056 
1057   close (fd);
1058 
1059   if (hw_type && hw_type->hw_type != if_req.ifr_hwaddr.sa_family)
1060     hw_type = NULL;
1061 
1062   for (i = 0; !hw_type && i < G_N_ELEMENTS (hw_types); i++)
1063     if (hw_types [i].hw_type == if_req.ifr_hwaddr.sa_family)
1064       hw_type = &hw_types [i];
1065 
1066   if (!hw_type || !hw_type->hw_name)
1067     {
1068       g_warning (G_STRLOC ": no support for hardware type %d\n",
1069 		 if_req.ifr_hwaddr.sa_family);
1070       return NULL;
1071     }
1072 
1073   if (hw_addr && if_req.ifr_hwaddr.sa_data && hw_type->print_hw_addr)
1074     *hw_addr = hw_type->print_hw_addr ((guchar *) if_req.ifr_hwaddr.sa_data);
1075 
1076   return hw_type;
1077 
1078 #else /* !defined(SIOCGIFHWADDR) */
1079   return NULL;
1080 #endif
1081 }
1082 
1083 gboolean
netstatus_iface_get_is_loopback(NetstatusIface * iface)1084 netstatus_iface_get_is_loopback (NetstatusIface *iface)
1085 {
1086   struct HwType *hw_type;
1087 
1088   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), FALSE);
1089 
1090   if (!(hw_type = netstatus_iface_get_hw_details (iface, NULL)))
1091     return FALSE;
1092 
1093 #ifdef ARPHRD_LOOPBACK
1094   return hw_type->hw_type == ARPHRD_LOOPBACK ? TRUE : FALSE;
1095 #else
1096   return FALSE;
1097 #endif
1098 }
1099 
1100 gboolean
netstatus_iface_get_device_details(NetstatusIface * iface,const char ** hw_name,char ** hw_addr)1101 netstatus_iface_get_device_details (NetstatusIface  *iface,
1102 				    const char     **hw_name,
1103 				    char           **hw_addr)
1104 {
1105   struct HwType *hw_type;
1106 
1107   g_return_val_if_fail (NETSTATUS_IS_IFACE (iface), FALSE);
1108 
1109   if (hw_name)
1110     *hw_name = NULL;
1111   if (hw_addr)
1112     *hw_addr = NULL;
1113 
1114   if (!(hw_type = netstatus_iface_get_hw_details (iface, hw_addr)))
1115     return FALSE;
1116 
1117   g_assert (hw_type->hw_name != NULL);
1118 
1119   if (hw_name)
1120     *hw_name = _(hw_type->hw_name);
1121 
1122   return TRUE;
1123 }
1124 
1125 #if !defined(HAVE_SOCKADDR_SA_LEN)
1126 #define NETSTATUS_SA_LEN(saddr) (sizeof (struct sockaddr))
1127 #else
1128 #define NETSTATUS_SA_LEN(saddr) (MAX ((saddr)->sa_len, sizeof (struct sockaddr)))
1129 #endif /* HAVE_SOCKADDR_SA_LEN */
1130 
1131 /* Taken From R. Stevens Unix Network Programming Vol. 1.
1132  *
1133  * SIOCGIFCONF does not return an error on all systems if
1134  * the buffer is not large enough for all available
1135  * interfaces. This loop portably ensures that we get the
1136  * information for all interfaces.
1137  */
1138 static struct ifconf *
get_ifconf(int fd,GError ** error)1139 get_ifconf (int      fd,
1140 	    GError **error)
1141 {
1142   struct ifconf  if_conf;
1143   struct ifconf *retval;
1144   int            len, lastlen;
1145 
1146   lastlen = 0;
1147   len = 10 * sizeof (struct ifreq);
1148 
1149   while (TRUE)
1150     {
1151       if_conf.ifc_len = len;
1152       if_conf.ifc_buf = g_malloc0 (len);
1153 
1154       if (ioctl (fd, SIOCGIFCONF, &if_conf) < 0)
1155 	{
1156 	  if (errno != EINVAL || lastlen != 0)
1157 	    {
1158 	      g_free (if_conf.ifc_buf);
1159 
1160 	      if (error)
1161 		*error = g_error_new (NETSTATUS_ERROR,
1162 				      NETSTATUS_ERROR_IOCTL_IFCONF,
1163 				      _("SIOCGIFCONF error: %s"),
1164 				      g_strerror (errno));
1165 
1166 	      return NULL;
1167 	    }
1168 	}
1169       else
1170 	{
1171 	  if (if_conf.ifc_len == lastlen)
1172 	    break;
1173 	  lastlen = if_conf.ifc_len;
1174 	}
1175 
1176       g_free (if_conf.ifc_buf);
1177       if_conf.ifc_buf = NULL;
1178 
1179       len *= 2;
1180     }
1181 
1182   retval = g_new0 (struct ifconf, 1);
1183 
1184   retval->ifc_len = if_conf.ifc_len;
1185   retval->ifc_buf = if_conf.ifc_buf;
1186 
1187   return retval;
1188 }
1189 
1190 GList *
netstatus_list_interface_names(GError ** error)1191 netstatus_list_interface_names (GError **error)
1192 {
1193 
1194   struct ifconf *if_conf;
1195   GList         *interfaces;
1196   GList         *loopbacks;
1197   char          *p;
1198   int            fd;
1199 
1200   if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
1201     {
1202       if (error)
1203 	*error = g_error_new (NETSTATUS_ERROR,
1204 			      NETSTATUS_ERROR_SOCKET,
1205 			      _("Unable to open socket: %s"),
1206 			      g_strerror (errno));
1207       return NULL;
1208     }
1209 
1210   if ((if_conf = get_ifconf (fd, error)) == NULL)
1211     {
1212       close (fd);
1213       return NULL;
1214     }
1215 
1216   interfaces = NULL;
1217   loopbacks  = NULL;
1218 
1219   for (p = if_conf->ifc_buf; p < if_conf->ifc_buf + if_conf->ifc_len;)
1220     {
1221       struct ifreq *if_req = (struct ifreq *) p;
1222       gboolean      loopback = FALSE;
1223 
1224       p += sizeof (if_req->ifr_name) + NETSTATUS_SA_LEN (&if_req->ifr_addr);
1225 
1226       if (ioctl (fd, SIOCGIFFLAGS, if_req) < 0)
1227 	{
1228 	  if (error)
1229 	    *error = g_error_new (NETSTATUS_ERROR,
1230 				  NETSTATUS_ERROR_IOCTL_IFFLAGS,
1231 				  _("SIOCGIFFLAGS error: %s"),
1232 				  g_strerror (errno));
1233 	}
1234       else
1235 	{
1236 	  loopback = (if_req->ifr_flags & IFF_LOOPBACK);
1237 	}
1238 
1239       if (!loopback)
1240 	interfaces = netstatus_list_insert_unique (interfaces,
1241 						   g_strdup (if_req->ifr_name));
1242       else
1243 	loopbacks  = netstatus_list_insert_unique (loopbacks,
1244 						   g_strdup (if_req->ifr_name));
1245     }
1246 
1247   interfaces = g_list_concat (interfaces, loopbacks);
1248 
1249   g_free (if_conf->ifc_buf);
1250   g_free (if_conf);
1251   close (fd);
1252 
1253   if (!interfaces && error)
1254     *error = g_error_new (NETSTATUS_ERROR,
1255 			  NETSTATUS_ERROR_NO_INTERFACES,
1256 			  _("No network devices found"));
1257 
1258   return interfaces;
1259 }
1260