diff options
Diffstat (limited to 'src/settings/plugins/ifnet/nms-ifnet-net-utils.c')
| -rw-r--r-- | src/settings/plugins/ifnet/nms-ifnet-net-utils.c | 830 |
1 files changed, 830 insertions, 0 deletions
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; +} |