/* NetworkManager -- Network link manager * * Dan Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * (C) Copyright 2004 Red Hat, Inc. */ #include #include #include #include #include #include #include #include "NetworkManager.h" #include "NetworkManagerMain.h" #include "NetworkManagerDevice.h" #include "NetworkManagerDevicePrivate.h" #include "NetworkManagerUtils.h" #include "NetworkManagerDbus.h" #include "NetworkManagerWireless.h" #include "NetworkManagerPolicy.h" #include "NetworkManagerAPList.h" #include "NetworkManagerSystem.h" #include "NetworkManagerDHCP.h" /* Local static prototypes */ static gpointer nm_device_worker (gpointer user_data); static gboolean nm_device_activate (gpointer user_data); static gboolean nm_device_activation_configure_ip (NMDevice *dev, gboolean do_only_autoip); static gboolean nm_device_wireless_scan (gpointer user_data); static gboolean supports_mii_carrier_detect (NMDevice *dev); static gboolean supports_ethtool_carrier_detect (NMDevice *dev); typedef struct { NMDevice *dev; struct wireless_scan_head scan_head; } NMWirelessScanResults; /******************************************************/ /******************************************************/ /* * nm_device_test_wireless_extensions * * Test whether a given device is a wireless one or not. * */ static gboolean nm_device_test_wireless_extensions (NMDevice *dev) { int sk; int err; char ioctl_buf[64]; g_return_val_if_fail (dev != NULL, FALSE); /* We obviously cannot probe test devices (since they don't * actually exist in hardware). */ if (dev->test_device) return (FALSE); ioctl_buf[63] = 0; strncpy(ioctl_buf, nm_device_get_iface(dev), 63); sk = iw_sockets_open (); err = ioctl(sk, SIOCGIWNAME, ioctl_buf); close (sk); return (err == 0); } /* * nm_device_supports_wireless_scan * * Test whether a given device is a wireless one or not. * */ static gboolean nm_device_supports_wireless_scan (NMDevice *dev) { int sk; int err; gboolean can_scan = TRUE; wireless_scan_head scan_data; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->type == DEVICE_TYPE_WIRELESS_ETHERNET, FALSE); /* A test wireless device can always scan (we generate fake scan data for it) */ if (dev->test_device) return (TRUE); sk = iw_sockets_open (); err = iw_scan (sk, (char *)nm_device_get_iface (dev), WIRELESS_EXT, &scan_data); nm_dispose_scan_results (scan_data.result); if ((err == -1) && (errno == EOPNOTSUPP)) can_scan = FALSE; close (sk); return (can_scan); } /* * nm_get_device_by_udi * * Search through the device list for a device with a given UDI. * * NOTE: the caller MUST hold the device list mutex already to make * this routine thread-safe. * */ NMDevice *nm_get_device_by_udi (NMData *data, const char *udi) { NMDevice *dev = NULL; GSList *elt; g_return_val_if_fail (data != NULL, NULL); g_return_val_if_fail (udi != NULL, NULL); for (elt = data->dev_list; elt; elt = g_slist_next (elt)) { dev = (NMDevice *)(elt->data); if (dev) { if (nm_null_safe_strcmp (nm_device_get_udi (dev), udi) == 0) break; } } return (dev); } /* * nm_get_device_by_iface * * Search through the device list for a device with a given iface. * * NOTE: the caller MUST hold the device list mutex already to make * this routine thread-safe. * */ NMDevice *nm_get_device_by_iface (NMData *data, const char *iface) { NMDevice *iter_dev = NULL; NMDevice *found_dev = NULL; GSList *elt; g_return_val_if_fail (data != NULL, NULL); g_return_val_if_fail (iface != NULL, NULL); for (elt = data->dev_list; elt; elt = g_slist_next (elt)) { iter_dev = (NMDevice *)(elt->data); if (iter_dev) { if (nm_null_safe_strcmp (nm_device_get_iface (iter_dev), iface) == 0) { found_dev = iter_dev; break; } } } return (found_dev); } /*****************************************************************************/ /* NMDevice object routines */ /*****************************************************************************/ /* * nm_device_new * * Creates and initializes the structure representation of an NM device. For test * devices, a device type other than DEVICE_TYPE_DONT_KNOW must be specified, this * argument is ignored for real hardware devices since they are auto-probed. * */ NMDevice *nm_device_new (const char *iface, const char *udi, gboolean test_dev, NMDeviceType test_dev_type, NMData *app_data) { NMDevice *dev; GError *error = NULL; g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (strlen (iface) > 0, NULL); g_return_val_if_fail (app_data != NULL, NULL); /* Test devices must have a valid type specified */ if (test_dev && !(test_dev_type != DEVICE_TYPE_DONT_KNOW)) return (NULL); /* Another check to make sure we don't create a test device unless * test devices were enabled on the command line. */ if (!app_data->enable_test_devices && test_dev) { syslog (LOG_ERR, "nm_device_new(): attempt to create a test device, but test devices were not enabled" " on the command line. Will not create the device.\n"); return (NULL); } dev = g_malloc0 (sizeof (NMDevice)); if (!dev) { syslog (LOG_ERR, "nm_device_new() could not allocate a new device... Not enough memory?"); return (NULL); } dev->refcount = 2; /* 1 for starters, and another 1 for the worker thread */ dev->app_data = app_data; dev->iface = g_strdup (iface); dev->test_device = test_dev; nm_device_set_udi (dev, udi); /* Real hardware devices are probed for their type, test devices must have * their type specified. */ if (test_dev) dev->type = test_dev_type; else dev->type = nm_device_test_wireless_extensions (dev) ? DEVICE_TYPE_WIRELESS_ETHERNET : DEVICE_TYPE_WIRED_ETHERNET; /* Device thread's main loop */ dev->context = g_main_context_new (); dev->loop = g_main_loop_new (dev->context, FALSE); if (!dev->context || !dev->loop) goto err; /* Have to bring the device up before checking link status and other stuff */ nm_device_bring_up (dev); g_usleep (G_USEC_PER_SEC); dev->driver_support_level = nm_get_driver_support_level (dev->app_data->hal_ctx, dev); /* Initialize wireless-specific options */ if (nm_device_is_wireless (dev)) { int sk; NMDeviceWirelessOptions *opts = &(dev->options.wireless); nm_device_set_mode (dev, NETWORK_MODE_INFRA); opts->scan_interval = 20; opts->scan_mutex = g_mutex_new (); opts->best_ap_mutex = g_mutex_new (); opts->ap_list = nm_ap_list_new (NETWORK_TYPE_DEVICE); if (!opts->scan_mutex || !opts->best_ap_mutex || !opts->ap_list) goto err; nm_register_mutex_desc (opts->scan_mutex, "Scan Mutex"); nm_register_mutex_desc (opts->best_ap_mutex, "Best AP Mutex"); opts->supports_wireless_scan = nm_device_supports_wireless_scan (dev); if ((sk = iw_sockets_open ()) >= 0) { iwrange range; if (iw_get_range_info (sk, nm_device_get_iface (dev), &range) >= 0) { int i; opts->max_qual.qual = range.max_qual.qual; opts->max_qual.level = range.max_qual.level; opts->max_qual.noise = range.max_qual.noise; opts->max_qual.updated = range.max_qual.updated; opts->avg_qual.qual = range.avg_qual.qual; opts->avg_qual.level = range.avg_qual.level; opts->avg_qual.noise = range.avg_qual.noise; opts->avg_qual.updated = range.avg_qual.updated; opts->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); for (i = 0; i < opts->num_freqs; i++) opts->freqs[i] = iw_freq2float (&(range.freq[i])); } close (sk); } } else if (nm_device_is_wired (dev)) { if (supports_ethtool_carrier_detect (dev) || supports_mii_carrier_detect (dev)) dev->options.wired.has_carrier_detect = TRUE; } if (nm_device_get_driver_support_level (dev) != NM_DRIVER_UNSUPPORTED) { nm_device_update_link_active (dev); nm_device_update_ip4_address (dev); nm_device_update_hw_address (dev); /* Grab IP config data for this device from the system configuration files */ nm_system_device_update_config_info (dev); } if (!g_thread_create (nm_device_worker, dev, FALSE, &error)) { syslog (LOG_CRIT, "nm_device_new (): could not create device worker thread. (glib said: '%s')", error->message); g_error_free (error); goto err; } /* Block until our device thread has actually had a chance to start. */ syslog (LOG_ERR, "nm_device_new(): waiting for device's worker thread to start.\n"); while (dev->worker_started == FALSE) g_usleep (G_USEC_PER_SEC / 2); syslog (LOG_ERR, "nm_device_new(): device's worker thread started, continuing.\n"); return (dev); err: /* Initial refcount is 2 */ nm_device_unref (dev); nm_device_unref (dev); return NULL; } /* * Refcounting functions */ void nm_device_ref (NMDevice *dev) { g_return_if_fail (dev != NULL); dev->refcount++; } /* * nm_device_unref * * Decreases the refcount on a device by 1, and if the refcount reaches 0, * deallocates memory used by the device. * * Returns: FALSE if device was not deallocated * TRUE if device was deallocated */ gboolean nm_device_unref (NMDevice *dev) { gboolean deleted = FALSE; g_return_val_if_fail (dev != NULL, TRUE); dev->refcount--; if (dev->refcount <= 0) { if (dev->loop) g_main_loop_quit (dev->loop); while (dev->worker_done == FALSE) g_usleep (300); if (nm_device_is_wireless (dev)) { nm_device_ap_list_clear (dev); g_mutex_free (dev->options.wireless.scan_mutex); if (dev->options.wireless.ap_list) nm_ap_list_unref (dev->options.wireless.ap_list); if (dev->options.wireless.best_ap) nm_ap_unref (dev->options.wireless.best_ap); g_mutex_free (dev->options.wireless.best_ap_mutex); } /* Get rid of DHCP state data */ if (dev->dhcp_iface) { dhcp_interface_free (dev->dhcp_iface); dev->dhcp_iface = NULL; } g_free (dev->udi); g_free (dev->iface); memset (dev, 0, sizeof (NMDevice)); g_free (dev); deleted = TRUE; } return deleted; } /* * nm_device_worker * * Main thread of the device. * */ static gpointer nm_device_worker (gpointer user_data) { NMDevice *dev = (NMDevice *)user_data; if (!dev) { syslog (LOG_CRIT, "nm_device_worker(): received NULL device object, NetworkManager cannot continue.\n"); exit (1); } dev->worker_started = TRUE; /* Do an initial wireless scan */ if (nm_device_is_wireless (dev)) { GSource *source = g_idle_source_new (); guint source_id = 0; g_source_set_callback (source, nm_device_wireless_scan, dev, NULL); source_id = g_source_attach (source, dev->context); g_source_unref (source); } g_main_loop_run (dev->loop); /* Remove any DHCP timeouts that might have been running */ if (nm_device_config_get_use_dhcp (dev)) nm_device_dhcp_remove_timeouts (dev); g_main_loop_unref (dev->loop); g_main_context_unref (dev->context); dev->loop = NULL; dev->context = NULL; dev->worker_done = TRUE; nm_device_unref (dev); return NULL; } void nm_device_worker_thread_stop (NMDevice *dev) { g_return_if_fail (dev != NULL); g_main_loop_quit (dev->loop); while (dev->worker_done == FALSE) g_usleep (G_USEC_PER_SEC / 2); } /* * nm_device_get_app_data * */ NMData *nm_device_get_app_data (const NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->app_data); } /* * Get/Set for "removed" flag */ gboolean nm_device_get_removed (const NMDevice *dev) { g_return_val_if_fail (dev != NULL, TRUE); return (dev->removed); } void nm_device_set_removed (NMDevice *dev, const gboolean removed) { g_return_if_fail (dev != NULL); dev->removed = removed; } /* * nm_device_open_sock * * Get a control socket for network operations. * */ int nm_device_open_sock (void) { int fd; /* Try to grab a control socket */ fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd >= 0) return (fd); fd = socket(PF_PACKET, SOCK_DGRAM, 0); if (fd >= 0) return (fd); fd = socket(PF_INET6, SOCK_DGRAM, 0); if (fd >= 0) return (fd); syslog (LOG_ERR, "nm_device_open_sock () could not get network control socket."); return (-1); } /* * Return the amount of time we should wait for the device * to get a link, based on the # of frequencies it has to * scan. */ gint nm_device_get_association_pause_value (NMDevice *dev) { g_return_val_if_fail (dev != NULL, -1); g_return_val_if_fail (nm_device_is_wireless (dev), -1); /* If the card supports more than 14 channels, we should probably wait * around 10s so it can scan them all. After we set the ESSID on the card, the card * has to scan all channels to find our requested AP (which can take a long time * if it is an A/B/G chipset like the Atheros 5212, for example). */ if (dev->options.wireless.num_freqs > 14) return 8; else return 5; } /* * Get/set functions for UDI */ char * nm_device_get_udi (NMDevice *dev) { g_return_val_if_fail (dev != NULL, NULL); return (dev->udi); } void nm_device_set_udi (NMDevice *dev, const char *udi) { g_return_if_fail (dev != NULL); g_return_if_fail (udi != NULL); if (dev->udi) g_free (dev->udi); dev->udi = g_strdup (udi); } /* * Get/set functions for iface */ const char * nm_device_get_iface (NMDevice *dev) { g_return_val_if_fail (dev != NULL, NULL); return (dev->iface); } /* * Get/set functions for type */ guint nm_device_get_type (NMDevice *dev) { g_return_val_if_fail (dev != NULL, DEVICE_TYPE_DONT_KNOW); return (dev->type); } gboolean nm_device_is_wireless (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->type == DEVICE_TYPE_WIRELESS_ETHERNET); } gboolean nm_device_is_wired (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->type == DEVICE_TYPE_WIRED_ETHERNET); } /* * Accessor for driver support level */ NMDriverSupportLevel nm_device_get_driver_support_level (NMDevice *dev) { g_return_val_if_fail (dev != NULL, NM_DRIVER_UNSUPPORTED); return (dev->driver_support_level); } /* * Get/set functions for link_active */ gboolean nm_device_get_link_active (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->link_active); } void nm_device_set_link_active (NMDevice *dev, const gboolean link_active) { g_return_if_fail (dev != NULL); dev->link_active = link_active; } /* * Get/set functions for now_scanning */ gboolean nm_device_get_now_scanning (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wireless (dev), FALSE); return (dev->options.wireless.now_scanning); } void nm_device_set_now_scanning (NMDevice *dev, const gboolean now_scanning) { gboolean old_val; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); old_val = nm_device_get_now_scanning (dev); dev->options.wireless.now_scanning = now_scanning; if (old_val != now_scanning) nm_dbus_schedule_device_status_change (dev, DEVICE_STATUS_CHANGE); } /* * Get function for supports_wireless_scan */ gboolean nm_device_get_supports_wireless_scan (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); if (!nm_device_is_wireless (dev)) return (FALSE); return (dev->options.wireless.supports_wireless_scan); } /* * nm_device_get_supports_carrier_detect */ gboolean nm_device_get_supports_carrier_detect (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); if (!nm_device_is_wired (dev)) return (FALSE); return (dev->options.wired.has_carrier_detect); } /* * nm_device_wireless_is_associated * * Figure out whether or not we're associated to an access point */ static gboolean nm_device_wireless_is_associated (NMDevice *dev) { struct iwreq wrq; int sk; gboolean associated = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); /* Test devices have their link state set through DBUS */ if (dev->test_device) return (nm_device_get_link_active (dev)); if ((sk = iw_sockets_open ()) < 0) return (FALSE); /* Some cards, for example ipw2x00 cards, can short-circuit the MAC * address check using this check on IWNAME. Its faster. */ if (iw_get_ext (sk, nm_device_get_iface (dev), SIOCGIWNAME, &wrq) >= 0) { if (!strcmp(wrq.u.name, "unassociated")) { associated = FALSE; goto out; } } if (!associated) { /* * For all other wireless cards, the best indicator of a "link" at this time * seems to be whether the card has a valid access point MAC address. * Is there a better way? Some cards don't work too well with this check, ie * Lucent WaveLAN. */ if (iw_get_ext (sk, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0) if (nm_ethernet_address_is_valid ((struct ether_addr *)(&(wrq.u.ap_addr.sa_data)))) associated = TRUE; } out: close (sk); return (associated); } /* * nm_device_wireless_link_active * * Gets the link state of a wireless device * */ static gboolean nm_device_wireless_link_active (NMDevice *dev) { gboolean link = FALSE; NMAccessPoint *best_ap; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); /* Test devices have their link state set through DBUS */ if (dev->test_device) return (nm_device_get_link_active (dev)); if (!nm_device_wireless_is_associated (dev)) return (FALSE); /* If we don't have a "best" ap, we can't logically have a valid link * that we want to use. */ if ((best_ap = nm_device_get_best_ap (dev))) { if (!nm_device_need_ap_switch (dev)) link = TRUE; nm_ap_unref (best_ap); } return (link); } /* * nm_device_wired_link_active * * * */ static gboolean nm_device_wired_link_active (NMDevice *dev) { gboolean link = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wired (dev) == TRUE, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); /* Test devices have their link state set through DBUS */ if (dev->test_device) return (nm_device_get_link_active (dev)); /* We say that non-carrier-detect devices always have a link, because * they never get auto-selected by NM. User has to force them on us, * so we just hope the user knows whether or not the cable's plugged in. */ if (dev->options.wired.has_carrier_detect != TRUE) link = TRUE; else { /* Device has carrier detect, yay! */ if (hal_device_property_exists (dev->app_data->hal_ctx, nm_device_get_udi (dev), "net.80203.link")) link = hal_device_get_property_bool (dev->app_data->hal_ctx, nm_device_get_udi (dev), "net.80203.link"); } return (link); } /* * nm_device_update_link_active * * Updates the link state for a particular device. * */ void nm_device_update_link_active (NMDevice *dev) { gboolean link = FALSE; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); switch (nm_device_get_type (dev)) { case DEVICE_TYPE_WIRELESS_ETHERNET: link = nm_device_wireless_link_active (dev); /* Update our current signal strength too */ nm_device_update_signal_strength (dev); break; case DEVICE_TYPE_WIRED_ETHERNET: link = nm_device_wired_link_active (dev); break; default: link = nm_device_get_link_active (dev); /* Can't get link info for this device, so don't change link status */ break; } /* Update device link status and global state variable if the status changed */ if (link != nm_device_get_link_active (dev)) { nm_device_set_link_active (dev, link); nm_dbus_schedule_device_status_change (dev, DEVICE_STATUS_CHANGE); nm_policy_schedule_state_update (dev->app_data); } } /* * nm_device_get_essid * * If a device is wireless, return the essid that it is attempting * to use. * * Returns: allocated string containing essid. Must be freed by caller. * */ char * nm_device_get_essid (NMDevice *dev) { int sk; int err; g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (nm_device_is_wireless (dev), NULL); /* Test devices return the essid of their "best" access point * or if there is none, the contents of the cur_essid field. */ if (dev->test_device) { NMAccessPoint *best_ap = nm_device_get_best_ap (dev); char *essid = dev->options.wireless.cur_essid; /* Or, if we've got a best ap, use that ESSID instead */ if (best_ap) { essid = nm_ap_get_essid (best_ap); nm_ap_unref (best_ap); } return (essid); } sk = iw_sockets_open (); if (sk >= 0) { wireless_config info; err = iw_get_basic_config(sk, nm_device_get_iface (dev), &info); if (err >= 0) { if (dev->options.wireless.cur_essid) g_free (dev->options.wireless.cur_essid); dev->options.wireless.cur_essid = g_strdup (info.essid); } else syslog (LOG_ERR, "nm_device_get_essid(): error getting ESSID for device %s. errno = %d", nm_device_get_iface (dev), errno); close (sk); } return (dev->options.wireless.cur_essid); } /* * nm_device_set_essid * * If a device is wireless, set the essid that it should use. */ void nm_device_set_essid (NMDevice *dev, const char *essid) { int sk; int err; struct iwreq wreq; unsigned char safe_essid[IW_ESSID_MAX_SIZE + 1] = "\0"; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); /* Test devices directly set cur_essid */ if (dev->test_device) { if (dev->options.wireless.cur_essid) g_free (dev->options.wireless.cur_essid); dev->options.wireless.cur_essid = g_strdup (essid); return; } /* Make sure the essid we get passed is a valid size */ if (!essid) safe_essid[0] = '\0'; else { strncpy (safe_essid, essid, IW_ESSID_MAX_SIZE); safe_essid[IW_ESSID_MAX_SIZE] = '\0'; } sk = iw_sockets_open (); if (sk >= 0) { wreq.u.essid.pointer = (caddr_t) safe_essid; wreq.u.essid.length = strlen (safe_essid) + 1; wreq.u.essid.flags = 1; /* Enable essid on card */ err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWESSID, &wreq); if (err == -1) syslog (LOG_ERR, "nm_device_set_essid(): error setting ESSID '%s' for device %s. errno = %d", safe_essid, nm_device_get_iface (dev), errno); close (sk); } } /* * nm_device_get_frequency * * For wireless devices, get the frequency we broadcast/receive on. * */ double nm_device_get_frequency (NMDevice *dev) { int sk; int err; double freq = 0; g_return_val_if_fail (dev != NULL, 0); g_return_val_if_fail (nm_device_is_wireless (dev), 0); /* Test devices don't really have a frequency, they always succeed */ if (dev->test_device) return 703000000; sk = iw_sockets_open (); if (sk >= 0) { struct iwreq wrq; err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCGIWFREQ, &wrq); if (err >= 0) freq = iw_freq2float (&wrq.u.freq); if (err == -1) syslog (LOG_ERR, "nm_device_get_frequency(): error getting frequency for device %s. errno = %d", nm_device_get_iface (dev), errno); close (sk); } return (freq); } /* * nm_device_set_frequency * * For wireless devices, set the frequency to broadcast/receive on. * A frequency <= 0 means "auto". * */ void nm_device_set_frequency (NMDevice *dev, const double freq) { int sk; int err; /* HACK FOR NOW */ if (freq <= 0) return; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); /* Test devices don't really have a frequency, they always succeed */ if (dev->test_device) return; if (nm_device_get_frequency (dev) == freq) return; sk = iw_sockets_open (); if (sk >= 0) { struct iwreq wrq; if (freq <= 0) { /* Auto */ /* People like to make things hard for us. Even though iwlib/iwconfig say * that wrq.u.freq.m should be -1 for "auto" mode, nobody actually supports * that. Madwifi actually uses "0" to mean "auto". So, we'll try 0 first * and if that doesn't work, fall back to the iwconfig method and use -1. * * As a further note, it appears that Atheros/Madwifi cards can't go back to * any-channel operation once you force set the channel on them. For example, * if you set a prism54 card to a specific channel, but then set the ESSID to * something else later, it will scan for the ESSID and switch channels just fine. * Atheros cards, however, just stay at the channel you previously set and don't * budge, no matter what you do to them, until you tell them to go back to * any-channel operation. */ wrq.u.freq.m = 0; wrq.u.freq.e = 0; wrq.u.freq.flags = 0; } else { /* Fixed */ wrq.u.freq.flags = IW_FREQ_FIXED; iw_float2freq (freq, &wrq.u.freq); } err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWFREQ, &wrq); if (err == -1) { gboolean success = FALSE; if ((freq <= 0) && ((errno == EINVAL) || (errno == EOPNOTSUPP))) { /* Ok, try "auto" the iwconfig way if the Atheros way didn't work */ wrq.u.freq.m = -1; wrq.u.freq.e = 0; wrq.u.freq.flags = 0; if (iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWFREQ, &wrq) != -1) success = TRUE; } } close (sk); } } /* * nm_device_get_bitrate * * For wireless devices, get the bitrate to broadcast/receive at. * Returned value is rate in KHz. * */ int nm_device_get_bitrate (NMDevice *dev) { int sk; int err = -1; struct iwreq wrq; g_return_val_if_fail (dev != NULL, 0); g_return_val_if_fail (nm_device_is_wireless (dev), 0); /* Test devices don't really have a bitrate, they always succeed */ if (dev->test_device) return 11; sk = iw_sockets_open (); if (sk >= 0) { err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCGIWRATE, &wrq); close (sk); } return ((err >= 0) ? wrq.u.bitrate.value / 1000 : 0); } /* * nm_device_set_bitrate * * For wireless devices, set the bitrate to broadcast/receive at. * Rate argument should be in Mbps (mega-bits per second), or 0 for automatic. * */ void nm_device_set_bitrate (NMDevice *dev, const int Mbps) { int sk; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); /* Test devices don't really have a bitrate, they always succeed */ if (dev->test_device) return; if (nm_device_get_bitrate (dev) == Mbps) return; sk = iw_sockets_open (); if (sk >= 0) { struct iwreq wrq; if (Mbps != 0) { wrq.u.bitrate.value = Mbps * 1000; wrq.u.bitrate.fixed = 1; } else { /* Auto bitrate */ wrq.u.bitrate.value = -1; wrq.u.bitrate.fixed = 0; } /* Silently fail as not all drivers support setting bitrate yet (ipw2x00 for example) */ iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWRATE, &wrq); close (sk); } } /* * nm_device_get_ap_address * * If a device is wireless, get the access point's ethernet address * that the card is associated with. */ void nm_device_get_ap_address (NMDevice *dev, struct ether_addr *addr) { int iwlib_socket; struct iwreq wrq; g_return_if_fail (dev != NULL); g_return_if_fail (addr != NULL); g_return_if_fail (nm_device_is_wireless (dev)); /* Test devices return an invalid address when there's no link, * and a made-up address when there is a link. */ if (dev->test_device) { struct ether_addr good_addr = { {0x70, 0x37, 0x03, 0x70, 0x37, 0x03} }; struct ether_addr bad_addr = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; gboolean link = nm_device_get_link_active (dev); memcpy ((link ? &good_addr : &bad_addr), &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr)); return; } iwlib_socket = iw_sockets_open (); if (iw_get_ext (iwlib_socket, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0) memcpy (addr, &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr)); else memset (addr, 0, sizeof (struct ether_addr)); close (iwlib_socket); } /* * nm_device_set_enc_key * * If a device is wireless, set the encryption key that it should use. * * key: encryption key to use, or NULL or "" to disable encryption. * NOTE that at this time, the key must be the raw HEX key, not * a passphrase. */ void nm_device_set_enc_key (NMDevice *dev, const char *key, NMDeviceAuthMethod auth_method) { int sk; int err; struct iwreq wreq; int keylen; unsigned char safe_key[IW_ENCODING_TOKEN_MAX + 1]; gboolean set_key = FALSE; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); /* Test devices just ignore encryption keys */ if (dev->test_device) return; /* Make sure the essid we get passed is a valid size */ if (!key) safe_key[0] = '\0'; else { strncpy (safe_key, key, IW_ENCODING_TOKEN_MAX); safe_key[IW_ENCODING_TOKEN_MAX] = '\0'; } sk = iw_sockets_open (); if (sk >= 0) { wreq.u.data.pointer = (caddr_t) NULL; wreq.u.data.length = 0; wreq.u.data.flags = IW_ENCODE_ENABLED; /* Unfortunately, some drivers (Cisco) don't make a distinction between * Open System authentication mode and whether or not to use WEP. You * DON'T have to use WEP when using Open System, but these cards force * it. Therefore, we have to set Open System mode when using WEP. */ if (strlen (safe_key) == 0) { wreq.u.data.flags |= IW_ENCODE_DISABLED | IW_ENCODE_NOKEY; set_key = TRUE; } else { unsigned char parsed_key[IW_ENCODING_TOKEN_MAX + 1]; keylen = iw_in_key_full (sk, nm_device_get_iface (dev), safe_key, &parsed_key[0], &wreq.u.data.flags); if (keylen > 0) { switch (auth_method) { case NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM: wreq.u.data.flags |= IW_ENCODE_OPEN; break; case NM_DEVICE_AUTH_METHOD_SHARED_KEY: wreq.u.data.flags |= IW_ENCODE_RESTRICTED; break; default: wreq.u.data.flags |= IW_ENCODE_RESTRICTED; break; } wreq.u.data.pointer = (caddr_t) &parsed_key; wreq.u.data.length = keylen; set_key = TRUE; } } if (set_key) { err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWENCODE, &wreq); if (err == -1) syslog (LOG_ERR, "nm_device_set_enc_key(): error setting key for device %s. errno = %d", nm_device_get_iface (dev), errno); } close (sk); } else syslog (LOG_ERR, "nm_device_set_enc_key(): could not get wireless control socket."); } /* * nm_device_get_signal_strength * * Get the current signal strength of a wireless device. This only works when * the card is associated with an access point, so will only work for the * active device. * * Returns: -1 on error * 0 - 100 strength percentage of the connection to the current access point * */ gint8 nm_device_get_signal_strength (NMDevice *dev) { g_return_val_if_fail (dev != NULL, -1); g_return_val_if_fail (nm_device_is_wireless (dev), -1); return (dev->options.wireless.strength); } /* * nm_device_update_signal_strength * * Update the device's idea of the strength of its connection to the * current access point. * */ void nm_device_update_signal_strength (NMDevice *dev) { gboolean has_range; int sk; iwrange range; iwstats stats; int percent = -1; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); g_return_if_fail (dev->app_data != NULL); /* Grab the scan lock since our strength is meaningless during a scan. */ if (!nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__)) return; /* If we aren't the active device, we don't really have a signal strength * that would mean anything. */ if (dev != dev->app_data->active_device) { dev->options.wireless.strength = -1; goto out; } /* Fake a value for test devices */ if (dev->test_device) { dev->options.wireless.strength = 75; goto out; } sk = iw_sockets_open (); has_range = (iw_get_range_info (sk, nm_device_get_iface (dev), &range) >= 0); if (iw_get_stats (sk, nm_device_get_iface (dev), &stats, &range, has_range) == 0) { percent = nm_wireless_qual_to_percent (&stats.qual, (const iwqual *)(&dev->options.wireless.max_qual), (const iwqual *)(&dev->options.wireless.avg_qual)); } close (sk); /* Try to smooth out the strength. Atmel cards, for example, will give no strength * one second and normal strength the next. */ if ((percent == -1) && (++dev->options.wireless.invalid_strength_counter <= 3)) percent = dev->options.wireless.strength; else dev->options.wireless.invalid_strength_counter = 0; dev->options.wireless.strength = percent; out: nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); } /* * nm_device_get_ip4_address * * Get a device's IPv4 address * */ guint32 nm_device_get_ip4_address(NMDevice *dev) { g_return_val_if_fail (dev != NULL, 0); return (dev->ip4_address); } void nm_device_update_ip4_address (NMDevice *dev) { guint32 new_address; struct ifreq req; int sk; int err; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); g_return_if_fail (nm_device_get_iface (dev) != NULL); /* Test devices get a nice, bogus IP address */ if (dev->test_device) { dev->ip4_address = 0x07030703; return; } if ((sk = nm_device_open_sock ()) < 0) return; memset (&req, 0, sizeof (struct ifreq)); strncpy ((char *)(&req.ifr_name), nm_device_get_iface (dev), strlen (nm_device_get_iface (dev))); err = ioctl (sk, SIOCGIFADDR, &req); close (sk); if (err != 0) return; new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr; /* If the new address is different, send an IP4AddressChanged signal on the bus */ if (new_address != nm_device_get_ip4_address (dev)) { nm_dbus_signal_device_ip4_address_change (dev->app_data->dbus_connection, dev); dev->ip4_address = new_address; } } /* * nm_device_get_ip6_address * * Get a device's IPv6 address * */ void nm_device_get_ip6_address(NMDevice *dev) { /* FIXME * Implement */ } /* * nm_device_get_hw_address * * Get a device's hardware address * */ void nm_device_get_hw_address(NMDevice *dev, unsigned char hw_addr[ETH_ALEN]) { g_return_if_fail (dev != NULL); memcpy (hw_addr, dev->hw_addr, ETH_ALEN); } void nm_device_update_hw_address (NMDevice *dev) { struct ifreq req; int sk; int err; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); g_return_if_fail (nm_device_get_iface (dev) != NULL); /* Test devices get a nice, bogus IP address */ if (dev->test_device) { memset (dev->hw_addr, 0, ETH_ALEN); return; } if ((sk = nm_device_open_sock ()) < 0) return; memset (&req, 0, sizeof (struct ifreq)); strncpy ((char *)(&req.ifr_name), nm_device_get_iface (dev), strlen (nm_device_get_iface (dev))); err = ioctl (sk, SIOCGIFHWADDR, &req); close (sk); if (err != 0) return; memcpy (dev->hw_addr, req.ifr_hwaddr.sa_data, ETH_ALEN); } /* * nm_device_set_up_down * * Set the up flag on the device on or off * */ static void nm_device_set_up_down (NMDevice *dev, gboolean up) { struct ifreq ifr; int sk; int err; guint32 flags = up ? IFF_UP : ~IFF_UP; g_return_if_fail (dev != NULL); /* Test devices do whatever we tell them to do */ if (dev->test_device) { dev->test_device_up = up; return; } if (nm_device_get_driver_support_level (dev) == NM_DRIVER_UNSUPPORTED) return; sk = nm_device_open_sock (); if (sk < 0) return; /* Get flags already there */ strcpy (ifr.ifr_name, nm_device_get_iface (dev)); err = ioctl (sk, SIOCGIFFLAGS, &ifr); if (!err) { /* If the interface doesn't have those flags already, * set them on it. */ if ((ifr.ifr_flags^flags) & IFF_UP) { ifr.ifr_flags &= ~IFF_UP; ifr.ifr_flags |= IFF_UP & flags; if ((err = ioctl (sk, SIOCSIFFLAGS, &ifr))) syslog (LOG_ERR, "nm_device_set_up_down() could not bring device %s %s. errno = %d", nm_device_get_iface (dev), (up ? "up" : "down"), errno ); } /* Make sure we have a valid MAC address, some cards reload firmware when they * are brought up. */ if (!nm_ethernet_address_is_valid((struct ether_addr *)dev->hw_addr)) nm_device_update_hw_address(dev); } else syslog (LOG_ERR, "nm_device_set_up_down() could not get flags for device %s. errno = %d", nm_device_get_iface (dev), errno ); close (sk); } /* * Interface state functions: bring up, down, check * */ void nm_device_bring_up (NMDevice *dev) { g_return_if_fail (dev != NULL); nm_device_set_up_down (dev, TRUE); } void nm_device_bring_down (NMDevice *dev) { g_return_if_fail (dev != NULL); nm_device_set_up_down (dev, FALSE); } gboolean nm_device_is_up (NMDevice *dev) { int sk; struct ifreq ifr; int err; g_return_val_if_fail (dev != NULL, FALSE); if (dev->test_device) return (dev->test_device_up); sk = nm_device_open_sock (); if (sk < 0) return (FALSE); /* Get device's flags */ strcpy (ifr.ifr_name, nm_device_get_iface (dev)); err = ioctl (sk, SIOCGIFFLAGS, &ifr); close (sk); if (!err) return (!((ifr.ifr_flags^IFF_UP) & IFF_UP)); syslog (LOG_ERR, "nm_device_is_up() could not get flags for device %s. errno = %d", nm_device_get_iface (dev), errno ); return (FALSE); } /* * nm_device_get_mode * * Get managed/infrastructure/adhoc mode on a device (currently wireless only) * */ NMNetworkMode nm_device_get_mode (NMDevice *dev) { int sk; NMNetworkMode mode = NETWORK_MODE_UNKNOWN; g_return_val_if_fail (dev != NULL, NETWORK_MODE_UNKNOWN); g_return_val_if_fail (nm_device_is_wireless (dev), NETWORK_MODE_UNKNOWN); /* Force the card into Managed/Infrastructure mode */ sk = iw_sockets_open (); if (sk >= 0) { struct iwreq wrq; int err; err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCGIWMODE, &wrq); if (err == 0) { switch (wrq.u.mode) { case IW_MODE_INFRA: mode = NETWORK_MODE_INFRA; break; case IW_MODE_ADHOC: mode = NETWORK_MODE_ADHOC; break; default: break; } } else syslog (LOG_ERR, "nm_device_get_mode (%s): error setting card to Infrastructure mode. errno = %d", nm_device_get_iface (dev), errno); close (sk); } return (mode); } /* * nm_device_set_mode * * Set managed/infrastructure/adhoc mode on a device (currently wireless only) * */ gboolean nm_device_set_mode (NMDevice *dev, const NMNetworkMode mode) { int sk; gboolean success = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wireless (dev), FALSE); g_return_val_if_fail ((mode == NETWORK_MODE_INFRA) || (mode == NETWORK_MODE_ADHOC), FALSE); if (nm_device_get_mode (dev) == mode) return TRUE; /* Force the card into Managed/Infrastructure mode */ sk = iw_sockets_open (); if (sk >= 0) { struct iwreq wreq; int err; gboolean mode_good = FALSE; switch (mode) { case NETWORK_MODE_INFRA: wreq.u.mode = IW_MODE_INFRA; mode_good = TRUE; break; case NETWORK_MODE_ADHOC: wreq.u.mode = IW_MODE_ADHOC; mode_good = TRUE; break; default: mode_good = FALSE; break; } if (mode_good) { err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWMODE, &wreq); if (err == 0) success = TRUE; else syslog (LOG_ERR, "nm_device_set_mode (%s): error setting card to Infrastructure mode. errno = %d", nm_device_get_iface (dev), errno); } close (sk); } return (success); } /* * nm_device_activation_schedule_finish * * Schedule an idle routine in the main thread to finish the activation. * */ void nm_device_activation_schedule_finish (NMDevice *dev, DeviceStatus activation_result) { GSource *source = NULL; NMActivationResult *result = NULL; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); result = g_malloc0 (sizeof (NMActivationResult)); nm_device_ref (dev); /* Ref device for idle handler */ result->dev = dev; result->result = activation_result; source = g_idle_source_new (); g_source_set_callback (source, nm_policy_activation_finish, (gpointer)result, NULL); g_source_attach (source, dev->app_data->main_context); g_source_unref (source); } /* * nm_device_activation_schedule_start * * Tell the device thread to begin activation. * * Returns: TRUE on success activation beginning * FALSE on error beginning activation (bad params, couldn't create thread) * */ gboolean nm_device_activation_schedule_start (NMDevice *dev) { NMData *data = NULL; GSource *source = NULL; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (!dev->activating, TRUE); /* Return if activation has already begun */ data = dev->app_data; g_return_val_if_fail (data != NULL, FALSE); /* Reset communication flags between worker and main thread */ dev->activating = TRUE; dev->quit_activation = FALSE; if (nm_device_is_wireless (dev)) { nm_device_set_now_scanning (dev, TRUE); dev->options.wireless.user_key_received = FALSE; } if (nm_device_get_driver_support_level (dev) == NM_DRIVER_UNSUPPORTED) { dev->activating = FALSE; return (FALSE); } source = g_idle_source_new (); g_source_set_callback (source, nm_device_activate, dev, NULL); g_source_attach (source, dev->context); g_source_unref (source); nm_dbus_signal_device_status_change (data->dbus_connection, dev, DEVICE_ACTIVATING); return (TRUE); } /* * nm_device_activation_handle_cancel * * Check whether we should stop activation, and if so clean up flags * and other random things. * */ static gboolean nm_device_activation_handle_cancel (NMDevice *dev) { g_return_val_if_fail (dev != NULL, TRUE); /* If we were told to quit activation, stop the thread and return */ if (dev->quit_activation) { syslog (LOG_DEBUG, "nm_device_activation_worker(%s): activation canceled.", nm_device_get_iface (dev)); if (nm_device_is_wireless (dev)) nm_device_set_now_scanning (dev, FALSE); return (TRUE); } return (FALSE); } /* * nm_device_wireless_wait_for_link * * Try to be clever about when the wireless card really has associated with the access point. * Return TRUE when we think that it has, and FALSE when we thing it has not associated. * */ static gboolean nm_device_wireless_wait_for_link (NMDevice *dev, const char *essid) { struct timeval end_time; struct timeval cur_time; gboolean link = FALSE; double last_freq = 0; guint assoc_count = 0; gint pause_value; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (time > 0, FALSE); pause_value = nm_device_get_association_pause_value (dev); if (pause_value < 1) return FALSE; gettimeofday (&end_time, NULL); end_time.tv_sec += pause_value; /* We more or less keep asking the driver for the frequency the card is on, and * when the frequency has stabilized (the driver has to scan channels to find the AP, * and when it finds the AP it stops scanning) and the MAC is valid, we think we * have a link. */ gettimeofday (&cur_time, NULL); while (cur_time.tv_sec < end_time.tv_sec) { double cur_freq = nm_device_get_frequency (dev); gboolean assoc = nm_device_wireless_is_associated (dev); char *cur_essid = nm_device_get_essid (dev); if ((cur_freq == last_freq) && assoc && !strcmp (essid, cur_essid)) assoc_count++; else assoc_count = 0; last_freq = cur_freq; g_usleep (G_USEC_PER_SEC / 2); if (nm_device_activation_should_cancel (dev)) break; gettimeofday (&cur_time, NULL); if ((cur_time.tv_sec >= end_time.tv_sec) && (cur_time.tv_usec >= end_time.tv_usec)) break; /* Assume that if we've been associated this long, we might as well just stop. */ if (assoc_count >= 9) break; } /* If we've had a reasonable association count, we say we have a link */ if (assoc_count > 6) link = TRUE; return (link); } /* * nm_device_set_wireless_config * * Bring up a wireless card with the essid and wep key of its "best" ap * * Returns: TRUE on successful activation * FALSE on unsuccessful activation (ie no best AP) * */ static gboolean nm_device_set_wireless_config (NMDevice *dev, NMAccessPoint *ap) { NMDeviceAuthMethod auth; const char *essid = NULL; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wireless (dev), FALSE); g_return_val_if_fail (ap != NULL, FALSE); g_return_val_if_fail (nm_ap_get_essid (ap) != NULL, FALSE); g_return_val_if_fail (nm_ap_get_auth_method (ap) != NM_DEVICE_AUTH_METHOD_UNKNOWN, FALSE); /* Force the card into Managed/Infrastructure mode */ nm_device_bring_down (dev); g_usleep (G_USEC_PER_SEC * 2); nm_device_bring_up (dev); g_usleep (G_USEC_PER_SEC * 2); nm_device_set_mode (dev, NETWORK_MODE_INFRA); essid = nm_ap_get_essid (ap); auth = nm_ap_get_auth_method (ap); nm_device_set_mode (dev, nm_ap_get_mode (ap)); nm_device_set_bitrate (dev, 0); if (nm_ap_get_user_created (ap) || (nm_ap_get_freq (ap) && (nm_ap_get_mode (ap) == NETWORK_MODE_ADHOC))) nm_device_set_frequency (dev, nm_ap_get_freq (ap)); else nm_device_set_frequency (dev, 0); /* auto */ if (nm_ap_get_encrypted (ap) && nm_ap_is_enc_key_valid (ap)) { char *hashed_key = nm_ap_get_enc_key_hashed (ap); if (auth == NM_DEVICE_AUTH_METHOD_NONE) { nm_ap_set_auth_method (ap, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM); syslog (LOG_ERR, "Activation (%s/wireless): AP '%s' said it was encrypted, but had " "'none' for authentication method. Using Open System authentication method.", nm_device_get_iface (dev), nm_ap_get_essid (ap)); } nm_device_set_enc_key (dev, hashed_key, auth); g_free (hashed_key); } else nm_device_set_enc_key (dev, NULL, NM_DEVICE_AUTH_METHOD_NONE); nm_device_set_essid (dev, essid); syslog (LOG_INFO, "Activation (%s/wireless): using essid '%s', with %s authentication.", nm_device_get_iface (dev), essid, (auth == NM_DEVICE_AUTH_METHOD_NONE) ? "no" : ((auth == NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM) ? "Open System" : ((auth == NM_DEVICE_AUTH_METHOD_SHARED_KEY) ? "Shared Key" : "unknown"))); /* Bring the device up and pause to allow card to associate. */ g_usleep (G_USEC_PER_SEC * 2); /* Some cards don't really work well in ad-hoc mode unless you explicitly set the bitrate * on them. (Netgear WG511T/Atheros 5212 with madwifi drivers). Until we can get rate information * from scanned access points out of iwlib, clamp bitrate for these cards at 11Mbps. */ if ((nm_ap_get_mode (ap) == NETWORK_MODE_ADHOC) && (nm_device_get_bitrate (dev) <= 0)) nm_device_set_bitrate (dev, 11000); /* In Kbps */ return (TRUE); } /* * nm_device_activate_wireless_adhoc * * Create an ad-hoc network (rather than associating with one). * */ static gboolean nm_device_activate_wireless_adhoc (NMDevice *dev, NMAccessPoint *ap) { gboolean success = FALSE; NMDeviceAuthMethod auth = NM_DEVICE_AUTH_METHOD_NONE; NMAPListIter *iter; NMAccessPoint *tmp_ap; double card_freqs[IW_MAX_FREQUENCIES]; int num_freqs = 0, i; double freq_to_use = 0; iwrange range; int sk; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (ap != NULL, FALSE); if (nm_ap_get_encrypted (ap)) auth = NM_DEVICE_AUTH_METHOD_SHARED_KEY; /* Build our local list of frequencies to whittle down until we find a free one */ memset (&card_freqs, 0, sizeof (card_freqs)); num_freqs = MIN (dev->options.wireless.num_freqs, IW_MAX_FREQUENCIES); for (i = 0; i < num_freqs; i++) card_freqs[i] = dev->options.wireless.freqs[i]; /* We need to find a clear wireless channel to use. We will * only use 802.11b channels for now. */ iter = nm_ap_list_iter_new (nm_device_ap_list_get (dev)); while ((tmp_ap = nm_ap_list_iter_next (iter))) { double ap_freq = nm_ap_get_freq (tmp_ap); for (i = 0; i < num_freqs && ap_freq; i++) { if (card_freqs[i] == ap_freq) card_freqs[i] = 0; } } nm_ap_list_iter_free (iter); if ((sk = iw_sockets_open ()) < 0) return FALSE; if (iw_get_range_info (sk, nm_device_get_iface (dev), &range) < 0) { close (sk); return FALSE; } close (sk); /* Ok, find the first non-zero freq in our table and use it. * For now we only try to use a channel in the 802.11b channel * space so that most everyone can see it. */ for (i = 0; i < num_freqs; i++) { int channel = iw_freq_to_channel (card_freqs[i], &range); if (card_freqs[i] && (channel > 0) && (channel < 15)) { freq_to_use = card_freqs[i]; break; } } /* Hmm, no free channels in 802.11b space. Pick one more or less randomly */ if (!freq_to_use) { double pfreq; int channel = (int)(random () % 14); int err; err = iw_channel_to_freq (channel, &pfreq, &range); if (err == channel) freq_to_use = pfreq; } if (freq_to_use) { nm_ap_set_freq (ap, freq_to_use); syslog (LOG_INFO, "Will create network '%s' with frequency %f.\n", nm_ap_get_essid (ap), nm_ap_get_freq (ap)); if ((success = nm_device_set_wireless_config (dev, ap))) success = nm_device_activation_configure_ip (dev, TRUE); } return (success); } static gboolean AP_NEED_KEY (NMDevice *dev, NMAccessPoint *ap) { char *essid; gboolean need_key = FALSE; g_return_val_if_fail (ap != NULL, FALSE); essid = nm_ap_get_essid (ap); if (!nm_ap_get_encrypted (ap)) { syslog (LOG_NOTICE, "Activation (%s/wireless): access point '%s' is unencrypted, no key needed.", nm_device_get_iface (dev), essid ? essid : "(null)"); } else { if (nm_ap_is_enc_key_valid (ap)) { syslog (LOG_NOTICE, "Activation (%s/wireless): access point '%s' is encrypted, and a key exists. No new key needed.", nm_device_get_iface (dev), essid ? essid : "(null)"); } else { syslog (LOG_NOTICE, "Activation (%s/wireless): access point '%s' is encrypted, but NO valid key exists. New key needed.", nm_device_get_iface (dev), essid ? essid : "(null)"); need_key = TRUE; } } return (need_key); } /* * get_initial_auth_method * * Update the auth method of the AP from the last-known-good one saved in the allowed list * (which is found from NMI) and ensure that its valid with the encryption status of the AP. * */ static NMDeviceAuthMethod get_initial_auth_method (NMAccessPoint *ap, NMAccessPointList *allowed_list) { g_return_val_if_fail (ap != NULL, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM); if (nm_ap_get_encrypted (ap)) { NMDeviceAuthMethod auth = nm_ap_get_auth_method (ap); NMAccessPoint *allowed_ap = nm_ap_list_get_ap_by_essid (allowed_list, nm_ap_get_essid (ap)); /* Prefer default auth method if we found one for this AP in our allowed list. */ if (allowed_ap) auth = nm_ap_get_auth_method (allowed_ap); if ( (auth == NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM) || (auth == NM_DEVICE_AUTH_METHOD_SHARED_KEY)) return (auth); else return (NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM); } return (NM_DEVICE_AUTH_METHOD_NONE); } void invalidate_ap (NMDevice *dev, NMAccessPoint *ap) { g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); g_return_if_fail (ap != NULL); /* If its an AP the user forced, notify the user it failed. */ /* FIXME: we dont' set ap's that are in our scan list as "artificial", * so we won't be able to signal the user when a connection to on of them * failed. */ if (nm_ap_get_artificial (ap)) nm_dbus_schedule_network_not_found_signal (dev->app_data, nm_ap_get_essid (ap)); nm_ap_set_invalid (ap, TRUE); nm_ap_list_append_ap (dev->app_data->invalid_ap_list, ap); nm_ap_unref (ap); nm_device_update_best_ap (dev); } /* * nm_device_activate_wireless * * Activate a wireless ethernet device. Locking could be confusing here, pay attention to it. * We grab the scan mutex because scanning requires us to set certain state on the card, * like mode, which could screw up device activation link state checks. * */ static gboolean nm_device_activate_wireless (NMDevice *dev) { NMAccessPoint *best_ap; gboolean success = FALSE; guint8 attempt = 1; char last_essid [50] = "\0"; gboolean need_key = FALSE; gboolean found_ap = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); /* Grab the scan mutex, we don't want the scan thread to mess up our settings * during activation and link detection. */ nm_lock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); if (!nm_device_is_up (dev)) nm_device_bring_up (dev); g_usleep (G_USEC_PER_SEC); get_ap: /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) goto out; /* Get a valid "best" access point we should connect to. We don't hold the scan * lock here because this might take a while. */ nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); while (!(best_ap = nm_device_get_best_ap (dev))) { nm_device_set_now_scanning (dev, TRUE); if (!found_ap) syslog (LOG_ERR, "Activation (%s/wireless): waiting for an access point.", nm_device_get_iface (dev)); g_usleep (G_USEC_PER_SEC * 2); /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) { /* Wierd as it may seem, we lock here to balance the unlock in "out:" */ nm_lock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); goto out; } found_ap = TRUE; } if (found_ap) syslog (LOG_ERR, "Activation (%s/wireless): found access point '%s' to use.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap)); /* Set ESSID early so that when we send out the DeviceStatusChanged signal below, * we are able to respond correctly to queries for "getActiveNetwork" against * our device. nm_device_get_path_for_ap() uses the /card's/ AP, not the best_ap. */ nm_device_set_essid (dev, nm_ap_get_essid (best_ap)); /* We grab the scan mutex so that scanning cannot screw up our link detection, since * a scan can change most any attribute on the card for a period of time. */ nm_device_set_now_scanning (dev, FALSE); nm_lock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); if (nm_ap_get_artificial (best_ap)) { /* Some Cisco cards (340/350 PCMCIA) don't return non-broadcasting APs * in their scan results, so we can't know beforehand whether or not the * AP was encrypted. We have to update their encryption status on the fly. */ if (nm_ap_get_encrypted (best_ap) || nm_ap_is_enc_key_valid (best_ap)) { nm_ap_set_encrypted (best_ap, TRUE); nm_ap_set_auth_method (best_ap, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM); } } need_key = AP_NEED_KEY (dev, best_ap); need_key: if (need_key) { char *essid = nm_ap_get_essid (best_ap); if (strcmp (essid, last_essid) != 0) attempt = 1; strncpy (&last_essid[0], essid, 49); /* Don't hold the mutex while waiting for a key */ nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); /* Get a wireless key */ dev->options.wireless.user_key_received = FALSE; nm_dbus_get_user_key_for_network (dev->app_data->dbus_connection, dev, best_ap, attempt); attempt++; need_key = FALSE; /* Wait for the key to come back */ syslog (LOG_DEBUG, "Activation (%s/wireless): asking for user key.", nm_device_get_iface (dev)); while (!dev->options.wireless.user_key_received && !dev->quit_activation) g_usleep (G_USEC_PER_SEC / 2); syslog (LOG_DEBUG, "Activation (%s/wireless): user key received.", nm_device_get_iface (dev)); /* Done waiting, grab lock again */ nm_lock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); /* User may have cancelled the key request, so we need to update our best AP again. */ nm_ap_unref (best_ap); goto get_ap; } if (nm_ap_get_mode (best_ap) == NETWORK_MODE_ADHOC) { /* Only do auto-ip on Ad-Hoc connections for now. We technically * could do DHCP on them though. */ success = nm_device_activation_configure_ip (dev, TRUE); goto connect_done; } try_connect: /* Initial authentication method */ nm_ap_set_auth_method (best_ap, get_initial_auth_method (best_ap, dev->app_data->allowed_ap_list)); while (success == FALSE) { NMAccessPoint *tmp_ap = NULL; gboolean link = FALSE; gboolean adhoc = (nm_ap_get_mode (best_ap) == NETWORK_MODE_ADHOC); /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) goto out; nm_device_set_wireless_config (dev, best_ap); link = nm_device_wireless_wait_for_link (dev, nm_ap_get_essid (best_ap)); /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) goto out; if (!link) { if (nm_ap_get_auth_method (best_ap) == NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM) { syslog (LOG_DEBUG, "Activation (%s/wireless): no hardware link to '%s' in Open System mode, trying Shared Key.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap) ? nm_ap_get_essid (best_ap) : "(none)"); /* Back down to Shared Key mode */ nm_ap_set_auth_method (best_ap, NM_DEVICE_AUTH_METHOD_SHARED_KEY); continue; } else if (nm_ap_get_auth_method (best_ap) == NM_DEVICE_AUTH_METHOD_SHARED_KEY) { /* Must be in Open System mode and it still didn't work, so * we'll invalidate the current "best" ap and get another one */ syslog (LOG_DEBUG, "Activation (%s/wireless): no hardware link to '%s' in Shared Key mode, trying another access point.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap) ? nm_ap_get_essid (best_ap) : "(none)"); } else { syslog (LOG_DEBUG, "Activation (%s/wireless): no hardware link to '%s' in non-encrypted mode.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap) ? nm_ap_get_essid (best_ap) : "(none)"); } /* All applicable modes failed, invalidate current best_ap and get a new one */ invalidate_ap (dev, best_ap); goto get_ap; } /* For those broken cards that report successful hardware link even when WEP key is wrong, * and also for Open System mode (where you cannot know WEP key is wrong ever), we try to * do DHCP and if that fails, fall back to next auth mode and try again. */ if ((success = nm_device_activation_configure_ip (dev, adhoc))) { /* Cache the last known good auth method in both NetworkManagerInfo and our allowed AP list */ nm_dbus_update_network_auth_method (dev->app_data->dbus_connection, nm_ap_get_essid (best_ap), nm_ap_get_auth_method (best_ap)); if ((tmp_ap = nm_ap_list_get_ap_by_essid (dev->app_data->allowed_ap_list, nm_ap_get_essid (best_ap)))) nm_ap_set_auth_method (tmp_ap, nm_ap_get_auth_method (best_ap)); } else { /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) goto out; if ((nm_ap_get_auth_method (best_ap) == NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM) && !adhoc) { /* Back down to Shared Key mode */ syslog (LOG_DEBUG, "Activation (%s/wireless): could not get IP configuration info for '%s' in Open System mode, trying Shared Key.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap) ? nm_ap_get_essid (best_ap) : "(none)"); nm_ap_set_auth_method (best_ap, NM_DEVICE_AUTH_METHOD_SHARED_KEY); continue; } else if ((nm_ap_get_auth_method (best_ap) == NM_DEVICE_AUTH_METHOD_SHARED_KEY) && !adhoc) { /* Shared Key mode failed, we must have bad WEP key */ syslog (LOG_DEBUG, "Activation (%s/wireless): could not get IP configuration info for '%s' in Shared Key mode, asking for new key.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap) ? nm_ap_get_essid (best_ap) : "(none)"); need_key = TRUE; goto need_key; } else { /* All applicable modes failed, invalidate current best_ap and get a new one */ invalidate_ap (dev, best_ap); goto get_ap; } } } connect_done: /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) goto out; if (success) { syslog (LOG_DEBUG, "Activation (%s/wireless): Success! Connected to access point '%s' and got an IP address.", nm_device_get_iface (dev), nm_ap_get_essid (best_ap) ? nm_ap_get_essid (best_ap) : "(none)"); nm_ap_unref (best_ap); } out: nm_device_set_now_scanning (dev, FALSE); nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); return (success); } /* * nm_device_activation_configure_ip * * Perform any IP-based configuration on a device, like running DHCP * or manually setting up the IP address, gateway, and default route. * */ static gboolean nm_device_activation_configure_ip (NMDevice *dev, gboolean do_only_autoip) { gboolean success = FALSE; g_return_val_if_fail (dev != NULL, FALSE); nm_system_delete_default_route (); if (do_only_autoip) { success = nm_device_do_autoip (dev); } else if (nm_device_config_get_use_dhcp (dev)) { int err; err = nm_device_dhcp_request (dev); if (err == RET_DHCP_BOUND) success = TRUE; else { /* Interfaces cannot be down if they are the active interface, * otherwise we cannot use them for scanning or link detection. */ if (nm_device_is_wireless (dev)) { nm_device_set_essid (dev, ""); nm_device_set_enc_key (dev, NULL, NM_DEVICE_AUTH_METHOD_NONE); } if (!nm_device_is_up (dev)) nm_device_bring_up (dev); } } else { /* Manually set up the device */ success = nm_system_device_setup_static_ip4_config (dev); } if (success) { nm_system_device_add_ip6_link_address (dev); nm_system_flush_arp_cache (); nm_system_restart_mdns_responder (); } return (success); } /* * nm_device_activate * * Activate a device, done from the device's worker thread. * */ static gboolean nm_device_activate (gpointer user_data) { NMDevice *dev = (NMDevice *)user_data; gboolean success = FALSE; gboolean finished = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); syslog (LOG_ERR, "Activation (%s) started...", nm_device_get_iface (dev)); /* Bring the device up */ if (!nm_device_is_up (dev)); nm_device_bring_up (dev); if (nm_device_is_wireless (dev)) { gboolean create_network = FALSE; NMAccessPoint *best_ap = nm_device_get_best_ap (dev); if (best_ap) { if (nm_ap_get_user_created (best_ap)) { create_network = TRUE; syslog (LOG_INFO, "Creating wireless network '%s'.\n", nm_ap_get_essid (best_ap)); success = nm_device_activate_wireless_adhoc (dev, best_ap); syslog (LOG_INFO, "Wireless network creation for '%s' was %s.\n", nm_ap_get_essid (best_ap), success ? "successful" : "unsuccessful"); } nm_ap_unref (best_ap); } if (!create_network) success = nm_device_activate_wireless (dev); } else if (nm_device_is_wired (dev)) success = nm_device_activation_configure_ip (dev, FALSE); /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) goto out; if (success) syslog (LOG_DEBUG, "Activation (%s) IP configuration/DHCP successful!\n", nm_device_get_iface (dev)); else syslog (LOG_DEBUG, "Activation (%s) IP configuration/DHCP unsuccessful! Ending activation...\n", nm_device_get_iface (dev)); finished = TRUE; out: syslog (LOG_DEBUG, "Activation (%s) ended.\n", nm_device_get_iface (dev)); dev->activating = FALSE; dev->quit_activation = FALSE; if (finished) nm_device_activation_schedule_finish (dev, success ? DEVICE_NOW_ACTIVE : DEVICE_ACTIVATION_FAILED); return FALSE; } /* * nm_device_is_activating * * Return whether or not the device is currently activating itself. * */ gboolean nm_device_is_activating (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->activating); } /* * nm_device_activation_should_cancel * * Return whether or not we've been told to cancel activation * */ gboolean nm_device_activation_should_cancel (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->quit_activation); } /* * nm_device_activation_cancel * * Signal activation worker that it should stop and die. * */ void nm_device_activation_cancel (NMDevice *dev) { g_return_if_fail (dev != NULL); if (nm_device_is_activating (dev)) { syslog (LOG_DEBUG, "nm_device_activation_cancel(%s): cancelling...", nm_device_get_iface (dev)); dev->quit_activation = TRUE; /* Spin until cancelled. Possible race conditions or deadlocks here. * The other problem with waiting here is that we hold up dbus traffic * that we should respond to. */ while (nm_device_is_activating (dev)) { /* Nice race here between quit activation and dhcp. We may not have * started DHCP when we're told to quit activation, so we need to keep * signalling dhcp to quit, which it will pick up whenever it starts. * This should really be taken care of a better way. */ if (dev->dhcp_iface) nm_device_dhcp_cease (dev); g_usleep (G_USEC_PER_SEC / 2); } syslog (LOG_DEBUG, "nm_device_activation_cancel(%s): cancelled.", nm_device_get_iface (dev)); } } /* * nm_device_deactivate * * Remove a device's routing table entries and IP address. * */ gboolean nm_device_deactivate (NMDevice *dev, gboolean just_added) { g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); nm_device_activation_cancel (dev); if (nm_device_get_driver_support_level (dev) == NM_DRIVER_UNSUPPORTED) return (TRUE); /* Remove any DHCP timeouts that might have been running */ if (nm_device_config_get_use_dhcp (dev)) nm_device_dhcp_remove_timeouts (dev); /* Take out any entries in the routing table and any IP address the device had. */ nm_system_device_flush_routes (dev); nm_system_device_flush_addresses (dev); nm_device_update_ip4_address (dev); if (!just_added && (dev == dev->app_data->active_device)) nm_dbus_signal_device_status_change (dev->app_data->dbus_connection, dev, DEVICE_NO_LONGER_ACTIVE); /* Clean up stuff, don't leave the card associated */ if (nm_device_is_wireless (dev)) { nm_device_set_essid (dev, ""); nm_device_set_enc_key (dev, NULL, NM_DEVICE_AUTH_METHOD_NONE); nm_device_set_mode (dev, NETWORK_MODE_INFRA); dev->options.wireless.scan_interval = 20; } return (TRUE); } /* * nm_device_set_user_key_for_network * * Called upon receipt of a NetworkManagerInfo reply with a * user-supplied key. * */ void nm_device_set_user_key_for_network (NMDevice *dev, NMAccessPointList *invalid_list, unsigned char *network, unsigned char *key, NMEncKeyType enc_type) { NMAccessPoint *best_ap; const char *cancel_message = "***canceled***"; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); g_return_if_fail (network != NULL); g_return_if_fail (key != NULL); /* If the user canceled, mark the ap as invalid */ if (strncmp (key, cancel_message, strlen (cancel_message)) == 0) { NMAccessPoint *ap; if ((ap = nm_device_ap_list_get_ap_by_essid (dev, network))) { NMAccessPoint *invalid_ap = nm_ap_new_from_ap (ap); if (invalid_list) nm_ap_list_append_ap (invalid_list, invalid_ap); } nm_device_update_best_ap (dev); } else if ((best_ap = nm_device_get_best_ap (dev))) { /* Make sure the "best" ap matches the essid we asked for the key of, * then set the new key on the access point. */ if (nm_null_safe_strcmp (network, nm_ap_get_essid (best_ap)) == 0) nm_ap_set_enc_key_source (best_ap, key, enc_type); nm_ap_unref (best_ap); } dev->options.wireless.user_key_received = TRUE; } /* * nm_device_ap_list_add_ap * * Add an access point to the devices internal AP list. * */ static void nm_device_ap_list_add_ap (NMDevice *dev, NMAccessPoint *ap) { g_return_if_fail (dev != NULL); g_return_if_fail (ap != NULL); g_return_if_fail (nm_device_is_wireless (dev)); nm_ap_list_append_ap (dev->options.wireless.ap_list, ap); /* Transfer ownership of ap to the list by unrefing it here */ nm_ap_unref (ap); } /* * nm_device_ap_list_clear * * Clears out the device's internal list of available access points. * */ void nm_device_ap_list_clear (NMDevice *dev) { g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); if (!dev->options.wireless.ap_list) return; nm_ap_list_unref (dev->options.wireless.ap_list); dev->options.wireless.ap_list = NULL; } /* * nm_device_ap_list_get_ap_by_essid * * Get the access point for a specific essid * */ NMAccessPoint *nm_device_ap_list_get_ap_by_essid (NMDevice *dev, const char *essid) { NMAccessPoint *ret_ap = NULL; g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (nm_device_is_wireless (dev), NULL); g_return_val_if_fail (essid != NULL, NULL); if (!dev->options.wireless.ap_list) return (NULL); ret_ap = nm_ap_list_get_ap_by_essid (dev->options.wireless.ap_list, essid); return (ret_ap); } /* * nm_device_ap_list_get_ap_by_address * * Get the access point for a specific MAC address * */ NMAccessPoint *nm_device_ap_list_get_ap_by_address (NMDevice *dev, const struct ether_addr *addr) { NMAccessPoint *ret_ap = NULL; g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (nm_device_is_wireless (dev), NULL); g_return_val_if_fail (addr != NULL, NULL); if (!dev->options.wireless.ap_list) return (NULL); ret_ap = nm_ap_list_get_ap_by_address (dev->options.wireless.ap_list, addr); return (ret_ap); } /* * nm_device_ap_list_get * * Return a pointer to the AP list * */ NMAccessPointList *nm_device_ap_list_get (NMDevice *dev) { g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (nm_device_is_wireless (dev), NULL); return (dev->options.wireless.ap_list); } /* * Get/Set functions for "best" access point * * Caller MUST unref returned access point when done with it. * */ NMAccessPoint *nm_device_get_best_ap (NMDevice *dev) { NMAccessPoint *best_ap; g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (nm_device_is_wireless (dev), NULL); nm_lock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); best_ap = dev->options.wireless.best_ap; /* Callers get a reffed AP */ if (best_ap) nm_ap_ref (best_ap); nm_unlock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); return (best_ap); } void nm_device_set_best_ap (NMDevice *dev, NMAccessPoint *ap) { g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); nm_lock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); if (dev->options.wireless.best_ap) nm_ap_unref (dev->options.wireless.best_ap); if (ap) nm_ap_ref (ap); dev->options.wireless.best_ap = ap; nm_device_unfreeze_best_ap (dev); nm_unlock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); } /* * Freeze/unfreeze best ap * * If the user explicitly picks a network to associate with, we don't * change the active network until it goes out of range. * */ void nm_device_freeze_best_ap (NMDevice *dev) { g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); dev->options.wireless.freeze_best_ap = TRUE; } void nm_device_unfreeze_best_ap (NMDevice *dev) { g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); dev->options.wireless.freeze_best_ap = FALSE; } gboolean nm_device_is_best_ap_frozen (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wireless (dev), FALSE); return (dev->options.wireless.freeze_best_ap); } /* * Accessor for dhcp_interface * */ struct dhcp_interface *nm_device_get_dhcp_iface (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->dhcp_iface); } void nm_device_set_dhcp_iface (NMDevice *dev, struct dhcp_interface *dhcp_iface) { g_return_if_fail (dev != NULL); /* NOTE: this function should only be used from the activation worker thread * which will take care of shutting down any active DHCP threads and cleaning * up the dev->dhcp_iface structure. */ dev->dhcp_iface = dhcp_iface; } /* * nm_device_get_path_for_ap * * Return the object path for an access point. * * NOTE: assumes the access point is actually in the device's access point list. * */ char * nm_device_get_path_for_ap (NMDevice *dev, NMAccessPoint *ap) { g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (ap != NULL, NULL); if (nm_ap_get_essid (ap)) return (g_strdup_printf ("%s/%s/Networks/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev), nm_ap_get_essid (ap))); else return (NULL); } /* * nm_device_need_ap_switch * * Returns TRUE if the essid of the card does not match the essid * of the "best" access point it should be associating with. * */ gboolean nm_device_need_ap_switch (NMDevice *dev) { NMAccessPoint *ap; gboolean need_switch = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wireless (dev), FALSE); /* Since the card's ESSID may change during a scan, we can't really * rely on checking the ESSID during that time. */ if (!nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__)) return FALSE; ap = nm_device_get_best_ap (dev); if (nm_null_safe_strcmp (nm_device_get_essid (dev), (ap ? nm_ap_get_essid (ap) : NULL)) != 0) need_switch = TRUE; if (ap) nm_ap_unref (ap); nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); return (need_switch); } /* * nm_device_update_best_ap * * Recalculate the "best" access point we should be associating with. This * function may disrupt the current connection, so it should be called only * when necessary, ie when the current access point is no longer in range * or is for some other reason invalid and should no longer be used. * */ void nm_device_update_best_ap (NMDevice *dev) { NMAccessPointList *ap_list; NMAPListIter *iter; NMAccessPoint *scan_ap = NULL; NMAccessPoint *best_ap = NULL; NMAccessPoint *trusted_best_ap = NULL; NMAccessPoint *untrusted_best_ap = NULL; GTimeVal trusted_latest_timestamp = {0, 0}; GTimeVal untrusted_latest_timestamp = {0, 0}; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); g_return_if_fail (nm_device_is_wireless (dev)); if (!(ap_list = nm_device_ap_list_get (dev))) return; /* Iterate over the device's ap list to make sure the current * "best" ap is still in the device's ap list (so that if its * not, we can "unfreeze" the best ap if its been frozen already). * If it is, we don't change the best ap here. */ if (nm_device_is_best_ap_frozen (dev)) { best_ap = nm_device_get_best_ap (dev); /* If its in the device's ap list still, don't change the * best ap, since its frozen. */ nm_lock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); if (best_ap) { char *essid = nm_ap_get_essid (best_ap); /* Two reasons to keep the current best_ap: * 1) Its still valid and we see it in our scan data * 2) Its an ad-hoc network that we've created (and therefore its not in our scan data) */ if ( ( !nm_ap_list_get_ap_by_essid (dev->app_data->invalid_ap_list, essid) && nm_device_ap_list_get_ap_by_essid (dev, essid)) || nm_ap_get_user_created (best_ap)) { nm_ap_unref (best_ap); nm_unlock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); return; } nm_ap_unref (best_ap); } /* Otherwise, its gone away and we don't care about it anymore */ nm_device_unfreeze_best_ap (dev); nm_unlock_mutex (dev->options.wireless.best_ap_mutex, __FUNCTION__); } if (!(iter = nm_ap_list_iter_new (ap_list))) return; while ((scan_ap = nm_ap_list_iter_next (iter))) { NMAccessPoint *tmp_ap; char *ap_essid = nm_ap_get_essid (scan_ap); /* Access points in the "invalid" list cannot be used */ if (nm_ap_list_get_ap_by_essid (dev->app_data->invalid_ap_list, ap_essid)) continue; if ((tmp_ap = nm_ap_list_get_ap_by_essid (dev->app_data->allowed_ap_list, ap_essid))) { const GTimeVal *curtime = nm_ap_get_timestamp (tmp_ap); if (nm_ap_get_trusted (tmp_ap) && (curtime->tv_sec > trusted_latest_timestamp.tv_sec)) { trusted_latest_timestamp = *nm_ap_get_timestamp (tmp_ap); trusted_best_ap = scan_ap; /* Merge access point data (mainly to get updated WEP key) */ nm_ap_set_enc_key_source (trusted_best_ap, nm_ap_get_enc_key_source (tmp_ap), nm_ap_get_enc_type (tmp_ap)); } else if (!nm_ap_get_trusted (tmp_ap) && (curtime->tv_sec > untrusted_latest_timestamp.tv_sec)) { untrusted_latest_timestamp = *nm_ap_get_timestamp (tmp_ap); untrusted_best_ap = scan_ap; /* Merge access point data (mainly to get updated WEP key) */ nm_ap_set_enc_key_source (untrusted_best_ap, nm_ap_get_enc_key_source (tmp_ap), nm_ap_get_enc_type (tmp_ap)); } } } best_ap = trusted_best_ap ? trusted_best_ap : untrusted_best_ap; nm_ap_list_iter_free (iter); nm_device_set_best_ap (dev, best_ap); } typedef struct NMDeviceForceData { NMDevice *dev; const char *net; const char *key; NMEncKeyType key_type; } NMDeviceForceData; static gboolean nm_device_wireless_force_use (NMDevice *dev, const char *essid, const char *key, NMEncKeyType key_type) { gboolean encrypted = FALSE; NMAccessPoint *ap = NULL; NMAccessPoint *tmp_ap = NULL; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); if (!essid) return FALSE; syslog (LOG_DEBUG, "Forcing AP '%s'", essid); if ( key && strlen (key) && (key_type != NM_ENC_TYPE_UNKNOWN) && (key_type != NM_ENC_TYPE_NONE)) encrypted = TRUE; /* Find the AP in our card's scan list first. * If its not there, create an entirely new AP. */ if (!(ap = nm_ap_list_get_ap_by_essid (nm_device_ap_list_get (dev), essid))) { /* Okay, the card didn't see it in the scan, Cisco cards sometimes do this. * So we make a "fake" access point and add it to the scan list. */ ap = nm_ap_new (); nm_ap_set_essid (ap, essid); nm_ap_set_encrypted (ap, encrypted); if (encrypted) nm_ap_set_auth_method (ap, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM); else nm_ap_set_auth_method (ap, NM_DEVICE_AUTH_METHOD_NONE); nm_ap_set_artificial (ap, TRUE); nm_ap_list_append_ap (nm_device_ap_list_get (dev), ap); nm_ap_unref (ap); } /* Now that this AP has an essid, copy over encryption keys and whatnot */ if ((tmp_ap = nm_ap_list_get_ap_by_essid (dev->app_data->allowed_ap_list, nm_ap_get_essid (ap)))) { nm_ap_set_enc_key_source (ap, nm_ap_get_enc_key_source (tmp_ap), nm_ap_get_enc_type (tmp_ap)); nm_ap_set_auth_method (ap, nm_ap_get_auth_method (tmp_ap)); nm_ap_set_invalid (ap, nm_ap_get_invalid (tmp_ap)); nm_ap_set_timestamp (ap, nm_ap_get_timestamp (tmp_ap)); } /* Use the encryption key and type the user sent us if its valid */ if (encrypted) nm_ap_set_enc_key_source (ap, key, key_type); nm_device_set_best_ap (dev, ap); nm_device_freeze_best_ap (dev); return TRUE; } gboolean nm_device_wired_force_use (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); return TRUE; } gboolean nm_device_force_use (gpointer user_data) { NMDeviceForceData *cb_data = (NMDeviceForceData *)user_data; NMData *app_data = NULL; gboolean success = FALSE; g_return_val_if_fail (cb_data != NULL, FALSE); if (!cb_data->dev || !cb_data->dev->app_data) goto out; app_data = cb_data->dev->app_data; if (nm_device_is_wireless (cb_data->dev)) success = nm_device_wireless_force_use (cb_data->dev, cb_data->net, cb_data->key, cb_data->key_type); else if (nm_device_is_wired (cb_data->dev)) success = nm_device_wired_force_use (cb_data->dev); if (success) nm_policy_schedule_device_switch (cb_data->dev, cb_data->dev->app_data); out: /* Function that scheduled us must ref the device */ nm_device_unref (cb_data->dev); app_data->forcing_device = FALSE; g_free (cb_data); return FALSE; } void nm_device_schedule_force_use (NMDevice *dev, const char *network, const char *key, NMEncKeyType key_type) { NMDeviceForceData *cb_data; GSource *source; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); g_return_if_fail (dev->app_data->main_context != NULL); cb_data = g_malloc0 (sizeof (NMDeviceForceData)); cb_data->dev = dev; cb_data->net = network ? g_strdup (network) : NULL; cb_data->key = key ? g_strdup (key) : NULL; cb_data->key_type = key_type; source = g_idle_source_new (); g_source_set_callback (source, nm_device_force_use, cb_data, NULL); g_source_attach (source, dev->context); g_source_unref (source); } /* * nm_device_do_pseudo_scan * * Brute-force the allowed access point list to find one that works, if any. * * FIXME * There's probably a better way to do the non-scanning access point discovery * than brute forcing it like this, but that makes the state machine here oh so * much more complicated. */ static void nm_device_do_pseudo_scan (NMDevice *dev) { NMAPListIter *iter; NMAccessPoint *ap; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); /* Test devices shouldn't get here since we fake the AP list earlier */ g_return_if_fail (!dev->test_device); nm_device_ref (dev); if (!(iter = nm_ap_list_iter_new (dev->app_data->allowed_ap_list))) return; nm_device_set_essid (dev, ""); while ((ap = nm_ap_list_iter_next (iter))) { gboolean valid = FALSE; struct ether_addr save_ap_addr; struct ether_addr cur_ap_addr; if (!nm_device_is_up (dev)); nm_device_bring_up (dev); /* Save the MAC address */ nm_device_get_ap_address (dev, &save_ap_addr); if (nm_ap_get_enc_key_source (ap)) { char *hashed_key = nm_ap_get_enc_key_hashed (ap); nm_device_set_enc_key (dev, hashed_key, NM_DEVICE_AUTH_METHOD_SHARED_KEY); g_free (hashed_key); } else nm_device_set_enc_key (dev, NULL, NM_DEVICE_AUTH_METHOD_NONE); nm_device_set_essid (dev, nm_ap_get_essid (ap)); /* Wait a bit for association */ g_usleep (G_USEC_PER_SEC * nm_device_get_association_pause_value (dev)); /* Do we have a valid MAC address? */ nm_device_get_ap_address (dev, &cur_ap_addr); valid = nm_ethernet_address_is_valid (&cur_ap_addr); /* If the ap address we had before, and the ap address we * have now, are the same, AP is invalid. Certain cards (orinoco) * will let the essid change, but the the card won't actually de-associate * from the previous access point if it can't associate with the new one * (ie signal too weak, etc). */ if (valid && (memcmp (&save_ap_addr, &cur_ap_addr, sizeof (struct ether_addr)) == 0)) valid = FALSE; if (valid) { syslog(LOG_INFO, "%s: setting AP '%s' best", nm_device_get_iface (dev), nm_ap_get_essid (ap)); nm_device_set_best_ap (dev, ap); nm_policy_schedule_state_update (dev->app_data); break; } } nm_ap_list_iter_free (iter); nm_device_unref (dev); } /* * nm_device_fake_ap_list * * Fake the access point list, used for test devices. * */ static void nm_device_fake_ap_list (NMDevice *dev) { #define NUM_FAKE_APS 4 int i; NMAccessPointList *old_ap_list = nm_device_ap_list_get (dev); char *fake_essids[NUM_FAKE_APS] = { "green", "bay", "packers", "rule" }; struct ether_addr fake_addrs[NUM_FAKE_APS] = {{{0x70, 0x37, 0x03, 0x70, 0x37, 0x03}}, {{0x12, 0x34, 0x56, 0x78, 0x90, 0xab}}, {{0xcd, 0xef, 0x12, 0x34, 0x56, 0x78}}, {{0x90, 0xab, 0xcd, 0xef, 0x12, 0x34}} }; guint8 fake_qualities[NUM_FAKE_APS] = { 150, 26, 200, 100 }; double fake_freqs[NUM_FAKE_APS] = { 3.1416, 4.1416, 5.1415, 6.1415 }; gboolean fake_enc[NUM_FAKE_APS] = { FALSE, TRUE, FALSE, TRUE }; g_return_if_fail (dev != NULL); g_return_if_fail (dev->app_data != NULL); dev->options.wireless.ap_list = nm_ap_list_new (NETWORK_TYPE_DEVICE); for (i = 0; i < NUM_FAKE_APS; i++) { NMAccessPoint *nm_ap = nm_ap_new (); NMAccessPoint *list_ap; /* Copy over info from scan to local structure */ nm_ap_set_essid (nm_ap, fake_essids[i]); if (fake_enc[i]) nm_ap_set_encrypted (nm_ap, FALSE); else nm_ap_set_encrypted (nm_ap, TRUE); nm_ap_set_address (nm_ap, (const struct ether_addr *)(&fake_addrs[i])); nm_ap_set_strength (nm_ap, fake_qualities[i]); nm_ap_set_freq (nm_ap, fake_freqs[i]); /* Merge settings from wireless networks, mainly keys */ if ((list_ap = nm_ap_list_get_ap_by_essid (dev->app_data->allowed_ap_list, nm_ap_get_essid (nm_ap)))) { nm_ap_set_timestamp (nm_ap, nm_ap_get_timestamp (list_ap)); nm_ap_set_enc_key_source (nm_ap, nm_ap_get_enc_key_source (list_ap), nm_ap_get_enc_type (list_ap)); } /* Add the AP to the device's AP list */ nm_device_ap_list_add_ap (dev, nm_ap); } if (dev == dev->app_data->active_device) nm_ap_list_diff (dev->app_data, dev, old_ap_list, nm_device_ap_list_get (dev)); if (old_ap_list) nm_ap_list_unref (old_ap_list); } /* * nm_device_wireless_schedule_scan * * Schedule a wireless scan in the /device's/ thread. * */ static void nm_device_wireless_schedule_scan (NMDevice *dev) { GSource *wscan_source; guint wscan_source_id; g_return_if_fail (dev != NULL); g_return_if_fail (nm_device_is_wireless (dev)); wscan_source = g_timeout_source_new (dev->options.wireless.scan_interval * 1000); g_source_set_callback (wscan_source, nm_device_wireless_scan, dev, NULL); wscan_source_id = g_source_attach (wscan_source, dev->context); g_source_unref (wscan_source); } /* * nm_device_wireless_process_scan_results * * Process results of an iwscan() into our own AP lists. We're an idle function, * but we never reschedule ourselves. * */ static gboolean nm_device_wireless_process_scan_results (gpointer user_data) { NMWirelessScanResults *results = (NMWirelessScanResults *)user_data; NMDevice *dev; wireless_scan *tmp_ap; gboolean have_blank_essids = FALSE; NMAPListIter *iter; GTimeVal cur_time; gboolean list_changed = FALSE; g_return_val_if_fail (results != NULL, FALSE); dev = results->dev; if (!dev || !results->scan_head.result) return FALSE; /* Test devices get their info faked */ if (dev->test_device) { nm_device_fake_ap_list (dev); return FALSE; } /* Devices that don't support scanning have their pseudo-scanning done in * the main thread anyway. */ if (!nm_device_get_supports_wireless_scan (dev)) { nm_device_do_pseudo_scan (dev); return FALSE; } g_get_current_time (&cur_time); /* Translate iwlib scan results to NM access point list */ for (tmp_ap = results->scan_head.result; tmp_ap; tmp_ap = tmp_ap->next) { /* We need at least an ESSID or a MAC address for each access point */ if (tmp_ap->b.has_essid || tmp_ap->has_ap_addr) { NMAccessPoint *nm_ap = nm_ap_new (); int percent; gboolean new = FALSE; gboolean strength_changed = FALSE; gboolean success = FALSE; /* Copy over info from scan to local structure */ /* ipw2x00 drivers fill in an essid of "" if they think the access point * is hiding its MAC address. Sigh. */ if ( !tmp_ap->b.has_essid || (tmp_ap->b.essid && !strlen (tmp_ap->b.essid)) || (tmp_ap->b.essid && !strcmp (tmp_ap->b.essid, ""))) /* Stupid ipw drivers use */ nm_ap_set_essid (nm_ap, NULL); else nm_ap_set_essid (nm_ap, tmp_ap->b.essid); if (tmp_ap->b.has_key && (tmp_ap->b.key_flags & IW_ENCODE_DISABLED)) { nm_ap_set_encrypted (nm_ap, FALSE); nm_ap_set_auth_method (nm_ap, NM_DEVICE_AUTH_METHOD_NONE); } else { nm_ap_set_encrypted (nm_ap, TRUE); nm_ap_set_auth_method (nm_ap, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM); } if (tmp_ap->has_ap_addr) nm_ap_set_address (nm_ap, (const struct ether_addr *)(tmp_ap->ap_addr.sa_data)); if (tmp_ap->b.has_mode) { NMNetworkMode mode = NETWORK_MODE_INFRA; switch (tmp_ap->b.mode) { case IW_MODE_INFRA: mode = NETWORK_MODE_INFRA; break; case IW_MODE_ADHOC: mode = NETWORK_MODE_ADHOC; break; default: mode = NETWORK_MODE_INFRA; break; } nm_ap_set_mode (nm_ap, mode); } else nm_ap_set_mode (nm_ap, NETWORK_MODE_INFRA); percent = nm_wireless_qual_to_percent (&(tmp_ap->stats.qual), (const iwqual *)(&dev->options.wireless.max_qual), (const iwqual *)(&dev->options.wireless.avg_qual)); nm_ap_set_strength (nm_ap, percent); if (tmp_ap->b.has_freq) nm_ap_set_freq (nm_ap, tmp_ap->b.freq); nm_ap_set_last_seen (nm_ap, &cur_time); /* If the AP is not broadcasting its ESSID, try to fill it in here from our * allowed list where we cache known MAC->ESSID associations. */ if (!nm_ap_get_essid (nm_ap)) nm_ap_list_copy_one_essid_by_address (nm_ap, dev->app_data->allowed_ap_list); /* Add the AP to the device's AP list */ success = nm_ap_list_merge_scanned_ap (nm_device_ap_list_get (dev), nm_ap, &new, &strength_changed); if (success) { /* Handle dbus signals that we need to broadcast when the AP is added to the list or changes * strength. */ if (new) { nm_dbus_signal_wireless_network_change (dev->app_data->dbus_connection, dev, nm_ap, NETWORK_STATUS_APPEARED, -1); list_changed = TRUE; } else if (strength_changed) { nm_dbus_signal_wireless_network_change (dev->app_data->dbus_connection, dev, nm_ap, NETWORK_STATUS_STRENGTH_CHANGED, nm_ap_get_strength (nm_ap)); } } nm_ap_unref (nm_ap); } } /* Once we have the list, copy in any relevant information from our Allowed list. */ nm_ap_list_copy_properties (nm_device_ap_list_get (dev), dev->app_data->allowed_ap_list); /* Walk the access point list and remove any access points older than 120s */ g_get_current_time (&cur_time); if (nm_device_ap_list_get (dev) && (iter = nm_ap_list_iter_new (nm_device_ap_list_get (dev)))) { NMAccessPoint *outdated_ap; GSList *outdated_list = NULL; GSList *elt; NMAccessPoint *best_ap = nm_device_get_best_ap (dev); while ((outdated_ap = nm_ap_list_iter_next (iter))) { const GTimeVal *ap_time = nm_ap_get_last_seen (outdated_ap); gboolean keep_around = FALSE; /* Don't ever get prune the AP we're currently associated with */ if ( nm_ap_get_essid (outdated_ap) && (best_ap && (nm_null_safe_strcmp (nm_ap_get_essid (best_ap), nm_ap_get_essid (outdated_ap))) == 0)) keep_around = TRUE; if (!keep_around && (ap_time->tv_sec + 120 < cur_time.tv_sec)) outdated_list = g_slist_append (outdated_list, outdated_ap); } nm_ap_list_iter_free (iter); /* nm_device_get_best_ap() refs the ap */ if (best_ap) nm_ap_unref (best_ap); /* Ok, now remove outdated ones. We have to do it after the lock * because nm_ap_list_remove_ap() locks the list too. */ for (elt = outdated_list; elt; elt = g_slist_next (elt)) { if ((outdated_ap = (NMAccessPoint *)(elt->data))) { nm_dbus_signal_wireless_network_change (dev->app_data->dbus_connection, dev, outdated_ap, NETWORK_STATUS_DISAPPEARED, -1); nm_ap_list_remove_ap (nm_device_ap_list_get (dev), outdated_ap); list_changed = TRUE; } } g_slist_free (outdated_list); } /* If the list changed, decrease our wireless scanning interval */ if (list_changed) dev->options.wireless.scan_interval = 20; else dev->options.wireless.scan_interval = MIN (60, dev->options.wireless.scan_interval + 10); return FALSE; } /* * nm_device_wireless_scan * * Get a list of access points this device can see. * */ static gboolean nm_device_wireless_scan (gpointer user_data) { NMDevice *dev = (NMDevice *)(user_data); int sk; NMWirelessScanResults *scan_results = NULL; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); /* We don't scan on test devices or devices that don't have scanning support */ if (dev->test_device || !nm_device_get_supports_wireless_scan (dev)) return FALSE; /* Just reschedule ourselves if scanning or all wireless is disabled */ if ( (dev->app_data->scanning_enabled == FALSE) || (dev->app_data->wireless_enabled == FALSE)) { dev->options.wireless.scan_interval = 10; goto reschedule; } /* Grab the scan mutex */ if (nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__)) { /* Device must be up before we can scan */ if (!nm_device_is_up (dev)) nm_device_bring_up (dev); g_usleep (G_USEC_PER_SEC); if ((sk = iw_sockets_open ()) >= 0) { int err; NMNetworkMode orig_mode = NETWORK_MODE_INFRA; double orig_freq = 0; int orig_rate = 0; orig_mode = nm_device_get_mode (dev); if (orig_mode == NETWORK_MODE_ADHOC) { orig_freq = nm_device_get_frequency (dev); orig_rate = nm_device_get_bitrate (dev); } /* Must be in infrastructure mode during scan, otherwise we don't get a full * list of scan results. Scanning doesn't work well in Ad-Hoc mode :( */ nm_device_set_mode (dev, NETWORK_MODE_INFRA); nm_device_set_frequency (dev, 0); scan_results = g_malloc0 (sizeof (NMWirelessScanResults)); err = iw_scan (sk, (char *)nm_device_get_iface (dev), WIRELESS_EXT, &(scan_results->scan_head)); if ((err == -1) && (errno == ENODATA)) { /* Card hasn't had time yet to compile full access point list. * Give it some more time and scan again. If that doesn't work * give up. */ g_usleep ((G_USEC_PER_SEC * nm_device_get_association_pause_value (dev)) / 2); err = iw_scan (sk, (char *)nm_device_get_iface (dev), WIRELESS_EXT, &(scan_results->scan_head)); if (err == -1) scan_results->scan_head.result = NULL; } else if ((err == -1) && (errno == ETIME)) syslog (LOG_ERR, "Warning: the wireless card (%s) requires too much time for scans. Its driver needs to be fixed.", nm_device_get_iface (dev)); nm_device_set_mode (dev, orig_mode); /* Only set frequency if ad-hoc mode */ if (orig_mode == NETWORK_MODE_ADHOC) { nm_device_set_frequency (dev, orig_freq); nm_device_set_bitrate (dev, orig_rate); } close (sk); } nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); } /* We run the scan processing function from the main thread, since it must deliver * messages over DBUS. Plus, that way the main thread is the only thread that has * to modify the device's access point list. */ if ((scan_results != NULL) && (scan_results->scan_head.result != NULL)) { guint scan_process_source_id = 0; GSource *scan_process_source = g_idle_source_new (); scan_results->dev = dev; g_source_set_callback (scan_process_source, nm_device_wireless_process_scan_results, scan_results, NULL); scan_process_source_id = g_source_attach (scan_process_source, dev->app_data->main_context); g_source_unref (scan_process_source); } reschedule: /* Make sure we reschedule ourselves so we keep scanning */ nm_device_wireless_schedule_scan (dev); return FALSE; } /* System config data accessors */ gboolean nm_device_config_get_use_dhcp (NMDevice *dev) { g_return_val_if_fail (dev != NULL, 0); return (dev->config_info.use_dhcp); } void nm_device_config_set_use_dhcp (NMDevice *dev, gboolean use_dhcp) { g_return_if_fail (dev != NULL); dev->config_info.use_dhcp = use_dhcp; } guint32 nm_device_config_get_ip4_address (NMDevice *dev) { g_return_val_if_fail (dev != NULL, 0); return (dev->config_info.ip4_address); } void nm_device_config_set_ip4_address (NMDevice *dev, guint32 addr) { g_return_if_fail (dev != NULL); dev->config_info.ip4_address = addr; } guint32 nm_device_config_get_ip4_gateway (NMDevice *dev) { g_return_val_if_fail (dev != NULL, 0); return (dev->config_info.ip4_gateway); } void nm_device_config_set_ip4_gateway (NMDevice *dev, guint32 gateway) { g_return_if_fail (dev != NULL); dev->config_info.ip4_gateway = gateway; } guint32 nm_device_config_get_ip4_netmask (NMDevice *dev) { g_return_val_if_fail (dev != NULL, 0); return (dev->config_info.ip4_netmask); } void nm_device_config_set_ip4_netmask (NMDevice *dev, guint32 netmask) { g_return_if_fail (dev != NULL); dev->config_info.ip4_netmask = netmask; } guint32 nm_device_config_get_ip4_broadcast (NMDevice *dev) { g_return_val_if_fail (dev != NULL, 0); return (dev->config_info.ip4_broadcast); } void nm_device_config_set_ip4_broadcast (NMDevice *dev, guint32 broadcast) { g_return_if_fail (dev != NULL); dev->config_info.ip4_broadcast = broadcast; } /* Define types for stupid headers */ typedef u_int8_t u8; typedef u_int16_t u16; typedef u_int32_t u32; typedef u_int64_t u64; /**************************************/ /* Ethtool capability detection */ /**************************************/ #include #include static gboolean supports_ethtool_carrier_detect (NMDevice *dev) { int sk; struct ifreq ifr; gboolean supports_ethtool = FALSE; struct ethtool_cmd edata; g_return_val_if_fail (dev != NULL, FALSE); if ((sk = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { syslog (LOG_ERR, "cannot open socket on interface %s for MII detect; errno=%d", nm_device_get_iface (dev), errno); return (FALSE); } strncpy (ifr.ifr_name, nm_device_get_iface (dev), sizeof(ifr.ifr_name)-1); edata.cmd = ETHTOOL_GLINK; ifr.ifr_data = (char *) &edata; if (ioctl(sk, SIOCETHTOOL, &ifr) == -1) goto out; supports_ethtool = TRUE; out: close (sk); return (supports_ethtool); } /**************************************/ /* MII capability detection */ /**************************************/ #include static int mdio_read (int sk, struct ifreq *ifr, int location) { struct mii_ioctl_data *mii; g_return_val_if_fail (sk < 0, -1); g_return_val_if_fail (ifr != NULL, -1); mii = (struct mii_ioctl_data *) &(ifr->ifr_data); mii->reg_num = location; if (ioctl (sk, SIOCGMIIREG, &ifr) < 0) return -1; return (mii->val_out); } static gboolean supports_mii_carrier_detect (NMDevice *dev) { int sk; struct ifreq ifr; int bmsr; gboolean supports_mii = FALSE; g_return_val_if_fail (dev != NULL, FALSE); if ((sk = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { syslog (LOG_ERR, "cannot open socket on interface %s for MII detect; errno=%d", nm_device_get_iface (dev), errno); return (FALSE); } strncpy (ifr.ifr_name, nm_device_get_iface (dev), sizeof(ifr.ifr_name)-1); if (ioctl(sk, SIOCGMIIPHY, &ifr) < 0) goto out; /* If we can read the BMSR register, we assume that the card supports MII link detection */ bmsr = mdio_read(sk, &ifr, MII_BMSR); supports_mii = (bmsr != -1) ? TRUE : FALSE; out: close (sk); return (supports_mii); } /****************************************/ /* End Code ripped from HAL */ /****************************************/ /****************************************/ /* Test device routes */ /****************************************/ /* * nm_device_is_test_device * */ gboolean nm_device_is_test_device (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); return (dev->test_device); }