1
/* NetworkManager -- Network link manager
3
* Dan Williams <dcbw@redhat.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program 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
13
* GNU General Public License for more details.
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 02111-1307, USA.
19
* (C) Copyright 2004 Red Hat, Inc.
24
#include <dbus/dbus-glib.h>
25
#include <hal/libhal.h>
27
#include <linux/netlink.h>
28
#include <linux/rtnetlink.h>
30
#include "NetworkManager.h"
31
#include "NetworkManagerDevice.h"
32
#include "NetworkManagerUtils.h"
34
extern gboolean debug;
36
static gboolean mii_get_link (NMDevice *dev);
37
static void nm_device_link_detection_init (NMDevice *dev);
40
* nm_device_is_wireless
42
* Test whether a given device is a wireless one or not.
45
static gboolean nm_device_is_wireless (NMDevice *dev)
51
g_return_val_if_fail (dev != NULL, FALSE);
53
iwlib_socket = iw_sockets_open ();
54
error = iw_get_stats (iwlib_socket, nm_device_get_iface (dev), &stats, NULL, FALSE);
61
* nm_device_supports_wireless_scan
63
* Test whether a given device is a wireless one or not.
66
static gboolean nm_device_supports_wireless_scan (NMDevice *dev)
71
gboolean can_scan = TRUE;
72
wireless_scan_head scan_data;
74
g_return_val_if_fail (dev != NULL, FALSE);
76
iwlib_socket = iw_sockets_open ();
77
error = iw_scan (iwlib_socket, nm_device_get_iface (dev), WIRELESS_EXT, &scan_data);
78
nm_dispose_scan_results (scan_data.result);
79
if ((error == -1) && (errno == EOPNOTSUPP))
87
* nm_get_device_by_udi
89
* Search through the device list for a device with a given UDI.
91
* NOTE: the caller MUST hold the device list mutex already to make
92
* this routine thread-safe.
95
NMDevice *nm_get_device_by_udi (NMData *data, const char *udi)
100
g_return_val_if_fail (data != NULL, NULL);
101
g_return_val_if_fail (udi != NULL, NULL);
103
element = data->dev_list;
106
dev = (NMDevice *)(element->data);
109
if (nm_null_safe_strcmp (nm_device_get_udi (dev), udi) == 0)
113
element = g_slist_next (element);
121
* nm_get_device_by_iface
123
* Search through the device list for a device with a given iface.
125
* NOTE: the caller MUST hold the device list mutex already to make
126
* this routine thread-safe.
129
NMDevice *nm_get_device_by_iface (NMData *data, const char *iface)
131
NMDevice *iter_dev = NULL;
132
NMDevice *found_dev = NULL;
135
g_return_val_if_fail (data != NULL, NULL);
136
g_return_val_if_fail (iface != NULL, NULL);
138
element = data->dev_list;
141
iter_dev = (NMDevice *)(element->data);
144
if (nm_null_safe_strcmp (nm_device_get_iface (iter_dev), iface) == 0)
146
found_dev = iter_dev;
151
element = g_slist_next (element);
158
/*****************************************************************************/
159
/* NMDevice object routines */
160
/*****************************************************************************/
162
typedef struct NMDeviceWirelessOptions
165
gboolean supports_wireless_scan;
166
GMutex *ap_list_mutex;
168
} NMDeviceWirelessOptions;
170
typedef struct NMDeviceWiredOptions
173
} NMDeviceWiredOptions;
175
typedef union NMDeviceOptions
177
NMDeviceWirelessOptions wireless;
178
NMDeviceWiredOptions wired;
182
* NetworkManager device structure
189
NMIfaceType iface_type;
190
gboolean link_active;
191
NMDeviceOptions dev_options;
198
* Creates and initializes the structure representation of an NLM device.
201
NMDevice *nm_device_new (const char *iface)
205
g_return_val_if_fail (iface != NULL, NULL);
207
dev = g_new0 (NMDevice, 1);
210
NM_DEBUG_PRINT("nm_device_new() could not allocate a new device... Not enough memory?\n");
215
dev->iface = g_strdup (iface);
216
dev->iface_type = nm_device_is_wireless (dev) ?
217
NM_IFACE_TYPE_WIRELESS_ETHERNET : NM_IFACE_TYPE_WIRED_ETHERNET;
219
if (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET)
221
dev->dev_options.wireless.supports_wireless_scan = nm_device_supports_wireless_scan (dev);
223
dev->dev_options.wireless.ap_list_mutex = g_mutex_new();
224
if (!dev->dev_options.wireless.ap_list_mutex)
231
/* Have to bring the device up before checking link status. */
232
if (!nm_device_is_up (dev))
233
nm_device_bring_up (dev);
234
nm_device_update_link_active (dev, TRUE);
241
* Refcounting functions
243
void nm_device_ref (NMDevice *dev)
245
g_return_if_fail (dev != NULL);
250
void nm_device_unref (NMDevice *dev)
252
g_return_if_fail (dev != NULL);
255
if (dev->refcount == 0)
257
nm_device_ap_list_clear (dev);
258
dev->dev_options.wireless.ap_list = NULL;
262
if (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET)
264
g_free (dev->dev_options.wireless.cur_essid);
265
g_mutex_free (dev->dev_options.wireless.ap_list_mutex);
275
* Get/set functions for UDI
277
char * nm_device_get_udi (NMDevice *dev)
279
g_return_val_if_fail (dev != NULL, NULL);
284
void nm_device_set_udi (NMDevice *dev, const char *udi)
286
g_return_if_fail (dev != NULL);
287
g_return_if_fail (udi != NULL);
292
dev->udi = g_strdup (udi);
297
* Get/set functions for iface
299
char * nm_device_get_iface (NMDevice *dev)
301
g_return_val_if_fail (dev != NULL, NULL);
308
* Get/set functions for iface_type
310
guint nm_device_get_iface_type (NMDevice *dev)
312
g_return_val_if_fail (dev != NULL, NM_IFACE_TYPE_DONT_KNOW);
314
return (dev->iface_type);
319
* Get/set functions for link_active
321
gboolean nm_device_get_link_active (NMDevice *dev)
323
g_return_val_if_fail (dev != NULL, FALSE);
325
return (dev->link_active);
328
void nm_device_set_link_active (NMDevice *dev, const gboolean link_active)
330
g_return_if_fail (dev != NULL);
332
dev->link_active = link_active;
337
* Get function for supports_wireless_scan
339
gboolean nm_device_get_supports_wireless_scan (NMDevice *dev)
341
g_return_val_if_fail (dev != NULL, FALSE);
343
return (dev->dev_options.wireless.supports_wireless_scan);
348
* nm_device_update_link_active
350
* Updates the link state for a particular device.
353
gboolean nm_device_update_link_active (NMDevice *dev, gboolean check_mii)
355
gboolean link_active = FALSE;
357
g_return_val_if_fail (dev != NULL, FALSE);
360
* For wireless cards, the best indicator of a "link" at this time
361
* seems to be whether the card has a valid access point MAC address.
362
* Is there a better way?
365
switch (nm_device_get_iface_type (dev))
367
case NM_IFACE_TYPE_WIRELESS_ETHERNET:
372
iwlib_socket = iw_sockets_open ();
373
if (iw_get_ext (iwlib_socket, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0)
375
struct ether_addr invalid_addr1;
376
struct ether_addr invalid_addr2;
377
struct ether_addr invalid_addr3;
378
struct ether_addr ap_addr;
380
/* Compare the AP address the card has with invalid ethernet MAC addresses.
382
memcpy (&ap_addr, &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr));
383
memset (&invalid_addr1, 0xFF, sizeof(struct ether_addr));
384
memset (&invalid_addr2, 0x00, sizeof(struct ether_addr));
385
memset (&invalid_addr2, 0x44, sizeof(struct ether_addr));
386
if ( (memcmp(&ap_addr, &invalid_addr1, sizeof(struct ether_addr)) != 0)
387
&& (memcmp(&ap_addr, &invalid_addr2, sizeof(struct ether_addr)) != 0)
388
&& (memcmp(&ap_addr, &invalid_addr3, sizeof(struct ether_addr)) != 0))
391
close (iwlib_socket);
395
case NM_IFACE_TYPE_WIRED_ETHERNET:
398
link_active = mii_get_link (dev);
400
if (hal_device_property_exists (nm_get_global_data()->hal_ctx, nm_device_get_udi (dev), "net.ethernet.link"))
401
link_active = hal_device_get_property_bool (nm_get_global_data()->hal_ctx, nm_device_get_udi (dev), "net.ethernet.link");
406
link_active = nm_device_get_link_active (dev); /* Can't get link info for this device, so don't change link status */
410
/* Update device link status and global state variable if the status changed */
411
if (link_active != nm_device_get_link_active (dev))
413
nm_device_set_link_active (dev, link_active);
414
nm_data_set_state_modified (nm_get_global_data(), TRUE);
416
return (link_active);
421
* nm_device_get_essid
423
* If a device is wireless, return the essid that it is attempting
426
* Returns: allocated string containing essid. Must be freed by caller.
429
char * nm_device_get_essid (NMDevice *dev)
434
char essid[IW_ESSID_MAX_SIZE + 1];
436
g_return_val_if_fail (dev != NULL, NULL);
437
g_return_val_if_fail (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET, NULL);
439
iwlib_socket = iw_sockets_open ();
440
if (iwlib_socket >= 0)
442
wreq.u.essid.pointer = (caddr_t) essid;
443
wreq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
444
wreq.u.essid.flags = 0;
445
err = iw_get_ext (iwlib_socket, nm_device_get_iface (dev), SIOCGIWESSID, &wreq);
448
if (dev->dev_options.wireless.cur_essid)
449
g_free (dev->dev_options.wireless.cur_essid);
450
dev->dev_options.wireless.cur_essid = g_strdup (essid);
453
NM_DEBUG_PRINT_2 ("nm_device_get_essid(): error setting ESSID for device %s. errno = %d\n", nm_device_get_iface (dev), errno);
455
close (iwlib_socket);
458
return (dev->dev_options.wireless.cur_essid);
463
* nm_device_set_essid
465
* If a device is wireless, set the essid that it should use.
467
void nm_device_set_essid (NMDevice *dev, const char *essid)
472
unsigned char safe_essid[IW_ESSID_MAX_SIZE + 1] = "\0";
474
g_return_if_fail (dev != NULL);
475
g_return_if_fail (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET);
477
/* Make sure the essid we get passed is a valid size */
479
safe_essid[0] = '\0';
482
strncpy (safe_essid, essid, IW_ESSID_MAX_SIZE);
483
safe_essid[IW_ESSID_MAX_SIZE] = '\0';
486
iwlib_socket = iw_sockets_open ();
487
if (iwlib_socket >= 0)
489
wreq.u.essid.pointer = (caddr_t) safe_essid;
490
wreq.u.essid.length = strlen (safe_essid) + 1;
491
wreq.u.essid.flags = 1; /* Enable essid on card */
493
err = iw_set_ext (iwlib_socket, nm_device_get_iface (dev), SIOCSIWESSID, &wreq);
495
NM_DEBUG_PRINT_2 ("nm_device_set_essid(): error setting ESSID for device %s. errno = %d\n", nm_device_get_iface (dev), errno);
497
close (iwlib_socket);
503
* nm_device_set_wep_key
505
* If a device is wireless, set the WEP key that it should use.
507
* wep_key: WEP key to use, or NULL or "" to disable WEP
509
void nm_device_set_wep_key (NMDevice *dev, const char *wep_key)
515
unsigned char safe_key[IW_ENCODING_TOKEN_MAX];
516
gboolean set_key = FALSE;
520
g_return_if_fail (dev != NULL);
521
g_return_if_fail (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET);
523
/* Make sure the essid we get passed is a valid size */
528
strncpy (safe_key, wep_key, IW_ENCODING_TOKEN_MAX);
529
safe_key[IW_ENCODING_TOKEN_MAX] = '\0';
532
iwlib_socket = iw_sockets_open ();
533
if (iwlib_socket >= 0)
535
wreq.u.data.pointer = (caddr_t) NULL;
536
wreq.u.data.flags = IW_ENCODE_ENABLED;
537
wreq.u.data.length = 0;
539
if (strlen (safe_key) == 0)
541
wreq.u.data.flags = IW_ENCODE_DISABLED | IW_ENCODE_NOKEY; /* Disable WEP */
546
keylen = iw_in_key_full(iwlib_socket, nm_device_get_iface (dev), "", safe_key, &wreq.u.data.flags);
549
wreq.u.data.pointer = (caddr_t) safe_key;
550
wreq.u.data.length = keylen;
557
err = iw_set_ext (iwlib_socket, nm_device_get_iface (dev), SIOCSIWENCODE, &wreq);
559
NM_DEBUG_PRINT_2 ("nm_device_set_wep_key(): error setting key for device %s. errno = %d\n", nm_device_get_iface (dev), errno);
562
close (iwlib_socket);
568
* nm_device_set_up_down
570
* Set the up flag on the device on or off
573
static void nm_device_set_up_down (NMDevice *dev, gboolean up)
578
guint32 flags = up ? IFF_UP : ~IFF_UP;
580
g_return_if_fail (dev != NULL);
582
iface_fd = nm_get_network_control_socket ();
586
/* Get flags already there */
587
strcpy (ifr.ifr_name, nm_device_get_iface (dev));
588
err = ioctl (iface_fd, SIOCGIFFLAGS, &ifr);
591
/* If the interface doesn't have those flags already,
594
if ((ifr.ifr_flags^flags) & IFF_UP)
596
ifr.ifr_flags &= ~IFF_UP;
597
ifr.ifr_flags |= IFF_UP & flags;
598
err = ioctl (iface_fd, SIOCSIFFLAGS, &ifr);
600
NM_DEBUG_PRINT_3 ("nm_device_set_up_down() could not bring device %s %s. errno = %d\n", nm_device_get_iface (dev), (up ? "up" : "down"), errno );
604
NM_DEBUG_PRINT_2 ("nm_device_set_up_down() could not get flags for device %s. errno = %d\n", nm_device_get_iface (dev), errno );
611
* Interface state functions: bring up, down, check
614
void nm_device_bring_up (NMDevice *dev)
616
g_return_if_fail (dev != NULL);
618
nm_device_set_up_down (dev, TRUE);
621
void nm_device_bring_down (NMDevice *dev)
625
g_return_if_fail (dev != NULL);
627
nm_device_set_up_down (dev, FALSE);
630
gboolean nm_device_is_up (NMDevice *dev)
636
g_return_if_fail (dev != NULL);
638
iface_fd = nm_get_network_control_socket ();
642
/* Get device's flags */
643
strcpy (ifr.ifr_name, nm_device_get_iface (dev));
644
err = ioctl (iface_fd, SIOCGIFFLAGS, &ifr);
647
return (!((ifr.ifr_flags^IFF_UP) & IFF_UP));
649
NM_DEBUG_PRINT_2 ("nm_device_is_up() could not get flags for device %s. errno = %d\n", nm_device_get_iface (dev), errno );
655
* nm_device_ap_list_add
657
* Add an access point to the devices internal AP list.
660
void nm_device_ap_list_add (NMDevice *dev, NMAccessPoint *ap)
662
g_return_if_fail (dev != NULL);
663
g_return_if_fail (ap != NULL);
664
g_return_if_fail (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET);
666
if (nm_try_acquire_mutex (dev->dev_options.wireless.ap_list_mutex, __FUNCTION__))
669
dev->dev_options.wireless.ap_list = g_slist_append (dev->dev_options.wireless.ap_list, ap);
671
nm_unlock_mutex (dev->dev_options.wireless.ap_list_mutex, __FUNCTION__);
677
* nm_device_ap_list_clear
679
* Clears out the device's internal list of available access points.
682
void nm_device_ap_list_clear (NMDevice *dev)
686
g_return_if_fail (dev != NULL);
687
g_return_if_fail (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET);
689
if (!dev->dev_options.wireless.ap_list)
692
if (nm_try_acquire_mutex (dev->dev_options.wireless.ap_list_mutex, __FUNCTION__))
694
element = dev->dev_options.wireless.ap_list;
699
nm_ap_unref (element->data);
700
element->data = NULL;
703
element = g_slist_next (element);
706
g_slist_free (dev->dev_options.wireless.ap_list);
707
dev->dev_options.wireless.ap_list = NULL;
709
nm_unlock_mutex (dev->dev_options.wireless.ap_list_mutex, __FUNCTION__);
715
* nm_device_ap_list_get_copy
717
* Copy the list of ESSIDs
720
NMAccessPoint *nm_device_ap_list_get_ap (NMDevice *dev, int index)
723
NMAccessPoint *ap = NULL;
725
g_return_val_if_fail (dev != NULL, NULL);
726
g_return_val_if_fail (dev->iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET, NULL);
728
if (!dev->dev_options.wireless.ap_list)
731
if (nm_try_acquire_mutex (dev->dev_options.wireless.ap_list_mutex, __FUNCTION__))
735
element = dev->dev_options.wireless.ap_list;
738
if (element->data && (index == i))
740
ap = (NMAccessPoint *)(element->data);
745
element = g_slist_next (element);
747
nm_unlock_mutex (dev->dev_options.wireless.ap_list_mutex, __FUNCTION__);
754
/****************************************/
755
/* Code ripped from HAL */
756
/* minor modifications made for */
757
/* integration with NLM */
758
/****************************************/
760
/** Read a word from the MII transceiver management registers
762
* @param iface Which interface
763
* @param location Which register
764
* @return Word that is read
766
static guint16 mdio_read (int sockfd, struct ifreq *ifr, int location, gboolean new_ioctl_nums)
768
guint16 *data = (guint16 *) &(ifr->ifr_data);
771
if (ioctl (sockfd, new_ioctl_nums ? 0x8948 : SIOCDEVPRIVATE + 1, ifr) < 0)
773
NM_DEBUG_PRINT_2("SIOCGMIIREG on %s failed: %s\n", ifr->ifr_name, strerror (errno));
779
static gboolean mii_get_link (NMDevice *dev)
783
gboolean new_ioctl_nums;
785
gboolean link_active = FALSE;
787
sockfd = socket (AF_INET, SOCK_DGRAM, 0);
790
NM_DEBUG_PRINT_2("cannot open socket on interface %s; errno=%d", nm_device_get_iface (dev), errno);
794
snprintf (ifr.ifr_name, IFNAMSIZ, nm_device_get_iface (dev));
795
if (ioctl (sockfd, 0x8947, &ifr) >= 0)
796
new_ioctl_nums = TRUE;
797
else if (ioctl (sockfd, SIOCDEVPRIVATE, &ifr) >= 0)
798
new_ioctl_nums = FALSE;
801
NM_DEBUG_PRINT_2("SIOCGMIIPHY on %s failed: %s", ifr.ifr_name, strerror (errno));
806
/* Refer to http://www.scyld.com/diag/mii-status.html for
807
* the full explanation of the numbers
809
* 0x8000 Capable of 100baseT4.
810
* 0x7800 Capable of 10/100 HD/FD (most common).
811
* 0x0040 Preamble suppression permitted.
812
* 0x0020 Autonegotiation complete.
813
* 0x0010 Remote fault.
814
* 0x0008 Capable of Autonegotiation.
815
* 0x0004 Link established ("sticky"* on link failure)
816
* 0x0002 Jabber detected ("sticky"* on transmit jabber)
817
* 0x0001 Extended MII register exist.
821
/* We have to read it twice to clear any "sticky" bits */
822
status_word = mdio_read (sockfd, &ifr, 1, new_ioctl_nums);
823
status_word = mdio_read (sockfd, &ifr, 1, new_ioctl_nums);
825
if ((status_word & 0x0016) == 0x0004)
832
return (link_active);
835
/****************************************/
836
/* End Code ripped from HAL */
837
/****************************************/