diff options
Diffstat (limited to 'system-settings/plugins')
81 files changed, 12531 insertions, 465 deletions
diff --git a/system-settings/plugins/Makefile.am b/system-settings/plugins/Makefile.am index 94f75600d1..5df57d5ea7 100644 --- a/system-settings/plugins/Makefile.am +++ b/system-settings/plugins/Makefile.am @@ -15,3 +15,7 @@ endif if TARGET_DEBIAN SUBDIRS+=ifupdown endif + +if TARGET_GENTOO +SUBDIRS+=ifnet +endif diff --git a/system-settings/plugins/ifcfg-rh/Makefile.am b/system-settings/plugins/ifcfg-rh/Makefile.am index 3f0bf222fd..54d686bb48 100644 --- a/system-settings/plugins/ifcfg-rh/Makefile.am +++ b/system-settings/plugins/ifcfg-rh/Makefile.am @@ -1,7 +1,7 @@ SUBDIRS=. tests nm-ifcfg-rh-glue.h: nm-ifcfg-rh.xml - dbus-binding-tool --prefix=nm_ifcfg_rh --mode=glib-server --output=$@ $< + $(AM_V_GEN) dbus-binding-tool --prefix=nm_ifcfg_rh --mode=glib-server --output=$@ $< BUILT_SOURCES = \ nm-ifcfg-rh-glue.h diff --git a/system-settings/plugins/ifcfg-rh/common.h b/system-settings/plugins/ifcfg-rh/common.h index f5ab1c896d..095e20e927 100644 --- a/system-settings/plugins/ifcfg-rh/common.h +++ b/system-settings/plugins/ifcfg-rh/common.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2008 - 2009 Red Hat, Inc. + * (C) Copyright 2008 - 2010 Red Hat, Inc. */ #ifndef __COMMON_H__ @@ -33,15 +33,19 @@ #define ORIG_TAG ".orig" #define REJ_TAG ".rej" #define RPMNEW_TAG ".rpmnew" +#define AUGNEW_TAG ".augnew" +#define AUGTMP_TAG ".augtmp" #define IFCFG_DIR SYSCONFDIR"/sysconfig/network-scripts" #define IFCFG_PLUGIN_NAME "ifcfg-rh" -#define IFCFG_PLUGIN_INFO "(c) 2007 - 2008 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." +#define IFCFG_PLUGIN_INFO "(c) 2007 - 2010 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." #define TYPE_ETHERNET "Ethernet" #define TYPE_WIRELESS "Wireless" +#define TYPE_BRIDGE "Bridge" +#define IFCFG_PLUGIN_ERROR (ifcfg_plugin_error_quark ()) GQuark ifcfg_plugin_error_quark (void); diff --git a/system-settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/system-settings/plugins/ifcfg-rh/nm-ifcfg-connection.c index b54bc8af21..52ce6e152d 100644 --- a/system-settings/plugins/ifcfg-rh/nm-ifcfg-connection.c +++ b/system-settings/plugins/ifcfg-rh/nm-ifcfg-connection.c @@ -178,6 +178,25 @@ update (NMSettingsConnectionInterface *connection, { NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection); GError *error = NULL; + NMConnection *reread; + char *unmanaged = NULL, *keyfile = NULL, *routefile = NULL, *route6file = NULL; + + /* To ensure we don't rewrite files that are only changed from other + * processes on-disk, read the existing connection back in and only rewrite + * it if it's really changed. + */ + reread = connection_from_file (priv->filename, NULL, NULL, NULL, + &unmanaged, &keyfile, &routefile, &route6file, + NULL, NULL); + g_free (unmanaged); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + + if (reread && nm_connection_compare (NM_CONNECTION (connection), + reread, + NM_SETTING_COMPARE_FLAG_EXACT)) + goto out; if (!writer_update_connection (NM_CONNECTION (connection), IFCFG_DIR, @@ -189,6 +208,9 @@ update (NMSettingsConnectionInterface *connection, return FALSE; } +out: + if (reread) + g_object_unref (reread); return parent_settings_connection_iface->update (connection, callback, user_data); } diff --git a/system-settings/plugins/ifcfg-rh/plugin.c b/system-settings/plugins/ifcfg-rh/plugin.c index 47ce0075f8..8dce0334a5 100644 --- a/system-settings/plugins/ifcfg-rh/plugin.c +++ b/system-settings/plugins/ifcfg-rh/plugin.c @@ -146,8 +146,7 @@ read_one_connection (SCPluginIfcfg *plugin, const char *filename) if (nm_ifcfg_connection_get_unmanaged_spec (connection)) { PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "Ignoring connection '%s' and its " - "device because NM_CONTROLLED was false.", cid); - g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED); + "device due to NM_CONTROLLED/BRIDGE/VLAN.", cid); } else { /* Wait for the connection to become unmanaged once it knows the * UDI of it's device, if/when the device gets plugged in. @@ -164,7 +163,7 @@ read_one_connection (SCPluginIfcfg *plugin, const char *filename) PLUGIN_PRINT (IFCFG_PLUGIN_NAME, " error: %s", (error && error->message) ? error->message : "(unknown)"); } - g_error_free (error); + g_clear_error (&error); } return connection; @@ -218,8 +217,6 @@ connection_changed_handler (SCPluginIfcfg *plugin, g_return_if_fail (do_remove != NULL); g_return_if_fail (do_new != NULL); - PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "updating %s", path); - new = (NMIfcfgConnection *) nm_ifcfg_connection_new (path, &error, &ignore_error); if (!new) { /* errors reading connection; remove it */ @@ -234,8 +231,19 @@ connection_changed_handler (SCPluginIfcfg *plugin, return; } + /* Successfully read connection changes */ + /* When the connections are the same, nothing is done */ + if (nm_connection_compare (NM_CONNECTION (connection), + NM_CONNECTION (new), + NM_SETTING_COMPARE_FLAG_EXACT)) { + g_object_unref (new); + return; + } + + PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "updating %s", path); + old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (connection)); new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new)); @@ -305,7 +313,9 @@ handle_connection_remove_or_new (SCPluginIfcfg *plugin, if (do_new) { connection = read_one_connection (plugin, path); if (connection) { - if (!nm_ifcfg_connection_get_unmanaged_spec (connection)) + if (nm_ifcfg_connection_get_unmanaged_spec (connection)) + g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED); + else g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, connection); } } @@ -334,25 +344,25 @@ dir_changed (GFileMonitor *monitor, g_free (path); connection = g_hash_table_lookup (priv->connections, name); - if (!connection) { - do_new = TRUE; - } else { - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", name); - do_remove = TRUE; - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - /* Update */ + switch (event_type) { + case G_FILE_MONITOR_EVENT_DELETED: + PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", name); + if (connection) + handle_connection_remove_or_new (plugin, name, connection, TRUE, FALSE); + break; + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + /* Update */ + if (!connection) + do_new = TRUE; + else connection_changed_handler (plugin, name, connection, &do_remove, &do_new); - break; - default: - break; - } - } - handle_connection_remove_or_new (plugin, name, connection, do_remove, do_new); + handle_connection_remove_or_new (plugin, name, connection, do_remove, do_new); + break; + default: + break; + } g_free (name); } @@ -544,7 +554,7 @@ impl_ifcfgrh_get_ifcfg_details (SCPluginIfcfg *plugin, } connection = g_hash_table_lookup (priv->connections, in_ifcfg); - if (!connection) { + if (!connection || nm_ifcfg_connection_get_unmanaged_spec (connection)) { g_set_error (error, NM_SETTINGS_INTERFACE_ERROR, NM_SETTINGS_INTERFACE_ERROR_INVALID_CONNECTION, diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c index 28244206a9..b4ee1307b9 100644 --- a/system-settings/plugins/ifcfg-rh/reader.c +++ b/system-settings/plugins/ifcfg-rh/reader.c @@ -18,6 +18,7 @@ * Copyright (C) 2008 - 2010 Red Hat, Inc. */ +#include <config.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> @@ -185,7 +186,7 @@ make_connection_setting (const char *file, } static gboolean -read_mac_address (shvarFile *ifcfg, GByteArray **array, GError **error) +read_mac_address (shvarFile *ifcfg, const char *key, GByteArray **array, GError **error) { char *value = NULL; struct ether_addr *mac; @@ -196,7 +197,7 @@ read_mac_address (shvarFile *ifcfg, GByteArray **array, GError **error) g_return_val_if_fail (error != NULL, FALSE); g_return_val_if_fail (*error == NULL, FALSE); - value = svGetValue (ifcfg, "HWADDR", FALSE); + value = svGetValue (ifcfg, key, FALSE); if (!value || !strlen (value)) { g_free (value); return TRUE; @@ -205,8 +206,8 @@ read_mac_address (shvarFile *ifcfg, GByteArray **array, GError **error) mac = ether_aton (value); if (!mac) { g_free (value); - g_set_error (error, ifcfg_plugin_error_quark (), 0, - "The MAC address '%s' was invalid.", value); + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "%s: the MAC address '%s' was invalid.", key, value); return FALSE; } @@ -282,23 +283,23 @@ fill_ip4_setting_from_ibft (shvarFile *ifcfg, return FALSE; if (!WIFEXITED (status)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "%s exited abnormally.", iscsiadm_path); goto done; } if (WEXITSTATUS (status) != 0) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "%s exited with error %d. Message: '%s'", iscsiadm_path, WEXITSTATUS (status), err ? err : "(none)"); goto done; } - if (!read_mac_address (ifcfg, &ifcfg_mac, error)) + if (!read_mac_address (ifcfg, "HWADDR", &ifcfg_mac, error)) goto done; /* Ensure we got a MAC */ if (!ifcfg_mac) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing device MAC address (no HWADDR tag present)."); goto done; } @@ -488,7 +489,7 @@ read_ip4_address (shvarFile *ifcfg, *out_addr = ip4_addr.s_addr; success = TRUE; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid %s IP4 address '%s'", tag, value); } g_free (value); @@ -514,7 +515,7 @@ parse_ip6_address (const char *value, *out_addr = ip6_addr; success = TRUE; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 address '%s'", value); } return success; @@ -588,7 +589,7 @@ read_full_ip4_address (shvarFile *ifcfg, errno = 0; prefix = strtol (value, NULL, 10); if (errno || prefix <= 0 || prefix > 32) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 prefix '%s'", value); g_free (value); goto error; @@ -619,7 +620,7 @@ read_full_ip4_address (shvarFile *ifcfg, /* Validate the prefix */ if (nm_ip4_address_get_prefix (addr) > 32) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing or invalid IP4 prefix '%d'", nm_ip4_address_get_prefix (addr)); goto error; @@ -682,12 +683,7 @@ read_one_ip4_route (shvarFile *ifcfg, /* Next hop */ if (!read_ip4_address (ifcfg, gw_tag, &tmp, error)) goto out; - if (!tmp) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, - "Missing or invalid IP4 gateway address '%d'", - tmp); - goto out; - } + /* No need to check tmp, because we don't make distinction between missing GATEWAY IP and 0.0.0.0 */ nm_ip4_route_set_next_hop (route, tmp); /* Prefix */ @@ -698,7 +694,7 @@ read_one_ip4_route (shvarFile *ifcfg, /* Validate the prefix */ if ( !nm_ip4_route_get_prefix (route) || nm_ip4_route_get_prefix (route) > 32) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing or invalid IP4 prefix '%d'", nm_ip4_route_get_prefix (route)); goto out; @@ -712,7 +708,7 @@ read_one_ip4_route (shvarFile *ifcfg, errno = 0; metric = strtol (value, NULL, 10); if (errno || metric < 0) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 route metric '%s'", value); g_free (value); goto out; @@ -796,7 +792,7 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError g_regex_match (regex_to2, *iter, 0, &match_info); if (!g_match_info_matches (match_info)) { g_match_info_free (match_info); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IP4 route destination address in record: '%s'", *iter); goto error; } @@ -806,7 +802,7 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError if (!strcmp (dest, "default")) strcpy (dest, "0.0.0.0"); if (inet_pton (AF_INET, dest, &ip4_addr) != 1) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 route destination address '%s'", dest); g_free (dest); goto error; @@ -821,7 +817,7 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError errno = 0; prefix_int = strtol (prefix, NULL, 10); if (errno || prefix_int < 0 || prefix_int > 32) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 route destination prefix '%s'", prefix); g_free (prefix); goto error; @@ -835,14 +831,14 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError g_regex_match (regex_via, *iter, 0, &match_info); if (!g_match_info_matches (match_info)) { g_match_info_free (match_info); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IP4 route gateway address in record: '%s'", *iter); goto error; } next_hop = g_match_info_fetch (match_info, 1); g_match_info_free (match_info); if (inet_pton (AF_INET, next_hop, &ip4_addr) != 1) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 route gateway address '%s'", next_hop); g_free (next_hop); goto error; @@ -859,7 +855,7 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError metric_int = strtol (metric, NULL, 10); if (errno || metric_int < 0) { g_match_info_free (match_info); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 route metric '%s'", metric); g_free (metric); goto error; @@ -905,7 +901,7 @@ parse_full_ip6_address (const char *addr_str, GError **error) /* Split the adddress and prefix */ list = g_strsplit_set (addr_str, "/", 2); if (g_strv_length (list) < 1) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 address '%s'", addr_str); goto error; } @@ -929,7 +925,7 @@ parse_full_ip6_address (const char *addr_str, GError **error) errno = 0; prefix = strtol (prefix_tag, NULL, 10); if (errno || prefix <= 0 || prefix > 128) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 prefix '%s'", prefix_tag); goto error; } @@ -1018,7 +1014,7 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro g_regex_match (regex_to2, *iter, 0, &match_info); if (!g_match_info_matches (match_info)) { g_match_info_free (match_info); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IP6 route destination address in record: '%s'", *iter); goto error; } @@ -1028,7 +1024,7 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro if (!strcmp (dest, "default")) strcpy (dest, "::"); if (inet_pton (AF_INET6, dest, &ip6_addr) != 1) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 route destination address '%s'", dest); g_free (dest); goto error; @@ -1043,7 +1039,7 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro errno = 0; prefix_int = strtol (prefix, NULL, 10); if (errno || prefix_int < 0 || prefix_int > 128) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 route destination prefix '%s'", prefix); g_free (prefix); goto error; @@ -1057,14 +1053,14 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro g_regex_match (regex_via, *iter, 0, &match_info); if (!g_match_info_matches (match_info)) { g_match_info_free (match_info); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IP6 route gateway address in record: '%s'", *iter); goto error; } next_hop = g_match_info_fetch (match_info, 1); g_match_info_free (match_info); if (inet_pton (AF_INET6, next_hop, &ip6_addr) != 1) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 route gateway address '%s'", next_hop); g_free (next_hop); goto error; @@ -1081,7 +1077,7 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro metric_int = strtol (metric, NULL, 10); if (errno || metric_int < 0 || metric_int > G_MAXUINT32) { g_match_info_free (match_info); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 route metric '%s'", metric); g_free (metric); goto error; @@ -1129,7 +1125,7 @@ make_ip4_setting (shvarFile *ifcfg, s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); if (!s_ip4) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not allocate IP4 setting"); return NULL; } @@ -1190,7 +1186,7 @@ make_ip4_setting (shvarFile *ifcfg, } else if (!g_ascii_strcasecmp (value, "none") || !g_ascii_strcasecmp (value, "static")) { /* Static IP */ } else if (strlen (value)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Unknown BOOTPROTO '%s'", value); g_free (value); goto done; @@ -1317,7 +1313,7 @@ make_ip4_setting (shvarFile *ifcfg, /* Static routes - route-<name> file */ route_path = utils_get_route_path (ifcfg->fileName); if (!route_path) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not get route file path for '%s'", ifcfg->fileName); goto done; } @@ -1397,7 +1393,7 @@ make_ip6_setting (shvarFile *ifcfg, s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); if (!s_ip6) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not allocate IP6 setting"); return NULL; } @@ -1562,7 +1558,7 @@ make_ip6_setting (shvarFile *ifcfg, /* Read static routes from route6-<interface> file */ route6_path = utils_get_route6_path (ifcfg->fileName); if (!route6_path) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not get route6 file path for '%s'", ifcfg->fileName); goto error; } @@ -1618,28 +1614,34 @@ add_one_wep_key (shvarFile *ifcfg, while (*p) { if (!g_ascii_isxdigit (*p)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid hexadecimal WEP key."); goto out; } p++; } key = g_strdup (value); - } else if ( strncmp (value, "s:", 2) + } else if ( !strncmp (value, "s:", 2) && (strlen (value) == 7 || strlen (value) == 15)) { - /* ASCII passphrase */ + /* ASCII key */ char *p = value + 2; while (*p) { if (!isascii ((int) (*p))) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, - "Invalid ASCII WEP passphrase."); + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "Invalid ASCII WEP key."); goto out; } p++; } - key = utils_bin2hexstr (value, strlen (value), strlen (value) * 2); + /* Remove 's:' prefix. + * Don't convert to hex string. wpa_supplicant takes 'wep_key0' option over D-Bus as byte array + * and converts it to hex string itself. Even though we convert hex string keys into a bin string + * before passing to wpa_supplicant, this prevents two unnecessary conversions. And mainly, + * ASCII WEP key doesn't change to HEX WEP key in UI, which could confuse users. + */ + key = g_strdup (value + 2); } } @@ -1647,7 +1649,7 @@ add_one_wep_key (shvarFile *ifcfg, nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key); success = TRUE; } else - g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid WEP key length."); + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid WEP key length."); out: g_free (value); @@ -1707,7 +1709,7 @@ make_wep_setting (shvarFile *ifcfg, default_key_idx--; /* convert to [0...3] */ g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, default_key_idx, NULL); } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid default WEP key '%s'", value); g_free (value); goto error; @@ -1732,15 +1734,15 @@ make_wep_setting (shvarFile *ifcfg, /* If there's a default key, ensure that key exists */ if ((default_key_idx == 1) && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Default WEP key index was 2, but no valid KEY2 exists."); goto error; } else if ((default_key_idx == 2) && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Default WEP key index was 3, but no valid KEY3 exists."); goto error; } else if ((default_key_idx == 3) && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Default WEP key index was 4, but no valid KEY4 exists."); goto error; } @@ -1757,7 +1759,7 @@ make_wep_setting (shvarFile *ifcfg, } else if (!strcmp (lcase, "restricted")) { g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL); } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid WEP authentication algorithm '%s'", lcase); g_free (lcase); @@ -1775,7 +1777,7 @@ make_wep_setting (shvarFile *ifcfg, auth_alg = nm_setting_wireless_security_get_auth_alg (s_wireless_sec); if (auth_alg && !strcmp (auth_alg, "shared")) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "WEP Shared Key authentication is invalid for " "unencrypted connections."); goto error; @@ -1871,7 +1873,7 @@ parse_wpa_psk (shvarFile *ifcfg, char *psk = NULL, *p, *hashed = NULL; gboolean quoted = FALSE; - /* Passphrase must be between 10 and 66 characters in length becuase WPA + /* Passphrase must be between 10 and 66 characters in length because WPA * hex keys are exactly 64 characters (no quoting), and WPA passphrases * are between 8 and 63 characters (inclusive), plus optional quoting if * the passphrase contains spaces. @@ -1889,7 +1891,7 @@ parse_wpa_psk (shvarFile *ifcfg, psk = svGetValue (ifcfg, "WPA_PSK", TRUE); if (!psk) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing WPA_PSK for WPA-PSK key management"); return NULL; } @@ -1903,7 +1905,7 @@ parse_wpa_psk (shvarFile *ifcfg, /* Verify the hex PSK; 64 digits */ while (*p) { if (!isxdigit (*p++)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid WPA_PSK (contains non-hexadecimal characters)"); goto out; } @@ -1924,7 +1926,7 @@ parse_wpa_psk (shvarFile *ifcfg, /* Length check */ if (strlen (p) < 8 || strlen (p) > 63) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid WPA_PSK (passphrases must be between " "8 and 63 characters long (inclusive))"); goto out; @@ -1934,7 +1936,7 @@ parse_wpa_psk (shvarFile *ifcfg, } if (!hashed) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid WPA_PSK (doesn't look like a passphrase or hex key)"); goto out; } @@ -1980,7 +1982,7 @@ eap_simple_reader (const char *eap_method, value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE); if (!value) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", eap_method); return FALSE; @@ -1995,7 +1997,7 @@ eap_simple_reader (const char *eap_method, } if (!value) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IEEE_8021X_PASSWORD for EAP method '%s'.", eap_method); return FALSE; @@ -2048,7 +2050,7 @@ eap_tls_reader (const char *eap_method, value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE); if (!value) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", eap_method); return FALSE; @@ -2095,7 +2097,7 @@ eap_tls_reader (const char *eap_method, } if (!privkey_password) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing %s for EAP method '%s'.", phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD", eap_method); @@ -2107,7 +2109,7 @@ eap_tls_reader (const char *eap_method, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", FALSE); if (!privkey) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing %s for EAP method '%s'.", phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", eap_method); @@ -2145,7 +2147,7 @@ eap_tls_reader (const char *eap_method, phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", FALSE); if (!client_cert) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing %s for EAP method '%s'.", phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", eap_method); @@ -2221,7 +2223,7 @@ eap_peap_reader (const char *eap_method, else if (!strcmp (peapver, "1")) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL); else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Unknown IEEE_8021X_PEAP_VERSION value '%s'", peapver); goto done; @@ -2233,7 +2235,7 @@ eap_peap_reader (const char *eap_method, inner_auth = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE); if (!inner_auth) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IEEE_8021X_INNER_AUTH_METHODS."); goto done; } @@ -2253,7 +2255,7 @@ eap_peap_reader (const char *eap_method, if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) goto done; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", *iter); goto done; @@ -2266,7 +2268,7 @@ eap_peap_reader (const char *eap_method, } if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "No valid IEEE_8021X_INNER_AUTH_METHODS found."); goto done; } @@ -2321,7 +2323,7 @@ eap_ttls_reader (const char *eap_method, tmp = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE); if (!tmp) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IEEE_8021X_INNER_AUTH_METHODS."); goto done; } @@ -2351,7 +2353,7 @@ eap_ttls_reader (const char *eap_method, goto done; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + strlen ("eap-")), NULL); } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", *iter); goto done; @@ -2385,7 +2387,7 @@ fill_8021x (shvarFile *ifcfg, value = svGetValue (ifcfg, "IEEE_8021X_EAP_METHODS", FALSE); if (!value) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing IEEE_8021X_EAP_METHODS for key management '%s'", key_mgmt); return NULL; @@ -2443,7 +2445,7 @@ fill_8021x (shvarFile *ifcfg, g_strfreev (list); if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "No valid EAP methods found in IEEE_8021X_EAP_METHODS."); goto error; } @@ -2521,7 +2523,7 @@ make_wpa_setting (shvarFile *ifcfg, } else if (!strcmp (value, "WPA-EAP") || !strcmp (value, "IEEE8021X")) { /* Adhoc mode is mutually exclusive with any 802.1x-based authentication */ if (adhoc) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", value); goto error; } @@ -2534,7 +2536,7 @@ make_wpa_setting (shvarFile *ifcfg, g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL); g_free (lower); } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Unknown wireless KEY_MGMT type '%s'", value); goto error; } @@ -2586,7 +2588,7 @@ make_leap_setting (shvarFile *ifcfg, value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE); if (!value || !strlen (value)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing LEAP identity"); goto error; } @@ -2652,7 +2654,7 @@ make_wireless_setting (shvarFile *ifcfg, s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - if (read_mac_address (ifcfg, &array, error)) { + if (read_mac_address (ifcfg, "HWADDR", &array, error)) { if (array) { g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, array, NULL); @@ -2675,6 +2677,14 @@ make_wireless_setting (shvarFile *ifcfg, return NULL; } + array = NULL; + if (read_mac_address (ifcfg, "MACADDR", &array, error)) { + if (array) { + g_object_set (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, array, NULL); + g_byte_array_free (array, TRUE); + } + } + value = svGetValue (ifcfg, "ESSID", TRUE); if (value) { gsize ssid_len = 0, value_len = strlen (value); @@ -2693,7 +2703,7 @@ make_wireless_setting (shvarFile *ifcfg, } else if ((value_len > 2) && (strncmp (value, "0x", 2) == 0)) { /* Hex representation */ if (value_len % 2) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid SSID '%s' size (looks like hex but length not multiple of 2)", value); g_free (value); @@ -2703,7 +2713,7 @@ make_wireless_setting (shvarFile *ifcfg, p = value + 2; while (*p) { if (!isxdigit (*p)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)", value, *p); g_free (value); @@ -2719,7 +2729,7 @@ make_wireless_setting (shvarFile *ifcfg, } if (ssid_len > 32 || ssid_len == 0) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", value, ssid_len); g_free (value); @@ -2734,7 +2744,7 @@ make_wireless_setting (shvarFile *ifcfg, } else { /* Only fail on lack of SSID if device is managed */ if (nm_controlled) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing SSID"); + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing SSID"); goto error; } } @@ -2755,7 +2765,7 @@ make_wireless_setting (shvarFile *ifcfg, } else if (!strcmp (lcase, "managed") || !strcmp (lcase, "auto")) { mode = "infrastructure"; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid mode '%s' (not 'Ad-Hoc', 'Managed', or 'Auto')", lcase); g_free (lcase); @@ -2773,7 +2783,7 @@ make_wireless_setting (shvarFile *ifcfg, eth = ether_aton (value); if (!eth) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid BSSID '%s'", value); goto error; } @@ -2791,7 +2801,7 @@ make_wireless_setting (shvarFile *ifcfg, errno = 0; chan = strtol (value, NULL, 10); if (errno || chan <= 0 || chan > 196) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid wireless channel '%s'", value); g_free (value); goto error; @@ -2810,7 +2820,7 @@ make_wireless_setting (shvarFile *ifcfg, errno = 0; mtu = strtol (value, NULL, 10); if (errno || mtu < 0 || mtu > 50000) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid wireless MTU '%s'", value); g_free (value); goto error; @@ -2851,7 +2861,7 @@ wireless_connection_from_ifcfg (const char *file, connection = nm_connection_new (); if (!connection) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Failed to allocate new connection for %s.", file); return NULL; } @@ -2897,7 +2907,7 @@ wireless_connection_from_ifcfg (const char *file, printable_ssid); g_free (printable_ssid); if (!con_setting) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Failed to create connection setting."); g_object_unref (connection); return NULL; @@ -2927,6 +2937,7 @@ make_wired_setting (shvarFile *ifcfg, char *value = NULL; int mtu; GByteArray *mac = NULL; + char *nettype; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); @@ -2942,7 +2953,7 @@ make_wired_setting (shvarFile *ifcfg, g_free (value); } - if (read_mac_address (ifcfg, &mac, error)) { + if (read_mac_address (ifcfg, "HWADDR", &mac, error)) { if (mac) { g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); @@ -2954,15 +2965,106 @@ make_wired_setting (shvarFile *ifcfg, } g_byte_array_free (mac, TRUE); - } else if (!nm_controlled) { - /* If NM_CONTROLLED=no but there wasn't a MAC address, notify - * the user that the device cannot be unmanaged. - */ - PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: NM_CONTROLLED was false but HWADDR was missing; device will be managed"); } } else { g_object_unref (s_wired); - s_wired = NULL; + return NULL; + } + + value = svGetValue (ifcfg, "SUBCHANNELS", FALSE); + if (value) { + const char *p = value; + gboolean success = TRUE; + char **chans = NULL; + + /* basic sanity checks */ + while (*p) { + if (!isxdigit (*p) && (*p != ',') && (*p != '.')) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid SUBCHANNELS '%s'", value); + success = FALSE; + break; + } + p++; + } + + if (success) { + guint32 num_chans; + + chans = g_strsplit_set (value, ",", 0); + num_chans = g_strv_length (chans); + if (num_chans < 2 || num_chans > 3) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid SUBCHANNELS '%s' (%d channels, 2 or 3 expected)", + value, g_strv_length (chans)); + } else { + GPtrArray *array = g_ptr_array_sized_new (num_chans); + + g_ptr_array_add (array, chans[0]); + g_ptr_array_add (array, chans[1]); + if (num_chans == 3) + g_ptr_array_add (array, chans[2]); + + g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, array, NULL); + g_ptr_array_free (array, TRUE); + + /* set the unmanaged spec too */ + if (!nm_controlled && !*unmanaged) + *unmanaged = g_strdup_printf ("s390-subchannels:%s", value); + } + g_strfreev (chans); + } + g_free (value); + } + + value = svGetValue (ifcfg, "PORTNAME", FALSE); + if (value && strlen (value)) { + nm_setting_wired_add_s390_option (s_wired, "portname", value); + } + g_free (value); + + nettype = svGetValue (ifcfg, "NETTYPE", FALSE); + if (nettype && strlen (nettype)) { + if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc")) + g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: unknown s390 NETTYPE '%s'", nettype); + } + + value = svGetValue (ifcfg, "OPTIONS", FALSE); + if (value && strlen (value)) { + char **options, **iter; + + iter = options = g_strsplit_set (value, " ", 0); + while (iter && *iter) { + char *equals = strchr (*iter, '='); + gboolean valid = FALSE; + + if (equals) { + *equals = '\0'; + valid = nm_setting_wired_add_s390_option (s_wired, *iter, equals + 1); + } + if (!valid) + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid s390 OPTION '%s'", *iter); + iter++; + } + g_strfreev (options); + } + g_free (value); + + g_free (nettype); + + if (!nm_controlled && !*unmanaged) { + /* If NM_CONTROLLED=no but there wasn't a MAC address or z/VM + * subchannels, notify the user that the device cannot be unmanaged. + */ + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: NM_CONTROLLED was false but HWADDR or SUBCHANNELS was missing; device will be managed"); + } + + mac = NULL; + if (read_mac_address (ifcfg, "MACADDR", &mac, error)) { + if (mac) { + g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, mac, NULL); + g_byte_array_free (mac, TRUE); + } } value = svGetValue (ifcfg, "KEY_MGMT", FALSE); @@ -2972,7 +3074,7 @@ make_wired_setting (shvarFile *ifcfg, if (!*s_8021x) goto error; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Unknown wired KEY_MGMT type '%s'", value); goto error; } @@ -3004,14 +3106,14 @@ wired_connection_from_ifcfg (const char *file, connection = nm_connection_new (); if (!connection) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Failed to allocate new connection for %s.", file); return NULL; } con_setting = make_connection_setting (file, ifcfg, NM_SETTING_WIRED_SETTING_NAME, NULL); if (!con_setting) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Failed to create connection setting."); g_object_unref (connection); return NULL; @@ -3077,6 +3179,12 @@ is_wireless_device (const char *iface) return is_wireless; } +enum { + IGNORE_REASON_NONE = 0x00, + IGNORE_REASON_BRIDGE = 0x01, + IGNORE_REASON_VLAN = 0x02, +}; + NMConnection * connection_from_file (const char *filename, const char *network_file, /* for unit tests only */ @@ -3086,16 +3194,18 @@ connection_from_file (const char *filename, char **keyfile, char **routefile, char **route6file, - GError **error, + GError **out_error, gboolean *ignore_error) { NMConnection *connection = NULL; shvarFile *parsed; - char *type, *nmc = NULL, *bootproto; + char *type, *nmc = NULL, *bootproto, *tmp; NMSetting *s_ip4, *s_ip6; const char *ifcfg_name = NULL; gboolean nm_controlled = TRUE; gboolean ip6_used = FALSE; + GError *error = NULL; + guint32 ignore_reason = IGNORE_REASON_NONE; g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (unmanaged != NULL, NULL); @@ -3116,14 +3226,14 @@ connection_from_file (const char *filename, ifcfg_name = utils_get_ifcfg_name (filename, TRUE); if (!ifcfg_name) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (out_error, IFCFG_PLUGIN_ERROR, 0, "Ignoring connection '%s' because it's not an ifcfg file.", filename); return NULL; } parsed = svNewFile (filename); if (!parsed) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (out_error, IFCFG_PLUGIN_ERROR, 0, "Couldn't parse file '%s'", filename); return NULL; } @@ -3137,14 +3247,15 @@ connection_from_file (const char *filename, */ device = svGetValue (parsed, "DEVICE", FALSE); if (!device) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, "File '%s' had neither TYPE nor DEVICE keys.", filename); goto done; } if (!strcmp (device, "lo")) { - *ignore_error = TRUE; - g_set_error (error, ifcfg_plugin_error_quark (), 0, + if (ignore_error) + *ignore_error = TRUE; + g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, "Ignoring loopback device config."); g_free (device); goto done; @@ -3179,12 +3290,33 @@ connection_from_file (const char *filename, g_free (lower); } + /* Ignore BRIDGE= and VLAN= connections for now too (rh #619863) */ + tmp = svGetValue (parsed, "BRIDGE", FALSE); + if (tmp) { + g_free (tmp); + nm_controlled = FALSE; + ignore_reason = IGNORE_REASON_BRIDGE; + } + + if (nm_controlled) { + tmp = svGetValue (parsed, "VLAN", FALSE); + if (tmp) { + g_free (tmp); + nm_controlled = FALSE; + ignore_reason = IGNORE_REASON_VLAN; + } + } + + /* Construct the connection */ if (!strcasecmp (type, TYPE_ETHERNET)) - connection = wired_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, error); + connection = wired_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, &error); else if (!strcasecmp (type, TYPE_WIRELESS)) - connection = wireless_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, error); - else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + connection = wireless_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, &error); + else if (!strcasecmp (type, TYPE_BRIDGE)) { + g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, + "Bridge connections are not yet supported"); + } else { + g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, "Unknown connection type '%s'", type); } @@ -3195,12 +3327,28 @@ connection_from_file (const char *filename, g_free (type); - /* Don't bother reading the connection fully if it's unmanaged */ - if (!connection || *unmanaged) + /* Don't bother reading the connection fully if it's unmanaged or ignored */ + if (!connection || *unmanaged || ignore_reason) { + if (connection && !*unmanaged) { + /* However,BRIDGE and VLAN connections that don't have HWADDR won't + * be unmanaged because the unmanaged state is keyed off HWADDR. + * They willl still be tagged 'ignore' from code that checks BRIDGE + * and VLAN above. Since they aren't marked unmanaged, kill them + * completely. + */ + if (ignore_reason) { + g_object_unref (connection); + connection = NULL; + g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, + "%s connections are not yet supported", + ignore_reason == IGNORE_REASON_BRIDGE ? "Bridge" : "VLAN"); + } + } goto done; + } - s_ip6 = make_ip6_setting (parsed, network_file, iscsiadm_path, error); - if (*error) { + s_ip6 = make_ip6_setting (parsed, network_file, iscsiadm_path, &error); + if (error) { g_object_unref (connection); connection = NULL; goto done; @@ -3213,8 +3361,8 @@ connection_from_file (const char *filename, ip6_used = TRUE; } - s_ip4 = make_ip4_setting (parsed, network_file, iscsiadm_path, ip6_used, error); - if (*error) { + s_ip4 = make_ip4_setting (parsed, network_file, iscsiadm_path, ip6_used, &error); + if (error) { g_object_unref (connection); connection = NULL; goto done; @@ -3236,7 +3384,7 @@ connection_from_file (const char *filename, g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_READ_ONLY, TRUE, NULL); } - if (!nm_connection_verify (connection, error)) { + if (!nm_connection_verify (connection, &error)) { g_object_unref (connection); connection = NULL; } @@ -3247,6 +3395,10 @@ connection_from_file (const char *filename, done: svCloseFile (parsed); + if (error && out_error) + *out_error = error; + else + g_clear_error (&error); return connection; } diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am b/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am index 66435acbcb..28cfb98f3f 100644 --- a/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am @@ -4,6 +4,7 @@ EXTRA_DIST = \ ifcfg-test-wired-static \ ifcfg-test-wired-static-bootproto \ ifcfg-test-wired-dhcp \ + ifcfg-test-wired-dhcp6-only \ ifcfg-test-wired-global-gateway \ network-test-wired-global-gateway \ ifcfg-test-wired-never-default \ @@ -60,7 +61,15 @@ EXTRA_DIST = \ ifcfg-test-wired-static-no-prefix-24 \ ifcfg-test-wired-ipv6-only \ ifcfg-test-wifi-wep-passphrase \ - keys-test-wifi-wep-passphrase + keys-test-wifi-wep-passphrase \ + ifcfg-test-wifi-wep-40-ascii \ + keys-test-wifi-wep-40-ascii \ + ifcfg-test-wifi-wep-104-ascii \ + keys-test-wifi-wep-104-ascii \ + ifcfg-test-wired-qeth-static \ + ifcfg-test-bridge-main \ + ifcfg-test-bridge-component \ + ifcfg-test-vlan-interface check-local: @for f in $(EXTRA_DIST); do \ diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-component b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-component new file mode 100644 index 0000000000..f586637ec3 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-component @@ -0,0 +1,5 @@ +DEVICE=eth0 +HWADDR=00:22:15:59:62:97 +ONBOOT=no +BRIDGE=br0 + diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main new file mode 100644 index 0000000000..c5caf3fc9b --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -0,0 +1,7 @@ +DEVICE=br0 +ONBOOT=no +TYPE=Bridge +BOOTPROTO=dhcp +STP=on +DELAY=0 + diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface new file mode 100644 index 0000000000..6c841855ed --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface @@ -0,0 +1,7 @@ +DEVICE=eth1.43 +VLAN=yes +ONBOOT=yes +BOOTPROTO=none +IPADDR=192.168.43.149 +NETMASK=255.255.255.0 + diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-104-ascii b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-104-ascii new file mode 100644 index 0000000000..250efa134c --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-104-ascii @@ -0,0 +1,14 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +SECURITYMODE=open diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-40-ascii b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-40-ascii new file mode 100644 index 0000000000..250efa134c --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-40-ascii @@ -0,0 +1,14 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +SECURITYMODE=open diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp6-only b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp6-only new file mode 100644 index 0000000000..de03e04480 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp6-only @@ -0,0 +1,11 @@ +DEVICE="eth0" +ONBOOT=no +TYPE=Ethernet +DEFROUTE=yes +PEERDNS=yes +PEERROUTES=yes +IPV6INIT=yes +IPV6_AUTOCONF=no +DHCPV6C=yes +HWADDR=00:13:20:F5:F5:E4 + diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static new file mode 100644 index 0000000000..4719de2175 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static @@ -0,0 +1,13 @@ +# IBM QETH +DEVICE=eth1 +BOOTPROTO=static +IPADDR=192.168.70.87 +NETMASK=255.255.255.0 +ONBOOT=yes +NETTYPE=qeth +SUBCHANNELS=0.0.0600,0.0.0601,0.0.0602 +TYPE=Ethernet +PORTNAME=OSAPORT +OPTIONS='layer2=1 portno=0' +MACADDR=02:00:00:23:65:1a + diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-104-ascii b/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-104-ascii new file mode 100644 index 0000000000..f5d532b283 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-104-ascii @@ -0,0 +1 @@ +KEY1=s:LoremIpsumSit diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-40-ascii b/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-40-ascii new file mode 100644 index 0000000000..b0d1470827 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-40-ascii @@ -0,0 +1 @@ +KEY1=s:Lorem diff --git a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c index cf2a7c03de..a6c54fd3d8 100644 --- a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c +++ b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c @@ -153,6 +153,8 @@ int main (int argc, char **argv) test_ignored ("ignored-orig", "ifcfg-FooBar" ORIG_TAG, TRUE); test_ignored ("ignored-rej", "ifcfg-FooBar" REJ_TAG, TRUE); test_ignored ("ignored-rpmnew", "ifcfg-FooBar" RPMNEW_TAG, TRUE); + test_ignored ("ignored-augnew", "ifcfg-FooBar" AUGNEW_TAG, TRUE); + test_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE); base = g_path_get_basename (argv[0]); fprintf (stdout, "%s: SUCCESS\n", base); diff --git a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 35cea36cf5..1945fb99cd 100644 --- a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -29,8 +29,6 @@ #include <sys/types.h> #include <sys/stat.h> -#include <dbus/dbus-glib.h> - #include <nm-utils.h> #include <nm-setting-connection.h> #include <nm-setting-wired.h> @@ -2319,6 +2317,111 @@ test_read_wired_ipv6_only (void) g_object_unref (connection); } +#define TEST_IFCFG_WIRED_DHCP6_ONLY TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-dhcp6-only" + +static void +test_read_wired_dhcp6_only (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System test-wired-dhcp6-only"; + const char *method; + + connection = connection_from_file (TEST_IFCFG_WIRED_DHCP6_ONLY, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wired-dhcp6-only-read", "failed to read %s: %s", TEST_IFCFG_WIRED_DHCP6_ONLY, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wired-dhcp6-only-verify", "failed to verify %s: %s", TEST_IFCFG_WIRED_DHCP6_ONLY, error->message); + + ASSERT (unmanaged == FALSE, + "wired-dhcp6-only-verify", "failed to verify %s: unexpected unmanaged value", TEST_IFCFG_WIRED_DHCP6_ONLY); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wired-dhcp6-only-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wired-dhcp6-only-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wired-dhcp6-only-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRED SETTING ===== */ + + s_wired = NM_SETTING_WIRED (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED)); + ASSERT (s_wired != NULL, + "wired-dhcp6-only-verify-wired", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_WIRED_SETTING_NAME); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + ASSERT (s_ip4 != NULL, + "wired-dhcp6-only-verify-ip4", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_IP4_CONFIG_SETTING_NAME); + + method = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0, + "wired-dhcp6-only-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + + /* ===== IPv6 SETTING ===== */ + + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG)); + ASSERT (s_ip6 != NULL, + "wired-dhcp6-only-verify-ip6", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_IP6_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip6_config_get_method (s_ip6); + ASSERT (strcmp (tmp, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0, + "wired-dhcp6-only-verify-ip6", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_DHCP6_ONLY, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_METHOD); + + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); +} + #define TEST_IFCFG_ONBOOT_NO TEST_IFCFG_DIR"/network-scripts/ifcfg-test-onboot-no" static void @@ -3680,6 +3783,272 @@ test_read_wifi_wep_passphrase (void) g_object_unref (connection); } +#define TEST_IFCFG_WIFI_WEP_40_ASCII TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-wep-40-ascii" + +static void +test_read_wifi_wep_40_ascii (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_wep_key0 = "Lorem"; + NMWepKeyType key_type; + + connection = connection_from_file (TEST_IFCFG_WIFI_WEP_40_ASCII, + NULL, + TYPE_WIRELESS, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wifi-wep-40-ascii-read", "failed to read %s: %s", TEST_IFCFG_WIFI_WEP_40_ASCII, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wifi-wep-40-ascii-verify", "failed to verify %s: %s", TEST_IFCFG_WIFI_WEP_40_ASCII, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wifi-wep-40-ascii-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + ASSERT (s_wireless != NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* Security */ + tmp = nm_setting_wireless_get_security (s_wireless); + ASSERT (tmp != NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + ASSERT (strcmp (tmp, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY)); + ASSERT (s_wsec != NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + /* Key management */ + ASSERT (strcmp (nm_setting_wireless_security_get_key_mgmt (s_wsec), "none") == 0, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + + /* WEP key index */ + ASSERT (nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) == 0, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX); + + /* WEP key type */ + key_type = nm_setting_wireless_security_get_wep_key_type (s_wsec); + ASSERT (key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected WEP key type %d", + TEST_IFCFG_WIFI_WEP_40_ASCII, + key_type); + + /* WEP key index 0 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 0); + ASSERT (tmp != NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + ASSERT (strcmp (tmp, expected_wep_key0) == 0, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + + /* WEP key index 1 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 1); + ASSERT (tmp == NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1); + + /* WEP key index 2 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 2); + ASSERT (tmp == NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2); + + /* WEP key index 3 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 3); + ASSERT (tmp == NULL, + "wifi-wep-40-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIFI_WEP_40_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3); + + g_object_unref (connection); +} + +#define TEST_IFCFG_WIFI_WEP_104_ASCII TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-wep-104-ascii" + +static void +test_read_wifi_wep_104_ascii (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_wep_key0 = "LoremIpsumSit"; + NMWepKeyType key_type; + + connection = connection_from_file (TEST_IFCFG_WIFI_WEP_104_ASCII, + NULL, + TYPE_WIRELESS, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wifi-wep-104-ascii-read", "failed to read %s: %s", TEST_IFCFG_WIFI_WEP_104_ASCII, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wifi-wep-104-ascii-verify", "failed to verify %s: %s", TEST_IFCFG_WIFI_WEP_104_ASCII, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wifi-wep-104-ascii-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + ASSERT (s_wireless != NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* Security */ + tmp = nm_setting_wireless_get_security (s_wireless); + ASSERT (tmp != NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + ASSERT (strcmp (tmp, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SEC); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY)); + ASSERT (s_wsec != NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + + /* Key management */ + ASSERT (strcmp (nm_setting_wireless_security_get_key_mgmt (s_wsec), "none") == 0, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + + /* WEP key index */ + ASSERT (nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) == 0, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX); + + /* WEP key type */ + key_type = nm_setting_wireless_security_get_wep_key_type (s_wsec); + ASSERT (key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected WEP key type %d", + TEST_IFCFG_WIFI_WEP_104_ASCII, + key_type); + + /* WEP key index 0 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 0); + ASSERT (tmp != NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + ASSERT (strcmp (tmp, expected_wep_key0) == 0, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); + + /* WEP key index 1 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 1); + ASSERT (tmp == NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1); + + /* WEP key index 2 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 2); + ASSERT (tmp == NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2); + + /* WEP key index 3 */ + tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 3); + ASSERT (tmp == NULL, + "wifi-wep-104-ascii-verify-wireless", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIFI_WEP_104_ASCII, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3); + + g_object_unref (connection); +} + #define TEST_IFCFG_WIFI_LEAP TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-leap" static void @@ -5080,6 +5449,178 @@ test_read_wifi_wep_eap_ttls_chap (void) g_object_unref (connection); } +#define TEST_IFCFG_WIRED_QETH_STATIC TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-qeth-static" + +static void +test_read_wired_qeth_static (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System test-wired-qeth-static"; + const GByteArray *array; + const char *expected_channel0 = "0.0.0600"; + const char *expected_channel1 = "0.0.0601"; + const char *expected_channel2 = "0.0.0602"; + const GPtrArray *subchannels; + + connection = connection_from_file (TEST_IFCFG_WIRED_QETH_STATIC, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wired-qeth-static-read", "failed to read %s: %s", TEST_IFCFG_WIRED_QETH_STATIC, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wired-qeth-static-verify", "failed to verify %s: %s", TEST_IFCFG_WIRED_QETH_STATIC, error->message); + + ASSERT (unmanaged == FALSE, + "wired-qeth-static-verify", "failed to verify %s: unexpected unmanaged value", TEST_IFCFG_WIRED_QETH_STATIC); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wired-qeth-static-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wired-qeth-static-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRED SETTING ===== */ + + s_wired = NM_SETTING_WIRED (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED)); + ASSERT (s_wired != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* MAC address */ + array = nm_setting_wired_get_mac_address (s_wired); + ASSERT (array == NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_MAC_ADDRESS); + + /* Subchannels */ + subchannels = nm_setting_wired_get_s390_subchannels (s_wired); + ASSERT (subchannels != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_SUBCHANNELS); + ASSERT (subchannels->len == 3, + "wired-qeth-static-verify-wired", "failed to verify %s: invalid %s / %s key (not 3 elements)", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_SUBCHANNELS); + + tmp = (const char *) g_ptr_array_index (subchannels, 0); + ASSERT (strcmp (tmp, expected_channel0) == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected subchannel #0", + TEST_IFCFG_WIRED_QETH_STATIC); + + tmp = (const char *) g_ptr_array_index (subchannels, 1); + ASSERT (strcmp (tmp, expected_channel1) == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected subchannel #1", + TEST_IFCFG_WIRED_QETH_STATIC); + + tmp = (const char *) g_ptr_array_index (subchannels, 2); + ASSERT (strcmp (tmp, expected_channel2) == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected subchannel #2", + TEST_IFCFG_WIRED_QETH_STATIC); + + /* Nettype */ + tmp = nm_setting_wired_get_s390_nettype (s_wired); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_NETTYPE); + ASSERT (strcmp (tmp, "qeth") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_NETTYPE); + + /* port name */ + tmp = nm_setting_wired_get_s390_option_by_key (s_wired, "portname"); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s s390 option 'portname'", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + ASSERT (strcmp (tmp, "OSAPORT") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s s390 option 'portname' value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* port number */ + tmp = nm_setting_wired_get_s390_option_by_key (s_wired, "portno"); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s s390 option 'portno'", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + ASSERT (strcmp (tmp, "0") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s s390 option 'portno' value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* layer */ + tmp = nm_setting_wired_get_s390_option_by_key (s_wired, "layer2"); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s s390 option 'layer2'", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + ASSERT (strcmp (tmp, "1") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s s390 option 'layer2' value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + ASSERT (s_ip4 != NULL, + "wired-qeth-static-verify-ip4", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_IP4_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0, + "wired-qeth-static-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + + g_object_unref (connection); +} + static void test_write_wired_static (void) { @@ -6063,6 +6604,8 @@ test_write_wifi_open (void) guint32 channel = 9, mtu = 1345; GByteArray *mac; const unsigned char mac_data[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + shvarFile *ifcfg; + char *tmp; connection = nm_connection_new (); ASSERT (connection != NULL, @@ -6157,6 +6700,22 @@ test_write_wifi_open (void) &route6file, &error, &ignore_error); + + /* Now make sure that the ESSID item isn't double-quoted (rh #606518) */ + ifcfg = svNewFile (testfile); + ASSERT (ifcfg != NULL, + "wifi-open-write-reread", "failed to load %s as shvarfile", testfile); + + tmp = svGetValue (ifcfg, "ESSID", TRUE); + ASSERT (tmp != NULL, + "wifi-open-write-reread", "failed to read ESSID key from %s", testfile); + + g_message ("%s", tmp); + ASSERT (strncmp (tmp, "\"\"", 2) != 0, + "wifi-open-write-reread", "unexpected ESSID double-quote in %s", testfile); + + svCloseFile (ifcfg); + unlink (testfile); ASSERT (reread != NULL, @@ -6752,6 +7311,308 @@ test_write_wifi_wep_passphrase (void) } static void +test_write_wifi_wep_40_ascii (void) +{ + NMConnection *connection; + NMConnection *reread; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *uuid; + gboolean success; + GError *error = NULL; + char *testfile = NULL; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GByteArray *ssid; + const unsigned char ssid_data[] = "blahblah40"; + struct stat statbuf; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "wifi-wep-40-ascii-write", "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + "wifi-wep-40-ascii-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wifi WEP 40 ASCII", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + ASSERT (s_wifi != NULL, + "wifi-wep-40-ascii-write", "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_byte_array_sized_new (sizeof (ssid_data)); + g_byte_array_append (ssid, ssid_data, sizeof (ssid_data)); + + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + + g_byte_array_free (ssid, TRUE); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + ASSERT (s_wsec != NULL, + "wifi-wep-40-ascii-write", "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, 2, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", + NULL); + nm_setting_wireless_security_set_wep_key (s_wsec, 0, "lorem"); + nm_setting_wireless_security_set_wep_key (s_wsec, 1, "ipsum"); + nm_setting_wireless_security_set_wep_key (s_wsec, 2, "dolor"); + nm_setting_wireless_security_set_wep_key (s_wsec, 3, "donec"); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wifi-wep-40-ascii-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + ASSERT (s_ip6 != NULL, + "wifi-wep-40-ascii-write", "failed to allocate new %s setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + "wifi-wep-40-ascii-write", "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + /* Save the ifcfg */ + success = writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile, + &error); + ASSERT (success == TRUE, + "wifi-wep-40-ascii-write", "failed to write connection to disk: %s", + (error && error->message) ? error->message : "(unknown)"); + + ASSERT (testfile != NULL, + "wifi-wep-40-ascii-write", "didn't get ifcfg file path back after writing connection"); + + /* re-read the connection for comparison */ + reread = connection_from_file (testfile, + NULL, + TYPE_WIRELESS, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + unlink (testfile); + + ASSERT (keyfile != NULL, + "wifi-wep-40-ascii-write-reread", "expected keyfile for '%s'", testfile); + + ASSERT (stat (keyfile, &statbuf) == 0, + "wifi-wep-40-ascii-write-reread", "couldn't stat() '%s'", keyfile); + ASSERT (S_ISREG (statbuf.st_mode), + "wifi-wep-40-ascii-write-reread", "keyfile '%s' wasn't a normal file", keyfile); + ASSERT ((statbuf.st_mode & 0077) == 0, + "wifi-wep-40-ascii-write-reread", "keyfile '%s' wasn't readable only by its owner", keyfile); + + unlink (keyfile); + + ASSERT (reread != NULL, + "wifi-wep-40-ascii-write-reread", "failed to read %s: %s", testfile, error->message); + + ASSERT (nm_connection_verify (reread, &error), + "wifi-wep-40-ascii-write-reread-verify", "failed to verify %s: %s", testfile, error->message); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "wifi-wep-40-ascii-write", "written and re-read connection weren't the same."); + + g_free (testfile); + g_object_unref (connection); + g_object_unref (reread); +} + +static void +test_write_wifi_wep_104_ascii (void) +{ + NMConnection *connection; + NMConnection *reread; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *uuid; + gboolean success; + GError *error = NULL; + char *testfile = NULL; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GByteArray *ssid; + const unsigned char ssid_data[] = "blahblah104"; + struct stat statbuf; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "wifi-wep-104-ascii-write", "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + "wifi-wep-104-ascii-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wifi WEP 104 ASCII", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + ASSERT (s_wifi != NULL, + "wifi-wep-104-ascii-write", "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_byte_array_sized_new (sizeof (ssid_data)); + g_byte_array_append (ssid, ssid_data, sizeof (ssid_data)); + + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_MODE, "infrastructure", + NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + + g_byte_array_free (ssid, TRUE); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + ASSERT (s_wsec != NULL, + "wifi-wep-104-ascii-write", "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, 0, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + nm_setting_wireless_security_set_wep_key (s_wsec, 0, "LoremIpsumSit"); + nm_setting_wireless_security_set_wep_key (s_wsec, 1, "AlfaBetaGamma"); + nm_setting_wireless_security_set_wep_key (s_wsec, 2, "WEP-104 ASCII"); + nm_setting_wireless_security_set_wep_key (s_wsec, 3, "thisismyascii"); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wifi-wep-104-ascii-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + ASSERT (s_ip6 != NULL, + "wifi-wep-104-ascii-write", "failed to allocate new %s setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + "wifi-wep-104-ascii-write", "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + /* Save the ifcfg */ + success = writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile, + &error); + ASSERT (success == TRUE, + "wifi-wep-104-ascii-write", "failed to write connection to disk: %s", + (error && error->message) ? error->message : "(unknown)"); + + ASSERT (testfile != NULL, + "wifi-wep-104-ascii-write", "didn't get ifcfg file path back after writing connection"); + + /* re-read the connection for comparison */ + reread = connection_from_file (testfile, + NULL, + TYPE_WIRELESS, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + unlink (testfile); + + ASSERT (keyfile != NULL, + "wifi-wep-104-ascii-write-reread", "expected keyfile for '%s'", testfile); + + ASSERT (stat (keyfile, &statbuf) == 0, + "wifi-wep-104-ascii-write-reread", "couldn't stat() '%s'", keyfile); + ASSERT (S_ISREG (statbuf.st_mode), + "wifi-wep-104-ascii-write-reread", "keyfile '%s' wasn't a normal file", keyfile); + ASSERT ((statbuf.st_mode & 0077) == 0, + "wifi-wep-104-ascii-write-reread", "keyfile '%s' wasn't readable only by its owner", keyfile); + + unlink (keyfile); + + ASSERT (reread != NULL, + "wifi-wep-104-ascii-write-reread", "failed to read %s: %s", testfile, error->message); + + ASSERT (nm_connection_verify (reread, &error), + "wifi-wep-104-ascii-write-reread-verify", "failed to verify %s: %s", testfile, error->message); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "wifi-wep-104-ascii-write", "written and re-read connection weren't the same."); + + g_free (testfile); + g_object_unref (connection); + g_object_unref (reread); +} + +static void test_write_wifi_leap (void) { NMConnection *connection; @@ -8129,6 +8990,140 @@ test_read_ibft_malformed (const char *name, const char *iscsiadm_path) } static void +test_write_wired_qeth_dhcp (void) +{ + NMConnection *connection; + NMConnection *reread; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *uuid; + GPtrArray *subchans; + gboolean success; + GError *error = NULL; + char *testfile = NULL; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "wired-qeth-dhcp-write", "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wired qeth Static", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + ASSERT (s_wired != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_WIRED_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + subchans = g_ptr_array_sized_new (3); + g_ptr_array_add (subchans, "0.0.600"); + g_ptr_array_add (subchans, "0.0.601"); + g_ptr_array_add (subchans, "0.0.602"); + g_object_set (s_wired, + NM_SETTING_WIRED_S390_SUBCHANNELS, subchans, + NM_SETTING_WIRED_S390_NETTYPE, "qeth", + NULL); + g_ptr_array_free (subchans, TRUE); + + nm_setting_wired_add_s390_option (s_wired, "portname", "FOOBAR"); + nm_setting_wired_add_s390_option (s_wired, "portno", "1"); + nm_setting_wired_add_s390_option (s_wired, "layer2", "0"); + nm_setting_wired_add_s390_option (s_wired, "protocol", "blahbalh"); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + ASSERT (s_ip6 != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + /* Verify */ + ASSERT (nm_connection_verify (connection, &error) == TRUE, + "wired-qeth-dhcp-write", "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + /* Save the ifcfg */ + success = writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile, + &error); + ASSERT (success == TRUE, + "wired-qeth-dhcp-write", "failed to write connection to disk: %s", + (error && error->message) ? error->message : "(unknown)"); + + ASSERT (testfile != NULL, + "wired-qeth-dhcp-write", "didn't get ifcfg file path back after writing connection"); + + /* re-read the connection for comparison */ + reread = connection_from_file (testfile, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + unlink (testfile); + + ASSERT (reread != NULL, + "wired-qeth-dhcp-write-reread", "failed to read %s: %s", testfile, error->message); + + ASSERT (nm_connection_verify (reread, &error), + "wired-qeth-dhcp-write-reread-verify", "failed to verify %s: %s", testfile, error->message); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "wired-qeth-dhcp-write", "written and re-read connection weren't the same."); + + if (route6file) + unlink (route6file); + + g_free (testfile); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); + g_object_unref (reread); +} + +static void test_write_wired_pppoe (void) { NMConnection *connection; @@ -8390,6 +9385,96 @@ test_write_mobile_broadband (gboolean gsm) g_object_unref (connection); } +#define TEST_IFCFG_BRIDGE_MAIN TEST_IFCFG_DIR"/network-scripts/ifcfg-test-bridge-main" + +static void +test_read_bridge_main (void) +{ + NMConnection *connection; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + + connection = connection_from_file (TEST_IFCFG_BRIDGE_MAIN, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection == NULL, + "bridge-main-read", "unexpected success reading %s", TEST_IFCFG_BRIDGE_MAIN); +} + +#define TEST_IFCFG_BRIDGE_COMPONENT TEST_IFCFG_DIR"/network-scripts/ifcfg-test-bridge-component" + +static void +test_read_bridge_component (void) +{ + NMConnection *connection; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + + connection = connection_from_file (TEST_IFCFG_BRIDGE_COMPONENT, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "bridge-component-read", "unexpected failure reading %s", TEST_IFCFG_BRIDGE_COMPONENT); + + ASSERT (unmanaged != NULL, + "bridge-component-read", "missing unmanaged spec from %s", TEST_IFCFG_BRIDGE_COMPONENT); + + ASSERT (g_strcmp0 (unmanaged, "mac:00:22:15:59:62:97") == 0, + "bridge-component-read", "unexpected unmanaged spec from %s", TEST_IFCFG_BRIDGE_COMPONENT); + + g_object_unref (connection); + g_free (unmanaged); +} + +#define TEST_IFCFG_VLAN_INTERFACE TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-interface" + +static void +test_read_vlan_interface (void) +{ + NMConnection *connection; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + + connection = connection_from_file (TEST_IFCFG_VLAN_INTERFACE, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection == NULL, + "vlan-interface-read", "unexpected success reading %s", TEST_IFCFG_VLAN_INTERFACE); +} + #define TEST_IFCFG_WIFI_OPEN_SSID_BAD_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex" #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted" #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-hex" @@ -8403,11 +9488,9 @@ test_write_mobile_broadband (gboolean gsm) int main (int argc, char **argv) { GError *error = NULL; - DBusGConnection *bus; char *base; g_type_init (); - bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); if (!nm_utils_init (&error)) FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); @@ -8429,6 +9512,7 @@ int main (int argc, char **argv) test_read_wired_static_routes_legacy (); test_read_wired_ipv6_manual (); test_read_wired_ipv6_only (); + test_read_wired_dhcp6_only (); test_read_onboot_no (); test_read_wired_8021x_peap_mschapv2 (); test_read_wifi_open (); @@ -8441,6 +9525,8 @@ int main (int argc, char **argv) test_read_wifi_wep (); test_read_wifi_wep_adhoc (); test_read_wifi_wep_passphrase (); + test_read_wifi_wep_40_ascii (); + test_read_wifi_wep_104_ascii (); test_read_wifi_leap (); test_read_wifi_wpa_psk (); test_read_wifi_wpa_psk_unquoted (); @@ -8450,6 +9536,7 @@ int main (int argc, char **argv) test_read_wifi_wpa_eap_tls (); test_read_wifi_wpa_eap_ttls_tls (); test_read_wifi_wep_eap_ttls_chap (); + test_read_wired_qeth_static (); test_write_wired_static (); test_write_wired_static_ip6_only (); @@ -8462,6 +9549,8 @@ int main (int argc, char **argv) test_write_wifi_wep (); test_write_wifi_wep_adhoc (); test_write_wifi_wep_passphrase (); + test_write_wifi_wep_40_ascii (); + test_write_wifi_wep_104_ascii (); test_write_wifi_leap (); test_write_wifi_wpa_psk ("Test Write Wifi WPA PSK", "wifi-wpa-psk-write", @@ -8497,6 +9586,7 @@ int main (int argc, char **argv) test_write_wifi_wpa_eap_tls (); test_write_wifi_wpa_eap_ttls_tls (); test_write_wifi_wpa_eap_ttls_mschapv2 (); + test_write_wired_qeth_dhcp (); /* iSCSI / ibft */ test_read_ibft_dhcp (); @@ -8513,6 +9603,9 @@ int main (int argc, char **argv) test_write_vpn (); test_write_mobile_broadband (TRUE); test_write_mobile_broadband (FALSE); + test_read_bridge_main (); + test_read_bridge_component (); + test_read_vlan_interface (); base = g_path_get_basename (argv[0]); fprintf (stdout, "%s: SUCCESS\n", base); diff --git a/system-settings/plugins/ifcfg-rh/utils.c b/system-settings/plugins/ifcfg-rh/utils.c index 211458bec8..92a0b802f0 100644 --- a/system-settings/plugins/ifcfg-rh/utils.c +++ b/system-settings/plugins/ifcfg-rh/utils.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2008 - 2009 Red Hat, Inc. + * (C) Copyright 2008 - 2010 Red Hat, Inc. */ #include <glib.h> @@ -115,6 +115,27 @@ utils_hexstr2bin (const char *hex, size_t len) /* End from hostap */ +/* + * Check ';[a-fA-F0-9]{8}' file suffix used for temporary files by rpm when + * installing packages. + * + * Implementation taken from upstart. + */ +static gboolean +check_rpm_temp_suffix (const char *path) +{ + const char *ptr; + + g_return_val_if_fail (path != NULL, FALSE); + + /* Matches *;[a-fA-F0-9]{8}; used by rpm */ + ptr = strrchr (path, ';'); + if (ptr && (strspn (ptr + 1, "abcdefABCDEF0123456789") == 8) + && (! ptr[9])) + return TRUE; + return FALSE; +} + static gboolean check_suffix (const char *base, const char *tag) { @@ -160,7 +181,10 @@ utils_should_ignore_file (const char *filename, gboolean only_ifcfg) && !check_suffix (base, TILDE_TAG) && !check_suffix (base, ORIG_TAG) && !check_suffix (base, REJ_TAG) - && !check_suffix (base, RPMNEW_TAG)) + && !check_suffix (base, RPMNEW_TAG) + && !check_suffix (base, AUGNEW_TAG) + && !check_suffix (base, AUGTMP_TAG) + && !check_rpm_temp_suffix (base)) ignore = FALSE; g_free (base); diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c index 30ef6e3941..cf1e2663a5 100644 --- a/system-settings/plugins/ifcfg-rh/writer.c +++ b/system-settings/plugins/ifcfg-rh/writer.c @@ -90,7 +90,7 @@ write_secret_file (const char *path, tmppath = g_malloc0 (strlen (path) + 10); if (!tmppath) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not allocate memory for temporary file for '%s'", path); return FALSE; @@ -102,7 +102,7 @@ write_secret_file (const char *path, errno = 0; fd = mkstemp (tmppath); if (fd < 0) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not create temporary file for '%s': %d", path, errno); goto out; @@ -113,7 +113,7 @@ write_secret_file (const char *path, if (fchmod (fd, S_IRUSR | S_IWUSR)) { close (fd); unlink (tmppath); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not set permissions for temporary file '%s': %d", path, errno); goto out; @@ -124,7 +124,7 @@ write_secret_file (const char *path, if (written != len) { close (fd); unlink (tmppath); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not write temporary file for '%s': %d", path, errno); goto out; @@ -135,7 +135,7 @@ write_secret_file (const char *path, errno = 0; if (rename (tmppath, path)) { unlink (tmppath); - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not rename temporary file to '%s': %d", path, errno); goto out; @@ -302,7 +302,7 @@ write_object (NMSetting8021x *s_8021x, new_file = utils_cert_path (ifcfg->fileName, objtype->suffix); if (!new_file) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not create file path for %s / %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key); return FALSE; @@ -317,7 +317,7 @@ write_object (NMSetting8021x *s_8021x, svSetValue (ifcfg, objtype->ifcfg_key, new_file, FALSE); return TRUE; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not write certificate/key for %s / %s: %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key, (write_error && write_error->message) ? write_error->message : "(unknown)"); @@ -535,7 +535,7 @@ write_wireless_security_setting (NMConnection *connection, s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); if (!s_wsec) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); return FALSE; } @@ -605,6 +605,8 @@ write_wireless_security_setting (NMConnection *connection, key = nm_setting_wireless_security_get_wep_key (s_wsec, i); if (key) { + char *ascii_key = NULL; + /* Passphrase needs a different ifcfg key since with WEP, there * are some passphrases that are indistinguishable from WEP hex * keys. @@ -612,11 +614,19 @@ write_wireless_security_setting (NMConnection *connection, key_type = nm_setting_wireless_security_get_wep_key_type (s_wsec); if (key_type == NM_WEP_KEY_TYPE_PASSPHRASE) tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1); - else + else { tmp = g_strdup_printf ("KEY%d", i + 1); + /* Add 's:' prefix for ASCII keys */ + if (strlen (key) == 5 || strlen (key) == 13) { + ascii_key = g_strdup_printf ("s:%s", key); + key = ascii_key; + } + } + set_secret (ifcfg, tmp, key, FALSE); g_free (tmp); + g_free (ascii_key); } } } @@ -694,7 +704,7 @@ write_wireless_setting (NMConnection *connection, { NMSettingWireless *s_wireless; char *tmp, *tmp2; - const GByteArray *ssid, *mac, *bssid; + const GByteArray *ssid, *device_mac, *cloned_mac, *bssid; const char *mode; char buf[33]; guint32 mtu, chan, i; @@ -702,21 +712,31 @@ write_wireless_setting (NMConnection *connection, s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); if (!s_wireless) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } svSetValue (ifcfg, "HWADDR", NULL, FALSE); - mac = nm_setting_wireless_get_mac_address (s_wireless); - if (mac) { + device_mac = nm_setting_wireless_get_mac_address (s_wireless); + if (device_mac) { tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", - mac->data[0], mac->data[1], mac->data[2], - mac->data[3], mac->data[4], mac->data[5]); + device_mac->data[0], device_mac->data[1], device_mac->data[2], + device_mac->data[3], device_mac->data[4], device_mac->data[5]); svSetValue (ifcfg, "HWADDR", tmp, FALSE); g_free (tmp); } + svSetValue (ifcfg, "MACADDR", NULL, FALSE); + cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); + if (cloned_mac) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + cloned_mac->data[0], cloned_mac->data[1], cloned_mac->data[2], + cloned_mac->data[3], cloned_mac->data[4], cloned_mac->data[5]); + svSetValue (ifcfg, "MACADDR", tmp, FALSE); + g_free (tmp); + } + svSetValue (ifcfg, "MTU", NULL, FALSE); mtu = nm_setting_wireless_get_mtu (s_wireless); if (mtu) { @@ -727,12 +747,12 @@ write_wireless_setting (NMConnection *connection, ssid = nm_setting_wireless_get_ssid (s_wireless); if (!ssid) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } if (!ssid->len || ssid->len > 32) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; } @@ -758,13 +778,20 @@ write_wireless_setting (NMConnection *connection, svSetValue (ifcfg, "ESSID", str->str, TRUE); g_string_free (str, TRUE); } else { - /* Printable SSIDs get quoted */ + /* Printable SSIDs always get quoted */ memset (buf, 0, sizeof (buf)); memcpy (buf, ssid->data, ssid->len); - tmp2 = svEscape (buf); - tmp = g_strdup_printf ("\"%s\"", tmp2); - svSetValue (ifcfg, "ESSID", tmp, TRUE); - g_free (tmp2); + tmp = svEscape (buf); + + /* svEscape will usually quote the string, but just for consistency, + * if svEscape doesn't quote the ESSID, we quote it ourselves. + */ + if (tmp[0] != '"' && tmp[strlen (tmp) - 1] != '"') { + tmp2 = g_strdup_printf ("\"%s\"", tmp); + svSetValue (ifcfg, "ESSID", tmp2, TRUE); + g_free (tmp2); + } else + svSetValue (ifcfg, "ESSID", tmp, TRUE); g_free (tmp); } @@ -775,7 +802,7 @@ write_wireless_setting (NMConnection *connection, svSetValue (ifcfg, "MODE", "Ad-Hoc", FALSE); adhoc = TRUE; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid mode '%s' in '%s' setting", mode, NM_SETTING_WIRELESS_SETTING_NAME); return FALSE; @@ -813,26 +840,39 @@ static gboolean write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingWired *s_wired; - const GByteArray *mac; + const GByteArray *device_mac, *cloned_mac; char *tmp; - guint32 mtu; + const char *nettype, *portname, *s390_key, *s390_val; + guint32 mtu, num_opts, i; + const GPtrArray *s390_subchannels; + GString *str; s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); if (!s_wired) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_WIRED_SETTING_NAME); return FALSE; } - mac = nm_setting_wired_get_mac_address (s_wired); - if (mac) { + svSetValue (ifcfg, "HWADDR", NULL, FALSE); + device_mac = nm_setting_wired_get_mac_address (s_wired); + if (device_mac) { tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", - mac->data[0], mac->data[1], mac->data[2], - mac->data[3], mac->data[4], mac->data[5]); + device_mac->data[0], device_mac->data[1], device_mac->data[2], + device_mac->data[3], device_mac->data[4], device_mac->data[5]); svSetValue (ifcfg, "HWADDR", tmp, FALSE); g_free (tmp); } + cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); + if (cloned_mac) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + cloned_mac->data[0], cloned_mac->data[1], cloned_mac->data[2], + cloned_mac->data[3], cloned_mac->data[4], cloned_mac->data[5]); + svSetValue (ifcfg, "MACADDR", tmp, FALSE); + g_free (tmp); + } + svSetValue (ifcfg, "MTU", NULL, FALSE); mtu = nm_setting_wired_get_mtu (s_wired); if (mtu) { @@ -841,6 +881,53 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (tmp); } + svSetValue (ifcfg, "SUBCHANNELS", NULL, FALSE); + s390_subchannels = nm_setting_wired_get_s390_subchannels (s_wired); + if (s390_subchannels) { + if (s390_subchannels->len == 2) { + tmp = g_strdup_printf ("%s,%s", + (const char *) g_ptr_array_index (s390_subchannels, 0), + (const char *) g_ptr_array_index (s390_subchannels, 1)); + } else if (s390_subchannels->len == 3) { + tmp = g_strdup_printf ("%s,%s,%s", + (const char *) g_ptr_array_index (s390_subchannels, 0), + (const char *) g_ptr_array_index (s390_subchannels, 1), + (const char *) g_ptr_array_index (s390_subchannels, 2)); + } + svSetValue (ifcfg, "SUBCHANNELS", tmp, FALSE); + g_free (tmp); + } + + svSetValue (ifcfg, "NETTYPE", NULL, FALSE); + nettype = nm_setting_wired_get_s390_nettype (s_wired); + if (nettype) + svSetValue (ifcfg, "NETTYPE", nettype, FALSE); + + svSetValue (ifcfg, "PORTNAME", NULL, FALSE); + portname = nm_setting_wired_get_s390_option_by_key (s_wired, "portname"); + if (portname) + svSetValue (ifcfg, "PORTNAME", portname, FALSE); + + svSetValue (ifcfg, "OPTIONS", NULL, FALSE); + num_opts = nm_setting_wired_get_num_s390_options (s_wired); + if (s390_subchannels && num_opts) { + str = g_string_sized_new (30); + for (i = 0; i < num_opts; i++) { + nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val); + + /* portname is handled separately */ + if (!strcmp (s390_key, "portname")) + continue; + + if (str->len) + g_string_append_c (str, ' '); + g_string_append_printf (str, "%s=%s", s390_key, s390_val); + } + if (str->len) + svSetValue (ifcfg, "OPTIONS", str->str, FALSE); + g_string_free (str, TRUE); + } + svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE); return TRUE; @@ -911,7 +998,7 @@ write_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError g_strfreev (route_items); if (!g_file_set_contents (filename, route_contents, -1, NULL)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Writing route file '%s' failed", filename); goto error; } @@ -934,6 +1021,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) guint32 i, num; GString *searches; gboolean success = FALSE; + gboolean fake_ip4 = FALSE; const char *method = NULL; s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); @@ -971,15 +1059,19 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) return TRUE; } - value = nm_setting_ip4_config_get_method (s_ip4); - g_assert (value); - if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + /* Temporarily create fake IP4 setting if missing; method set to DHCP above */ + if (!s_ip4) { + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + fake_ip4 = TRUE; + } + + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) svSetValue (ifcfg, "BOOTPROTO", "dhcp", FALSE); - else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) svSetValue (ifcfg, "BOOTPROTO", "none", FALSE); - else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) svSetValue (ifcfg, "BOOTPROTO", "autoip", FALSE); - else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) svSetValue (ifcfg, "BOOTPROTO", "shared", FALSE); num = nm_setting_ip4_config_get_num_addresses (s_ip4); @@ -1069,7 +1161,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) svSetValue (ifcfg, "PEERROUTES", NULL, FALSE); svSetValue (ifcfg, "DHCP_HOSTNAME", NULL, FALSE); svSetValue (ifcfg, "DHCP_CLIENT_ID", NULL, FALSE); - if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { svSetValue (ifcfg, "PEERDNS", nm_setting_ip4_config_get_ignore_auto_dns (s_ip4) ? "no" : "yes", FALSE); @@ -1094,7 +1186,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) /* Static routes - route-<name> file */ route_path = utils_get_route_path (ifcfg->fileName); if (!route_path) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not get route file path for '%s'", ifcfg->fileName); goto out; } @@ -1105,7 +1197,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (route_path); routefile = utils_get_route_ifcfg (ifcfg->fileName, TRUE); if (!routefile) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not create route file '%s'", routefile->fileName); goto out; } @@ -1161,7 +1253,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (metric_key); } if (svWriteFile (routefile, 0644)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not update route file '%s'", routefile->fileName); svCloseFile (routefile); goto out; @@ -1177,6 +1269,9 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) success = TRUE; out: + if (fake_ip4) + g_object_unref (s_ip4); + return success; } @@ -1227,7 +1322,7 @@ write_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **err g_strfreev (route_items); if (!g_file_set_contents (filename, route_contents, -1, NULL)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Writing route6 file '%s' failed", filename); goto error; } @@ -1256,7 +1351,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); if (!s_ip6) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_IP6_CONFIG_SETTING_NAME); return FALSE; } @@ -1384,7 +1479,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) /* Static routes go to route6-<dev> file */ route6_path = utils_get_route6_path (ifcfg->fileName); if (!route6_path) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Could not get route6 file path for '%s'", ifcfg->fileName); goto error; } @@ -1438,7 +1533,7 @@ write_connection (NMConnection *connection, s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); if (!s_con) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_CONNECTION_SETTING_NAME); return FALSE; } @@ -1457,14 +1552,14 @@ write_connection (NMConnection *connection, } if (!ifcfg) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Failed to open/create ifcfg file '%s'", ifcfg_name); goto out; } type = nm_setting_connection_get_connection_type (s_con); if (!type) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing connection type!"); goto out; } @@ -1472,7 +1567,7 @@ write_connection (NMConnection *connection, if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { // FIXME: can't write PPPoE at this time if (nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Can't write connection type '%s'", NM_SETTING_PPPOE_SETTING_NAME); goto out; @@ -1485,7 +1580,7 @@ write_connection (NMConnection *connection, if (!write_wireless_setting (connection, ifcfg, &no_8021x, error)) goto out; } else { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Can't write connection type '%s'", type); goto out; } @@ -1507,7 +1602,7 @@ write_connection (NMConnection *connection, write_connection_setting (s_con, ifcfg); if (svWriteFile (ifcfg, 0644)) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Can't write connection '%s'", ifcfg->fileName); goto out; } diff --git a/system-settings/plugins/ifnet/Makefile.am b/system-settings/plugins/ifnet/Makefile.am new file mode 100644 index 0000000000..f63f8ca710 --- /dev/null +++ b/system-settings/plugins/ifnet/Makefile.am @@ -0,0 +1,61 @@ +SUBDIRS = . tests +INCLUDES = \ + -I$(top_srcdir)/src/system-settings \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-glib \ + -I$(top_srcdir)/libnm-util + +pkglib_LTLIBRARIES = libnm-settings-plugin-ifnet.la + +noinst_LTLIBRARIES = lib-ifnet-io.la + +libnm_settings_plugin_ifnet_la_SOURCES = \ + nm-ifnet-connection.c \ + nm-ifnet-connection.h \ + plugin.c \ + plugin.h + +libnm_settings_plugin_ifnet_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(GMODULE_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\"\ + -g + + +libnm_settings_plugin_ifnet_la_LDFLAGS = -module -avoid-version +libnm_settings_plugin_ifnet_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/libnm-glib/libnm-glib.la \ + lib-ifnet-io.la\ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) \ + $(GUDEV_LIBS) \ + $(GIO_LIBS) + +lib_ifnet_io_la_SOURCES = \ + net_parser.c\ + net_parser.h\ + connection_parser.c \ + connection_parser.h \ + net_utils.h\ + net_utils.c\ + wpa_parser.h\ + wpa_parser.c + +lib_ifnet_io_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DSBINDIR=\"$(sbindir)\"\ + -g + +lib_ifnet_io_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS)\ + $(GIO_LIBS) + + diff --git a/system-settings/plugins/ifnet/connection_parser.c b/system-settings/plugins/ifnet/connection_parser.c new file mode 100644 index 0000000000..f9fae51f35 --- /dev/null +++ b/system-settings/plugins/ifnet/connection_parser.c @@ -0,0 +1,2997 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <string.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <netinet/ether.h> +#include <errno.h> +#include <ctype.h> +#include <glib/gi18n.h> + +#include <nm-setting-connection.h> +#include <nm-setting-ip4-config.h> +#include <nm-setting-ip6-config.h> +#include <nm-setting-ppp.h> +#include <nm-setting-pppoe.h> +#include <nm-setting-wired.h> +#include <nm-setting-wireless.h> +#include <nm-setting-8021x.h> +#include <nm-system-config-interface.h> +#include <nm-utils.h> + +#include "net_utils.h" +#include "wpa_parser.h" +#include "connection_parser.h" +#include "nm-ifnet-connection.h" + +static const char * +get_prefix (void) +{ + return _("System"); +} + +static void +update_connection_id (NMConnection * connection, gchar * conn_name) +{ + gchar *idstr = NULL; + gchar *uuid_base = NULL; + gchar *uuid = NULL; + NMSettingConnection *setting; + + idstr = g_strdup_printf ("%s (%s)", get_prefix (), conn_name); + uuid_base = idstr; + uuid = nm_utils_uuid_generate_from_string (uuid_base); + setting = + (NMSettingConnection *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_CONNECTION); + g_object_set (setting, NM_SETTING_CONNECTION_ID, idstr, + NM_SETTING_CONNECTION_UUID, uuid, NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "update_connection_setting_from_config_block: name:%s, id:%s, uuid: %s", + conn_name, idstr, uuid); + + g_free (uuid); + g_free (idstr); +} + +static gboolean +eap_simple_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, gboolean phase2, GError ** error); +static gboolean eap_tls_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, + gboolean phase2, GError ** error); + +static gboolean eap_peap_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, + gboolean phase2, GError ** error); + +static gboolean eap_ttls_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, + gboolean phase2, GError ** error); + +typedef struct { + const char *method; + gboolean (*reader) (const char *eap_method, gchar * ssid, + NMSetting8021x * s_8021x, + gboolean phase2, GError ** error); + gboolean wifi_phase2_only; +} EAPReader; + +static EAPReader eap_readers[] = { + {"md5", eap_simple_reader, TRUE}, + {"pap", eap_simple_reader, TRUE}, + {"chap", eap_simple_reader, TRUE}, + {"mschap", eap_simple_reader, TRUE}, + {"mschapv2", eap_simple_reader, TRUE}, + {"leap", eap_simple_reader, TRUE}, + {"tls", eap_tls_reader, FALSE}, + {"peap", eap_peap_reader, FALSE}, + {"ttls", eap_ttls_reader, FALSE}, + {NULL, NULL} +}; + +/* reading identity and password */ +static gboolean +eap_simple_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, gboolean phase2, GError ** error) +{ + char *value; + + /* identity */ + value = wpa_get_value (ssid, "identity"); + if (!value) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", + eap_method); + return FALSE; + } + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); + + /* password */ + value = wpa_get_value (ssid, "password"); + if (!value) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing IEEE_8021X_PASSWORD for EAP method '%s'.", + eap_method); + return FALSE; + } + + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); + + return TRUE; +} + +static gboolean +eap_tls_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, gboolean phase2, GError ** error) +{ + char *value; + char *ca_cert = NULL; + char *client_cert = NULL; + char *privkey = NULL; + char *privkey_password = NULL; + gboolean success = FALSE; + NMSetting8021xCKFormat privkey_format = + NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + + /* identity */ + value = wpa_get_value (ssid, "identity"); + if (!value) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", + eap_method); + return FALSE; + } + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); + + /* ca cert */ + ca_cert = wpa_get_value (ssid, phase2 ? "ca_cert2" : "ca_cert"); + if (ca_cert) { + if (phase2) { + if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } else { + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } + } else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: missing %s for EAP" + " method '%s'; this is insecure!", + phase2 ? "IEEE_8021X_INNER_CA_CERT" : + "IEEE_8021X_CA_CERT", eap_method); + } + + /* Private key password */ + privkey_password = wpa_get_value (ssid, + phase2 ? "private_key_passwd2" : + "private_key_passwd"); + + if (!privkey_password) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing %s for EAP method '%s'.", + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : + "IEEE_8021X_PRIVATE_KEY_PASSWORD", eap_method); + goto done; + } + + /* The private key itself */ + privkey = wpa_get_value (ssid, phase2 ? "private_key2" : "private_key"); + if (!privkey) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing %s for EAP method '%s'.", + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : + "IEEE_8021X_PRIVATE_KEY", eap_method); + goto done; + } + + if (phase2) { + if (!nm_setting_802_1x_set_phase2_private_key (s_8021x, + privkey, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + &privkey_format, + error)) + goto done; + } else { + if (!nm_setting_802_1x_set_private_key (s_8021x, + privkey, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + &privkey_format, error)) + goto done; + } + + /* Only set the client certificate if the private key is not PKCS#12 format, + * as NM (due to supplicant restrictions) requires. If the key was PKCS#12, + * then nm_setting_802_1x_set_private_key() already set the client certificate + * to the same value as the private key. + */ + if (privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY + || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) { + client_cert = wpa_get_value (ssid, + phase2 ? "client_cert2" : + "client_cert"); + if (!client_cert) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing %s for EAP method '%s'.", + phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : + "IEEE_8021X_CLIENT_CERT", eap_method); + goto done; + } + + if (phase2) { + if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, + client_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) + goto done; + } else { + if (!nm_setting_802_1x_set_client_cert (s_8021x, + client_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } + } + + success = TRUE; + + done: + return success; +} + +static gboolean +eap_peap_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, gboolean phase2, GError ** error) +{ + char *ca_cert = NULL; + char *inner_auth = NULL; + char *peapver = NULL; + char *lower; + char **list = NULL, **iter; + gboolean success = FALSE; + + ca_cert = wpa_get_value (ssid, "ca_cert"); + if (ca_cert) { + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, " warning: missing " + "IEEE_8021X_CA_CERT for EAP method '%s'; this is" + " insecure!", eap_method); + } + + peapver = wpa_get_value (ssid, "phase1"); + /* peap version, default is automatic */ + if (peapver && strstr (peapver, "peapver")) { + if (strstr (peapver, "peapver=0")) + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, + "0", NULL); + else if (strstr (peapver, "peapver=1")) + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, + "1", NULL); + else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Unknown IEEE_8021X_PEAP_VERSION value '%s'", + peapver); + goto done; + } + } + + /* peaplabel */ + if (peapver && strstr (peapver, "peaplabel=1")) + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", + NULL); + + inner_auth = wpa_get_value (ssid, "phase2"); + if (!inner_auth) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing IEEE_8021X_INNER_AUTH_METHODS."); + goto done; + } + /* Handle options for the inner auth method */ + list = g_strsplit (inner_auth, " ", 0); + for (iter = list; iter && *iter; iter++) { + gchar *pos = NULL; + + if (!strlen (*iter)) + continue; + + if (!(pos = strstr (*iter, "MSCHAPV2")) + || !(pos = strstr (*iter, "MD5")) + || !(pos = strstr (*iter, "GTC"))) { + if (!eap_simple_reader + (pos, ssid, s_8021x, TRUE, error)) + goto done; + } else if (!(pos = strstr (*iter, "TLS"))) { + if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, error)) + goto done; + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", + *iter); + goto done; + } + + pos = strchr (*iter, '='); + pos++; + lower = g_ascii_strdown (pos, -1); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, + NULL); + g_free (lower); + break; + } + + if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "No valid IEEE_8021X_INNER_AUTH_METHODS found."); + goto done; + } + + success = TRUE; + + done: + if (list) + g_strfreev (list); + return success; +} + +static gboolean +eap_ttls_reader (const char *eap_method, + gchar * ssid, + NMSetting8021x * s_8021x, gboolean phase2, GError ** error) +{ + gboolean success = FALSE; + char *anon_ident = NULL; + char *ca_cert = NULL; + char *tmp; + char **list = NULL, **iter; + char *inner_auth = NULL; + + /* ca cert */ + ca_cert = wpa_get_value (ssid, "ca_cert"); + if (ca_cert) { + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, " warning: missing " + "IEEE_8021X_CA_CERT for EAP method '%s'; this is" + " insecure!", eap_method); + } + + /* anonymous indentity for tls */ + anon_ident = wpa_get_value (ssid, "anonymous_identity"); + if (anon_ident && strlen (anon_ident)) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + anon_ident, NULL); + + tmp = wpa_get_value (ssid, "phase2"); + if (!tmp) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing IEEE_8021X_INNER_AUTH_METHODS."); + goto done; + } + + /* Handle options for the inner auth method */ + inner_auth = g_ascii_strdown (tmp, -1); + list = g_strsplit (inner_auth, " ", 0); + for (iter = list; iter && *iter; iter++) { + gchar *pos = NULL; + + if (!strlen (*iter)) + continue; + if ((pos = strstr (*iter, "mschapv2")) != NULL + || (pos = strstr (*iter, "mschap")) != NULL + || (pos = strstr (*iter, "pap")) != NULL + || (pos = strstr (*iter, "chap")) != NULL) { + if (!eap_simple_reader + (pos, ssid, s_8021x, TRUE, error)) + goto done; + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, + pos, NULL); + } else if ((pos = strstr (*iter, "tls")) != NULL) { + if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, error)) + goto done; + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, + "tls", NULL); + } else if ((pos = strstr (*iter, "mschapv2")) != NULL + || (pos = strstr (*iter, "md5")) != NULL) { + if (!eap_simple_reader + (pos, ssid, s_8021x, TRUE, error)) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "SIMPLE ERROR"); + goto done; + } + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, + pos, NULL); + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", + *iter); + goto done; + } + break; + } + + success = TRUE; + done: + if (list) + g_strfreev (list); + if (inner_auth) + g_free (inner_auth); + return success; +} + +/* type is already decided by net_parser, this function is just used to + * doing tansformation*/ +static const gchar * +guess_connection_type (gchar * conn_name) +{ + const gchar *type = ifnet_get_data (conn_name, "type"); + const gchar *name = conn_name; + const gchar *ret_type = NULL; + + if (name && !strcmp ("ppp", type)) + ret_type = NM_SETTING_PPPOE_SETTING_NAME; + + if (name && !strcmp ("wireless", type)) + ret_type = NM_SETTING_WIRELESS_SETTING_NAME; + + if (!ret_type) + ret_type = NM_SETTING_WIRED_SETTING_NAME; + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "guessed connection type (%s) = %s", name, ret_type); + return ret_type; +} + +/* Reading mac address for setting connection option. + * Unmanaged device mac address is required by NetworkManager*/ +static gboolean +read_mac_address (gchar * conn_name, GByteArray ** array, GError ** error) +{ + gchar *value = ifnet_get_data (conn_name, "mac"); + struct ether_addr *mac; + + if (!value || !strlen (value)) { + return TRUE; + } + + mac = ether_aton (value); + if (!mac) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "The MAC address '%s' was invalid.", value); + return FALSE; + } + + *array = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (*array, (guint8 *) mac->ether_addr_octet, + ETH_ALEN); + return TRUE; +} + +static void +make_wired_connection_setting (NMConnection * connection, gchar * conn_name, + GError ** error) +{ + GByteArray *mac = NULL; + NMSettingWired *s_wired = NULL; + gchar *value = NULL; + + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + + /* mtu_xxx */ + value = ifnet_get_data (conn_name, "mtu"); + if (value) { + long int mtu; + + errno = 0; + mtu = strtol (value, NULL, 10); + if (errno || mtu < 0 || mtu > 65535) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: invalid MTU '%s' for %s", + value, conn_name); + } else + g_object_set (s_wired, NM_SETTING_WIRED_MTU, + (guint32) mtu, NULL); + } + + if (read_mac_address (conn_name, &mac, error)) { + if (mac) { + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, + mac, NULL); + g_byte_array_free (mac, TRUE); + } + } else { + g_object_unref (s_wired); + s_wired = NULL; + } + if (s_wired) + nm_connection_add_setting (connection, NM_SETTING (s_wired)); +} + +/* add NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, + * NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID in future*/ +static void +make_ip4_setting (NMConnection * connection, gchar * conn_name, GError ** error) +{ + + NMSettingIP4Config *ip4_setting = + NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + gchar *value; + gboolean is_static_block = is_static_ip4 (conn_name); + ip_block *iblock = NULL; + + /* set dhcp options (dhcp_xxx) */ + value = ifnet_get_data (conn_name, "dhcp"); + g_object_set (ip4_setting, NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, value + && strstr (value, "nodns") ? TRUE : FALSE, + NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, value + && strstr (value, "nogateway") ? TRUE : FALSE, NULL); + + if (!is_static_block) { + g_object_set (ip4_setting, + NM_SETTING_IP4_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, FALSE, NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Using DHCP for %s", + conn_name); + } else { + iblock = convert_ip4_config_block (conn_name); + if (!iblock) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Ifnet plugin: can't aquire ip configuration for %s", + conn_name); + g_object_unref (ip4_setting); + return; + } + /************** add all ip settings to the connection**********/ + while (iblock) { + ip_block *current_iblock; + NMIP4Address *ip4_addr = nm_ip4_address_new (); + + nm_ip4_address_set_address (ip4_addr, iblock->ip); + nm_ip4_address_set_prefix (ip4_addr, + nm_utils_ip4_netmask_to_prefix + (iblock->netmask)); + /* currently all the IPs has the same gateway */ + nm_ip4_address_set_gateway (ip4_addr, iblock->gateway); + if (iblock->gateway) + g_object_set (ip4_setting, + NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, NULL); + if (nm_setting_ip4_config_add_address + (ip4_setting, ip4_addr)) { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "new address: %d", iblock->ip); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "ipv4 addresses count: %d", + nm_setting_ip4_config_get_num_addresses + (ip4_setting)); + } else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "ignoring duplicate IP4 address"); + } + nm_ip4_address_unref (ip4_addr); + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + + } + g_object_set (ip4_setting, + NM_SETTING_IP4_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, + !has_default_ip4_route (conn_name), NULL); + } + + /* add dhcp hostname and client id */ + if (!is_static_block) { + gchar *dhcp_hostname, *client_id; + + get_dhcp_hostname_and_client_id (&dhcp_hostname, &client_id); + if (dhcp_hostname) { + g_object_set (ip4_setting, + NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, + dhcp_hostname, NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "DHCP hostname: %s", + dhcp_hostname); + g_free (dhcp_hostname); + } + if (client_id) { + g_object_set (ip4_setting, + NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, + client_id, NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "DHCP client id: %s", + client_id); + g_free (client_id); + } + } + + /* add all IPv4 dns servers, IPv6 servers will be ignored */ + set_ip4_dns_servers (ip4_setting, conn_name); + + /* DNS searches */ + value = (gchar *) ifnet_get_data (conn_name, "dns_search"); + if (value) { + char **searches = NULL; + + strip_string (value, '"'); + + searches = g_strsplit (value, " ", 0); + if (searches) { + char **item; + + for (item = searches; *item; item++) { + if (strlen (*item)) { + if (!nm_setting_ip4_config_add_dns_search (ip4_setting, *item)) + PLUGIN_WARN + (IFNET_PLUGIN_NAME, + " warning: duplicate DNS domain '%s'", + *item); + } + } + g_strfreev (searches); + } + } + + /* static routes */ + iblock = convert_ip4_routes_block (conn_name); + while (iblock) { + ip_block *current_iblock = iblock; + gchar *metric_str; + long int metric; + NMIP4Route *route = nm_ip4_route_new (); + + nm_ip4_route_set_dest (route, iblock->ip); + nm_ip4_route_set_next_hop (route, iblock->gateway); + nm_ip4_route_set_prefix (route, + nm_utils_ip4_netmask_to_prefix + (iblock->netmask)); + if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) { + metric = strtol (metric_str, NULL, 10); + nm_ip4_route_set_metric (route, (guint32) metric); + } else { + metric_str = ifnet_get_global_data ("metric"); + if (metric_str) { + metric = strtol (metric_str, NULL, 10); + nm_ip4_route_set_metric (route, + (guint32) metric); + g_free (metric_str); + } + } + + if (!nm_setting_ip4_config_add_route (ip4_setting, route)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "warning: duplicate IP4 route"); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "new IP4 route:%d\n", iblock->ip); + + nm_ip4_route_unref (route); + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + } + + /* Finally add setting to connection */ + nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); +} + +static void +make_ip6_setting (NMConnection * connection, gchar * conn_name, GError ** error) +{ + NMSettingIP6Config *s_ip6 = NULL; + gboolean is_static_block = is_static_ip6 (conn_name); + + // used to disable IPv6 + gboolean ipv6_enabled = FALSE; + gchar *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + gchar *value; + ip6_block *iblock; + gboolean never_default = !has_default_ip6_route (conn_name); + + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + if (!s_ip6) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Could not allocate IP6 setting"); + return; + } + + value = ifnet_get_data (conn_name, "enable_ipv6"); + if (value && is_true (value)) + ipv6_enabled = TRUE; + + //FIXME Handle other methods that NM supports in future + // Currently only Manual and DHCP are supported + if (!ipv6_enabled) { + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + goto done; + } else if (!is_static_block) { + // config_eth* contains "dhcp6" + method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + never_default = FALSE; + } + // else if (!has_ip6_address(conn_name)) + // doesn't have "dhcp6" && doesn't have any ipv6 address + // method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + else + // doesn't have "dhcp6" && has at least one ipv6 address + method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "IPv6 for %s enabled, using %s", + conn_name, method); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, method, + NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, FALSE, + NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, FALSE, + NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, never_default, NULL); + + /* Make manual settings */ + if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + ip6_block *current_iblock; + + iblock = convert_ip6_config_block (conn_name); + if (!iblock) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Ifnet plugin: can't aquire ip6 configuration for %s", + conn_name); + goto error; + } + /* add all IPv6 addresses */ + while (iblock) { + NMIP6Address *ip6_addr = nm_ip6_address_new (); + + nm_ip6_address_set_address (ip6_addr, iblock->ip); + nm_ip6_address_set_prefix (ip6_addr, iblock->prefix); + if (nm_setting_ip6_config_add_address (s_ip6, ip6_addr)) { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "ipv6 addresses count: %d", + nm_setting_ip6_config_get_num_addresses + (s_ip6)); + } else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "ignoring duplicate IP4 address"); + } + nm_ip6_address_unref (ip6_addr); + current_iblock = iblock; + iblock = iblock->next; + destroy_ip6_block (current_iblock); + } + + } else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + /* - autoconf or DHCPv6 stuff goes here */ + } + // DNS Servers, set NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS TRUE here + set_ip6_dns_servers (s_ip6, conn_name); + + /* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIP4Config */ + + // Add routes + iblock = convert_ip6_routes_block (conn_name); + if (iblock) + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, NULL); + /* Add all IPv6 routes */ + while (iblock) { + ip6_block *current_iblock = iblock; + gchar *metric_str; + long int metric = 1; + NMIP6Route *route = nm_ip6_route_new (); + + nm_ip6_route_set_dest (route, iblock->ip); + nm_ip6_route_set_next_hop (route, iblock->next_hop); + nm_ip6_route_set_prefix (route, iblock->prefix); + /* metric is not per routes configuration right now + * global metric is also supported (metric="x") */ + if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) { + metric = strtol (metric_str, NULL, 10); + nm_ip6_route_set_metric (route, (guint32) metric); + } else { + metric_str = ifnet_get_global_data ("metric"); + if (metric_str) { + metric = strtol (metric_str, NULL, 10); + nm_ip6_route_set_metric (route, + (guint32) metric); + g_free (metric_str); + } else + nm_ip6_route_set_metric (route, (guint32) 1); + } + + if (!nm_setting_ip6_config_add_route (s_ip6, route)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: duplicate IP6 route"); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, " info: new IP6 route"); + nm_ip6_route_unref (route); + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip6_block (current_iblock); + } + + done: + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + return; + + error: + g_object_unref (s_ip6); + PLUGIN_WARN (IFNET_PLUGIN_NAME, " warning: Ignore IPv6 for %s", + conn_name); + return; +} + +static NMSetting * +make_wireless_connection_setting (gchar * conn_name, + NMSetting8021x ** s_8021x, GError ** error) +{ + GByteArray *array, *mac = NULL; + NMSettingWireless *wireless_setting = NULL; + gboolean adhoc = FALSE; + gchar *value; + gchar *type; + + /* PPP over WIFI is not supported */ + g_return_val_if_fail (conn_name != NULL + && strcmp (ifnet_get_data (conn_name, "type"), + "ppp") != 0, NULL); + type = ifnet_get_data (conn_name, "type"); + if (strcmp (type, "ppp") == 0) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "PPP over WIFI is not supported yet"); + return NULL; + } + + wireless_setting = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + if (read_mac_address (conn_name, &mac, error)) { + if (mac) { + g_object_set (wireless_setting, + NM_SETTING_WIRELESS_MAC_ADDRESS, mac, + NULL); + g_byte_array_free (mac, TRUE); + + } + } else { + g_object_unref (wireless_setting); + return NULL; + } + + /* handle ssid (hex and ascii) */ + if (conn_name) { + gsize ssid_len = 0, value_len = strlen (conn_name); + char *p = conn_name, *tmp; + char buf[33]; + + ssid_len = value_len; + if ((value_len > 2) && (g_str_has_prefix (conn_name, "0x"))) { + /* Hex representation */ + if (value_len % 2) { + g_set_error (error, ifnet_plugin_error_quark (), + 0, + "Invalid SSID '%s' size (looks like hex but length not multiple of 2)", + conn_name); + goto error; + } + // ignore "0x" + p = conn_name + 2; + if (!is_hex (p)) { + g_set_error (error, + ifnet_plugin_error_quark (), + 0, + "Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)", + conn_name, *p); + goto error; + + } + tmp = utils_hexstr2bin (conn_name + 2, value_len - 2); + ssid_len = (value_len - 2) / 2; + memcpy (buf, tmp, ssid_len); + g_free (tmp); + p = &buf[0]; + } + + if (ssid_len > 32 || ssid_len == 0) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", + conn_name, ssid_len); + goto error; + } + array = g_byte_array_sized_new (ssid_len); + g_byte_array_append (array, (const guint8 *) p, ssid_len); + g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, array, + NULL); + g_byte_array_free (array, TRUE); + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing SSID"); + goto error; + } + + /* mode=0: infrastructure + * mode=1: adhoc */ + value = wpa_get_value (conn_name, "mode"); + if (value) + adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; + + if (exist_ssid (conn_name)) { + const char *mode = adhoc ? "adhoc" : "infrastructure"; + + g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, mode, + NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Using mode: %s", mode); + } + + /* BSSID setting */ + value = wpa_get_value (conn_name, "bssid"); + if (value) { + struct ether_addr *eth; + GByteArray *bssid; + + eth = ether_aton (value); + if (!eth) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid BSSID '%s'", value); + goto error; + } + + bssid = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (bssid, eth->ether_addr_octet, ETH_ALEN); + g_object_set (wireless_setting, NM_SETTING_WIRELESS_BSSID, + bssid, NULL); + g_byte_array_free (bssid, TRUE); + + } + + /* mtu_ssid="xx" */ + value = ifnet_get_data (conn_name, "mtu"); + if (value) { + long int mtu; + + errno = 0; + mtu = strtol (value, NULL, 10); + if (errno || mtu < 0 || mtu > 50000) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: invalid MTU '%s' for %s", + value, conn_name); + } else + g_object_set (wireless_setting, NM_SETTING_WIRELESS_MTU, + (guint32) mtu, NULL); + + } + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "wireless_setting added for %s", + conn_name); + return NM_SETTING (wireless_setting); + error: + if (wireless_setting) + g_object_unref (wireless_setting); + return NULL; + +} + +static NMSettingWirelessSecurity * +make_leap_setting (gchar * ssid, GError ** error) +{ + NMSettingWirelessSecurity *wsec; + char *value; + + wsec = + NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + + value = wpa_get_value (ssid, "key_mgmt"); + if (!value || strcmp (value, "IEEE8021X")) + goto error; /* Not LEAP */ + + value = wpa_get_value (ssid, "eap"); + if (!value || strcasecmp (value, "LEAP")) + goto error; /* Not LEAP */ + + value = wpa_get_value (ssid, "password"); + if (value && strlen (value)) + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + value, NULL); + + value = wpa_get_value (ssid, "identity"); + if (!value || !strlen (value)) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing LEAP identity"); + goto error; + } + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, + NULL); + + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL); + + return wsec; + error: + if (wsec) + g_object_unref (wsec); + return NULL; +} + +static gboolean +add_one_wep_key (gchar * ssid, gchar * key, int key_idx, + NMSettingWirelessSecurity * s_wsec, GError ** error) +{ + gchar *value; + gboolean success = FALSE; + + g_return_val_if_fail (ssid != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (key_idx >= 0 && key_idx <= 3, FALSE); + g_return_val_if_fail (s_wsec != NULL, FALSE); + value = wpa_get_value (ssid, key); + if (!value) + return TRUE; + key = NULL; + /* Validate keys */ + if (strlen (value) == 10 || strlen (value) == 26) { + /* Hexadecimal WEP key */ + char *p = value; + + if (!is_hex (p)) { + g_set_error (error, ifnet_plugin_error_quark (), + 0, "Invalid hexadecimal WEP key."); + goto out; + } + key = g_strdup (value); + } else if (value[0] == '"' + && (strlen (value) == 7 || strlen (value) == 15)) { + /* ASCII passphrase */ + char *tmp = g_strdup (value); + char *p = strip_string (tmp, '"'); + + if (!is_ascii (p)) { + g_set_error (error, ifnet_plugin_error_quark (), + 0, "Invalid ASCII WEP passphrase."); + g_free (tmp); + goto out; + + } + + key = utils_bin2hexstr (tmp, strlen (tmp), strlen (tmp) * 2); + g_free (tmp); + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid WEP key length. Key: %s", value); + goto out; + } + + if (key) { + nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key); + g_free (key); + success = TRUE; + } + + out: + return success; + +} + +static gboolean +add_wep_keys (gchar * ssid, NMSettingWirelessSecurity * s_wsec, GError ** error) +{ + if (!add_one_wep_key (ssid, "wep_key0", 0, s_wsec, error)) + return FALSE; + if (!add_one_wep_key (ssid, "wep_key1", 1, s_wsec, error)) + return FALSE; + if (!add_one_wep_key (ssid, "wep_key2", 2, s_wsec, error)) + return FALSE; + if (!add_one_wep_key (ssid, "wep_key3", 3, s_wsec, error)) + return FALSE; + return TRUE; + +} + +static NMSettingWirelessSecurity * +make_wep_setting (gchar * ssid, GError ** error) +{ + gchar *auth_alg; + int default_key_idx = 0; + gchar *value; + NMSettingWirelessSecurity *s_wireless_sec; + + s_wireless_sec = + NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", NULL); + + /* default key index */ + value = wpa_get_value (ssid, "wep_tx_keyidx"); + if (value) { + default_key_idx = atoi (value); + if (default_key_idx >= 0 && default_key_idx <= 3) { + g_object_set (s_wireless_sec, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + default_key_idx, NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "Default key index: %d", default_key_idx); + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid default WEP key '%s'", value); + goto error; + } + } + + if (!add_wep_keys (ssid, s_wireless_sec, error)) + goto error; + + /* If there's a default key, ensure that key exists */ + if ((default_key_idx == 1) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Default WEP key index was 2, but no valid KEY2 exists."); + goto error; + } else if ((default_key_idx == 2) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, + 2)) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Default WEP key index was 3, but no valid KEY3 exists."); + goto error; + } else if ((default_key_idx == 3) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, + 3)) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Default WEP key index was 4, but no valid KEY4 exists."); + goto error; + } + + /* authentication algorithms */ + auth_alg = wpa_get_value (ssid, "auth_alg"); + if (auth_alg) { + if (strcmp (auth_alg, "OPEN") == 0) { + g_object_set (s_wireless_sec, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "WEP: Use open system authentication"); + } else if (strcmp (auth_alg, "SHARED") == 0) { + g_object_set (s_wireless_sec, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "shared", NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "WEP: Use shared system authentication"); + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid WEP authentication algorithm '%s'", + auth_alg); + goto error; + } + + } + + if (!nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3) + && !nm_setting_wireless_security_get_wep_tx_keyidx (s_wireless_sec)) { + if (auth_alg && !strcmp (auth_alg, "shared")) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "WEP Shared Key authentication is invalid for " + "unencrypted connections."); + goto error; + } + /* Unencrypted */ + g_object_unref (s_wireless_sec); + s_wireless_sec = NULL; + } + return s_wireless_sec; + + error: + if (s_wireless_sec) + g_object_unref (s_wireless_sec); + return NULL; +} + +static char * +parse_wpa_psk (gchar * psk, GError ** error) +{ + gchar *p, *hashed = NULL; + gboolean quoted = FALSE; + + if (!psk) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing WPA_PSK for WPA-PSK key management"); + return NULL; + } + + /* Passphrase must be between 10 and 66 characters in length becuase WPA + * hex keys are exactly 64 characters (no quoting), and WPA passphrases + * are between 8 and 63 characters (inclusive), plus optional quoting if + * the passphrase contains spaces. + */ + + p = psk; + if (p[0] == '"' && psk[strlen (psk) - 1] == '"') + quoted = TRUE; + if (!quoted && (strlen (psk) == 64)) { + /* Verify the hex PSK; 64 digits */ + if (!is_hex (p)) { + g_set_error (error, ifnet_plugin_error_quark (), + 0, + "Invalid WPA_PSK (contains non-hexadecimal characters)"); + goto out; + } + hashed = g_strdup (psk); + } else { + strip_string (p, '"'); + + /* Length check */ + if (strlen (p) < 8 || strlen (p) > 63) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid WPA_PSK (passphrases must be between " + "8 and 63 characters long (inclusive))"); + goto out; + } + + hashed = g_strdup (p); + } + + if (!hashed) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid WPA_PSK (doesn't look like a passphrase or hex key)"); + goto out; + } + + out: + return hashed; +} + +static gboolean +fill_wpa_ciphers (gchar * ssid, + NMSettingWirelessSecurity * wsec, + gboolean group, gboolean adhoc) +{ + char *value = NULL, *p; + char **list = NULL, **iter; + int i = 0; + + p = value = wpa_get_value (ssid, group ? "group" : "pairwise"); + if (!value) + return TRUE; + + list = g_strsplit_set (p, " ", 0); + for (iter = list; iter && *iter; iter++, i++) { + /* Ad-Hoc configurations cannot have pairwise ciphers, and can only + * have one group cipher. Ignore any additional group ciphers and + * any pairwise ciphers specified. + */ + if (adhoc) { + if (group && (i > 0)) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: ignoring group cipher '%s' (only one group cipher allowed in Ad-Hoc mode)", + *iter); + continue; + } else if (!group) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: ignoring pairwise cipher '%s' (pairwise not used in Ad-Hoc mode)", + *iter); + continue; + } + } + + if (!strcmp (*iter, "CCMP")) { + if (group) + nm_setting_wireless_security_add_group (wsec, + "ccmp"); + else + nm_setting_wireless_security_add_pairwise (wsec, + "ccmp"); + } else if (!strcmp (*iter, "TKIP")) { + if (group) + nm_setting_wireless_security_add_group (wsec, + "tkip"); + else + nm_setting_wireless_security_add_pairwise (wsec, + "tkip"); + } else if (group && !strcmp (*iter, "WEP104")) + nm_setting_wireless_security_add_group (wsec, "wep104"); + else if (group && !strcmp (*iter, "WEP40")) + nm_setting_wireless_security_add_group (wsec, "wep40"); + else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: ignoring invalid %s cipher '%s'", + group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", + *iter); + } + } + + if (list) + g_strfreev (list); + return TRUE; +} + +static NMSetting8021x * +fill_8021x (gchar * ssid, gchar * key_mgmt, gboolean wifi, GError ** error) +{ + NMSetting8021x *s_8021x; + char *value; + char **list, **iter; + + value = wpa_get_value (ssid, "eap"); + if (!value) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing IEEE_8021X_EAP_METHODS for key management '%s'", + key_mgmt); + return NULL; + } + + list = g_strsplit (value, " ", 0); + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + /* Validate and handle each EAP method */ + for (iter = list; iter && *iter; iter++) { + EAPReader *eap = &eap_readers[0]; + gboolean found = FALSE; + char *lower = NULL; + + lower = g_ascii_strdown (*iter, -1); + while (eap->method && !found) { + if (strcmp (eap->method, lower)) + goto next; + + /* Some EAP methods don't provide keying material, thus they + * cannot be used with WiFi unless they are an inner method + * used with TTLS or PEAP or whatever. + */ + if (wifi && eap->wifi_phase2_only) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: ignored invalid " + "IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.", + lower); + goto next; + } + + /* Parse EAP method specific options */ + if (!(*eap->reader) + (lower, ssid, s_8021x, FALSE, error)) { + g_free (lower); + goto error; + } + nm_setting_802_1x_add_eap_method (s_8021x, lower); + found = TRUE; + + next: + eap++; + } + + if (!found) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: ignored unknown" + "IEEE_8021X_EAP_METHOD '%s'.", lower); + } + g_free (lower); + } + g_strfreev (list); + + if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "No valid EAP methods found in IEEE_8021X_EAP_METHODS."); + goto error; + } + + return s_8021x; + + error: + g_object_unref (s_8021x); + return NULL; +} + +static NMSettingWirelessSecurity * +make_wpa_setting (gchar * ssid, NMSetting8021x ** s_8021x, GError ** error) +{ + NMSettingWirelessSecurity *wsec; + char *value, *lower; + gboolean adhoc = FALSE; + + if (!exist_ssid (ssid)) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "No security info found for ssid: %s", ssid); + return NULL; + } + + wsec = + NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + + /* mode=1: adhoc + * mode=0: infrastructure */ + value = wpa_get_value (ssid, "mode"); + if (value) + adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; + + value = wpa_get_value (ssid, "key_mgmt"); + /* Not WPA or Dynamic WEP */ + if (!value) + goto error; + if (strcmp (value, "WPA-PSK") && strcmp (value, "WPA-EAP")) + goto error; + /* Pairwise and Group ciphers */ + fill_wpa_ciphers (ssid, wsec, FALSE, adhoc); + fill_wpa_ciphers (ssid, wsec, TRUE, adhoc); + + /* WPA and/or RSN */ + if (adhoc) { + /* Ad-Hoc mode only supports WPA proto for now */ + nm_setting_wireless_security_add_proto (wsec, "wpa"); + } else { + nm_setting_wireless_security_add_proto (wsec, "wpa"); + nm_setting_wireless_security_add_proto (wsec, "rsn"); + + } + + if (!strcmp (value, "WPA-PSK")) { + gchar *psk = parse_wpa_psk (wpa_get_value (ssid, "psk"), error); + + if (!psk) + goto error; + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, + NULL); + g_free (psk); + + if (adhoc) + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-none", NULL); + else + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", NULL); + } else if (!strcmp (value, "WPA-EAP") || !strcmp (value, "IEEE8021X")) { + if (adhoc) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", + value); + goto error; + } + *s_8021x = fill_8021x (ssid, value, TRUE, error); + if (!*s_8021x) + goto error; + + lower = g_ascii_strdown (value, -1); + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + lower, NULL); + g_free (lower); + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Unknown wireless KEY_MGMT type '%s'", value); + goto error; + } + return wsec; + error: + if (wsec) + g_object_unref (wsec); + return NULL; +} + +static NMSettingWirelessSecurity * +make_wireless_security_setting (gchar * + conn_name, + NMSetting8021x ** s_8021x, GError ** error) +{ + NMSettingWirelessSecurity *wsec = NULL; + gchar *ssid; + gboolean adhoc = FALSE; + gchar *value; + + g_return_val_if_fail (conn_name != NULL + && strcmp (ifnet_get_data (conn_name, "type"), + "ppp") != 0, NULL); + if (!wpa_get_value (conn_name, "ssid")) + return NULL; + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "updating wireless security settings (%s).", conn_name); + + ssid = conn_name; + value = wpa_get_value (ssid, "mode"); + if (value) + adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; + + if (!adhoc) { + wsec = make_leap_setting (ssid, error); + if (error && *error) + goto error; + } + if (!wsec) { + wsec = make_wpa_setting (ssid, s_8021x, error); + if (error && *error) + goto error; + } + if (!wsec) { + wsec = make_wep_setting (ssid, error); + if (error && *error) + goto error; + } + + if (!wsec) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Can't handle security information for ssid: %s", + conn_name); + } + + return wsec; + error: + return NULL; +} + +/* Currently only support username and password */ +static void +make_pppoe_connection_setting (NMConnection * connection, + gchar * conn_name, GError ** error) +{ + NMSettingPPPOE *s_pppoe; + NMSettingPPP *s_ppp; + gchar *value; + + s_pppoe = NM_SETTING_PPPOE (nm_setting_pppoe_new ()); + + /* username */ + value = ifnet_get_data (conn_name, "username"); + if (!value) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "ppp requires at lease a username"); + return; + } + g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, value, NULL); + + /* password */ + value = ifnet_get_data (conn_name, "password"); + if (!value) { + value = ""; + } + + g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, value, NULL); + nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); + + /* PPP setting */ + s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); +} + +NMConnection * +ifnet_update_connection_from_config_block (gchar * conn_name, GError ** error) +{ + const gchar *type = NULL; + NMConnection *connection = NULL; + NMSettingConnection *setting = NULL; + NMSetting8021x *s_8021x = NULL; + NMSettingWirelessSecurity *wsec = NULL; + gboolean auto_conn = TRUE; + gchar *value = NULL; + gboolean success = FALSE; + + connection = nm_connection_new (); + if (!connection) + return NULL; + setting = + (NMSettingConnection *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_CONNECTION); + if (!setting) { + setting = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + g_assert (setting); + nm_connection_add_setting (connection, NM_SETTING (setting)); + } + + type = guess_connection_type (conn_name); + value = ifnet_get_data (conn_name, "auto"); + if (value && !strcmp (value, "false")) + auto_conn = FALSE; + update_connection_id (connection, conn_name); + g_object_set (setting, NM_SETTING_CONNECTION_TYPE, type, + NM_SETTING_CONNECTION_READ_ONLY, FALSE, + NM_SETTING_CONNECTION_AUTOCONNECT, auto_conn, NULL); + + if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type) + || !strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { + /* wired setting */ + make_wired_connection_setting (connection, conn_name, error); + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto error; + } + /* pppoe setting */ + if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) + make_pppoe_connection_setting (connection, conn_name, + error); + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto error; + } + } else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) { + /* wireless setting */ + NMSetting *wireless_setting = + make_wireless_connection_setting (conn_name, + &s_8021x, + error); + + if (!wireless_setting) { + goto error; + } + nm_connection_add_setting (connection, wireless_setting); + + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto error; + } + + /* wireless security setting */ + wsec = + make_wireless_security_setting (conn_name, &s_8021x, error); + if (wsec) { + nm_connection_add_setting (connection, + NM_SETTING (wsec)); + if (s_8021x) + nm_connection_add_setting (connection, + NM_SETTING + (s_8021x)); + g_object_set (wireless_setting, NM_SETTING_WIRELESS_SEC, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL); + } + + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto error; + } + + } else + goto error; + + /* IPv4 setting */ + make_ip4_setting (connection, conn_name, error); + if (error && *error) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + + /* IPv6 setting */ + make_ip6_setting (connection, conn_name, error); + if (error && *error) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + + success = nm_connection_verify (connection, error); + if (error && *error) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Connection verified %s:%d", conn_name, + success); + if (!success) + goto error; + return connection; + error: + g_object_unref (setting); + g_object_unref (connection); + return NULL; +} + +typedef NMSetting8021xCKScheme (*SchemeFunc) (NMSetting8021x * setting); +typedef const char *(*PathFunc) (NMSetting8021x * setting); +typedef const GByteArray *(*BlobFunc) (NMSetting8021x * setting); + +typedef struct ObjectType { + const char *setting_key; + SchemeFunc scheme_func; + PathFunc path_func; + BlobFunc blob_func; + const char *conn_name_key; + const char *suffix; +} ObjectType; + +static const ObjectType ca_type = { + NM_SETTING_802_1X_CA_CERT, + nm_setting_802_1x_get_ca_cert_scheme, + nm_setting_802_1x_get_ca_cert_path, + nm_setting_802_1x_get_ca_cert_blob, + "ca_cert", + "ca-cert.der" +}; + +static const ObjectType phase2_ca_type = { + NM_SETTING_802_1X_PHASE2_CA_CERT, + nm_setting_802_1x_get_phase2_ca_cert_scheme, + nm_setting_802_1x_get_phase2_ca_cert_path, + nm_setting_802_1x_get_phase2_ca_cert_blob, + "ca_cert2", + "inner-ca-cert.der" +}; + +static const ObjectType client_type = { + NM_SETTING_802_1X_CLIENT_CERT, + nm_setting_802_1x_get_client_cert_scheme, + nm_setting_802_1x_get_client_cert_path, + nm_setting_802_1x_get_client_cert_blob, + "client_cert", + "client-cert.der" +}; + +static const ObjectType phase2_client_type = { + NM_SETTING_802_1X_PHASE2_CLIENT_CERT, + nm_setting_802_1x_get_phase2_client_cert_scheme, + nm_setting_802_1x_get_phase2_client_cert_path, + nm_setting_802_1x_get_phase2_client_cert_blob, + "client_cert2", + "inner-client-cert.der" +}; + +static const ObjectType pk_type = { + NM_SETTING_802_1X_PRIVATE_KEY, + nm_setting_802_1x_get_private_key_scheme, + nm_setting_802_1x_get_private_key_path, + nm_setting_802_1x_get_private_key_blob, + "private_key", + "private-key.pem" +}; + +static const ObjectType phase2_pk_type = { + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, + nm_setting_802_1x_get_phase2_private_key_scheme, + nm_setting_802_1x_get_phase2_private_key_path, + nm_setting_802_1x_get_phase2_private_key_blob, + "private_key2", + "inner-private-key.pem" +}; + +static const ObjectType p12_type = { + NM_SETTING_802_1X_PRIVATE_KEY, + nm_setting_802_1x_get_private_key_scheme, + nm_setting_802_1x_get_private_key_path, + nm_setting_802_1x_get_private_key_blob, + "private_key", + "private-key.p12" +}; + +static const ObjectType phase2_p12_type = { + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, + nm_setting_802_1x_get_phase2_private_key_scheme, + nm_setting_802_1x_get_phase2_private_key_path, + nm_setting_802_1x_get_phase2_private_key_blob, + "private_key2", + "inner-private-key.p12" +}; + +static gboolean +write_object (NMSetting8021x * s_8021x, + gchar * conn_name, + const GByteArray * override_data, + const ObjectType * objtype, GError ** error) +{ + NMSetting8021xCKScheme scheme; + const char *path = NULL; + const GByteArray *blob = NULL; + + g_return_val_if_fail (conn_name != NULL, FALSE); + g_return_val_if_fail (objtype != NULL, FALSE); + if (override_data) + /* if given explicit data to save, always use that instead of asking + * the setting what to do. + */ + blob = override_data; + else { + scheme = (*(objtype->scheme_func)) (s_8021x); + switch (scheme) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + blob = (*(objtype->blob_func)) (s_8021x); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = (*(objtype->path_func)) (s_8021x); + break; + default: + break; + } + } + + /* If the object path was specified, prefer that over any raw cert data that + * may have been sent. + */ + if (path) { + wpa_set_data (conn_name, (gchar *) objtype->conn_name_key, + (gchar *) path); + return TRUE; + } + + /* does not support writing encryption data now */ + if (blob) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + " warning: Currently we do not support certs writing."); + } + + return TRUE; +} + +static gboolean +write_8021x_certs (NMSetting8021x * s_8021x, + gboolean phase2, gchar * conn_name, GError ** error) +{ + char *password = NULL; + const ObjectType *otype = NULL; + gboolean is_pkcs12 = FALSE, success = FALSE; + const GByteArray *blob = NULL; + GByteArray *enc_key = NULL; + gchar *generated_pw = NULL; + + /* CA certificate */ + if (phase2) + otype = &phase2_ca_type; + else + otype = &ca_type; + + if (!write_object (s_8021x, conn_name, NULL, otype, error)) + return FALSE; + + /* Private key */ + if (phase2) { + if (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x) != + NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + if (nm_setting_802_1x_get_phase2_private_key_format + (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + is_pkcs12 = TRUE; + } + password = (char *) + nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + } else { + if (nm_setting_802_1x_get_private_key_scheme (s_8021x) != + NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + if (nm_setting_802_1x_get_private_key_format (s_8021x) + == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + is_pkcs12 = TRUE; + } + password = (char *) + nm_setting_802_1x_get_private_key_password (s_8021x); + } + + if (is_pkcs12) + otype = phase2 ? &phase2_p12_type : &p12_type; + else + otype = phase2 ? &phase2_pk_type : &pk_type; + + if ((*(otype->scheme_func)) (s_8021x) == + NM_SETTING_802_1X_CK_SCHEME_BLOB) + blob = (*(otype->blob_func)) (s_8021x); + + /* Only do the private key re-encrypt dance if we got the raw key data, which + * by definition will be unencrypted. If we're given a direct path to the + * private key file, it'll be encrypted, so we don't need to re-encrypt. + */ + if (blob && !is_pkcs12) { + /* Encrypt the unencrypted private key with the fake password */ + enc_key = + nm_utils_rsa_key_encrypt (blob, password, &generated_pw, + error); + if (!enc_key) + goto out; + + if (generated_pw) + password = generated_pw; + } + + /* Save the private key */ + if (!write_object + (s_8021x, conn_name, enc_key ? enc_key : blob, otype, error)) + goto out; + + if (phase2) + wpa_set_data (conn_name, "private_key_passwd2", password); + else + wpa_set_data (conn_name, "private_key_passwd", password); + + /* Client certificate */ + if (is_pkcs12) { + wpa_set_data (conn_name, + phase2 ? "client_cert2" : "client_cert", NULL); + } else { + if (phase2) + otype = &phase2_client_type; + else + otype = &client_type; + + /* Save the client certificate */ + if (!write_object (s_8021x, conn_name, NULL, otype, error)) + goto out; + } + + success = TRUE; + out: + if (generated_pw) { + memset (generated_pw, 0, strlen (generated_pw)); + g_free (generated_pw); + } + if (enc_key) { + memset (enc_key->data, 0, enc_key->len); + g_byte_array_free (enc_key, TRUE); + } + return success; +} + +static gboolean +write_8021x_setting (NMConnection * connection, + gchar * conn_name, gboolean wired, GError ** error) +{ + NMSetting8021x *s_8021x; + const char *value; + char *tmp = NULL; + gboolean success = FALSE; + GString *phase2_auth; + GString *phase1; + + s_8021x = + (NMSetting8021x *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_802_1X); + + if (!s_8021x) { + return TRUE; + } + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Adding 8021x setting for %s", + conn_name); + + /* If wired, write KEY_MGMT */ + if (wired) + wpa_set_data (conn_name, "key_mgmt", "IEEE8021X"); + + /* EAP method */ + if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) { + value = nm_setting_802_1x_get_eap_method (s_8021x, 0); + if (value) + tmp = g_ascii_strup (value, -1); + } + wpa_set_data (conn_name, "eap", tmp ? tmp : NULL); + g_free (tmp); + + wpa_set_data (conn_name, "identity", + (gchar *) nm_setting_802_1x_get_identity (s_8021x)); + + wpa_set_data (conn_name, "anonymous_identity", (gchar *) + nm_setting_802_1x_get_anonymous_identity (s_8021x)); + + wpa_set_data (conn_name, "password", + (gchar *) nm_setting_802_1x_get_password (s_8021x)); + + phase1 = g_string_new (NULL); + + /* PEAP version */ + wpa_set_data (conn_name, "phase1", NULL); + value = nm_setting_802_1x_get_phase1_peapver (s_8021x); + if (value && (!strcmp (value, "0") || !strcmp (value, "1"))) + g_string_append_printf (phase1, "peapver=%s ", value); + + /* PEAP label */ + value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x); + if (value && !strcmp (value, "1")) + g_string_append_printf (phase1, "peaplabel=%s ", value); + if (phase1->len) { + tmp = g_strstrip (g_strdup (phase1->str)); + wpa_set_data (conn_name, "phase1", tmp); + g_free (tmp); + } + + /* Phase2 auth methods */ + wpa_set_data (conn_name, "phase2", NULL); + phase2_auth = g_string_new (NULL); + + value = nm_setting_802_1x_get_phase2_auth (s_8021x); + if (value) { + tmp = g_ascii_strup (value, -1); + g_string_append_printf (phase2_auth, "auth=%s ", tmp); + g_free (tmp); + } + + /* Phase2 auth heap */ + value = nm_setting_802_1x_get_phase2_autheap (s_8021x); + if (value) { + tmp = g_ascii_strup (value, -1); + g_string_append_printf (phase2_auth, "autheap=%s ", tmp); + g_free (tmp); + } + tmp = g_strstrip (g_strdup (phase2_auth->str)); + wpa_set_data (conn_name, "phase2", phase2_auth->len ? tmp : NULL); + g_free (tmp); + + g_string_free (phase2_auth, TRUE); + g_string_free (phase1, TRUE); + + success = write_8021x_certs (s_8021x, FALSE, conn_name, error); + if (success) { + /* phase2/inner certs */ + success = write_8021x_certs (s_8021x, TRUE, conn_name, error); + } + + return success; +} + +static gboolean +write_wireless_security_setting (NMConnection * connection, + gchar * conn_name, + gboolean adhoc, + gboolean * no_8021x, GError ** error) +{ + NMSettingWirelessSecurity *s_wsec; + const char *key_mgmt, *auth_alg, *key, *cipher, *psk; + gboolean wep = FALSE, wpa = FALSE; + char *tmp; + guint32 i, num; + GString *str; + + s_wsec = + (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_WIRELESS_SECURITY); + if (!s_wsec) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + return FALSE; + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + g_assert (key_mgmt); + + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + if (!strcmp (key_mgmt, "none")) { + wpa_set_data (conn_name, "key_mgmt", "NONE"); + wep = TRUE; + *no_8021x = TRUE; + } else if (!strcmp (key_mgmt, "wpa-none") + || !strcmp (key_mgmt, "wpa-psk")) { + wpa_set_data (conn_name, "key_mgmt", "WPA-PSK"); + wpa = TRUE; + *no_8021x = TRUE; + } else if (!strcmp (key_mgmt, "ieee8021x")) { + wpa_set_data (conn_name, "key_mgmt", "IEEE8021X"); + } else if (!strcmp (key_mgmt, "wpa-eap")) { + wpa_set_data (conn_name, "key_mgmt", "WPA-EAP"); + wpa = TRUE; + } + + if (auth_alg) { + if (!strcmp (auth_alg, "shared")) + wpa_set_data (conn_name, "auth_alg", "SHARED"); + else if (!strcmp (auth_alg, "open")) + wpa_set_data (conn_name, "auth_alg", "OPEN"); + else if (!strcmp (auth_alg, "leap")) { + wpa_set_data (conn_name, "auth_alg", "LEAP"); + wpa_set_data (conn_name, "eap", "LEAP"); + wpa_set_data (conn_name, "identity", (gchar *) + nm_setting_wireless_security_get_leap_username + (s_wsec)); + wpa_set_data (conn_name, "password", (gchar *) + nm_setting_wireless_security_get_leap_password + (s_wsec)); + *no_8021x = TRUE; + } + } else + wpa_set_data (conn_name, "auth_alg", NULL); + + /* Default WEP TX key index */ + wpa_set_data (conn_name, "wep_tx_keyidx", NULL); + if (wep) { + tmp = + g_strdup_printf ("%d", + nm_setting_wireless_security_get_wep_tx_keyidx + (s_wsec)); + wpa_set_data (conn_name, "wep_tx_keyidx", tmp); + g_free (tmp); + } + + /* WEP keys */ + for (i = 0; i < 4; i++) { + int length; + + key = nm_setting_wireless_security_get_wep_key (s_wsec, i); + if (!key) + continue; + tmp = g_strdup_printf ("wep_key%d", i); + length = strlen (key); + if (length == 10 || length == 26 || length == 58) + wpa_set_data (conn_name, tmp, (gchar *) key); + else { + gchar *tmp_key = g_strdup_printf ("\"%s\"", key); + + wpa_set_data (conn_name, tmp, tmp_key); + g_free (tmp_key); + } + g_free (tmp); + } + + /* WPA Pairwise ciphers */ + wpa_set_data (conn_name, "pairwise", NULL); + str = g_string_new (NULL); + num = nm_setting_wireless_security_get_num_pairwise (s_wsec); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c (str, ' '); + cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i); + tmp = g_ascii_strup (cipher, -1); + g_string_append (str, tmp); + g_free (tmp); + } + if (strlen (str->str)) + wpa_set_data (conn_name, "pairwise", str->str); + g_string_free (str, TRUE); + + /* WPA Group ciphers */ + wpa_set_data (conn_name, "group", NULL); + str = g_string_new (NULL); + num = nm_setting_wireless_security_get_num_groups (s_wsec); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c (str, ' '); + cipher = nm_setting_wireless_security_get_group (s_wsec, i); + tmp = g_ascii_strup (cipher, -1); + g_string_append (str, tmp); + g_free (tmp); + } + if (strlen (str->str)) + wpa_set_data (conn_name, "group", str->str); + g_string_free (str, TRUE); + + /* WPA Passphrase */ + if (wpa) { + GString *quoted = NULL; + + psk = nm_setting_wireless_security_get_psk (s_wsec); + if (psk && (strlen (psk) != 64)) { + quoted = g_string_sized_new (strlen (psk) + 2); + g_string_append_c (quoted, '"'); + g_string_append (quoted, psk); + g_string_append_c (quoted, '"'); + } + wpa_set_data (conn_name, "psk", + quoted ? quoted->str : (gchar *) psk); + if (quoted) + g_string_free (quoted, TRUE); + } else + wpa_set_data (conn_name, "psk", NULL); + + return TRUE; +} + +/* remove old ssid and add new one*/ +static void +update_wireless_ssid (NMConnection * connection, gchar * conn_name, + gchar * ssid, gboolean hex) +{ + ifnet_delete_network (conn_name); + ifnet_add_connection (ssid, "wireless"); + + wpa_delete_security (conn_name); + wpa_add_security (ssid); +} + +static gboolean +write_wireless_setting (NMConnection * connection, + gchar ** conn_name_ptr, gboolean * no_8021x, + GError ** error) +{ + NMSettingWireless *s_wireless; + const GByteArray *ssid, *mac, *bssid; + const char *mode; + char buf[33]; + guint32 mtu, i; + gboolean adhoc = FALSE, hex_ssid = FALSE; + gchar *ssid_str, *tmp; + gchar *conn_name = *conn_name_ptr; + + s_wireless = + (NMSettingWireless *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + if (!ssid->len || ssid->len > 32) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Invalid SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + /* If the SSID contains any non-printable characters, we need to use the + * hex notation of the SSID instead. + */ + for (i = 0; i < ssid->len; i++) { + if (!isprint (ssid->data[i])) { + hex_ssid = TRUE; + break; + } + } + + if (hex_ssid) { + GString *str; + + /* Hex SSIDs don't get quoted */ + str = g_string_sized_new (ssid->len * 2 + 3); + g_string_append (str, "0x"); + for (i = 0; i < ssid->len; i++) + g_string_append_printf (str, "%02X", ssid->data[i]); + update_wireless_ssid (connection, conn_name, str->str, + hex_ssid); + ssid_str = g_strdup (str->str); + g_string_free (str, TRUE); + } else { + /* Printable SSIDs get quoted */ + memset (buf, 0, sizeof (buf)); + memcpy (buf, ssid->data, ssid->len); + g_strstrip (buf); + update_wireless_ssid (connection, conn_name, buf, hex_ssid); + ssid_str = g_strdup (buf); + } + + ifnet_set_data (ssid_str, "mac", NULL); + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (mac) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + mac->data[0], mac->data[1], mac->data[2], + mac->data[3], mac->data[4], + mac->data[5]); + ifnet_set_data (ssid_str, "mac", tmp); + g_free (tmp); + } + + ifnet_set_data (ssid_str, "mtu", NULL); + mtu = nm_setting_wireless_get_mtu (s_wireless); + if (mtu) { + tmp = g_strdup_printf ("%u", mtu); + ifnet_set_data (ssid_str, "mtu", tmp); + g_free (tmp); + } + + ifnet_set_data (ssid_str, "mode", NULL); + mode = nm_setting_wireless_get_mode (s_wireless); + if (!mode || !strcmp (mode, "infrastructure")) { + wpa_set_data (ssid_str, "mode", "0"); + } else if (!strcmp (mode, "adhoc")) { + wpa_set_data (ssid_str, "mode", "1"); + adhoc = TRUE; + } else { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Invalid mode '%s' in '%s' setting", + mode, NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + wpa_set_data (ssid_str, "bssid", NULL); + bssid = nm_setting_wireless_get_bssid (s_wireless); + if (bssid) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + bssid->data[0], bssid->data[1], + bssid->data[2], bssid->data[3], + bssid->data[4], bssid->data[5]); + wpa_set_data (ssid_str, "bssid", tmp); + g_free (tmp); + } + + if (nm_setting_wireless_get_security (s_wireless)) { + if (!write_wireless_security_setting + (connection, ssid_str, adhoc, no_8021x, error)) + return FALSE; + } else + wpa_delete_security (ssid_str); + *conn_name_ptr = ifnet_get_data (ssid_str, "name"); + g_free (ssid_str); + return TRUE; +} + +static gboolean +write_wired_setting (NMConnection * connection, gchar * conn_name, + GError ** error) +{ + NMSettingWired *s_wired; + const GByteArray *mac; + char *tmp; + guint32 mtu; + + s_wired = + (NMSettingWired *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_WIRED); + if (!s_wired) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_WIRED_SETTING_NAME); + return FALSE; + } + + ifnet_set_data (conn_name, "mac", NULL); + mac = nm_setting_wired_get_mac_address (s_wired); + if (mac) { + tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", + mac->data[0], mac->data[1], mac->data[2], + mac->data[3], mac->data[4], + mac->data[5]); + ifnet_set_data (conn_name, "mac", tmp); + g_free (tmp); + } + + ifnet_set_data (conn_name, "mtu", NULL); + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu) { + tmp = g_strdup_printf ("%u", mtu); + ifnet_set_data (conn_name, "mtu", tmp); + g_free (tmp); + } + //FIXME may add connection type in future + //ifnet_set_data (conn_name, "TYPE", TYPE_ETHERNET); + + return TRUE; +} + +static void +write_connection_setting (NMSettingConnection * s_con, gchar * conn_name) +{ + ifnet_set_data (conn_name, "auto", + nm_setting_connection_get_autoconnect (s_con) ? "true" : + "false"); +} + +static gboolean +write_ip4_setting (NMConnection * connection, gchar * conn_name, + GError ** error) +{ + NMSettingIP4Config *s_ip4; + const char *value; + char *tmp; + guint32 i, num; + GString *searches; + GString *ips; + GString *routes; + GString *dns; + gboolean has_def_route = FALSE; + gboolean success = FALSE; + + s_ip4 = + (NMSettingIP4Config *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + return FALSE; + } + routes = g_string_new (NULL); + + value = nm_setting_ip4_config_get_method (s_ip4); + g_assert (value); + if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + + num = nm_setting_ip4_config_get_num_addresses (s_ip4); + ips = g_string_new (NULL); + /* IPv4 addresses */ + for (i = 0; i < num; i++) { + char buf[INET_ADDRSTRLEN + 1]; + NMIP4Address *addr; + guint32 ip; + + addr = nm_setting_ip4_config_get_address (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_address (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], + sizeof (buf)); + g_string_append_printf (ips, "\"%s", &buf[0]); + + tmp = + g_strdup_printf ("%u", + nm_ip4_address_get_prefix (addr)); + g_string_append_printf (ips, "/%s\" ", tmp); + g_free (tmp); + + /* only the first gateway will be written */ + if (!has_def_route && nm_ip4_address_get_gateway (addr)) { + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_gateway (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], + sizeof (buf)); + g_string_append_printf (routes, + "\"default via %s\" ", + &buf[0]); + has_def_route = TRUE; + } + } + ifnet_set_data (conn_name, "config", ips->str); + g_string_free (ips, TRUE); + } else + ifnet_set_data (conn_name, "config", "dhcp"); + + /* DNS Servers */ + ifnet_set_data (conn_name, "dns_servers", NULL); + num = nm_setting_ip4_config_get_num_dns (s_ip4); + if (num > 0) { + dns = g_string_new (NULL); + for (i = 0; i < num; i++) { + char buf[INET_ADDRSTRLEN + 1]; + guint32 ip; + + ip = nm_setting_ip4_config_get_dns (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], + sizeof (buf)); + g_string_append_printf (dns, " %s", buf); + } + ifnet_set_data (conn_name, "dns_servers", dns->str); + g_string_free (dns, TRUE); + } else + ifnet_set_data (conn_name, "dns_servers", NULL); + + /* DNS Searches */ + num = nm_setting_ip4_config_get_num_dns_searches (s_ip4); + if (num > 0) { + searches = g_string_new (NULL); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c (searches, ' '); + g_string_append (searches, + nm_setting_ip4_config_get_dns_search + (s_ip4, i)); + } + ifnet_set_data (conn_name, "dns_search", searches->str); + g_string_free (searches, TRUE); + } else + ifnet_set_data (conn_name, "dns_search", NULL); + /* FIXME Will be implemented when configuration supports it + if (!strcmp(value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + value = nm_setting_ip4_config_get_dhcp_hostname(s_ip4); + if (value) + ifnet_set_data(conn_name, "DHCP_HOSTNAME", value, + FALSE); + + value = nm_setting_ip4_config_get_dhcp_client_id(s_ip4); + if (value) + ifnet_set_data(conn_name, "DHCP_CLIENT_ID", value, + FALSE); + } + */ + + /* Static routes */ + num = nm_setting_ip4_config_get_num_routes (s_ip4); + if (num > 0) { + for (i = 0; i < num; i++) { + char buf[INET_ADDRSTRLEN + 1]; + NMIP4Route *route; + guint32 ip; + + route = nm_setting_ip4_config_get_route (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_route_get_dest (route); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], + sizeof (buf)); + g_string_append_printf (routes, "\"%s", buf); + + tmp = + g_strdup_printf ("%u", + nm_ip4_route_get_prefix (route)); + g_string_append_printf (routes, "/%s via ", tmp); + g_free (tmp); + + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_route_get_next_hop (route); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], + sizeof (buf)); + g_string_append_printf (routes, "%s\" ", buf); + } + } + if (routes->len > 0) + ifnet_set_data (conn_name, "routes", routes->str); + else + ifnet_set_data (conn_name, "routes", NULL); + g_string_free (routes, TRUE); + + success = TRUE; + + return success; +} + +static gboolean +write_route6_file (NMSettingIP6Config * s_ip6, gchar * conn_name, + GError ** error) +{ + char dest[INET6_ADDRSTRLEN + 1]; + char next_hop[INET6_ADDRSTRLEN + 1]; + NMIP6Route *route; + const struct in6_addr *ip; + guint32 prefix; + guint32 i, num; + GString *routes_string; + gchar *old_routes; + + g_return_val_if_fail (s_ip6 != NULL, FALSE); + num = nm_setting_ip6_config_get_num_routes (s_ip6); + if (num == 0) { + return TRUE; + } + + old_routes = ifnet_get_data (conn_name, "routes"); + routes_string = g_string_new (old_routes); + if (old_routes) + g_string_append (routes_string, "\" "); + for (i = 0; i < num; i++) { + route = nm_setting_ip6_config_get_route (s_ip6, i); + + memset (dest, 0, sizeof (dest)); + ip = nm_ip6_route_get_dest (route); + inet_ntop (AF_INET6, (const void *) ip, &dest[0], + sizeof (dest)); + + prefix = nm_ip6_route_get_prefix (route); + + memset (next_hop, 0, sizeof (next_hop)); + ip = nm_ip6_route_get_next_hop (route); + inet_ntop (AF_INET6, (const void *) ip, &next_hop[0], + sizeof (next_hop)); + + g_string_append_printf (routes_string, "\"%s/%u via %s\" ", + dest, prefix, next_hop); + } + if (num > 0) + ifnet_set_data (conn_name, "routes", routes_string->str); + g_string_free (routes_string, TRUE); + + return TRUE; +} + +static gboolean +write_ip6_setting (NMConnection * connection, gchar * conn_name, + GError ** error) +{ + NMSettingIP6Config *s_ip6; + const char *value; + char *prefix; + guint32 i, num; + GString *searches; + char buf[INET6_ADDRSTRLEN + 1]; + NMIP6Address *addr; + const struct in6_addr *ip; + + s_ip6 = + (NMSettingIP6Config *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_IP6_CONFIG); + if (!s_ip6) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + return FALSE; + } + + value = nm_setting_ip6_config_get_method (s_ip6); + g_assert (value); + if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + ifnet_set_data (conn_name, "enable_ipv6", "false"); + return TRUE; + } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + /* nothing to do now */ + } else { + // if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + gchar *config = ifnet_get_data (conn_name, "config"); + gchar *tmp; + + if (!config) + tmp = g_strdup_printf ("dhcp6"); + else + tmp = g_strdup_printf ("%s\" \"dhcp6\"", config); + ifnet_set_data (conn_name, "config", tmp); + g_free (tmp); + } + /* else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + } */ + + /* Remember to set IPv6 enabled */ + ifnet_set_data (conn_name, "enable_ipv6", "true"); + + if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + gchar *config = ifnet_get_data (conn_name, "config"); + gchar *tmp; + GString *ip_str; + + if (!config) + config = ""; + num = nm_setting_ip6_config_get_num_addresses (s_ip6); + + /* IPv6 addresses */ + ip_str = g_string_new (NULL); + for (i = 0; i < num; i++) { + addr = nm_setting_ip6_config_get_address (s_ip6, i); + ip = nm_ip6_address_get_address (addr); + prefix = + g_strdup_printf ("%u", + nm_ip6_address_get_prefix (addr)); + memset (buf, 0, sizeof (buf)); + inet_ntop (AF_INET6, (const void *) ip, buf, + sizeof (buf)); + g_string_append_printf (ip_str, "\"%s/", buf); + g_string_append_printf (ip_str, "%s\" ", prefix); + g_free (prefix); + } + tmp = g_strdup_printf ("%s\" %s", config, ip_str->str); + ifnet_set_data (conn_name, "config", tmp); + g_free (tmp); + g_string_free (ip_str, TRUE); + } + + /* DNS Servers */ + num = nm_setting_ip6_config_get_num_dns (s_ip6); + if (num > 0) { + gchar *dns_servers = ifnet_get_data (conn_name, "dns_servers"); + gchar *tmp; + GString *dns_string = g_string_new (NULL); + + if (!dns_servers) + dns_servers = ""; + for (i = 0; i < num; i++) { + ip = nm_setting_ip6_config_get_dns (s_ip6, i); + + memset (buf, 0, sizeof (buf)); + inet_ntop (AF_INET6, (const void *) ip, buf, + sizeof (buf)); + if (!strstr (dns_servers, buf)) + g_string_append_printf (dns_string, "%s ", buf); + } + tmp = g_strdup_printf ("%s %s", dns_servers, dns_string->str); + ifnet_set_data (conn_name, "dns_servers", tmp); + g_free (tmp); + g_string_free (dns_string, TRUE); + + } else + /* DNS Searches */ + num = nm_setting_ip6_config_get_num_dns_searches (s_ip6); + if (num > 0) { + char *ip4_domains; + + ip4_domains = ifnet_get_data (conn_name, "dns_search"); + if (!ip4_domains) + ip4_domains = ""; + searches = g_string_new (ip4_domains); + for (i = 0; i < num; i++) { + const gchar *search = NULL; + + search = + nm_setting_ip6_config_get_dns_search (s_ip6, i); + if (search && !strstr (searches->str, search)) { + if (searches->len > 0) + g_string_append_c (searches, ' '); + g_string_append (searches, search); + } + } + ifnet_set_data (conn_name, "dns_search", searches->str); + g_string_free (searches, TRUE); + } + + write_route6_file (s_ip6, conn_name, error); + if (error && *error) + return FALSE; + return TRUE; +} + +static gboolean +write_pppoe_setting (gchar * conn_name, NMSettingPPPOE * s_pppoe) +{ + const gchar *value; + + value = nm_setting_pppoe_get_username (s_pppoe); + if (!value) { + return FALSE; + } + ifnet_set_data (conn_name, "username", (gchar *) value); + + value = nm_setting_pppoe_get_password (s_pppoe); + /* password could be NULL here */ + if (value) { + ifnet_set_data (conn_name, "password", (gchar *) value); + } + return TRUE; +} + +gboolean +ifnet_update_parsers_by_connection (NMConnection * connection, + gchar * conn_name, + gchar ** nm_conn_name, + gchar * config_file, + gchar * wpa_file, GError ** error) +{ + NMSettingConnection *s_con; + NMSettingIP6Config *s_ip6; + gboolean success = FALSE; + const char *type; + gboolean no_8021x = FALSE; + gboolean wired = FALSE, pppoe = TRUE; + + s_con = + NM_SETTING_CONNECTION (nm_connection_get_setting + (connection, NM_TYPE_SETTING_CONNECTION)); + if (!s_con) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_CONNECTION_SETTING_NAME); + return FALSE; + } + + type = nm_setting_connection_get_connection_type (s_con); + if (!type) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing connection type!"); + goto out; + } + + if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { + /* Writing wired setting */ + if (!write_wired_setting (connection, conn_name, error)) + goto out; + wired = TRUE; + no_8021x = TRUE; + } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { + /* Writing wireless setting */ + if (!write_wireless_setting + (connection, &conn_name, &no_8021x, error)) + goto out; + } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { + /* Writing pppoe setting */ + if (! + (write_pppoe_setting + (conn_name, + NM_SETTING_PPPOE (nm_connection_get_setting + (connection, NM_TYPE_SETTING_PPPOE))))) + goto out; + pppoe = TRUE; + wired = TRUE; + no_8021x = TRUE; + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Can't write connection type '%s'", type); + goto out; + } + + //FIXME wired connection doesn't support 8021x now + if (!no_8021x) { + if (!write_8021x_setting (connection, conn_name, wired, error)) + goto out; + } + + /* IPv4 Setting */ + if (!write_ip4_setting (connection, conn_name, error)) + goto out; + + s_ip6 = + (NMSettingIP6Config *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6) { + /* IPv6 Setting */ + if (!write_ip6_setting (connection, conn_name, error)) + goto out; + } + + /* Connection Setting */ + write_connection_setting (s_con, conn_name); + + /* connection id will be displayed in nm-applet */ + update_connection_id (connection, conn_name); + + if (nm_conn_name) + *nm_conn_name = g_strdup (conn_name); + success = ifnet_flush_to_file (config_file); + if (success) + wpa_flush_to_file (wpa_file); + + out: + return success; +} + +gboolean +ifnet_delete_connection_in_parsers (gchar * conn_name, + gchar * config_file, gchar * wpa_file) +{ + gboolean result = FALSE; + + ifnet_delete_network (conn_name); + result = ifnet_flush_to_file (config_file); + if (result) { + /* connection may not have security information + * so simply ignore the return value*/ + wpa_delete_security (conn_name); + wpa_flush_to_file (wpa_file); + } + + return result; +} + +/* get the available wired name(eth*). */ +static gchar * +get_wired_name () +{ + int i = 0; + + for (; i < 256; i++) { + gchar *conn_name = g_strdup_printf ("eth%d", i); + + if (!ifnet_has_connection (conn_name)) { + return conn_name; + } else + g_free (conn_name); + } + return NULL; +} + +/* get the available pppoe name(ppp*). */ +static gchar * +get_ppp_name () +{ + int i = 0; + + for (; i < 256; i++) { + gchar *conn_name = g_strdup_printf ("ppp%d", i); + + if (!ifnet_has_connection (conn_name)) { + return conn_name; + } else + g_free (conn_name); + } + return NULL; +} + +/* get wireless ssid */ +static gchar * +get_wireless_name (NMConnection * connection) +{ + NMSettingWireless *s_wireless; + const GByteArray *ssid; + gboolean hex_ssid = FALSE; + gchar *result = NULL; + char buf[33]; + int i = 0; + + s_wireless = + (NMSettingWireless *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) + return NULL; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid->len || ssid->len > 32) { + return NULL; + } + + for (i = 0; i < ssid->len; i++) { + if (!isprint (ssid->data[i])) { + hex_ssid = TRUE; + break; + } + } + + if (hex_ssid) { + GString *str; + + str = g_string_sized_new (ssid->len * 2 + 3); + g_string_append (str, "0x"); + for (i = 0; i < ssid->len; i++) + g_string_append_printf (str, "%02X", ssid->data[i]); + result = g_strdup (str->str); + g_string_free (str, TRUE); + } else { + memset (buf, 0, sizeof (buf)); + memcpy (buf, ssid->data, ssid->len); + result = g_strdup_printf ("%s", buf); + g_strstrip (result); + } + + return result; +} + +gboolean +ifnet_add_new_connection (NMConnection * connection, + gchar * config_file, gchar * wpa_file, + GError ** error) +{ + NMSettingConnection *s_con; + gboolean success = FALSE; + const char *type; + gchar *new_type, *new_name = NULL; + + s_con = + NM_SETTING_CONNECTION (nm_connection_get_setting + (connection, NM_TYPE_SETTING_CONNECTION)); + if (!s_con) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing '%s' setting", + NM_SETTING_CONNECTION_SETTING_NAME); + return FALSE; + } + + type = nm_setting_connection_get_connection_type (s_con); + if (!type) { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Missing connection type!"); + goto out; + } + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Adding %s connection", type); + + /* get name and type + * Wireless type: wireless + * Wired type: wired + * PPPoE type: ppp*/ + if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { + new_name = get_wired_name (); + if (!new_name) + goto out; + new_type = "wired"; + } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { + new_name = get_wireless_name (connection); + new_type = "wireless"; + } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { + new_name = get_ppp_name (); + if (!new_name) + goto out; + new_type = "ppp"; + } else { + g_set_error (error, ifnet_plugin_error_quark (), 0, + "Can't write connection type '%s'", type); + goto out; + } + + if (ifnet_add_connection (new_name, new_type)) + success = + ifnet_update_parsers_by_connection (connection, new_name, + NULL, config_file, + wpa_file, error); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Added new connection: %s, result: %s", + new_name, success ? "success" : "fail"); + + out: + if (new_name) + g_free (new_name); + return success; +} diff --git a/system-settings/plugins/ifnet/connection_parser.h b/system-settings/plugins/ifnet/connection_parser.h new file mode 100644 index 0000000000..b006954cc8 --- /dev/null +++ b/system-settings/plugins/ifnet/connection_parser.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#ifndef _CONNECTION_PARSER_H +#define _CONNECTION_PARSER_H +#include <nm-connection.h> +#include "net_parser.h" + +NMConnection *ifnet_update_connection_from_config_block (gchar * conn_name, + GError ** error); + +/* nm_conn_name is used to update nm_ifnet_connection's priv data */ +gboolean ifnet_update_parsers_by_connection (NMConnection * connection, + gchar * conn_name, + gchar ** nm_conn_name, + gchar * config_file, + gchar * wpa_file, GError ** error); + +gboolean ifnet_delete_connection_in_parsers (gchar * conn_name, + gchar * config_file, + gchar * wpa_file); +gboolean ifnet_add_new_connection (NMConnection * connection, + gchar * config_file, gchar * wpa_file, + GError ** error); +#endif diff --git a/system-settings/plugins/ifnet/net_parser.c b/system-settings/plugins/ifnet/net_parser.c new file mode 100644 index 0000000000..b4a381dee8 --- /dev/null +++ b/system-settings/plugins/ifnet/net_parser.c @@ -0,0 +1,635 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <string.h> +#include <nm-system-config-interface.h> +#include <stdio.h> +#include "net_parser.h" +#include "net_utils.h" + +/* Save all the connection information */ +static GHashTable *conn_table; + +/* Save global settings which are used for writing*/ +static GHashTable *global_settings_table; + +/* Save functions */ +static GList *functions_list; + +/* Used to decide whether to write changes to file*/ +static gboolean net_parser_data_changed = FALSE; + +static GHashTable * +add_new_connection_config (const gchar * type, const gchar * name) +{ + GHashTable *new_conn; + gchar *new_name; + + if (!name) + return NULL; + + /* Return existing connection */ + if ((new_conn = g_hash_table_lookup (conn_table, name)) != NULL) + return new_conn; + new_conn = g_hash_table_new (g_str_hash, g_str_equal); + new_name = g_strdup (name); + g_hash_table_insert (new_conn, g_strdup ("name"), new_name); + g_hash_table_insert (new_conn, g_strdup ("type"), g_strdup (type)); + g_hash_table_insert (conn_table, new_name, new_conn); + return new_conn; +} + +gboolean +ifnet_add_connection (gchar * name, gchar * type) +{ + if (add_new_connection_config (type, name)) { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Adding network for %s", name); + net_parser_data_changed = TRUE; + return TRUE; + } else + return FALSE; +} + +gboolean +ifnet_has_connection (gchar * conn_name) +{ + return g_hash_table_lookup (conn_table, conn_name) != NULL; +} + +static GHashTable * +get_connection_config (gchar * name) +{ + return g_hash_table_lookup (conn_table, name); +} + +/* Ignored name won't be treated as wireless ssid */ +static gchar *ignore_name[] = { + "vlan", "bond", "atm", "ath", "ippp", "vpn", "tap", "tun", "1", + "br", "nas", "6to4", "timeout", "kvm", "force", NULL +}; + +static gboolean +ignore_connection_name (gchar * name) +{ + gboolean result = FALSE; + guint i = 0; + + /* check ignore_name list */ + while (ignore_name[i] != NULL) { + if (g_ascii_strncasecmp + (name, ignore_name[i], strlen (ignore_name[i])) == 0) { + return TRUE; + } + i++; + } + /* Ignore mac address based configuration */ + if (strlen (name) == 12 && is_hex (name)) + result = TRUE; + return result; + +} + +static gboolean +is_global_setting (char *key) +{ + static gchar *global_settings[] = { "wpa_supplicant_", NULL }; + int i; + + for (i = 0; global_settings[i] != NULL; i++) { + if (strstr (key, global_settings[i])) + return 1; + } + return 0; +} + +/* Parse a complete line */ +/* Connection type is determined here */ +static void +init_block_by_line (gchar * buf) +{ + gchar **key_value; + gchar *pos; + gchar *data; + gchar *tmp; + GHashTable *conn; + + key_value = g_strsplit (buf, "=", 2); + if (g_strv_length (key_value) != 2) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Can't handle this line: %s\n", + buf); + g_strfreev (key_value); + return; + } + pos = g_strrstr (key_value[0], "_"); + if (pos == NULL || is_global_setting (key_value[0])) { + /* global data */ + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "global:%s-%s\n", key_value[0], + key_value[1]); + g_hash_table_insert (global_settings_table, + g_strdup (key_value[0]), + g_strdup (key_value[1])); + g_strfreev (key_value); + return; + } + *pos++ = '\0'; + if ((conn = get_connection_config (pos)) == NULL) { + if (g_ascii_strncasecmp (pos, "eth", 3) == 0 + && strlen (pos) == 4) + /* wired connection */ + conn = add_new_connection_config ("wired", pos); + else if (g_ascii_strncasecmp (pos, "ppp", 3) == 0 + && strlen (pos) == 4) + /* pppoe connection */ + conn = add_new_connection_config ("ppp", pos); + else if (ignore_connection_name (pos)) { + /* ignored connection */ + conn = add_new_connection_config ("ignore", pos); + } else + /* wireless connection */ + conn = add_new_connection_config ("wireless", pos); + } + data = g_strdup (key_value[1]); + tmp = strip_string (data, '('); + tmp = strip_string (tmp, ')'); + strip_string (tmp, '"'); + strip_string (tmp, '\''); + if (conn) + g_hash_table_insert (conn, g_strdup (key_value[0]), + g_strdup (tmp)); + g_free (data); + g_strfreev (key_value); +} + +static void +destroy_connection_config (GHashTable * conn) +{ + gpointer key, value; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, conn); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_free (key); + g_free (value); + } + + g_hash_table_destroy (conn); +} + +// read settings from /etc/NetworkManager/nm-system-settings.conf +gchar * +ifnet_get_global_setting (gchar * group, gchar * key) +{ + GError *error = NULL; + GKeyFile *keyfile = g_key_file_new (); + gchar *result = NULL; + + if (!g_key_file_load_from_file (keyfile, + IFNET_SYSTEM_SETTINGS_KEY_FILE, + G_KEY_FILE_NONE, &error)) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "loading system config file (%s) caused error: (%d) %s", + IFNET_SYSTEM_SETTINGS_KEY_FILE, + error ? error->code : -1, error + && error->message ? error->message : "(unknown)"); + } else { + result = g_key_file_get_string (keyfile, group, key, &error); + } + g_key_file_free (keyfile); + return result; +} + +static void +strip_function (GIOChannel * channel, gchar * line) +{ + + int counter = 0; + gchar *p, *tmp; + gboolean begin = FALSE; + GString *function_str = g_string_new (line); + + g_string_append (function_str, "\n"); + while (1) { + p = line; + while (*p != '\0') { + if (*p == '{') { + counter++; + begin = TRUE; + } else if (*p == '}') + counter--; + p++; + } + if (begin && counter == 0) { + g_free (line); + goto done; + } + while (1) { + g_free (line); + if (g_io_channel_read_line + (channel, &line, NULL, NULL, + NULL) == G_IO_STATUS_EOF) + goto done; + g_string_append (function_str, line); + tmp = g_strdup (line); + g_strstrip (tmp); + if (tmp[0] != '#' && tmp[0] != '\0') { + g_free (tmp); + break; + } else + g_free (tmp); + } + } + done: + functions_list = + g_list_append (functions_list, g_strdup (function_str->str)); + g_string_free (function_str, TRUE); +} + +static gboolean +is_function (gchar * line) +{ + static gchar *func_names[] = + { "preup", "predown", "postup", "postdown", "failup", "faildown", + NULL, + }; + int i; + + for (i = 0; func_names[i]; i++) { + if (g_str_has_prefix (line, func_names[i])) { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "Ignoring function: %s", func_names[i]); + return TRUE; + } + } + return FALSE; +} + +gboolean +ifnet_init (gchar * config_file) +{ + GIOChannel *channel = NULL; + gchar *line; + + /* Handle multiple lines with brackets */ + gboolean complete = TRUE; + + /* line buffer */ + GString *buf; + + net_parser_data_changed = FALSE; + + conn_table = g_hash_table_new (g_str_hash, g_str_equal); + global_settings_table = g_hash_table_new (g_str_hash, g_str_equal); + functions_list = NULL; + + if (g_file_test (config_file, G_FILE_TEST_IS_REGULAR)) + channel = g_io_channel_new_file (config_file, "r", NULL); + if (channel == NULL) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Error: Can't open %s\n", config_file); + return FALSE; + } + + buf = g_string_new (NULL); + while (g_io_channel_read_line + (channel, &line, NULL, NULL, NULL) != G_IO_STATUS_EOF) { + g_strstrip (line); + /* convert multiple lines to a complete line and + * pass it to init_block_by_line() */ + if (is_function (line)) { + strip_function (channel, line); + continue; + } + if (line[0] != '#' && line[0] != '\0') { + gchar *pos = NULL; + + if (!complete) { + complete = + g_strrstr (line, + ")") == NULL ? FALSE : TRUE; + if ((pos = strchr (line, '#')) != NULL) + *pos = '\0'; + g_strstrip (line); + if (line[0] != '\0') { + g_string_append_printf (buf, + " %s", line); + } + g_free (line); + if (!complete) + continue; + } else { + complete = + (g_strrstr (line, "(") != NULL + && g_strrstr (line, ")") != NULL) + || g_strrstr (line, "(") == NULL; + if ((pos = strchr (line, '#')) != NULL) + *pos = '\0'; + g_strstrip (line); + if (line[0] != '\0') + g_string_append (buf, line); + g_free (line); + if (!complete) + continue; + } + init_block_by_line (buf->str); + g_string_free (buf, TRUE); + buf = g_string_new (NULL); + } else + /* Blank line or comment line */ + g_free (line); + } + + g_string_free (buf, TRUE); + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + return TRUE; +} + +gchar * +ifnet_get_data (gchar * conn_name, const gchar * key) +{ + GHashTable *conn = g_hash_table_lookup (conn_table, conn_name); + + if (conn) + return g_hash_table_lookup (conn, key); + return NULL; +} + +void +ifnet_set_data (gchar * conn_name, gchar * key, gchar * value) +{ + gpointer orin_key = NULL, orin_value = NULL; + GHashTable *conn = g_hash_table_lookup (conn_table, conn_name); + + if (!conn) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "%s does not exsit!", conn_name); + return; + } + /* Remove existing key value pair */ + if (g_hash_table_lookup_extended (conn, key, &orin_key, &orin_value)) { + g_hash_table_remove (conn, orin_key); + g_free (orin_key); + g_free (orin_value); + } + if (value) + g_hash_table_insert (conn, g_strdup (key), + strip_string (g_strdup (value), '"')); + net_parser_data_changed = TRUE; +} + +// Remember to free return value +gchar * +ifnet_get_global_data (const gchar * key) +{ + gchar *result = g_hash_table_lookup (global_settings_table, key); + + if (result) + result = g_strdup (result); + else + return NULL; + strip_string (result, '"'); + return result; +} + +// Return names of legal connections +GList * +ifnet_get_connection_names () +{ + GList *names = g_hash_table_get_keys (conn_table); + GList *result = NULL; + + while (names) { + if (!ignore_connection_name (names->data)) + result = g_list_append (result, names->data); + names = names->next; + } + return result; +} + +/* format IP and route for writing */ +static void +format_ips (gchar * value, gchar ** out_line, gchar * key, gchar * name) +{ + gchar **ipset; + guint length, i; + GString *formated_string = g_string_new (NULL); + + strip_string (value, '"'); + ipset = g_strsplit (value, "\" \"", 0); + length = g_strv_length (ipset); + + //only one line + if (length < 2) { + *out_line = + g_strdup_printf ("%s_%s=( \"%s\" )\n", key, name, value); + goto done; + } + // Multiple lines + g_string_append_printf (formated_string, "%s_%s=(\n", key, name); + for (i = 0; i < length; i++) + g_string_append_printf (formated_string, + "\t\"%s\"\n", ipset[i]); + g_string_append (formated_string, ")\n"); + *out_line = g_strdup (formated_string->str); + done: + g_string_free (formated_string, TRUE); + g_strfreev (ipset); +} + +gboolean +ifnet_flush_to_file (gchar * config_file) +{ + GIOChannel *channel; + GError **error = NULL; + gpointer key, value, name, network; + GHashTableIter iter, iter_network; + GList *list_iter; + gchar *out_line; + gsize bytes_written; + gboolean result = FALSE; + + if (!net_parser_data_changed) + return FALSE; + if (!conn_table || !global_settings_table) + return FALSE; + + channel = g_io_channel_new_file (config_file, "w", NULL); + if (!channel) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't open file %s for writing", config_file); + return FALSE; + } + g_hash_table_iter_init (&iter, global_settings_table); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Writing to %s", config_file); + g_io_channel_write_chars (channel, + "#Generated by NetworkManager\n" + "###### Global Configuration ######\n", + -1, &bytes_written, error); + /* Writing global data */ + while (g_hash_table_iter_next (&iter, &key, &value)) { + out_line = + g_strdup_printf ("%s=%s\n", (gchar *) key, (gchar *) value); + g_io_channel_write_chars (channel, out_line, -1, + &bytes_written, error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto done; + } + + /* Writing connection data */ + g_io_channel_write_chars (channel, + "\n###### Connection Configuration ######\n", + -1, &bytes_written, error); + g_hash_table_iter_init (&iter, conn_table); + while (g_hash_table_iter_next (&iter, &name, &network)) { + g_hash_table_iter_init (&iter_network, (GHashTable *) network); + g_io_channel_write_chars (channel, + "#----------------------------------\n", + -1, &bytes_written, error); + + while (g_hash_table_iter_next (&iter_network, &key, &value)) { + if (!g_str_has_prefix ((gchar *) key, "name") + && !g_str_has_prefix ((gchar *) key, "type")) { + /* These keys contain brackets */ + if (strcmp + ((gchar *) key, + "config") == 0 + || strcmp ((gchar *) key, + "routes") == 0 + || strcmp ((gchar *) key, + "pppd") == 0 + || strcmp ((gchar *) key, "chat") == 0) + format_ips (value, &out_line, (gchar *) + key, (gchar *) + name); + else + out_line = + g_strdup_printf + ("%s_%s=\"%s\"\n", + (gchar *) key, + (gchar *) name, (gchar *) value); + g_io_channel_write_chars + (channel, out_line, -1, + &bytes_written, error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + } + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto done; + } + + /* Writing reserved functions */ + if (functions_list) { + g_io_channel_write_chars (channel, + "\n###### Reserved Functions ######\n", + -1, &bytes_written, error); + /* Writing functions */ + for (list_iter = functions_list; list_iter; + list_iter = g_list_next (list_iter)) { + out_line = + g_strdup_printf ("%s\n", (gchar *) list_iter->data); + g_io_channel_write_chars (channel, out_line, -1, + &bytes_written, error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto done; + } + } + + g_io_channel_flush (channel, error); + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Found error: %s", (*error)->message); + goto done; + } + result = TRUE; + net_parser_data_changed = FALSE; + done: + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + return result; +} + +gboolean +ifnet_delete_network (gchar * conn_name) +{ + GHashTable *network = NULL; + + g_return_val_if_fail (conn_table != NULL && conn_name != NULL, FALSE); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Deleting network for %s", conn_name); + network = g_hash_table_lookup (conn_table, conn_name); + if (!network) + return FALSE; + g_hash_table_remove (conn_table, conn_name); + destroy_connection_config (network); + net_parser_data_changed = TRUE; + return TRUE; +} + +void +ifnet_destroy (void) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + GList *list_iter; + + /* Destroy connection setting */ + if (conn_table) { + g_hash_table_iter_init (&iter, conn_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + destroy_connection_config ((GHashTable *) + value); + } + g_hash_table_destroy (conn_table); + conn_table = NULL; + } + + /* Destroy global data */ + if (global_settings_table) { + g_hash_table_iter_init (&iter, global_settings_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_free (key); + g_free (value); + } + g_hash_table_destroy (global_settings_table); + global_settings_table = NULL; + } + for (list_iter = functions_list; list_iter; + list_iter = g_list_next (list_iter)) + g_free (list_iter->data); + g_list_free (functions_list); +} diff --git a/system-settings/plugins/ifnet/net_parser.h b/system-settings/plugins/ifnet/net_parser.h new file mode 100644 index 0000000000..73a44c857d --- /dev/null +++ b/system-settings/plugins/ifnet/net_parser.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#ifndef _NET_PARSER_H +#define _NET_PARSER_H + +#include <glib.h> + +#define CONF_NET_FILE "/etc/conf.d/net" +#define IFNET_SYSTEM_SETTINGS_KEY_FILE "/etc/NetworkManager/nm-system-settings.conf" +#define IFNET_KEY_FILE_GROUP "ifnet" + +gboolean ifnet_init (gchar * config_file); +void ifnet_destroy (void); + +/* Reader functions */ +GList *ifnet_get_connection_names (void); +gchar *ifnet_get_data (gchar * conn_name, const gchar * key); +gchar *ifnet_get_global_data (const gchar * key); +gchar *ifnet_get_global_setting (gchar * group, gchar * key); +gboolean ifnet_has_connection (gchar * conn_name); + +/* Writer functions */ +gboolean ifnet_flush_to_file (gchar * config_file); +void ifnet_set_data (gchar * conn_name, gchar * key, gchar * value); +gboolean ifnet_add_connection (gchar * name, gchar * type); +gboolean ifnet_delete_network (gchar * conn_name); +#endif diff --git a/system-settings/plugins/ifnet/net_utils.c b/system-settings/plugins/ifnet/net_utils.c new file mode 100644 index 0000000000..8a541979d8 --- /dev/null +++ b/system-settings/plugins/ifnet/net_utils.c @@ -0,0 +1,932 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <nm-utils.h> +#include <nm-system-config-interface.h> +#include "net_utils.h" +#include "wpa_parser.h" +#include "net_parser.h" + +/* emit heading and tailing blank space, tab, character t */ +gchar * +strip_string (gchar * str, gchar t) +{ + gchar *ret = str; + gint length = 0; + guint i = 0; + + while (ret[i] != '\0' + && (ret[i] == '\t' || ret[i] == ' ' || ret[i] == t)) { + length++; + i++; + } + i = 0; + while (ret[i + length] != '\0') { + ret[i] = ret[i + length]; + i++; + } + ret[i] = '\0'; + length = strlen (ret); + while ((length - 1) >= 0 + && (ret[length - 1] == ' ' || ret[length - 1] == '\n' + || ret[length - 1] == '\t' || ret[length - 1] == t)) + length--; + ret[length] = '\0'; + return ret; +} + +gboolean +is_hex (gchar * value) +{ + gchar *p; + + if (!value) + return FALSE; + p = value; + while (*p) { + if (!isxdigit (*p)) { + return FALSE; + } + p++; + } + return TRUE; +} + +gboolean +is_ascii (gchar * value) +{ + gchar *p; + + p = value; + while (*p) { + if (!isascii (*p)) { + return FALSE; + } + p++; + } + return TRUE; + +} + +gboolean +is_true (char *str) +{ + if (!g_ascii_strcasecmp (str, "yes") + || !g_ascii_strcasecmp (str, "true")) + return TRUE; + return FALSE; +} + +static int +hex2num (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int +hex2byte (const char *hex) +{ + int a, b; + + a = hex2num (*hex++); + if (a < 0) + return -1; + b = hex2num (*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +/* free return value by caller */ +gchar * +utils_hexstr2bin (const gchar * hex, size_t len) +{ + size_t i; + int a; + const gchar *ipos = hex; + gchar *buf = NULL; + gchar *opos; + + /* Length must be a multiple of 2 */ + if ((len % 2) != 0) + return NULL; + + opos = buf = g_malloc0 ((len / 2) + 1); + for (i = 0; i < len; i += 2) { + a = hex2byte (ipos); + if (a < 0) { + g_free (buf); + return NULL; + } + *opos++ = a; + ipos += 2; + } + return buf; +} + +/* free return value by caller */ +gchar * +utils_bin2hexstr (const gchar * bytes, int len, int final_len) +{ + static gchar hex_digits[] = "0123456789abcdef"; + gchar *result; + int i; + gsize buflen = (len * 2) + 1; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + g_return_val_if_fail (len < 4096, NULL); /* Arbitrary limit */ + if (final_len > -1) + g_return_val_if_fail (final_len < buflen, NULL); + + result = g_malloc0 (buflen); + for (i = 0; i < len; i++) { + result[2 * i] = hex_digits[(bytes[i] >> 4) & 0xf]; + result[2 * i + 1] = hex_digits[bytes[i] & 0xf]; + } + /* Cut converted key off at the correct length for this cipher type */ + if (final_len > -1) + result[final_len] = '\0'; + else + result[buflen - 1] = '\0'; + + return result; +} + +GQuark +ifnet_plugin_error_quark (void) +{ + static GQuark error_quark = 0; + + if (G_UNLIKELY (error_quark == 0)) + error_quark = + g_quark_from_static_string ("ifnet-plugin-error-quark"); + return error_quark; +} + +gboolean +reload_parsers () +{ + ifnet_destroy (); + wpa_parser_destroy (); + if (!ifnet_init (CONF_NET_FILE)) + return FALSE; + wpa_parser_init (WPA_SUPPLICANT_CONF); + return TRUE; +} + +gchar * +read_hostname (gchar * path) +{ + gchar *contents = NULL, *result = NULL, *tmp; + gchar **all_lines = NULL; + guint line_num, i; + + if (!g_file_get_contents (path, &contents, NULL, NULL)) + return NULL; + all_lines = g_strsplit (contents, "\n", 0); + line_num = g_strv_length (all_lines); + for (i = 0; i < line_num; i++) { + g_strstrip (all_lines[i]); + if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') + continue; + if (g_str_has_prefix (all_lines[i], "hostname")) { + tmp = strstr (all_lines[i], "="); + tmp++; + tmp = strip_string (tmp, '"'); + result = g_strdup (tmp); + break; + } + + } + g_strfreev (all_lines); + g_free (contents); + return result; +} + +gboolean +write_hostname (const gchar * hostname, gchar * path) +{ + gchar *contents = g_strdup_printf ("#Generated by NetworkManager\n" + "hostname=\"%s\"\n", hostname); + gboolean result = g_file_set_contents (path, contents, -1, NULL); + + g_free (contents); + return result; +} + +gboolean +is_static_ip4 (gchar * conn_name) +{ + gchar *data = ifnet_get_data (conn_name, "config"); + gchar *dhcp6; + + if (!data) + return FALSE; + dhcp6 = strstr (data, "dhcp6"); + if (dhcp6) { + gchar *dhcp4; + + if (strstr (data, "dhcp ")) + return FALSE; + dhcp4 = strstr (data, "dhcp"); + if (!dhcp4) + return TRUE; + if (dhcp4[4] == '\0') + return FALSE; + return TRUE; + } + return strstr (data, "dhcp") == NULL ? TRUE : FALSE; +} + +gboolean +is_static_ip6 (gchar * conn_name) +{ + gchar *data = ifnet_get_data (conn_name, "config"); + + if (!data) + return TRUE; + return strstr (data, "dhcp6") == NULL ? TRUE : FALSE; +} + +gboolean +is_ip4_address (gchar * in_address) +{ + gchar *pattern = + "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.((\\{\\d{1,3}\\.\\.\\d{1,3}\\})|\\d{1,3})$"; + gchar *address = g_strdup (in_address); + gboolean result = FALSE; + gchar *tmp; + GRegex *regex = g_regex_new (pattern, 0, 0, NULL); + GMatchInfo *match_info; + + if (!address) + goto done; + g_strstrip (address); + if ((tmp = strstr (address, "/")) != NULL) + *tmp = '\0'; + if ((tmp = strstr (address, " ")) != NULL) + *tmp = '\0'; + g_regex_match (regex, address, 0, &match_info); + result = g_match_info_matches (match_info); + done: + if (match_info) + g_match_info_free (match_info); + g_regex_unref (regex); + g_free (address); + return result; +} + +gboolean +is_ip6_address (gchar * in_address) +{ + struct in6_addr tmp_ip6_addr; + gchar *tmp; + gchar *address = g_strdup (in_address); + gboolean result = FALSE; + + if (!address) { + g_free (address); + return FALSE; + } + g_strstrip (address); + if ((tmp = strchr (address, '/')) != NULL) + *tmp = '\0'; + if (inet_pton (AF_INET6, address, &tmp_ip6_addr)) + result = TRUE; + g_free (address); + return result; + +} + +gboolean +has_ip6_address (gchar * conn_name) +{ + gchar **ipset; + guint length; + guint i; + + g_return_val_if_fail (conn_name != NULL, FALSE); + ipset = g_strsplit (ifnet_get_data (conn_name, "config"), "\" \"", 0); + length = g_strv_length (ipset); + for (i = 0; i < length; i++) { + if (!is_ip6_address (ipset[i])) + continue; + else { + g_strfreev (ipset); + return TRUE; + } + + } + g_strfreev (ipset); + return FALSE; +} + +gboolean +has_default_route (gchar * conn_name, gboolean (*check_fn) (gchar *)) +{ + gchar *routes = NULL, *tmp, *end; + + g_return_val_if_fail (conn_name != NULL, FALSE); + tmp = ifnet_get_data (conn_name, "routes"); + if (!tmp) + return FALSE; + routes = g_strdup (tmp); + tmp = strstr (routes, "default via "); + if (!tmp) { + goto error; + } + tmp += strlen ("default via "); + g_strstrip (tmp); + if ((end = strstr (tmp, "\"")) != NULL) + *end = '\0'; + if (check_fn (tmp)) { + g_free (routes); + return TRUE; + } + error: + g_free (routes); + return FALSE; +} + +static ip_block * +create_ip4_block (gchar * ip) +{ + ip_block *iblock = g_slice_new0 (ip_block); + struct in_addr tmp_ip4_addr; + int i; + guint length; + gchar **ip_mask; + + /* prefix format */ + if (strstr (ip, "/")) { + gchar *prefix; + + ip_mask = g_strsplit (ip, "/", 0); + length = g_strv_length (ip_mask); + if (!inet_pton (AF_INET, ip_mask[0], &tmp_ip4_addr)) + goto error; + iblock->ip = tmp_ip4_addr.s_addr; + prefix = ip_mask[1]; + i = 0; + while (i < length && isdigit (prefix[i])) + i++; + prefix[i] = '\0'; + iblock->netmask = nm_utils_ip4_prefix_to_netmask ((guint32) + atoi (ip_mask + [1])); + } else if (strstr (ip, "netmask")) { + ip_mask = g_strsplit (ip, " ", 0); + length = g_strv_length (ip_mask); + if (!inet_pton (AF_INET, ip_mask[0], &tmp_ip4_addr)) + goto error; + iblock->ip = tmp_ip4_addr.s_addr; + i = 0; + while (i < length && !strstr (ip_mask[++i], "netmask")) ; + while (i < length && ip_mask[++i][0] == '\0') ; + if (i >= length) + goto error; + if (!inet_pton (AF_INET, ip_mask[i], &tmp_ip4_addr)) + goto error; + iblock->netmask = tmp_ip4_addr.s_addr; + } else { + g_slice_free (ip_block, iblock); + if (!is_ip6_address (ip) && !strstr (ip, "dhcp")) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't handle ipv4 address: %s, missing netmask or prefix", + ip); + return NULL; + } + g_strfreev (ip_mask); + return iblock; + error: + if (!is_ip6_address (ip)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Can't handle IPv4 address: %s", + ip); + g_strfreev (ip_mask); + g_slice_free (ip_block, iblock); + return NULL; +} + +static ip6_block * +create_ip6_block (gchar * ip) +{ + ip6_block *iblock = g_slice_new0 (ip6_block); + gchar *dup_ip = g_strdup (ip); + struct in6_addr *tmp_ip6_addr = g_slice_new0 (struct in6_addr); + gchar *prefix = NULL; + + if ((prefix = strstr (dup_ip, "/")) != NULL) { + *prefix = '\0'; + prefix++; + } + if (!inet_pton (AF_INET6, dup_ip, tmp_ip6_addr)) { + goto error; + } + iblock->ip = tmp_ip6_addr; + if (prefix) { + errno = 0; + iblock->prefix = strtol (prefix, NULL, 10); + if (errno || iblock->prefix <= 0 || iblock->prefix > 128) { + goto error; + } + } else + iblock->prefix = 64; + g_free (dup_ip); + return iblock; + error: + if (!is_ip4_address (ip)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Can't handle IPv6 address: %s", + ip); + g_slice_free (ip6_block, iblock); + g_slice_free (struct in6_addr, tmp_ip6_addr); + + g_free (dup_ip); + return NULL; +} + +static guint32 +get_ip4_gateway (gchar * gateway) +{ + gchar *tmp, *split; + struct in_addr tmp_ip4_addr; + + if (!gateway) + return 0; + tmp = strstr (gateway, "via "); + tmp = g_strdup (tmp + strlen ("via ")); + strip_string (tmp, ' '); + strip_string (tmp, '"'); + if ((split = strstr (tmp, "\"")) != NULL) + *split = '\0'; + if (!inet_pton (AF_INET, tmp, &tmp_ip4_addr)) + goto error; + g_free (tmp); + return tmp_ip4_addr.s_addr; + error: + if (!is_ip6_address (tmp)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Can't handle IPv4 gateway: %s", + tmp); + g_free (tmp); + return 0; +} + +static struct in6_addr * +get_ip6_next_hop (gchar * next_hop) +{ + gchar *tmp; + struct in6_addr *tmp_ip6_addr = g_slice_new0 (struct in6_addr); + + if (!next_hop) + return 0; + tmp = strstr (next_hop, "via "); + tmp = g_strdup (tmp + strlen ("via ")); + strip_string (tmp, ' '); + strip_string (tmp, '"'); + g_strstrip (tmp); + if (!inet_pton (AF_INET6, tmp, tmp_ip6_addr)) + goto error; + g_free (tmp); + return tmp_ip6_addr; + error: + if (!is_ip4_address (tmp)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't handle IPv6 next_hop: %s", tmp); + g_free (tmp); + g_slice_free (struct in6_addr, tmp_ip6_addr); + + return NULL; +} + +ip_block * +convert_ip4_config_block (gchar * conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip; + guint32 def_gateway; + gchar *routes; + gchar *pos; + ip_block *start = NULL, *current = NULL, *iblock = NULL; + gchar *pattern = + "((\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.)\\{(\\d{1,3})\\.\\.(\\d{1,3})\\}(/\\d{1,2}))"; + GRegex *regex = g_regex_new (pattern, 0, 0, NULL); + + g_return_val_if_fail (conn_name != NULL, NULL); + ipset = g_strsplit (ifnet_get_data (conn_name, "config"), "\" \"", 0); + length = g_strv_length (ipset); + routes = ifnet_get_data (conn_name, "routes"); + if (routes) + def_gateway = get_ip4_gateway (strstr (routes, "default")); + else + def_gateway = 0; + for (i = 0; i < length; i++) { + ip = ipset[i]; + ip = strip_string (ip, '"'); + //Handle ip like 192.168.4.{1..3} + if ((pos = strchr (ip, '{')) != NULL) { + gchar *ip_start, *ip_prefix; + gchar *begin_str, *end_str; + int begin, end, j; + GMatchInfo *match_info; + + g_regex_match (regex, ip, 0, &match_info); + if (!g_match_info_matches (match_info)) { + g_match_info_free (match_info); + continue; + } + begin_str = g_match_info_fetch (match_info, 3); + end_str = g_match_info_fetch (match_info, 4); + begin = atoi (begin_str); + end = atoi (end_str); + ip_start = g_match_info_fetch (match_info, 2); + ip_prefix = g_match_info_fetch (match_info, 5); + if (end < begin || begin < 1 || end > 254) { + g_match_info_free (match_info); + continue; + } + + for (j = begin; j <= end; j++) { + char suf[4]; + gchar *newip; + + sprintf (suf, "%d", j); + newip = + g_strconcat (ip_start, suf, ip_prefix, + NULL); + iblock = create_ip4_block (newip); + if (iblock == NULL) { + g_free (newip); + continue; + } + if (!iblock->gateway && def_gateway != 0) + iblock->gateway = def_gateway; + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + g_free (newip); + } + g_free (begin_str); + g_free (end_str); + g_free (ip_start); + g_free (ip_prefix); + g_match_info_free (match_info); + } else { + iblock = create_ip4_block (ip); + if (iblock == NULL) + continue; + if (!iblock->gateway && def_gateway != 0) + iblock->gateway = def_gateway; + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + } + } + g_strfreev (ipset); + g_regex_unref (regex); + return start; +} + +ip6_block * +convert_ip6_config_block (gchar * conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip; + ip6_block *start = NULL, *current = NULL, *iblock = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + ipset = g_strsplit (ifnet_get_data (conn_name, "config"), "\" \"", 0); + length = g_strv_length (ipset); + for (i = 0; i < length; i++) { + ip = ipset[i]; + ip = strip_string (ip, '"'); + iblock = create_ip6_block (ip); + if (iblock == NULL) + continue; + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + } + g_strfreev (ipset); + return start; +} + +ip_block * +convert_ip4_routes_block (gchar * conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip; + gchar *routes; + ip_block *start = NULL, *current = NULL, *iblock = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + routes = ifnet_get_data (conn_name, "routes"); + if (!routes) + return NULL; + ipset = g_strsplit (routes, "\" \"", 0); + length = g_strv_length (ipset); + for (i = 0; i < length; i++) { + ip = ipset[i]; + if (strstr (ip, "default via ") || strstr (ip, "::") + || !strstr (ip, "via")) + continue; + ip = strip_string (ip, '"'); + iblock = create_ip4_block (ip); + if (iblock == NULL) + continue; + iblock->gateway = get_ip4_gateway (ip); + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + } + g_strfreev (ipset); + return start; +} + +ip6_block * +convert_ip6_routes_block (gchar * conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip, *tmp_addr; + gchar *routes; + ip6_block *start = NULL, *current = NULL, *iblock = NULL; + struct in6_addr *tmp_ip6_addr; + + g_return_val_if_fail (conn_name != NULL, NULL); + routes = ifnet_get_data (conn_name, "routes"); + if (!routes) + return NULL; + ipset = g_strsplit (routes, "\" \"", 0); + length = g_strv_length (ipset); + for (i = 0; i < length; i++) { + ip = ipset[i]; + ip = strip_string (ip, '"'); + if (ip[0] == '\0') + continue; + printf ("ip:%s\n", ip); + if ((tmp_addr = strstr (ip, "default via ")) != NULL) { + tmp_addr += strlen ("default via "); + if (!is_ip6_address (tmp_addr)) + continue; + else { + tmp_ip6_addr = g_slice_new0 (struct in6_addr); + + if (inet_pton (AF_INET6, "::", tmp_ip6_addr)) { + iblock = g_slice_new0 (ip6_block); + iblock->ip = tmp_ip6_addr; + iblock->prefix = 128; + } else { + g_slice_free (struct in6_addr, + tmp_ip6_addr); + continue; + } + } + } else + iblock = create_ip6_block (ip); + if (iblock == NULL) + continue; + iblock->next_hop = get_ip6_next_hop (ip); + if (iblock->next_hop == NULL) { + destroy_ip6_block (iblock); + continue; + } + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + } + g_strfreev (ipset); + return start; +} + +void +destroy_ip_block (ip_block * iblock) +{ + g_slice_free (ip_block, iblock); +} + +void +destroy_ip6_block (ip6_block * iblock) +{ + g_slice_free (struct in6_addr, iblock->ip); + g_slice_free (struct in6_addr, iblock->next_hop); + + g_slice_free (ip6_block, iblock); +} + +void +set_ip4_dns_servers (NMSettingIP4Config * s_ip4, gchar * conn_name) +{ + gchar *dns_servers = ifnet_get_data (conn_name, "dns_servers"); + gchar **server_list; + guint length, i; + struct in_addr tmp_ip4_addr; + guint32 new_dns; + + if (!dns_servers) + return; + strip_string (dns_servers, '"'); + server_list = g_strsplit (dns_servers, " ", 0); + length = g_strv_length (server_list); + if (length) + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, + TRUE, NULL); + for (i = 0; i < length; i++) { + g_strstrip (server_list[i]); + if (server_list[i][0] == '\0') + continue; + if (!inet_pton (AF_INET, server_list[i], &tmp_ip4_addr)) { + if (!is_ip6_address (server_list[i])) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "ignored dns: %s\n", + server_list[i]); + continue; + } + new_dns = tmp_ip4_addr.s_addr; + if (new_dns && !nm_setting_ip4_config_add_dns (s_ip4, new_dns)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "warning: duplicate DNS server %s", + server_list[i]); + } + g_strfreev (server_list); +} + +void +set_ip6_dns_servers (NMSettingIP6Config * s_ip6, gchar * conn_name) +{ + gchar *dns_servers = ifnet_get_data (conn_name, "dns_servers"); + gchar **server_list; + guint length, i; + struct in6_addr tmp_ip6_addr; + + if (!dns_servers) + return; + strip_string (dns_servers, '"'); + server_list = g_strsplit (dns_servers, " ", 0); + length = g_strv_length (server_list); + if (length) + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, + TRUE, NULL); + for (i = 0; i < length; i++) { + g_strstrip (server_list[i]); + if (server_list[i][0] == '\0') + continue; + if (!inet_pton (AF_INET6, server_list[i], &tmp_ip6_addr)) { + if (is_ip6_address (server_list[i])) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "ignored dns: %s\n", + server_list[i]); + continue; + } + if (!IN6_IS_ADDR_UNSPECIFIED (&tmp_ip6_addr) + && !nm_setting_ip6_config_add_dns (s_ip6, &tmp_ip6_addr)) + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "warning: duplicate DNS server %s", + server_list[i]); + } + g_strfreev (server_list); +} + +gboolean +is_managed (gchar * conn_name) +{ + gchar *config; + + g_return_val_if_fail (conn_name != NULL, FALSE); + config = (gchar *) ifnet_get_data (conn_name, "managed"); + if (!config) + return TRUE; + if (strcmp (config, "false") == 0) + return FALSE; + return TRUE; +} + +void +get_dhcp_hostname_and_client_id (char **hostname, char **client_id) +{ + gchar *dhcp_client = ifnet_get_global_setting ("main", "dhcp"); + const gchar *dhcpcd_conf = "/etc/dhcpcd.conf"; + const gchar *dhclient_conf = "/etc/dhcp/dhclient.conf"; + gchar *line = NULL, *tmp = NULL, *contents = NULL; + gchar **all_lines; + guint line_num, i; + + *hostname = NULL; + *client_id = NULL; + if (dhcp_client) { + if (!strcmp (dhcp_client, "dhclient")) + g_file_get_contents (dhclient_conf, &contents, NULL, + NULL); + else if (!strcmp (dhcp_client, "dhcpcd")) + g_file_get_contents (dhcpcd_conf, &contents, NULL, + NULL); + } else { + if (g_file_test (dhclient_conf, G_FILE_TEST_IS_REGULAR)) + g_file_get_contents (dhclient_conf, &contents, NULL, + NULL); + else if (g_file_test (dhcpcd_conf, G_FILE_TEST_IS_REGULAR)) + g_file_get_contents (dhcpcd_conf, &contents, NULL, + NULL); + } + if (!contents) + return; + all_lines = g_strsplit (contents, "\n", 0); + line_num = g_strv_length (all_lines); + for (i = 0; i < line_num; i++) { + line = all_lines[i]; + // dhcpcd.conf + g_strstrip (line); + if (g_str_has_prefix (line, "hostname")) { + tmp = line + strlen ("hostname"); + g_strstrip (tmp); + if (tmp[0] != '\0') + *hostname = g_strdup (tmp); + else + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "dhcpcd hostname not defined, ignoring"); + } else if (g_str_has_prefix (line, "clientid")) { + tmp = line + strlen ("clientid"); + g_strstrip (tmp); + if (tmp[0] != '\0') + *client_id = g_strdup (tmp); + else + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "dhcpcd clientid not defined, ignoring"); + } + // dhclient.conf + else if ((tmp = strstr (line, "send host-name")) != NULL) { + tmp += strlen ("send host-name"); + g_strstrip (tmp); + strip_string (tmp, '"'); + strip_string (tmp, ';'); + if (tmp[0] != '\0') + *hostname = g_strdup (tmp); + else + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "dhclient hostname not defined, ignoring"); + } else if ((tmp = strstr (line, "send dhcp-client-identifier")) + != NULL) { + tmp += strlen ("send dhcp-client-identifier"); + g_strstrip (tmp); + strip_string (tmp, ';'); + if (tmp[0] != '\0') + *client_id = g_strdup (tmp); + else + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "dhclient clientid not defined, ignoring"); + } + } + g_strfreev (all_lines); + g_free (contents); +} diff --git a/system-settings/plugins/ifnet/net_utils.h b/system-settings/plugins/ifnet/net_utils.h new file mode 100644 index 0000000000..ba7af39c27 --- /dev/null +++ b/system-settings/plugins/ifnet/net_utils.h @@ -0,0 +1,80 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#ifndef _IFNET_UTILS_H +#define _IFNET_UTILS_H +#define IFNET_PLUGIN_NAME "SCPlugin-Ifnet" +#include <glib.h> +#include <arpa/inet.h> +#include <nm-setting-ip6-config.h> +#include <nm-setting-ip4-config.h> +#include "net_parser.h" +#define has_default_ip4_route(conn_name) has_default_route((conn_name),&is_ip4_address) +#define has_default_ip6_route(conn_name) has_default_route((conn_name),&is_ip6_address) + +typedef struct _ip_block { + guint32 ip; + guint32 netmask; + guint32 gateway; + struct _ip_block *next; +} ip_block; + +typedef struct _ip6_block { + struct in6_addr *ip; + long int prefix; + struct in6_addr *next_hop; + struct _ip6_block *next; +} ip6_block; + +gchar *read_hostname (gchar * path); +gboolean write_hostname (const gchar * hostname, gchar * path); +gboolean is_static_ip4 (gchar * conn_name); +gboolean is_static_ip6 (gchar * conn_name); +gboolean is_ip4_address (gchar * in_address); +gboolean is_ip6_address (gchar * in_address); +gboolean has_ip6_address (gchar * conn_name); +gboolean has_default_route (gchar * conn_name, gboolean (*check_fn) (gchar *)); +gboolean reload_parsers (void); + +ip_block *convert_ip4_config_block (gchar * conn_name); +ip6_block *convert_ip6_config_block (gchar * conn_name); +ip_block *convert_ip4_routes_block (gchar * conn_name); +ip6_block *convert_ip6_routes_block (gchar * conn_name); +void destroy_ip_block (ip_block * iblock); +void destroy_ip6_block (ip6_block * iblock); + +void set_ip4_dns_servers (NMSettingIP4Config * s_ip4, gchar * conn_name); +void set_ip6_dns_servers (NMSettingIP6Config * s_ip6, gchar * conn_name); + +gchar *strip_string (gchar * str, gchar t); +gboolean is_managed (gchar * conn_name); + +GQuark ifnet_plugin_error_quark (void); +gchar *utils_hexstr2bin (const gchar * hex, size_t len); +gchar *utils_bin2hexstr (const gchar * bytes, int len, int final_len); + +gboolean is_hex (gchar * value); +gboolean is_ascii (gchar * value); +gboolean is_true (gchar * str); + +void get_dhcp_hostname_and_client_id (char **hostname, char **client_id); + +#endif diff --git a/system-settings/plugins/ifnet/nm-ifnet-connection.c b/system-settings/plugins/ifnet/nm-ifnet-connection.c new file mode 100644 index 0000000000..e47495cfbe --- /dev/null +++ b/system-settings/plugins/ifnet/nm-ifnet-connection.c @@ -0,0 +1,251 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <string.h> +#include <glib/gstdio.h> +#include <NetworkManager.h> +#include <nm-utils.h> +#include <nm-setting-wireless-security.h> +#include <nm-sysconfig-connection.h> +#include <nm-system-config-interface.h> +#include <nm-system-config-error.h> +#include "nm-ifnet-connection.h" +#include "connection_parser.h" +#include "net_parser.h" +#include "net_utils.h" +#include "wpa_parser.h" +#include "plugin.h" + +static NMSettingsConnectionInterface *parent_settings_connection_iface; + +static void settings_connection_interface_init (NMSettingsConnectionInterface * + klass); + +G_DEFINE_TYPE_EXTENDED (NMIfnetConnection, nm_ifnet_connection, + NM_TYPE_SYSCONFIG_CONNECTION, 0, + G_IMPLEMENT_INTERFACE + (NM_TYPE_SETTINGS_CONNECTION_INTERFACE, + settings_connection_interface_init)) +// G_DEFINE_TYPE(NMIfnetConnection, nm_ifnet_connection, +// NM_TYPE_SYSCONFIG_CONNECTION) +#define NM_IFNET_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionPrivate)) +enum { + PROP_ZERO, + PROP_CONN_NAME, + _PROP_END, +}; + +enum { + IFNET_SETUP_MONITORS, + IFNET_CANCEL_MONITORS, + IFNET_LAST_SIGNAL +}; + +static guint signals[IFNET_LAST_SIGNAL] = { 0 }; + +typedef struct { + gchar *conn_name; + NMSystemConfigInterface *config; +} NMIfnetConnectionPrivate; + +NMIfnetConnection * +nm_ifnet_connection_new (gchar * conn_name) +{ + NMConnection *tmp; + GObject *object; + GError **error = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + tmp = ifnet_update_connection_from_config_block (conn_name, error); + if (!tmp) + return NULL; + object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, + NM_IFNET_CONNECTION_CONN_NAME, + conn_name, NULL); + if (!object) { + g_object_unref (tmp); + return NULL; + } + nm_sysconfig_connection_update (NM_SYSCONFIG_CONNECTION (object), tmp, + FALSE, NULL); + g_object_unref (tmp); + return NM_IFNET_CONNECTION (object); +} + +static void +nm_ifnet_connection_init (NMIfnetConnection * connection) +{ +} + +static gboolean +update (NMSettingsConnectionInterface * connection, + NMSettingsConnectionInterfaceUpdateFunc callback, gpointer user_data) +{ + GError *error = NULL; + gchar *new_conn_name = NULL; + gboolean result; + NMIfnetConnectionPrivate *priv = + NM_IFNET_CONNECTION_GET_PRIVATE (connection); + g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); + if (!ifnet_update_parsers_by_connection + (NM_CONNECTION (connection), priv->conn_name, &new_conn_name, + CONF_NET_FILE, WPA_SUPPLICANT_CONF, &error)) { + if (new_conn_name) + g_free (new_conn_name); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Failed to update %s", + priv->conn_name); + reload_parsers (); + callback (connection, error, user_data); + g_error_free (error); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + return FALSE; + } + + g_free (priv->conn_name); + priv->conn_name = new_conn_name; + result = + parent_settings_connection_iface->update (connection, callback, + user_data); + if (result) + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Successfully updated %s", + priv->conn_name); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + return result; +} + +static gboolean +do_delete (NMSettingsConnectionInterface * connection, + NMSettingsConnectionInterfaceDeleteFunc callback, gpointer user_data) +{ + GError *error = NULL; + gboolean result; + NMIfnetConnectionPrivate *priv = + NM_IFNET_CONNECTION_GET_PRIVATE (connection); + g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); + if (!ifnet_delete_connection_in_parsers + (priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF)) { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Failed to delete %s", + priv->conn_name); + reload_parsers (); + callback (connection, error, user_data); + g_error_free (error); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + return FALSE; + } + result = + parent_settings_connection_iface->delete (connection, callback, + user_data); + if (result) + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Successfully deleted %s", + priv->conn_name); + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + return result; +} + +static void +settings_connection_interface_init (NMSettingsConnectionInterface * iface) +{ + parent_settings_connection_iface = g_type_interface_peek_parent (iface); + iface->update = update; + iface->delete = do_delete; +} + +static void +set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + NMIfnetConnectionPrivate *priv = + NM_IFNET_CONNECTION_GET_PRIVATE (object); + g_return_if_fail (priv); + + switch (prop_id) { + case PROP_CONN_NAME: + if (priv->conn_name) + g_free (priv->conn_name); + priv->conn_name = g_strdup (g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + NMIfnetConnectionPrivate *priv = + NM_IFNET_CONNECTION_GET_PRIVATE (object); + g_return_if_fail (priv); + + switch (prop_id) { + case PROP_CONN_NAME: + g_value_set_pointer (value, priv->conn_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject * object) +{ + NMIfnetConnectionPrivate *priv = + NM_IFNET_CONNECTION_GET_PRIVATE (object); + g_return_if_fail (priv); + + if (priv->conn_name) + g_free (priv->conn_name); + G_OBJECT_CLASS (nm_ifnet_connection_parent_class)->finalize (object); +} + +static void +nm_ifnet_connection_class_init (NMIfnetConnectionClass * ifnet_connection_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ifnet_connection_class); + + g_type_class_add_private (ifnet_connection_class, + sizeof (NMIfnetConnectionPrivate)); + + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_CONN_NAME, + g_param_spec_pointer (NM_IFNET_CONNECTION_CONN_NAME, + "config_block", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + signals[IFNET_SETUP_MONITORS] = + g_signal_new ("ifnet_setup_monitors", + G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[IFNET_CANCEL_MONITORS] = + g_signal_new ("ifnet_cancel_monitors", + G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} diff --git a/system-settings/plugins/ifnet/nm-ifnet-connection.h b/system-settings/plugins/ifnet/nm-ifnet-connection.h new file mode 100644 index 0000000000..8b3d495f0b --- /dev/null +++ b/system-settings/plugins/ifnet/nm-ifnet-connection.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#ifndef NM_IFNET_CONNECTION_H +#define NM_IFNET_CONNECTION_H + +#include <nm-sysconfig-connection.h> +#include "net_parser.h" + +G_BEGIN_DECLS +#define NM_TYPE_IFNET_CONNECTION (nm_ifnet_connection_get_type ()) +#define NM_IFNET_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnection)) +#define NM_IFNET_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass)) +#define NM_IS_IFNET_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFNET_CONNECTION)) +#define NM_IS_IFNET_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IFNET_CONNECTION)) +#define NM_IFNET_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass)) +#define NM_IFNET_CONNECTION_CONN_NAME "connection_name" + typedef struct { + NMSysconfigConnection parent; +} NMIfnetConnection; + +typedef struct { + NMSysconfigConnectionClass parent; +} NMIfnetConnectionClass; + +GType nm_ifnet_connection_get_type (void); + +NMIfnetConnection *nm_ifnet_connection_new (gchar * conn_name); + +G_END_DECLS +#endif /* NM_IFNET_CONNECTION_H */ diff --git a/system-settings/plugins/ifnet/plugin.c b/system-settings/plugins/ifnet/plugin.c new file mode 100644 index 0000000000..51d560246d --- /dev/null +++ b/system-settings/plugins/ifnet/plugin.c @@ -0,0 +1,585 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service (ifnet) + * + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <string.h> + +#include <gmodule.h> +#include <glib.h> +#include <gio/gio.h> + +#include <nm-utils.h> +#include <nm-setting-connection.h> + +#include "NetworkManager.h" +#include "nm-system-config-interface.h" +#include "nm-ifnet-connection.h" + +#include "plugin.h" +#include "net_utils.h" +#include "net_parser.h" +#include "wpa_parser.h" +#include "connection_parser.h" + +#define IFNET_PLUGIN_NAME_PRINT "ifnet" +#define IFNET_PLUGIN_INFO "(C) 1999-2010 Gentoo Foundation, Inc. To report bugs please use bugs.gentoo.org with [networkmanager] or [dagger] prefix." +#define IFNET_SYSTEM_HOSTNAME_FILE "/etc/conf.d/hostname" +#define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE +#define IFNET_KEY_FILE_KEY_MANAGED "managed" + +typedef struct { + GHashTable *config_connections; + gchar *hostname; + gboolean unmanaged_well_known; + + GFileMonitor *hostname_monitor; + GFileMonitor *net_monitor; + GFileMonitor *wpa_monitor; + +} SCPluginIfnetPrivate; + +typedef void (*FileChangedFn) (gpointer user_data); + +typedef struct { + FileChangedFn callback; + gpointer user_data; +} FileMonitorInfo; + +static void system_config_interface_init (NMSystemConfigInterface * + system_config_interface_class); + +static void + reload_connections (gpointer config); + +G_DEFINE_TYPE_EXTENDED (SCPluginIfnet, sc_plugin_ifnet, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE, + system_config_interface_init)) +#define SC_PLUGIN_IFNET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_IFNET, SCPluginIfnetPrivate)) +/* +static void +ignore_cb(NMSettingsConnectionInterface * connection, + GError * error, gpointer user_data) +{ +} +*/ +static const char * +get_hostname (NMSystemConfigInterface * config) +{ + return SC_PLUGIN_IFNET_GET_PRIVATE (config)->hostname; +} + +static void +update_system_hostname (gpointer config) +{ + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (config); + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Updating hostname"); + + if (priv->hostname) + g_free (priv->hostname); + priv->hostname = read_hostname (IFNET_SYSTEM_HOSTNAME_FILE); + + g_object_notify (G_OBJECT (config), + NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Hostname updated to: %s", + priv->hostname); +} + +static void +write_system_hostname (NMSystemConfigInterface * config, + const gchar * newhostname) +{ + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (config); + + g_return_if_fail (newhostname); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Write system hostname: %s", + newhostname); + if (write_hostname (newhostname, IFNET_SYSTEM_HOSTNAME_FILE)) { + if (priv->hostname) + g_free (priv->hostname); + priv->hostname = g_strdup (newhostname); + g_object_notify (G_OBJECT (config), + NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME); + } else + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Write system hostname: %s failed", newhostname); +} + +static gboolean +is_managed_plugin () +{ + gchar *result = NULL; + + result = + ifnet_get_global_setting (IFNET_KEY_FILE_GROUP, + IFNET_KEY_FILE_KEY_MANAGED); + if (result) { + if (is_true (result)) { + g_free (result); + return TRUE; + } else { + g_free (result); + return FALSE; + } + } + return IFNET_MANAGE_WELL_KNOWN_DEFAULT; +} + +static void +file_changed (GFileMonitor * monitor, + GFile * file, + GFile * other_file, + GFileMonitorEvent event_type, gpointer user_data) +{ + FileMonitorInfo *info; + + switch (event_type) { + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + info = (FileMonitorInfo *) user_data; + info->callback (info->user_data); + break; + default: + break; + } +} + +static GFileMonitor * +monitor_file_changes (const char *filename, + FileChangedFn callback, gpointer user_data) +{ + GFile *file; + GFileMonitor *monitor; + FileMonitorInfo *info; + GError **error = NULL; + + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + return NULL; + file = g_file_new_for_path (filename); + monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, error); + g_object_unref (file); + + if (monitor) { + info = g_new0 (FileMonitorInfo, 1); + info->callback = callback; + info->user_data = user_data; + g_object_weak_ref (G_OBJECT (monitor), (GWeakNotify) g_free, + info); + g_signal_connect (monitor, "changed", G_CALLBACK (file_changed), + info); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Monitoring %s", filename); + + } else + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Monitoring %s failed, error: %s", filename, + error == NULL ? "nothing" : (*error)->message); + + return monitor; +} + +static void +update_old_connection (gchar * conn_name, + NMIfnetConnection * old_conn, + NMIfnetConnection * new_conn, + SCPluginIfnetPrivate * priv) +{ + GError **error = NULL; + + if (!nm_sysconfig_connection_update (NM_SYSCONFIG_CONNECTION (old_conn), + NM_CONNECTION (new_conn), TRUE, + error)) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "error updating: %s", + (error + && (*error)) ? (*error)->message : "(unknown)"); + } else + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Connection %s updated", + conn_name); + g_object_unref (new_conn); +} + +static void +setup_monitors (NMIfnetConnection * connection, gpointer user_data) +{ + SCPluginIfnet *self = SC_PLUGIN_IFNET (user_data); + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self); + + priv->hostname_monitor = + monitor_file_changes (IFNET_SYSTEM_HOSTNAME_FILE, + update_system_hostname, user_data); + priv->net_monitor = + monitor_file_changes (CONF_NET_FILE, reload_connections, user_data); + priv->wpa_monitor = + monitor_file_changes (WPA_SUPPLICANT_CONF, reload_connections, + user_data); +} + +static void +cancel_monitors (NMIfnetConnection * connection, gpointer user_data) +{ + SCPluginIfnet *self = SC_PLUGIN_IFNET (user_data); + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self); + + if (priv->hostname_monitor) { + g_file_monitor_cancel (priv->hostname_monitor); + g_object_unref (priv->hostname_monitor); + } + if (priv->net_monitor) { + g_file_monitor_cancel (priv->net_monitor); + g_object_unref (priv->net_monitor); + } + if (priv->wpa_monitor) { + g_file_monitor_cancel (priv->wpa_monitor); + g_object_unref (priv->wpa_monitor); + } +} + +static void +reload_connections (gpointer config) +{ + SCPluginIfnet *self = SC_PLUGIN_IFNET (config); + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self); + GList *conn_names = NULL, *n_iter = NULL; + + /* save names for removing unused connections */ + GHashTable *new_conn_names = NULL; + GHashTableIter iter; + gpointer key; + gpointer value; + + if (priv->unmanaged_well_known) + return; + + if (!reload_parsers ()) + return; + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Loading connections"); + conn_names = ifnet_get_connection_names (); + new_conn_names = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + for (n_iter = conn_names; n_iter; n_iter = g_list_next (n_iter)) { + NMIfnetConnection *exported; + NMIfnetConnection *old; + gchar *conn_name = g_strdup (n_iter->data); + + /* add the new connection */ + exported = nm_ifnet_connection_new (conn_name); + if (!exported) { + g_free (conn_name); + continue; + } + g_signal_connect (G_OBJECT (exported), "ifnet_setup_monitors", + G_CALLBACK (setup_monitors), config); + g_signal_connect (G_OBJECT (exported), "ifnet_cancel_monitors", + G_CALLBACK (cancel_monitors), config); + old = g_hash_table_lookup (priv->config_connections, conn_name); + if (old && exported) { + gchar *auto_refresh = + ifnet_get_global_setting (IFNET_KEY_FILE_GROUP, + "auto_refresh"); + + if (auto_refresh && is_true (auto_refresh)) { + if (!nm_connection_compare (NM_CONNECTION (old), + NM_CONNECTION + (exported), + NM_SETTING_COMPARE_FLAG_EXACT)) + { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "Auto refreshing %s", + conn_name); + g_signal_emit_by_name (old, + NM_SETTINGS_CONNECTION_INTERFACE_REMOVED); + g_hash_table_remove + (priv->config_connections, + conn_name); + g_hash_table_insert + (priv->config_connections, + g_strdup (conn_name), exported); + if (is_managed (conn_name)) + g_signal_emit_by_name (self, + NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, + exported); + } + } else + update_old_connection (conn_name, old, + exported, priv); + g_signal_emit_by_name (self, + NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED); + } else if (exported) { + g_hash_table_insert (priv->config_connections, + g_strdup (conn_name), exported); + if (is_managed (conn_name)) + g_signal_emit_by_name (self, + NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, + exported); + } + g_hash_table_insert (new_conn_names, conn_name, conn_name); + } + /* remove unused connections */ + g_hash_table_iter_init (&iter, priv->config_connections); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (!g_hash_table_lookup (new_conn_names, key)) { + g_signal_emit_by_name (value, + NM_SETTINGS_CONNECTION_INTERFACE_REMOVED); + g_hash_table_remove (priv->config_connections, key); + } + } + g_hash_table_remove_all (new_conn_names); + g_hash_table_destroy (new_conn_names); + g_list_free (conn_names); +} + +static gboolean +add_connection (NMSystemConfigInterface * config, + NMConnection * connection, GError ** error) +{ + gboolean result; + + result = ifnet_add_new_connection (connection, CONF_NET_FILE, + WPA_SUPPLICANT_CONF, error); + reload_connections (config); + return result; +} + +static void +check_unmanaged (gpointer key, gpointer data, gpointer user_data) +{ + GSList **list = (GSList **) user_data; + gchar *conn_name = (gchar *) key; + const char *unmanaged_spec; + GSList *iter; + + if (is_managed (conn_name)) + return; + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Checking unmanaged: %s", conn_name); + unmanaged_spec = ifnet_get_data (conn_name, "mac"); + if (!unmanaged_spec) + return; + + /* Just return if the unmanaged spec is already in the list */ + for (iter = *list; iter; iter = g_slist_next (iter)) { + if (!strcmp ((char *) iter->data, unmanaged_spec)) + return; + } + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Add unmanaged: %s", unmanaged_spec); + *list = + g_slist_prepend (*list, g_strdup_printf ("mac:%s", unmanaged_spec)); +} + +static GSList * +get_unmanaged_specs (NMSystemConfigInterface * config) +{ + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (config); + GSList *list = NULL; + + g_return_val_if_fail (priv->config_connections != NULL, NULL); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "getting unmanaged specs..."); + g_hash_table_foreach (priv->config_connections, check_unmanaged, &list); + return list; +} + +static void +SCPluginIfnet_init (NMSystemConfigInterface * config) +{ + SCPluginIfnet *self = SC_PLUGIN_IFNET (config); + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self); + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Initializing!"); + if (!priv->config_connections) + priv->config_connections = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + g_object_unref); + priv->unmanaged_well_known = !is_managed_plugin (); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "management mode: %s", + priv->unmanaged_well_known ? "unmanaged" : "managed"); + // GFileMonitor setup + setup_monitors (NULL, config); + reload_connections (config); + /* Now if we're running in managed mode, let NM know there are new connections */ + if (!priv->unmanaged_well_known) { + GHashTableIter iter; + gpointer key; + gpointer value; + + g_hash_table_iter_init (&iter, priv->config_connections); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (is_managed ((gchar *) key)) + g_signal_emit_by_name + (self, + NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, + NM_EXPORTED_CONNECTION (value)); + } + } + /* Read hostname */ + update_system_hostname (self); + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Initialzation complete!"); +} + +static GSList * +SCPluginIfnet_get_connections (NMSystemConfigInterface * config) +{ + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (config); + GSList *connections = NULL; + GHashTableIter iter; + gpointer key, value; + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "(%d) ... get_connections.", + GPOINTER_TO_UINT (config)); + if (priv->unmanaged_well_known) { + PLUGIN_PRINT (IFNET_PLUGIN_NAME, + "(%d) ... get_connections (managed=false): return empty list.", + GPOINTER_TO_UINT (config)); + return NULL; + } + + g_hash_table_iter_init (&iter, priv->config_connections); + while (g_hash_table_iter_next (&iter, &key, &value)) + if (is_managed ((gchar *) key)) + connections = g_slist_prepend (connections, value); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "(%d) connections count: %d", + GPOINTER_TO_UINT (config), g_slist_length (connections)); + return connections; +} + +static void +system_config_interface_init (NMSystemConfigInterface * + system_config_interface_class) +{ + system_config_interface_class->init = SCPluginIfnet_init; + system_config_interface_class->get_connections = + SCPluginIfnet_get_connections; + system_config_interface_class->get_unmanaged_specs = + get_unmanaged_specs; + system_config_interface_class->add_connection = add_connection; +} + +static void +sc_plugin_ifnet_init (SCPluginIfnet * plugin) +{ +} + +static void +get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + NMSystemConfigInterface *self = NM_SYSTEM_CONFIG_INTERFACE (object); + + switch (prop_id) { + case NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME: + g_value_set_string (value, IFNET_PLUGIN_NAME_PRINT); + break; + case NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO: + g_value_set_string (value, IFNET_PLUGIN_INFO); + break; + case NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES: + g_value_set_uint (value, + NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS + | + NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME); + break; + case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME: + g_value_set_string (value, get_hostname (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME:{ + const gchar *hostname = g_value_get_string (value); + + if (hostname && strlen (hostname) < 1) + hostname = NULL; + write_system_hostname (NM_SYSTEM_CONFIG_INTERFACE + (object), hostname); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject * object) +{ + SCPluginIfnet *plugin = SC_PLUGIN_IFNET (object); + SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (plugin); + + cancel_monitors (NULL, object); + if (priv->config_connections) { + g_hash_table_remove_all (priv->config_connections); + g_hash_table_destroy (priv->config_connections); + } + + if (priv->hostname) + g_free (priv->hostname); + ifnet_destroy (); + wpa_parser_destroy (); + G_OBJECT_CLASS (sc_plugin_ifnet_parent_class)->dispose (object); +} + +static void +sc_plugin_ifnet_class_init (SCPluginIfnetClass * req_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (req_class); + + g_type_class_add_private (req_class, sizeof (SCPluginIfnetPrivate)); + + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME, + NM_SYSTEM_CONFIG_INTERFACE_NAME); + + g_object_class_override_property (object_class, + NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO, + NM_SYSTEM_CONFIG_INTERFACE_INFO); + + g_object_class_override_property (object_class, + NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES, + NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES); + + g_object_class_override_property (object_class, + NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME, + NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME); +} + +G_MODULE_EXPORT GObject * +nm_system_config_factory (void) +{ + static SCPluginIfnet *singleton = NULL; + + if (!singleton) + singleton + = + SC_PLUGIN_IFNET (g_object_new (SC_TYPE_PLUGIN_IFNET, NULL)); + else + g_object_ref (singleton); + return G_OBJECT (singleton); +} diff --git a/system-settings/plugins/ifnet/plugin.h b/system-settings/plugins/ifnet/plugin.h new file mode 100644 index 0000000000..83099b63a0 --- /dev/null +++ b/system-settings/plugins/ifnet/plugin.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service (ifnet) + * + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +#include <glib-object.h> + +#define SC_TYPE_PLUGIN_IFNET (sc_plugin_ifnet_get_type ()) +#define SC_PLUGIN_IFNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_PLUGIN_IFNET, SCPluginIfnet)) +#define SC_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_PLUGIN_IFNET, SCPluginIfnetClass)) +#define SC_IS_PLUGIN_IFNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_PLUGIN_IFNET)) +#define SC_IS_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SC_TYPE_PLUGIN_IFNET)) +#define SC_PLUGIN_IFNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SC_TYPE_PLUGIN_IFNET, SCPluginIfnetClass)) + +typedef struct _SCPluginIfnet SCPluginIfnet; +typedef struct _SCPluginIfnetClass SCPluginIfnetClass; + +struct _SCPluginIfnet { + GObject parent; +}; + +struct _SCPluginIfnetClass { + GObjectClass parent; +}; + +GType sc_plugin_ifnet_get_type (void); +#endif diff --git a/system-settings/plugins/ifnet/tests/Makefile.am b/system-settings/plugins/ifnet/tests/Makefile.am new file mode 100644 index 0000000000..ead3f1fc4f --- /dev/null +++ b/system-settings/plugins/ifnet/tests/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES=-I$(top_srcdir)/system-settings/plugins/ifnet\ + -I$(top_srcdir)/libnm-glib \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src/system-settings +TESTS = check_ifnet +check_PROGRAMS = check_ifnet +check_ifnet_SOURCES = test_all.c +check_ifnet_LDFLAGS = -g +check_ifnet_CPPFLAGS = $(CHECK_CFLAGS) $(GLIB_CFLAGS) -g +check_ifnet_LDADD = $(top_srcdir)/libnm-util/libnm-util.la\ + $(top_srcdir)/system-settings/plugins/ifnet/lib-ifnet-io.la\ + $(CHECK_LIBS)\ + $(GLIB_LIBS) diff --git a/system-settings/plugins/ifnet/tests/hostname b/system-settings/plugins/ifnet/tests/hostname new file mode 100644 index 0000000000..25c761655a --- /dev/null +++ b/system-settings/plugins/ifnet/tests/hostname @@ -0,0 +1,2 @@ +#Generated by NetworkManager +hostname="gentoo" diff --git a/system-settings/plugins/ifnet/tests/net b/system-settings/plugins/ifnet/tests/net new file mode 100644 index 0000000000..e755000238 --- /dev/null +++ b/system-settings/plugins/ifnet/tests/net @@ -0,0 +1,147 @@ +# This blank configuration will automatically use DHCP for any net.* +# scripts in /etc/init.d. To create a more complete configuration, +# please review /etc/conf.d/net.example and save your configuration +# in /etc/conf.d/net (this file :]!). + +config_eth0=( +"202.117.16.121 netmask 255.255.255.0 brd 202.117.16.255" +"192.168.4.121/24" +"dhcp6" +) +routes_eth0=( "default via 202.117.16.1" + "192.168.4.0/24 via 192.168.4.1") +dns_servers_eth0="202.117.0.20 202.117.0.21" +dns_search_eth0="p12.edu.cn p13.edu.cn" + +config_eth1=( + "dhcp" +) +enable_ipv6_eth1="true" +routes_eth1=( "default via 202.117.16.1" ) +dns_servers_eth1="202.117.0.20 202.117.0.21" +config_eth2=( +"202.117.16.1211 netmask 255.255.255.0 brd 202.117.16.255" +"192.168.4.121/24" +"4321:0:1:2:3:4:567:89ab/64" +) +routes_eth2=("default via 4321:0:1:2:3:4:567:89ab") +enable_ipv6_eth2="true" +config_eth3=("nufjlsjlll") +managed_eth4=("false") +routes_eth4=("default via 4321:0:1:2:3:4:567:89ab") +config_eth5=("dhcp") +config_eth6=("192.168.4.{1..101}/24") + +config_eth7=( "dhcp" ) +auto_eth7="true" + + +config_myxjtu2=("202.117.16.121/24 brd 202.117.16.255") +routes_myxjtu2=("default via 202.117.16.1") +dns_servers_myxjtu2="202.117.0.20 202.117.0.21" +#key_myxjtu2="[1] s:xjtud key [1] enc restricted" +#key_eth6="[1] aaaa-4444-3d [2] s:xjtudlc key [1] enc open" + + +username_ppp0='user' +password_ppp0='password' + +config_qiaomuf=("dhcp") + +config_1xtest=("dhcp") + +config_0xab3ace=("dhcp") + +modules=( "iproute2" ) + config_kvm0=( "null" ) + config_kvm1=( "null" ) + + tuntap_kvm0="tap" + tuntap_kvm1="tap" + tunctl_kvm0="-u user" + tunctl_kvm1="-u user" + +bridge_br0="eth0 kvm0 kvm1" +config_br0=( "192.168.1.10/24" ) + brctl_br0=( "setfd 0") + dhcp_eth1="nosendhost nontp -I" + +predown() { + # The default in the script is to test for NFS root and disallow + # downing interfaces in that case. Note that if you specify a + # predown() function you will override that logic. Here it is, in + # case you still want it... + if is_net_fs /; then + eerror "root filesystem is network mounted -- can't stop ${IFACE}" + return 1 + fi + + # Remember to return 0 on success + return 0 +} + +postup() { + # This function could be used, for example, to register with a + # dynamic DNS service. Another possibility would be to + # send/receive mail once the interface is brought up. + + # Here is an example that allows the use of iproute rules + # which have been configured using the rules_eth0 variable. + #rules_eth0=" \ + # 'from 24.80.102.112/32 to 192.168.1.0/24 table localnet priority 100' \ + # 'from 216.113.223.51/32 to 192.168.1.0/24 table localnet priority 100' \ + #" + eval set -- \$rules_${IFVAR} + if [ $# != 0 ]; then + einfo "Adding IP policy routing rules" + eindent + # Ensure that the kernel supports policy routing + if ! ip rule list | grep -q "^"; then + eerror "You need to enable IP Policy Routing (CONFIG_IP_MULTIPLE_TABLES)" + eerror "in your kernel to use ip rules" + else + for x; do + ebegin "${x}" + ip rule add ${x} + eend $? + done + fi + eoutdent + # Flush the cache + ip route flush cache dev "${IFACE}" + fi + +} + +postdown() { + # Enable Wake-On-LAN for every interface except for lo + # Probably a good idea to set ifdown="no" in /etc/conf.d/net + # as well ;) + [ "${IFACE}" != "lo" ] && ethtool -s "${IFACE}" wol g + + Automatically erase any ip rules created in the example postup above + if interface_exists "${IFACE}"; then + # Remove any rules for this interface + local rule + ip rule list | grep " iif ${IFACE}[ ]*" | { + while read rule; do + rule="${rule#*:}" + ip rule del ${rule} + done + } + # Flush the route cache + ip route flush cache dev "${IFACE}" + fi + + # Return 0 always + return 0 +} + +failup() { + # This function is mostly here for completeness... I haven't + # thought of anything nifty to do with it yet ;-) +} + +faildown() +{} + diff --git a/system-settings/plugins/ifnet/tests/net.all b/system-settings/plugins/ifnet/tests/net.all new file mode 100644 index 0000000000..a30a1b95e6 --- /dev/null +++ b/system-settings/plugins/ifnet/tests/net.all @@ -0,0 +1,864 @@ +############################################################################## +# QUICK-START +# +# The quickest start is if you want to use DHCP. +# In that case, everything should work out of the box, no configuration +# necessary, though the startup script will warn you that you haven't +# specified anything. + +# WARNING :- some examples have a mixture of IPv4 (ie 192.168.0.1) and IPv6 +# (ie 4321:0:1:2:3:4:567:89ab) internet addresses. They only work if you have +# the relevant kernel option enabled. So if you don't have an IPv6 enabled +# kernel then remove the IPv6 address from your config. + +# If you want to use a static address or use DHCP explicitly, jump +# down to the section labelled INTERFACE HANDLERS. +# +# If you want to do anything more fancy, you should take the time to +# read through the rest of this file. + +############################################################################## +# MODULES +# +# We now support modular networking scripts which means we can easily +# add support for new interface types and modules while keeping +# compatability with existing ones. +# +# Modules load by default if the package they need is installed. If +# you specify a module here that doesn't have it's package installed +# then you get an error stating which package you need to install. +# Ideally, you only use the modules setting when you have two or more +# packages installed that supply the same service. +# +# In other words, you probably should DO NOTHING HERE... + +# Prefer ifconfig over iproute2 +modules=( "ifconfig" ) + +# You can also specify other modules for an interface +# In this case we prefer udhcpc over dhcpcd +modules_eth0=( "udhcpc" ) + +# You can also specify which modules not to use - for example you may be +# using a supplicant or linux-wlan-ng to control wireless configuration but +# you still want to configure network settings per ESSID associated with. +modules=( "!iwconfig" "!wpa_supplicant" ) +# IMPORTANT: If you need the above, please disable modules in that order + + +############################################################################## +# INTERFACE HANDLERS +# +# We provide two interface handlers presently: ifconfig and iproute2. +# You need one of these to do any kind of network configuration. +# For ifconfig support, emerge sys-apps/net-tools +# For iproute2 support, emerge sys-apps/iproute2 + +# If you don't specify an interface then we prefer iproute2 if it's installed +# To prefer ifconfig over iproute2 +modules=( "ifconfig" ) + +# For a static configuration, use something like this +# (They all do exactly the same thing btw) +config_eth0=( "192.168.0.2/24" ) +config_eth0=( "192.168.0.2 netmask 255.255.255.0" ) + +# We can also specify a broadcast +config_eth0=( "192.168.0.2/24 brd 192.168.0.255" ) +config_eth0=( "192.168.0.2 netmask 255.255.255.0 broadcast 192.168.0.255" ) + +# If you need more than one address, you can use something like this +# NOTE: ifconfig creates an aliased device for each extra IPv4 address +# (eth0:1, eth0:2, etc) +# iproute2 does not do this as there is no need to +config_eth0=( + "192.168.0.2/24" + "192.168.0.3/24" + "192.168.0.4/24" +) +# Or you can use sequence expressions +config_eth0=( "192.168.0.{2..4}/24" ) +# which does the same as above. Be careful though as if you use this and +# fallbacks, you have to ensure that both end up with the same number of +# values otherwise your fallback won't work correctly. + +# You can also use IPv6 addresses +# (you should always specify a prefix length with IPv6 here) +config_eth0=( + "192.168.0.2/24" + "4321:0:1:2:3:4:567:89ab/64" + "4321:0:1:2:3:4:567:89ac/64" +) + +# If you wish to keep existing addresses + routing and the interface is up, +# you can specify a noop (no operation). If the interface is down or there +# are no addresses assigned, then we move onto the next step (default dhcp) +# This is useful when configuring your interface with a kernel command line +# or similar +config_eth0=( "noop" "192.168.0.2/24" ) + +# If you don't want ANY address (only useful when calling for advanced stuff) +config_eth0=( "null" ) + +# Here's how to do routing if you need it +routes_eth0=( + "default via 192.168.0.1" # IPv4 default route + "10.0.0.0/8 via 192.168.0.1" # IPv4 subnet route + "::/0" # IPv6 unicast +) + +# If a specified module fails (like dhcp - see below), you can specify a +# fallback like so +fallback_eth0=( "192.168.0.2 netmask 255.255.255.0" ) +fallback_route_eth0=( "default via 192.168.0.1" ) + +# NOTE: fallback entry must match the entry location in config_eth0 +# As such you can only have one fallback route. + +# Some users may need to alter the MTU - here's how +mtu_eth0="1500" + +# Each module described below can set a default base metric, lower is +# preferred over higher. This is so we can prefer a wired route over a +# wireless route automaticaly. You can override this by setting +metric_eth0="100" +# or on a global basis +metric="100" +# The only downside of the global setting is that you have to ensure that +# there are no conflicting routes yourself. For users with large routing +# tables you may have to set a global metric as the due to a simple read of +# the routing table taking over a minute at a time. + +############################################################################## +# OPTIONAL MODULES + +# INTERFACE RENAMING +# There is no consistent device renaming scheme for Linux. +# The preferred way of naming devices is via the kernel module directly or +# by using udev (http://www.reactivated.net/udevrules.php) + +# If you are unable to write udev rules, then we do provide a way of renaming +# the interface based on it's MAC address, but it is not optimal. +# Here is how to rename an interface whose MAC address is 00:11:22:33:44:55 +# to foo1 +rename_001122334455="foo1" + +# You can also do this based on current device name - although this is not +# recommended. Here we rename eth1 to foo2. +rename_eth1="foo2" + +#----------------------------------------------------------------------------- +# WIRELESS (802.11 support) +# Wireless can be provided by iwconfig or wpa_supplicant + +# iwconfig +# emerge net-wireless/wireless-tools +# Wireless options are held in /etc/conf.d/wireless - but could be here too +# Consult the sample file /etc/conf.d/wireless.example for instructions +# iwconfig is the default + +# wpa_supplicant +# emerge net-wireless/wpa-supplicant +# Wireless options are held in /etc/wpa_supplicant.conf +# Consult the sample file /etc/wpa_supplicant.conf.example for instructions +# To choose wpa_supplicant over iwconfig +modules=( "wpa_supplicant" ) +# To configure wpa_supplicant +wpa_supplicant_eth0="-Dwext" # For generic wireless +wpa_supplicant_ath0="-Dmadwifi" # For Atheros based cards +# Consult wpa_supplicant for more drivers +# By default don't wait for wpa_suppliant to associate and authenticate. +# If you would like to, so can specify how long in seconds +associate_timeout_eth0=60 +# A value of 0 means wait forever. + +# GENERIC WIRELESS OPTIONS +# PLEASE READ THE INSTRUCTIONS IN /etc/conf.d/wireless.example FOR +# HOW TO USE THIS ESSID VARIABLE +# You can also override any settings found here per ESSID - which is very +# handy if you use different networks a lot +config_ESSID=( "dhcp" ) +dhcpcd_ESSID="-t 5" + +# Setting name/domain server causes /etc/resolv.conf to be overwritten +# Note that if DHCP is used, and you want this to take precedence then + set dhcp_ESSID="nodns" +dns_servers_ESSID=( "192.168.0.1" "192.168.0.2" ) +dns_domain_ESSID="some.domain" +dns_search_ESSID="search.this.domain search.that.domain" +# Please check the man page for resolv.conf for more information +# as domain and search are mutually exclusive. + +# You can also override any settings found here per MAC address of the AP +# in case you use Access Points with the same ESSID but need different +# networking configs. Below is an example - of course you use the same +# method with other variables +mac_config_001122334455=( "dhcp" ) +mac_dhcpcd_001122334455="-t 10" +mac_dns_servers_001122334455=( "192.168.0.1" "192.168.0.2" ) + +# When an interface has been associated with an Access Point, a global +# variable called ESSID is set to the Access Point's ESSID for use in the +# pre/post user functions below (although it's not available in preup as you +# won't have associated then) + +# If you're using anything else to configure wireless on your interface AND +# you have installed any of the above packages, you need to disable them +modules=( "!iwconfig" "!wpa_supplicant" ) + +#----------------------------------------------------------------------------- +# DHCP +# DHCP can be provided by dhclient, dhcpcd, pump or udhcpc. +# +# dhclient: emerge net-misc/dhcp +# dhcpcd: emerge net-misc/dhcpcd +# pump: emerge net-misc/pump +# udhcpc: emerge net-misc/udhcp + +# If you have more than one DHCP client installed, you need to specify which +# one to use - otherwise we default to dhcpcd if available. +modules=( "dhclient" ) # to select dhclient over dhcpcd +# +# Notes: +# - All clients send the current hostname to the DHCP server by default +# - dhcpcd does not daemonize when the lease time is infinite +# - udhcp-0.9.3-r3 and earlier do not support getting NTP servers +# - pump does not support getting NIS servers +# - DHCP tends to erase any existing device information - so add +# static addresses after dhcp if you need them +# - dhclient and udhcpc can set other resolv.conf options such as "option" +# and "sortlist"- see the System module for more details + +# Regardless of which DHCP client you prefer, you configure them the +# same way using one of following depending on which interface modules +# you're using. +config_eth0=( "dhcp" ) + +# For passing custom options to dhcpcd use something like the following. This +# example reduces the timeout for retrieving an address from 60 seconds (the +# default) to 10 seconds. +dhcpcd_eth0="-t 10" + +# dhclient, udhcpc and pump don't have many runtime options +# You can pass options to them in a similar manner to dhcpcd though +dhclient_eth0="..." +udhcpc_eth0="..." +pump_eth0="..." + +# GENERIC DHCP OPTIONS +# Set generic DHCP options like so +dhcp_eth0="release nodns nontp nonis nogateway nosendhost" + +# This tells the dhcp client to release it's lease when it stops, not to +# overwrite dns, ntp and nis settings, not to set a default route and not to +# send the current hostname to the dhcp server and when it starts. +# You can use any combination of the above options - the default is not to +# use any of them. + +#----------------------------------------------------------------------------- +# For APIPA support, emerge net-misc/iputils or net-analyzer/arping + +# APIPA is a module that tries to find a free address in the range +# 169.254.0.0-169.254.255.255 by arping a random address in that range on the +# interface. If no reply is found then we assign that address to the interface + +# This is only useful for LANs where there is no DHCP server and you don't +# connect directly to the internet. +config_eth0=( "dhcp" ) +fallback_eth0=( "apipa" ) + +#----------------------------------------------------------------------------- +# ARPING Gateway configuration +# and +# Automatic Private IP Addressing (APIPA) +# For arpingnet / apipa support, emerge net-misc/iputils or net-analyzer/arping +# +# This is a module that tries to find a gateway IP. If it exists then we use +# that gateways configuration for our own. For the configuration variables +# simply ensure that each octet is zero padded and the dots are removed. +# Below is an example. +# +gateways_eth0="192.168.0.1 10.0.0.1" +config_192168000001=( "192.168.0.2/24" ) +routes_192168000001=( "default via 192.168.0.1" ) +dns_servers_192168000001=( "192.168.0.1" ) +config_010000000001=( "10.0.0.254/8" ) +routes_010000000001=( "default via 10.0.0.1" ) +dns_servers_010000000001=( "10.0.0.1" ) + +# We can also specify a specific MAC address for each gateway if different +# networks have the same gateway. +gateways_eth0="192.168.0.1,00:11:22:AA:BB:CC 10.0.0.1,33:44:55:DD:EE:FF" +config_192168000001_001122AABBCC=( "192.168.0.2/24" ) +routes_192168000001_001122AABBCC=( "default via 192.168.0.1" ) +dns_servers_192168000001_001122AABBCC=( "192.168.0.1" ) +config_010000000001_334455DDEEFF=( "10.0.0.254/8" ) +routes_010000000001_334455DDEEFF=( "default via 10.0.0.1" ) +dns_servers_010000000001_334455DDEEFF=( "10.0.0.1" ) + +# If we don't find any gateways (or there are none configured) then we try and +# use APIPA to find a free address in the range 169.254.0.0-169.254.255.255 +# by arping a random address in that range on the interface. If no reply is +# found then we assign that address to the interface. + +# This is only useful for LANs where there is no DHCP server. +config_eth0=( "arping" ) + +# or if no DHCP server can be found +config_eth0=( "dhcp" ) +fallback_eth0=( "arping" ) + +# NOTE: We default to sleeping for 1 second the first time we attempt an +# arping to give the interface time to settle on the LAN. This appears to +# be a good default for most instances, but if not you can alter it here. +arping_sleep=5 +arping_sleep_lan=7 + +# NOTE: We default to waiting 3 seconds to get an arping response. You can +# change the default wait like so. +arping_wait=3 +arping_wait_lan=2 + +#----------------------------------------------------------------------------- +# VLAN (802.1q support) +# For VLAN support, emerge net-misc/vconfig + +# Specify the VLAN numbers for the interface like so +# Please ensure your VLAN IDs are NOT zero-padded +vlans_eth0="1 2" + +# You may not want to assign an IP the the physical interface, but we still +# need it up. +config_eth0=( "null" ) + +# You can also configure the VLAN - see for vconfig man page for more details +vconfig_eth0=( "set_name_type VLAN_PLUS_VID_NO_PAD" ) +vconfig_vlan1=( "set_flag 1" "set_egress_map 2 6" ) +config_vlan1=( "172.16.3.1 netmask 255.255.254.0" ) +config_vlan2=( "172.16.2.1 netmask 255.255.254.0" ) + +# NOTE: Vlans can be configured with a . in their interface names +# When configuring vlans with this name type, you need to replace . with a _ +config_eth0.1=( "dhcp" ) - does not work +config_eth0_1=( "dhcp" ) - does work + +# NOTE: Vlans are controlled by their physical interface and not per vlan +# This means you do not need to create init scripts in /etc/init.d for each +# vlan, you must need to create one for the physical interface. +# If you wish to control the configuration of each vlan through a separate +# script, or wish to rename the vlan interface to something that vconfig +# cannot then you need to do this. +vlan_start_eth0="no" + +# If you do the above then you may want to depend on eth0 like so + RC_NEED_vlan1="net.eth0" +# NOTE: depend functions only work in /etc/conf.d/net +# and not in profile configs such as /etc/conf.d/net.foo + +#----------------------------------------------------------------------------- +# Bonding +# For link bonding/trunking emerge net-misc/ifenslave + +# To bond interfaces together +slaves_bond0="eth0 eth1 eth2" +config_bond0=( "null" ) # You may not want to assign an IP the the bond + +# If any of the slaves require extra configuration - for example wireless or +# ppp devices - we need to depend function on the bonded interfaces +RC_NEED_bond0="net.eth0 net.eth1" + + +#----------------------------------------------------------------------------- +# Classical IP over ATM +# For CLIP support emerge net-dialup/linux-atm + +# Ensure that you have /etc/atmsigd.conf setup correctly +# Now setup each clip interface like so +clip_atm0=( "peer_ip [if.]vpi.vci [opts]" ... ) +# where "peer_ip" is the IP address of a PVC peer (in case of an ATM connection +# with your ISP, your only peer is usually the ISP gateway closest to you), +# "if" is the number of the ATM interface which will carry the PVC, "vpi.vci" +# is the ATM VC address, and "opts" may optionally specify VC parameters like +# qos, pcr, and the like (see "atmarp -s" for further reference). Please also +# note quoting: it is meant to distinguish the VCs you want to create. You may, +# in example, create an atm0 interface to more peers, like this: +clip_atm0=( "1.1.1.254 0.8.35" "1.1.1.253 1.8.35" ) + +# By default, the PVC will use the LLC/SNAP encapsulation. If you rather need a +# null encapsulation (aka "VC mode"), please add the keyword "null" to opts. + + +#----------------------------------------------------------------------------- +# PPP +# For PPP support, emerge net-dialup/ppp +# PPP is used for most dialup connections, including ADSL. +# The older ADSL module is documented below, but you are encouraged to try +# this module first. +# +# You need to create the PPP net script yourself. Make it like so +#ln -s net.lo /etc/init.d/net.ppp0 +# +# We have to instruct ppp0 to actually use ppp +config_ppp0=( "ppp" ) +# +# Each PPP interface requires an interface to use as a "Link" +link_ppp0="/dev/ttyS0" # Most PPP links will use a serial port +link_ppp0="eth0" # PPPoE requires an ethernet interface +link_ppp0="[itf.]vpi.vci" # PPPoA requires the ATM VC's address +link_ppp0="/dev/null" # ISDN links should have this +link_ppp0="pty 'your_link_command'" # PPP links over ssh, rsh, etc +# +# Here you should specify what pppd plugins you want to use +# Available plugins are: pppoe, pppoa, capi, dhcpc, minconn, radius, +# radattr, radrealms and winbind +plugins_ppp0=( + "pppoe" # Required plugin for PPPoE + "pppoa vc-encaps" # Required plugin for PPPoA with an option + "capi" # Required plugin for ISDN +) +# +# PPP requires at least a username. You can optionally set a password here too +# If you don't, then it will use the password specified in /etc/ppp/*-secrets +# against the specified username +username_ppp0='user' +password_ppp0='password' +# NOTE: You can set a blank password like so +password_ppp0= +# +# The PPP daemon has many options you can specify - although there are many +# and may seem daunting, it is recommended that you read the pppd man page +# before enabling any of them +pppd_ppp0=( + "maxfail 0" # WARNING: It's not recommended you use this + # if you don't specify maxfail then we assume 0 + "updetach" # If not set, "/etc/init.d/net.ppp0 start" will return + # immediately, without waiting the link to come up + # for the first time. + # Do not use it for dial-on-demand links! + "debug" # Enables syslog debugging + "noauth" # Do not require the peer to authenticate itself + "defaultroute" # Make this PPP interface the default route + "usepeerdns" # Use the DNS settings provided by PPP + +# On demand options + "demand" # Enable dial on demand + "idle 30" # Link goes down after 30 seconds of inactivity + "10.112.112.112:10.112.112.113" # Phony IP addresses + "ipcp-accept-remote" # Accept the peers idea of remote address + "ipcp-accept-local" # Accept the peers idea of local address + "holdoff 3" # Wait 3 seconds after link dies before re-starting + +# Dead peer detection + "lcp-echo-interval 15" # Send a LCP echo every 15 seconds + "lcp-echo-failure 3" # Make peer dead after 3 consective + # echo-requests + +# Compression options - use these to completely disable compression +# noaccomp noccp nobsdcomp nodeflate nopcomp novj novjccomp + +# Dial-up settings + "lock" # Lock serial port + "115200" # Set the serial port baud rate + "modem crtscts" # Enable hardware flow control + "192.168.0.1:192.168.0.2" # Local and remote IP addresses +) +# +# Dial-up PPP users need to specify at least one telephone number +phone_number_ppp0=( "12345689" ) # Maximum 2 phone numbers are supported +# They will also need a chat script - here's a good one +chat_ppp0=( +# 'ABORT' 'BUSY' +# 'ABORT' 'ERROR' +# 'ABORT' 'NO ANSWER' +# 'ABORT' 'NO CARRIER' +# 'ABORT' 'NO DIALTONE' +# 'ABORT' 'Invalid Login' +# 'ABORT' 'Login incorrect' +# 'TIMEOUT' '5' +# '' 'ATZ' +# 'OK' 'AT' # Put your modem initialization string here +# 'OK' 'ATDT\T' +# 'TIMEOUT' '60' +# 'CONNECT' '' +# 'TIMEOUT' '5' +# '~--' '' +) + +# If the link require extra configuration - for example wireless or +# RFC 268 bridge - we need to depend on the bridge so they get +# configured correctly. +RC_NEED_ppp0="net.nas0" + +#WARNING: if MTU of the PPP interface is less than 1500 and you use this +#machine as a router, you should add the following rule to your firewall +# +#iptables -I FORWARD 1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + +#----------------------------------------------------------------------------- +# ADSL +# For ADSL support, emerge net-dialup/rp-pppoe +# WARNING: This ADSL module is being deprecated in favour of the PPP module +# above. +# You should make the following settings and also put your +# username/password information in /etc/ppp/pap-secrets + +# Configure the interface to use ADSL +config_eth0=( "adsl" ) + +# You probably won't need to edit /etc/ppp/pppoe.conf if you set this +adsl_user_eth0="my-adsl-username" + +#----------------------------------------------------------------------------- +# ISDN +# For ISDN support, emerge net-dialup/isdn4k-utils +# You should make the following settings and also put your +# username/password information in /etc/ppp/pap-secrets + +# Configure the interface to use ISDN +config_ippp0=( "dhcp" ) +# It's important to specify dhcp if you need it! +config_ippp0=( "192.168.0.1/24" ) +# Otherwise, you can use a static IP + +# NOTE: The interface name must be either ippp or isdn followed by a number + +# You may need this option to set the default route +ipppd_eth0="defaultroute" + +#----------------------------------------------------------------------------- +# MAC changer +# To set a specific MAC address +mac_eth0="00:11:22:33:44:55" + +# For changing MAC addresses using the below, emerge net-analyzer/macchanger +# - to randomize the last 3 bytes only +mac_eth0="random-ending" +# - to randomize between the same physical type of connection (e.g. fibre, +# copper, wireless) , all vendors +mac_eth0="random-samekind" +# - to randomize between any physical type of connection (e.g. fibre, copper, +# wireless) , all vendors +mac_eth0="random-anykind" +# - full randomization - WARNING: some MAC addresses generated by this may NOT +# act as expected +mac_eth0="random-full" +# custom - passes all parameters directly to net-analyzer/macchanger +mac_eth0="some custom set of parameters" + +# You can also set other options based on the MAC address of your network card +# Handy if you use different docking stations with laptops +config_001122334455=( "dhcp" ) + +#----------------------------------------------------------------------------- +# TUN/TAP +# For TUN/TAP support emerge net-misc/openvpn or sys-apps/usermode-utilities +# +# You must specify if we're a tun or tap device. Then you can give it any +# name you like - such as vpn +tuntap_vpn="tun" +config_vpn=( "192.168.0.1/24") + +# Or stick wit the generic names - like tap0 +tuntap_tap0="tap" +config_tap0=( "192.168.0.1/24") + +# For passing custom options to tunctl use something like the following. This +# example sets the owner to adm +tunctl_tun1="-u adm" +# When using openvpn, there are no options + +#----------------------------------------------------------------------------- +# Bridging (802.1d) +# For bridging support emerge net-misc/bridge-utils + +# To add ports to bridge br0 +bridge_br0="eth0 eth1" +# or dynamically add them when the interface comes up +bridge_add_eth0="br0" +bridge_add_eth1="br0" + +# You need to configure the ports to null values so dhcp does not get started +config_eth0=( "null" ) +config_eth1=( "null" ) + +# Finally give the bridge an address - dhcp or a static IP +config_br0=( "dhcp" ) # may not work when adding ports dynamically +config_br0=( "192.168.0.1/24" ) + +# If any of the ports require extra configuration - for example wireless or +# ppp devices - we need to depend on them like so. +RC_NEED_br0="net.eth0 net.eth1" + +# Below is an example of configuring the bridge +# Consult "man brctl" for more details +brctl_br0=( "setfd 0" "sethello 0" "stp off" ) + +#----------------------------------------------------------------------------- +# RFC 2684 Bridge Support +# For RFC 2684 bridge support emerge net-misc/br2684ctl + +# Interface names have to be of the form nas0, nas1, nas2, etc. +# You have to specify a VPI and VCI for the interface like so +br2684ctl_nas0="-a 0.38" # UK VPI and VCI + +# You may want to configure the encapsulation method as well by adding the -e +# option to the command above (may need to be before the -a command) +# -e 0 # LLC (default) +# -e 1 # VC mux + +# Then you can configure the interface as normal +config_nas0=( "192.168.0.1/24" ) + +#----------------------------------------------------------------------------- +# Tunnelling +# WARNING: For tunnelling it is highly recommended that you +# emerge sys-apps/iproute2 +# +# For GRE tunnels +iptunnel_vpn0="mode gre remote 207.170.82.1 key 0xffffffff ttl 255" + +# For IPIP tunnels +iptunnel_vpn0="mode ipip remote 207.170.82.2 ttl 255" + +# To configure the interface +config_vpn0=( "192.168.0.2 pointopoint 192.168.1.2" ) # ifconfig style +config_vpn0=( "192.168.0.2 peer 192.168.1.1" ) # iproute2 style + +# 6to4 Tunnels allow IPv6 to work over IPv4 addresses, provided you +# have a non-private address configured on an interface. + link_6to4="eth0" # Interface to base it's addresses on + config_6to4=( "ip6to4" ) +# You may want to depend on eth0 like so +RC_NEED_6to4="net.eth0" +# To ensure that eth0 is configured before 6to4. Of course, the tunnel could be +# any name and this also works for any configured interface. +# NOTE: If you're not using iproute2 then your 6to4 tunnel has to be called +# sit0 - otherwise use a different name like 6to4 in the example above. + + +#----------------------------------------------------------------------------- +# System +# For configuring system specifics such as domain, dns, ntp and nis servers +# It's rare that you would need todo this, but you can anyway. +# This is most benefit to wireless users who don't use DHCP so they can change +# their configs based on ESSID. See wireless.example for more details + +# To use dns settings such as these, dns_servers_eth0 must be set! +# If you omit the _eth0 suffix, then it applies to all interfaces unless +# overridden by the interface suffix. +dns_domain_eth0="your.domain" +dns_servers_eth0="192.168.0.2 192.168.0.3" +dns_search_eth0="this.domain that.domain" +dns_options_eth0=( "timeout 1" "rotate" ) +dns_sortlist_eth0="130.155.160.0/255.255.240.0 130.155.0.0" +# See the man page for resolv.conf for details about the options and sortlist +# directives + +ntp_servers_eth0="192.168.0.2 192.168.0.3" + +nis_domain_eth0="domain" +nis_servers_eth0="192.168.0.2 192.168.0.3" + +# NOTE: Setting any of these will stamp on the files in question. So if you +# don't specify dns_servers but you do specify dns_domain then no nameservers +# will be listed in /etc/resolv.conf even if there were any there to start +# with. +# If this is an issue for you then maybe you should look into a resolv.conf +# manager like resolvconf-gentoo to manage this file for you. All packages +# that baselayout supports use resolvconf-gentoo if installed. + +#----------------------------------------------------------------------------- +# Cable in/out detection +# Sometimes the cable is in, others it's out. Obviously you don't want to +# restart net.eth0 every time when you plug it in either. +# +# netplug is a package that detects this and requires no extra configuration +# on your part. +# emerge sys-apps/netplug +# or +# emerge sys-apps/ifplugd +# and you're done :) + +# By default we don't wait for netplug/ifplugd to configure the interface. +# If you would like it to wait so that other services now that network is up +# then you can specify a timeout here. +plug_timeout="10" +# A value of 0 means wait forever. + +# If you don't want to use netplug on a specific interface but you have it +# installed, you can disable it for that interface via the modules statement +modules_eth0=( "!netplug" ) +# You can do the same for ifplugd +# +# You can disable them both with the generic plug +modules_eth0=( "!plug" ) + +# To use specific ifplugd options, fex specifying wireless mode +ifplugd_eth0="--api-mode=wlan" +# man ifplugd for more options + +############################################################################## +# ADVANCED CONFIGURATION +# +# Four functions can be defined which will be called surrounding the +# start/stop operations. The functions are called with the interface +# name first so that one function can control multiple adapters. An extra two +# functions can be defined when an interface fails to start or stop. +# +# The return values for the preup and predown functions should be 0 +# (success) to indicate that configuration or deconfiguration of the +# interface can continue. If preup returns a non-zero value, then +# interface configuration will be aborted. If predown returns a +# non-zero value, then the interface will not be allowed to continue +# deconfiguration. +# +# The return values for the postup, postdown, failup and faildown functions are +# ignored since there's nothing to do if they indicate failure. +# +# ${IFACE} is set to the interface being brought up/down +# ${IFVAR} is ${IFACE} converted to variable name bash allows + +#preup() { +# # Test for link on the interface prior to bringing it up. This +# # only works on some network adapters and requires the mii-diag +# # package to be installed. +# if mii-tool "${IFACE}" 2> /dev/null | grep -q 'no link'; then +# ewarn "No link on ${IFACE}, aborting configuration" +# return 1 +# fi +# +# # Test for link on the interface prior to bringing it up. This +# # only works on some network adapters and requires the ethtool +# # package to be installed. +# if ethtool "${IFACE}" | grep -q 'Link detected: no'; then +# ewarn "No link on ${IFACE}, aborting configuration" +# return 1 +# fi +# +# +# # Remember to return 0 on success +# return 0 +#} + +#predown() { +# # The default in the script is to test for NFS root and disallow +# # downing interfaces in that case. Note that if you specify a +# # predown() function you will override that logic. Here it is, in +# # case you still want it... +# if is_net_fs /; then +# eerror "root filesystem is network mounted -- can't stop ${IFACE}" +# return 1 +# fi +# +# # Remember to return 0 on success +# return 0 +#} + +#postup() { +# # This function could be used, for example, to register with a +# # dynamic DNS service. Another possibility would be to +# # send/receive mail once the interface is brought up. + +# # Here is an example that allows the use of iproute rules +# # which have been configured using the rules_eth0 variable. +# #rules_eth0=( +# # "from 24.80.102.112/32 to 192.168.1.0/24 table localnet priority 100" +# # "from 216.113.223.51/32 to 192.168.1.0/24 table localnet priority 100" +# #) +# local x="rules_${IFVAR}[@]" +# local -a rules=( "${!x}" ) +# if [[ -n ${rules} ]] ; then +# einfo "Adding IP policy routing rules" +# eindent +# # Ensure that the kernel supports policy routing +# if ! ip rule list | grep -q "^" ; then +# eerror "You need to enable IP Policy Routing (CONFIG_IP_MULTIPLE_TABLES)" +# eerror "in your kernel to use ip rules" +# else +# for x in "${rules[@]}" ; do +# ebegin "${x}" +# ip rule add ${x} dev "${IFACE}" +# eend $? +# done +# fi +# eoutdent +# # Flush the cache +# ip route flush cache dev "${IFACE}" +# fi + +#} + +#postdown() { +# # Enable Wake-On-LAN for every interface except for lo +# # Probably a good idea to set RC_DOWN_INTERFACE="no" in /etc/conf.d/rc +# # as well ;) +# [[ ${IFACE} != "lo" ]] && ethtool -s "${IFACE}" wol g + +# Automatically erase any ip rules created in the example postup above +# if interface_exists "${IFACE}" ; then +# # Remove any rules for this interface +# local rule +# ip rule list | grep " iif ${IFACE}[ ]*" | { +# while read rule ; do +# rule="${rule#*:}" +# ip rule del ${rule} +# done +# } +# # Flush the route cache +# ip route flush cache dev "${IFACE}" +# fi + +# # Return 0 always +# return 0 +#} + +#failup() { +# # This function is mostly here for completeness... I haven't +# # thought of anything nifty to do with it yet ;-) +#} + +#faildown() { +# # This function is mostly here for completeness... I haven't +# # thought of anything nifty to do with it yet ;-) +#} + +############################################################################## +# FORCING MODULES +# The Big Fat Warning :- If you use module forcing do not complain to us or +# file bugs about it not working! +# +# Loading modules is a slow affair - we have to check each one for the following +# 1) Code sanity +# 2) Has the required package been emerged? +# 3) Has it modified anything? +# 4) Have all the dependant modules been loaded? + +# Then we have to strip out the conflicting modules based on user preference +# and default configuration and sort them into the correct order. +# Finally we check the end result for dependencies. + +# This, of course, takes valuable CPU time so we provide module forcing as a +# means to speed things up. We still do *some* checking but not much. + +# It is essential that you force modules in the correct order and supply all +# the modules you need. You must always supply an interface module - we +# supply ifconfig or iproute2. + +# The Big Fat Warning :- If you use module forcing do not complain to us or +# file bugs about it not working! + +# Now that we've warned you twice, here's how to do it +modules_force=( "ifconfig" ) +modules_force=( "iproute2" "dhcpcd" ) + +# We can also apply this to a specific interface +modules_force_eth1=( "iproute2" ) + +# The below will not work +modules_force=( "dhcpcd" ) +# No interface (ifconfig/iproute2) +modules_force=( "ifconfig" "essidnet" "iwconfig" ) +# Although it will not crash, essidnet will not work as it has to come after +# iwconfig +modules_force=( "iproute2" "ifconfig" ) +# The interface will be setup twice which will cause problems diff --git a/system-settings/plugins/ifnet/tests/nm-system-settings.conf b/system-settings/plugins/ifnet/tests/nm-system-settings.conf new file mode 100644 index 0000000000..39bc87b8be --- /dev/null +++ b/system-settings/plugins/ifnet/tests/nm-system-settings.conf @@ -0,0 +1,5 @@ +[main] +plugins=ifnet,keyfile + +[ifnet] +managed=false diff --git a/system-settings/plugins/ifnet/tests/test_all.c b/system-settings/plugins/ifnet/tests/test_all.c new file mode 100644 index 0000000000..ba98397616 --- /dev/null +++ b/system-settings/plugins/ifnet/tests/test_all.c @@ -0,0 +1,379 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service (ifnet) + * + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <stdio.h> +#include <string.h> +#include <glib.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <nm-utils.h> + +#include "net_parser.h" +#include "nm-test-helpers.h" +#include "net_utils.h" +#include "wpa_parser.h" +#include "connection_parser.h" + +static void +test_getdata () +{ + ASSERT (ifnet_get_data ("eth1", "config") + && strcmp (ifnet_get_data ("eth1", "config"), "dhcp") == 0, + "get data", "config_eth1 is not correct"); + ASSERT (ifnet_get_data ("ppp0", "username") + && strcmp (ifnet_get_data ("ppp0", "username"), "user") == 0, + "get data", "config_ppp0 username is not correctly read"); + ASSERT (ifnet_get_data ("ppp0", "password") + && strcmp (ifnet_get_data ("ppp0", "password"), + "password") == 0, "get data", + "config_ppp0 password is not correctly read"); +} + +static void +test_read_hostname () +{ + gchar *hostname = read_hostname ("hostname"); + + ASSERT (hostname != NULL, "get hostname", "hostname is NULL"); + ASSERT (strcmp ("gentoo", hostname) == 0, + "get hostname", + "hostname is not correctly read, read:%s, expected: gentoo", + hostname); +} + +static void +test_write_hostname () +{ + gchar *hostname = read_hostname ("hostname"); + + write_hostname ("gentoo-nm", "hostname"); + ASSERT (strcmp (read_hostname ("hostname"), "gentoo-nm") == 0, + "write hostname", "write hostname error"); + write_hostname (hostname, "hostname"); +} + +static void +test_is_static () +{ + ASSERT (is_static_ip4 ("eth1") == FALSE, "is static", + "a dhcp interface is recognized as static"); + ASSERT (is_static_ip4 ("eth0") == TRUE, "is static", + "a static interface is recognized as dhcp"); + ASSERT (!is_static_ip6 ("eth0") == TRUE, "is static", + "a static interface is recognized as dhcp"); +} + +static void +test_has_default_route () +{ + ASSERT (has_default_ip4_route ("eth0"), "has default route", + "eth0 should have a default ipv4 route"); + ASSERT (has_default_ip6_route ("eth4"), "has default route", + "eth4 should have a default ipv6 route"); + + ASSERT (!has_default_ip4_route ("eth5") + && !has_default_ip6_route ("eth5"), "has default route", + "eth5 shouldn't have a default route"); +} + +static void +test_has_ip6_address () +{ + ASSERT (has_ip6_address ("eth2"), "has ip6 address", + "eth2 should have a ipv6 address"); + ASSERT (!has_ip6_address ("eth0"), "has ip6 address", + "eth0 shouldn't have a ipv6 address") + +} + +static void +test_is_ip4_address () +{ + gchar *address1 = "192.168.4.232/24"; + gchar *address2 = "192.168.100.{1..254}/24"; + gchar *address3 = "192.168.4.2555/24"; + + ASSERT (is_ip4_address (address1), "is ip4 address", + "%s should be a valid address", address1); + ASSERT (is_ip4_address (address2), "is ip4 address", + "%s should be a valid address", address2); + ASSERT (!is_ip4_address (address3), "is ip4 address", + "%s should be an invalid address", address3); +} + +static void +test_is_ip6_address () +{ + gchar *address1 = "4321:0:1:2:3:4:567:89ac/24"; + + ASSERT (is_ip6_address (address1), "is ip6 address", + "%s should be a valid address", address1); +} + +static void +check_ip_block (ip_block * iblock, gchar * ip, gchar * netmask, gchar * gateway) +{ + char *str; + struct in_addr tmp_ip4_addr; + + str = malloc (INET_ADDRSTRLEN); + tmp_ip4_addr.s_addr = iblock->ip; + inet_ntop (AF_INET, &tmp_ip4_addr, str, INET_ADDRSTRLEN); + ASSERT (strcmp (ip, str) == 0, "check ip", "ip expected:%s, find:%s", + ip, str); + tmp_ip4_addr.s_addr = iblock->netmask; + inet_ntop (AF_INET, &tmp_ip4_addr, str, INET_ADDRSTRLEN); + ASSERT (strcmp (netmask, str) == 0, "check netmask", + "netmask expected:%s, find:%s", netmask, str); + tmp_ip4_addr.s_addr = iblock->gateway; + inet_ntop (AF_INET, &tmp_ip4_addr, str, INET_ADDRSTRLEN); + ASSERT (strcmp (gateway, str) == 0, "check gateway", + "gateway expected:%s, find:%s", gateway, str); + free (str); +} + +static void +test_convert_ipv4_config_block () +{ + ip_block *iblock = convert_ip4_config_block ("eth0"); + ip_block *tmp = iblock; + + ASSERT (iblock != NULL, "convert ipv4 block", + "block eth0 should not be NULL"); + check_ip_block (iblock, "202.117.16.121", "255.255.255.0", + "202.117.16.1"); + iblock = iblock->next; + destroy_ip_block (tmp); + ASSERT (iblock != NULL, "convert ipv4 block", + "block eth0 should have a second IP address"); + check_ip_block (iblock, "192.168.4.121", "255.255.255.0", + "202.117.16.1"); + destroy_ip_block (iblock); + iblock = convert_ip4_config_block ("eth2"); + ASSERT (iblock != NULL + && iblock->next == NULL, "convert error IPv4 address", + "should only get one address"); + check_ip_block (iblock, "192.168.4.121", "255.255.255.0", "0.0.0.0"); + destroy_ip_block (iblock); + iblock = convert_ip4_config_block ("eth3"); + ASSERT (iblock == NULL, "convert config_block", + "convert error configuration"); + destroy_ip_block (iblock); + iblock = convert_ip4_config_block ("eth6"); + ASSERT (iblock != NULL, "convert config_block", + "convert error configuration"); + destroy_ip_block (iblock); + +} + +static void +test_convert_ipv4_routes_block () +{ + ip_block *iblock = convert_ip4_routes_block ("eth0"); + ip_block *tmp = iblock; + + ASSERT (iblock != NULL, "convert ip4 routes", "should get one route"); + check_ip_block (iblock, "192.168.4.0", "255.255.255.0", "192.168.4.1"); + iblock = iblock->next; + destroy_ip_block (tmp); + ASSERT (iblock == NULL, "convert ip4 routes", + "should only get one route"); +} + +static void +test_wpa_parser () +{ + gchar *value; + + ASSERT (exist_ssid ("example"), "get wsec", + "ssid myxjtu2 is not found"); + ASSERT (exist_ssid ("static-wep-test"), "exist_ssid", + "ssid static-wep-test is not found"); + value = wpa_get_value ("static-wep-test", "key_mgmt"); + ASSERT (value && strcmp (value, "NONE") == 0, "get wpa data", + "key_mgmt of static-wep-test should be NONE, find %s", value); + value = wpa_get_value ("static-wep-test", "wep_key0"); + ASSERT (value && strcmp (value, "\"abcde\"") == 0, "get wpa data", + "wep_key0 of static-wep-test should be abcde, find %s", value); + ASSERT (exist_ssid ("leap-example"), "get wsec", + "ssid leap-example is not found"); +} + +static void +test_strip_string () +{ + gchar *str = "( \"default via 202.117.16.1\" )"; + gchar *result = g_strdup (str); + gchar *result_b = result; + + result = strip_string (result, '('); + result = strip_string (result, ')'); + result = strip_string (result, '"'); + ASSERT (strcmp (result, "default via 202.117.16.1") == 0, + "strip_string", "string isn't stripped, result is: %s", result); + g_free (result_b); +} + +static void +test_is_unmanaged () +{ + ASSERT (is_managed ("eth0"), "test_is_unmanaged", + "eth0 should be managed"); + ASSERT (!is_managed ("eth4"), "test_is_unmanaged", + "eth4 should be unmanaged"); +} + +static void +test_new_connection () +{ + GError **error = NULL; + NMConnection *connection; + + connection = ifnet_update_connection_from_config_block ("eth2", error); + ASSERT (connection != NULL, "new connection", + "new connection failed: %s", + error == NULL ? "None" : (*error)->message); + g_object_unref (connection); + connection = + ifnet_update_connection_from_config_block ("qiaomuf", error); + ASSERT (connection != NULL, "new connection", + "new connection failed: %s", error + && (*error) ? (*error)->message : "NONE"); + g_object_unref (connection); + connection = + ifnet_update_connection_from_config_block ("myxjtu2", error); + ASSERT (connection != NULL, "new connection", + "new connection failed: %s", error + && (*error) ? (*error)->message : "NONE"); + g_object_unref (connection); + +} + +static void +test_update_connection () +{ + GError **error = NULL; + NMConnection *connection; + + connection = ifnet_update_connection_from_config_block ("eth0", error); + ASSERT (connection != NULL, "get connection", + "get connection failed: %s", + error == NULL ? "None" : (*error)->message); + ASSERT (ifnet_update_parsers_by_connection + (connection, "eth0", NULL, "net.generate", + "wpa_supplicant.conf.generate", error), "update connection", + "update connection failed %s", "eth0"); + connection = + ifnet_update_connection_from_config_block ("0xab3ace", error); + ASSERT (connection != NULL, "get connection", + "get connection failed: %s", + error == NULL ? "None" : (*error)->message); + ASSERT (ifnet_update_parsers_by_connection + (connection, "0xab3ace", NULL, "net.generate", + "wpa_supplicant.conf.generate", error), "update connection", + "update connection failed %s", "0xab3ace"); +} + +static void +test_add_connection () +{ + GError **error = NULL; + NMConnection *connection; + + connection = ifnet_update_connection_from_config_block ("eth0", error); + ASSERT (ifnet_add_new_connection + (connection, "net.generate", "wpa_supplicant.conf.generate", + error), "add connection", "add connection failed: %s", "eth0"); + connection = + ifnet_update_connection_from_config_block ("myxjtu2", error); + ASSERT (ifnet_add_new_connection + (connection, "net.generate", "wpa_supplicant.conf.generate", + error), "add connection", "add connection failed: %s", + "myxjtu2"); +} + +static void +test_delete_connection () +{ + GError **error = NULL; + NMConnection *connection; + + connection = ifnet_update_connection_from_config_block ("eth7", error); + ASSERT (connection != NULL, "get connection", + "get connection failed: %s", + error == NULL ? "None" : (*error)->message); + ASSERT (ifnet_delete_connection_in_parsers + ("eth7", "net.generate", "wpa_supplicant.conf.generate"), + "delete connection", "delete connection failed: %s", "eth7"); + connection = + ifnet_update_connection_from_config_block ("qiaomuf", error); + ASSERT (connection != NULL, "get connection", + "get connection failed: %s", + error == NULL ? "None" : (*error)->message); + ASSERT (ifnet_delete_connection_in_parsers + ("qiaomuf", "net.generate", "wpa_supplicant.conf.generate"), + "delete connection", "delete connection failed: %s", "qiaomuf"); +} + +static void +run_all (gboolean run) +{ + if (run) { + test_strip_string (); + test_is_static (); + test_has_ip6_address (); + test_has_default_route (); + test_getdata (); + test_read_hostname (); + test_write_hostname (); + test_is_ip4_address (); + test_is_ip6_address (); + test_convert_ipv4_config_block (); + test_convert_ipv4_routes_block (); + test_is_unmanaged (); + test_wpa_parser (); + test_convert_ipv4_routes_block (); + test_new_connection (); + test_update_connection (); + test_add_connection (); + test_delete_connection (); + } +} + +int +main (void) +{ +// g_mem_set_vtable(glib_mem_profiler_table); +// g_atexit(g_mem_profile); + g_type_init (); + ifnet_destroy (); + wpa_parser_destroy (); + ifnet_init ("net"); + wpa_parser_init ("wpa_supplicant.conf"); + printf("Initialization complete\n"); + + run_all (TRUE); + + ifnet_destroy (); + wpa_parser_destroy (); + return 0; +} diff --git a/system-settings/plugins/ifnet/tests/wpa_supplicant.conf b/system-settings/plugins/ifnet/tests/wpa_supplicant.conf new file mode 100644 index 0000000000..7763d2155f --- /dev/null +++ b/system-settings/plugins/ifnet/tests/wpa_supplicant.conf @@ -0,0 +1,876 @@ +##### Example wpa_supplicant configuration file ############################### +# +# This file describes configuration file format and lists all available option. +# Please also take a look at simpler configuration examples in 'examples' +# subdirectory. +# +# Empty lines and lines starting with # are ignored + +# NOTE! This file may contain password information and should probably be made +# readable only by root user on multiuser systems. + +# Note: All file paths in this configuration file should use full (absolute, +# not relative to working directory) path in order to allow working directory +# to be changed. This can happen if wpa_supplicant is run in the background. + +# Whether to allow wpa_supplicant to update (overwrite) configuration +# +# This option can be used to allow wpa_supplicant to overwrite configuration +# file whenever configuration is changed (e.g., new network block is added with +# wpa_cli or wpa_gui, or a password is changed). This is required for +# wpa_cli/wpa_gui to be able to store the configuration changes permanently. +# Please note that overwriting configuration file will remove the comments from +# it. +#update_config=1 + +# global configuration (shared by all network blocks) +# +# Parameters for the control interface. If this is specified, wpa_supplicant +# will open a control interface that is available for external programs to +# manage wpa_supplicant. The meaning of this string depends on which control +# interface mechanism is used. For all cases, the existance of this parameter +# in configuration is used to determine whether the control interface is +# enabled. +# +# For UNIX domain sockets (default on Linux and BSD): This is a directory that +# will be created for UNIX domain sockets for listening to requests from +# external programs (CLI/GUI, etc.) for status information and configuration. +# The socket file will be named based on the interface name, so multiple +# wpa_supplicant processes can be run at the same time if more than one +# interface is used. +# /var/run/wpa_supplicant is the recommended directory for sockets and by +# default, wpa_cli will use it when trying to connect with wpa_supplicant. +# +# Access control for the control interface can be configured by setting the +# directory to allow only members of a group to use sockets. This way, it is +# possible to run wpa_supplicant as root (since it needs to change network +# configuration and open raw sockets) and still allow GUI/CLI components to be +# run as non-root users. However, since the control interface can be used to +# change the network configuration, this access needs to be protected in many +# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you +# want to allow non-root users to use the control interface, add a new group +# and change this value to match with that group. Add users that should have +# control interface access to this group. If this variable is commented out or +# not included in the configuration file, group will not be changed from the +# value it got by default when the directory or socket was created. +# +# When configuring both the directory and group, use following format: +# DIR=/var/run/wpa_supplicant GROUP=wheel +# DIR=/var/run/wpa_supplicant GROUP=0 +# (group can be either group name or gid) +# +# For UDP connections (default on Windows): The value will be ignored. This +# variable is just used to select that the control interface is to be created. +# The value can be set to, e.g., udp (ctrl_interface=udp) +# +# For Windows Named Pipe: This value can be used to set the security descriptor +# for controlling access to the control interface. Security descriptor can be +# set using Security Descriptor String Format (see http://msdn.microsoft.com/ +# library/default.asp?url=/library/en-us/secauthz/security/ +# security_descriptor_string_format.asp). The descriptor string needs to be +# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty +# DACL (which will reject all connections). See README-Windows.txt for more +# information about SDDL string format. +# +ctrl_interface=/var/run/wpa_supplicant + +# IEEE 802.1X/EAPOL version +# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines +# EAPOL version 2. However, there are many APs that do not handle the new +# version number correctly (they seem to drop the frames completely). In order +# to make wpa_supplicant interoperate with these APs, the version number is set +# to 1 by default. This configuration value can be used to set it to the new +# version (2). +eapol_version=1 + +# AP scanning/selection +# By default, wpa_supplicant requests driver to perform AP scanning and then +# uses the scan results to select a suitable AP. Another alternative is to +# allow the driver to take care of AP scanning and selection and use +# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association +# information from the driver. +# 1: wpa_supplicant initiates scanning and AP selection +# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association +# parameters (e.g., WPA IE generation); this mode can also be used with +# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with +# APs (i.e., external program needs to control association). This mode must +# also be used when using wired Ethernet drivers. +# 2: like 0, but associate with APs using security policy and SSID (but not +# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to +# enable operation with hidden SSIDs and optimized roaming; in this mode, +# the network blocks in the configuration file are tried one by one until +# the driver reports successful association; each network block should have +# explicit security policy (i.e., only one option in the lists) for +# key_mgmt, pairwise, group, proto variables +ap_scan=1 + +# EAP fast re-authentication +# By default, fast re-authentication is enabled for all EAP methods that +# support it. This variable can be used to disable fast re-authentication. +# Normally, there is no need to disable this. +fast_reauth=1 + +# OpenSSL Engine support +# These options can be used to load OpenSSL engines. +# The two engines that are supported currently are shown below: +# They are both from the opensc project (http://www.opensc.org/) +# By default no engines are loaded. +# make the opensc engine available +#opensc_engine_path=/usr/lib64/engine_opensc.so +# make the pkcs11 engine available +#pkcs11_engine_path=/usr/lib64/engine_pkcs11.so +# configure the path to the pkcs11 module required by the pkcs11 engine +#pkcs11_module_path=/usr/lib64/opensc-pkcs11.so + +# Dynamic EAP methods +# If EAP methods were built dynamically as shared object files, they need to be +# loaded here before being used in the network blocks. By default, EAP methods +# are included statically in the build, so these lines are not needed +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so + +# Driver interface parameters +# This field can be used to configure arbitrary driver interace parameters. The +# format is specific to the selected driver interface. This field is not used +# in most cases. +#driver_param="field=value" + +# Country code +# The ISO/IEC alpha2 country code for the country in which this device is +# currently operating. +#country=US + +# Maximum lifetime for PMKSA in seconds; default 43200 +#dot11RSNAConfigPMKLifetime=43200 +# Threshold for reauthentication (percentage of PMK lifetime); default 70 +#dot11RSNAConfigPMKReauthThreshold=70 +# Timeout for security association negotiation in seconds; default 60 +#dot11RSNAConfigSATimeout=60 + +# Wi-Fi Protected Setup (WPS) parameters + +# Universally Unique IDentifier (UUID; see RFC 4122) of the device +# If not configured, UUID will be generated based on the local MAC address. +#uuid=12345678-9abc-def0-1234-56789abcdef0 + +# Device Name +# User-friendly description of device; up to 32 octets encoded in UTF-8 +#device_name=Wireless Client + +# Manufacturer +# The manufacturer of the device (up to 64 ASCII characters) +#manufacturer=Company + +# Model Name +# Model of the device (up to 32 ASCII characters) +#model_name=cmodel + +# Model Number +# Additional device description (up to 32 ASCII characters) +#model_number=123 + +# Serial Number +# Serial number of the device (up to 32 characters) +#serial_number=12345 + +# Primary Device Type +# Used format: <categ>-<OUI>-<subcateg> +# categ = Category as an integer value +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for +# default WPS OUI +# subcateg = OUI-specific Sub Category as an integer value +# Examples: +# 1-0050F204-1 (Computer / PC) +# 1-0050F204-2 (Computer / Server) +# 5-0050F204-1 (Storage / NAS) +# 6-0050F204-1 (Network Infrastructure / AP) +#device_type=1-0050F204-1 + +# OS Version +# 4-octet operating system version number (hex string) +#os_version=01020300 + +# Credential processing +# 0 = process received credentials internally (default) +# 1 = do not process received credentials; just pass them over ctrl_iface to +# external program(s) +# 2 = process received credentials internally and pass them over ctrl_iface +# to external program(s) +#wps_cred_processing=0 + +# network block +# +# Each network (usually AP's sharing the same SSID) is configured as a separate +# block in this configuration file. The network blocks are in preference order +# (the first match is used). +# +# network block fields: +# +# disabled: +# 0 = this network can be used (default) +# 1 = this network block is disabled (can be enabled through ctrl_iface, +# e.g., with wpa_cli or wpa_gui) +# +# id_str: Network identifier string for external scripts. This value is passed +# to external action script through wpa_cli as WPA_ID_STR environment +# variable to make it easier to do network specific configuration. +# +# ssid: SSID (mandatory); either as an ASCII string with double quotation or +# as hex string; network name +# +# scan_ssid: +# 0 = do not scan this SSID with specific Probe Request frames (default) +# 1 = scan with SSID-specific Probe Request frames (this can be used to +# find APs that do not accept broadcast SSID or use multiple SSIDs; +# this will add latency to scanning, so enable this only when needed) +# +# bssid: BSSID (optional); if set, this network block is used only when +# associating with the AP using the configured BSSID +# +# priority: priority group (integer) +# By default, all networks will get same priority group (0). If some of the +# networks are more desirable, this field can be used to change the order in +# which wpa_supplicant goes through the networks when selecting a BSS. The +# priority groups will be iterated in decreasing priority (i.e., the larger the +# priority value, the sooner the network is matched against the scan results). +# Within each priority group, networks will be selected based on security +# policy, signal strength, etc. +# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not +# using this priority to select the order for scanning. Instead, they try the +# networks in the order that used in the configuration file. +# +# mode: IEEE 802.11 operation mode +# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default) +# 1 = IBSS (ad-hoc, peer-to-peer) +# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) +# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In addition, ap_scan has +# to be set to 2 for IBSS. WPA-None requires following network block options: +# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not +# both), and psk must also be set. +# +# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g., +# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial +# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode. +# In addition, this value is only used by the station that creates the IBSS. If +# an IBSS network with the configured SSID is already present, the frequency of +# the network will be used instead of this configured value. +# +# proto: list of accepted protocols +# WPA = WPA/IEEE 802.11i/D3.0 +# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) +# If not set, this defaults to: WPA RSN +# +# key_mgmt: list of accepted authenticated key management protocols +# WPA-PSK = WPA pre-shared key (this requires 'psk' field) +# WPA-EAP = WPA using EAP authentication +# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically +# generated WEP keys +# NONE = WPA is not used; plaintext or static WEP could be used +# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms +# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms +# If not set, this defaults to: WPA-PSK WPA-EAP +# +# auth_alg: list of allowed IEEE 802.11 authentication algorithms +# OPEN = Open System authentication (required for WPA/WPA2) +# SHARED = Shared Key authentication (requires static WEP keys) +# LEAP = LEAP/Network EAP (only used with LEAP) +# If not set, automatic selection is used (Open System with LEAP enabled if +# LEAP is allowed as one of the EAP methods). +# +# pairwise: list of accepted pairwise (unicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# NONE = Use only Group Keys (deprecated, should not be included if APs support +# pairwise keys) +# If not set, this defaults to: CCMP TKIP +# +# group: list of accepted group (broadcast/multicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key +# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] +# If not set, this defaults to: CCMP TKIP WEP104 WEP40 +# +# psk: WPA preshared key; 256-bit pre-shared key +# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., +# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be +# generated using the passphrase and SSID). ASCII passphrase must be between +# 8 and 63 characters (inclusive). +# This field is not needed, if WPA-EAP is used. +# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys +# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant +# startup and reconfiguration time can be optimized by generating the PSK only +# only when the passphrase or SSID has actually changed. +# +# eapol_flags: IEEE 802.1X/EAPOL options (bit field) +# Dynamic WEP key required for non-WPA mode +# bit0 (1): require dynamically generated unicast WEP key +# bit1 (2): require dynamically generated broadcast WEP key +# (3 = require both keys; default) +# Note: When using wired authentication, eapol_flags must be set to 0 for the +# authentication to be completed successfully. +# +# mixed_cell: This option can be used to configure whether so called mixed +# cells, i.e., networks that use both plaintext and encryption in the same +# SSID, are allowed when selecting a BSS form scan results. +# 0 = disabled (default) +# 1 = enabled +# +# proactive_key_caching: +# Enable/disable opportunistic PMKSA caching for WPA2. +# 0 = disabled (default) +# 1 = enabled +# +# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or +# hex without quotation, e.g., 0102030405) +# wep_tx_keyidx: Default WEP key index (TX) (0..3) +# +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is +# allowed. This is only used with RSN/WPA2. +# 0 = disabled (default) +# 1 = enabled +#peerkey=1 +# +# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to +# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. +# +# Following fields are only used with internal EAP implementation. +# eap: space-separated list of accepted EAP methods +# MD5 = EAP-MD5 (unsecure and does not generate keying material -> +# cannot be used with WPA; to be used as a Phase 2 method +# with EAP-PEAP or EAP-TTLS) +# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# OTP = EAP-OTP (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# GTC = EAP-GTC (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# TLS = EAP-TLS (client and server certificate) +# PEAP = EAP-PEAP (with tunnelled EAP authentication) +# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2 +# authentication) +# If not set, all compiled in methods are allowed. +# +# identity: Identity string for EAP +# This field is also used to configure user NAI for +# EAP-PSK/PAX/SAKE/GPSK. +# anonymous_identity: Anonymous identity string for EAP (to be used as the +# unencrypted identity with EAP types that support different tunnelled +# identity, e.g., EAP-TTLS) +# password: Password string for EAP. This field can include either the +# plaintext password (using ASCII or hex string) or a NtPasswordHash +# (16-byte MD4 hash of password) in hash:<32 hex digits> format. +# NtPasswordHash can only be used when the password is for MSCHAPv2 or +# MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). +# EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit +# PSK) is also configured using this field. For EAP-GPSK, this is a +# variable length PSK. +# ca_cert: File path to CA certificate file (PEM/DER). This file can have one +# or more trusted CA certificates. If ca_cert and ca_path are not +# included, server certificate will not be verified. This is insecure and +# a trusted CA certificate should always be configured when using +# EAP-TLS/TTLS/PEAP. Full path should be used since working directory may +# change when wpa_supplicant is run in the background. +# On Windows, trusted CA certificates can be loaded from the system +# certificate store by setting this to cert_store://<name>, e.g., +# ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# ca_path: Directory path for CA certificate files (PEM). This path may +# contain multiple CA certificates in OpenSSL format. Common use for this +# is to point to system trusted CA list which is often installed into +# directory like /etc/ssl/certs. If configured, these certificates are +# added to the list of trusted CAs. ca_cert may also be included in that +# case, but it is not required. +# client_cert: File path to client certificate file (PEM/DER) +# Full path should be used since working directory may change when +# wpa_supplicant is run in the background. +# Alternatively, a named configuration blob can be used by setting this +# to blob://<blob name>. +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read from +# the PKCS#12 file in this case. Full path should be used since working +# directory may change when wpa_supplicant is run in the background. +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# cert://substring_to_match +# hash://certificate_thumbprint_in_hex +# for example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# Alternatively, a named configuration blob can be used by setting this +# to blob://<blob name>. +# private_key_passwd: Password for private key file (if left out, this will be +# asked through control interface) +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. In most cases, the default RSA +# authentication does not use this configuration. However, it is possible +# setup RSA to use ephemeral DH key exchange. In addition, ciphers with +# DSA keys always use ephemeral DH keys. This can be used to achieve +# forward secrecy. If the file is in DSA parameters format, it will be +# automatically converted into DH params. +# subject_match: Substring to be matched against the subject of the +# authentication server certificate. If this string is set, the server +# sertificate is only accepted if it contains this string in the subject. +# The subject string is in following format: +# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com +# altsubject_match: Semicolon separated string of entries to be matched against +# the alternative subject name of the authentication server certificate. +# If this string is set, the server sertificate is only accepted if it +# contains one of the entries in an alternative subject name extension. +# altSubjectName string is in following format: TYPE:VALUE +# Example: EMAIL:server@example.com +# Example: DNS:server.example.com;DNS:server2.example.com +# Following types are supported: EMAIL, DNS, URI +# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters +# (string with field-value pairs, e.g., "peapver=0" or +# "peapver=1 peaplabel=1") +# 'peapver' can be used to force which PEAP version (0 or 1) is used. +# 'peaplabel=1' can be used to force new label, "client PEAP encryption", +# to be used during key derivation when PEAPv1 or newer. Most existing +# PEAPv1 implementation seem to be using the old label, "client EAP +# encryption", and wpa_supplicant is now using that as the default value. +# Some servers, e.g., Radiator, may require peaplabel=1 configuration to +# interoperate with PEAPv1; see eap_testing.txt for more details. +# 'peap_outer_success=0' can be used to terminate PEAP authentication on +# tunneled EAP-Success. This is required with some RADIUS servers that +# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., +# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode) +# include_tls_length=1 can be used to force wpa_supplicant to include +# TLS Message Length field in all TLS messages even if they are not +# fragmented. +# sim_min_num_chal=3 can be used to configure EAP-SIM to require three +# challenges (by default, it accepts 2 or 3) +# result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use +# protected result indication. +# 'crypto_binding' option can be used to control PEAPv0 cryptobinding +# behavior: +# * 0 = do not use cryptobinding (default) +# * 1 = use cryptobinding if server supports it +# * 2 = require cryptobinding +# EAP-WSC (WPS) uses following options: pin=<Device Password> or +# pbc=1. +# phase2: Phase2 (inner authentication with TLS tunnel) parameters +# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or +# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# Following certificate/private key fields are used in inner Phase2 +# authentication when using EAP-TTLS or EAP-PEAP. +# ca_cert2: File path to CA certificate file. This file can have one or more +# trusted CA certificates. If ca_cert2 and ca_path2 are not included, +# server certificate will not be verified. This is insecure and a trusted +# CA certificate should always be configured. +# ca_path2: Directory path for CA certificate files (PEM) +# client_cert2: File path to client certificate file +# private_key2: File path to client private key file +# private_key2_passwd: Password for private key file +# dh_file2: File path to DH/DSA parameters file (in PEM format) +# subject_match2: Substring to be matched against the subject of the +# authentication server certificate. +# altsubject_match2: Substring to be matched against the alternative subject +# name of the authentication server certificate. +# +# fragment_size: Maximum EAP fragment size in bytes (default 1398). +# This value limits the fragment size for EAP methods that support +# fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set +# small enough to make the EAP messages fit in MTU of the network +# interface used for EAPOL. The default value is suitable for most +# cases. +# +# EAP-FAST variables: +# pac_file: File path for the PAC entries. wpa_supplicant will need to be able +# to create this file and write updates to it when PAC is being +# provisioned or refreshed. Full path to the file should be used since +# working directory may change when wpa_supplicant is run in the +# background. Alternatively, a named configuration blob can be used by +# setting this to blob://<blob name> +# phase1: fast_provisioning option can be used to enable in-line provisioning +# of EAP-FAST credentials (PAC): +# 0 = disabled, +# 1 = allow unauthenticated provisioning, +# 2 = allow authenticated provisioning, +# 3 = allow both unauthenticated and authenticated provisioning +# fast_max_pac_list_len=<num> option can be used to set the maximum +# number of PAC entries to store in a PAC list (default: 10) +# fast_pac_format=binary option can be used to select binary format for +# storing PAC entries in order to save some space (the default +# text format uses about 2.5 times the size of minimal binary +# format) +# +# wpa_supplicant supports number of "EAP workarounds" to work around +# interoperability issues with incorrectly behaving authentication servers. +# These are enabled by default because some of the issues are present in large +# number of authentication servers. Strict EAP conformance mode can be +# configured by disabling workarounds with eap_workaround=0. + +# Example blocks: + +# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers +network={ + ssid="simple" + psk="very secret passphrase" + priority=5 +} + +# Same as previous, but request SSID-specific scanning (for APs that reject +# broadcast SSID) +network={ + ssid="second ssid" + scan_ssid=1 + psk="very secret passphrase" + priority=2 +} + +# Only WPA-PSK is used. Any valid cipher combination is accepted. +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb + priority=2 +} + +# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=TKIP + group=TKIP + psk="not so secure passphrase" + wpa_ptk_rekey=600 +} + +# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104 +# or WEP40 as the group cipher will not be accepted. +network={ + ssid="example" + proto=RSN + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + priority=1 +} + +# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel +# (e.g., Radiator) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=1" + phase2="auth=MSCHAPV2" + priority=10 +} + +# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the +# unencrypted use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + priority=2 +} + +# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted +# use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MSCHAPV2" +} + +# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner +# authentication. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + # Phase1 / outer authentication + anonymous_identity="anonymous@example.com" + ca_cert="/etc/cert/ca.pem" + # Phase 2 / inner authentication + phase2="autheap=TLS" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" + priority=2 +} + +# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and +# group cipher. +network={ + ssid="example" + bssid=00:11:22:33:44:55 + proto=WPA RSN + key_mgmt=WPA-PSK WPA-EAP + pairwise=CCMP + group=CCMP + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb +} + +# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP +# and all valid ciphers. +network={ + ssid=00010203 + psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +} + + +# EAP-SIM with a GSM SIM or USIM +network={ + ssid="eap-sim-test" + key_mgmt=WPA-EAP + eap=SIM + pin="1234" + pcsc="" +} + + +# EAP-PSK +network={ + ssid="eap-psk-test" + key_mgmt=WPA-EAP + eap=PSK + anonymous_identity="eap_psk_user" + password=06b4be19da289f475aa46a33cb793029 + identity="eap_psk_user@example.com" +} + + +# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using +# EAP-TLS for authentication and key generation; require both unicast and +# broadcast WEP keys. +network={ + ssid="1xtest" + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +# LEAP with dynamic WEP keys +network={ + ssid="leap-example" + key_mgmt=IEEE8021X + eap=LEAP + identity="user" + password="foobar" +} + +# EAP-IKEv2 using shared secrets for both server and peer authentication +network={ + ssid="ikev2-example" + key_mgmt=WPA-EAP + eap=IKEV2 + identity="user" + password="foobar" +} + +# EAP-FAST with WPA (WPA or WPA2) +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="/etc/wpa_supplicant.eap-fast-pac" +} + +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="blob://eap-fast-pac" +} + +# Plaintext connection (no WPA, no IEEE 802.1X) +network={ + ssid="plaintext-test" + key_mgmt=NONE +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) +network={ + ssid="static-wep-test" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key +# IEEE 802.11 authentication +network={ + ssid="static-wep-test2" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 + auth_alg=SHARED +} + + +# IBSS/ad-hoc network with WPA-None/TKIP. +network={ + ssid="test adhoc" + mode=1 + frequency=2412 + proto=WPA + key_mgmt=WPA-NONE + pairwise=NONE + group=TKIP + psk="secret passphrase" +} + + +# Catch all example that allows more or less all configuration modes +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" +} + +# Example of EAP-TLS with smartcard (openssl engine) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TLS + proto=RSN + pairwise=CCMP TKIP + group=CCMP TKIP + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + + engine=1 + + # The engine configured here must be available. Look at + # OpenSSL engine support in the global section. + # The key available through the engine must be the private key + # matching the client certificate configured above. + + # use the opensc engine + #engine_id="opensc" + #key_id="45" + + # use the pkcs11 engine + engine_id="pkcs11" + key_id="id_45" + + # Optional PIN configuration; this can be left out and PIN will be + # asked through the control interface + pin="1234" +} + +# Example configuration showing how to use an inlined blob as a CA certificate +# data instead of using external file +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="blob://exampleblob" + priority=20 +} + +blob-base64-exampleblob={ +SGVsbG8gV29ybGQhCg== +} + + +# Wildcard match for SSID (plaintext APs only). This example select any +# open AP regardless of its SSID. +network={ + key_mgmt=NONE +} +network={ + ssid="qiaomuf" + key_mgmt=WPA-EAP + eap=TLS + identity="user@example.com" + ca_cert="/home/gentoo/temp/ca.pem" + client_cert="/home/gentoo/temp/client.pem" + private_key="/home/gentoo/temp/client.p12" + private_key_passwd="whatever" +# phase2="auth=MSCHAPV2" + priority=10 +} +network={ + ssid="myxjtu2" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="xjtudlc3731" + disabled=0 + key_mgmt=NONE + wep_key0="12345" + wep_key1=1234567890 + wep_key2="zxcvb" + wep_tx_keyidx=1 + auth_alg=OPEN + mode=1 +} +network={ + ssid=ab3ace + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="blob://exampleblob" + priority=20 +} diff --git a/system-settings/plugins/ifnet/wpa_parser.c b/system-settings/plugins/ifnet/wpa_parser.c new file mode 100644 index 0000000000..5e94108e98 --- /dev/null +++ b/system-settings/plugins/ifnet/wpa_parser.c @@ -0,0 +1,558 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include <string.h> +#include <stdlib.h> +#include <nm-system-config-interface.h> +#include "wpa_parser.h" +#include "net_parser.h" +#include "net_utils.h" + +/* Security information */ +static GHashTable *wsec_table = NULL; + +/* Global information used for writing */ +static GHashTable *wsec_global_table = NULL; + +static gboolean wpa_parser_data_changed = FALSE; + +static long +wpa_get_long (GHashTable * table, gchar * key) +{ + return atol (g_hash_table_lookup (table, key)); +} + +static void +destroy_security (GHashTable * network) +{ + gpointer key, value; + GHashTableIter iter; + + g_return_if_fail (network); + g_hash_table_iter_init (&iter, network); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_free (key); + g_free (value); + } + + g_hash_table_destroy (network); +} + +static GHashTable * +add_security (GHashTable * security) +{ + GHashTable *oldsecurity; + gchar *ssid = g_hash_table_lookup (security, "ssid"), *ssid_key; + gchar *value; + gboolean is_hex_ssid; + + /* Every security information should have a ssid */ + if (!ssid) { + destroy_security (security); + return NULL; + } + + /* Hex format begins with " */ + is_hex_ssid = (ssid[0] != '"'); + if ((value = g_hash_table_lookup (security, "disabled")) != NULL) { + if (strcmp (value, "1") == 0) + return NULL; + } + + /* Default priority is 1 */ + if (g_hash_table_lookup (security, "priority") == NULL) + g_hash_table_insert (security, g_strdup ("priority"), + g_strdup ("1")); + + oldsecurity = g_hash_table_lookup (wsec_table, ssid); + /* Security with lower priority will be ignored */ + if (oldsecurity != NULL) { + if (wpa_get_long (oldsecurity, "priority") >= + wpa_get_long (security, "priority")) { + destroy_security (security); + return NULL; + } else { + g_hash_table_remove (wsec_table, ssid); + destroy_security (oldsecurity); + } + } + + /* format ssid */ + ssid_key = + is_hex_ssid ? g_strdup_printf ("0x%s", + ssid) : + strip_string (g_strdup (ssid), '"'); + g_hash_table_insert (wsec_table, ssid_key, security); + return security; +} + +static void +add_key_value (GHashTable * network, gchar * line) +{ + gchar **key_value; + + if (g_str_has_prefix (line, "network={")) + line += 9; + strip_string (line, '{'); + strip_string (line, '}'); + if (line[0] == '\0') + return; + key_value = g_strsplit (line, "=", 2); + if (g_strv_length (key_value) != 2) { + g_strfreev (key_value); + return; + } + g_strstrip (key_value[0]); + g_strstrip (key_value[1]); + + /* Reserve quotes for psk, wep_key, ssid + * Quotes will determine whether they are hex format */ + if (strcmp (key_value[0], "psk") != 0 + && !g_str_has_prefix (key_value[0], "wep_key") + && strcmp (key_value[0], "ssid") != 0) + strip_string (key_value[1], '"'); + g_hash_table_insert (network, g_strdup (key_value[0]), + g_strdup (key_value[1])); + g_strfreev (key_value); +} + +static void +add_one_wep_key (GHashTable * table, int key_num, gchar * one_wep_key) +{ + if (one_wep_key[0] == 's') { + //asc key + g_hash_table_insert (table, + g_strdup_printf ("wep_key%d", key_num - 1), + g_strdup_printf ("\"%s\"", + one_wep_key + 2)); + } else { + gchar buf[30]; + int i = 0, j = 0; + + //hex key + while (one_wep_key[i] != '\0') { + if (one_wep_key[i] != '-') + buf[j++] = one_wep_key[i]; + i++; + } + buf[j] = '\0'; + g_hash_table_insert (table, + g_strdup_printf ("wep_key%d", key_num - 1), + g_strdup (buf)); + + } +} + +/* Reading wep security information from /etc/conf.d/net. + * This should not be used in futre, use wpa_supplicant instead. */ +static void +add_keys_from_net () +{ + GList *names = ifnet_get_connection_names (); + GList *iter = names; + gchar *wep_keys = "(\\[([1-4])\\]\\s+(s:\\w{5}|s:\\w{13}|" + "([\\da-fA-F]{4}\\-){2}[\\da-fA-F]{2}|" + "([\\da-fA-F]{4}\\-){6}[\\da-fA-F]{2})\\s+)"; + gchar *key_method = + "\\s+key\\s+\\[([1-4])\\]\\s+enc\\s+(open|restricted)"; + GRegex *regex_keys = g_regex_new (wep_keys, 0, 0, NULL); + GRegex *regex_method = g_regex_new (key_method, 0, 0, NULL); + GMatchInfo *keys_info; + GMatchInfo *method_info; + + while (iter) { + gchar *conn_name = iter->data; + GHashTable *table; + gchar *key_str; + + if ((key_str = ifnet_get_data (conn_name, "key")) == NULL) { + iter = g_list_next (iter); + continue; + } + + wpa_add_security (conn_name); + table = _get_hash_table (conn_name); + /* Give lowest priority */ + wpa_set_data (conn_name, "priority", "0"); + g_regex_match (regex_keys, key_str, 0, &keys_info); + /* add wep keys */ + while (g_match_info_matches (keys_info)) { + gchar *key_num = g_match_info_fetch (keys_info, 2); + gchar *one_wep_key = g_match_info_fetch (keys_info, 3); + + add_one_wep_key (table, atoi (key_num), one_wep_key); + g_free (key_num); + g_free (one_wep_key); + g_match_info_next (keys_info, NULL); + } + g_match_info_free (keys_info); + + g_regex_match (regex_method, key_str, 0, &method_info); + /* set default key index and auth alg */ + if (g_match_info_matches (method_info)) { + gchar *default_idx = + g_match_info_fetch (method_info, 1); + gchar *method = g_match_info_fetch (method_info, 2); + + default_idx[0]--; + g_hash_table_insert (table, g_strdup ("wep_tx_keyidx"), + default_idx); + g_hash_table_insert (table, g_strdup ("auth_alg"), + g_ascii_strup (method, -1)); + } + g_match_info_free (method_info); + add_security (table); + iter = g_list_next (iter); + } + g_list_free (names); + g_regex_unref (regex_keys); + g_regex_unref (regex_method); +} + +static void +add_global_data (gchar * line) +{ + gchar **key_value; + + g_strstrip (line); + key_value = g_strsplit (line, "=", 2); + if (g_strv_length (key_value) != 2) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Can't handle this line: %s\n", + line); + g_strfreev (key_value); + return; + } + g_hash_table_insert (wsec_global_table, + g_strdup (g_strstrip (key_value[0])), + g_strdup (g_strstrip (key_value[1]))); + g_strfreev (key_value); +} + +void +wpa_parser_init (gchar * wpa_supplicant_conf) +{ + GIOChannel *channel = NULL; + gchar *line; + gboolean complete = FALSE; + + wpa_parser_data_changed = FALSE; + wsec_table = g_hash_table_new (g_str_hash, g_str_equal); + wsec_global_table = g_hash_table_new (g_str_hash, g_str_equal); + + if (g_file_test (wpa_supplicant_conf, G_FILE_TEST_IS_REGULAR)) + channel = + g_io_channel_new_file (wpa_supplicant_conf, "r", NULL); + if (channel == NULL) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't open %s for wireless security", + wpa_supplicant_conf); + return; + } + + while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL) + != G_IO_STATUS_EOF) { + g_strstrip (line); + if (line[0] != '#' && line[0] != '\0') { + if (strstr (line, "network={") == NULL) { + add_global_data (line); + g_free (line); + continue; + } else { + GHashTable *network = + g_hash_table_new (g_str_hash, g_str_equal); + gchar *tmp; + + do { + if (line[0] == '#' || line[0] == '\0') { + g_free (line); + continue; + } + /* ignore inline comments */ + if ((tmp = strchr (line, '#')) != NULL) + *tmp = '\0'; + if (strstr (line, "}") != NULL) + complete = TRUE; + add_key_value (network, line); + g_free (line); + } while (complete == FALSE + && + g_io_channel_read_line + (channel, &line, NULL, + NULL, NULL) != G_IO_STATUS_EOF); + add_security (network); + //EOF in inner loop + if (complete == FALSE) { + g_free (line); + break; + } + complete = FALSE; + } + } else + g_free (line); + } + + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + + add_keys_from_net (); +} + +gchar * +wpa_get_value (gchar * ssid, gchar * key) +{ + GHashTable *target = g_hash_table_lookup (wsec_table, ssid); + + if (target) + return g_hash_table_lookup (target, key); + return NULL; +} + +gboolean +exist_ssid (gchar * ssid) +{ + return g_hash_table_lookup (wsec_table, ssid) != NULL; +} + +GHashTable * +_get_hash_table (gchar * ssid) +{ + return g_hash_table_lookup (wsec_table, ssid); +} + +static gchar *quoted_keys[] = + { "identity", "cert", "private", "phase", "password", NULL }; + +/* tell whether the key needs quotes when writing is performed */ +static gboolean +need_quote (gchar * key) +{ + int i = 0; + + while (quoted_keys[i] != NULL) { + if (strstr (key, quoted_keys[i])) + return TRUE; + i++; + } + return FALSE; +} + +gboolean +wpa_flush_to_file (gchar * config_file) +{ + GIOChannel *channel; + GError **error = NULL; + gpointer key, value, ssid, security; + GHashTableIter iter, iter_security; + gchar *out_line; + gsize bytes_written; + gboolean result = FALSE; + + if (!wpa_parser_data_changed) + return FALSE; + if (!wsec_table || !wsec_global_table) + return FALSE; + + channel = g_io_channel_new_file (config_file, "w", NULL); + if (!channel) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, + "Can't open file %s for writing", config_file); + return FALSE; + } + g_hash_table_iter_init (&iter, wsec_global_table); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Writing to %s", config_file); + g_io_channel_write_chars (channel, + "#Generated by NetworkManager\n" + "###### Global Configuration ######\n", + -1, &bytes_written, error); + + /* Writing global information */ + while (g_hash_table_iter_next (&iter, &key, &value)) { + out_line = + g_strdup_printf ("%s=%s\n", (gchar *) key, (gchar *) value); + g_io_channel_write_chars (channel, out_line, -1, &bytes_written, + error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", + (*error)->message); + goto done; + } + g_io_channel_write_chars (channel, + "\n###### Security Configuration ######\n", + -1, &bytes_written, error); + + g_hash_table_iter_init (&iter, wsec_table); + /* Writing security */ + while (g_hash_table_iter_next (&iter, &ssid, &security)) { + g_hash_table_iter_init (&iter_security, + (GHashTable *) security); + g_io_channel_write_chars (channel, "network={\n", -1, + &bytes_written, error); + while (g_hash_table_iter_next (&iter_security, &key, &value)) { + out_line = + g_strdup_printf (need_quote ((gchar *) key) ? + "\t%s=\"%s\"\n" : "\t%s=%s\n", + (gchar *) key, (gchar *) value); + g_io_channel_write_chars (channel, out_line, -1, + &bytes_written, error); + if (bytes_written == 0 || (error && *error)) + break; + g_free (out_line); + } + g_io_channel_write_chars (channel, + "}\n\n", -1, &bytes_written, error); + + } + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", + (*error)->message); + goto done; + } + g_io_channel_flush (channel, error); + + if (error && *error) { + PLUGIN_WARN (IFNET_PLUGIN_NAME, "Found error: %s", + (*error)->message); + goto done; + } + wpa_parser_data_changed = FALSE; + result = TRUE; + done: + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + return result; +} + +/* If value is NULL, this method will delete old key value pair */ +void +wpa_set_data (gchar * ssid, gchar * key, gchar * value) +{ + gpointer orig_key = NULL, orig_value = NULL; + GHashTable *security = g_hash_table_lookup (wsec_table, ssid); + + g_return_if_fail (security != NULL); + + /* Remove old key value pairs */ + if (g_hash_table_lookup_extended + (security, key, &orig_key, &orig_value)) { + g_hash_table_remove (security, orig_key); + g_free (orig_key); + g_free (orig_value); + } + + /* Add new key value */ + if (value) { + gchar *new_value = g_strdup (value); + + if (strcmp (key, "ssid") != 0 && strcmp (key, "psk") != 0 + && !g_str_has_prefix (key, "wep_key")) + strip_string (new_value, '"'); + g_hash_table_insert (security, g_strdup (key), new_value); + } + wpa_parser_data_changed = TRUE; +} + +gboolean +wpa_has_security (gchar * ssid) +{ + return g_hash_table_lookup (wsec_table, ssid) != NULL; +} + +gboolean +wpa_add_security (gchar * ssid) +{ + if (wpa_has_security (ssid)) + return FALSE; + else { + GHashTable *security = + g_hash_table_new (g_str_hash, g_str_equal); + gchar *ssid_i; + + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Adding security for %s", + ssid); + if (g_str_has_prefix (ssid, "0x")) { + /* hex ssid */ + ssid_i = g_strdup (ssid + 2); + } else { + /* ascii ssid requires quotes */ + ssid_i = g_strdup_printf ("\"%s\"", ssid); + } + g_hash_table_insert (security, strdup ("ssid"), ssid_i); + g_hash_table_insert (security, strdup ("priority"), + strdup ("1")); + g_hash_table_insert (wsec_table, g_strdup (ssid), security); + wpa_parser_data_changed = TRUE; + return TRUE; + } +} + +gboolean +wpa_delete_security (gchar * ssid) +{ + gpointer orig_key, orig_value; + + g_return_val_if_fail (wsec_table != NULL && ssid != NULL, FALSE); + PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Deleting security for %s", ssid); + if (!g_hash_table_lookup_extended + (wsec_table, ssid, &orig_key, &orig_value)) + return FALSE; + g_hash_table_remove (wsec_table, orig_key); + g_free (orig_key); + destroy_security ((GHashTable *) orig_value); + wpa_parser_data_changed = TRUE; + return TRUE; + +} + +void +wpa_parser_destroy (void) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + + /* Destroy security */ + if (wsec_table) { + g_hash_table_iter_init (&iter, wsec_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + destroy_security ((GHashTable *) value); + g_free (key); + } + + g_hash_table_destroy (wsec_table); + wsec_table = NULL; + } + + /* Destroy global data */ + if (wsec_global_table) { + g_hash_table_iter_init (&iter, wsec_global_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + g_free (key); + g_free (value); + } + + g_hash_table_destroy (wsec_global_table); + wsec_global_table = NULL; + } +} diff --git a/system-settings/plugins/ifnet/wpa_parser.h b/system-settings/plugins/ifnet/wpa_parser.h new file mode 100644 index 0000000000..55b0ec0c60 --- /dev/null +++ b/system-settings/plugins/ifnet/wpa_parser.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#ifndef _WPA_PARSER_H +#define _WPA_PARSER_H +#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant/wpa_supplicant.conf" +#include <glib.h> +void wpa_parser_init (gchar * wpa_supplicant_conf); +void wpa_parser_destroy (void); + +/* reader functions */ +gchar *wpa_get_value (gchar * ssid, gchar * key); +gboolean exist_ssid (gchar * ssid); +GHashTable *_get_hash_table (gchar * ssid); +gboolean wpa_has_security (gchar * ssid); + +/* writer functions */ +gboolean wpa_flush_to_file (gchar * config_file); +void wpa_set_data (gchar * ssid, gchar * key, gchar * value); +gboolean wpa_add_security (gchar * ssid); +gboolean wpa_delete_security (gchar * ssid); +#endif diff --git a/system-settings/plugins/ifupdown/Makefile.am b/system-settings/plugins/ifupdown/Makefile.am index ec50311554..652e545fce 100644 --- a/system-settings/plugins/ifupdown/Makefile.am +++ b/system-settings/plugins/ifupdown/Makefile.am @@ -1,3 +1,4 @@ +SUBDIRS=. tests INCLUDES = \ -I$(top_srcdir)/src/system-settings \ @@ -5,15 +6,30 @@ INCLUDES = \ -I$(top_srcdir)/libnm-glib \ -I$(top_srcdir)/libnm-util -pkglib_LTLIBRARIES = libnm-settings-plugin-ifupdown.la +noinst_LTLIBRARIES = libifupdown-io.la -libnm_settings_plugin_ifupdown_la_SOURCES = \ +libifupdown_io_la_SOURCES = \ interface_parser.c \ interface_parser.h \ + parser.c \ + parser.h + +libifupdown_io_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + -DG_DISABLE_DEPRECATED \ + -DSYSCONFDIR=\"$(sysconfdir)\" + +libifupdown_io_la_LIBADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) \ + $(GMODULE_LIBS) + +pkglib_LTLIBRARIES = libnm-settings-plugin-ifupdown.la + +libnm_settings_plugin_ifupdown_la_SOURCES = \ nm-ifupdown-connection.c \ nm-ifupdown-connection.h \ - parser.c \ - parser.h \ plugin.c \ plugin.h @@ -28,6 +44,7 @@ libnm_settings_plugin_ifupdown_la_CPPFLAGS = \ libnm_settings_plugin_ifupdown_la_LDFLAGS = -module -avoid-version libnm_settings_plugin_ifupdown_la_LIBADD = \ $(top_builddir)/libnm-util/libnm-util.la \ + libifupdown-io.la \ $(GLIB_LIBS) \ $(GMODULE_LIBS) \ $(GUDEV_LIBS) diff --git a/system-settings/plugins/ifupdown/interface_parser.c b/system-settings/plugins/ifupdown/interface_parser.c index f28cf7cab1..4635db2ca1 100644 --- a/system-settings/plugins/ifupdown/interface_parser.c +++ b/system-settings/plugins/ifupdown/interface_parser.c @@ -73,85 +73,147 @@ void add_data(const char *key,const char *data) //printf("added data '%s' with key '%s'\n",data,key); } -#define SPACE_OR_TAB(string,ret) {ret = strchr(string,' ');ret=(ret == NULL?strchr(string,'\t'):ret);} +// join values in src with spaces into dst; dst needs to be large enough +static char *join_values_with_spaces(char *dst, char **src) +{ + if (dst != NULL) { + *dst = '\0'; + if (src != NULL && *src != NULL) { + strcat(dst, *src); + + for (src++; *src != NULL; src++) { + strcat(dst, " "); + strcat(dst, *src); + } + } + } + return(dst); +} -void ifparser_init(void) +void ifparser_init (const char *eni_file, int quiet) { - FILE *inp = fopen(ENI_INTERFACES_FILE, "r"); - int ret = 0; - char *line; - char *space; - char rline[255]; + FILE *inp = fopen (eni_file, "r"); + char line[255]; + int skip_to_block = 1; + int skip_long_line = 0; + int offs = 0; - if (inp == NULL) - { - nm_warning ("Error: Can't open %s\n", ENI_INTERFACES_FILE); + if (inp == NULL) { + if (!quiet) + g_warning ("Error: Can't open %s\n", eni_file); return; } + first = last = NULL; - while(1) + while (!feof(inp)) { - line = space = NULL; - ret = fscanf(inp,"%255[^\n]\n",rline); - if (ret == EOF) + char *token[128]; // 255 chars can only be split into 127 tokens + char value[255]; // large enough to join previously split tokens + char *safeptr; + int toknum; + int len = 0; + + char *ptr = fgets(line+offs, 255-offs, inp); + if (ptr == NULL) break; - // If the line did not match, skip it - if (ret == 0) { - char *ignored; - ignored = fgets(rline, 255, inp); + len = strlen(line); + // skip over-long lines + if (!feof(inp) && len > 0 && line[len-1] != '\n') { + if (!skip_long_line) { + if (!quiet) + g_message ("Error: Skipping over-long-line '%s...'\n", line); + } + skip_long_line = 1; + continue; + } + + // trailing '\n' found: remove it & reset offset to 0 + if (len > 0 && line[len-1] == '\n') { + line[--len] = '\0'; + offs = 0; + } + + // if we're in long_line_skip mode, terminate it for real next line + if (skip_long_line) { + if (len == 0 || line[len-1] != '\\') + skip_long_line = 0; continue; } - line = rline; - while(line[0] == ' ') - line++; - if (line[0]=='#' || line[0]=='\0') + // unwrap wrapped lines + if (len > 0 && line[len-1] == '\\') { + offs = len - 1; continue; + } - SPACE_OR_TAB(line,space) - if (space == NULL) - { - nm_warning ("Error: Can't parse interface line '%s'\n",line); - continue; + //printf(">>%s<<\n", line); + +#define SPACES " \t" + // tokenize input; + for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr); + token[toknum] != NULL; + toknum++, token[toknum] = strtok_r(NULL, SPACES, &safeptr)) + ; + + // ignore comments and empty lines + if (toknum == 0 || *token[0]=='#') + continue; + + if (toknum < 2) { + if (!quiet) { + g_message ("Error: Can't parse interface line '%s'\n", + join_values_with_spaces(value, token)); } - space[0] = '\0'; + skip_to_block = 1; + continue; + } // There are four different stanzas: // iface, mapping, auto and allow-*. Create a block for each of them. - if (strcmp(line,"iface")==0) - { - char *space2 = strchr(space+1,' '); - if (space2 == NULL) - { - nm_warning ("Error: Can't parse iface line '%s'\n",space+1); + + // iface stanza takes at least 3 parameters + if (strcmp(token[0], "iface") == 0) { + if (toknum < 4) { + if (!quiet) { + g_message ("Error: Can't parse iface line '%s'\n", + join_values_with_spaces(value, token)); + } continue; } - space2[0]='\0'; - add_block(line,space+1); - - if (space2[1]!='\0') - { - space = strchr(space2+1,' '); - if (space == NULL) - { - nm_warning ("Error: Can't parse data '%s'\n",space2+1); - continue; + add_block(token[0], token[1]); + skip_to_block = 0; + add_data(token[2], join_values_with_spaces(value, token + 3)); + } + // auto and allow-auto stanzas are equivalent, + // both can take multiple interfaces as parameters: add one block for each + else if (strcmp(token[0], "auto") == 0 || + strcmp(token[0], "allow-auto") == 0) { + int i; + for (i = 1; i < toknum; i++) + add_block("auto", token[i]); + skip_to_block = 0; + } + else if (strcmp(token[0], "mapping") == 0) { + add_block(token[0], join_values_with_spaces(value, token + 1)); + skip_to_block = 0; + } + // allow-* can take multiple interfaces as parameters: add one block for each + else if (strncmp(token[0],"allow-",6) == 0) { + int i; + for (i = 1; i < toknum; i++) + add_block(token[0], token[i]); + skip_to_block = 0; + } + else { + if (skip_to_block) { + if (!quiet) { + g_message ("Error: ignoring out-of-block data '%s'\n", + join_values_with_spaces(value, token)); } - space[0] = '\0'; - add_data(space2+1,space+1); - } + } else + add_data(token[0], join_values_with_spaces(value, token + 1)); } - else if (strcmp(line,"auto")==0) - add_block(line,space+1); - else if (strcmp(line,"mapping")==0) - add_block(line,space+1); - else if (strncmp(line,"allow-",6)==0) - add_block(line,space+1); - else - add_data(line,space+1); - - //printf("line: '%s' ret=%d\n",rline,ret); } fclose(inp); } @@ -190,6 +252,18 @@ if_block *ifparser_getfirst(void) return first; } +int ifparser_get_num_blocks(void) +{ + int i = 0; + if_block *iter = first; + + while (iter) { + i++; + iter = iter->next; + } + return i; +} + if_block *ifparser_getif(const char* iface) { if_block *curr = first; @@ -213,3 +287,15 @@ const char *ifparser_getkey(if_block* iface, const char *key) } return NULL; } + +int ifparser_get_num_info(if_block* iface) +{ + int i = 0; + if_data *iter = iface->info; + + while (iter) { + i++; + iter = iter->next; + } + return i; +} diff --git a/system-settings/plugins/ifupdown/interface_parser.h b/system-settings/plugins/ifupdown/interface_parser.h index 52b98d04c4..ea991c32d1 100644 --- a/system-settings/plugins/ifupdown/interface_parser.h +++ b/system-settings/plugins/ifupdown/interface_parser.h @@ -26,8 +26,6 @@ #include "config.h" -#define ENI_INTERFACES_FILE "/etc/network/interfaces" - typedef struct _if_data { char *key; @@ -43,12 +41,14 @@ typedef struct _if_block struct _if_block *next; } if_block; -void ifparser_init(void); +void ifparser_init(const char *eni_file, int quiet); void ifparser_destroy(void); if_block *ifparser_getif(const char* iface); if_block *ifparser_getfirst(void); const char *ifparser_getkey(if_block* iface, const char *key); +int ifparser_get_num_blocks(void); +int ifparser_get_num_info(if_block* iface); void add_block(const char *type, const char* name); void add_data(const char *key,const char *data); diff --git a/system-settings/plugins/ifupdown/nm-ifupdown-connection.h b/system-settings/plugins/ifupdown/nm-ifupdown-connection.h index 4eb20e5032..2aa74df6b7 100644 --- a/system-settings/plugins/ifupdown/nm-ifupdown-connection.h +++ b/system-settings/plugins/ifupdown/nm-ifupdown-connection.h @@ -39,11 +39,11 @@ G_BEGIN_DECLS #define NM_IFUPDOWN_CONNECTION_IFBLOCK "ifblock" typedef struct { - NMExportedConnection parent; + NMSysconfigConnection parent; } NMIfupdownConnection; typedef struct { - NMExportedConnectionClass parent; + NMSysconfigConnectionClass parent; } NMIfupdownConnectionClass; GType nm_ifupdown_connection_get_type (void); diff --git a/system-settings/plugins/ifupdown/plugin.c b/system-settings/plugins/ifupdown/plugin.c index e2358b9f51..8cfbedcd64 100644 --- a/system-settings/plugins/ifupdown/plugin.c +++ b/system-settings/plugins/ifupdown/plugin.c @@ -55,6 +55,8 @@ #define G_UDEV_API_IS_SUBJECT_TO_CHANGE #include <gudev/gudev.h> +#define ENI_INTERFACES_FILE "/etc/network/interfaces" + #define IFUPDOWN_PLUGIN_NAME "ifupdown" #define IFUPDOWN_PLUGIN_INFO "(C) 2008 Canonical Ltd. To report bugs please use the NetworkManager mailing list." #define IFUPDOWN_SYSTEM_HOSTNAME_FILE "/etc/hostname" @@ -355,17 +357,60 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config) update_system_hostname (inotify_helper, NULL, NULL, config); /* Read in all the interfaces */ - ifparser_init (); + ifparser_init (ENI_INTERFACES_FILE, 0); block = ifparser_getfirst (); while (block) { if(!strcmp ("auto", block->type) || !strcmp ("allow-hotplug", block->type)) g_hash_table_insert (auto_ifaces, block->name, GUINT_TO_POINTER (1)); - else if (!strcmp ("iface", block->type) && strcmp ("lo", block->name)) { + else if (!strcmp ("iface", block->type)) { NMIfupdownConnection *exported; + /* Bridge configuration */ + if(!strncmp ("br", block->name, 2)) { + /* Try to find bridge ports */ + const char *ports = ifparser_getkey (block, "bridge_ports"); + if (ports) { + int i; + int state = 0; + char **port_ifaces; + + PLUGIN_PRINT("SCPlugin-Ifupdown", "found bridge ports %s for %s", ports, block->name); + + port_ifaces = g_strsplit_set (ports, " \t", -1); + for (i = 0; i < g_strv_length (port_ifaces); i++) { + char *token = port_ifaces[i]; + /* Skip crazy stuff like regex or all */ + if (!strcmp ("all", token)) { + continue; + } + /* Small SM to skip everything inside regex */ + if (!strcmp ("regex", token)) { + state++; + continue; + } + if (!strcmp ("noregex", token)) { + state--; + continue; + } + if (state == 0 && strlen (token) > 0) { + PLUGIN_PRINT("SCPlugin-Ifupdown", "adding bridge port %s to well_known_interfaces", token); + g_hash_table_insert (priv->well_known_interfaces, g_strdup (token), "known"); + } + } + g_strfreev (port_ifaces); + } + goto next; + } + + /* Skip loopback configuration */ + if(!strcmp ("lo", block->name)) { + goto next; + } + /* Remove any connection for this block that was previously found */ exported = g_hash_table_lookup (priv->iface_connections, block->name); if (exported) { + PLUGIN_PRINT("SCPlugin-Ifupdown", "deleting %s from iface_connections", block->name); nm_settings_connection_interface_delete (NM_SETTINGS_CONNECTION_INTERFACE (exported), ignore_cb, NULL); @@ -375,12 +420,16 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config) /* add the new connection */ exported = nm_ifupdown_connection_new (block); if (exported) { + PLUGIN_PRINT("SCPlugin-Ifupdown", "adding %s to iface_connections", block->name); g_hash_table_insert (priv->iface_connections, block->name, exported); - g_hash_table_insert (priv->well_known_interfaces, block->name, "known"); } + PLUGIN_PRINT("SCPlugin-Ifupdown", "adding iface %s to well_known_interfaces", block->name); + g_hash_table_insert (priv->well_known_interfaces, block->name, "known"); } else if (!strcmp ("mapping", block->type)) { g_hash_table_insert (priv->well_known_interfaces, block->name, "known"); + PLUGIN_PRINT("SCPlugin-Ifupdown", "adding mapping %s to well_known_interfaces", block->name); } + next: block = block->next; } @@ -563,6 +612,12 @@ update_system_hostname(NMInotifyHelper *inotify_helper, priv->hostname = g_strstrip(hostname_file); + /* We shouldn't return a zero-length hostname, but NULL */ + if (priv->hostname && !strlen (priv->hostname)) { + g_free (priv->hostname); + priv->hostname = NULL; + } + g_object_notify (G_OBJECT (config), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME); } diff --git a/system-settings/plugins/ifupdown/tests/Makefile.am b/system-settings/plugins/ifupdown/tests/Makefile.am new file mode 100644 index 0000000000..b5dbd13d63 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/Makefile.am @@ -0,0 +1,32 @@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_srcdir)/libnm-glib \ + -I$(top_srcdir)/system-settings/plugins/ifupdown + +noinst_PROGRAMS = test-ifupdown + +test_ifupdown_SOURCES = \ + test-ifupdown.c + +test_ifupdown_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + -DTEST_ENI_DIR=\"$(abs_srcdir)\" + +test_ifupdown_LDADD = \ + $(top_builddir)/libnm-glib/libnm-glib.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/system-settings/plugins/ifupdown/libifupdown-io.la \ + $(DBUS_LIBS) + +if WITH_TESTS + +check-local: test-ifupdown + $(abs_builddir)/test-ifupdown + +endif + +EXTRA_DIST = \ + test1 test2 test3 test4 test5 test6 test7 test8 test9 test11 test12 \ + test13 test14 test15 test16 diff --git a/system-settings/plugins/ifupdown/tests/test-ifupdown.c b/system-settings/plugins/ifupdown/tests/test-ifupdown.c new file mode 100644 index 0000000000..1646536452 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test-ifupdown.c @@ -0,0 +1,496 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2010 Red Hat, Inc. + * + */ + +#include <glib.h> +#include <string.h> + +#include "interface_parser.h" +#include "parser.h" + +typedef struct { + char *key; + char *data; +} ExpectedKey; + +typedef struct { + char *type; + char *name; + GSList *keys; +} ExpectedBlock; + +typedef struct { + GSList *blocks; +} Expected; + +static ExpectedKey * +expected_key_new (const char *key, const char *data) +{ + ExpectedKey *k; + + k = g_malloc0 (sizeof (ExpectedKey)); + g_assert (k); + k->key = g_strdup (key); + g_assert (k->key); + k->data = g_strdup (data); + g_assert (k->data); + return k; +} + +static void +expected_key_free (ExpectedKey *k) +{ + g_assert (k); + g_free (k->key); + g_free (k->data); + memset (k, 0, sizeof (ExpectedKey)); + g_free (k); +} + +static ExpectedBlock * +expected_block_new (const char *type, const char *name) +{ + ExpectedBlock *b; + + g_assert (type); + g_assert (name); + b = g_malloc0 (sizeof (ExpectedBlock)); + g_assert (b); + b->type = g_strdup (type); + b->name = g_strdup (name); + return b; +} + +static void +expected_block_free (ExpectedBlock *b) +{ + g_assert (b); + g_slist_foreach (b->keys, (GFunc) expected_key_free, NULL); + g_slist_free (b->keys); + g_free (b->type); + g_free (b->name); + memset (b, 0, sizeof (ExpectedBlock)); + g_free (b); +} + +static void +expected_block_add_key (ExpectedBlock *b, ExpectedKey *k) +{ + g_assert (b); + g_assert (k); + b->keys = g_slist_append (b->keys, k); +} + +static Expected * +expected_new (void) +{ + Expected *e; + + e = g_malloc0 (sizeof (Expected)); + g_assert (e); + return e; +} + +static void +expected_add_block (Expected *e, ExpectedBlock *b) +{ + g_assert (e); + g_assert (b); + e->blocks = g_slist_append (e->blocks, b); +} + +static void +expected_free (Expected *e) +{ + g_assert (e); + g_slist_foreach (e->blocks, (GFunc) expected_block_free, NULL); + g_slist_free (e->blocks); + memset (e, 0, sizeof (Expected)); + g_free (e); +} + +static void +compare_expected_to_ifparser (Expected *e) +{ + if_block *n; + GSList *biter, *kiter; + + g_assert_cmpint (g_slist_length (e->blocks), ==, ifparser_get_num_blocks ()); + + for (n = ifparser_getfirst (), biter = e->blocks; + n && biter; + n = n->next, biter = g_slist_next (biter)) { + if_data *m; + ExpectedBlock *b = biter->data; + + g_assert (b->type && n->type); + g_assert_cmpstr (b->type, ==, n->type); + g_assert (b->name && n->name); + g_assert_cmpstr (b->name, ==, n->name); + + g_assert_cmpint (g_slist_length (b->keys), ==, ifparser_get_num_info (n)); + + for (m = n->info, kiter = b->keys; + m && kiter; + m = m->next, kiter = g_slist_next (kiter)) { + ExpectedKey *k = kiter->data; + + g_assert (k->key && m->key); + g_assert_cmpstr (k->key, ==, m->key); + g_assert (k->data && m->data); + g_assert_cmpstr (k->data, ==, m->data); + } + } +} + +static void +dump_blocks (void) +{ + if_block *n; + + g_message ("\n***************************************************"); + for (n = ifparser_getfirst (); n != NULL; n = n->next) { + if_data *m; + + // each block start with its type & name + // (single quotes used to show typ & name baoundaries) + g_print("'%s' '%s'\n", n->type, n->name); + + // each key-value pair within a block is indented & separated by a tab + // (single quotes used to show typ & name baoundaries) + for (m = n->info; m != NULL; m = m->next) + g_print("\t'%s'\t'%s'\n", m->key, m->data); + + // blocks are separated by an empty line + g_print("\n"); + } + g_message ("##################################################\n"); +} + +static void +init_ifparser_with_file (const char *path, const char *file) +{ + char *tmp; + + tmp = g_strdup_printf ("%s/%s", path, file); + ifparser_init (tmp, 1); + g_free (tmp); +} + +static void +test1_ignore_line_before_first_block (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("auto", "eth0"); + expected_add_block (e, b); + b = expected_block_new ("iface", "eth0"); + expected_add_block (e, b); + expected_block_add_key (b, expected_key_new ("inet", "dhcp")); + + init_ifparser_with_file (path, "test1"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test2_wrapped_line (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("auto", "lo"); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test2"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test3_wrapped_multiline_multiarg (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("allow-hotplug", "eth0"); + expected_add_block (e, b); + b = expected_block_new ("allow-hotplug", "wlan0"); + expected_add_block (e, b); + b = expected_block_new ("allow-hotplug", "bnep0"); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test3"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test4_allow_auto_is_auto (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("auto", "eth0"); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test4"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test5_allow_auto_multiarg (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("allow-hotplug", "eth0"); + expected_add_block (e, b); + b = expected_block_new ("allow-hotplug", "wlan0"); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test5"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test6_mixed_whitespace (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "lo"); + expected_block_add_key (b, expected_key_new ("inet", "loopback")); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test6"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test7_long_line (const char *path) +{ + init_ifparser_with_file (path, "test7"); + g_assert_cmpint (ifparser_get_num_blocks (), ==, 0); + ifparser_destroy (); +} + +static void +test8_long_line_wrapped (const char *path) +{ + init_ifparser_with_file (path, "test8"); + g_assert_cmpint (ifparser_get_num_blocks (), ==, 0); + ifparser_destroy (); +} + +static void +test9_wrapped_lines_in_block (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "eth0"); + expected_add_block (e, b); + expected_block_add_key (b, expected_key_new ("inet", "static")); + expected_block_add_key (b, expected_key_new ("address", "10.250.2.3")); + expected_block_add_key (b, expected_key_new ("netmask", "255.255.255.192")); + expected_block_add_key (b, expected_key_new ("broadcast", "10.250.2.63")); + expected_block_add_key (b, expected_key_new ("gateway", "10.250.2.50")); + + init_ifparser_with_file (path, "test9"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test11_complex_wrap (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "pppoe"); + expected_add_block (e, b); + expected_block_add_key (b, expected_key_new ("inet", "manual")); + expected_block_add_key (b, expected_key_new ("pre-up", "/sbin/ifconfig eth0 up")); + + init_ifparser_with_file (path, "test11"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test12_complex_wrap_split_word (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "pppoe"); + expected_add_block (e, b); + expected_block_add_key (b, expected_key_new ("inet", "manual")); + expected_block_add_key (b, expected_key_new ("up", "ifup ppp0=dsl")); + + init_ifparser_with_file (path, "test12"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test13_more_mixed_whitespace (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "dsl"); + expected_block_add_key (b, expected_key_new ("inet", "ppp")); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test13"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test14_mixed_whitespace_block_start (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "wlan0"); + expected_block_add_key (b, expected_key_new ("inet", "manual")); + expected_add_block (e, b); + b = expected_block_new ("iface", "wlan-adpm"); + expected_block_add_key (b, expected_key_new ("inet", "dhcp")); + expected_add_block (e, b); + b = expected_block_new ("iface", "wlan-default"); + expected_block_add_key (b, expected_key_new ("inet", "dhcp")); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test14"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test15_trailing_space (const char *path) +{ + Expected *e; + ExpectedBlock *b; + + e = expected_new (); + b = expected_block_new ("iface", "bnep0"); + expected_block_add_key (b, expected_key_new ("inet", "static")); + expected_add_block (e, b); + + init_ifparser_with_file (path, "test15"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +static void +test16_missing_newline (const char *path) +{ + Expected *e; + + e = expected_new (); + expected_add_block (e, expected_block_new ("mapping", "eth0")); + + init_ifparser_with_file (path, "test16"); + compare_expected_to_ifparser (e); + + ifparser_destroy (); + expected_free (e); +} + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else +typedef void (*TCFunc)(void); +#endif + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + + g_test_init (&argc, &argv, NULL); + + suite = g_test_get_root (); + + if (0) + dump_blocks (); + + g_test_suite_add (suite, TESTCASE (test1_ignore_line_before_first_block, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test2_wrapped_line, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test3_wrapped_multiline_multiarg, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test4_allow_auto_is_auto, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test5_allow_auto_multiarg, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test6_mixed_whitespace, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test7_long_line, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test8_long_line_wrapped, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test9_wrapped_lines_in_block, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test11_complex_wrap, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test12_complex_wrap_split_word, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test13_more_mixed_whitespace, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test14_mixed_whitespace_block_start, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test15_trailing_space, TEST_ENI_DIR)); + g_test_suite_add (suite, TESTCASE (test16_missing_newline, TEST_ENI_DIR)); + + return g_test_run (); +} + diff --git a/system-settings/plugins/ifupdown/tests/test1 b/system-settings/plugins/ifupdown/tests/test1 new file mode 100644 index 0000000000..74c23b457d --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test1 @@ -0,0 +1,6 @@ +# case 1: line before 1st block (must be ignored) +address 10.250.2.3 + +auto eth0 +iface eth0 inet dhcp + diff --git a/system-settings/plugins/ifupdown/tests/test11 b/system-settings/plugins/ifupdown/tests/test11 new file mode 100644 index 0000000000..89561dd7df --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test11 @@ -0,0 +1,5 @@ +iface pppoe inet manual +# case 11: wrapped line (without leading space on the wrapped part, wrap within a multi-word value) + pre-up /sbin/ifconfig \ +eth0 up + diff --git a/system-settings/plugins/ifupdown/tests/test12 b/system-settings/plugins/ifupdown/tests/test12 new file mode 100644 index 0000000000..6096842e1e --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test12 @@ -0,0 +1,5 @@ +iface pppoe inet manual +# case 12: wrapped line, splitting a word (must be joined again) + up ifup ppp0\ +=dsl + diff --git a/system-settings/plugins/ifupdown/tests/test13 b/system-settings/plugins/ifupdown/tests/test13 new file mode 100644 index 0000000000..c001f7ef1b --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test13 @@ -0,0 +1,3 @@ +# case 13: variations of tabs & spaces +iface dsl inet ppp + diff --git a/system-settings/plugins/ifupdown/tests/test14 b/system-settings/plugins/ifupdown/tests/test14 new file mode 100644 index 0000000000..4a153ab3b5 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test14 @@ -0,0 +1,5 @@ +# case 14: variations of tabs and spaces (all must be recognized as lines starting an iface block) +iface wlan0 inet manual + iface wlan-adpm inet dhcp +iface wlan-default inet dhcp + diff --git a/system-settings/plugins/ifupdown/tests/test15 b/system-settings/plugins/ifupdown/tests/test15 new file mode 100644 index 0000000000..c3ceca2409 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test15 @@ -0,0 +1,3 @@ +# case 15: trailing space (must be ignored) +iface bnep0 inet static + diff --git a/system-settings/plugins/ifupdown/tests/test16 b/system-settings/plugins/ifupdown/tests/test16 new file mode 100644 index 0000000000..f4f74fb5a1 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test16 @@ -0,0 +1,2 @@ +# case 16: last line that is not followed by LF (added with 'echo -n "mapping eth0" >> /e/n/i') +mapping eth0
\ No newline at end of file diff --git a/system-settings/plugins/ifupdown/tests/test2 b/system-settings/plugins/ifupdown/tests/test2 new file mode 100644 index 0000000000..7462b35268 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test2 @@ -0,0 +1,4 @@ +# case 2: wrapped line +auto \ +lo + diff --git a/system-settings/plugins/ifupdown/tests/test3 b/system-settings/plugins/ifupdown/tests/test3 new file mode 100644 index 0000000000..f6293bbdc6 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test3 @@ -0,0 +1,5 @@ +# case 3: line wrapped over multiple lines & multi-argument allow-* +allow-hotplug eth0 \ + wlan0 \ + bnep0 + diff --git a/system-settings/plugins/ifupdown/tests/test4 b/system-settings/plugins/ifupdown/tests/test4 new file mode 100644 index 0000000000..46a40bc9c2 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test4 @@ -0,0 +1,3 @@ +# case 4: 'allow-auto' is synonymous to 'auto' +allow-auto eth0 + diff --git a/system-settings/plugins/ifupdown/tests/test5 b/system-settings/plugins/ifupdown/tests/test5 new file mode 100644 index 0000000000..b69fc42bd0 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test5 @@ -0,0 +1,3 @@ +# case 5: multi-argument allow-* (even worse: trailing space) +allow-hotplug eth0 wlan0 + diff --git a/system-settings/plugins/ifupdown/tests/test6 b/system-settings/plugins/ifupdown/tests/test6 new file mode 100644 index 0000000000..50ac69bd98 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test6 @@ -0,0 +1,3 @@ +# case 6: mix between tabs and spaces + iface lo inet loopback + diff --git a/system-settings/plugins/ifupdown/tests/test7 b/system-settings/plugins/ifupdown/tests/test7 new file mode 100644 index 0000000000..03cb131a98 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test7 @@ -0,0 +1,3 @@ +# case 7: over-long line (must be ignored completely) +123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 + diff --git a/system-settings/plugins/ifupdown/tests/test8 b/system-settings/plugins/ifupdown/tests/test8 new file mode 100644 index 0000000000..311f7e15a4 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test8 @@ -0,0 +1,5 @@ +# case 8: over-long line that wraps to consecutive lines (must be ignored completely) +123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 \ +allow-test eth0 \ +eth0 + diff --git a/system-settings/plugins/ifupdown/tests/test9 b/system-settings/plugins/ifupdown/tests/test9 new file mode 100644 index 0000000000..7d94563af9 --- /dev/null +++ b/system-settings/plugins/ifupdown/tests/test9 @@ -0,0 +1,10 @@ +iface eth0 inet static +# case 9: wrapped lines inside a block (to be on the safe side) + address \ + 10.250.2.3 + netmask \ + 255.255.255.192 + + broadcast 10.250.2.63 + gateway 10.250.2.50 + diff --git a/system-settings/plugins/keyfile/Makefile.am b/system-settings/plugins/keyfile/Makefile.am index ca4d3cd3f3..128775ee97 100644 --- a/system-settings/plugins/keyfile/Makefile.am +++ b/system-settings/plugins/keyfile/Makefile.am @@ -1,40 +1,58 @@ -SUBDIRS=io tests +SUBDIRS=. tests INCLUDES = \ -I$(top_srcdir)/src/system-settings \ -I$(top_srcdir)/include \ -I$(top_srcdir)/libnm-util \ - -I$(top_srcdir)/libnm-glib \ - -I$(top_srcdir)/system-settings/plugins/keyfile/io + -I$(top_srcdir)/libnm-glib pkglib_LTLIBRARIES = libnm-settings-plugin-keyfile.la +noinst_LTLIBRARIES = libkeyfile-io.la + +libkeyfile_io_la_SOURCES = \ + reader.c \ + reader.h \ + writer.c \ + writer.h \ + errors.c \ + utils.c \ + utils.h \ + common.h + +libkeyfile_io_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DG_DISABLE_DEPRECATED + +libkeyfile_io_la_LIBADD = $(GLIB_LIBS) + libnm_settings_plugin_keyfile_la_SOURCES = \ nm-keyfile-connection.c \ nm-keyfile-connection.h \ plugin.c \ plugin.h -keyfiledir=$(sysconfdir)/NetworkManager/system-connections - libnm_settings_plugin_keyfile_la_CPPFLAGS = \ $(GLIB_CFLAGS) \ $(GMODULE_CFLAGS) \ $(DBUS_CFLAGS) \ -DSYSCONFDIR=\"$(sysconfdir)\" \ - -DG_DISABLE_DEPRECATED \ - -DKEYFILE_DIR=\""$(keyfiledir)"\" + -DG_DISABLE_DEPRECATED libnm_settings_plugin_keyfile_la_LDFLAGS = -module -avoid-version libnm_settings_plugin_keyfile_la_LIBADD = \ $(top_builddir)/libnm-util/libnm-util.la \ $(top_builddir)/libnm-glib/libnm-glib.la \ - $(top_builddir)/system-settings/plugins/keyfile/io/libkeyfile-io.la \ + libkeyfile-io.la \ $(GLIB_LIBS) \ $(GMODULE_LIBS) \ $(DBUS_LIBS) \ $(GIO_LIBS) +keyfiledir=$(sysconfdir)/NetworkManager/system-connections + install-data-hook: $(mkinstalldirs) -m 0755 $(DESTDIR)$(keyfiledir) diff --git a/system-settings/plugins/keyfile/common.h b/system-settings/plugins/keyfile/common.h new file mode 100644 index 0000000000..6c8f9cebb8 --- /dev/null +++ b/system-settings/plugins/keyfile/common.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 - 2010 Red Hat, Inc. + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include <glib.h> + +#define SWP_TAG ".swp" +#define SWPX_TAG ".swpx" + +#define KEYFILE_PLUGIN_NAME "keyfile" +#define KEYFILE_PLUGIN_INFO "(c) 2007 - 2010 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." + +#define KEYFILE_DIR SYSCONFDIR "/NetworkManager/system-connections" + +#define VPN_SECRETS_GROUP "vpn-secrets" + +#define KEYFILE_PLUGIN_ERROR (keyfile_plugin_error_quark ()) +GQuark keyfile_plugin_error_quark (void); + +#endif /* __COMMON_H__ */ + diff --git a/system-settings/plugins/keyfile/errors.c b/system-settings/plugins/keyfile/errors.c new file mode 100644 index 0000000000..e2e97690f1 --- /dev/null +++ b/system-settings/plugins/keyfile/errors.c @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 - 2010 Red Hat, Inc. + */ + +#include <glib.h> +#include "common.h" + +GQuark +keyfile_plugin_error_quark (void) +{ + static GQuark error_quark = 0; + + if (G_UNLIKELY (error_quark == 0)) + error_quark = g_quark_from_static_string ("keyfile-plugin-error-quark"); + + return error_quark; +} + + diff --git a/system-settings/plugins/keyfile/io/Makefile.am b/system-settings/plugins/keyfile/io/Makefile.am deleted file mode 100644 index 9333c50976..0000000000 --- a/system-settings/plugins/keyfile/io/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -INCLUDES = \ - -I$(top_srcdir)/system-settings/src \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/libnm-util \ - -I$(top_srcdir)/libnm-glib - -noinst_LTLIBRARIES = libkeyfile-io.la - -libkeyfile_io_la_SOURCES = \ - reader.h \ - reader.c \ - writer.h \ - writer.c - -libkeyfile_io_la_CPPFLAGS = \ - $(GLIB_CFLAGS) \ - $(DBUS_CFLAGS) - -libkeyfile_io_la_LIBADD = $(GLIB_LIBS) - diff --git a/system-settings/plugins/keyfile/nm-keyfile-connection.c b/system-settings/plugins/keyfile/nm-keyfile-connection.c index ed56d69d2e..3c27a54c79 100644 --- a/system-settings/plugins/keyfile/nm-keyfile-connection.c +++ b/system-settings/plugins/keyfile/nm-keyfile-connection.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. */ #include <string.h> @@ -26,10 +26,12 @@ #include <nm-utils.h> #include <nm-settings-connection-interface.h> +#include "nm-system-config-interface.h" #include "nm-dbus-glib-types.h" #include "nm-keyfile-connection.h" #include "reader.h" #include "writer.h" +#include "common.h" static NMSettingsConnectionInterface *parent_settings_connection_iface; @@ -53,13 +55,55 @@ enum { }; NMKeyfileConnection * -nm_keyfile_connection_new (const char *filename) +nm_keyfile_connection_new (const char *filename, GError **error) { + GObject *object; + NMKeyfileConnectionPrivate *priv; + NMSettingConnection *s_con; + NMConnection *tmp; + g_return_val_if_fail (filename != NULL, NULL); - return (NMKeyfileConnection *) g_object_new (NM_TYPE_KEYFILE_CONNECTION, - NM_KEYFILE_CONNECTION_FILENAME, filename, - NULL); + tmp = connection_from_file (filename, error); + if (!tmp) + return NULL; + + object = (GObject *) g_object_new (NM_TYPE_KEYFILE_CONNECTION, + NM_KEYFILE_CONNECTION_FILENAME, filename, + NULL); + if (!object) { + g_object_unref (tmp); + return NULL; + } + + priv = NM_KEYFILE_CONNECTION_GET_PRIVATE (object); + g_assert (priv->filename); + + /* Update our settings with what was read from the file */ + nm_sysconfig_connection_update (NM_SYSCONFIG_CONNECTION (object), tmp, FALSE, NULL); + g_object_unref (tmp); + + /* if for some reason the connection didn't have a UUID, add one */ + s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (object), NM_TYPE_SETTING_CONNECTION); + if (s_con && !nm_setting_connection_get_uuid (s_con)) { + GError *write_error = NULL; + char *uuid; + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, NM_SETTING_CONNECTION_UUID, uuid, NULL); + g_free (uuid); + + if (!write_connection (NM_CONNECTION (object), KEYFILE_DIR, 0, 0, NULL, &write_error)) { + PLUGIN_WARN (KEYFILE_PLUGIN_NAME, + "Couldn't update connection %s with a UUID: (%d) %s", + nm_setting_connection_get_id (s_con), + write_error ? write_error->code : -1, + (write_error && write_error->message) ? write_error->message : "(unknown)"); + g_propagate_error (error, write_error); + } + } + + return NM_KEYFILE_CONNECTION (object); } const char * @@ -78,22 +122,21 @@ update (NMSettingsConnectionInterface *connection, NMKeyfileConnectionPrivate *priv = NM_KEYFILE_CONNECTION_GET_PRIVATE (connection); char *filename = NULL; GError *error = NULL; - gboolean success; - success = write_connection (NM_CONNECTION (connection), KEYFILE_DIR, 0, 0, &filename, &error); - if (success && filename && strcmp (priv->filename, filename)) { + if (!write_connection (NM_CONNECTION (connection), KEYFILE_DIR, 0, 0, &filename, &error)) { + callback (connection, error, user_data); + g_clear_error (&error); + return FALSE; + } + + if (g_strcmp0 (priv->filename, filename)) { /* Update the filename if it changed */ g_free (priv->filename); priv->filename = filename; - success = parent_settings_connection_iface->update (connection, callback, user_data); - } else { - callback (connection, error, user_data); - if (error) - g_error_free (error); + } else g_free (filename); - } - return success; + return parent_settings_connection_iface->update (connection, callback, user_data); } static gboolean @@ -123,56 +166,6 @@ nm_keyfile_connection_init (NMKeyfileConnection *connection) { } -static GObject * -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - NMKeyfileConnectionPrivate *priv; - NMSettingConnection *s_con; - NMConnection *tmp; - - object = G_OBJECT_CLASS (nm_keyfile_connection_parent_class)->constructor (type, n_construct_params, construct_params); - - if (!object) - return NULL; - - priv = NM_KEYFILE_CONNECTION_GET_PRIVATE (object); - - g_assert (priv->filename); - - tmp = connection_from_file (priv->filename); - if (!tmp) { - g_object_unref (object); - return NULL; - } - - nm_sysconfig_connection_update (NM_SYSCONFIG_CONNECTION (object), tmp, FALSE, NULL); - g_object_unref (tmp); - - /* if for some reason the connection didn't have a UUID, add one */ - s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (object), NM_TYPE_SETTING_CONNECTION); - if (s_con && !nm_setting_connection_get_uuid (s_con)) { - GError *error = NULL; - char *uuid; - - uuid = nm_utils_uuid_generate (); - g_object_set (s_con, NM_SETTING_CONNECTION_UUID, uuid, NULL); - g_free (uuid); - - if (!write_connection (NM_CONNECTION (object), KEYFILE_DIR, 0, 0, NULL, &error)) { - g_warning ("Couldn't update connection %s with a UUID: (%d) %s", - nm_setting_connection_get_id (s_con), - error ? error->code : 0, - (error && error->message) ? error->message : "unknown"); - g_error_free (error); - } - } - - return object; -} - static void finalize (GObject *object) { @@ -226,7 +219,6 @@ nm_keyfile_connection_class_init (NMKeyfileConnectionClass *keyfile_connection_c g_type_class_add_private (keyfile_connection_class, sizeof (NMKeyfileConnectionPrivate)); /* Virtual methods */ - object_class->constructor = constructor; object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; diff --git a/system-settings/plugins/keyfile/nm-keyfile-connection.h b/system-settings/plugins/keyfile/nm-keyfile-connection.h index 3b4e050c2f..68e795a6ce 100644 --- a/system-settings/plugins/keyfile/nm-keyfile-connection.h +++ b/system-settings/plugins/keyfile/nm-keyfile-connection.h @@ -45,7 +45,7 @@ typedef struct { GType nm_keyfile_connection_get_type (void); -NMKeyfileConnection *nm_keyfile_connection_new (const char *filename); +NMKeyfileConnection *nm_keyfile_connection_new (const char *filename, GError **error); const char *nm_keyfile_connection_get_filename (NMKeyfileConnection *self); diff --git a/system-settings/plugins/keyfile/plugin.c b/system-settings/plugins/keyfile/plugin.c index 36f47ccdc5..da6456d983 100644 --- a/system-settings/plugins/keyfile/plugin.c +++ b/system-settings/plugins/keyfile/plugin.c @@ -16,13 +16,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. */ #include <config.h> #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> +#include <netinet/ether.h> #include <string.h> #include <gmodule.h> @@ -38,9 +39,8 @@ #include "nm-system-config-interface.h" #include "nm-keyfile-connection.h" #include "writer.h" - -#define KEYFILE_PLUGIN_NAME "keyfile" -#define KEYFILE_PLUGIN_INFO "(c) 2007 - 2008 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." +#include "common.h" +#include "utils.h" #define CONF_FILE SYSCONFDIR "/NetworkManager/NetworkManager.conf" #define OLD_CONF_FILE SYSCONFDIR "/NetworkManager/nm-system-settings.conf" @@ -74,31 +74,52 @@ read_connections (NMSystemConfigInterface *config) { SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (config); GDir *dir; - GError *err = NULL; + GError *error = NULL; + const char *item; + + dir = g_dir_open (KEYFILE_DIR, 0, &error); + if (!dir) { + PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "Cannot read directory '%s': (%d) %s", + KEYFILE_DIR, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } - dir = g_dir_open (KEYFILE_DIR, 0, &err); - if (dir) { - const char *item; + while ((item = g_dir_read_name (dir))) { + NMKeyfileConnection *connection; + char *full_path; - while ((item = g_dir_read_name (dir))) { - NMKeyfileConnection *connection; - char *full_path; + if (utils_should_ignore_file (item)) + continue; - full_path = g_build_filename (KEYFILE_DIR, item, NULL); - connection = nm_keyfile_connection_new (full_path); - if (connection) { - g_hash_table_insert (priv->hash, - (gpointer) nm_keyfile_connection_get_filename (connection), - connection); - } - g_free (full_path); - } + full_path = g_build_filename (KEYFILE_DIR, item, NULL); + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "parsing %s ... ", item); + connection = nm_keyfile_connection_new (full_path, &error); + if (connection) { + NMSettingConnection *s_con; + const char *cid; - g_dir_close (dir); - } else { - g_warning ("Can not read directory '%s': %s", KEYFILE_DIR, err->message); - g_error_free (err); + s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + cid = nm_setting_connection_get_id (s_con); + g_assert (cid); + + g_hash_table_insert (priv->hash, + (gpointer) nm_keyfile_connection_get_filename (connection), + connection); + + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, " read connection '%s'", cid); + } else { + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, " error: %s", + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + } + g_free (full_path); } + g_dir_close (dir); } typedef struct { @@ -148,28 +169,46 @@ update_connection_settings (NMKeyfileConnection *orig, /* Monitoring */ static void +remove_connection (SCPluginKeyfile *self, + NMKeyfileConnection *connection, + const char *name) +{ + g_return_if_fail (connection != NULL); + g_return_if_fail (name != NULL); + + /* Removing from the hash table should drop the last reference */ + g_object_ref (connection); + g_hash_table_remove (SC_PLUGIN_KEYFILE_GET_PRIVATE (self)->hash, name); + g_signal_emit_by_name (connection, "removed"); + g_object_unref (connection); +} + +static void dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) { NMSystemConfigInterface *config = NM_SYSTEM_CONFIG_INTERFACE (user_data); SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (config); char *name; NMKeyfileConnection *connection; + GError *error = NULL; name = g_file_get_path (file); + if (utils_should_ignore_file (name)) { + g_free (name); + return; + } + connection = g_hash_table_lookup (priv->hash, name); switch (event_type) { case G_FILE_MONITOR_EVENT_DELETED: if (connection) { - /* Removing from the hash table should drop the last reference */ - g_object_ref (connection); - g_hash_table_remove (priv->hash, name); - g_signal_emit_by_name (connection, "removed"); - g_object_unref (connection); + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "removed %s.", name); + remove_connection (SC_PLUGIN_KEYFILE (config), connection, name); } break; case G_FILE_MONITOR_EVENT_CREATED: @@ -178,14 +217,27 @@ dir_changed (GFileMonitor *monitor, /* Update */ NMKeyfileConnection *tmp; - tmp = (NMKeyfileConnection *) nm_keyfile_connection_new (name); + tmp = nm_keyfile_connection_new (name, &error); if (tmp) { - update_connection_settings (connection, tmp); + if (!nm_connection_compare (NM_CONNECTION (connection), + NM_CONNECTION (tmp), + NM_SETTING_COMPARE_FLAG_EXACT)) { + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", name); + update_connection_settings (connection, tmp); + } g_object_unref (tmp); + } else { + /* Error; remove the connection */ + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, " error: %s", + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + remove_connection (SC_PLUGIN_KEYFILE (config), connection, name); } } else { + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", name); + /* New */ - connection = nm_keyfile_connection_new (name); + connection = nm_keyfile_connection_new (name, &error); if (connection) { NMSettingConnection *s_con; const char *connection_uuid; @@ -234,6 +286,10 @@ dir_changed (GFileMonitor *monitor, connection); g_signal_emit_by_name (config, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, connection); } + } else { + PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, " error: %s", + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); } } break; @@ -363,8 +419,23 @@ get_unmanaged_specs (NMSystemConfigInterface *config) udis = g_strsplit (str, ";", -1); g_free (str); - for (i = 0; udis[i] != NULL; i++) - specs = g_slist_append (specs, udis[i]); + for (i = 0; udis[i] != NULL; i++) { + /* Verify unmanaged specification and add it to the list */ + if (strlen (udis[i]) > 4 && !strncmp (udis[i], "mac:", 4) && ether_aton (udis[i] + 4)) { + char *p = udis[i]; + + /* To accept uppercase MACs in configuration file, we have to convert values to lowercase here. + * Unmanaged MACs in specs are always in lowercase. */ + while (*p) { + *p = g_ascii_tolower (*p); + p++; + } + specs = g_slist_append (specs, udis[i]); + } else { + g_warning ("Error in file '%s': invalid unmanaged-devices entry: '%s'", priv->conf_file, udis[i]); + g_free (udis[i]); + } + } g_free (udis); /* Yes, g_free, not g_strfreev because we need the strings in the list */ } diff --git a/system-settings/plugins/keyfile/io/reader.c b/system-settings/plugins/keyfile/reader.c index a71c05c577..841315d9c6 100644 --- a/system-settings/plugins/keyfile/io/reader.c +++ b/system-settings/plugins/keyfile/reader.c @@ -32,12 +32,19 @@ #include <nm-setting-connection.h> #include <nm-setting-wired.h> #include <nm-setting-wireless.h> +#include <nm-setting-bluetooth.h> +#include <nm-setting-serial.h> +#include <nm-setting-gsm.h> +#include <nm-setting-cdma.h> +#include <nm-setting-ppp.h> #include <arpa/inet.h> #include <netinet/ether.h> #include <string.h> +#include <ctype.h> #include "nm-dbus-glib-types.h" #include "reader.h" +#include "common.h" static gboolean read_array_of_uint (GKeyFile *file, @@ -725,6 +732,68 @@ read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key) g_strfreev (keys); } +static void +ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile) +{ + const char *setting_name = nm_setting_get_name (setting); + GByteArray *array = NULL; + char *p, *tmp_string; + gint *tmp_list; + gsize length; + int i; + + /* New format: just a string. We try parsing the new format if there are + * no ';' in the string or it's not just numbers. + */ + p = tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL); + if (tmp_string) { + gboolean new_format = FALSE; + + if (strchr (p, ';') == NULL) + new_format = TRUE; + else { + new_format = TRUE; + while (p && *p) { + if (!isdigit (*p++)) { + new_format = FALSE; + break; + } + } + } + + if (new_format) { + array = g_byte_array_sized_new (strlen (tmp_string)); + g_byte_array_append (array, (guint8 *) tmp_string, strlen (tmp_string)); + goto done; + } + } + g_free (tmp_string); + + /* Old format; list of ints */ + tmp_list = g_key_file_get_integer_list (keyfile, setting_name, key, &length, NULL); + array = g_byte_array_sized_new (length); + for (i = 0; i < length; i++) { + int val = tmp_list[i]; + unsigned char v = (unsigned char) (val & 0xFF); + + if (val < 0 || val > 255) { + g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not " + " between 0 and 255 inclusive)", __func__, setting_name, + key, val); + } else + g_byte_array_append (array, (const unsigned char *) &v, sizeof (v)); + } + g_free (tmp_list); + +done: + if (array->len) + g_object_set (setting, key, array, NULL); + else { + g_warning ("%s: ignoring invalid SSID for %s / %s", + __func__, setting_name, key); + } + g_byte_array_free (array, TRUE); +} typedef struct { const char *setting_name; @@ -733,7 +802,7 @@ typedef struct { void (*parser) (NMSetting *setting, const char *key, GKeyFile *keyfile); } KeyParser; -/* A table of keys that require further parsing/conversion becuase they are +/* A table of keys that require further parsing/conversion because they are * stored in a format that can't be automatically read using the key's type. * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored @@ -768,14 +837,30 @@ static KeyParser key_parsers[] = { NM_SETTING_WIRED_MAC_ADDRESS, TRUE, mac_address_parser }, + { NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + TRUE, + mac_address_parser }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS, TRUE, mac_address_parser }, { NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + TRUE, + mac_address_parser }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID, TRUE, mac_address_parser }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_BDADDR, + TRUE, + mac_address_parser }, + { NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID, + TRUE, + ssid_parser }, { NULL, NULL, FALSE } }; @@ -969,65 +1054,120 @@ read_vpn_secrets (GKeyFile *file, NMSettingVPN *s_vpn) } NMConnection * -connection_from_file (const char *filename) +connection_from_file (const char *filename, GError **error) { GKeyFile *key_file; struct stat statbuf; gboolean bad_owner, bad_permissions; NMConnection *connection = NULL; - GError *err = NULL; + NMSettingConnection *s_con; + NMSettingBluetooth *s_bt; + NMSetting *setting; + gchar **groups; + gsize length; + int i; + gboolean vpn_secrets = FALSE; + const char *ctype, *tmp; + GError *verify_error = NULL; - if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) + if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) { + g_set_error_literal (error, KEYFILE_PLUGIN_ERROR, 0, + "File did not exist or was not a regular file"); return NULL; + } bad_owner = getuid () != statbuf.st_uid; bad_permissions = statbuf.st_mode & 0077; if (bad_owner || bad_permissions) { - g_warning ("Ignoring insecure configuration file '%s'", filename); + g_set_error (error, KEYFILE_PLUGIN_ERROR, 0, + "File permissions (%o) or owner (%d) were insecure", + statbuf.st_mode, statbuf.st_uid); return NULL; } key_file = g_key_file_new (); - if (g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &err)) { - gchar **groups; - gsize length; - int i; - gboolean vpn_secrets = FALSE; + if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) + goto out; - connection = nm_connection_new (); + connection = nm_connection_new (); - groups = g_key_file_get_groups (key_file, &length); - for (i = 0; i < length; i++) { - NMSetting *setting; + groups = g_key_file_get_groups (key_file, &length); + for (i = 0; i < length; i++) { + /* Only read out secrets when needed */ + if (!strcmp (groups[i], VPN_SECRETS_GROUP)) { + vpn_secrets = TRUE; + continue; + } - /* Only read out secrets when needed */ - if (!strcmp (groups[i], VPN_SECRETS_GROUP)) { - vpn_secrets = TRUE; - continue; - } + setting = read_setting (key_file, groups[i]); + if (setting) + nm_connection_add_setting (connection, setting); + } - setting = read_setting (key_file, groups[i]); - if (setting) - nm_connection_add_setting (connection, setting); - } + /* Make sure that we have the base device type setting even if + * the keyfile didn't include it, which can happen when the base + * device type setting is all default values (like ethernet). + */ + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + if (s_con) { + ctype = nm_setting_connection_get_connection_type (s_con); + setting = nm_connection_get_setting_by_name (connection, ctype); + if (ctype) { + gboolean add_serial = FALSE; + NMSetting *new_setting = NULL; + + if (!setting && !strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME)) + new_setting = nm_setting_wired_new (); + else if (!strcmp (ctype, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + s_bt = (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH); + if (s_bt) { + tmp = nm_setting_bluetooth_get_connection_type (s_bt); + if (tmp && !strcmp (tmp, NM_SETTING_BLUETOOTH_TYPE_DUN)) + add_serial = TRUE; + } + } else if (!strcmp (ctype, NM_SETTING_GSM_SETTING_NAME)) + add_serial = TRUE; + else if (!strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME)) + add_serial = TRUE; - /* Handle vpn secrets after the 'vpn' setting was read */ - if (vpn_secrets) { - NMSettingVPN *s_vpn; + /* Bluetooth DUN, GSM, and CDMA connections require a serial setting */ + if (add_serial && !nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL)) + new_setting = nm_setting_serial_new (); - s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN); - if (s_vpn) - read_vpn_secrets (key_file, s_vpn); + if (new_setting) + nm_connection_add_setting (connection, new_setting); } + } - g_strfreev (groups); - } else { - g_warning ("Error parsing file '%s': %s", filename, err->message); - g_error_free (err); + /* Serial connections require a PPP setting too */ + if (nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL)) { + if (!nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP)) + nm_connection_add_setting (connection, nm_setting_ppp_new ()); } - g_key_file_free (key_file); + /* Handle vpn secrets after the 'vpn' setting was read */ + if (vpn_secrets) { + NMSettingVPN *s_vpn; + + s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN); + if (s_vpn) + read_vpn_secrets (key_file, s_vpn); + } + g_strfreev (groups); + + /* Verify the connection */ + if (!nm_connection_verify (connection, &verify_error)) { + g_set_error (error, KEYFILE_PLUGIN_ERROR, 0, + "invalid or missing connection property '%s'", + (verify_error && verify_error->message) ? verify_error->message : "(unknown)"); + g_clear_error (&verify_error); + g_object_unref (connection); + connection = NULL; + } + +out: + g_key_file_free (key_file); return connection; } diff --git a/system-settings/plugins/keyfile/io/reader.h b/system-settings/plugins/keyfile/reader.h index beac866a25..3572715495 100644 --- a/system-settings/plugins/keyfile/io/reader.h +++ b/system-settings/plugins/keyfile/reader.h @@ -22,11 +22,9 @@ #ifndef _KEYFILE_PLUGIN_READER_H #define _KEYFILE_PLUGIN_READER_H -#define VPN_SECRETS_GROUP "vpn-secrets" - #include <glib.h> #include <nm-connection.h> -NMConnection *connection_from_file (const char *filename); +NMConnection *connection_from_file (const char *filename, GError **error); #endif /* _KEYFILE_PLUGIN_READER_H */ diff --git a/system-settings/plugins/keyfile/tests/Makefile.am b/system-settings/plugins/keyfile/tests/Makefile.am index a0af14b269..af76667867 100644 --- a/system-settings/plugins/keyfile/tests/Makefile.am +++ b/system-settings/plugins/keyfile/tests/Makefile.am @@ -4,7 +4,7 @@ INCLUDES = \ -I$(top_srcdir)/include \ -I$(top_srcdir)/libnm-util \ -I$(top_srcdir)/libnm-glib \ - -I$(top_srcdir)/system-settings/plugins/keyfile/io + -I$(top_srcdir)/system-settings/plugins/keyfile noinst_PROGRAMS = test-keyfile @@ -18,7 +18,7 @@ test_keyfile_CPPFLAGS = \ -DTEST_SCRATCH_DIR=\"$(abs_builddir)/keyfiles\" test_keyfile_LDADD = \ - $(top_builddir)/system-settings/plugins/keyfile/io/libkeyfile-io.la \ + $(top_builddir)/system-settings/plugins/keyfile/libkeyfile-io.la \ $(top_builddir)/libnm-glib/libnm-glib.la \ $(top_builddir)/libnm-util/libnm-util.la \ $(DBUS_LIBS) diff --git a/system-settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_BT b/system-settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_BT new file mode 100644 index 0000000000..cc8a9ee390 --- /dev/null +++ b/system-settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_BT @@ -0,0 +1,23 @@ + +[connection] +id=AT&T Data Connect BT +uuid=089130ab-ce28-46e4-ad77-d44869b03d19 +type=bluetooth +autoconnect=false + +[ipv4] +method=auto + +[gsm] +number=*99# +username=ISP@CINGULARGPRS.COM +password=CINGULAR1 +apn=ISP.CINGULAR + +[serial] +baud=115200 + +[bluetooth] +bdaddr=00:11:22:33:44:55 +type=dun + diff --git a/system-settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain b/system-settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain new file mode 100644 index 0000000000..236cca0edf --- /dev/null +++ b/system-settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain @@ -0,0 +1,20 @@ + +[connection] +id=AT&T Data Connect +uuid=15d742f1-2b5a-421e-9f27-fcb1fc26d72c +type=gsm +autoconnect=false + +[ipv4] +method=auto + +[gsm] +number=*99# +username=ISP@CINGULARGPRS.COM +password=CINGULAR1 +apn=ISP.CINGULAR +network-id=24005 +pin=2345 + +[serial] +baud=115200 diff --git a/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am b/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am index bf5a5ab060..d6ee018340 100644 --- a/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am +++ b/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am @@ -3,7 +3,10 @@ EXTRA_DIST = \ Test_GSM_Connection \ Test_Wireless_Connection \ Test_Wired_Connection_MAC_Case \ - Test_Wired_Connection_IP6 + Test_Wired_Connection_IP6 \ + ATT_Data_Connect_BT \ + ATT_Data_Connect_Plain \ + Test_String_SSID check-local: @for f in $(EXTRA_DIST); do \ diff --git a/system-settings/plugins/keyfile/tests/keyfiles/Test_String_SSID b/system-settings/plugins/keyfile/tests/keyfiles/Test_String_SSID new file mode 100644 index 0000000000..4a3b56d24f --- /dev/null +++ b/system-settings/plugins/keyfile/tests/keyfiles/Test_String_SSID @@ -0,0 +1,11 @@ +[connection] +id=Test +uuid=2f962388-e5f3-45af-a62c-ac220b8f7baa +type=802-11-wireless + +[802-11-wireless] +ssid=blah blah ssid 1234 + +[ipv4] +method=auto + diff --git a/system-settings/plugins/keyfile/tests/test-keyfile.c b/system-settings/plugins/keyfile/tests/test-keyfile.c index aae823642b..05131c8fe8 100644 --- a/system-settings/plugins/keyfile/tests/test-keyfile.c +++ b/system-settings/plugins/keyfile/tests/test-keyfile.c @@ -27,14 +27,16 @@ #include <arpa/inet.h> #include <sys/socket.h> -#include <dbus/dbus-glib.h> - #include <nm-utils.h> #include <nm-setting-connection.h> #include <nm-setting-wired.h> #include <nm-setting-wireless.h> #include <nm-setting-ip4-config.h> #include <nm-setting-ip6-config.h> +#include <nm-setting-bluetooth.h> +#include <nm-setting-serial.h> +#include <nm-setting-ppp.h> +#include <nm-setting-gsm.h> #include "nm-test-helpers.h" @@ -81,7 +83,7 @@ test_read_valid_wired_connection (void) NMIP6Address *ip6_addr; NMIP6Route *ip6_route; - connection = connection_from_file (TEST_WIRED_FILE); + connection = connection_from_file (TEST_WIRED_FILE, NULL); ASSERT (connection != NULL, "connection-read", "failed to read %s", TEST_WIRED_FILE); @@ -583,7 +585,7 @@ test_write_wired_connection (void) const char *address2_gw = "1.2.1.1"; const char *route1 = "10.10.10.2"; const char *route1_nh = "10.10.10.1"; - const char *route2 = "0.0.0.0"; + const char *route2 = "1.1.1.1"; const char *route2_nh = "1.2.1.1"; const char *dns6_1 = "1::cafe"; const char *dns6_2 = "2::cafe"; @@ -591,7 +593,7 @@ test_write_wired_connection (void) const char *address6_2 = "dcba::beef"; const char *route6_1 = "1:2:3:4:5:6:7:8"; const char *route6_1_nh = "8:7:6:5:4:3:2:1"; - const char *route6_2 = "::"; + const char *route6_2 = "2001::1000"; const char *route6_2_nh = "2001::1111"; guint64 timestamp = 0x12345678L; @@ -700,7 +702,7 @@ test_write_wired_connection (void) "connection-write", "didn't get keyfile name back after writing connection"); /* Read the connection back in and compare it to the one we just wrote out */ - reread = connection_from_file (testfile); + reread = connection_from_file (testfile, NULL); ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, @@ -733,7 +735,7 @@ test_read_ip6_wired_connection (void) const char *expected6_gw1 = "abcd:1234:ffff::cdd1"; NMIP6Address *ip6_addr; - connection = connection_from_file (TEST_WIRED_IP6_FILE); + connection = connection_from_file (TEST_WIRED_IP6_FILE, NULL); ASSERT (connection != NULL, "connection-read", "failed to read %s", TEST_WIRED_IP6_FILE); @@ -960,7 +962,7 @@ test_write_ip6_wired_connection (void) "connection-write", "didn't get keyfile name back after writing connection"); /* Read the connection back in and compare it to the one we just wrote out */ - reread = connection_from_file (testfile); + reread = connection_from_file (testfile, NULL); ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, @@ -989,7 +991,7 @@ test_read_wired_mac_case (void) const char *expected_id = "Test Wired Connection MAC Case"; const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57"; - connection = connection_from_file (TEST_WIRED_MAC_CASE_FILE); + connection = connection_from_file (TEST_WIRED_MAC_CASE_FILE, NULL); ASSERT (connection != NULL, "connection-read", "failed to read %s", TEST_WIRED_MAC_CASE_FILE); @@ -1075,7 +1077,7 @@ test_read_valid_wireless_connection (void) const guint64 expected_timestamp = 1226604314; guint64 timestamp; - connection = connection_from_file (TEST_WIRELESS_FILE); + connection = connection_from_file (TEST_WIRELESS_FILE, NULL); ASSERT (connection != NULL, "connection-read", "failed to read %s", TEST_WIRELESS_FILE); @@ -1278,7 +1280,697 @@ test_write_wireless_connection (void) "connection-write", "didn't get keyfile name back after writing connection"); /* Read the connection back in and compare it to the one we just wrote out */ - reread = connection_from_file (testfile); + reread = connection_from_file (testfile, NULL); + ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "connection-write", "written and re-read connection weren't the same"); + + g_clear_error (&error); + unlink (testfile); + g_free (testfile); + + g_object_unref (reread); + g_object_unref (connection); +} + +#define TEST_STRING_SSID_FILE TEST_KEYFILES_DIR"/Test_String_SSID" + +static void +test_read_string_ssid (void) +{ + NMConnection *connection; + NMSettingWireless *s_wireless; + GError *error = NULL; + const GByteArray *array; + const char *expected_ssid = "blah blah ssid 1234"; + + connection = connection_from_file (TEST_STRING_SSID_FILE, NULL); + ASSERT (connection != NULL, + "connection-read", "failed to read %s", TEST_STRING_SSID_FILE); + + ASSERT (nm_connection_verify (connection, &error), + "connection-verify", "failed to verify %s: %s", TEST_STRING_SSID_FILE, error->message); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + ASSERT (s_wireless != NULL, + "connection-verify-wireless", "failed to verify %s: missing %s setting", + TEST_STRING_SSID_FILE, + NM_SETTING_WIRELESS_SETTING_NAME); + + /* SSID */ + array = nm_setting_wireless_get_ssid (s_wireless); + ASSERT (array != NULL, + "connection-verify-wireless", "failed to verify %s: missing %s / %s key", + TEST_STRING_SSID_FILE, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + ASSERT (memcmp (array->data, expected_ssid, sizeof (expected_ssid)) == 0, + "connection-verify-wireless", "failed to verify %s: unexpected %s / %s key value", + TEST_STRING_SSID_FILE, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + + g_object_unref (connection); +} + +static void +test_write_string_ssid (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingIP4Config *s_ip4; + char *uuid, *testfile = NULL, *tmp; + GByteArray *ssid; + unsigned char tmpssid[] = { 65, 49, 50, 51, 32, 46, 92, 46, 36, 37, 126, 93 }; + gboolean success; + NMConnection *reread; + GError *error = NULL; + pid_t owner_grp; + uid_t owner_uid; + GKeyFile *keyfile; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "connection-write", "failed to allocate new connection"); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + ASSERT (s_con != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "String SSID Test", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wireless setting */ + + s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + ASSERT (s_wireless != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_WIRELESS_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wireless)); + + ssid = g_byte_array_sized_new (sizeof (tmpssid)); + g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid)); + g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_byte_array_free (ssid, TRUE); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + ASSERT (s_ip4 != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* Write out the connection */ + owner_uid = geteuid (); + owner_grp = getegid (); + success = write_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error); + ASSERT (success == TRUE, + "connection-write", "failed to allocate write keyfile: %s", + error ? error->message : "(none)"); + + ASSERT (testfile != NULL, + "connection-write", "didn't get keyfile name back after writing connection"); + + /* Ensure the SSID was written out as a string */ + keyfile = g_key_file_new (); + ASSERT (g_key_file_load_from_file (keyfile, testfile, 0, NULL) == TRUE, + "string-ssid-verify", "failed to load keyfile to verify"); + tmp = g_key_file_get_string (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, NULL); + ASSERT (tmp, "string-ssid-verify", "failed to load 'ssid' key from file"); + ASSERT (strlen (tmp) == sizeof (tmpssid), + "string-ssid-verify", "reread SSID and expected were different sizes"); + ASSERT (memcmp (tmp, tmpssid, sizeof (tmpssid)) == 0, + "string-ssid-verify", "reread SSID and expected were different"); + g_free (tmp); + g_key_file_free (keyfile); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = connection_from_file (testfile, NULL); + ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "connection-write", "written and re-read connection weren't the same"); + + g_clear_error (&error); + unlink (testfile); + g_free (testfile); + + g_object_unref (reread); + g_object_unref (connection); +} + +#define TEST_BT_DUN_FILE TEST_KEYFILES_DIR"/ATT_Data_Connect_BT" + +static void +test_read_bt_dun_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingBluetooth *s_bluetooth; + NMSettingSerial *s_serial; + NMSettingPPP *s_ppp; + NMSettingGsm *s_gsm; + GError *error = NULL; + const GByteArray *array; + char expected_bdaddr[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + const char *tmp; + const char *expected_id = "AT&T Data Connect BT"; + const char *expected_uuid = "089130ab-ce28-46e4-ad77-d44869b03d19"; + const char *expected_apn = "ISP.CINGULAR"; + const char *expected_username = "ISP@CINGULARGPRS.COM"; + const char *expected_password = "CINGULAR1"; + + connection = connection_from_file (TEST_BT_DUN_FILE, NULL); + ASSERT (connection != NULL, + "connection-read", "failed to read %s", TEST_BT_DUN_FILE); + + ASSERT (nm_connection_verify (connection, &error), + "connection-verify", "failed to verify %s: %s", TEST_BT_DUN_FILE, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "connection-verify-connection", "failed to verify %s: missing %s setting", + TEST_BT_DUN_FILE, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "connection-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_BT_DUN_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_BT_DUN_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* UUID */ + tmp = nm_setting_connection_get_uuid (s_con); + ASSERT (tmp != NULL, + "connection-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_WIRELESS_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_UUID); + ASSERT (strcmp (tmp, expected_uuid) == 0, + "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRELESS_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_UUID); + + /* ===== BLUETOOTH SETTING ===== */ + + s_bluetooth = NM_SETTING_BLUETOOTH (nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH)); + ASSERT (s_bluetooth != NULL, + "connection-verify-bt", "failed to verify %s: missing %s setting", + TEST_WIRELESS_FILE, + NM_SETTING_WIRED_SETTING_NAME); + + /* BDADDR */ + array = nm_setting_bluetooth_get_bdaddr (s_bluetooth); + ASSERT (array != NULL, + "connection-verify-bt", "failed to verify %s: missing %s / %s key", + TEST_BT_DUN_FILE, + NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_BDADDR); + ASSERT (array->len == ETH_ALEN, + "connection-verify-bt", "failed to verify %s: unexpected %s / %s key value length", + TEST_BT_DUN_FILE, + NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_BDADDR); + ASSERT (memcmp (array->data, &expected_bdaddr[0], sizeof (expected_bdaddr)) == 0, + "connection-verify-bt", "failed to verify %s: unexpected %s / %s key value", + TEST_BT_DUN_FILE, + NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_BDADDR); + + /* Type */ + tmp = nm_setting_bluetooth_get_connection_type (s_bluetooth); + ASSERT (tmp != NULL, + "connection-verify-bt", "failed to verify %s: missing %s / %s key", + TEST_BT_DUN_FILE, + NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_TYPE); + ASSERT (strcmp (tmp, NM_SETTING_BLUETOOTH_TYPE_DUN) == 0, + "connection-verify-bt", "failed to verify %s: unexpected %s / %s key value", + TEST_BT_DUN_FILE, + NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_TYPE); + + /* ===== GSM SETTING ===== */ + + s_gsm = NM_SETTING_GSM (nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM)); + ASSERT (s_gsm != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s setting", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME); + + /* APN */ + tmp = nm_setting_gsm_get_apn (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_APN); + ASSERT (strcmp (tmp, expected_apn) == 0, + "connection-verify-bt", "failed to verify %s: unexpected %s / %s key value", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_APN); + + /* Username */ + tmp = nm_setting_gsm_get_username (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_USERNAME); + ASSERT (strcmp (tmp, expected_username) == 0, + "connection-verify-bt", "failed to verify %s: unexpected %s / %s key value", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_USERNAME); + + /* Password */ + tmp = nm_setting_gsm_get_password (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_PASSWORD); + ASSERT (strcmp (tmp, expected_password) == 0, + "connection-verify-bt", "failed to verify %s: unexpected %s / %s key value", + TEST_BT_DUN_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_PASSWORD); + + /* ===== SERIAL SETTING ===== */ + + s_serial = NM_SETTING_SERIAL (nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL)); + ASSERT (s_serial != NULL, + "connection-verify-serial", "failed to verify %s: missing %s setting", + TEST_BT_DUN_FILE, + NM_SETTING_SERIAL_SETTING_NAME); + + /* ===== PPP SETTING ===== */ + + s_ppp = NM_SETTING_PPP (nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP)); + ASSERT (s_ppp != NULL, + "connection-verify-ppp", "failed to verify %s: missing %s setting", + TEST_BT_DUN_FILE, + NM_SETTING_PPP_SETTING_NAME); + + g_object_unref (connection); +} + +static void +test_write_bt_dun_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingBluetooth *s_bt; + NMSettingIP4Config *s_ip4; + NMSettingGsm *s_gsm; + char *uuid; + GByteArray *bdaddr; + unsigned char tmpbdaddr[] = { 0xaa, 0xb9, 0xa1, 0x74, 0x55, 0x44 }; + gboolean success; + NMConnection *reread; + char *testfile = NULL; + GError *error = NULL; + pid_t owner_grp; + uid_t owner_uid; + guint64 timestamp = 0x12344433L; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "connection-write", "failed to allocate new connection"); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + ASSERT (s_con != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "T-Mobile Funkadelic", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + g_free (uuid); + + /* Bluetooth setting */ + + s_bt = NM_SETTING_BLUETOOTH (nm_setting_bluetooth_new ()); + ASSERT (s_bt != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_BLUETOOTH_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_bt)); + + bdaddr = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (bdaddr, &tmpbdaddr[0], sizeof (tmpbdaddr)); + + g_object_set (s_bt, + NM_SETTING_BLUETOOTH_BDADDR, bdaddr, + NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN, + NULL); + + g_byte_array_free (bdaddr, TRUE); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + ASSERT (s_ip4 != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* GSM setting */ + s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); + ASSERT (s_gsm != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_GSM_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + + g_object_set (s_gsm, + NM_SETTING_GSM_APN, "internet2.voicestream.com", + NM_SETTING_GSM_USERNAME, "george.clinton", + NM_SETTING_GSM_PASSWORD, "parliament", + NM_SETTING_GSM_NUMBER, "*99#", + NULL); + + /* Serial setting */ + nm_connection_add_setting (connection, nm_setting_serial_new ()); + + /* PPP setting */ + nm_connection_add_setting (connection, nm_setting_ppp_new ()); + + + /* Write out the connection */ + owner_uid = geteuid (); + owner_grp = getegid (); + success = write_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error); + ASSERT (success == TRUE, + "connection-write", "failed to allocate write keyfile: %s", + error ? error->message : "(none)"); + + ASSERT (testfile != NULL, + "connection-write", "didn't get keyfile name back after writing connection"); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = connection_from_file (testfile, NULL); + ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "connection-write", "written and re-read connection weren't the same"); + + g_clear_error (&error); + unlink (testfile); + g_free (testfile); + + g_object_unref (reread); + g_object_unref (connection); +} + +#define TEST_GSM_FILE TEST_KEYFILES_DIR"/ATT_Data_Connect_Plain" + +static void +test_read_gsm_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingSerial *s_serial; + NMSettingPPP *s_ppp; + NMSettingGsm *s_gsm; + NMSetting *s_bluetooth; + GError *error = NULL; + const char *tmp; + const char *expected_id = "AT&T Data Connect"; + const char *expected_apn = "ISP.CINGULAR"; + const char *expected_username = "ISP@CINGULARGPRS.COM"; + const char *expected_password = "CINGULAR1"; + const char *expected_network_id = "24005"; + const char *expected_pin = "2345"; + + connection = connection_from_file (TEST_GSM_FILE, NULL); + ASSERT (connection != NULL, + "connection-read", "failed to read %s", TEST_GSM_FILE); + + ASSERT (nm_connection_verify (connection, &error), + "connection-verify", "failed to verify %s: %s", TEST_GSM_FILE, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "connection-verify-connection", "failed to verify %s: missing %s setting", + TEST_GSM_FILE, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "connection-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + tmp = nm_setting_connection_get_connection_type (s_con); + ASSERT (tmp != NULL, + "connection-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, NM_SETTING_GSM_SETTING_NAME) == 0, + "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_TYPE); + + /* ===== BLUETOOTH SETTING ===== */ + + /* Plain GSM, so no BT setting expected */ + s_bluetooth = nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH); + ASSERT (s_bluetooth == NULL, + "connection-verify-bt", "unexpected %s setting", + TEST_GSM_FILE, + NM_SETTING_BLUETOOTH_SETTING_NAME); + + /* ===== GSM SETTING ===== */ + + s_gsm = NM_SETTING_GSM (nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM)); + ASSERT (s_gsm != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s setting", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME); + + /* APN */ + tmp = nm_setting_gsm_get_apn (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_APN); + ASSERT (strcmp (tmp, expected_apn) == 0, + "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_APN); + + /* Username */ + tmp = nm_setting_gsm_get_username (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_USERNAME); + ASSERT (strcmp (tmp, expected_username) == 0, + "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_USERNAME); + + /* Password */ + tmp = nm_setting_gsm_get_password (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_PASSWORD); + ASSERT (strcmp (tmp, expected_password) == 0, + "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_PASSWORD); + + /* Network ID */ + tmp = nm_setting_gsm_get_network_id (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_NETWORK_ID); + ASSERT (strcmp (tmp, expected_network_id) == 0, + "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_NETWORK_ID); + + /* PIN */ + tmp = nm_setting_gsm_get_pin (s_gsm); + ASSERT (tmp != NULL, + "connection-verify-gsm", "failed to verify %s: missing %s / %s key", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_PIN); + ASSERT (strcmp (tmp, expected_pin) == 0, + "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", + TEST_GSM_FILE, + NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_GSM_PIN); + + /* ===== SERIAL SETTING ===== */ + + s_serial = NM_SETTING_SERIAL (nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL)); + ASSERT (s_serial != NULL, + "connection-verify-serial", "failed to verify %s: missing %s setting", + TEST_GSM_FILE, + NM_SETTING_SERIAL_SETTING_NAME); + + /* ===== PPP SETTING ===== */ + + s_ppp = NM_SETTING_PPP (nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP)); + ASSERT (s_ppp != NULL, + "connection-verify-ppp", "failed to verify %s: missing %s setting", + TEST_GSM_FILE, + NM_SETTING_PPP_SETTING_NAME); + + g_object_unref (connection); +} + +static void +test_write_gsm_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingIP4Config *s_ip4; + NMSettingGsm *s_gsm; + char *uuid; + gboolean success; + NMConnection *reread; + char *testfile = NULL; + GError *error = NULL; + pid_t owner_grp; + uid_t owner_uid; + guint64 timestamp = 0x12344433L; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "connection-write", "failed to allocate new connection"); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + ASSERT (s_con != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "T-Mobile Funkadelic 2", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + g_free (uuid); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + ASSERT (s_ip4 != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* GSM setting */ + s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); + ASSERT (s_gsm != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_GSM_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + + g_object_set (s_gsm, + NM_SETTING_GSM_APN, "internet2.voicestream.com", + NM_SETTING_GSM_USERNAME, "george.clinton.again", + NM_SETTING_GSM_PASSWORD, "parliament2", + NM_SETTING_GSM_NUMBER, "*99#", + NM_SETTING_GSM_PIN, "123456", + NM_SETTING_GSM_NETWORK_ID, "254098", + NM_SETTING_GSM_HOME_ONLY, TRUE, + NM_SETTING_GSM_NETWORK_TYPE, NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA, + NULL); + + /* Serial setting */ + nm_connection_add_setting (connection, nm_setting_serial_new ()); + + /* PPP setting */ + nm_connection_add_setting (connection, nm_setting_ppp_new ()); + + + /* Write out the connection */ + owner_uid = geteuid (); + owner_grp = getegid (); + success = write_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error); + ASSERT (success == TRUE, + "connection-write", "failed to allocate write keyfile: %s", + error ? error->message : "(none)"); + + ASSERT (testfile != NULL, + "connection-write", "didn't get keyfile name back after writing connection"); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = connection_from_file (testfile, NULL); ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, @@ -1295,11 +1987,9 @@ test_write_wireless_connection (void) int main (int argc, char **argv) { GError *error = NULL; - DBusGConnection *bus; char *base; g_type_init (); - bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); if (!nm_utils_init (&error)) FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); @@ -1316,10 +2006,18 @@ int main (int argc, char **argv) test_read_valid_wireless_connection (); test_write_wireless_connection (); + test_read_string_ssid (); + test_write_string_ssid (); + + test_read_bt_dun_connection (); + test_write_bt_dun_connection (); + + test_read_gsm_connection (); + test_write_gsm_connection (); + base = g_path_get_basename (argv[0]); fprintf (stdout, "%s: SUCCESS\n", base); g_free (base); - dbus_g_connection_unref (bus); return 0; } diff --git a/system-settings/plugins/keyfile/utils.c b/system-settings/plugins/keyfile/utils.c new file mode 100644 index 0000000000..de64f7913b --- /dev/null +++ b/system-settings/plugins/keyfile/utils.c @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2010 Red Hat, Inc. + */ + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include "utils.h" + + +static const char temp_letters[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +/* + * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()). + */ +static gboolean +check_mkstemp_suffix (const char *path) +{ + const char *ptr; + + g_return_val_if_fail (path != NULL, FALSE); + + /* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */ + ptr = strrchr (path, '.'); + if (ptr && (strspn (ptr + 1, temp_letters) == 6) && (! ptr[7])) + return TRUE; + return FALSE; +} + +static gboolean +check_prefix (const char *base, const char *tag) +{ + int len, tag_len; + + g_return_val_if_fail (base != NULL, TRUE); + g_return_val_if_fail (tag != NULL, TRUE); + + len = strlen (base); + tag_len = strlen (tag); + if ((len > tag_len) && !strncasecmp (base, tag, tag_len)) + return TRUE; + return FALSE; +} + +static gboolean +check_suffix (const char *base, const char *tag) +{ + int len, tag_len; + + g_return_val_if_fail (base != NULL, TRUE); + g_return_val_if_fail (tag != NULL, TRUE); + + len = strlen (base); + tag_len = strlen (tag); + if ((len > tag_len) && !strcasecmp (base + len - tag_len, tag)) + return TRUE; + return FALSE; +} + +gboolean +utils_should_ignore_file (const char *filename) +{ + char *base; + gboolean ignore = FALSE; + + g_return_val_if_fail (filename != NULL, TRUE); + + base = g_path_get_basename (filename); + g_return_val_if_fail (base != NULL, TRUE); + + /* Ignore files with certain patterns */ + if ( (check_prefix (base, ".") && check_suffix (base, SWP_TAG)) /* vim temporary files: .filename.swp */ + || (check_prefix (base, ".") && check_suffix (base, SWPX_TAG)) /* vim temporary files: .filename.swpx */ + || check_mkstemp_suffix (base)) /* temporary files created by mkstemp() */ + ignore = TRUE; + + g_free (base); + return ignore; +} + diff --git a/system-settings/plugins/keyfile/utils.h b/system-settings/plugins/keyfile/utils.h new file mode 100644 index 0000000000..3c1a6104bd --- /dev/null +++ b/system-settings/plugins/keyfile/utils.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2010 Red Hat, Inc. + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include <glib.h> +#include "common.h" + +gboolean utils_should_ignore_file (const char *filename); + +#endif /* _UTILS_H_ */ + diff --git a/system-settings/plugins/keyfile/io/writer.c b/system-settings/plugins/keyfile/writer.c index 355d624c19..ffac35cff5 100644 --- a/system-settings/plugins/keyfile/io/writer.c +++ b/system-settings/plugins/keyfile/writer.c @@ -31,15 +31,16 @@ #include <nm-setting-wired.h> #include <nm-setting-wireless.h> #include <nm-setting-ip4-config.h> +#include <nm-setting-bluetooth.h> #include <nm-utils.h> #include <string.h> #include <arpa/inet.h> #include <netinet/ether.h> -#include <nm-settings-interface.h> +#include <ctype.h> #include "nm-dbus-glib-types.h" #include "writer.h" -#include "reader.h" +#include "common.h" static gboolean write_array_of_uint (GKeyFile *file, @@ -453,13 +454,56 @@ write_hash_of_string (GKeyFile *file, g_hash_table_foreach (hash, write_hash_of_string_helper, &info); } +static void +ssid_writer (GKeyFile *file, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GByteArray *array; + const char *setting_name = nm_setting_get_name (setting); + gboolean new_format = TRUE; + int i, *tmp_array; + char *ssid; + + g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY)); + + array = (GByteArray *) g_value_get_boxed (value); + if (!array || !array->len) + return; + + /* Check whether each byte is printable. If not, we have to use an + * integer list, otherwise we can just use a string. + */ + for (i = 0; i < array->len; i++) { + char c = array->data[i] & 0xFF; + if (!isprint (c)) { + new_format = FALSE; + break; + } + } + + if (new_format) { + ssid = g_malloc0 (array->len + 1); + memcpy (ssid, array->data, array->len); + g_key_file_set_string (file, setting_name, key, ssid); + g_free (ssid); + } else { + tmp_array = g_new (gint, array->len); + for (i = 0; i < array->len; i++) + tmp_array[i] = (int) array->data[i]; + g_key_file_set_integer_list (file, setting_name, key, tmp_array, array->len); + g_free (tmp_array); + } +} + typedef struct { const char *setting_name; const char *key; void (*writer) (GKeyFile *keyfile, NMSetting *setting, const char *key, const GValue *value); } KeyWriter; -/* A table of keys that require further parsing/conversion becuase they are +/* A table of keys that require further parsing/conversion because they are * stored in a format that can't be automatically read using the key's type. * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored @@ -487,12 +531,24 @@ static KeyWriter key_writers[] = { { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS, mac_address_writer }, + { NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + mac_address_writer }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS, mac_address_writer }, { NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + mac_address_writer }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID, mac_address_writer }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_BLUETOOTH_BDADDR, + mac_address_writer }, + { NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID, + ssid_writer }, { NULL, NULL, NULL } }; @@ -505,10 +561,9 @@ write_setting_value (NMSetting *setting, { GKeyFile *file = (GKeyFile *) user_data; const char *setting_name; - GType type; + GType type = G_VALUE_TYPE (value); KeyWriter *writer = &key_writers[0]; - - type = G_VALUE_TYPE (value); + GParamSpec *pspec; /* Setting name gets picked up from the keyfile's section name instead */ if (!strcmp (key, NM_SETTING_NAME)) @@ -521,6 +576,15 @@ write_setting_value (NMSetting *setting, setting_name = nm_setting_get_name (setting); + /* If the value is the default value, remove the item from the keyfile */ + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key); + if (pspec) { + if (g_param_value_defaults (pspec, (GValue *) value)) { + g_key_file_remove_key (file, setting_name, key, NULL); + return; + } + } + /* Look through the list of handlers for non-standard format key values */ while (writer->setting_name) { if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) { @@ -649,18 +713,14 @@ write_connection (NMConnection *connection, g_file_set_contents (path, data, len, error); if (chown (path, owner_uid, owner_grp) < 0) { - g_set_error (error, - NM_SETTINGS_INTERFACE_ERROR, - NM_SETTINGS_INTERFACE_ERROR_INTERNAL_ERROR, + g_set_error (error, KEYFILE_PLUGIN_ERROR, 0, "%s.%d: error chowning '%s': %d", __FILE__, __LINE__, path, errno); unlink (path); } else { err = chmod (path, S_IRUSR | S_IWUSR); if (err) { - g_set_error (error, - NM_SETTINGS_INTERFACE_ERROR, - NM_SETTINGS_INTERFACE_ERROR_INTERNAL_ERROR, + g_set_error (error, KEYFILE_PLUGIN_ERROR, 0, "%s.%d: error setting permissions on '%s': %d", __FILE__, __LINE__, path, errno); unlink (path); diff --git a/system-settings/plugins/keyfile/io/writer.h b/system-settings/plugins/keyfile/writer.h index fa04deef98..fa04deef98 100644 --- a/system-settings/plugins/keyfile/io/writer.h +++ b/system-settings/plugins/keyfile/writer.h |