diff options
Diffstat (limited to 'src/NetworkManagerPolicy.c')
-rw-r--r-- | src/NetworkManagerPolicy.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c new file mode 100644 index 0000000000..1aaf0a88a9 --- /dev/null +++ b/src/NetworkManagerPolicy.c @@ -0,0 +1,437 @@ +/* NetworkManager -- Network link manager + * + * Dan Williams <dcbw@redhat.com> + * + * 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 <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "NetworkManagerPolicy.h" +#include "NetworkManagerUtils.h" +#include "NetworkManagerAP.h" + +extern gboolean debug; + + +/* + * nm_policy_activate_interface + * + * Performs interface switching and related networking goo. + * + */ +static void nm_policy_switch_interface (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; + unsigned char pid[20]; + + 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; + + fgets (pid, 20, pidfile); + len = strnlen (buf, 20); + if (len >= 20) + pid[0] = '\0'; + else + pid[len-1] = '\0'; + fclose (pidfile); + + snprintf (buf, 500, "kill -9 %s", pid); + system (buf); + } + + 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 + * + * Called every 2s and figures out which interface to switch the active + * network connection to if our global network state has changed. + * Global network state changes are triggered by: + * 1) insertion/deletion of interfaces + * 2) link state change of an interface + * 3) appearance/disappearance of an allowed wireless access point + * + */ +gboolean nm_state_modification_monitor (gpointer user_data) +{ + NMData *data = (NMData *)user_data; + gboolean modified = FALSE; + + g_return_val_if_fail (data != NULL, TRUE); + + /* Check global state modified variable, and reset it with + * appropriate locking. + */ + g_mutex_lock (data->state_modified_mutex); + modified = data->state_modified; + if (data->state_modified) + data->state_modified = FALSE; + g_mutex_unlock (data->state_modified_mutex); + + /* If any modifications to the data model were made, update + * network state based on policy applied to the data model. + */ + if (modified) + { + if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) + { + GSList *element = data->dev_list; + NMDevice *dev = NULL; + NMDevice *best_wired_dev = NULL; + guint best_wired_prio = 0; + NMDevice *best_wireless_dev = NULL; + guint best_wireless_prio = 0; + guint highest_priority = 0; + NMDevice *highest_priority_dev = NULL; + gboolean essid_change_needed = FALSE; + + while (element) + { + guint priority = 0; + guint iface_type; + gboolean link_active; + + dev = (NMDevice *)(element->data); + + iface_type = nm_device_get_iface_type (dev); + link_active = nm_device_get_link_active (dev); + + if (iface_type == NM_IFACE_TYPE_WIRED_ETHERNET) + { + guint prio = 0; + + if (link_active) + prio += 1; + + if (data->active_device + && (dev == data->active_device) + && link_active) + prio += 1; + if (prio > best_wired_prio) + { + best_wired_dev = dev; + best_wired_prio = prio; + } + } + else if (iface_type == NM_IFACE_TYPE_WIRELESS_ETHERNET) + { + guint prio = 0; + + if (link_active) + prio += 1; + + if (nm_device_get_supports_wireless_scan (dev)) + prio += 2; + + if ( data->active_device + && (dev == data->active_device) + && link_active) + prio += 3; + + if (prio > best_wireless_prio) + { + best_wireless_dev = dev; + best_wireless_prio = prio; + } + } + + element = g_slist_next (element); + } + + NM_DEBUG_PRINT_1 ("Best wired device = %s\n", best_wired_dev ? nm_device_get_iface (best_wired_dev) : "(null)"); + NM_DEBUG_PRINT_1 ("Best wireless device = %s\n", best_wireless_dev ? nm_device_get_iface (best_wireless_dev) : "(null)"); + + if (best_wireless_dev || best_wired_dev) + { + if (best_wired_dev) + highest_priority_dev = best_wired_dev; + else + highest_priority_dev = best_wireless_dev; + } + else + { + /* No devices at all, wait for them to change status */ + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); + return (TRUE); + } + + /* If the current essid of the wireless card and the desired ap's essid are different, + * trigger an interface switch. We switch to the same interface, but we still need to bring + * the device up again to get a new DHCP address. However, don't switch if there is no + * desired ap. + */ + 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); + + 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)) + essid_change_needed = TRUE; + } + + /* If the highest priority device is different than data->active_device, switch the connection. */ + if ( essid_change_needed + || (!data->active_device || (highest_priority_dev == data->active_device))) + { + + NM_DEBUG_PRINT_2 ("**** Switching active interface from '%s' to '%s'\n", + data->active_device ? nm_device_get_iface (data->active_device) : "(null)", + nm_device_get_iface (highest_priority_dev)); + + /* FIXME + * How long should we wait between the signal to the bus, + * and deactivating the device? + */ + if (data->active_device) + { + nm_dbus_signal_device_no_longer_active (data->dbus_connection, data->active_device); + sleep (2); + } + + nm_policy_switch_interface (data, highest_priority_dev, data->active_device); + + if (data->active_device) + nm_device_unref (data->active_device); + 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_DEBUG_PRINT ("**** Switched.\n"); + } + + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); + } + else + NM_DEBUG_PRINT("nm_state_modification_monitor() could not get device list mutex\n"); + } + + return (TRUE); +} + + +/* + * nm_policy_update_allowed_access_points + * + * Grabs a list of allowed access points from the user's preferences + * + */ +void nm_policy_update_allowed_access_points (NMData *data) +{ +#define NM_ALLOWED_AP_FILE "/etc/sysconfig/networking/allowed_access_points" + + FILE *ap_file; + + g_return_if_fail (data != NULL); + + if (nm_try_acquire_mutex (data->allowed_ap_list_mutex, NULL)) + { + ap_file = fopen (NM_ALLOWED_AP_FILE, "r"); + if (ap_file) + { + gchar line[ 500 ]; + gchar prio[ 20 ]; + gchar essid[ 50 ]; + gchar wep_key[ 50 ]; + + /* Free the old list of allowed access points */ + nm_data_allowed_ap_list_free (data); + + while (fgets (line, 499, ap_file)) + { + guint len = strnlen (line, 499); + gchar *p = &line[0]; + gchar *end = strchr (line, '\n'); + guint op = 0; + + strcpy (prio, "\0"); + strcpy (essid, "\0"); + strcpy (wep_key, "\0"); + + if (end) + *end = '\0'; + else + end = p + len - 1; + + while ((end-p > 0) && (*p=='\t')) + p++; + + while (end-p > 0) + { + switch (op) + { + case 0: + strncat (prio, p, 1); + break; + case 1: + strncat (essid, p, 1); + break; + case 2: + strncat (wep_key, p, 1); + break; + default: + break; + } + p++; + + if ((end-p > 0) && (*p=='\t')) + { + op++; + while ((end-p > 0) && (*p=='\t')) + p++; + } + } + + /* Create a new entry for this essid */ + if (strlen (essid) > 0) + { + NMAccessPoint *ap; + guint prio_num = atoi (prio); + + if (prio_num < 1) + prio_num = NM_AP_PRIORITY_WORST; + else if (prio_num > NM_AP_PRIORITY_WORST) + prio_num = NM_AP_PRIORITY_WORST; + + ap = nm_ap_new (); + nm_ap_set_priority (ap, prio_num); + nm_ap_set_essid (ap, essid); + nm_ap_set_wep_key (ap, wep_key); + + data->allowed_ap_list = g_slist_append (data->allowed_ap_list, ap); + /* + NM_DEBUG_PRINT_3( "FOUND: allowed ap, prio=%d essid=%s wep_key=%s\n", prio_num, essid, wep_key ); + */ + } + } + + 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_unlock_mutex (data->allowed_ap_list_mutex, NULL); + } + else + NM_DEBUG_PRINT( "nm_policy_update_allowed_access_points() could not lock allowed ap list mutex\n" ); +} + |