diff --git a/ChangeLog b/ChangeLog index f1ca873703..7ff858bc80 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,42 @@ +2004-07-05 Dan Williams + + * dispatcher-daemon/NetworkManagerDispatcher.c + - A bit more descriptive state message + - Don't segfault when reading directory + + * src/NetworkManager.h + - Remove NMData desired_ap member, its now + per-device rather than global + + * src/NetworkManager.c + - Remove references to desired_ap + - Move the allowed AP list refresh stuff into a thread + + * src/NetworkManagerDevice.c + src/NetworkManagerDevice.h + - Each wireless device now has a "best ap" + - Make device activate/deactivate functions per-device + - Make wireless scanning per-device + - Add IPv4 address discover functions, stub IPv6 ones + - Move ethernet address validation functions to NetworkManagerUtils.c + - Add wireless access point accessor function + - Get/Set functions for "best ap" + + * src/NetworkManagerPolicy.c + - Move activate/deactivate stuff into NetworkManagerDevice.c, per-device + - Deal with per-device "best ap" rather than data->desired_apa + - Implement allowed access point worker thread + - Add nm_policy_essid_is_allowed() function + + * src/NetworkManagerUtils.c + src/NetworkManagerUtils.h + - Add nm_ethernet_address_is_valid() function + - Add IPv4/IPv6 address get functions + + * src/NetworkManagerWireless.c + src/NetworkManagerWireless.h + - Move scanning stuff into NetworkManagerDevice.c, per-device + 2004-06-29 Dan Williams * dispatcher-daemon/NetworkManagerDispatcher.c diff --git a/dispatcher-daemon/NetworkManagerDispatcher.c b/dispatcher-daemon/NetworkManagerDispatcher.c index 3de18ee6aa..46141db523 100644 --- a/dispatcher-daemon/NetworkManagerDispatcher.c +++ b/dispatcher-daemon/NetworkManagerDispatcher.c @@ -58,7 +58,7 @@ void nmd_execute_scripts (NMDAction action, char *iface_name) return; } - while (dir) + do { errno = 0; if ((ent = readdir (dir)) != NULL) @@ -83,9 +83,8 @@ void nmd_execute_scripts (NMDAction action, char *iface_name) system (cmd); } } - else fprintf( stderr, "d_name = %s, errno = %d\n", ent->d_name, errno); } - } + } while (ent); closedir (dir); } @@ -173,7 +172,9 @@ static DBusHandlerResult nmd_dbus_filter (DBusConnection *connection, DBusMessag { char *dev_iface_name = nmd_get_device_name (connection, dev_object_path); - fprintf (stderr, "Device %s (%s) now has state %d.\n", dev_object_path, dev_iface_name, action); + fprintf (stderr, "Device %s (%s) is now %s.\n", dev_object_path, dev_iface_name, + (action == NMD_DEVICE_NOW_INACTIVE ? "down" : + (action == NMD_DEVICE_NOW_ACTIVE ? "up" : "error"))); nmd_execute_scripts (action, dev_iface_name); diff --git a/src/NetworkManager.c b/src/NetworkManager.c index 9f7fa17d3e..6ff73d05f2 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -44,7 +44,7 @@ static GMainLoop *loop = NULL; static NMData *nm_data = NULL; gboolean debug = TRUE; static gboolean quit = FALSE; - +extern gboolean allowed_ap_worker_exit; static void nm_data_free (NMData *data); @@ -342,6 +342,11 @@ gboolean nm_link_state_monitor (gpointer user_data) nm_device_bring_up (dev); nm_device_update_link_active (dev, FALSE); + + /* Check if the device's IP address has changed + * (ie dhcp lease renew/address change) + */ + /* Implement me */ } element = g_slist_next (element); @@ -482,7 +487,6 @@ static void nm_data_free (NMData *data) nm_device_unref (data->active_device); nm_data_allowed_ap_list_free (data); - nm_ap_unref (data->desired_ap); } @@ -546,6 +550,7 @@ int main( int argc, char *argv[] ) guint policy_source; guint wireless_scan_source; gboolean become_daemon = TRUE; + GThread *allowed_ap_thread = NULL; /* Parse options */ while (1) @@ -649,6 +654,7 @@ int main( int argc, char *argv[] ) /* Initialize our list of allowed access points */ nm_policy_update_allowed_access_points (nm_data); + allowed_ap_thread = g_thread_create (nm_policy_allowed_ap_refresh_worker, nm_data, FALSE, NULL); /* Create our dbus service */ nm_data->dbus_connection = nm_dbus_init (); @@ -670,19 +676,19 @@ int main( int argc, char *argv[] ) wireless_scan_source = g_timeout_add (10000, nm_wireless_scan_monitor, nm_data); /* Watch all devices that HAL knows about for state changes */ - /* Don't need this now because our polling function takes care of it and - * HAL drops the ball for some cards. - */ hal_device_property_watch_all (nm_data->hal_ctx); - /* Since we do what dhclient does, and do it better, kill dhclient */ + /* We run dhclient when we need to, and we don't want any stray ones + * lying around upon launch. + */ system ("killall dhclient"); - /* Run the main loop, all events processed by callbacks from libhal. */ + /* Wheeee!!! */ loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); - /* Kill the watch functions */ + /* Kill the watch functions & threads */ + allowed_ap_worker_exit = TRUE; g_source_remove (link_source); g_source_remove (policy_source); g_source_remove (wireless_scan_source); diff --git a/src/NetworkManager.h b/src/NetworkManager.h index f7b6fea710..33868e527e 100644 --- a/src/NetworkManager.h +++ b/src/NetworkManager.h @@ -37,7 +37,6 @@ struct NMData GMutex *state_modified_mutex; GSList *allowed_ap_list; GMutex *allowed_ap_list_mutex; - NMAccessPoint *desired_ap; DBusConnection *dbus_connection; }; diff --git a/src/NetworkManagerDevice.c b/src/NetworkManagerDevice.c index 2da37c3aee..f37f1f7a5d 100644 --- a/src/NetworkManagerDevice.c +++ b/src/NetworkManagerDevice.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include "NetworkManager.h" #include "NetworkManagerDevice.h" @@ -165,6 +163,7 @@ typedef struct NMDeviceWirelessOptions gboolean supports_wireless_scan; GMutex *ap_list_mutex; GSList *ap_list; + NMAccessPoint *best_ap; } NMDeviceWirelessOptions; typedef struct NMDeviceWiredOptions @@ -188,6 +187,8 @@ struct NMDevice gchar *iface; NMIfaceType iface_type; gboolean link_active; + guint32 ip_address; + /* FIXME: ipv6 address too */ NMDeviceOptions dev_options; }; @@ -263,6 +264,7 @@ void nm_device_unref (NMDevice *dev) { g_free (dev->dev_options.wireless.cur_essid); g_mutex_free (dev->dev_options.wireless.ap_list_mutex); + nm_ap_unref (dev->dev_options.wireless.best_ap); } dev->udi = NULL; @@ -350,11 +352,11 @@ gboolean nm_device_get_supports_wireless_scan (NMDevice *dev) * Updates the link state for a particular device. * */ -gboolean nm_device_update_link_active (NMDevice *dev, gboolean check_mii) +void nm_device_update_link_active (NMDevice *dev, gboolean check_mii) { gboolean link_active = FALSE; - g_return_val_if_fail (dev != NULL, FALSE); + g_return_if_fail (dev != NULL); /* FIXME * For wireless cards, the best indicator of a "link" at this time @@ -371,23 +373,7 @@ gboolean nm_device_update_link_active (NMDevice *dev, gboolean check_mii) iwlib_socket = iw_sockets_open (); if (iw_get_ext (iwlib_socket, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0) - { - struct ether_addr invalid_addr1; - struct ether_addr invalid_addr2; - struct ether_addr invalid_addr3; - struct ether_addr ap_addr; - - /* Compare the AP address the card has with invalid ethernet MAC addresses. - */ - memcpy (&ap_addr, &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr)); - memset (&invalid_addr1, 0xFF, sizeof(struct ether_addr)); - memset (&invalid_addr2, 0x00, sizeof(struct ether_addr)); - memset (&invalid_addr2, 0x44, sizeof(struct ether_addr)); - if ( (memcmp(&ap_addr, &invalid_addr1, sizeof(struct ether_addr)) != 0) - && (memcmp(&ap_addr, &invalid_addr2, sizeof(struct ether_addr)) != 0) - && (memcmp(&ap_addr, &invalid_addr3, sizeof(struct ether_addr)) != 0)) - link_active = TRUE; - } + link_active = nm_ethernet_address_is_valid (&(wrq.u.ap_addr.sa_data)); close (iwlib_socket); break; } @@ -413,7 +399,6 @@ gboolean nm_device_update_link_active (NMDevice *dev, gboolean check_mii) nm_device_set_link_active (dev, link_active); nm_data_set_state_modified (nm_get_global_data(), TRUE); } - return (link_active); } @@ -499,6 +484,30 @@ void nm_device_set_essid (NMDevice *dev, const char *essid) } +/* + * 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); + + /* Do we have a valid MAC address? */ + 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_wep_key * @@ -564,6 +573,46 @@ void nm_device_set_wep_key (NMDevice *dev, const char *wep_key) } +/* + * nm_device_get_ip4_address + * + * Get a device's IPv4 address + * + */ +guint32 nm_device_get_ip4_address(NMDevice *dev) +{ + struct ifreq req; + int socket; + + g_return_val_if_fail (dev != NULL, 0); + g_return_val_if_fail (nm_device_get_iface (dev) != NULL, 0); + + socket = nm_get_network_control_socket (); + if (socket < 0) + return (0); + + strncpy ((char *)(&req.ifr_name), nm_device_get_iface (dev), 16); // 16 == IF_NAMESIZE + if (ioctl (socket, SIOCGIFADDR, &req) != 0) + return (0); + + return (((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr); +} + + +/* + * nm_device_get_ip6_address + * + * Get a device's IPv6 address + * + */ +void nm_device_get_ip6_address(NMDevice *dev) +{ + /* FIXME + * Implement + */ +} + + /* * nm_device_set_up_down * @@ -651,6 +700,158 @@ gboolean nm_device_is_up (NMDevice *dev) } +/* + * nm_device_activate + * + * Activate the device, bringing it up and getting it an + * IP address. + * + */ +gboolean nm_device_activate (NMDevice *dev) +{ + unsigned char buf[500]; + gboolean success = FALSE; + unsigned char *iface; + unsigned char hostname[500] = "\0"; + int host_err; + NMData *data = nm_get_global_data (); + int dhclient_err; + FILE *pidfile; + + g_return_val_if_fail (dev != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + /* If its a wireless device, set the ESSID and WEP key */ + if (nm_device_get_iface_type (dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET) + { + NMAccessPoint *ap = nm_device_get_best_ap (dev); + + /* If the card is just inserted, we may not have had a chance to scan yet */ + if (!ap) + { + nm_device_do_wireless_scan (dev); + ap = nm_device_get_best_ap (dev); + } + + /* If there is a desired AP to connect to, use that essid and possible WEP key */ + if (nm_ap_get_essid (ap) != NULL) + { + nm_device_bring_down (dev); + + nm_device_set_essid (dev, nm_ap_get_essid (ap)); + + /* Disable WEP */ + nm_device_set_wep_key (dev, NULL); + if (nm_ap_get_wep_key (ap)) + nm_device_set_wep_key (dev, nm_ap_get_wep_key (ap)); + } + NM_DEBUG_PRINT_2 ("nm_device_activate(%s) using essid '%s'\n", nm_device_get_iface (dev), nm_ap_get_essid (ap)); + } + + /* Bring the device up */ + if (!nm_device_is_up (dev)); + nm_device_bring_up (dev); + + /* Kill the old default route */ + snprintf (buf, 500, "/sbin/ip route del default"); + system (buf); + + /* Find and kill the previous dhclient process for this interface */ + iface = nm_device_get_iface (dev); + snprintf (buf, 500, "/var/run/dhclient-%s.pid", iface); + pidfile = fopen (buf, "r"); + if (pidfile) + { + int len; + unsigned char s_pid[20]; + pid_t n_pid = -1; + + memset (s_pid, 0, 20); + fgets (s_pid, 19, pidfile); + len = strnlen (s_pid, 20); + fclose (pidfile); + + n_pid = atoi (s_pid); + if (n_pid > 0) + kill (n_pid, 9); + } + + snprintf (buf, 500, "/sbin/dhclient -1 -q -lf /var/lib/dhcp/dhclient-%s.leases -pf /var/run/dhclient-%s.pid -cf /etc/dhclient-%s.conf %s\n", + iface, iface, iface, iface); + dhclient_err = system (buf); + if (dhclient_err != 0) + { + /* Interfaces cannot be down if they are the active interface, + * otherwise we cannot use them for scanning or link detection. + * If dhclient doesn't get a DHCP address, it will take the interface + * down, so we reactivate it here. + */ + if (nm_device_get_iface_type (dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET) + { + nm_device_set_essid (dev, ""); + nm_device_set_wep_key (dev, NULL); + } + + nm_device_bring_up (dev); + success = FALSE; + } + + /* Set the hostname back to what it was before so that X11 doesn't + * puke when the hostname changes, and so users can actually launch stuff. + */ + if (host_err >= 0) + sethostname (hostname, strlen (hostname)); + + /* Restart the nameservice caching daemon to make apps aware of new DNS servers */ + snprintf (buf, 500, "/sbin/service nscd restart"); + system (buf); + + nm_dbus_signal_device_now_active (data->dbus_connection, dev); + + return (success); +} + + +/* + * nm_device_deactivate + * + * Remove a device's routing table entries and IP address. + * + */ +gboolean nm_device_deactivate (NMDevice *dev) +{ + unsigned char buf[500]; + unsigned char *iface; + gboolean success = FALSE; + NMData *data = nm_get_global_data (); + + g_return_val_if_fail (dev != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + iface = nm_device_get_iface (dev); + + /* Take out any entries in the routing table and any IP address the old interface + * had. + */ + if (iface && strlen (iface)) + { + /* Remove routing table entries */ + snprintf (buf, 500, "/sbin/ip route flush dev %s", iface); + system (buf); + + /* Remove ip address */ + snprintf (buf, 500, "/sbin/ip address flush dev %s", iface); + system (buf); + + success = TRUE; + } + + nm_dbus_signal_device_no_longer_active (data->dbus_connection, dev); + + return (success); +} + + /* * nm_device_ap_list_add * @@ -712,7 +913,7 @@ void nm_device_ap_list_clear (NMDevice *dev) /* - * nm_device_ap_list_get_copy + * nm_device_ap_list_get_ap * * Copy the list of ESSIDs * @@ -751,6 +952,255 @@ NMAccessPoint *nm_device_ap_list_get_ap (NMDevice *dev, int index) } +/* + * Get/Set functions for "best" access point + * + */ +NMAccessPoint *nm_device_get_best_ap (NMDevice *dev) +{ + g_return_val_if_fail (dev != NULL, NULL); + g_return_val_if_fail (nm_device_get_iface_type (dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET, NULL); + + return (dev->dev_options.wireless.best_ap); +} + +void nm_device_set_best_ap (NMDevice *dev, NMAccessPoint *ap) +{ + g_return_if_fail (dev != NULL); + g_return_if_fail (ap != NULL); + g_return_if_fail (nm_device_get_iface_type (dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET); + + if (dev->dev_options.wireless.best_ap) + nm_ap_unref (dev->dev_options.wireless.best_ap); + + nm_ap_ref (ap); + dev->dev_options.wireless.best_ap = ap; +} + + +/* + * nm_device_do_normal_scan + * + * Scan for access points on cards that support wireless scanning. + * + */ +static void nm_device_do_normal_scan (NMDevice *dev) +{ + int iwlib_socket; + + g_return_if_fail (dev != NULL); + + /* Device must be up before we can scan */ + if (!nm_device_is_up (dev)) + nm_device_bring_up (dev); + + iwlib_socket = iw_sockets_open (); + if (iwlib_socket >= 0) + { + wireless_scan_head scan_results = { NULL, 0 }; + wireless_scan *tmp_ap; + int err; + NMAccessPoint *highest_priority_ap = NULL; + int highest_priority = NM_AP_PRIORITY_WORST; + + /* Clear out the device's ap list */ + nm_device_ap_list_clear (dev); + + err = iw_scan (iwlib_socket, nm_device_get_iface (dev), WIRELESS_EXT, &scan_results); + + /* Iterate over scan results and pick a "most" preferred access point. */ + tmp_ap = scan_results.result; + while (tmp_ap) + { + /* Blank essids usually indicate an AP that is not broadcasting its essid, + * but since its not broadcasting the essid, we cannot use that ap yet. + */ + if (tmp_ap->b.has_essid && tmp_ap->b.essid_on && (strlen (tmp_ap->b.essid) > 0)) + { + NMAccessPoint *nm_ap = nm_ap_new (); + + /* Copy over info from scan to local structure */ + nm_ap_set_essid (nm_ap, tmp_ap->b.essid); + + if (tmp_ap->has_ap_addr) + { + char buf[20]; + + memset (&buf[0], 0, 20); + iw_ether_ntop((const struct ether_addr *) (tmp_ap->ap_addr.sa_data), &buf[0]); + nm_ap_set_address (nm_ap, buf); + } + + nm_ap_set_quality (nm_ap, tmp_ap->stats.qual.qual); + + if (tmp_ap->b.has_freq) + nm_ap_set_freq (nm_ap, tmp_ap->b.freq); + + /* Add the AP to the device's AP list, no matter if its allowed or not */ + nm_device_ap_list_add (dev, nm_ap); + + if (nm_wireless_is_most_prefered_ap (nm_ap, &highest_priority)) + { + if (highest_priority_ap) + nm_ap_unref (highest_priority_ap); + + highest_priority_ap = nm_ap_new_from_ap (nm_ap); + } + nm_ap_unref (nm_ap); + } + tmp_ap = tmp_ap->next; + } + nm_dispose_scan_results (scan_results.result); + + /* If we have the "most" preferred access point, and its different than the current + * access point, switch to it during the next cycle. + */ + if ( highest_priority_ap + && (!nm_device_get_best_ap (dev) || (nm_null_safe_strcmp (nm_device_get_essid (dev), nm_ap_get_essid (highest_priority_ap)) != 0))) + { + nm_device_set_best_ap (dev, nm_ap_new_from_ap (highest_priority_ap)); + nm_data_set_state_modified (nm_get_global_data (), TRUE); + + nm_ap_unref (highest_priority_ap); + } + close (iwlib_socket); + } + else + NM_DEBUG_PRINT_1 ("nm_device_do_normal_scan() could not get a control socket for the wireless card %s.\n", nm_device_get_iface (dev) ); +} + + +/* + * 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) +{ + NMData *data = nm_get_global_data (); + + g_return_if_fail (data != NULL); + g_return_if_fail (dev != NULL); + + nm_device_ref (dev); + +fprintf (stderr, "Begining try all\n" ); + /* Acquire allowed AP list mutex, silently fail if we cannot */ + if (nm_try_acquire_mutex (data->allowed_ap_list_mutex, __FUNCTION__)) + { + GSList *element = data->allowed_ap_list; + + /* Turn off the essid so we can tell if its changed when + * we set it below. + */ + nm_device_set_essid (dev, ""); + + while (element) + { + NMAccessPoint *ap = (NMAccessPoint *)(element->data); + + /* Attempt to associate with this access point */ + if (ap) + { + struct iwreq wrq; + int iwlib_socket; + gboolean valid = FALSE; + struct ether_addr save_ap_addr; + struct ether_addr cur_ap_addr; + +fprintf( stderr, "Looking at AP %s\n", nm_ap_get_essid (ap) ); + + if (!nm_device_is_up (dev)); + nm_device_bring_up (dev); + + /* Save the MAC address */ + nm_device_get_ap_address (dev, &save_ap_addr); + + nm_device_set_essid (dev, nm_ap_get_essid (ap)); + if (nm_ap_get_wep_key (ap)) + nm_device_set_wep_key (dev, nm_ap_get_wep_key (ap)); + else + nm_device_set_wep_key (dev, NULL); + + /* Wait a bit for association */ + g_usleep (G_USEC_PER_SEC * 2); + + /* 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); +fprintf( stderr, "Current ap ether is: %s save = %s\n", iw_ether_ntoa (&cur_ap_addr), iw_ether_ntoa(&save_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; + + /* FIXME + * We should probably lock access to data->desired_ap + */ + if (valid) + { +fprintf( stderr, "AP %s looks good, setting to desired\n", nm_ap_get_essid (ap)); + nm_device_set_best_ap (dev, nm_ap_new_from_ap (ap)); + nm_data_set_state_modified (nm_get_global_data (), TRUE); + break; + } + } + element = g_slist_next (element); + } + + nm_unlock_mutex (data->allowed_ap_list_mutex, __FUNCTION__); +fprintf( stderr, "Try all done.\n"); + } + + nm_device_unref (dev); +} + + +/* + * nm_device_do_wireless_scan + * + * Get a list of access points this device can see. + * + */ +void nm_device_do_wireless_scan (NMDevice *dev) +{ + NMData *data = nm_get_global_data (); + + g_return_if_fail (data != NULL); + g_return_if_fail (dev != NULL); + g_return_if_fail (nm_device_get_iface_type (dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET); + + if (nm_device_get_supports_wireless_scan (dev)) + nm_device_do_normal_scan (dev); + else + { + struct ether_addr ap_addr; + + /* We can't pseudo-scan without switching APs, therefore + * if the card has a valid access point and its an allowed + * access point, don't pseudo-scan for others. + */ + nm_device_get_ap_address (dev, &ap_addr); + if ( !nm_ethernet_address_is_valid (&ap_addr) + || !nm_policy_essid_is_allowed (data, nm_device_get_essid (dev)) + || !nm_device_get_best_ap (dev)) + { + nm_device_do_pseudo_scan (dev); + } + } +} + + /****************************************/ /* Code ripped from HAL */ /* minor modifications made for */ diff --git a/src/NetworkManagerDevice.h b/src/NetworkManagerDevice.h index 86ade2ac7a..2d40818c91 100644 --- a/src/NetworkManagerDevice.h +++ b/src/NetworkManagerDevice.h @@ -23,6 +23,7 @@ #define NETWORK_MANAGER_DEVICE_H #include "NetworkManager.h" +#include /* * Types of NetworkManager devices @@ -53,18 +54,29 @@ NMIfaceType nm_device_get_iface_type (NMDevice *dev); gboolean nm_device_get_link_active (NMDevice *dev); void nm_device_set_link_active (NMDevice *dev, const gboolean active); -gboolean nm_device_update_link_active (NMDevice *dev, gboolean check_mii); - -gboolean nm_device_check_link_status (NMDevice *dev); +void nm_device_update_link_active (NMDevice *dev, gboolean check_mii); char * nm_device_get_essid (NMDevice *dev); void nm_device_set_essid (NMDevice *dev, const char *essid); +void nm_device_get_ap_address (NMDevice *dev, struct ether_addr *addr); + +guint32 nm_device_get_ip4_address (NMDevice *dev); +void nm_device_update_ip4_address (NMDevice *dev); + +void nm_device_get_ip6_address (NMDevice *dev); + gboolean nm_device_get_supports_wireless_scan (NMDevice *dev); +void nm_device_do_wireless_scan (NMDevice *dev); +NMAccessPoint *nm_device_get_best_ap (NMDevice *dev); +void nm_device_set_best_ap (NMDevice *dev, NMAccessPoint *ap); /* There is no function to get the WEP key since that's a slight security risk */ void nm_device_set_wep_key (NMDevice *dev, const char *wep_key); +gboolean nm_device_deactivate (NMDevice *dev); +gboolean nm_device_activate (NMDevice *dev); + void nm_device_bring_up (NMDevice *dev); void nm_device_bring_down (NMDevice *dev); gboolean nm_device_is_up (NMDevice *dev); diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c index 1c6dbf2285..2edf509f8f 100644 --- a/src/NetworkManagerPolicy.c +++ b/src/NetworkManagerPolicy.c @@ -1,3 +1,4 @@ + /* NetworkManager -- Network link manager * * Dan Williams @@ -26,137 +27,16 @@ #include #include #include +#include #include "NetworkManagerPolicy.h" #include "NetworkManagerUtils.h" #include "NetworkManagerAP.h" +gboolean allowed_ap_worker_exit = FALSE; extern gboolean debug; -/* - * nm_policy_activate_device - * - * Performs interface switching and related networking goo. - * - */ -static void nm_policy_switch_device (NMData *data, NMDevice *switch_to_dev, NMDevice *old_dev) -{ - unsigned char buf[500]; - unsigned char hostname[500] = "\0"; - const unsigned char *new_iface; - int host_err; - int dhclient_err; - FILE *pidfile; - - g_return_if_fail (data != NULL); - g_return_if_fail (switch_to_dev != NULL); - g_return_if_fail (nm_device_get_iface (switch_to_dev)); - g_return_if_fail (strlen (nm_device_get_iface (switch_to_dev)) >= 0); - - /* If its a wireless device, set the ESSID and WEP key */ - if (nm_device_get_iface_type (switch_to_dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET) - { - /* If there is a desired AP to connect to, use that essid and possible WEP key */ - if ( data->desired_ap - && (nm_ap_get_essid (data->desired_ap) != NULL)) - { - nm_device_bring_down (switch_to_dev); - - nm_device_set_essid (switch_to_dev, nm_ap_get_essid (data->desired_ap)); - - /* Disable WEP */ - nm_device_set_wep_key (switch_to_dev, NULL); - if (nm_ap_get_wep_key (data->desired_ap)) - nm_device_set_wep_key (switch_to_dev, nm_ap_get_wep_key (data->desired_ap)); - } - else - { - /* If the card isn't up, bring it up so that we can scan. We may be too early here to have - * gotten all the scanning results, and therefore have no desired_ap. Just wait. - */ - if (!nm_device_is_up (switch_to_dev)); - nm_device_bring_up (switch_to_dev); - - NM_DEBUG_PRINT ("nm_policy_activate_interface() could not find a desired AP. Card doesn't support scanning?\n"); - return; /* Don't associate with any non-allowed access points */ - } - - NM_DEBUG_PRINT_1 ("nm_policy_activate_interface() using essid '%s'\n", nm_ap_get_essid (data->desired_ap)); - } - - host_err = gethostname (hostname, 500); - - /* Take out any entries in the routing table and any IP address the old interface - * had. - */ - if (old_dev && strlen (nm_device_get_iface (old_dev))) - { - /* Remove routing table entries */ - snprintf (buf, 500, "/sbin/ip route flush dev %s", nm_device_get_iface (old_dev)); - system (buf); - - /* Remove ip address */ - snprintf (buf, 500, "/sbin/ip address flush dev %s", nm_device_get_iface (old_dev)); - system (buf); - } - - /* Bring the device up */ - if (!nm_device_is_up (switch_to_dev)); - nm_device_bring_up (switch_to_dev); - - /* Kill the old default route */ - snprintf (buf, 500, "/sbin/ip route del default"); - system (buf); - - /* Find and kill the previous dhclient process for this interface */ - new_iface = nm_device_get_iface (switch_to_dev); - snprintf (buf, 500, "/var/run/dhclient-%s.pid", new_iface); - pidfile = fopen (buf, "r"); - if (pidfile) - { - int len; - unsigned char s_pid[20]; - pid_t n_pid = -1; - - memset (s_pid, 0, 20); - fgets (s_pid, 19, pidfile); - len = strnlen (s_pid, 20); - fclose (pidfile); - - n_pid = atoi (s_pid); - if (n_pid > 0) - kill (n_pid, 9); - } - - snprintf (buf, 500, "/sbin/dhclient -1 -q -lf /var/lib/dhcp/dhclient-%s.leases -pf /var/run/dhclient-%s.pid -cf /etc/dhclient-%s.conf %s\n", - new_iface, new_iface, new_iface, new_iface); - dhclient_err = system (buf); - if (dhclient_err != 0) - { - /* Wireless devices cannot be down if they are the active interface, - * otherwise we cannot use them for scanning. - */ - if (nm_device_get_iface_type (switch_to_dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET) - { - nm_device_set_essid (switch_to_dev, ""); - nm_device_set_wep_key (switch_to_dev, NULL); - nm_device_bring_up (switch_to_dev); - } - } - - /* Set the hostname back to what it was before so that X11 doesn't - * puke when the hostname changes, so that users can actually launch stuff. - */ - if (host_err >= 0) - sethostname (hostname, strlen (hostname)); - - /* Restart the nameservice caching daemon to make apps aware of new DNS servers */ - snprintf (buf, 500, "/sbin/service nscd restart"); - system (buf); -} - - /* * nm_state_modification_monitor * @@ -276,13 +156,11 @@ gboolean nm_state_modification_monitor (gpointer user_data) */ if (nm_device_get_iface_type (highest_priority_dev) == NM_IFACE_TYPE_WIRELESS_ETHERNET) { - /* If we don't yet have a desired AP, attempt to get one. */ - if (!data->desired_ap) - nm_wireless_do_scan (data, highest_priority_dev); + NMAccessPoint *ap = nm_device_get_best_ap (highest_priority_dev); - if ( data->desired_ap - && (nm_null_safe_strcmp (nm_device_get_essid (highest_priority_dev), nm_ap_get_essid (data->desired_ap)) != 0) - && (strlen (nm_ap_get_essid (data->desired_ap)) > 0)) + if ( ap + && (nm_null_safe_strcmp (nm_device_get_essid (highest_priority_dev), nm_ap_get_essid (ap)) != 0) + && (strlen (nm_ap_get_essid (ap)) > 0)) essid_change_needed = TRUE; } @@ -302,9 +180,7 @@ gboolean nm_state_modification_monitor (gpointer user_data) * after a main loop iteration, we make a much more complicated state machine. */ if (data->active_device) - nm_dbus_signal_device_no_longer_active (data->dbus_connection, data->active_device); - - nm_policy_switch_device (data, highest_priority_dev, data->active_device); + nm_device_deactivate (data->active_device); if (data->active_device) nm_device_unref (data->active_device); @@ -312,7 +188,7 @@ gboolean nm_state_modification_monitor (gpointer user_data) data->active_device = highest_priority_dev; nm_device_ref (data->active_device); - nm_dbus_signal_device_now_active (data->dbus_connection, data->active_device); + nm_device_activate (data->active_device); NM_DEBUG_PRINT ("**** Switched.\n"); } @@ -327,6 +203,41 @@ gboolean nm_state_modification_monitor (gpointer user_data) } +/* + * nm_policy_allowed_ap_refresh_worker + * + * Worker thread function to periodically refresh the allowed + * access point list with updated data. + * + */ +gpointer nm_policy_allowed_ap_refresh_worker (gpointer user_data) +{ + NMData *data = (NMData *)(user_data); + struct timeval timeout; + + g_return_if_fail (data != NULL); + + /* Simply loop and every 20s update the available allowed ap data */ + while (!allowed_ap_worker_exit) + { + int err; + + timeout.tv_sec = 20; + timeout.tv_usec = 0; + + /* Wait, but don't execute the update if select () returned an error, + * since it may have immediately returned, so that we don't hammer + * GConf (or the hard drive). + */ + err = select (0, NULL, NULL, NULL, &timeout); + if (err >= 0) + nm_policy_update_allowed_access_points (data); + } + + g_thread_exit (0); +} + + /* * nm_policy_update_allowed_access_points * @@ -341,7 +252,7 @@ void nm_policy_update_allowed_access_points (NMData *data) g_return_if_fail (data != NULL); - if (nm_try_acquire_mutex (data->allowed_ap_list_mutex, NULL)) + if (nm_try_acquire_mutex (data->allowed_ap_list_mutex, __FUNCTION__)) { ap_file = fopen (NM_ALLOWED_AP_FILE, "r"); if (ap_file) @@ -425,11 +336,49 @@ void nm_policy_update_allowed_access_points (NMData *data) fclose (ap_file); } else - NM_DEBUG_PRINT_1( "nm_policy_update_allowed_access_points() could not open and lock allowed ap list file %s. errno %d\n", NM_ALLOWED_AP_FILE ); + NM_DEBUG_PRINT_2( "nm_policy_update_allowed_access_points() could not open allowed ap list file %s. errno %d\n", NM_ALLOWED_AP_FILE, errno ); - nm_unlock_mutex (data->allowed_ap_list_mutex, NULL); + nm_unlock_mutex (data->allowed_ap_list_mutex, __FUNCTION__); } else NM_DEBUG_PRINT( "nm_policy_update_allowed_access_points() could not lock allowed ap list mutex\n" ); } + +/* + * nm_policy_essid_is_allowed + * + * Searches for a specific essid in the list of allowed access points. + */ +gboolean nm_policy_essid_is_allowed (NMData *data, const unsigned char *essid) +{ + gboolean allowed = FALSE; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (essid != NULL, FALSE); + + if (strlen (essid) <= 0) + return FALSE; + + /* Acquire allowed AP list mutex, silently fail if we cannot */ + if (nm_try_acquire_mutex (data->allowed_ap_list_mutex, __FUNCTION__)) + { + GSList *element = data->allowed_ap_list; + + while (element) + { + NMAccessPoint *ap = (NMAccessPoint *)(element->data); + + if (ap && (nm_null_safe_strcmp (nm_ap_get_essid (ap), essid) == 0)) + { + allowed = TRUE; + break; + } + element = g_slist_next (element); + } + + nm_unlock_mutex (data->allowed_ap_list_mutex, __FUNCTION__); + } + + return (allowed); +} diff --git a/src/NetworkManagerPolicy.h b/src/NetworkManagerPolicy.h index 5fd5f8533b..bd99d0a99e 100644 --- a/src/NetworkManagerPolicy.h +++ b/src/NetworkManagerPolicy.h @@ -25,8 +25,12 @@ #include "NetworkManager.h" #include "NetworkManagerDevice.h" -gboolean nm_state_modification_monitor (gpointer user_data); +gboolean nm_state_modification_monitor (gpointer user_data); -void nm_policy_update_allowed_access_points (NMData *data); +void nm_policy_update_allowed_access_points (NMData *data); + +gboolean nm_policy_essid_is_allowed (NMData *data, const unsigned char *essid); + +gpointer nm_policy_allowed_ap_refresh_worker (gpointer user_data); #endif diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 2d80e862f5..bcd3d62b39 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "NetworkManager.h" #include "NetworkManagerUtils.h" @@ -126,6 +127,30 @@ int nm_get_network_control_socket (void) } +/* + * nm_ethernet_address_is_valid + * + * Compares an ethernet address against known invalid addresses. + * + */ +gboolean nm_ethernet_address_is_valid (struct ether_addr *test_addr) +{ + gboolean valid = FALSE; + struct ether_addr invalid_addr1 = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }; + struct ether_addr invalid_addr2 = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; + struct ether_addr invalid_addr3 = { {0x44, 0x44, 0x44, 0x44, 0x44, 0x44} }; + + g_return_if_fail (test_addr != NULL); + + /* Compare the AP address the card has with invalid ethernet MAC addresses. */ + if ( (memcmp(test_addr, &invalid_addr1, sizeof(struct ether_addr)) != 0) + && (memcmp(test_addr, &invalid_addr2, sizeof(struct ether_addr)) != 0) + && (memcmp(test_addr, &invalid_addr3, sizeof(struct ether_addr)) != 0)) + valid = TRUE; + + return (valid); +} + /* * nm_dispose_scan_results @@ -145,3 +170,43 @@ void nm_dispose_scan_results (wireless_scan *result_list) free (tmp2); } } + + +/* + * nm_get_ip4_address_for_device + * + * Get a device's IPv4 address + * + */ +guint32 nm_get_ip4_address_for_device(NMDevice *dev) +{ + struct ifreq req; + int socket; + + g_return_val_if_fail (dev != NULL, 0); + g_return_val_if_fail (nm_device_get_iface (dev) != NULL, 0); + + socket = nm_get_network_control_socket (); + if (socket < 0) + return (0); + + strncpy ((char *)(&req.ifr_name), nm_device_get_iface (dev), 16); // 16 == IF_NAMESIZE + if (ioctl (socket, SIOCGIFADDR, &req) != 0) + return (0); + + return (((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr); +} + + +/* + * nm_get_ip6_address_for_device + * + * Get a device's IPv6 address + * + */ +void nm_get_ip6_address_for_device(NMDevice *dev) +{ + /* FIXME + * Implement + */ +} diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 07cef689f4..1fe55d3e27 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -24,9 +24,11 @@ #include #include +#include #include #include "NetworkManager.h" +#include "NetworkManagerDevice.h" #define NM_DEBUG_PRINT( s ) if (debug) fprintf( stderr, s ); #define NM_DEBUG_PRINT_1( s, a ) if (debug) fprintf( stderr, s, a ); @@ -42,6 +44,11 @@ int nm_null_safe_strcmp (const char *s1, const char *s2); int nm_get_network_control_socket (void); +gboolean nm_ethernet_address_is_valid (struct ether_addr *test_addr); + void nm_dispose_scan_results (wireless_scan *result_list); +guint32 nm_get_ipv4_address_for_device (NMDevice *dev); +void nm_get_ipv6_address_for_device (NMDevice *dev); + #endif diff --git a/src/NetworkManagerWireless.c b/src/NetworkManagerWireless.c index 52f2690e48..a4bfce8c7e 100644 --- a/src/NetworkManagerWireless.c +++ b/src/NetworkManagerWireless.c @@ -37,7 +37,7 @@ extern gboolean debug; * both allowed _and_ has a better priority than highest_priority. * */ -static gboolean nm_wireless_is_most_prefered_ap (NMAccessPoint *ap, int *highest_priority) +gboolean nm_wireless_is_most_prefered_ap (NMAccessPoint *ap, int *highest_priority) { NMData *data = nm_get_global_data (); GSList *element; @@ -77,108 +77,6 @@ static gboolean nm_wireless_is_most_prefered_ap (NMAccessPoint *ap, int *highest } -/* - * nm_wireless_do_scan - * - * Runs the actual scan fore access points. - * - */ -void nm_wireless_do_scan (NMData *data, NMDevice *dev) -{ - int iwlib_socket; - - g_return_if_fail (data != NULL); - g_return_if_fail (dev != NULL); - - if (nm_device_get_iface_type (dev) != NM_IFACE_TYPE_WIRELESS_ETHERNET) - return; - - if (nm_device_get_supports_wireless_scan (dev) == FALSE) - return; - - /* Device must be up before we can scan */ - if (!nm_device_is_up (dev)) - nm_device_bring_up (dev); - - iwlib_socket = iw_sockets_open (); - if (iwlib_socket >= 0) - { - wireless_scan_head scan_results = { NULL, 0 }; - wireless_scan *tmp_ap; - int err; - NMAccessPoint *highest_priority_ap = NULL; - int highest_priority = NM_AP_PRIORITY_WORST; - - /* Clear out the device's ap list */ - nm_device_ap_list_clear (dev); - - err = iw_scan (iwlib_socket, nm_device_get_iface (dev), WIRELESS_EXT, &scan_results); - - /* Iterate over scan results and pick a "most" preferred access point. */ - tmp_ap = scan_results.result; - while (tmp_ap) - { - /* Blank essids usually indicate an AP that is not broadcasting its essid, - * but since its not broadcasting the essid, we cannot use that ap yet. - */ - if (tmp_ap->b.has_essid && tmp_ap->b.essid_on && (strlen (tmp_ap->b.essid) > 0)) - { - NMAccessPoint *nm_ap = nm_ap_new (); - - /* Copy over info from scan to local structure */ - nm_ap_set_essid (nm_ap, tmp_ap->b.essid); - - if (tmp_ap->has_ap_addr) - { - char buf[20]; - - memset (&buf[0], 0, 20); - iw_ether_ntop((const struct ether_addr *) (tmp_ap->ap_addr.sa_data), &buf[0]); - nm_ap_set_address (nm_ap, buf); - } - - nm_ap_set_quality (nm_ap, tmp_ap->stats.qual.qual); - - if (tmp_ap->b.has_freq) - nm_ap_set_freq (nm_ap, tmp_ap->b.freq); - - /* Add the AP to the device's AP list, no matter if its allowed or not */ - nm_device_ap_list_add (dev, nm_ap); - - if (nm_wireless_is_most_prefered_ap (nm_ap, &highest_priority)) - { - if (highest_priority_ap) - nm_ap_unref (highest_priority_ap); - - highest_priority_ap = nm_ap_new_from_ap (nm_ap); - } - nm_ap_unref (nm_ap); - } - tmp_ap = tmp_ap->next; - } - nm_dispose_scan_results (scan_results.result); - - /* If we have the "most" preferred access point, and its different than the current - * access point, switch to it during the next cycle. - */ - if ( highest_priority_ap - && (!data->desired_ap || (nm_null_safe_strcmp (nm_device_get_essid (dev), nm_ap_get_essid (highest_priority_ap)) != 0))) - { - if (data->desired_ap) - nm_ap_unref (data->desired_ap); - - data->desired_ap = nm_ap_new_from_ap (highest_priority_ap); - data->state_modified = TRUE; - - nm_ap_unref (highest_priority_ap); - } - close (iwlib_socket); - } - else - NM_DEBUG_PRINT ("nm_wireless_do_scan() could not get a control socket for the wireless card.\n" ); -} - - /* * nm_wireless_scan_monitor * @@ -188,23 +86,19 @@ void nm_wireless_do_scan (NMData *data, NMDevice *dev) gboolean nm_wireless_scan_monitor (gpointer user_data) { NMData *data = (NMData *)user_data; - GSList *element; - NMDevice *dev; g_return_val_if_fail (data != NULL, TRUE); + if (!data->active_device) return (TRUE); - /* Grab a current list of allowed access points */ - nm_policy_update_allowed_access_points (data); - - /* Attempt to acquire mutex for device list iteration. + /* Attempt to acquire mutex so that data->active_device sticks around. * If the acquire fails, just ignore the scan completely. */ if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) { if (data->active_device && (nm_device_get_iface_type (data->active_device) == NM_IFACE_TYPE_WIRELESS_ETHERNET)) - nm_wireless_do_scan (data, data->active_device); + nm_device_do_wireless_scan (data->active_device); nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); } diff --git a/src/NetworkManagerWireless.h b/src/NetworkManagerWireless.h index dce45a697a..ccbbe88acf 100644 --- a/src/NetworkManagerWireless.h +++ b/src/NetworkManagerWireless.h @@ -22,6 +22,11 @@ #ifndef NETWORK_MANAGER_WIRELESS_H #define NETWORK_MANAGER_WIRELESS_H +#include "NetworkManager.h" +#include "NetworkManagerDevice.h" + + +gboolean nm_wireless_is_most_prefered_ap (NMAccessPoint *ap, int *highest_priority); gboolean nm_wireless_scan_monitor (gpointer user_data);