diff options
Diffstat (limited to 'src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c')
| -rw-r--r-- | src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c | 686 |
1 files changed, 686 insertions, 0 deletions
diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c new file mode 100644 index 00000000..a4974a96 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -0,0 +1,686 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Alexander Sack <asac@ubuntu.com> + * Copyright (C) 2008 Canonical Ltd. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifupdown-parser.h" + +#include <arpa/inet.h> +#include <stdlib.h> +#include <ctype.h> + +#include "nm-core-internal.h" +#include "settings/nm-settings-plugin.h" + +#include "nms-ifupdown-plugin.h" +#include "nms-ifupdown-parser.h" + +/*****************************************************************************/ + +#define _NMLOG_PREFIX_NAME "ifupdown" +#define _NMLOG_DOMAIN LOGD_SETTINGS +#define _NMLOG(level, ...) \ + nm_log((level), \ + _NMLOG_DOMAIN, \ + NULL, \ + NULL, \ + "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME ": " _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +/*****************************************************************************/ + +#define _str_has_prefix(val, prefix, require_suffix) \ + ({ \ + const char *_val = (val); \ + \ + (strncmp(_val, "" prefix "", NM_STRLEN(prefix)) == 0) \ + && (!(require_suffix) || _val[NM_STRLEN(prefix)] != '\0'); \ + }) + +static const char * +_ifupdownplugin_guess_connection_type(if_block *block) +{ + const char *ret_type = NULL; + + if (nm_streq0(ifparser_getkey(block, "inet"), "ppp")) + ret_type = NM_SETTING_PPP_SETTING_NAME; + else { + if_data *ifb; + + c_list_for_each_entry (ifb, &block->data_lst_head, data_lst) { + if (_str_has_prefix(ifb->key, "wireless-", FALSE) + || _str_has_prefix(ifb->key, "wpa-", FALSE)) { + ret_type = NM_SETTING_WIRELESS_SETTING_NAME; + break; + } + } + if (!ret_type) + ret_type = NM_SETTING_WIRED_SETTING_NAME; + } + + _LOGI("guessed connection type (%s) = %s", block->name, ret_type); + return ret_type; +} + +struct _Mapping { + const char * domain; + const gpointer target; +}; + +static gpointer +map_by_mapping(struct _Mapping *mapping, const char *key) +{ + struct _Mapping *curr = mapping; + + while (curr->domain) { + if (nm_streq(curr->domain, key)) + return curr->target; + curr++; + } + return NULL; +} + +static void +update_wireless_setting_from_if_block(NMConnection *connection, if_block *block) +{ + if_data * curr; + const char * value = ifparser_getkey(block, "inet"); + struct _Mapping mapping[] = {{"ssid", "ssid"}, + {"essid", "ssid"}, + {"mode", "mode"}, + {NULL, NULL}}; + + NMSettingWireless *wireless_setting = NULL; + + if (nm_streq0(value, "ppp")) + return; + + _LOGI("update wireless settings (%s).", block->name); + wireless_setting = NM_SETTING_WIRELESS(nm_setting_wireless_new()); + + c_list_for_each_entry (curr, &block->data_lst_head, data_lst) { + if (_str_has_prefix(curr->key, "wireless-", TRUE)) { + const char *newkey = map_by_mapping(mapping, curr->key + NM_STRLEN("wireless-")); + + _LOGI("wireless setting key: %s='%s'", newkey, curr->data); + if (nm_streq0(newkey, "ssid")) { + GBytes *ssid; + int len = strlen(curr->data); + + ssid = g_bytes_new(curr->data, len); + g_object_set(wireless_setting, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref(ssid); + _LOGI("setting wireless ssid = %d", len); + } else if (nm_streq0(newkey, "mode")) { + if (!g_ascii_strcasecmp(curr->data, "Managed") + || !g_ascii_strcasecmp(curr->data, "Auto")) + g_object_set(wireless_setting, + NM_SETTING_WIRELESS_MODE, + NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + else if (!g_ascii_strcasecmp(curr->data, "Ad-Hoc")) + g_object_set(wireless_setting, + NM_SETTING_WIRELESS_MODE, + NM_SETTING_WIRELESS_MODE_ADHOC, + NULL); + else if (!g_ascii_strcasecmp(curr->data, "Master")) + g_object_set(wireless_setting, + NM_SETTING_WIRELESS_MODE, + NM_SETTING_WIRELESS_MODE_AP, + NULL); + else + _LOGW("Invalid mode '%s' (not 'Ad-Hoc', 'Ap', 'Managed', or 'Auto')", + curr->data); + } else { + g_object_set(wireless_setting, newkey, curr->data, NULL); + } + } else if (_str_has_prefix(curr->key, "wpa-", TRUE)) { + const char *newkey = map_by_mapping(mapping, curr->key + NM_STRLEN("wpa-")); + + if (nm_streq0(newkey, "ssid")) { + GBytes *ssid; + int len = strlen(curr->data); + + ssid = g_bytes_new(curr->data, len); + g_object_set(wireless_setting, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref(ssid); + _LOGI("setting wpa ssid = %d", len); + } else if (newkey) { + g_object_set(wireless_setting, newkey, curr->data, NULL); + _LOGI("setting wpa newkey(%s)=data(%s)", newkey, curr->data); + } + } + } + nm_connection_add_setting(connection, (NMSetting *) wireless_setting); +} + +typedef char *(*IfupdownStrDupeFunc)(gconstpointer value, gpointer data); +typedef gpointer (*IfupdownStrToTypeFunc)(const char *value); + +static char * +normalize_dupe_wireless_key(gpointer value, gpointer data) +{ + char *valuec = value; + char *endc = valuec + strlen(valuec); + char *delim = valuec; + char *next = delim; + char *result = malloc(strlen(valuec) + 1); + char *result_cur = result; + + while (*delim && (next = strchr(delim, '-')) != NULL) { + if (next == delim) { + delim++; + continue; + } + strncpy(result_cur, delim, next - delim); + result_cur += next - delim; + delim = next + 1; + } + if (*delim && strlen(valuec) > GPOINTER_TO_UINT(delim - valuec)) { + strncpy(result_cur, delim, endc - delim); + result_cur += endc - delim; + } + *result_cur = '\0'; + return result; +} + +static char * +normalize_dupe(gpointer value, gpointer data) +{ + return g_strdup(value); +} + +static char * +normalize_tolower(gpointer value, gpointer data) +{ + return g_ascii_strdown(value, -1); +} + +static char * +normalize_psk(gpointer value, gpointer data) +{ + if (strlen(value) >= 8 && strlen(value) <= 64) + return g_strdup(value); + return NULL; +} + +static gpointer +string_to_gpointerint(const char *data) +{ + int result = (int) strtol(data, NULL, 10); + return GINT_TO_POINTER(result); +} + +static gpointer +string_to_glist_of_strings(const char *data) +{ + GSList *ret = NULL; + char * string = (char *) data; + while (string) { + char *next = NULL; + if ((next = strchr(string, ' ')) || (next = strchr(string, '\t')) + || (next = strchr(string, '\0'))) { + char *part = g_strndup(string, (next - string)); + ret = g_slist_append(ret, part); + if (*next) + string = next + 1; + else + string = NULL; + } else { + string = NULL; + } + } + return ret; +} + +static void +slist_free_all(gpointer slist) +{ + g_slist_free_full((GSList *) slist, g_free); +} + +static void +update_wireless_security_setting_from_if_block(NMConnection *connection, if_block *block) +{ + if_data * curr; + const char * value = ifparser_getkey(block, "inet"); + struct _Mapping mapping[] = {{"psk", "psk"}, + {"identity", "leap-username"}, + {"password", "leap-password"}, + {"key", "wep-key0"}, + {"key-mgmt", "key-mgmt"}, + {"group", "group"}, + {"pairwise", "pairwise"}, + {"proto", "proto"}, + {"pin", "pin"}, + {"wep-key0", "wep-key0"}, + {"wep-key1", "wep-key1"}, + {"wep-key2", "wep-key2"}, + {"wep-key3", "wep-key3"}, + {"wep-tx-keyidx", "wep-tx-keyidx"}, + {NULL, NULL}}; + + struct _Mapping dupe_mapping[] = {{"psk", normalize_psk}, + {"identity", normalize_dupe}, + {"password", normalize_dupe}, + {"key", normalize_dupe_wireless_key}, + {"key-mgmt", normalize_tolower}, + {"group", normalize_tolower}, + {"pairwise", normalize_tolower}, + {"proto", normalize_tolower}, + {"pin", normalize_dupe}, + {"wep-key0", normalize_dupe_wireless_key}, + {"wep-key1", normalize_dupe_wireless_key}, + {"wep-key2", normalize_dupe_wireless_key}, + {"wep-key3", normalize_dupe_wireless_key}, + {"wep-tx-keyidx", normalize_dupe}, + {NULL, NULL}}; + + struct _Mapping type_mapping[] = {{"group", string_to_glist_of_strings}, + {"pairwise", string_to_glist_of_strings}, + {"proto", string_to_glist_of_strings}, + {"wep-tx-keyidx", string_to_gpointerint}, + {NULL, NULL}}; + + struct _Mapping free_type_mapping[] = {{"group", slist_free_all}, + {"pairwise", slist_free_all}, + {"proto", slist_free_all}, + {NULL, NULL}}; + + NMSettingWirelessSecurity *wireless_security_setting; + NMSettingWireless * s_wireless; + gboolean security = FALSE; + + if (nm_streq0(value, "ppp")) + return; + + s_wireless = nm_connection_get_setting_wireless(connection); + g_return_if_fail(s_wireless); + + _LOGI("update wireless security settings (%s).", block->name); + wireless_security_setting = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new()); + + c_list_for_each_entry (curr, &block->data_lst_head, data_lst) { + if (_str_has_prefix(curr->key, "wireless-", TRUE)) { + const char * key = curr->key + NM_STRLEN("wireless-"); + char * property_value = NULL; + gpointer typed_property_value = NULL; + const char * newkey = map_by_mapping(mapping, key); + IfupdownStrDupeFunc dupe_func = map_by_mapping(dupe_mapping, key); + IfupdownStrToTypeFunc type_map_func = map_by_mapping(type_mapping, key); + GFreeFunc free_func = map_by_mapping(free_type_mapping, key); + if (!newkey || !dupe_func) + goto next; + + property_value = (*dupe_func)(curr->data, connection); + _LOGI("setting wireless security key: %s=%s", newkey, property_value); + + if (type_map_func) { + errno = 0; + typed_property_value = (*type_map_func)(property_value); + if (errno) + goto wireless_next; + } + + g_object_set(wireless_security_setting, + newkey, + typed_property_value ?: property_value, + NULL); + security = TRUE; + +wireless_next: + g_free(property_value); + if (typed_property_value && free_func) + (*free_func)(typed_property_value); + + } else if (_str_has_prefix(curr->key, "wpa-", TRUE)) { + const char * key = curr->key + NM_STRLEN("wpa-"); + char * property_value = NULL; + gpointer typed_property_value = NULL; + const char * newkey = map_by_mapping(mapping, key); + IfupdownStrDupeFunc dupe_func = map_by_mapping(dupe_mapping, key); + IfupdownStrToTypeFunc type_map_func = map_by_mapping(type_mapping, key); + GFreeFunc free_func = map_by_mapping(free_type_mapping, key); + if (!newkey || !dupe_func) + goto next; + + property_value = (*dupe_func)(curr->data, connection); + _LOGI("setting wpa security key: %s=%s", + newkey, + NM_IN_STRSET(newkey, + "key", + "leap-password", + "pin", + "psk", + "wep-key0", + "wep-key1", + "wep-key2", + "wep-key3") + ? "<omitted>" + : property_value); + + if (type_map_func) { + errno = 0; + typed_property_value = (*type_map_func)(property_value); + if (errno) + goto wpa_next; + } + + g_object_set(wireless_security_setting, + newkey, + typed_property_value ?: property_value, + NULL); + security = TRUE; + +wpa_next: + g_free(property_value); + if (free_func && typed_property_value) + (*free_func)(typed_property_value); + } +next:; + } + + if (security) + nm_connection_add_setting(connection, NM_SETTING(wireless_security_setting)); +} + +static void +update_wired_setting_from_if_block(NMConnection *connection, if_block *block) +{ + NMSettingWired *s_wired = NULL; + s_wired = NM_SETTING_WIRED(nm_setting_wired_new()); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); +} + +static void +ifupdown_ip4_add_dns(NMSettingIPConfig *s_ip4, const char *dns) +{ + gs_free const char **list = NULL; + const char ** iter; + guint32 addr; + + if (dns == NULL) + return; + + list = nm_utils_strsplit_set(dns, " \t"); + for (iter = list; iter && *iter; iter++) { + if (!inet_pton(AF_INET, *iter, &addr)) { + _LOGW(" ignoring invalid nameserver '%s'", *iter); + continue; + } + + if (!nm_setting_ip_config_add_dns(s_ip4, *iter)) + _LOGW(" duplicate DNS domain '%s'", *iter); + } +} + +static gboolean +update_ip4_setting_from_if_block(NMConnection *connection, if_block *block, GError **error) +{ + gs_unref_object NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG(nm_setting_ip4_config_new()); + const char * type = ifparser_getkey(block, "inet"); + + if (!nm_streq0(type, "static")) { + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + } else { + guint32 tmp_mask; + NMIPAddress *addr; + const char * address_v; + const char * netmask_v; + const char * gateway_v; + const char * nameserver_v; + const char * nameservers_v; + const char * search_v; + guint32 netmask_int = 32; + + /* Address */ + address_v = ifparser_getkey(block, "address"); + if (!address_v) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IPv4 address"); + return FALSE; + } + + /* mask/prefix */ + netmask_v = ifparser_getkey(block, "netmask"); + if (netmask_v) { + if (strlen(netmask_v) < 7) { + netmask_int = atoi(netmask_v); + } else if (!inet_pton(AF_INET, netmask_v, &tmp_mask)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IPv4 netmask '%s'", + netmask_v); + return FALSE; + } else { + netmask_int = nm_utils_ip4_netmask_to_prefix(tmp_mask); + } + } + + /* Add the new address to the setting */ + addr = nm_ip_address_new(AF_INET, address_v, netmask_int, error); + if (!addr) + return FALSE; + + if (nm_setting_ip_config_add_address(s_ip4, addr)) { + _LOGI("addresses count: %d", nm_setting_ip_config_get_num_addresses(s_ip4)); + } else { + _LOGI("ignoring duplicate IP4 address"); + } + nm_ip_address_unref(addr); + + /* gateway */ + gateway_v = ifparser_getkey(block, "gateway"); + if (gateway_v) { + if (!nm_utils_ipaddr_is_valid(AF_INET, gateway_v)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IPv4 gateway '%s'", + gateway_v); + return FALSE; + } + if (!nm_setting_ip_config_get_gateway(s_ip4)) + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_v, NULL); + } + + nameserver_v = ifparser_getkey(block, "dns-nameserver"); + ifupdown_ip4_add_dns(s_ip4, nameserver_v); + + nameservers_v = ifparser_getkey(block, "dns-nameservers"); + ifupdown_ip4_add_dns(s_ip4, nameservers_v); + + if (!nm_setting_ip_config_get_num_dns(s_ip4)) + _LOGI("No dns-nameserver configured in /etc/network/interfaces"); + + /* DNS searches */ + search_v = ifparser_getkey(block, "dns-search"); + if (search_v) { + gs_free const char **list = NULL; + const char ** iter; + + list = nm_utils_strsplit_set(search_v, " \t"); + for (iter = list; iter && *iter; iter++) { + if (!nm_setting_ip_config_add_dns_search(s_ip4, *iter)) + _LOGW(" duplicate DNS domain '%s'", *iter); + } + } + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); + } + + nm_connection_add_setting(connection, NM_SETTING(g_steal_pointer(&s_ip4))); + return TRUE; +} + +static void +ifupdown_ip6_add_dns(NMSettingIPConfig *s_ip6, const char *dns) +{ + gs_free const char **list = NULL; + const char ** iter; + struct in6_addr addr; + + if (dns == NULL) + return; + + list = nm_utils_strsplit_set(dns, " \t"); + for (iter = list; iter && *iter; iter++) { + if (!inet_pton(AF_INET6, *iter, &addr)) { + _LOGW(" ignoring invalid nameserver '%s'", *iter); + continue; + } + + if (!nm_setting_ip_config_add_dns(s_ip6, *iter)) + _LOGW(" duplicate DNS domain '%s'", *iter); + } +} + +static gboolean +update_ip6_setting_from_if_block(NMConnection *connection, if_block *block, GError **error) +{ + gs_unref_object NMSettingIPConfig *s_ip6 = NM_SETTING_IP_CONFIG(nm_setting_ip6_config_new()); + const char * type = ifparser_getkey(block, "inet6"); + + if (!NM_IN_STRSET(type, "static", "v4tunnel")) { + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + } else { + NMIPAddress *addr; + const char * address_v; + const char * prefix_v; + const char * gateway_v; + const char * nameserver_v; + const char * nameservers_v; + const char * search_v; + guint prefix_int; + + address_v = ifparser_getkey(block, "address"); + if (!address_v) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IPv6 address"); + return FALSE; + } + + prefix_v = ifparser_getkey(block, "netmask"); + if (prefix_v) + prefix_int = _nm_utils_ascii_str_to_int64(prefix_v, 10, 0, 128, G_MAXINT); + else + prefix_int = 128; + + addr = nm_ip_address_new(AF_INET6, address_v, prefix_int, error); + if (!addr) + return FALSE; + + if (nm_setting_ip_config_add_address(s_ip6, addr)) { + _LOGI("addresses count: %d", nm_setting_ip_config_get_num_addresses(s_ip6)); + } else { + _LOGI("ignoring duplicate IP6 address"); + } + nm_ip_address_unref(addr); + + gateway_v = ifparser_getkey(block, "gateway"); + if (gateway_v) { + if (!nm_utils_ipaddr_is_valid(AF_INET6, gateway_v)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IPv6 gateway '%s'", + gateway_v); + return FALSE; + } + if (!nm_setting_ip_config_get_gateway(s_ip6)) + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, gateway_v, NULL); + } + + nameserver_v = ifparser_getkey(block, "dns-nameserver"); + ifupdown_ip6_add_dns(s_ip6, nameserver_v); + + nameservers_v = ifparser_getkey(block, "dns-nameservers"); + ifupdown_ip6_add_dns(s_ip6, nameservers_v); + + if (!nm_setting_ip_config_get_num_dns(s_ip6)) + _LOGI("No dns-nameserver configured in /etc/network/interfaces"); + + search_v = ifparser_getkey(block, "dns-search"); + if (search_v) { + gs_free const char **list = NULL; + const char ** iter; + + list = nm_utils_strsplit_set(search_v, " \t"); + for (iter = list; iter && *iter; iter++) { + if (!nm_setting_ip_config_add_dns_search(s_ip6, *iter)) + _LOGW(" duplicate DNS domain '%s'", *iter); + } + } + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL); + } + + nm_connection_add_setting(connection, NM_SETTING(g_steal_pointer(&s_ip6))); + return TRUE; +} + +NMConnection * +ifupdown_new_connection_from_if_block(if_block *block, gboolean autoconnect, GError **error) +{ + gs_unref_object NMConnection *connection = NULL; + const char * type; + gs_free char * idstr = NULL; + gs_free char * uuid = NULL; + NMSettingConnection * s_con; + + connection = nm_simple_connection_new(); + + s_con = NM_SETTING_CONNECTION(nm_setting_connection_new()); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + type = _ifupdownplugin_guess_connection_type(block); + idstr = g_strconcat("Ifupdown (", block->name, ")", NULL); + + uuid = nm_utils_uuid_generate_from_string(idstr, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + g_object_set(s_con, + NM_SETTING_CONNECTION_TYPE, + type, + NM_SETTING_CONNECTION_INTERFACE_NAME, + block->name, + NM_SETTING_CONNECTION_ID, + idstr, + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, + (gboolean)(!!autoconnect), + NULL); + + _LOGD("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s", + block->name, + type, + idstr, + nm_setting_connection_get_uuid(s_con)); + + if (nm_streq(type, NM_SETTING_WIRED_SETTING_NAME)) + update_wired_setting_from_if_block(connection, block); + else if (nm_streq(type, NM_SETTING_WIRELESS_SETTING_NAME)) { + update_wireless_setting_from_if_block(connection, block); + update_wireless_security_setting_from_if_block(connection, block); + } + + if (ifparser_haskey(block, "inet6")) { + if (!update_ip6_setting_from_if_block(connection, block, error)) + return FALSE; + } else { + if (!update_ip4_setting_from_if_block(connection, block, error)) + return FALSE; + } + + if (!nm_connection_normalize(connection, NULL, NULL, error)) + return NULL; + + return g_steal_pointer(&connection); +} |