diff options
| author | Michael Biebl <biebl@debian.org> | 2018-05-11 22:08:45 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-05-11 22:08:45 +0200 |
| commit | ee9c73a923909e23a649407be77e25235d769e25 (patch) | |
| tree | e21c923621fa278e737da693df9eb60ea31a6067 /src/settings/plugins/ifnet | |
| parent | f60117b41d5433be1b4a96d82cd11d0c3dce9b63 (diff) | |
New upstream version 1.10.8 upstream/1.10.8
Diffstat (limited to 'src/settings/plugins/ifnet')
18 files changed, 7630 insertions, 0 deletions
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c new file mode 100644 index 00000000..ed0a757f --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c @@ -0,0 +1,2912 @@ +/* -*- 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 "nm-default.h" + +#include "nms-ifnet-connection-parser.h" + +#include <string.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <errno.h> + +#include "settings/nm-settings-plugin.h" +#include "nm-core-internal.h" +#include "NetworkManagerUtils.h" +#include "nm-meta-setting.h" + +#include "nms-ifnet-net-utils.h" +#include "nms-ifnet-wpa-parser.h" +#include "nms-ifnet-connection.h" + +static char * +connection_id_from_ifnet_name (const char *conn_name) +{ + int name_len = strlen (conn_name); + + /* Convert a hex-encoded conn_name (only used for wifi SSIDs) to human-readable one */ + if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x"))) { + GBytes *bytes = nm_utils_hexstr2bin (conn_name); + char *buf; + + if (bytes) { + buf = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); + g_bytes_unref (bytes); + return buf; + } + } + + return g_strdup (conn_name); +} + +static gboolean eap_simple_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +static gboolean eap_tls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +static gboolean eap_peap_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +static gboolean eap_ttls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +typedef struct { + const char *method; + gboolean (*reader) (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + gboolean wifi_phase2_only; +} EAPReader; + +static EAPReader eap_readers[] = { + {"md5", eap_simple_reader, TRUE}, + {"pwd", 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, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + const char *value; + + /* identity */ + value = wpa_get_value (ssid, "identity"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 char * +get_cert (const char *ssid, const char *key, const char *basepath) +{ + const char *orig; + + /* If it's a relative path, convert to absolute using 'basepath' */ + orig = wpa_get_value (ssid, key); + if (g_path_is_absolute (orig)) + return g_strdup (orig); + return g_strdup_printf ("%s/%s", basepath, orig); +} + +static gboolean +eap_tls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + const char *value; + char *ca_cert = NULL; + char *client_cert = NULL; + char *privkey = NULL; + const 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 = get_cert (ssid, phase2 ? "ca_cert2" : "ca_cert", basepath); + 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 { + nm_log_warn (LOGD_SETTINGS, " 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_key2_passwd" : + "private_key_passwd"); + + if (!privkey_password) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 = get_cert (ssid, phase2 ? "private_key2" : "private_key", basepath); + if (!privkey) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 = get_cert (ssid, phase2 ? "client_cert2" : "client_cert", basepath); + if (!client_cert) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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: + g_free (ca_cert); + g_free (client_cert); + g_free (privkey); + return success; +} + +static gboolean +eap_peap_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + char *ca_cert = NULL; + const char *inner_auth = NULL; + const char *peapver = NULL; + char **list = NULL, **iter, *lower; + gboolean success = FALSE; + + ca_cert = get_cert (ssid, "ca_cert", basepath); + 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 { + nm_log_warn (LOGD_SETTINGS, " 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, basepath, error)) + goto done; + } else if (!(pos = strstr (*iter, "TLS"))) { + if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error)) + goto done; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", + *iter); + goto done; + } + + pos = strchr (*iter, '='); + if (pos && *pos) { + pos++; + lower = g_ascii_strdown (pos, -1); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, + NULL); + g_free (lower); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No IEEE_8021X_INNER_AUTH_METHOD."); + goto done; + } + break; + } + + if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No valid IEEE_8021X_INNER_AUTH_METHODS found."); + goto done; + } + + success = TRUE; + +done: + g_free (ca_cert); + if (list) + g_strfreev (list); + return success; +} + +static gboolean +eap_ttls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + gboolean success = FALSE; + const char *anon_ident = NULL; + char *ca_cert = NULL; + const char *tmp; + char **list = NULL, **iter, *inner_auth = NULL; + + /* ca cert */ + ca_cert = get_cert (ssid, "ca_cert", basepath); + 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 { + nm_log_warn (LOGD_SETTINGS, " 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, basepath, 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, basepath, 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, basepath, error)) { + nm_log_warn (LOGD_SETTINGS, "SIMPLE ERROR"); + goto done; + } + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, + pos, NULL); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", + *iter); + goto done; + } + break; + } + + success = TRUE; +done: + g_free (ca_cert); + if (list) + g_strfreev (list); + 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 (const char *conn_name) +{ + const gchar *type = ifnet_get_data (conn_name, "type"); + const gchar *ret_type = NULL; + + if (!g_strcmp0 (type, "ppp")) + ret_type = NM_SETTING_PPPOE_SETTING_NAME; + + if (!g_strcmp0 (type, "wireless")) + ret_type = NM_SETTING_WIRELESS_SETTING_NAME; + + if (!ret_type) + ret_type = NM_SETTING_WIRED_SETTING_NAME; + + nm_log_info (LOGD_SETTINGS, "guessed connection type (%s) = %s", conn_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 (const char *conn_name, const char **mac, GError **error) +{ + const char *value = ifnet_get_data (conn_name, "mac"); + + if (!value || !strlen (value)) + return TRUE; + + if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "The MAC address '%s' was invalid.", value); + return FALSE; + } + + *mac = value; + return TRUE; +} + +static gboolean +make_wired_connection_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + const char *mac = NULL; + NMSettingWired *s_wired = NULL; + const char *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) { + nm_log_warn (LOGD_SETTINGS, " 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)) { + g_object_unref (s_wired); + return FALSE; + } + + if (mac) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + return TRUE; +} + +/* add NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, + * NM_SETTING_IP_CONFIG_DHCP_CLIENT_ID in future*/ +static gboolean +make_ip4_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + + NMSettingIPConfig *ip4_setting = + NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + const char *value, *method = NULL; + 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_IP_CONFIG_IGNORE_AUTO_DNS, value + && strstr (value, "nodns") ? TRUE : FALSE, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, value + && strstr (value, "nogateway") ? TRUE : FALSE, NULL); + + if (!is_static_block) { + method = ifnet_get_data (conn_name, "config"); + if (!method){ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown config for %s", conn_name); + g_object_unref (ip4_setting); + return FALSE; + } + if (strstr (method, "dhcp")) + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + else if (strstr (method, "autoip")) { + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); + return TRUE; + } else if (strstr (method, "shared")) { + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_SHARED, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); + return TRUE; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown config for %s", conn_name); + g_object_unref (ip4_setting); + return FALSE; + } + nm_log_info (LOGD_SETTINGS, "Using %s method for %s", method, conn_name); + }else { + iblock = convert_ip4_config_block (conn_name); + if (!iblock) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ifnet plugin: can't aquire ip configuration for %s", + conn_name); + g_object_unref (ip4_setting); + return FALSE; + } + /************** add all ip settings to the connection**********/ + while (iblock) { + ip_block *current_iblock; + NMIPAddress *ip4_addr; + GError *local = NULL; + + ip4_addr = nm_ip_address_new (AF_INET, iblock->ip, iblock->prefix, &local); + if (iblock->next_hop) + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, NULL); + + if (ip4_addr) { + if (!nm_setting_ip_config_add_address (ip4_setting, ip4_addr)) + nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP4 address"); + nm_ip_address_unref (ip4_addr); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid address entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + + } + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, !has_default_ip4_route (conn_name), + NULL); + } + + /* add dhcp hostname and client id */ + if (!is_static_block && strstr (method, "dhcp")) { + 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_IP_CONFIG_DHCP_HOSTNAME, + dhcp_hostname, NULL); + nm_log_info (LOGD_SETTINGS, "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); + nm_log_info (LOGD_SETTINGS, "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 = ifnet_get_data (conn_name, "dns_search"); + if (value) { + gs_free char *stripped = g_strdup (value); + char **searches = NULL; + + strip_string (stripped, '"'); + + searches = g_strsplit (stripped, " ", 0); + if (searches) { + char **item; + + for (item = searches; *item; item++) { + if (strlen (*item)) { + if (!nm_setting_ip_config_add_dns_search (ip4_setting, *item)) + nm_log_warn (LOGD_SETTINGS, " duplicate DNS domain '%s'", *item); + } + } + g_strfreev (searches); + } + } + + /* static routes */ + iblock = convert_ip4_routes_block (conn_name); + while (iblock) { + ip_block *current_iblock = iblock; + const char *metric_str; + char *stripped; + gint64 metric; + NMIPRoute *route; + GError *local = NULL; + + if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) { + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + } else { + metric_str = ifnet_get_global_data ("metric"); + if (metric_str) { + stripped = g_strdup (metric_str); + strip_string (stripped, '"'); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + g_free (stripped); + } else + metric = -1; + } + + route = nm_ip_route_new (AF_INET, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local); + if (route) { + if (nm_setting_ip_config_add_route (ip4_setting, route)) + nm_log_info (LOGD_SETTINGS, "new IP4 route:%s\n", iblock->ip); + else + nm_log_warn (LOGD_SETTINGS, "duplicate IP4 route"); + nm_ip_route_unref (route); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid route entry: %s", local->message); + g_clear_error (&local); + } + + 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)); + + return TRUE; +} + +static gboolean +make_ip6_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + NMSettingIPConfig *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; + const char *value; + ip_block *iblock; + gboolean never_default = !has_default_ip6_route (conn_name); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + + 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_IP_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; + nm_log_info (LOGD_SETTINGS, "IPv6 for %s enabled, using %s", conn_name, method); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, method, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, FALSE, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, FALSE, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, NULL); + + /* Make manual settings */ + if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + ip_block *current_iblock; + + iblock = convert_ip6_config_block (conn_name); + if (!iblock) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ifnet plugin: can't aquire ip6 configuration for %s", + conn_name); + goto error; + } + /* add all IPv6 addresses */ + while (iblock) { + NMIPAddress *ip6_addr; + GError *local = NULL; + + ip6_addr = nm_ip_address_new (AF_INET6, iblock->ip, iblock->prefix, &local); + if (ip6_addr) { + if (nm_setting_ip_config_add_address (s_ip6, ip6_addr)) { + nm_log_info (LOGD_SETTINGS, "ipv6 addresses count: %d", + nm_setting_ip_config_get_num_addresses (s_ip6)); + } else { + nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP6 address"); + } + nm_ip_address_unref (ip6_addr); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid address entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + } + + } else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + /* - autoconf or DHCPv6 stuff goes here */ + } + // DNS Servers, set NM_SETTING_IP_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 NMSettingIPConfig */ + + // Add routes + iblock = convert_ip6_routes_block (conn_name); + if (iblock) + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, NULL); + /* Add all IPv6 routes */ + while (iblock) { + ip_block *current_iblock = iblock; + const char *metric_str; + char *stripped; + gint64 metric; + NMIPRoute *route; + GError *local = NULL; + + /* 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 = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + else { + metric_str = ifnet_get_global_data ("metric"); + if (metric_str) { + stripped = g_strdup (metric_str); + strip_string (stripped, '"'); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + g_free (stripped); + } else + metric = 1; + } + + route = nm_ip_route_new (AF_INET6, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local); + if (route) { + if (nm_setting_ip_config_add_route (s_ip6, route)) + nm_log_info (LOGD_SETTINGS, " new IP6 route"); + else + nm_log_warn (LOGD_SETTINGS, " duplicate IP6 route"); + nm_ip_route_unref (route); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid route entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + } + +done: + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + return TRUE; + +error: + g_object_unref (s_ip6); + nm_log_warn (LOGD_SETTINGS, " Ignore IPv6 for %s", conn_name); + return FALSE; +} + +static NMSetting * +make_wireless_connection_setting (const char *conn_name, + NMSetting8021x **s_8021x, + GError **error) +{ + const char *mac = NULL; + NMSettingWireless *wireless_setting = NULL; + gboolean adhoc = FALSE; + const char *value; + const char *type; + + /* PPP over WIFI is not supported yet */ + 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) { + nm_log_warn (LOGD_SETTINGS, "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); + } + } else { + g_object_unref (wireless_setting); + return NULL; + } + + /* handle ssid (hex and ascii) */ + if (conn_name) { + GBytes *bytes; + gsize ssid_len = 0, value_len = strlen (conn_name); + + 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' size (looks like hex but length not multiple of 2)", + conn_name); + goto error; + } + + bytes = nm_utils_hexstr2bin (conn_name); + if (!bytes) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' (looks like hex SSID but isn't)", + conn_name); + goto error; + } + } else + bytes = g_bytes_new (conn_name, value_len); + + ssid_len = g_bytes_get_size (bytes); + if (ssid_len > 32 || ssid_len == 0) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", + conn_name, ssid_len); + goto error; + } + + g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, bytes, NULL); + g_bytes_unref (bytes); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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); + nm_log_info (LOGD_SETTINGS, "Using mode: %s", mode); + } + + /* BSSID setting */ + value = wpa_get_value (conn_name, "bssid"); + if (value) { + if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid BSSID '%s'", value); + goto error; + } + + g_object_set (wireless_setting, NM_SETTING_WIRELESS_BSSID, + value, NULL); + + } + + /* 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) { + nm_log_warn (LOGD_SETTINGS, " invalid MTU '%s' for %s", value, conn_name); + } else + g_object_set (wireless_setting, NM_SETTING_WIRELESS_MTU, + (guint32) mtu, NULL); + + } + + nm_log_info (LOGD_SETTINGS, "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 (const char *ssid, GError **error) +{ + NMSettingWirelessSecurity *wsec; + const char *value; + + wsec = + NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + + 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 (const char *ssid, + const char *key, + int key_idx, + NMSettingWirelessSecurity *s_wsec, + GError **error) +{ + const char *value; + char *converted = NULL; + 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; + + /* Validate keys */ + if (strlen (value) == 10 || strlen (value) == 26) { + /* Hexadecimal WEP key */ + if (!is_hex (value)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid hexadecimal WEP key."); + goto out; + } + converted = 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid ASCII WEP passphrase."); + g_free (tmp); + goto out; + + } + + converted = nm_utils_bin2hexstr (tmp, strlen (tmp), -1); + g_free (tmp); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WEP key length. Key: %s", value); + goto out; + } + + if (converted) { + nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, converted); + g_free (converted); + success = TRUE; + } + +out: + return success; +} + +static gboolean +add_wep_keys (const char *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 (const char *ssid, GError **error) +{ + const char *auth_alg, *value; + int default_key_idx = 0; + 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); + nm_log_info (LOGD_SETTINGS, "Default key index: %d", default_key_idx); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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); + nm_log_info (LOGD_SETTINGS, "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); + nm_log_info (LOGD_SETTINGS, "WEP: Use shared system authentication"); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 (const char *psk, GError **error) +{ + char *hashed = NULL; + gboolean quoted = FALSE; + + if (!psk) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing WPA_PSK for WPA-PSK key management"); + return NULL; + } + + /* 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. + */ + + if (psk[0] == '"' && psk[strlen (psk) - 1] == '"') + quoted = TRUE; + if (!quoted && (strlen (psk) == 64)) { + /* Verify the hex PSK; 64 digits */ + if (!is_hex (psk)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (contains non-hexadecimal characters)"); + goto out; + } + hashed = g_strdup (psk); + } else { + char *stripped = g_strdup (psk); + + strip_string (stripped, '"'); + + /* Length check */ + if (strlen (stripped) < 8 || strlen (stripped) > 63) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (passphrases must be between " + "8 and 63 characters long (inclusive))"); + g_free (stripped); + goto out; + } + + hashed = g_strdup (stripped); + g_free (stripped); + } + + if (!hashed) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (doesn't look like a passphrase or hex key)"); + goto out; + } + +out: + return hashed; +} + +static gboolean +fill_wpa_ciphers (const char *ssid, + NMSettingWirelessSecurity *wsec, + gboolean group, + gboolean adhoc) +{ + const char *value; + char **list = NULL, **iter; + int i = 0; + + value = wpa_get_value (ssid, group ? "group" : "pairwise"); + if (!value) + return TRUE; + + list = g_strsplit_set (value, " ", 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)) { + nm_log_warn (LOGD_SETTINGS, " ignoring group cipher '%s' (only one group cipher allowed in Ad-Hoc mode)", + *iter); + continue; + } else if (!group) { + nm_log_warn (LOGD_SETTINGS, " 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 { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid %s cipher '%s'", + group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", + *iter); + } + } + + if (list) + g_strfreev (list); + return TRUE; +} + +static NMSetting8021x * +fill_8021x (const char *ssid, + const char *key_mgmt, + gboolean wifi, + const char *basepath, + GError **error) +{ + NMSetting8021x *s_8021x; + const char *value; + char **list, **iter; + + value = wpa_get_value (ssid, "eap"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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) { + 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) { + nm_log_warn (LOGD_SETTINGS, " 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, basepath, error)) { + g_free (lower); + goto error; + } + nm_setting_802_1x_add_eap_method (s_8021x, lower); + found = TRUE; + break; + + next: + eap++; + } + + if (!found) { + nm_log_warn (LOGD_SETTINGS, " 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 (const char *ssid, + const char *basepath, + NMSetting8021x **s_8021x, + GError **error) +{ + NMSettingWirelessSecurity *wsec; + const char *value; + char *lower; + gboolean adhoc = FALSE; + + if (!exist_ssid (ssid)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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; + + /* 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"); + + } + + value = wpa_get_value (ssid, "key_mgmt"); + if (!strcmp (value, "WPA-PSK")) { + char *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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", + value); + goto error; + } + *s_8021x = fill_8021x (ssid, value, TRUE, basepath, 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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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 (const char *conn_name, + const char *basepath, + NMSetting8021x **s_8021x, + GError ** error) +{ + NMSettingWirelessSecurity *wsec = NULL; + const char *ssid; + gboolean adhoc = FALSE; + const char *value; + + g_return_val_if_fail (conn_name != NULL + && strcmp (ifnet_get_data (conn_name, "type"), + "ppp") != 0, NULL); + nm_log_info (LOGD_SETTINGS, "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; + + value = wpa_get_value (ssid, "key_mgmt"); + if (!adhoc && g_strcmp0 (value, "IEEE8021X") == 0) { + value = wpa_get_value (ssid, "eap"); + if (value && strcasecmp (value, "LEAP") == 0) { + wsec = make_leap_setting (ssid, error); + if (wsec == NULL) + goto error; + } + } else if (g_strcmp0 (value, "WPA-PSK") == 0 || g_strcmp0 (value, "WPA-EAP") == 0) { + wsec = make_wpa_setting (ssid, basepath, s_8021x, error); + if (wsec == NULL) + goto error; + } + if (!wsec) { + wsec = make_wep_setting (ssid, error); + if (wsec == NULL) + goto error; + } + return wsec; + +error: + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Can't handle security information for ssid: %s", + conn_name); + return NULL; +} + +/* Currently only support username and password */ +static gboolean +make_pppoe_connection_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + NMSettingPppoe *s_pppoe; + NMSettingPpp *s_ppp; + const char *value; + + s_pppoe = NM_SETTING_PPPOE (nm_setting_pppoe_new ()); + + /* username */ + value = ifnet_get_data (conn_name, "username"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ppp requires at lease a username"); + return FALSE; + } + 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)); + + return TRUE; +} + +NMConnection * +ifnet_update_connection_from_config_block (const char *conn_name, + const char *basepath, + GError **error) +{ + const gchar *type = NULL; + NMConnection *connection = NULL; + NMSettingConnection *setting = NULL; + NMSetting8021x *s_8021x = NULL; + NMSettingWirelessSecurity *wsec = NULL; + gboolean auto_conn = TRUE; + const char *value = NULL; + gchar *id, *uuid; + gboolean success = FALSE; + + connection = nm_simple_connection_new (); + setting = nm_connection_get_setting_connection (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; + + /* Try to read UUID from the ifnet block, otherwise generate UUID from + * the connection ID. + */ + id = connection_id_from_ifnet_name (conn_name); + uuid = g_strdup (ifnet_get_data (conn_name, "uuid")); + if (!uuid) + uuid = nm_utils_uuid_generate_from_string (id, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + + g_object_set (setting, + NM_SETTING_CONNECTION_TYPE, type, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_INTERFACE_NAME, conn_name, + NM_SETTING_CONNECTION_READ_ONLY, FALSE, + NM_SETTING_CONNECTION_AUTOCONNECT, auto_conn, + NULL); + nm_log_info (LOGD_SETTINGS, "name:%s, id:%s, uuid: %s", conn_name, id, uuid); + g_free (id); + g_free (uuid); + + if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type) + || !strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { + /* wired setting */ + if (!make_wired_connection_setting (connection, conn_name, error)) + goto error; + + /* pppoe setting */ + if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { + if (!make_pppoe_connection_setting (connection, conn_name, error)) + goto error; + } + } else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) { + /* wireless setting */ + NMSetting *wireless_setting; + + wireless_setting = make_wireless_connection_setting (conn_name, &s_8021x, error); + if (!wireless_setting) + goto error; + nm_connection_add_setting (connection, wireless_setting); + + /* wireless security setting */ + if (wpa_get_value (conn_name, "ssid")) { + wsec = make_wireless_security_setting (conn_name, basepath, &s_8021x, error); + if (!wsec) + goto error; + nm_connection_add_setting (connection, NM_SETTING (wsec)); + if (s_8021x) + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + } + } else + goto error; + + /* IPv4 setting */ + if (!make_ip4_setting (connection, conn_name, error)) + goto error; + + /* IPv6 setting */ + if (!make_ip6_setting (connection, conn_name, error)) + goto error; + + if (nm_connection_verify (connection, error)) { + nm_log_info (LOGD_SETTINGS, "Connection verified %s:%d", conn_name, success); + } else { + goto error; + } + + return connection; +error: + g_object_unref (connection); + return NULL; +} + +typedef struct Setting8021xSchemeVtable { + const NMSetting8021xSchemeVtable *vtable; + const char *ifnet_key; +} Setting8021xSchemeVtable; + +static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { + [NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], + .ifnet_key = "ca_cert", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT], + .ifnet_key = "ca_cert2", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], + .ifnet_key = "client_cert", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT], + .ifnet_key = "client_cert2", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY], + .ifnet_key = "private_key", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY], + .ifnet_key = "private_key2", + }, +}; + +static gboolean +write_object (NMSetting8021x *s_8021x, + const char *conn_name, + GBytes *override_data, + const Setting8021xSchemeVtable *objtype, + GError **error) +{ + NMSetting8021xCKScheme scheme; + const char *path = NULL; + GBytes *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->vtable->scheme_func)) (s_8021x); + switch (scheme) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + blob = (*(objtype->vtable->blob_func)) (s_8021x); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = (*(objtype->vtable->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->ifnet_key, + (gchar *) path); + return TRUE; + } + + /* does not support writing encryption data now */ + if (blob) + nm_log_warn (LOGD_SETTINGS, " Currently we do not support cert writing."); + + return TRUE; +} + +static gboolean +write_8021x_certs (NMSetting8021x *s_8021x, + gboolean phase2, + const char *conn_name, + GError **error) +{ + char *password = NULL; + const Setting8021xSchemeVtable *otype = NULL; + gboolean is_pkcs12 = FALSE, success = FALSE; + GBytes *blob = NULL; + GBytes *enc_key = NULL; + gchar *generated_pw = NULL; + + /* CA certificate */ + otype = phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT]; + + 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); + } + + otype = phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; + + if ((*(otype->vtable->scheme_func)) (s_8021x) == + NM_SETTING_802_1X_CK_SCHEME_BLOB) + blob = (*(otype->vtable->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) { + GByteArray *tmp_enc_key; + + /* Encrypt the unencrypted private key with the fake password */ + tmp_enc_key = + nm_utils_rsa_key_encrypt (g_bytes_get_data (blob, NULL), g_bytes_get_size (blob), + password, &generated_pw, error); + if (!tmp_enc_key) + goto out; + + enc_key = g_byte_array_free_to_bytes (tmp_enc_key); + + 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_key2_passwd", 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 { + otype = phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT]; + + /* 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 ((gpointer) g_bytes_get_data (enc_key, NULL), 0, g_bytes_get_size (enc_key)); + g_bytes_unref (enc_key); + } + return success; +} + +static gboolean +write_8021x_setting (NMConnection *connection, + const char *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 = nm_connection_get_setting_802_1x (connection); + if (!s_8021x) { + return TRUE; + } + + nm_log_info (LOGD_SETTINGS, "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 = nm_connection_get_setting_wireless_security (connection); + if (!s_wsec) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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; + } else + nm_log_warn (LOGD_SETTINGS, "Unknown key_mgmt: %s", key_mgmt); + + 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 */ + 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); + } else + wpa_set_data (conn_name, "wep_tx_keyidx", NULL); + + /* 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, '"'); + } + /* psk will be lost here if we don't check it for NULL */ + if (psk) + 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, + const char *conn_name, + const char *ssid, + gboolean hex) +{ + if(strcmp (conn_name, ssid)){ + ifnet_delete_network (conn_name); + wpa_delete_security (conn_name); + } + + ifnet_add_network (ssid, "wireless"); + wpa_add_security (ssid); +} + +static gboolean +write_wireless_setting (NMConnection *connection, + const char *conn_name, + gboolean *no_8021x, + const char **out_new_name, + GError **error) +{ + NMSettingWireless *s_wireless; + GBytes *ssid; + const guint8 *ssid_data; + gsize ssid_len; + const char *mac, *bssid, *mode; + char buf[33]; + guint32 mtu, i; + gboolean adhoc = FALSE, hex_ssid = FALSE; + gchar *ssid_str, *tmp; + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + ssid_data = g_bytes_get_data (ssid, &ssid_len); + if (!ssid_len || ssid_len > 32) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + /* If the SSID contains any non-alnum characters, we need to use + * the hex notation of the SSID instead. (Because openrc doesn't + * support these characters, see bug #356337) + */ + for (i = 0; i < ssid_len; i++) { + if (!g_ascii_isalnum (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_string_free (str, FALSE); + } 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) + ifnet_set_data (ssid_str, "mac", mac); + + 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 { + nm_log_warn (LOGD_SETTINGS, "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) + wpa_set_data (ssid_str, "bssid", bssid); + + if (nm_connection_get_setting_wireless_security (connection)) { + if (!write_wireless_security_setting + (connection, ssid_str, adhoc, no_8021x, error)) + return FALSE; + } else + wpa_delete_security (ssid_str); + + if (out_new_name) + *out_new_name = ifnet_get_data (ssid_str, "name"); + g_free (ssid_str); + return TRUE; +} + +static gboolean +write_wired_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + NMSettingWired *s_wired; + const char *mac; + char *tmp; + guint32 mtu; + + s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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) + ifnet_set_data (conn_name, "mac", mac); + + 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 gboolean +write_ip4_setting (NMConnection *connection, const char *conn_name, GError **error) +{ + NMSettingIPConfig *s_ip4; + const char *value; + guint32 i, num; + GString *searches; + GString *ips; + GString *routes; + GString *dns; + gboolean success = FALSE; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + return FALSE; + } + routes = g_string_new (NULL); + + value = nm_setting_ip_config_get_method (s_ip4); + g_assert (value); + if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + + num = nm_setting_ip_config_get_num_addresses (s_ip4); + ips = g_string_new (NULL); + /* IPv4 addresses */ + for (i = 0; i < num; i++) { + NMIPAddress *addr; + + addr = nm_setting_ip_config_get_address (s_ip4, i); + + g_string_append_printf (ips, "\"%s/%u", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + + /* only the first gateway will be written */ + if (i == 0 && nm_setting_ip_config_get_gateway (s_ip4)) { + g_string_append_printf (routes, + "\"default via %s\" ", + nm_setting_ip_config_get_gateway (s_ip4)); + } + } + ifnet_set_data (conn_name, "config", ips->str); + g_string_free (ips, TRUE); + } else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + ifnet_set_data (conn_name, "config", "shared"); + else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + ifnet_set_data (conn_name, "config", "autoip"); + else + ifnet_set_data (conn_name, "config", "dhcp"); + + /* DNS Servers */ + num = nm_setting_ip_config_get_num_dns (s_ip4); + if (num > 0) { + dns = g_string_new (NULL); + for (i = 0; i < num; i++) { + const char *ip; + + ip = nm_setting_ip_config_get_dns (s_ip4, i); + g_string_append_printf (dns, " %s", ip); + } + 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_ip_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_ip_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_ip_config_get_dhcp_hostname(s_ip4); + if (value) + ifnet_set_data(conn_name, "DHCP_HOSTNAME", value, + FALSE); + + value = nm_setting_ip_config_get_dhcp_client_id(s_ip4); + if (value) + ifnet_set_data(conn_name, "DHCP_CLIENT_ID", value, + FALSE); + } + */ + + /* Static routes */ + num = nm_setting_ip_config_get_num_routes (s_ip4); + if (num > 0) { + for (i = 0; i < num; i++) { + NMIPRoute *route; + const char *next_hop; + + route = nm_setting_ip_config_get_route (s_ip4, i); + + next_hop = nm_ip_route_get_next_hop (route); + if (!next_hop) + next_hop = "0.0.0.0"; + + g_string_append_printf (routes, "\"%s/%u via %s\" ", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + next_hop); + } + } + 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 void +write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name) +{ + NMIPRoute *route; + const char *next_hop; + guint32 i, num; + GString *routes_string; + const char *old_routes; + + g_return_if_fail (s_ip6 != NULL); + num = nm_setting_ip_config_get_num_routes (s_ip6); + if (num == 0) + return; + + 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_ip_config_get_route (s_ip6, i); + + next_hop = nm_ip_route_get_next_hop (route); + if (!next_hop) + next_hop = "::"; + + g_string_append_printf (routes_string, "\"%s/%u via %s\" ", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + next_hop); + } + if (num > 0) + ifnet_set_data (conn_name, "routes", routes_string->str); + g_string_free (routes_string, TRUE); +} + +static gboolean +write_ip6_setting (NMConnection *connection, const char *conn_name, GError **error) +{ + NMSettingIPConfig *s_ip6; + const char *value; + guint32 i, num; + GString *searches; + NMIPAddress *addr; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!s_ip6) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + return FALSE; + } + + value = nm_setting_ip_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)) { + const char *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)) { + const char *config = ifnet_get_data (conn_name, "config"); + gchar *tmp; + GString *ip_str; + + if (!config) + config = ""; + num = nm_setting_ip_config_get_num_addresses (s_ip6); + + /* IPv6 addresses */ + ip_str = g_string_new (NULL); + for (i = 0; i < num; i++) { + addr = nm_setting_ip_config_get_address (s_ip6, i); + + g_string_append_printf (ip_str, "\"%s/%u\"", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + } + 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_ip_config_get_num_dns (s_ip6); + if (num > 0) { + const char *dns_servers = ifnet_get_data (conn_name, "dns_servers"); + gchar *tmp; + GString *dns_string = g_string_new (NULL); + const char *dns; + + if (!dns_servers) + dns_servers = ""; + for (i = 0; i < num; i++) { + dns = nm_setting_ip_config_get_dns (s_ip6, i); + + if (!strstr (dns_servers, dns)) + g_string_append_printf (dns_string, "%s ", dns); + } + 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_ip_config_get_num_dns_searches (s_ip6); + if (num > 0) { + const 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_ip_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); + return TRUE; +} + +static gboolean +write_pppoe_setting (const char *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, + const char *conn_name, + const char *config_file, + const char *wpa_file, + gchar **out_new_name, + gchar **out_backup, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip6; + gboolean success = FALSE; + const char *type; + gboolean no_8021x = FALSE; + gboolean wired = FALSE, pppoe = TRUE; + const char *new_name = NULL; + + if (!ifnet_can_write_connection (connection, error)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + type = nm_setting_connection_get_connection_type (s_con); + if (!type) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "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, &new_name, error)) + goto out; + } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { + NMSettingPppoe *s_pppoe; + + /* Writing pppoe setting */ + s_pppoe = nm_connection_get_setting_pppoe (connection); + if (!write_pppoe_setting (conn_name, s_pppoe)) + goto out; + pppoe = TRUE; + wired = TRUE; + no_8021x = TRUE; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "Can't write connection type '%s'", type); + goto out; + } + + /* connection name may have been updated; use it when writing out + * the rest of the settings. + */ + if (new_name) + conn_name = new_name; + + //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 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6) { + /* IPv6 Setting */ + if (!write_ip6_setting (connection, conn_name, error)) + goto out; + } + + /* Connection Setting */ + ifnet_set_data (conn_name, "auto", + nm_setting_connection_get_autoconnect (s_con) ? "true" : "false"); + ifnet_set_data (conn_name, "uuid", nm_connection_get_uuid (connection)); + + /* Write changes to disk */ + success = ifnet_flush_to_file (config_file, out_backup); + if (success) + wpa_flush_to_file (wpa_file); + + if (out_new_name) + *out_new_name = g_strdup (conn_name); + +out: + return success; +} + +gboolean +ifnet_delete_connection_in_parsers (const char *conn_name, + const char *config_file, + const char *wpa_file, + gchar **out_backup) +{ + gboolean result = FALSE; + + ifnet_delete_network (conn_name); + result = ifnet_flush_to_file (config_file, out_backup); + 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; +} + +static void +check_unsupported_secrets (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + gboolean *unsupported_secret = user_data; + + if (flags & NM_SETTING_PARAM_SECRET) { + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) + g_return_if_reached (); + if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) + *unsupported_secret = TRUE; + } +} + +gboolean +ifnet_can_write_connection (NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + gboolean has_unsupported_secrets = FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + /* If the connection is not available for all users, ignore + * it as this plugin only deals with System Connections */ + if (nm_setting_connection_get_num_permissions (s_con)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "The ifnet plugin does not support non-system-wide connections."); + return FALSE; + } + + /* Only support wired, wifi, and PPPoE */ + if ( !nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME) + && !nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME) + && !nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "The ifnet plugin cannot write the connection '%s' (type '%s')", + nm_connection_get_id (connection), + nm_setting_connection_get_connection_type (s_con)); + return FALSE; + } + + /* If the connection has flagged secrets, ignore + * it as this plugin does not deal with user agent service */ + nm_connection_for_each_setting_value (connection, + check_unsupported_secrets, + &has_unsupported_secrets); + if (has_unsupported_secrets) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "The ifnet plugin only supports persistent system secrets."); + return FALSE; + } + + return TRUE; +} + +/* get the available wired name(eth*). */ +static gchar * +get_wired_name (void) +{ + int i = 0; + + for (; i < 256; i++) { + gchar *conn_name = g_strdup_printf ("eth%d", i); + + if (!ifnet_has_network (conn_name)) { + return conn_name; + } else + g_free (conn_name); + } + return NULL; +} + +/* get the available pppoe name(ppp*). */ +static gchar * +get_ppp_name (void) +{ + int i = 0; + + for (; i < 256; i++) { + gchar *conn_name = g_strdup_printf ("ppp%d", i); + + if (!ifnet_has_network (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; + GBytes *ssid; + const guint8 *ssid_data; + gsize ssid_len; + gboolean hex_ssid = FALSE; + gchar *result = NULL; + char buf[33]; + int i = 0; + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) + return NULL; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + ssid_data = g_bytes_get_data (ssid, &ssid_len); + if (!ssid_len || ssid_len > 32) { + return NULL; + } + + for (i = 0; i < ssid_len; i++) { + if (!g_ascii_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, + const char *config_file, + const char *wpa_file, + gchar **out_new_name, + gchar **out_backup, + GError **error) +{ + NMSettingConnection *s_con; + gboolean success = FALSE; + const char *type; + gchar *new_type, *new_name = NULL; + + if (!ifnet_can_write_connection (connection, error)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + type = nm_setting_connection_get_connection_type (s_con); + g_assert (type); + + nm_log_info (LOGD_SETTINGS, "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, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "Can't write connection type '%s'", type); + goto out; + } + + if (ifnet_add_network (new_name, new_type)) { + success = ifnet_update_parsers_by_connection (connection, + new_name, + config_file, + wpa_file, + NULL, + out_backup, + error); + } + + nm_log_info (LOGD_SETTINGS, "Added new connection: %s, result: %s", + new_name, success ? "success" : "fail"); + +out: + if (!success || !out_new_name) + g_free (new_name); + else if (out_new_name) + *out_new_name = new_name; + return success; +} + diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h new file mode 100644 index 00000000..51bc34b9 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h @@ -0,0 +1,55 @@ +/* -*- 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 "nms-ifnet-net-parser.h" + +gboolean ifnet_can_write_connection (NMConnection *connection, GError **error); + +NMConnection *ifnet_update_connection_from_config_block (const char *conn_name, + const char *basepath, + GError **error); + +/* nm_conn_name is used to update nm_ifnet_connection's priv data */ +gboolean ifnet_update_parsers_by_connection (NMConnection *connection, + const char *conn_name, + const char *config_file, + const char *wpa_file, + gchar **out_new_name, + gchar **out_backup, + GError **error); + +gboolean ifnet_delete_connection_in_parsers (const char *conn_name, + const char *config_file, + const char *wpa_file, + gchar **out_backup); + +gboolean ifnet_add_new_connection (NMConnection *connection, + const char *config_file, + const char *wpa_file, + gchar **out_new_name, + gchar **out_backup, + GError ** error); +#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.c b/src/settings/plugins/ifnet/nms-ifnet-connection.c new file mode 100644 index 00000000..ce0b3f2b --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-connection.c @@ -0,0 +1,233 @@ +/* -*- 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 "nm-default.h" + +#include "nms-ifnet-connection.h" + +#include <string.h> +#include <glib/gstdio.h> + +#include "nm-dbus-interface.h" +#include "nm-utils.h" +#include "nm-setting-wireless-security.h" +#include "settings/nm-settings-connection.h" +#include "settings/nm-settings-plugin.h" + +#include "nms-ifnet-connection-parser.h" +#include "nms-ifnet-net-parser.h" +#include "nms-ifnet-net-utils.h" +#include "nms-ifnet-wpa-parser.h" +#include "nms-ifnet-plugin.h" + +/*****************************************************************************/ + +enum { + IFNET_SETUP_MONITORS, + IFNET_CANCEL_MONITORS, + IFNET_LAST_SIGNAL +}; + +static guint signals[IFNET_LAST_SIGNAL] = { 0 }; + +typedef struct { + gchar *conn_name; + NMSettingsPlugin *config; +} NMIfnetConnectionPrivate; + +struct _NMIfnetConnection { + NMSettingsConnection parent; + NMIfnetConnectionPrivate _priv; +}; + +struct _NMIfnetConnectionClass { + NMSettingsConnectionClass parent; +}; + +G_DEFINE_TYPE (NMIfnetConnection, nm_ifnet_connection, NM_TYPE_SETTINGS_CONNECTION) + +#define NM_IFNET_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfnetConnection, NM_IS_IFNET_CONNECTION) + +/*****************************************************************************/ + +const char * +nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection) +{ + return NM_IFNET_CONNECTION_GET_PRIVATE (connection)->conn_name; +} + +static gboolean +commit_changes (NMSettingsConnection *connection, + NMConnection *new_connection, + NMSettingsConnectionCommitReason commit_reason, + NMConnection **out_reread_connection, + char **out_logmsg_change, + GError **error) +{ + NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); + char *new_name = NULL; + gboolean success = FALSE; + gboolean added = FALSE; + + nm_assert (out_reread_connection && !*out_reread_connection); + nm_assert (!out_logmsg_change || !*out_logmsg_change); + + g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); + + if (priv->conn_name) { + success = ifnet_update_parsers_by_connection (new_connection, + priv->conn_name, + CONF_NET_FILE, + WPA_SUPPLICANT_CONF, + &new_name, + NULL, + error); + } else { + added = TRUE; + success = ifnet_add_new_connection (new_connection, + CONF_NET_FILE, + WPA_SUPPLICANT_CONF, + &new_name, + NULL, + error); + } + + g_assert (!!success == (new_name != NULL)); + if (success) { + g_free (priv->conn_name); + priv->conn_name = new_name; + } + + reload_parsers (); + + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + + if (success) { + NM_SET_OUT (out_logmsg_change, + g_strdup_printf ("ifcfg-rh: %s %s", + added ? "persist" : "updated", + new_name)); + } + return success; +} + +static gboolean +delete (NMSettingsConnection *connection, + GError **error) +{ + NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); + + /* Only connections which exist in /etc/conf.d/net will have a conn_name */ + if (priv->conn_name) { + g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); + + if (!ifnet_delete_connection_in_parsers (priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL)) { + nm_log_warn (LOGD_SETTINGS, "Failed to delete %s", priv->conn_name); + reload_parsers (); + /* let's not return an error. */ + } + + g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +nm_ifnet_connection_init (NMIfnetConnection * connection) +{ +} + +NMIfnetConnection * +nm_ifnet_connection_new (NMConnection *source, const char *conn_name) +{ + NMConnection *tmp; + GObject *object; + GError *error = NULL; + gboolean update_unsaved = TRUE; + + g_return_val_if_fail (source || conn_name, NULL); + + if (source) + tmp = g_object_ref (source); + else { + tmp = ifnet_update_connection_from_config_block (conn_name, NULL, &error); + if (!tmp) { + nm_log_warn (LOGD_SETTINGS, "Could not read connection '%s': %s", + conn_name, error->message); + g_error_free (error); + return NULL; + } + + /* If we just read the connection from disk, it's clearly not Unsaved */ + update_unsaved = FALSE; + } + + object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, NULL); + + NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->conn_name = g_strdup (conn_name); + if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), + tmp, + update_unsaved + ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED + : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + NULL, + NULL)) { + g_object_unref (object); + return NULL; + } + g_object_unref (tmp); + + return NM_IFNET_CONNECTION (object); +} + +static void +finalize (GObject * object) +{ + g_free (NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->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); + NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifnet_connection_class); + + object_class->finalize = finalize; + + settings_class->delete = delete; + settings_class->commit_changes = commit_changes; + + 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/src/settings/plugins/ifnet/nms-ifnet-connection.h b/src/settings/plugins/ifnet/nms-ifnet-connection.h new file mode 100644 index 00000000..1bc06644 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-connection.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 __NETWORKMANAGER_IFNET_CONNECTION_H__ +#define __NETWORKMANAGER_IFNET_CONNECTION_H__ + +#include "settings/nm-settings-connection.h" + +#include "nms-ifnet-net-parser.h" + +#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 ((klass), NM_TYPE_IFNET_CONNECTION)) +#define NM_IFNET_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass)) + +typedef struct _NMIfnetConnection NMIfnetConnection; +typedef struct _NMIfnetConnectionClass NMIfnetConnectionClass; + +GType nm_ifnet_connection_get_type (void); + +NMIfnetConnection *nm_ifnet_connection_new (NMConnection *source, + const char *conn_name); + +const char *nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection); + +#endif /* __NETWORKMANAGER_IFNET_CONNECTION_H__ */ diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c new file mode 100644 index 00000000..d3e47219 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c @@ -0,0 +1,734 @@ +/* -*- 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 "nm-default.h" + +#include "nms-ifnet-net-parser.h" + +#include <string.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "settings/nm-settings-plugin.h" +#include "platform/nm-platform.h" + +#include "nms-ifnet-plugin.h" +#include "nms-ifnet-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 (nm_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_network (const char *name, const char *type) +{ + if (ifnet_has_network (name)) + return TRUE; + if (add_new_connection_config (type, name)) { + nm_log_info (LOGD_SETTINGS, "Adding network for %s", name); + net_parser_data_changed = TRUE; + return TRUE; + } + return FALSE; +} + +gboolean +ifnet_has_network (const char *conn_name) +{ + return g_hash_table_lookup (conn_table, conn_name) != NULL; +} + +static GHashTable * +get_connection_config (const char *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 (const char *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) { + nm_log_warn (LOGD_SETTINGS, "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 */ + data = g_strdup (key_value[1]); + tmp = strip_string (data, '"'); + strip_string (tmp, '\''); + nm_log_info (LOGD_SETTINGS, "global:%s-%s\n", key_value[0], tmp); + g_hash_table_insert (global_settings_table, g_strdup (key_value[0]), g_strdup (tmp)); + g_strfreev (key_value); + g_free (data); + 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 { + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, pos); + + if (ifindex && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) != NM_LINK_TYPE_WIFI) + /* wired connection */ + conn = add_new_connection_config ("wired", pos); + else + /* wireless connection */ + conn = add_new_connection_config ("wireless", pos); + } + } + data = g_strdup (key_value[1]); + tmp = strip_string (data, '"'); + strip_string (tmp, '\''); + if (conn) + g_hash_table_insert (conn, strip_string (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); +} + +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])) { + nm_log_info (LOGD_SETTINGS, "Ignoring function: %s", func_names[i]); + return TRUE; + } + } + return FALSE; +} + +static void +append_line (GString *buf, gchar* line) +{ + gchar *pos = NULL; + + if ((pos = strchr (line, '#')) != NULL) + *pos = '\0'; + g_strstrip (line); + + if (line[0] != '\0') + g_string_append_printf (buf, " %s", line); + g_free (line); +} + +gboolean +ifnet_init (gchar * config_file) +{ + GIOChannel *channel = NULL; + gchar *line; + + /* Handle multiple lines with brackets */ + gboolean complete = TRUE; + + gboolean openrc_style = TRUE; + + /* line buffer */ + GString *buf; + + net_parser_data_changed = FALSE; + + conn_table = g_hash_table_new (nm_str_hash, g_str_equal); + global_settings_table = g_hash_table_new (nm_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) { + nm_log_warn (LOGD_SETTINGS, "Can't open %s", 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; + } + + // New openrc style, bash arrays are not allowed. We only care about '"' + if (openrc_style && line[0] != '#' && line[0] != '\0' + && !strchr (line, '(') && !strchr (line, ')')) { + gchar *tmp = line; + + while ((tmp = strchr (tmp, '"')) != NULL) { + complete = !complete; + ++tmp; + } + + append_line (buf, line); + // Add "(separator) for routes. It will be easier for later parsing + if (strstr (buf->str, "via")) + g_string_append_printf (buf, "\""); + + if (!complete) + continue; + + strip_string (buf->str, '"'); + + init_block_by_line (buf->str); + g_string_free (buf, TRUE); + buf = g_string_new (NULL); + } + // Old bash arrays for baselayout-1, to be deleted + else if (line[0] != '#' && line[0] != '\0') { + if (!complete) { + complete = + g_strrstr (line, + ")") == NULL ? FALSE : TRUE; + + append_line (buf, line); + if (!complete) { + openrc_style = FALSE; + continue; + } + else { + openrc_style = TRUE; + } + } else { + complete = + (g_strrstr (line, "(") != NULL + && g_strrstr (line, ")") != NULL) + || g_strrstr (line, "(") == NULL; + + append_line (buf, line); + if (!complete) + { + openrc_style = FALSE; + continue; + } else { + openrc_style = TRUE; + } + } + 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; +} + +const char * +ifnet_get_data (const char *conn_name, const char *key) +{ + GHashTable *conn; + + g_return_val_if_fail (conn_name && key, NULL); + + conn = g_hash_table_lookup (conn_table, conn_name); + + if (conn) + return g_hash_table_lookup (conn, key); + return NULL; +} + +/* format ip values for comparison */ +static gchar* +format_ip_for_comparison (const gchar * value) +{ + gchar **ipset; + guint length, i; + GString *formated_string = g_string_new (NULL); + gchar *formatted = NULL; + + ipset = g_strsplit (value, "\"", 0); + length = g_strv_length (ipset); + + for (i = 0; i < length; i++) + { + strip_string (ipset[i], ' '); + if (ipset[i][0] != '\0') + g_string_append_printf (formated_string, + "%s ", ipset[i]); + } + formatted = g_strdup (formated_string->str); + formatted[formated_string->len - 1] = '\0'; + + g_string_free (formated_string, TRUE); + g_strfreev (ipset); + + return formatted; +} + +void +ifnet_set_data (const char *conn_name, const char *key, const char *value) +{ + gpointer old_key = NULL, old_value = NULL; + GHashTable *conn = g_hash_table_lookup (conn_table, conn_name); + gchar * stripped = NULL; + + if (!conn) { + nm_log_warn (LOGD_SETTINGS, "%s does not exist!", conn_name); + return; + } + if (value){ + stripped = g_strdup (value); + strip_string (stripped, '"'); + } + /* Remove existing key value pair */ + if (g_hash_table_lookup_extended (conn, key, &old_key, &old_value)) { + + /* This ugly hack is due to baselayout compatibility. We have to + * deal with different ip format. So sometimes we have the same ips + * but different strings. + */ + if (stripped && + (!strcmp (key, "config") + || !strcmp (key, "routes") + || !strcmp (key, "pppd") + || !strcmp (key, "chat"))) + { + gchar *old_ips = format_ip_for_comparison (old_value); + gchar *new_ips = format_ip_for_comparison (value); + if(!strcmp (old_ips, new_ips)) + { + g_free (stripped); + g_free (old_ips); + g_free (new_ips); + return; + } + g_free (old_ips); + g_free (new_ips); + } + + if (stripped && !strcmp (old_value, stripped)) { + g_free (stripped); + return; + } + g_hash_table_remove (conn, old_key); + g_free (old_key); + g_free (old_value); + } else if (!value) + return; + if (stripped) + g_hash_table_insert (conn, g_strdup (key), stripped); + net_parser_data_changed = TRUE; +} + +// Remember to free return value +const char * +ifnet_get_global_data (const gchar * key) +{ + return g_hash_table_lookup (global_settings_table, key); +} + +// Return names of legal connections +GList * +ifnet_get_connection_names (void) +{ + GList *names = g_hash_table_get_keys (conn_table); + GList *iter, *result = NULL; + + for (iter = names; iter; iter = iter->next) { + if (!ignore_connection_name (iter->data)) + result = g_list_prepend (result, iter->data); + } + + g_list_free (names); + return g_list_reverse (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, '('); + strip_string (value, ')'); + 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++) + { + strip_string (ipset[i], ' '); + if (ipset[i][0] != '\0') + g_string_append_printf (formated_string, + "%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 (const char *config_file, gchar **out_backup) +{ + GIOChannel *channel; + GError *error = NULL; + gpointer key, value, name, network; + GHashTableIter iter, iter_network; + GList *list_iter; + gchar *out_line = NULL; + gsize bytes_written; + gboolean result = FALSE; + gchar *backup; + + if (!net_parser_data_changed) + return TRUE; + if (!conn_table || !global_settings_table) + return FALSE; + + backup = backup_file (config_file); + + channel = g_io_channel_new_file (config_file, "w", NULL); + if (!channel) { + nm_log_warn (LOGD_SETTINGS, "Can't open file %s for writing", config_file); + g_free (backup); + return FALSE; + } + g_hash_table_iter_init (&iter, global_settings_table); + nm_log_info (LOGD_SETTINGS, "Writing to %s", config_file); + g_io_channel_write_chars (channel, + "#Generated by NetworkManager\n" + "###### Global Configuration ######\n", + -1, &bytes_written, &error); + if (error) + goto done; + + /* 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) + goto done; + g_free (out_line); + } + + /* Writing connection data */ + g_io_channel_write_chars (channel, + "\n###### Connection Configuration ######\n", + -1, &bytes_written, &error); + if (error) + goto done; + + 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); + if (error) + goto done; + + 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) + goto done; + g_free (out_line); + } + } + } + + /* Writing reserved functions */ + if (functions_list) { + g_io_channel_write_chars (channel, + "\n###### Reserved Functions ######\n", + -1, &bytes_written, &error); + if (error) + goto done; + + /* 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) + goto done; + g_free (out_line); + } + } + + g_io_channel_flush (channel, &error); + if (error) + goto done; + result = TRUE; + net_parser_data_changed = FALSE; + +done: + if (error) { + nm_log_warn (LOGD_SETTINGS, "Error writing the configuration file: %s", error->message); + g_error_free (error); + } + + if (result && out_backup) + *out_backup = backup; + else + g_free (backup); + + g_io_channel_shutdown (channel, FALSE, NULL); + g_io_channel_unref (channel); + return result; +} + +gboolean +ifnet_delete_network (const char *conn_name) +{ + GHashTable *network = NULL; + + g_return_val_if_fail (conn_table != NULL && conn_name != NULL, FALSE); + nm_log_info (LOGD_SETTINGS, "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/src/settings/plugins/ifnet/nms-ifnet-net-parser.h b/src/settings/plugins/ifnet/nms-ifnet-net-parser.h new file mode 100644 index 00000000..31fc9ead --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-net-parser.h @@ -0,0 +1,42 @@ +/* -*- 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 + +#define CONF_NET_FILE SYSCONFDIR "/conf.d/net" + +gboolean ifnet_init (gchar * config_file); +void ifnet_destroy (void); + +/* Reader functions */ +GList *ifnet_get_connection_names (void); +const char *ifnet_get_data (const char *conn_name, const char *key); +const char *ifnet_get_global_data (const char *key); +gboolean ifnet_has_network (const char *conn_name); + +/* Writer functions */ +gboolean ifnet_flush_to_file (const char *config_file, gchar **out_backup); +void ifnet_set_data (const char *conn_name, const char *key, const char *value); +gboolean ifnet_add_network (const char *name, const char *type); +gboolean ifnet_delete_network (const char *conn_name); + +#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.c b/src/settings/plugins/ifnet/nms-ifnet-net-utils.c new file mode 100644 index 00000000..6531a238 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-net-utils.c @@ -0,0 +1,830 @@ +/* -*- 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 "nm-default.h" + +#include "nms-ifnet-net-utils.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "nm-utils.h" +#include "NetworkManagerUtils.h" +#include "settings/nm-settings-plugin.h" +#include "nm-config.h" +#include "dhcp/nm-dhcp-manager.h" + +#include "nms-ifnet-wpa-parser.h" +#include "nms-ifnet-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 (const char *value) +{ + const char *p = value; + + if (!p) + return FALSE; + while (*p) { + if (!g_ascii_isxdigit (*p++)) + return FALSE; + } + return TRUE; +} + +gboolean +is_ascii (const char *value) +{ + const char *p = value; + + while (*p) { + if (!g_ascii_isprint (*p++)) + return FALSE; + } + return TRUE; + +} + +gboolean +is_true (const char *str) +{ + if (!g_ascii_strcasecmp (str, "yes") + || !g_ascii_strcasecmp (str, "true")) + return TRUE; + return FALSE; +} + +static char * +find_default_gateway_str (char *str) +{ + char *tmp; + + if ((tmp = strstr (str, "default via ")) != NULL) { + return tmp + strlen ("default via "); + } else if ((tmp = strstr (str, "default gw ")) != NULL) { + return tmp + strlen ("default gw "); + } + return NULL; +} + +static char * +find_gateway_str (char *str) +{ + char *tmp; + + if ((tmp = strstr (str, "via ")) != NULL) { + return tmp + strlen ("via "); + } else if ((tmp = strstr (str, "gw ")) != NULL) { + return tmp + strlen ("gw "); + } + return NULL; +} + +gboolean +reload_parsers (void) +{ + ifnet_destroy (); + wpa_parser_destroy (); + if (!ifnet_init (CONF_NET_FILE)) + return FALSE; + wpa_parser_init (WPA_SUPPLICANT_CONF); + return TRUE; +} + +gboolean +is_static_ip4 (const char *conn_name) +{ + const char *data = ifnet_get_data (conn_name, "config"); + const char *dhcp6; + + if (!data) + return FALSE; + if (!strcmp (data, "shared")) + return FALSE; + if (!strcmp (data, "autoip")) + 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 (const char *conn_name) +{ + const char *data = ifnet_get_data (conn_name, "config"); + + if (!data) + return TRUE; + return strstr (data, "dhcp6") == NULL ? TRUE : FALSE; +} + +gboolean +is_ip4_address (const char *in_address) +{ + const char *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 = NULL; + + 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 (const char *in_address) +{ + struct in6_addr tmp_ip6_addr; + gchar *tmp, *address; + gboolean result = FALSE; + + if (!in_address) + return FALSE; + address = g_strdup (in_address); + 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; + +} + +// 'c' is only used for openrc style +static gchar ** +split_addresses_by_char (const gchar *addresses, const gchar *c) +{ + gchar **ipset; + + if (addresses == NULL) + return NULL; + + if (strchr (addresses, '(') != NULL) { // old baselayout style + gchar *tmp = g_strdup (addresses); + strip_string (tmp, '('); + strip_string (tmp, ')'); + strip_string (tmp, '"'); + strip_string (tmp, '\''); + ipset = g_strsplit (tmp, "\" \"", 0); + g_free(tmp); + } else { // openrc style + if (strstr (addresses, "netmask")) + // There is only one ip address if "netmask" is specified. + // '\n' is not used in config so there will be only one split. + ipset = g_strsplit (addresses, "\n", 0); + else + ipset = g_strsplit (addresses, c, 0); + } + + return ipset; +} + +static gchar ** +split_addresses (const gchar* addresses) +{ + // " " is only used by openrc style + return split_addresses_by_char (addresses, " "); +} + +static gchar ** +split_routes (const gchar* routes) +{ + // "\"" is only used by openrc style + return split_addresses_by_char (routes, "\""); +} + +gboolean +has_ip6_address (const char *conn_name) +{ + gchar **ipset; + guint length; + guint i; + + g_return_val_if_fail (conn_name != NULL, FALSE); + ipset = split_addresses (ifnet_get_data (conn_name, "config")); + length = ipset ? g_strv_length (ipset) : 0; + 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 (const char *conn_name, gboolean (*check_fn) (const char *)) +{ + char *routes = NULL, *end, *tmp; + gboolean success = FALSE; + + g_return_val_if_fail (conn_name != NULL, FALSE); + + routes = g_strdup (ifnet_get_data (conn_name, "routes")); + if (!routes) + return FALSE; + tmp = find_default_gateway_str (routes); + if (tmp) { + g_strstrip (tmp); + if ((end = strstr (tmp, "\"")) != NULL) + *end = '\0'; + if (check_fn (tmp)) + success = TRUE; + } + + g_free (routes); + return success; +} + +static ip_block * +create_ip4_block (gchar * ip) +{ + ip_block *iblock = g_slice_new0 (ip_block); + guint32 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 (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0])) + goto error; + iblock->ip = g_strdup (ip_mask[0]); + prefix = ip_mask[1]; + i = 0; + while (i < length && g_ascii_isdigit (prefix[i])) + i++; + prefix[i] = '\0'; + iblock->prefix = (guint32) atoi (ip_mask[1]); + } else if (strstr (ip, "netmask")) { + ip_mask = g_strsplit (ip, " ", 0); + length = g_strv_length (ip_mask); + if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0])) + goto error; + iblock->ip = g_strdup (ip_mask[0]); + 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->prefix = nm_utils_ip4_netmask_to_prefix (tmp_ip4_addr); + } else { + g_slice_free (ip_block, iblock); + if (!is_ip6_address (ip) && !strstr (ip, "dhcp")) + nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, missing netmask or prefix", ip); + return NULL; + } + if (iblock->prefix == 0 || iblock->prefix > 32) { + nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, invalid prefix", ip); + goto error; + } + g_strfreev (ip_mask); + return iblock; +error: + if (!is_ip6_address (ip)) + nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 address: %s", ip); + g_strfreev (ip_mask); + g_free (iblock->ip); + g_slice_free (ip_block, iblock); + return NULL; +} + +static ip_block * +create_ip_block (gchar * ip) +{ + ip_block *iblock = g_slice_new0 (ip_block); + gchar *dup_ip = g_strdup (ip); + gchar *prefix = NULL; + + if ((prefix = strstr (dup_ip, "/")) != NULL) { + *prefix = '\0'; + prefix++; + } + if (!nm_utils_ipaddr_valid (AF_INET6, dup_ip)) + goto error; + iblock->ip = dup_ip; + if (prefix) { + errno = 0; + iblock->prefix = strtol (prefix, NULL, 10); + if (errno || iblock->prefix <= 0 || iblock->prefix > 128) { + goto error; + } + } else + iblock->prefix = 64; + return iblock; +error: + if (!is_ip4_address (ip)) + nm_log_warn (LOGD_SETTINGS, "Can't handle IPv6 address: %s", ip); + g_slice_free (ip_block, iblock); + g_free (dup_ip); + return NULL; +} + +static char * +get_ip4_gateway (gchar * gateway) +{ + gchar *tmp, *split; + + if (!gateway) + return NULL; + tmp = find_gateway_str (gateway); + if (!tmp) { + nm_log_warn (LOGD_SETTINGS, "Couldn't obtain gateway in \"%s\"", gateway); + return NULL; + } + tmp = g_strdup (tmp); + strip_string (tmp, ' '); + strip_string (tmp, '"'); + + // Only one gateway is selected + if ((split = strstr (tmp, "\"")) != NULL) + *split = '\0'; + + if (!nm_utils_ipaddr_valid (AF_INET, tmp)) + goto error; + return tmp; +error: + if (!is_ip6_address (tmp)) + nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 gateway: %s", tmp); + g_free (tmp); + return NULL; +} + +static char * +get_ip6_next_hop (gchar * next_hop) +{ + gchar *tmp; + + if (!next_hop) + return NULL; + tmp = find_gateway_str (next_hop); + if (!tmp) { + nm_log_warn (LOGD_SETTINGS, "Couldn't obtain next_hop in \"%s\"", next_hop); + return NULL; + } + tmp = g_strdup (tmp); + strip_string (tmp, ' '); + strip_string (tmp, '"'); + g_strstrip (tmp); + if (!nm_utils_ipaddr_valid (AF_INET6, tmp)) + goto error; + return tmp; +error: + if (!is_ip4_address (tmp)) + nm_log_warn (LOGD_SETTINGS, "Can't handle IPv6 next_hop: %s", tmp); + g_free (tmp); + + return NULL; +} + +ip_block * +convert_ip4_config_block (const char *conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip; + char *def_gateway = NULL; + const char *routes; + ip_block *start = NULL, *current = NULL, *iblock = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + + ipset = split_addresses (ifnet_get_data (conn_name, "config")); + length = ipset ? g_strv_length (ipset) : 0; + + routes = ifnet_get_data (conn_name, "routes"); + if (routes) + def_gateway = get_ip4_gateway (strstr (routes, "default")); + + for (i = 0; i < length; i++) { + ip = ipset[i]; + ip = strip_string (ip, '"'); + iblock = create_ip4_block (ip); + if (iblock == NULL) + continue; + if (!iblock->next_hop && def_gateway != NULL) + iblock->next_hop = g_strdup (def_gateway); + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + } + g_strfreev (ipset); + g_free (def_gateway); + return start; +} + +ip_block * +convert_ip6_config_block (const char *conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip; + ip_block *start = NULL, *current = NULL, *iblock = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + ipset = split_addresses (ifnet_get_data (conn_name, "config")); + length = ipset ? g_strv_length (ipset) : 0; + for (i = 0; i < length; i++) { + ip = ipset[i]; + ip = strip_string (ip, '"'); + iblock = create_ip_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 (const char *conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip; + ip_block *start = NULL, *current = NULL, *iblock = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + + ipset = split_routes (ifnet_get_data (conn_name, "routes")); + length = ipset ? g_strv_length (ipset) : 0; + for (i = 0; i < length; i++) { + ip = ipset[i]; + if (find_default_gateway_str (ip) || strstr (ip, "::") + || !find_gateway_str (ip)) + continue; + ip = strip_string (ip, '"'); + iblock = create_ip4_block (ip); + if (iblock == NULL) + continue; + iblock->next_hop = get_ip4_gateway (ip); + if (start == NULL) + start = current = iblock; + else { + current->next = iblock; + current = iblock; + } + } + g_strfreev (ipset); + return start; +} + +ip_block * +convert_ip6_routes_block (const char *conn_name) +{ + gchar **ipset; + guint length; + guint i; + gchar *ip, *tmp_addr; + ip_block *start = NULL, *current = NULL, *iblock = NULL; + + g_return_val_if_fail (conn_name != NULL, NULL); + ipset = split_routes (ifnet_get_data (conn_name, "routes")); + length = ipset ? g_strv_length (ipset) : 0; + for (i = 0; i < length; i++) { + ip = ipset[i]; + ip = strip_string (ip, '"'); + if (ip[0] == '\0') + continue; + if ((tmp_addr = find_default_gateway_str (ip)) != NULL) { + if (!is_ip6_address (tmp_addr)) + continue; + else { + iblock = g_slice_new0 (ip_block); + iblock->ip = g_strdup ("::"); + iblock->prefix = 128; + } + } else + iblock = create_ip_block (ip); + if (iblock == NULL) + continue; + iblock->next_hop = get_ip6_next_hop (ip); + if (iblock->next_hop == NULL) { + destroy_ip_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_free (iblock->ip); + g_free (iblock->next_hop); + g_slice_free (ip_block, iblock); +} + +void +set_ip4_dns_servers (NMSettingIPConfig *s_ip4, const char *conn_name) +{ + const char *dns_servers; + gchar **server_list, *stripped; + guint length, i; + guint32 tmp_ip4_addr; + + dns_servers = ifnet_get_data (conn_name, "dns_servers"); + if (!dns_servers) + return; + stripped = g_strdup (dns_servers); + strip_string (stripped, '"'); + server_list = g_strsplit (stripped, " ", 0); + g_free (stripped); + + length = g_strv_length (server_list); + if (length) + g_object_set (s_ip4, NM_SETTING_IP_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])) + nm_log_warn (LOGD_SETTINGS, "ignored dns: %s\n", server_list[i]); + continue; + } + if (!nm_setting_ip_config_add_dns (s_ip4, server_list[i])) + nm_log_warn (LOGD_SETTINGS, "warning: duplicate DNS server %s", server_list[i]); + } + g_strfreev (server_list); +} + +void +set_ip6_dns_servers (NMSettingIPConfig *s_ip6, const char *conn_name) +{ + const char *dns_servers; + gchar **server_list, *stripped; + guint length, i; + struct in6_addr tmp_ip6_addr; + + dns_servers = ifnet_get_data (conn_name, "dns_servers"); + if (!dns_servers) + return; + + stripped = g_strdup (dns_servers); + strip_string (stripped, '"'); + server_list = g_strsplit (stripped, " ", 0); + g_free (stripped); + + length = g_strv_length (server_list); + if (length) + g_object_set (s_ip6, NM_SETTING_IP_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])) + nm_log_warn (LOGD_SETTINGS, "ignored dns: %s\n", server_list[i]); + continue; + } + if (!nm_setting_ip_config_add_dns (s_ip6, server_list[i])) + nm_log_warn (LOGD_SETTINGS, "warning: duplicate DNS server %s", server_list[i]); + } + g_strfreev (server_list); +} + +gboolean +is_managed (const char *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; +} + +static char * +_has_prefix_impl (char *str, const char *prefix, gsize prefix_len) +{ + if (!g_str_has_prefix (str, prefix)) + return NULL; + str += prefix_len; + if (!g_ascii_isspace (str[0])) + return NULL; + do { + str++; + } while (g_ascii_isspace (str[0])); + return str; +} +#define _has_prefix(STR, PREFIX) _has_prefix_impl (STR, PREFIX, NM_STRLEN (PREFIX)) + +void +get_dhcp_hostname_and_client_id (char **hostname, char **client_id) +{ + const char *dhcp_client; + const gchar *dhcpcd_conf = SYSCONFDIR "/dhcpcd.conf"; + const gchar *dhclient_conf = SYSCONFDIR "/dhcp/dhclient.conf"; + gchar *line = NULL, *tmp = NULL, *contents = NULL, *tmp1; + gchar **all_lines; + guint line_num, i; + gboolean use_dhclient = FALSE; + + *hostname = NULL; + *client_id = NULL; + dhcp_client = nm_dhcp_manager_get_config (nm_dhcp_manager_get ()); + if (dhcp_client) { + if (!strcmp (dhcp_client, "dhclient")) { + g_file_get_contents (dhclient_conf, &contents, NULL, + NULL); + use_dhclient = TRUE; + } 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); + use_dhclient = TRUE; + } 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]; + g_strstrip (line); + if (line[0] == '#' || line[0] == '\0') + continue; + if (!use_dhclient) { + // dhcpcd.conf + if ((tmp = _has_prefix (line, "hostname"))) { + if (tmp[0] != '\0') { + g_free (*hostname); + *hostname = g_strdup (tmp); + } else + nm_log_info (LOGD_SETTINGS, "dhcpcd hostname not defined, ignoring"); + } else if ((tmp = _has_prefix (line, "clientid"))) { + if (tmp[0] != '\0') { + g_free (*client_id); + *client_id = g_strdup (tmp); + } else + nm_log_info (LOGD_SETTINGS, "dhcpcd clientid not defined, ignoring"); + } + } else { + // dhclient.conf + if ((tmp1 = _has_prefix (line, "send"))) { + if ((tmp = _has_prefix (tmp1, "host-name"))) { + strip_string (tmp, ';'); + strip_string (tmp, '"'); + if (tmp[0] != '\0') { + g_free (*hostname); + *hostname = g_strdup (tmp); + } else + nm_log_info (LOGD_SETTINGS, "dhclient hostname not defined, ignoring"); + } else if ((tmp = _has_prefix (tmp1, "dhcp-client-identifier"))) { + strip_string (tmp, ';'); + if (tmp[0] != '\0') { + g_free (*client_id); + *client_id = g_strdup (tmp); + } else + nm_log_info (LOGD_SETTINGS, "dhclient clientid not defined, ignoring"); + } + } + } + } + g_strfreev (all_lines); + g_free (contents); +} + +gchar *backup_file (const gchar* target) +{ + GFile *source, *backup; + gchar* backup_path; + GError *error = NULL; + + source = g_file_new_for_path (target); + + if (!g_file_query_exists (source, NULL)) { + g_object_unref (source); + return NULL; + } + + backup_path = g_strdup_printf ("%s.bak", target); + backup = g_file_new_for_path (backup_path); + + if (!g_file_copy (source, backup, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) { + nm_log_warn (LOGD_SETTINGS, "Backup failed: %s", error->message); + g_free (backup_path); + backup_path = NULL; + g_error_free (error); + } + + g_object_unref (source); + g_object_unref (backup); + + return backup_path; +} diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h new file mode 100644 index 00000000..cc273aa7 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h @@ -0,0 +1,71 @@ +/* -*- 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 "SettingsPlugin-Ifnet" + +#include <arpa/inet.h> + +#include "nm-setting-ip6-config.h" +#include "nm-setting-ip4-config.h" + +#include "nms-ifnet-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 { + char *ip; + guint32 prefix; + char *next_hop; + struct _ip_block *next; +} ip_block; + +gboolean is_static_ip4 (const char *conn_name); +gboolean is_static_ip6 (const char *conn_name); +gboolean is_ip4_address (const char *in_address); +gboolean is_ip6_address (const char *in_address); +gboolean has_ip6_address (const char *conn_name); +gboolean has_default_route (const char *conn_name, gboolean (*check_fn) (const char *)); +gboolean reload_parsers (void); + +ip_block *convert_ip4_config_block (const char *conn_name); +ip_block *convert_ip6_config_block (const char *conn_name); +ip_block *convert_ip4_routes_block (const char *conn_name); +ip_block *convert_ip6_routes_block (const char *conn_name); +void destroy_ip_block (ip_block * iblock); + +void set_ip4_dns_servers (NMSettingIPConfig * s_ip4, const char *conn_name); +void set_ip6_dns_servers (NMSettingIPConfig * s_ip6, const char *conn_name); + +gchar *strip_string (gchar *str, gchar t); +gboolean is_managed (const char *conn_name); + +gboolean is_hex (const char *value); +gboolean is_ascii (const char *value); +gboolean is_true (const char *str); + +void get_dhcp_hostname_and_client_id (char **hostname, char **client_id); + +gchar *backup_file (const gchar* target); +#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.c b/src/settings/plugins/ifnet/nms-ifnet-plugin.c new file mode 100644 index 00000000..8332358f --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.c @@ -0,0 +1,524 @@ +/* -*- 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 "nm-default.h" + +#include "nms-ifnet-plugin.h" + +#include <string.h> +#include <gmodule.h> + +#include "nm-utils.h" +#include "nm-setting-connection.h" +#include "nm-dbus-interface.h" +#include "settings/nm-settings-plugin.h" +#include "nm-config.h" +#include "NetworkManagerUtils.h" + +#include "nms-ifnet-connection.h" +#include "nms-ifnet-net-utils.h" +#include "nms-ifnet-net-parser.h" +#include "nms-ifnet-wpa-parser.h" +#include "nms-ifnet-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 [qiaomuf] prefix." +#define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE + +/*****************************************************************************/ + +typedef void (*FileChangedFn) (gpointer user_data); + +typedef struct { + FileChangedFn callback; + gpointer user_data; +} FileMonitorInfo; + +/*****************************************************************************/ + +typedef struct { + GHashTable *connections; /* uuid::connection */ + gboolean unmanaged_well_known; + + GFileMonitor *net_monitor; + GFileMonitor *wpa_monitor; +} SettingsPluginIfnetPrivate; + +struct _SettingsPluginIfnet { + GObject parent; + SettingsPluginIfnetPrivate _priv; +}; + +struct _SettingsPluginIfnetClass { + GObjectClass parent; +}; + +static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface); + +G_DEFINE_TYPE_EXTENDED (SettingsPluginIfnet, settings_plugin_ifnet, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN, + settings_plugin_interface_init)) + +#define SETTINGS_PLUGIN_IFNET_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfnet, SETTINGS_IS_PLUGIN_IFNET) + +/*****************************************************************************/ + +static SettingsPluginIfnet *settings_plugin_ifnet_get (void); + +NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfnet, settings_plugin_ifnet_get, SETTINGS_TYPE_PLUGIN_IFNET); + +/*****************************************************************************/ + +static void reload_connections (NMSettingsPlugin *config); + +/*****************************************************************************/ + +static gboolean +is_managed_plugin (void) +{ + return nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED, + 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); + } else { + nm_log_warn (LOGD_SETTINGS, "Monitoring %s failed, error: %s", filename, + error == NULL ? "nothing" : (*error)->message); + } + + return monitor; +} + +static void +setup_monitors (NMIfnetConnection *connection, gpointer user_data) +{ + SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); + + if (!nm_config_get_monitor_connection_files (nm_config_get ())) + return; + + if (priv->net_monitor || priv->wpa_monitor) + return; + + priv->net_monitor = monitor_file_changes (CONF_NET_FILE, + (FileChangedFn) reload_connections, + user_data); + priv->wpa_monitor = monitor_file_changes (WPA_SUPPLICANT_CONF, + (FileChangedFn) reload_connections, + user_data); +} + +static void +cancel_monitors (NMIfnetConnection *connection, gpointer user_data) +{ + SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); + + if (priv->net_monitor) { + g_file_monitor_cancel (priv->net_monitor); + g_clear_object (&priv->net_monitor); + } + if (priv->wpa_monitor) { + g_file_monitor_cancel (priv->wpa_monitor); + g_clear_object (&priv->wpa_monitor); + } +} + +static void +connection_removed_cb (NMSettingsConnection *obj, gpointer user_data) +{ + g_hash_table_remove (SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) user_data)->connections, + nm_connection_get_uuid (NM_CONNECTION (obj))); +} + +static void +track_new_connection (SettingsPluginIfnet *self, NMIfnetConnection *connection) +{ + g_hash_table_insert (SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self)->connections, + g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))), + g_object_ref (connection)); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, + G_CALLBACK (connection_removed_cb), + self); +} + +static void +reload_connections (NMSettingsPlugin *config) +{ + SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (config); + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); + GList *conn_names = NULL, *n_iter = NULL; + gboolean auto_refresh; + GError *error = NULL; + + /* save names for removing unused connections */ + GHashTable *new_connections = NULL; + GHashTableIter iter; + const char *uuid; + NMSettingsConnection *candidate; + + if (priv->unmanaged_well_known) + return; + + if (!reload_parsers ()) + return; + + nm_log_info (LOGD_SETTINGS, "Loading connections"); + + auto_refresh = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH, + FALSE); + + new_connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); + + /* Reread on-disk data and refresh in-memory connections from it */ + conn_names = ifnet_get_connection_names (); + for (n_iter = conn_names; n_iter; n_iter = g_list_next (n_iter)) { + NMIfnetConnection *new; + NMIfnetConnection *old; + const char *conn_name = n_iter->data; + + /* read the new connection */ + new = nm_ifnet_connection_new (NULL, conn_name); + if (!new) + continue; + + g_signal_connect (G_OBJECT (new), "ifnet_setup_monitors", + G_CALLBACK (setup_monitors), config); + g_signal_connect (G_OBJECT (new), "ifnet_cancel_monitors", + G_CALLBACK (cancel_monitors), config); + + old = g_hash_table_lookup (priv->connections, + nm_connection_get_uuid (NM_CONNECTION (new))); + if (old && new) { + if (auto_refresh) { + /* If connection has changed, remove the old one and add the + * new one to force a disconnect/reconnect with new settings + */ + if (!nm_connection_compare (NM_CONNECTION (old), + NM_CONNECTION (new), + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | + NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { + nm_log_info (LOGD_SETTINGS, "Auto refreshing %s", conn_name); + + nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (old)); + track_new_connection (self, new); + if (is_managed_plugin () && is_managed (conn_name)) + g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, new); + } + } else { + /* Update existing connection with new settings */ + if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (old), + NM_CONNECTION (new), + NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, + NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + "ifnet-update", + &error)) { + /* Shouldn't ever get here as 'new' was verified by the reader already + * and the UUID did not change. */ + g_assert_not_reached (); + } + g_assert_no_error (error); + nm_log_info (LOGD_SETTINGS, "Connection %s updated", + nm_connection_get_id (NM_CONNECTION (new))); + } + g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED); + } else if (new) { + track_new_connection (self, new); + if (is_managed_plugin () && is_managed (conn_name)) + g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, new); + } + + /* Track all valid connections so we can remove deleted ones later */ + g_hash_table_insert (new_connections, + (gpointer) nm_connection_get_uuid (NM_CONNECTION (new)), + new); + } + + /* remove deleted/unused connections */ + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, (gpointer) &uuid, (gpointer) &candidate)) { + /* only saved connections (which have a conn_name) get removed; unsaved + * ones obviously don't exist in /etc/conf.d/net yet and shouldn't get + * blown away by net file changes. + */ + if ( nm_ifnet_connection_get_conn_name (NM_IFNET_CONNECTION (candidate)) + && !g_hash_table_lookup (new_connections, uuid)) { + nm_settings_connection_signal_remove (candidate); + g_hash_table_iter_remove (&iter); + } + } + g_hash_table_destroy (new_connections); + g_list_free (conn_names); +} + +static NMSettingsConnection * +add_connection (NMSettingsPlugin *config, + NMConnection *source, + gboolean save_to_disk, + GError **error) +{ + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config); + NMIfnetConnection *new = NULL; + + /* Ensure we reject attempts to add the connection long before we're + * asked to write it to disk. + */ + if (!ifnet_can_write_connection (source, error)) + goto out; + + if (save_to_disk) { + if (!ifnet_add_new_connection (source, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL, NULL, error)) + goto out; + reload_connections (config); + new = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (source)); + } else { + new = nm_ifnet_connection_new (source, NULL); + if (new) { + track_new_connection (SETTINGS_PLUGIN_IFNET (config), new); + /* track_new_connection refs 'new' */ + g_object_unref (new); + } + } + +out: + if (!new && error && !*error) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "The ifnet plugin cannot add the connection (unknown error)."); + } + return (NMSettingsConnection *) new; +} + +static void +check_unmanaged (gpointer key, gpointer data, gpointer user_data) +{ + NMIfnetConnection *connection = NM_IFNET_CONNECTION (data); + GSList **list = (GSList **) user_data; + const char *mac, *conn_name; + char *unmanaged_spec; + GSList *iter; + + conn_name = nm_ifnet_connection_get_conn_name (connection); + + if (!conn_name || is_managed (conn_name)) + return; + + nm_log_info (LOGD_SETTINGS, "Checking unmanaged: %s", conn_name); + mac = ifnet_get_data (conn_name, "mac"); + if (mac) + unmanaged_spec = g_strdup_printf ("mac:%s", mac); + else + unmanaged_spec = g_strdup_printf ("interface-name:%s", conn_name); + + /* Just return if the unmanaged spec is already in the list */ + for (iter = *list; iter; iter = g_slist_next (iter)) { + if (g_str_equal (iter->data, unmanaged_spec)) { + g_free (unmanaged_spec); + return; + } + } + + nm_log_info (LOGD_SETTINGS, "Add unmanaged: %s", unmanaged_spec); + *list = g_slist_prepend (*list, unmanaged_spec); +} + +static GSList * +get_unmanaged_specs (NMSettingsPlugin * config) +{ + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config); + GSList *list = NULL; + + nm_log_info (LOGD_SETTINGS, "getting unmanaged specs..."); + g_hash_table_foreach (priv->connections, check_unmanaged, &list); + return list; +} + +static GSList * +get_connections (NMSettingsPlugin *config) +{ + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config); + GSList *connections = NULL; + GHashTableIter iter; + NMIfnetConnection *connection; + + nm_log_info (LOGD_SETTINGS, "(%p) ... get_connections.", config); + + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) { + const char *conn_name = nm_ifnet_connection_get_conn_name (connection); + + if (!conn_name || (!priv->unmanaged_well_known && is_managed (conn_name))) + connections = g_slist_prepend (connections, connection); + } + nm_log_info (LOGD_SETTINGS, "(%p) connections count: %d", + config, g_slist_length (connections)); + return connections; +} + +/*****************************************************************************/ + +static void +get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + case NM_SETTINGS_PLUGIN_PROP_NAME: + g_value_set_string (value, IFNET_PLUGIN_NAME_PRINT); + break; + case NM_SETTINGS_PLUGIN_PROP_INFO: + g_value_set_string (value, IFNET_PLUGIN_INFO); + break; + case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES: + g_value_set_uint (value, + NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +init (NMSettingsPlugin *config) +{ + SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (config); + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); + + nm_log_info (LOGD_SETTINGS, "Initializing!"); + + priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + priv->unmanaged_well_known = !is_managed_plugin (); + nm_log_info (LOGD_SETTINGS, "management mode: %s", + priv->unmanaged_well_known ? "unmanaged" : "managed"); + + setup_monitors (NULL, config); + reload_connections (config); + + nm_log_info (LOGD_SETTINGS, "Initialzation complete!"); +} + +/*****************************************************************************/ + +static void +settings_plugin_ifnet_init (SettingsPluginIfnet * plugin) +{ +} + +static void +dispose (GObject * object) +{ + SettingsPluginIfnet *plugin = SETTINGS_PLUGIN_IFNET (object); + SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) plugin); + + cancel_monitors (NULL, object); + if (priv->connections) { + g_hash_table_destroy (priv->connections); + priv->connections = NULL; + } + + ifnet_destroy (); + wpa_parser_destroy (); + G_OBJECT_CLASS (settings_plugin_ifnet_parent_class)->dispose (object); +} + +static void +settings_plugin_ifnet_class_init (SettingsPluginIfnetClass * req_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (req_class); + + object_class->dispose = dispose; + object_class->get_property = get_property; + + g_object_class_override_property (object_class, + NM_SETTINGS_PLUGIN_PROP_NAME, + NM_SETTINGS_PLUGIN_NAME); + + g_object_class_override_property (object_class, + NM_SETTINGS_PLUGIN_PROP_INFO, + NM_SETTINGS_PLUGIN_INFO); + + g_object_class_override_property (object_class, + NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, + NM_SETTINGS_PLUGIN_CAPABILITIES); +} + +static void +settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface) +{ + plugin_iface->init = init; + plugin_iface->get_connections = get_connections; + plugin_iface->get_unmanaged_specs = get_unmanaged_specs; + plugin_iface->add_connection = add_connection; + plugin_iface->reload_connections = reload_connections; +} + +/*****************************************************************************/ + +G_MODULE_EXPORT GObject * +nm_settings_plugin_factory (void) +{ + return G_OBJECT (g_object_ref (settings_plugin_ifnet_get ())); +} diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.h b/src/settings/plugins/ifnet/nms-ifnet-plugin.h new file mode 100644 index 00000000..f006e7e4 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.h @@ -0,0 +1,38 @@ +/* -*- 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_ + +#define SETTINGS_TYPE_PLUGIN_IFNET (settings_plugin_ifnet_get_type ()) +#define SETTINGS_PLUGIN_IFNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnet)) +#define SETTINGS_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnetClass)) +#define SETTINGS_IS_PLUGIN_IFNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFNET)) +#define SETTINGS_IS_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFNET)) +#define SETTINGS_PLUGIN_IFNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnetClass)) + +typedef struct _SettingsPluginIfnet SettingsPluginIfnet; +typedef struct _SettingsPluginIfnetClass SettingsPluginIfnetClass; + +GType settings_plugin_ifnet_get_type (void); + +#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c new file mode 100644 index 00000000..2b62e886 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c @@ -0,0 +1,584 @@ +/* -*- 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 "nm-default.h" + +#include "nms-ifnet-wpa-parser.h" + +#include <string.h> +#include <stdlib.h> + +#include "nm-utils/nm-hash-utils.h" +#include "settings/nm-settings-plugin.h" + +#include "nms-ifnet-net-parser.h" +#include "nms-ifnet-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, const char *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; + const char *ssid, *value; + char *ssid_key; + gboolean is_hex_ssid; + + /* Every security information should have a ssid */ + ssid = g_hash_table_lookup (security, "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) { + destroy_security (security); + 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) +{ + gpointer orig_key, orig_value; + 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], '"'); + + /* This sucks */ + if (g_hash_table_lookup_extended (network, key_value[0], &orig_key, &orig_value)) { + g_hash_table_remove (network, orig_key); + g_free (orig_key); + g_free (orig_value); + } + + 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 future, use wpa_supplicant instead. */ +static void +add_keys_from_net (void) +{ + 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; + const char *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) { + nm_log_warn (LOGD_SETTINGS, "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 (const char *wpa_supplicant_conf) +{ + GIOChannel *channel = NULL; + gchar *line; + gboolean complete = FALSE; + + wpa_parser_data_changed = FALSE; + wsec_table = g_hash_table_new (nm_str_hash, g_str_equal); + wsec_global_table = g_hash_table_new (nm_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) { + nm_log_warn (LOGD_SETTINGS, "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 (nm_str_hash, g_str_equal); + + do { + gchar *quote_start, *quote_end = NULL, *comment; + + if (line[0] == '#' || line[0] == '\0') { + g_free (line); + continue; + } + /* ignore inline comments unless inside + a double-quoted string */ + if ((quote_start = strchr (line, '"')) != NULL) + quote_end = strrchr (quote_start + 1, '"'); + if ((comment = strchr ((quote_end != NULL) ? + quote_end : line, '#')) != NULL) + *comment = '\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 (); +} + +const char * +wpa_get_value (const char *ssid, const char *key) +{ + GHashTable *target = g_hash_table_lookup (wsec_table, ssid); + + if (target) + return g_hash_table_lookup (target, key); + return NULL; +} + +gboolean +exist_ssid (const char *ssid) +{ + return g_hash_table_lookup (wsec_table, ssid) != NULL; +} + +GHashTable * +_get_hash_table (const char *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 (const char *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 TRUE; + if (!wsec_table || !wsec_global_table) + return FALSE; + + backup_file (config_file); + + channel = g_io_channel_new_file (config_file, "w", NULL); + if (!channel) { + nm_log_warn (LOGD_SETTINGS, "Can't open file %s for writing", config_file); + return FALSE; + } + g_hash_table_iter_init (&iter, wsec_global_table); + nm_log_info (LOGD_SETTINGS, "Writing to %s", config_file); + g_io_channel_write_chars (channel, + "#Generated by NetworkManager\n" + "###### Global Configuration ######\n", + -1, &bytes_written, &error); + if (error) + goto done; + + /* 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) + break; + g_free (out_line); + } + if (error) + goto done; + g_io_channel_write_chars (channel, + "\n###### Security Configuration ######\n", + -1, &bytes_written, &error); + if (error) + goto done; + + 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); + if (error) + goto done; + 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) + goto done; + g_free (out_line); + } + g_io_channel_write_chars (channel, "}\n\n", -1, &bytes_written, &error); + + } + g_io_channel_flush (channel, &error); + if (error) + goto done; + + wpa_parser_data_changed = FALSE; + result = TRUE; +done: + if (error) { + nm_log_warn (LOGD_SETTINGS, "Error writing WPA configuration: %s", error->message); + g_error_free (error); + } + 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 (const char *ssid, const char *key, const char *value) +{ + gpointer old_key = NULL, old_value = NULL; + GHashTable *security = g_hash_table_lookup (wsec_table, ssid); + gchar * stripped = NULL; + + g_return_if_fail (security != NULL); + + if (value){ + stripped = g_strdup(value); + if (strcmp (key, "ssid") != 0 && strcmp (key, "psk") != 0 + && !g_str_has_prefix (key, "wep_key")) + strip_string (stripped, '"'); + } + + /* Remove old key value pairs */ + if (g_hash_table_lookup_extended + (security, key, &old_key, &old_value)) { + if (stripped && !strcmp(old_value, stripped)){ + g_free (stripped); + return; + } + g_hash_table_remove (security, old_key); + g_free (old_key); + g_free (old_value); + } else if (!value) + return; + + /* Add new key value */ + if (stripped) + g_hash_table_insert (security, g_strdup (key), stripped); + wpa_parser_data_changed = TRUE; +} + +gboolean +wpa_has_security (const char *ssid) +{ + return g_hash_table_lookup (wsec_table, ssid) != NULL; +} + +gboolean +wpa_add_security (const char *ssid) +{ + if (wpa_has_security (ssid)) + return TRUE; + else { + GHashTable *security = + g_hash_table_new (nm_str_hash, g_str_equal); + gchar *ssid_i; + + nm_log_info (LOGD_SETTINGS, "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 (const char *ssid) +{ + gpointer old_key, old_value; + + g_return_val_if_fail (wsec_table != NULL && ssid != NULL, FALSE); + nm_log_info (LOGD_SETTINGS, "Deleting security for %s", ssid); + if (!g_hash_table_lookup_extended + (wsec_table, ssid, &old_key, &old_value)) + return FALSE; + g_hash_table_remove (wsec_table, old_key); + g_free (old_key); + destroy_security ((GHashTable *) old_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/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h new file mode 100644 index 00000000..d096f468 --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h @@ -0,0 +1,41 @@ +/* -*- 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 SYSCONFDIR "/wpa_supplicant/wpa_supplicant.conf" + +void wpa_parser_init (const char *wpa_supplicant_conf); +void wpa_parser_destroy (void); + +/* reader functions */ +const char *wpa_get_value (const char *ssid, const char *key); +gboolean exist_ssid (const char *ssid); +GHashTable *_get_hash_table (const char *ssid); +gboolean wpa_has_security (const char *ssid); + +/* writer functions */ +gboolean wpa_flush_to_file (const char *config_file); +void wpa_set_data (const char *ssid, const char *key, const char *value); +gboolean wpa_add_security (const char *ssid); +gboolean wpa_delete_security (const char *ssid); +#endif diff --git a/src/settings/plugins/ifnet/tests/net b/src/settings/plugins/ifnet/tests/net new file mode 100644 index 00000000..a5ac9ca2 --- /dev/null +++ b/src/settings/plugins/ifnet/tests/net @@ -0,0 +1,158 @@ +# 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 :]!). + +modules="!wpa_supplicant" + +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_eth7=( "dhcp" ) +auto_eth7="true" + +# missing config_eth8 +auto_eth8="true" + +#new openrc style +config_eth9="202.117.16.10/24 202.117.17.10/24" +routes_eth9="default via 202.117.16.1 +10.0.0.0/8 via 192.168.0.1 +" +config_eth10="202.117.16.2 netmask 255.255.255.0" +routes_eth10="10.0.0.0/8 via 192.168.0.1" + +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/src/settings/plugins/ifnet/tests/net.all b/src/settings/plugins/ifnet/tests/net.all new file mode 100644 index 00000000..285a4cdf --- /dev/null +++ b/src/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 to 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 to 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/src/settings/plugins/ifnet/tests/nm-system-settings.conf b/src/settings/plugins/ifnet/tests/nm-system-settings.conf new file mode 100644 index 00000000..39bc87b8 --- /dev/null +++ b/src/settings/plugins/ifnet/tests/nm-system-settings.conf @@ -0,0 +1,5 @@ +[main] +plugins=ifnet,keyfile + +[ifnet] +managed=false diff --git a/src/settings/plugins/ifnet/tests/test-ifnet.c b/src/settings/plugins/ifnet/tests/test-ifnet.c new file mode 100644 index 00000000..68f2b928 --- /dev/null +++ b/src/settings/plugins/ifnet/tests/test-ifnet.c @@ -0,0 +1,396 @@ +/* -*- 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 "nm-default.h" + +#include <stdio.h> +#include <string.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <unistd.h> + +#include "nm-utils.h" + +#include "platform/nm-linux-platform.h" +#include "dhcp/nm-dhcp-manager.h" + +#include "settings/plugins/ifnet/nms-ifnet-net-parser.h" +#include "settings/plugins/ifnet/nms-ifnet-net-utils.h" +#include "settings/plugins/ifnet/nms-ifnet-wpa-parser.h" +#include "settings/plugins/ifnet/nms-ifnet-connection-parser.h" + +#include "nm-test-utils-core.h" + +/* Fake config handling; the values it returns don't matter, so this + * is easier than forcing it to read our own config file, etc. + */ +NMDhcpManager * +nm_dhcp_manager_get (void) +{ + return NULL; +} + +const char * +nm_dhcp_manager_get_config (NMDhcpManager *dhcp_manager) +{ + return "dhclient"; +} + +static void +test_getdata (void) +{ + g_assert (ifnet_get_data ("eth1", "config") && + strcmp (ifnet_get_data ("eth1", "config"), "( \"dhcp\" )") == 0); + g_assert (ifnet_get_data ("ppp0", "username") && + strcmp (ifnet_get_data ("ppp0", "username"), "user") == 0); + g_assert (ifnet_get_data ("ppp0", "password") && + strcmp (ifnet_get_data ("ppp0", "password"), "password") == 0); + g_assert (ifnet_get_global_data ("modules") && + strcmp ("!wpa_supplicant", ifnet_get_global_data ("modules")) == 0); +} + +static void +test_is_static (void) +{ + g_assert (!is_static_ip4 ("eth1")); + g_assert (is_static_ip4 ("eth0")); + g_assert (!is_static_ip6 ("eth0")); +} + +static void +test_has_default_route (void) +{ + g_assert (has_default_ip4_route ("eth0")); + g_assert (has_default_ip6_route ("eth4")); + g_assert (!has_default_ip4_route ("eth5") && + !has_default_ip6_route ("eth5")); +} + +static void +test_has_ip6_address (void) +{ + g_assert (has_ip6_address ("eth2")); + g_assert (!has_ip6_address ("eth0")); +} + +static void +test_is_ip4_address (void) +{ + gchar *address1 = "192.168.4.232/24"; + gchar *address2 = "192.168.100.{1..254}/24"; + gchar *address3 = "192.168.4.2555/24"; + + g_assert (is_ip4_address (address1)); + g_assert (is_ip4_address (address2)); + g_assert (!is_ip4_address (address3)); +} + +static void +test_is_ip6_address (void) +{ + gchar *address1 = "4321:0:1:2:3:4:567:89ac/24"; + + g_assert (is_ip6_address (address1)); +} + +static void +check_ip_block (ip_block * iblock, gchar * ip, guint32 prefix, gchar * gateway) +{ + g_assert_cmpstr (ip, ==, iblock->ip); + g_assert (prefix == iblock->prefix); + g_assert_cmpstr (gateway, ==, iblock->next_hop); +} + +static void +test_convert_ipv4_config_block (void) +{ + ip_block *iblock = convert_ip4_config_block ("eth0"); + ip_block *tmp = iblock; + + g_assert (iblock != NULL); + check_ip_block (iblock, "202.117.16.121", 24, "202.117.16.1"); + iblock = iblock->next; + destroy_ip_block (tmp); + g_assert (iblock != NULL); + check_ip_block (iblock, "192.168.4.121", 24, "202.117.16.1"); + destroy_ip_block (iblock); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, + "*Can't handle IPv4 address*202.117.16.1211*"); + iblock = convert_ip4_config_block ("eth2"); + g_test_assert_expected_messages (); + g_assert (iblock != NULL && iblock->next == NULL); + check_ip_block (iblock, "192.168.4.121", 24, NULL); + destroy_ip_block (iblock); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, + "*missing netmask or prefix*"); + iblock = convert_ip4_config_block ("eth3"); + g_assert (iblock == NULL); +} + +static void +test_convert_ipv4_routes_block (void) +{ + ip_block *iblock = convert_ip4_routes_block ("eth0"); + ip_block *tmp = iblock; + + g_assert (iblock != NULL); + check_ip_block (iblock, "192.168.4.0", 24, "192.168.4.1"); + iblock = iblock->next; + destroy_ip_block (tmp); + g_assert (iblock == NULL); + + iblock = convert_ip4_routes_block ("eth9"); + tmp = iblock; + + g_assert (iblock != NULL); + check_ip_block (iblock, "10.0.0.0", 8, "192.168.0.1"); + iblock = iblock->next; + destroy_ip_block (tmp); + g_assert (iblock == NULL); +} + +static void +test_wpa_parser (void) +{ + const char *value; + + g_assert (exist_ssid ("example")); + + g_assert (exist_ssid ("static-wep-test")); + value = wpa_get_value ("static-wep-test", "key_mgmt"); + g_assert_cmpstr (value, ==, "NONE"); + value = wpa_get_value ("static-wep-test", "wep_key0"); + g_assert_cmpstr (value, ==, "\"abcde\""); + + g_assert (exist_ssid ("leap-example")); + + value = wpa_get_value ("test-with-hash-in-psk", "psk"); + g_assert_cmpstr (value, ==, "\"xjtudlc3731###asdfasdfasdf\""); +} + +static void +test_strip_string (void) +{ + 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, '"'); + g_assert_cmpstr (result, ==, "default via 202.117.16.1"); + g_free (result_b); +} + +static void +test_is_unmanaged (void) +{ + g_assert (is_managed ("eth0")); + g_assert (!is_managed ("eth4")); +} + +static void +test_new_connection (void) +{ + GError *error = NULL; + NMConnection *connection; + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, + "*Can't handle IPv4 address*202.117.16.1211*"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, + "*Can't handle IPv6 address*202.117.16.1211*"); + connection = ifnet_update_connection_from_config_block ("eth2", NULL, &error); + g_test_assert_expected_messages (); + g_assert (connection != NULL); + g_object_unref (connection); + + connection = ifnet_update_connection_from_config_block ("qiaomuf", NULL, &error); + g_assert (connection != NULL); + g_object_unref (connection); + + connection = ifnet_update_connection_from_config_block ("myxjtu2", NULL, &error); + g_assert (connection != NULL); + g_object_unref (connection); + + connection = ifnet_update_connection_from_config_block ("eth9", NULL, &error); + g_assert (connection != NULL); + g_object_unref (connection); + + connection = ifnet_update_connection_from_config_block ("eth10", NULL, &error); + g_assert (connection != NULL); + g_object_unref (connection); +} + +static void +kill_backup (char **path) +{ + if (*path) { + unlink (*path); + g_free (*path); + *path = NULL; + } +} + +#define NET_GEN_NAME "net.generate" +#define SUP_GEN_NAME "wpa_supplicant.conf.generate" + +static void +test_update_connection (void) +{ + GError *error = NULL; + NMConnection *connection; + gboolean success; + char *backup = NULL; + char *basepath = TEST_IFNET_DIR; + + connection = ifnet_update_connection_from_config_block ("eth0", basepath, &error); + g_assert (connection != NULL); + + success = ifnet_update_parsers_by_connection (connection, "eth0", + NET_GEN_NAME, + SUP_GEN_NAME, + NULL, + &backup, + &error); + kill_backup (&backup); + g_assert (success); + g_object_unref (connection); + + connection = ifnet_update_connection_from_config_block ("0xab3ace", basepath, &error); + g_assert (connection != NULL); + + success = ifnet_update_parsers_by_connection (connection, "0xab3ace", + NET_GEN_NAME, + SUP_GEN_NAME, + NULL, + &backup, + &error); + kill_backup (&backup); + g_assert (success); + g_object_unref (connection); + + unlink (NET_GEN_NAME); + unlink (SUP_GEN_NAME); +} + +static void +test_add_connection (void) +{ + NMConnection *connection; + char *backup = NULL; + const char *basepath = TEST_IFNET_DIR; + + connection = ifnet_update_connection_from_config_block ("eth0", basepath, NULL); + g_assert (ifnet_add_new_connection (connection, NET_GEN_NAME, SUP_GEN_NAME, NULL, &backup, NULL)); + kill_backup (&backup); + g_object_unref (connection); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, + "*Can't handle ipv4 address: brd, missing netmask or prefix*"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, + "*Can't handle ipv4 address: 202.117.16.255, missing netmask or prefix*"); + connection = ifnet_update_connection_from_config_block ("myxjtu2", basepath, NULL); + g_test_assert_expected_messages (); + g_assert (ifnet_add_new_connection (connection, NET_GEN_NAME, SUP_GEN_NAME, NULL, &backup, NULL)); + kill_backup (&backup); + g_object_unref (connection); + + unlink (NET_GEN_NAME); + unlink (SUP_GEN_NAME); +} + +static void +test_delete_connection (void) +{ + GError *error = NULL; + NMConnection *connection; + char *backup = NULL; + + connection = ifnet_update_connection_from_config_block ("eth7", NULL, &error); + g_assert (connection != NULL); + g_assert (ifnet_delete_connection_in_parsers ("eth7", NET_GEN_NAME, SUP_GEN_NAME, &backup)); + kill_backup (&backup); + g_object_unref (connection); + + connection = ifnet_update_connection_from_config_block ("qiaomuf", NULL, &error); + g_assert (connection != NULL); + g_assert (ifnet_delete_connection_in_parsers ("qiaomuf", NET_GEN_NAME, SUP_GEN_NAME, &backup)); + kill_backup (&backup); + g_object_unref (connection); + + unlink (NET_GEN_NAME); + unlink (SUP_GEN_NAME); +} + +static void +test_missing_config (void) +{ + gs_free_error GError *error = NULL; + NMConnection *connection; + + connection = ifnet_update_connection_from_config_block ("eth8", NULL, &error); + g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert (connection == NULL && error != NULL); +} + +NMTST_DEFINE (); + +#define TPATH "/settings/plugins/ifnet/" + +int +main (int argc, char **argv) +{ + int ret; + + nm_linux_platform_setup (); + + nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT"); + + ifnet_init (TEST_IFNET_DIR "/net"); + wpa_parser_init (TEST_IFNET_DIR "/wpa_supplicant.conf"); + + g_test_add_func (TPATH "strip-string", test_strip_string); + g_test_add_func (TPATH "is-static", test_is_static); + g_test_add_func (TPATH "has-ip6-address", test_has_ip6_address); + g_test_add_func (TPATH "has-default-route", test_has_default_route); + g_test_add_func (TPATH "get-data", test_getdata); + g_test_add_func (TPATH "is-ip4-address", test_is_ip4_address); + g_test_add_func (TPATH "is-ip6-address", test_is_ip6_address); + g_test_add_func (TPATH "convert-ip4-config", test_convert_ipv4_config_block); + g_test_add_func (TPATH "convert-ip4-routes", test_convert_ipv4_routes_block); + g_test_add_func (TPATH "is-unmanaged", test_is_unmanaged); + g_test_add_func (TPATH "wpa-parser", test_wpa_parser); + g_test_add_func (TPATH "new-connection", test_new_connection); + g_test_add_func (TPATH "update-connection", test_update_connection); + g_test_add_func (TPATH "add-connection", test_add_connection); + g_test_add_func (TPATH "delete-connection", test_delete_connection); + g_test_add_func (TPATH "missing-config", test_missing_config); + + ret = g_test_run (); + + ifnet_destroy (); + wpa_parser_destroy (); + + return ret; +} diff --git a/src/settings/plugins/ifnet/tests/test_ca_cert.pem b/src/settings/plugins/ifnet/tests/test_ca_cert.pem new file mode 100644 index 00000000..ef1be20d --- /dev/null +++ b/src/settings/plugins/ifnet/tests/test_ca_cert.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEjzCCA3egAwIBAgIJAOvnZPt59yIZMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD +VQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcw +FQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UE +AxMEdGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0wOTAzMTAx +NTEyMTRaFw0xOTAzMDgxNTEyMTRaMIGLMQswCQYDVQQGEwJVUzESMBAGA1UECBMJ +QmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5NeSBDb21wYW55 +IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UEAxMEdGVzdDEcMBoGCSqGSIb3 +DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKot9j+/+CX1/gZLgJHIXCRgCItKLGnf7qGbgqB9T2ACBqR0jllKWwDKrcWU +xjXNIc+GF9Wnv+lX6G0Okn4Zt3/uRNobL+2b/yOF7M3Td3/9W873zdkQQX930YZc +Rr8uxdRPP5bxiCgtcw632y21sSEbG9mjccAUnV/0jdvfmMNj0i8gN6E0fMBiJ9S3 +FkxX/KFvt9JWE9CtoyL7ki7UIDq+6vj7Gd5N0B3dOa1y+rRHZzKlJPcSXQSEYUS4 +HmKDwiKSVahft8c4tDn7KPi0vex91hlgZVd3usL2E/Vq7o5D9FAZ5kZY0AdFXwdm +J4lO4Mj7ac7GE4vNERNcXVIX59sCAwEAAaOB8zCB8DAdBgNVHQ4EFgQUuDU3Mr7P +T3n1e3Sy8hBauoDFahAwgcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDF +ahChgZGkgY4wgYsxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAO +BgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQL +EwdUZXN0aW5nMQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRl +c3QuY29tggkA6+dk+3n3IhkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC +AQEAVRG4aALIvCXCiKfe7K+iJxjBVRDFPEf7JWA9LGgbFOn6pNvbxonrR+0BETdc +JV1ET4ct2xsE7QNFIkp9GKRC+6J32zCo8qtLCD5+v436r8TUG2/t2JRMkb9I2XVT +p7RJoot6M0Ltf8KNQUPYh756xmKZ4USfQUwc58MOSDGY8VWEXJOYij9Pf0e0c52t +qiCEjXH7uXiS8Pgq9TYm7AkWSOrglYhSa83x0f8mtT8Q15nBESIHZ6o8FAS2bBgn +B0BkrKRjtBUkuJG3vTox+bYINh2Gxi1JZHWSV1tN5z3hd4VFcKqanW5OgQwToBqp +3nniskIjbH0xjgZf/nVMyLnjxg== +-----END CERTIFICATE----- diff --git a/src/settings/plugins/ifnet/tests/wpa_supplicant.conf b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf new file mode 100644 index 00000000..917d495d --- /dev/null +++ b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf @@ -0,0 +1,70 @@ +# 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 +} + +# LEAP with dynamic WEP keys +network={ + ssid="leap-example" + key_mgmt=IEEE8021X + eap=LEAP + identity="user" + password="foobar" +} + +# 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 +} + +# Wildcard match for SSID (plaintext APs only). This example select any +# open AP regardless of its SSID. +network={ + key_mgmt=NONE +} + +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="test_ca_cert.pem" + phase2="auth=CHAP" + priority=20 +} + +network={ + ssid="test-with-hash-in-psk" + key_mgmt=WPA-PSK + psk="xjtudlc3731###asdfasdfasdf" +} + |