diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/core/settings/plugins/ifupdown | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/core/settings/plugins/ifupdown')
33 files changed, 2347 insertions, 0 deletions
diff --git a/src/core/settings/plugins/ifupdown/meson.build b/src/core/settings/plugins/ifupdown/meson.build new file mode 100644 index 00000000..dd252783 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/meson.build @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +libnms_ifupdown_core = static_library( + 'nms-ifupdown-core', + sources: files( + 'nms-ifupdown-interface-parser.c', + 'nms-ifupdown-parser.c', + ), + dependencies: core_default_dep, + c_args: daemon_c_flags, +) + +libnm_settings_plugin_ifupdown = shared_module( + 'nm-settings-plugin-ifupdown', + sources: 'nms-ifupdown-plugin.c', + dependencies: core_plugin_dep, + c_args: daemon_c_flags, + link_with: libnms_ifupdown_core, + link_args: ldflags_linker_script_settings, + link_depends: linker_script_settings, + install: true, + install_dir: nm_plugindir, +) + +core_plugins += libnm_settings_plugin_ifupdown + +if enable_tests + subdir('tests') +endif diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c new file mode 100644 index 00000000..a8a9c40a --- /dev/null +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Tom Parker <palfrey@tevp.net> + * Copyright (C) 2004 Tom Parker + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifupdown-interface-parser.h" + +#include <stdio.h> +#include <stdlib.h> +#include <wordexp.h> +#include <libgen.h> + +#include "nm-utils.h" + +/*****************************************************************************/ + +static void +_ifparser_source(if_parser *parser, const char *path, const char *en_dir, int quiet, int dir); + +/*****************************************************************************/ + +#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__)) + +/*****************************************************************************/ + +static void +add_block(if_parser *parser, const char *type, const char *name) +{ + if_block *ifb; + gsize l_type, l_name; + + l_type = strlen(type) + 1; + l_name = strlen(name) + 1; + + ifb = g_malloc(sizeof(if_block) + l_type + l_name); + memcpy((char *) ifb->name, name, l_name); + ifb->type = &ifb->name[l_name]; + memcpy((char *) ifb->type, type, l_type); + c_list_init(&ifb->data_lst_head); + c_list_link_tail(&parser->block_lst_head, &ifb->block_lst); +} + +static void +add_data(if_parser *parser, const char *key, const char *data) +{ + if_block *last_block; + if_data * ifd; + char * idx; + gsize l_key, l_data; + + last_block = c_list_last_entry(&parser->block_lst_head, if_block, block_lst); + + /* Check if there is a block where we can attach our data */ + if (!last_block) + return; + + l_key = strlen(key) + 1; + l_data = strlen(data) + 1; + + ifd = g_malloc(sizeof(if_data) + l_key + l_data); + memcpy((char *) ifd->key, key, l_key); + ifd->data = &ifd->key[l_key]; + memcpy((char *) ifd->data, data, l_data); + + /* Normalize keys. Convert '_' to '-', as ifupdown accepts both variants. + * When querying keys via ifparser_getkey(), use '-'. */ + idx = (char *) ifd->key; + while ((idx = strchr(idx, '_'))) + *(idx++) = '-'; + + c_list_link_tail(&last_block->data_lst_head, &ifd->data_lst); +} + +/* join values in src with spaces into dst; dst needs to be large enough */ +static char * +join_values_with_spaces(char *dst, char **src) +{ + if (dst != NULL) { + *dst = '\0'; + if (src != NULL && *src != NULL) { + strcat(dst, *src); + + for (src++; *src != NULL; src++) { + strcat(dst, " "); + strcat(dst, *src); + } + } + } + return (dst); +} + +static void +_recursive_ifparser(if_parser *parser, const char *eni_file, int quiet) +{ + FILE *inp; + char line[255]; + int skip_to_block = 1; + int skip_long_line = 0; + int offs = 0; + + /* Check if interfaces file exists and open it */ + if (!g_file_test(eni_file, G_FILE_TEST_EXISTS)) { + if (!quiet) + _LOGW("interfaces file %s doesn't exist", eni_file); + return; + } + inp = fopen(eni_file, "re"); + if (inp == NULL) { + if (!quiet) + _LOGW("Can't open %s", eni_file); + return; + } + if (!quiet) + _LOGI(" interface-parser: parsing file %s", eni_file); + + while (!feof(inp)) { + char *token[128]; /* 255 chars can only be split into 127 tokens */ + char value[255]; /* large enough to join previously split tokens */ + char *safeptr; + int toknum; + int len = 0; + + char *ptr = fgets(line + offs, 255 - offs, inp); + if (ptr == NULL) + break; + + len = strlen(line); + /* skip over-long lines */ + if (!feof(inp) && len > 0 && line[len - 1] != '\n') { + if (!skip_long_line) { + if (!quiet) + _LOGW("Skipping over-long-line '%s...'", line); + } + skip_long_line = 1; + continue; + } + + /* trailing '\n' found: remove it & reset offset to 0 */ + if (len > 0 && line[len - 1] == '\n') { + line[--len] = '\0'; + offs = 0; + } + + /* if we're in long_line_skip mode, terminate it for real next line */ + if (skip_long_line) { + if (len == 0 || line[len - 1] != '\\') + skip_long_line = 0; + continue; + } + + /* unwrap wrapped lines */ + if (len > 0 && line[len - 1] == '\\') { + offs = len - 1; + continue; + } + +#define SPACES " \t" + /* tokenize input; */ + for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr); token[toknum] != NULL; + toknum++, token[toknum] = strtok_r(NULL, SPACES, &safeptr)) + ; + + /* ignore comments and empty lines */ + if (toknum == 0 || *token[0] == '#') + continue; + + if (toknum < 2) { + if (!quiet) { + _LOGW("Can't parse interface line '%s'", join_values_with_spaces(value, token)); + } + skip_to_block = 1; + continue; + } + + /* There are six different stanzas: + * iface, mapping, auto, allow-*, source, and source-directory. + * Create a block for each of them except source and source-directory. */ + + /* iface stanza takes at least 3 parameters */ + if (nm_streq(token[0], "iface")) { + if (toknum < 4) { + if (!quiet) { + _LOGW("Can't parse iface line '%s'", join_values_with_spaces(value, token)); + } + continue; + } + add_block(parser, token[0], token[1]); + skip_to_block = 0; + add_data(parser, token[2], join_values_with_spaces(value, token + 3)); + } + /* auto and allow-auto stanzas are equivalent, + * both can take multiple interfaces as parameters: add one block for each */ + else if (NM_IN_STRSET(token[0], "auto", "allow-auto")) { + int i; + + for (i = 1; i < toknum; i++) + add_block(parser, "auto", token[i]); + skip_to_block = 0; + } else if (nm_streq(token[0], "mapping")) { + add_block(parser, token[0], join_values_with_spaces(value, token + 1)); + skip_to_block = 0; + } + /* allow-* can take multiple interfaces as parameters: add one block for each */ + else if (g_str_has_prefix(token[0], "allow-")) { + int i; + for (i = 1; i < toknum; i++) + add_block(parser, token[0], token[i]); + skip_to_block = 0; + } + /* source and source-directory stanzas take one or more paths as parameters */ + else if (NM_IN_STRSET(token[0], "source", "source-directory")) { + int i; + char *en_dir; + + skip_to_block = 0; + en_dir = g_path_get_dirname(eni_file); + for (i = 1; i < toknum; ++i) { + if (nm_streq(token[0], "source-directory")) + _ifparser_source(parser, token[i], en_dir, quiet, TRUE); + else + _ifparser_source(parser, token[i], en_dir, quiet, FALSE); + } + g_free(en_dir); + } else { + if (skip_to_block) { + if (!quiet) { + _LOGW("ignoring out-of-block data '%s'", join_values_with_spaces(value, token)); + } + } else + add_data(parser, token[0], join_values_with_spaces(value, token + 1)); + } + } + fclose(inp); + + if (!quiet) + _LOGI(" interface-parser: finished parsing file %s", eni_file); +} + +static void +_ifparser_source(if_parser *parser, const char *path, const char *en_dir, int quiet, int dir) +{ + char * abs_path; + const char *item; + wordexp_t we; + GDir * source_dir; + GError * error = NULL; + uint i; + + if (g_path_is_absolute(path)) + abs_path = g_strdup(path); + else + abs_path = g_build_filename(en_dir, path, NULL); + + if (!quiet) + _LOGI(" interface-parser: source line includes interfaces file(s) %s", abs_path); + + /* ifupdown uses WRDE_NOCMD for wordexp. */ + if (wordexp(abs_path, &we, WRDE_NOCMD)) { + if (!quiet) + _LOGW("word expansion for %s failed", abs_path); + } else { + for (i = 0; i < we.we_wordc; i++) { + if (dir) { + source_dir = g_dir_open(we.we_wordv[i], 0, &error); + if (!source_dir) { + if (!quiet) { + _LOGW("Failed to open directory %s: %s", we.we_wordv[i], error->message); + } + g_clear_error(&error); + } else { + while ((item = g_dir_read_name(source_dir))) + _ifparser_source(parser, item, we.we_wordv[i], quiet, FALSE); + g_dir_close(source_dir); + } + } else + _recursive_ifparser(parser, we.we_wordv[i], quiet); + } + wordfree(&we); + } + g_free(abs_path); +} + +if_parser * +ifparser_parse(const char *eni_file, int quiet) +{ + if_parser *parser; + + parser = g_slice_new(if_parser); + c_list_init(&parser->block_lst_head); + _recursive_ifparser(parser, eni_file, quiet); + return parser; +} + +static void +_destroy_data(if_data *ifd) +{ + c_list_unlink_stale(&ifd->data_lst); + g_free(ifd); +} + +static void +_destroy_block(if_block *ifb) +{ + if_data *ifd; + + while ((ifd = c_list_first_entry(&ifb->data_lst_head, if_data, data_lst))) + _destroy_data(ifd); + c_list_unlink_stale(&ifb->block_lst); + g_free(ifb); +} + +void +ifparser_destroy(if_parser *parser) +{ + if_block *ifb; + + while ((ifb = c_list_first_entry(&parser->block_lst_head, if_block, block_lst))) + _destroy_block(ifb); + g_slice_free(if_parser, parser); +} + +if_block * +ifparser_getfirst(if_parser *parser) +{ + return c_list_first_entry(&parser->block_lst_head, if_block, block_lst); +} + +guint +ifparser_get_num_blocks(if_parser *parser) +{ + return c_list_length(&parser->block_lst_head); +} + +if_block * +ifparser_getif(if_parser *parser, const char *iface) +{ + if_block *ifb; + + c_list_for_each_entry (ifb, &parser->block_lst_head, block_lst) { + if (nm_streq(ifb->type, "iface") && nm_streq(ifb->name, iface)) + return ifb; + } + return NULL; +} + +static if_data * +ifparser_findkey(if_block *iface, const char *key) +{ + if_data *ifd; + + c_list_for_each_entry (ifd, &iface->data_lst_head, data_lst) { + if (nm_streq(ifd->key, key)) + return ifd; + } + return NULL; +} + +const char * +ifparser_getkey(if_block *iface, const char *key) +{ + if_data *ifd; + + ifd = ifparser_findkey(iface, key); + return ifd ? ifd->data : NULL; +} + +gboolean +ifparser_haskey(if_block *iface, const char *key) +{ + return !!ifparser_findkey(iface, key); +} + +guint +ifparser_get_num_info(if_block *iface) +{ + return c_list_length(&iface->data_lst_head); +} diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/core/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h new file mode 100644 index 00000000..dd0e15fb --- /dev/null +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Tom Parker <palfrey@tevp.net> + * Copyright (C) 2004 Tom Parker + */ + +#ifndef _INTERFACE_PARSER_H +#define _INTERFACE_PARSER_H + +#include "c-list/src/c-list.h" + +typedef struct { + CList data_lst; + const char *data; + const char key[]; +} if_data; + +typedef struct { + CList block_lst; + CList data_lst_head; + const char *type; + const char name[]; +} if_block; + +typedef struct { + CList block_lst_head; +} if_parser; + +if_parser *ifparser_parse(const char *eni_file, int quiet); + +void ifparser_destroy(if_parser *parser); +NM_AUTO_DEFINE_FCN0(if_parser *, _nm_auto_ifparser, ifparser_destroy); +#define nm_auto_ifparser nm_auto(_nm_auto_ifparser) + +if_block * ifparser_getif(if_parser *parser, const char *iface); +if_block * ifparser_getfirst(if_parser *parser); +const char *ifparser_getkey(if_block *iface, const char *key); +gboolean ifparser_haskey(if_block *iface, const char *key); + +guint ifparser_get_num_blocks(if_parser *parser); +guint ifparser_get_num_info(if_block *iface); + +#endif 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); +} diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.h new file mode 100644 index 00000000..ced98dba --- /dev/null +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Alexander Sack <asac@ubuntu.com> + * Copyright (C) 2008 Canonical Ltd. + */ + +#ifndef __NMS_IFUPDOWN_PARSER_H__ +#define __NMS_IFUPDOWN_PARSER_H__ + +#include "nm-connection.h" +#include "nms-ifupdown-interface-parser.h" + +NMConnection * +ifupdown_new_connection_from_if_block(if_block *block, gboolean autoconnect, GError **error); + +#endif /* __NMS_IFUPDOWN_PARSER_H__ */ diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c new file mode 100644 index 00000000..34523f8d --- /dev/null +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -0,0 +1,399 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Alexander Sack <asac@ubuntu.com> + * Copyright (C) 2007, 2008 Canonical Ltd. + * Copyright (C) 2009 - 2011 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifupdown-plugin.h" + +#include "nm-core-internal.h" +#include "nm-core-utils.h" +#include "nm-config.h" +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" + +#include "nms-ifupdown-interface-parser.h" +#include "nms-ifupdown-parser.h" + +#define ENI_INTERFACES_FILE "/etc/network/interfaces" + +#define IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT TRUE + +/*****************************************************************************/ + +typedef struct { + NMConnection * connection; + NMSettingsStorage *storage; +} StorageData; + +typedef struct { + /* Stores an entry for blocks/interfaces read from /e/n/i and (if exists) + * the StorageData associated with the block. + */ + GHashTable *eni_ifaces; + + bool ifupdown_managed : 1; + + bool initialized : 1; + + bool already_reloaded : 1; +} NMSIfupdownPluginPrivate; + +struct _NMSIfupdownPlugin { + NMSettingsPlugin parent; + NMSIfupdownPluginPrivate _priv; +}; + +struct _NMSIfupdownPluginClass { + NMSettingsPluginClass parent; +}; + +G_DEFINE_TYPE(NMSIfupdownPlugin, nms_ifupdown_plugin, NM_TYPE_SETTINGS_PLUGIN) + +#define NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMSIfupdownPlugin, NMS_IS_IFUPDOWN_PLUGIN) + +/*****************************************************************************/ + +#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__)) + +/*****************************************************************************/ + +static GHashTable *load_eni_ifaces(NMSIfupdownPlugin *self); + +/*****************************************************************************/ + +static void +_storage_data_destroy(StorageData *sd) +{ + if (!sd) + return; + nm_g_object_unref(sd->connection); + nm_g_object_unref(sd->storage); + g_slice_free(StorageData, sd); +} + +/*****************************************************************************/ + +static void +initialize(NMSIfupdownPlugin *self) +{ + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self); + gboolean ifupdown_managed; + + nm_assert(!priv->initialized); + + priv->initialized = TRUE; + + ifupdown_managed = nm_config_data_get_value_boolean(NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, + NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, + !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT); + _LOGI("management mode: %s", ifupdown_managed ? "managed" : "unmanaged"); + priv->ifupdown_managed = ifupdown_managed; + + priv->eni_ifaces = load_eni_ifaces(self); +} + +static void +reload_connections(NMSettingsPlugin * plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfupdownPlugin * self = NMS_IFUPDOWN_PLUGIN(plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self); + gs_unref_hashtable GHashTable *eni_ifaces_old = NULL; + GHashTableIter iter; + StorageData * sd; + StorageData * sd2; + const char * block_name; + + if (!priv->initialized) + initialize(self); + else if (!priv->already_reloaded) { + /* This is the first call to reload, but we are already initialized. + * + * This happens because during start NMSettings first queries unmanaged-specs, + * and then issues a reload call right away. + * + * On future reloads, we really want to load /e/n/i again. */ + priv->already_reloaded = TRUE; + } else { + eni_ifaces_old = priv->eni_ifaces; + priv->eni_ifaces = load_eni_ifaces(self); + + g_hash_table_iter_init(&iter, eni_ifaces_old); + while (g_hash_table_iter_next(&iter, (gpointer *) &block_name, (gpointer *) &sd)) { + if (!sd) + continue; + + sd2 = g_hash_table_lookup(priv->eni_ifaces, block_name); + if (!sd2) + continue; + + nm_assert(nm_streq(nm_settings_storage_get_uuid(sd->storage), + nm_settings_storage_get_uuid(sd2->storage))); + nm_g_object_ref_set(&sd2->storage, sd->storage); + g_hash_table_iter_remove(&iter); + } + } + + if (!priv->ifupdown_managed) + _LOGD("load: no connections due to managed=false"); + + g_hash_table_iter_init(&iter, priv->eni_ifaces); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &sd)) { + gs_unref_object NMConnection *connection = NULL; + + if (!sd) + continue; + + connection = g_steal_pointer(&sd->connection); + + if (!priv->ifupdown_managed) + continue; + + _LOGD("load: %s (%s)", + nm_settings_storage_get_uuid(sd->storage), + nm_connection_get_id(connection)); + callback(plugin, sd->storage, connection, user_data); + } + if (eni_ifaces_old && priv->ifupdown_managed) { + g_hash_table_iter_init(&iter, eni_ifaces_old); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &sd)) { + if (!sd) + continue; + _LOGD("unload: %s", nm_settings_storage_get_uuid(sd->storage)); + callback(plugin, sd->storage, NULL, user_data); + } + } +} + +/*****************************************************************************/ + +static GSList * +_unmanaged_specs(GHashTable *eni_ifaces) +{ + gs_free const char **keys = NULL; + GSList * specs = NULL; + guint i, len; + + keys = nm_utils_strdict_get_keys(eni_ifaces, TRUE, &len); + for (i = len; i > 0;) { + i--; + specs = g_slist_prepend(specs, + g_strdup_printf(NM_MATCH_SPEC_INTERFACE_NAME_TAG "=%s", keys[i])); + } + return specs; +} + +static GSList * +get_unmanaged_specs(NMSettingsPlugin *plugin) +{ + NMSIfupdownPlugin * self = NMS_IFUPDOWN_PLUGIN(plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self); + + if (G_UNLIKELY(!priv->initialized)) + initialize(self); + + if (priv->ifupdown_managed) + return NULL; + + _LOGD("unmanaged-specs: unmanaged devices count %u", g_hash_table_size(priv->eni_ifaces)); + + return _unmanaged_specs(priv->eni_ifaces); +} + +/*****************************************************************************/ + +static GHashTable * +load_eni_ifaces(NMSIfupdownPlugin *self) +{ + gs_unref_hashtable GHashTable *eni_ifaces = NULL; + gs_unref_hashtable GHashTable *auto_ifaces = NULL; + nm_auto_ifparser if_parser *parser = NULL; + if_block * block; + StorageData * sd; + + eni_ifaces = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) _storage_data_destroy); + + parser = ifparser_parse(ENI_INTERFACES_FILE, 0); + + c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { + if (NM_IN_STRSET(block->type, "auto", "allow-hotplug")) { + if (!auto_ifaces) + auto_ifaces = g_hash_table_new(nm_str_hash, g_str_equal); + g_hash_table_add(auto_ifaces, (char *) block->name); + } + } + + c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { + if (NM_IN_STRSET(block->type, "auto", "allow-hotplug")) + continue; + + if (nm_streq(block->type, "iface")) { + gs_free_error GError *local = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMSettingsStorage *storage = NULL; + const char * uuid = NULL; + StorageData * sd_repl; + + /* Bridge configuration */ + if (g_str_has_prefix(block->name, "br")) { + /* Try to find bridge ports */ + const char *ports = ifparser_getkey(block, "bridge-ports"); + + if (ports) { + int state = 0; + gs_free const char **port_ifaces = NULL; + gsize i; + + _LOGD("parse: found bridge ports %s for %s", ports, block->name); + + port_ifaces = nm_utils_strsplit_set(ports, " \t"); + for (i = 0; port_ifaces && port_ifaces[i]; i++) { + const char *token = port_ifaces[i]; + + /* Skip crazy stuff like regex or all */ + if (nm_streq(token, "all")) + continue; + + /* Small SM to skip everything inside regex */ + if (nm_streq(token, "regex")) { + state++; + continue; + } + if (nm_streq(token, "noregex")) { + state--; + continue; + } + if (nm_streq(token, "none")) + continue; + if (state == 0) { + sd = g_hash_table_lookup(eni_ifaces, block->name); + if (!sd) { + _LOGD("parse: adding bridge port \"%s\"", token); + g_hash_table_insert(eni_ifaces, g_strdup(token), NULL); + } else { + _LOGD("parse: adding bridge port \"%s\" (have connection %s)", + token, + nm_settings_storage_get_uuid(sd->storage)); + } + } + } + } + continue; + } + + /* Skip loopback configuration */ + if (nm_streq(block->name, "lo")) + continue; + + sd_repl = g_hash_table_lookup(eni_ifaces, block->name); + if (sd_repl) { + _LOGD("parse: replace connection \"%s\" (%s)", + block->name, + nm_settings_storage_get_uuid(sd_repl->storage)); + storage = g_steal_pointer(&sd_repl->storage); + g_hash_table_remove(eni_ifaces, block->name); + } + + connection = ifupdown_new_connection_from_if_block( + block, + auto_ifaces && g_hash_table_contains(auto_ifaces, block->name), + &local); + + if (!connection) { + _LOGD("parse: adding place holder for \"%s\"%s%s%s", + block->name, + NM_PRINT_FMT_QUOTED(local, " (", local->message, ")", "")); + sd = NULL; + } else { + nmtst_connection_assert_unchanging(connection); + uuid = nm_connection_get_uuid(connection); + + if (!storage) + storage = nm_settings_storage_new(NM_SETTINGS_PLUGIN(self), uuid, NULL); + + sd = g_slice_new(StorageData); + *sd = (StorageData){ + .connection = g_steal_pointer(&connection), + .storage = g_steal_pointer(&storage), + }; + _LOGD("parse: adding connection \"%s\" (%s)", block->name, uuid); + } + + g_hash_table_replace(eni_ifaces, g_strdup(block->name), sd); + continue; + } + + if (nm_streq(block->type, "mapping")) { + sd = g_hash_table_lookup(eni_ifaces, block->name); + if (!sd) { + _LOGD("parse: adding mapping \"%s\"", block->name); + g_hash_table_insert(eni_ifaces, g_strdup(block->name), NULL); + } else { + _LOGD("parse: adding mapping \"%s\" (have connection %s)", + block->name, + nm_settings_storage_get_uuid(sd->storage)); + } + continue; + } + } + + nm_clear_pointer(&auto_ifaces, g_hash_table_destroy); + + return g_steal_pointer(&eni_ifaces); +} + +/*****************************************************************************/ + +static void +nms_ifupdown_plugin_init(NMSIfupdownPlugin *self) +{} + +static void +dispose(GObject *object) +{ + NMSIfupdownPlugin * plugin = NMS_IFUPDOWN_PLUGIN(object); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(plugin); + + nm_clear_pointer(&priv->eni_ifaces, g_hash_table_destroy); + + G_OBJECT_CLASS(nms_ifupdown_plugin_parent_class)->dispose(object); +} + +static void +nms_ifupdown_plugin_class_init(NMSIfupdownPluginClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS(klass); + + object_class->dispose = dispose; + + plugin_class->plugin_name = "ifupdown"; + plugin_class->reload_connections = reload_connections; + plugin_class->get_unmanaged_specs = get_unmanaged_specs; +} + +/*****************************************************************************/ + +G_MODULE_EXPORT NMSettingsPlugin * + nm_settings_plugin_factory(void) +{ + return g_object_new(NMS_TYPE_IFUPDOWN_PLUGIN, NULL); +} diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h new file mode 100644 index 00000000..edf1b317 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Alexander Sack <asac@ubuntu.com> + * Copyright (C) 2008 Canonical Ltd. + */ + +#ifndef __NMS_IFUPDOWN_PLUGIN_H__ +#define __NMS_IFUPDOWN_PLUGIN_H__ + +#define PLUGIN_NAME "ifupdown" + +#define NMS_TYPE_IFUPDOWN_PLUGIN (nms_ifupdown_plugin_get_type()) +#define NMS_IFUPDOWN_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin)) +#define NMS_IFUPDOWN_PLUGIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) +#define NMS_IS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_IFUPDOWN_PLUGIN)) +#define NMS_IS_IFUPDOWN_PLUGIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMS_TYPE_IFUPDOWN_PLUGIN)) +#define NMS_IFUPDOWN_PLUGIN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) + +typedef struct _NMSIfupdownPlugin NMSIfupdownPlugin; +typedef struct _NMSIfupdownPluginClass NMSIfupdownPluginClass; + +GType nms_ifupdown_plugin_get_type(void); + +#endif /* __NMS_IFUPDOWN_PLUGIN_H__ */ diff --git a/src/core/settings/plugins/ifupdown/tests/meson.build b/src/core/settings/plugins/ifupdown/tests/meson.build new file mode 100644 index 00000000..882287cb --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/meson.build @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +exe = executable( + 'test-ifupdown', + 'test-ifupdown.c', + dependencies: libNetworkManagerTest_dep, + c_args: test_c_flags, + link_with: libnms_ifupdown_core, +) + +test( + 'ifupdown/test-ifupdown', + test_script, + args: test_args + [exe.full_path()], +) diff --git a/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c new file mode 100644 index 00000000..6e3eb0e7 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c @@ -0,0 +1,648 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-core-internal.h" + +#include "settings/plugins/ifupdown/nms-ifupdown-interface-parser.h" +#include "settings/plugins/ifupdown/nms-ifupdown-parser.h" + +#include "nm-test-utils-core.h" + +#define TEST_DIR NM_BUILD_SRCDIR "/src/core/settings/plugins/ifupdown/tests" + +/*****************************************************************************/ + +#define _connection_from_if_block(block) \ + ({ \ + NMConnection *_con; \ + if_block * _block = (block); \ + GError * _local = NULL; \ + \ + g_assert(_block); \ + _con = ifupdown_new_connection_from_if_block(_block, FALSE, &_local); \ + nmtst_assert_success(NM_IS_CONNECTION(_con), _local); \ + nmtst_assert_connection_verifies_without_normalization(_con); \ + _con; \ + }) + +#define _connection_first_from_parser(parser) \ + ({ \ + if_parser *_parser = (parser); \ + \ + g_assert(_parser); \ + _connection_from_if_block(ifparser_getfirst(_parser)); \ + }) + +/*****************************************************************************/ + +typedef struct { + char *key; + char *data; +} ExpectedKey; + +typedef struct { + char * type; + char * name; + GSList *keys; +} ExpectedBlock; + +typedef struct { + GSList *blocks; +} Expected; + +static ExpectedKey * +expected_key_new(const char *key, const char *data) +{ + ExpectedKey *k; + + k = g_malloc0(sizeof(ExpectedKey)); + k->key = g_strdup(key); + k->data = g_strdup(data); + return k; +} + +static void +expected_key_free(gpointer ptr) +{ + ExpectedKey *k = ptr; + + g_assert(k); + g_free(k->key); + g_free(k->data); + memset(k, 0, sizeof(ExpectedKey)); + g_free(k); +} + +static ExpectedBlock * +expected_block_new(const char *type, const char *name) +{ + ExpectedBlock *b; + + g_assert(type); + g_assert(name); + b = g_malloc0(sizeof(ExpectedBlock)); + g_assert(b); + b->type = g_strdup(type); + b->name = g_strdup(name); + return b; +} + +static void +expected_block_free(gpointer ptr) +{ + ExpectedBlock *b = ptr; + + g_assert(b); + g_slist_free_full(b->keys, expected_key_free); + g_free(b->type); + g_free(b->name); + memset(b, 0, sizeof(ExpectedBlock)); + g_free(b); +} + +static void +expected_block_add_key(ExpectedBlock *b, ExpectedKey *k) +{ + g_assert(b); + g_assert(k); + b->keys = g_slist_append(b->keys, k); +} + +static Expected * +expected_new(void) +{ + return g_malloc0(sizeof(Expected)); +} + +static void +expected_add_block(Expected *e, ExpectedBlock *b) +{ + g_assert(e); + g_assert(b); + e->blocks = g_slist_append(e->blocks, b); +} + +static void +expected_free(Expected *e) +{ + g_assert(e); + g_slist_free_full(e->blocks, expected_block_free); + memset(e, 0, sizeof(Expected)); + g_free(e); +} + +NM_AUTO_DEFINE_FCN_VOID0(Expected *, _nm_auto_free_expected, expected_free); +#define nm_auto_free_expected nm_auto(_nm_auto_free_expected) + +static void +compare_expected_to_ifparser(if_parser *parser, Expected *e) +{ + if_block *n; + GSList * biter, *kiter; + + g_assert_cmpint(g_slist_length(e->blocks), ==, ifparser_get_num_blocks(parser)); + + biter = e->blocks; + c_list_for_each_entry (n, &parser->block_lst_head, block_lst) { + if_data * m; + ExpectedBlock *b = biter->data; + + g_assert(b->type && n->type); + g_assert_cmpstr(b->type, ==, n->type); + g_assert(b->name); + g_assert_cmpstr(b->name, ==, n->name); + + g_assert_cmpint(g_slist_length(b->keys), ==, ifparser_get_num_info(n)); + + kiter = b->keys; + c_list_for_each_entry (m, &n->data_lst_head, data_lst) { + ExpectedKey *k = kiter->data; + + g_assert(k->key); + g_assert_cmpstr(k->key, ==, m->key); + g_assert(k->data && m->data); + g_assert_cmpstr(k->data, ==, m->data); + + kiter = g_slist_next(kiter); + } + g_assert(!kiter); + + biter = g_slist_next(biter); + } + g_assert(!biter); +} + +static void +dump_blocks(if_parser *parser) +{ + if_block *n; + + g_message("\n***************************************************"); + c_list_for_each_entry (n, &parser->block_lst_head, block_lst) { + if_data *m; + + // each block start with its type & name + // (single quotes used to show typ & name baoundaries) + g_print("'%s' '%s'\n", n->type, n->name); + + // each key-value pair within a block is indented & separated by a tab + // (single quotes used to show type & name boundaries) + c_list_for_each_entry (m, &n->data_lst_head, data_lst) + g_print("\t'%s'\t'%s'\n", m->key, m->data); + + // blocks are separated by an empty line + g_print("\n"); + } + g_message("##################################################\n"); +} + +static if_parser * +init_ifparser_with_file(const char *file) +{ + if_parser * parser; + gs_free char *tmp = NULL; + + tmp = g_strdup_printf("%s/%s", TEST_DIR, file); + parser = ifparser_parse(tmp, 1); + g_assert(parser); + return parser; +} + +static void +test1_ignore_line_before_first_block(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test1"); + + e = expected_new(); + b = expected_block_new("auto", "eth0"); + expected_add_block(e, b); + b = expected_block_new("iface", "eth0"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "dhcp")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test2_wrapped_line(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test2"); + + e = expected_new(); + b = expected_block_new("auto", "lo"); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test3_wrapped_multiline_multiarg(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test3"); + + e = expected_new(); + b = expected_block_new("allow-hotplug", "eth0"); + expected_add_block(e, b); + b = expected_block_new("allow-hotplug", "wlan0"); + expected_add_block(e, b); + b = expected_block_new("allow-hotplug", "bnep0"); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test4_allow_auto_is_auto(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test4"); + + e = expected_new(); + b = expected_block_new("auto", "eth0"); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test5_allow_auto_multiarg(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test5"); + + e = expected_new(); + b = expected_block_new("allow-hotplug", "eth0"); + expected_add_block(e, b); + b = expected_block_new("allow-hotplug", "wlan0"); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test6_mixed_whitespace(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test6"); + + e = expected_new(); + b = expected_block_new("iface", "lo"); + expected_block_add_key(b, expected_key_new("inet", "loopback")); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test7_long_line(void) +{ + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test7"); + + g_assert_cmpint(ifparser_get_num_blocks(parser), ==, 0); +} + +static void +test8_long_line_wrapped(void) +{ + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test8"); + + g_assert_cmpint(ifparser_get_num_blocks(parser), ==, 0); +} + +static void +test9_wrapped_lines_in_block(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test9"); + + e = expected_new(); + b = expected_block_new("iface", "eth0"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "static")); + expected_block_add_key(b, expected_key_new("address", "10.250.2.3")); + expected_block_add_key(b, expected_key_new("netmask", "255.255.255.192")); + expected_block_add_key(b, expected_key_new("broadcast", "10.250.2.63")); + expected_block_add_key(b, expected_key_new("gateway", "10.250.2.50")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test11_complex_wrap(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test11"); + + e = expected_new(); + b = expected_block_new("iface", "pppoe"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "manual")); + expected_block_add_key(b, expected_key_new("pre-up", "/sbin/ifconfig eth0 up")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test12_complex_wrap_split_word(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test12"); + + e = expected_new(); + b = expected_block_new("iface", "pppoe"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "manual")); + expected_block_add_key(b, expected_key_new("up", "ifup ppp0=dsl")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test13_more_mixed_whitespace(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test13"); + + e = expected_new(); + b = expected_block_new("iface", "dsl"); + expected_block_add_key(b, expected_key_new("inet", "ppp")); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test14_mixed_whitespace_block_start(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test14"); + + e = expected_new(); + b = expected_block_new("iface", "wlan0"); + expected_block_add_key(b, expected_key_new("inet", "manual")); + expected_add_block(e, b); + b = expected_block_new("iface", "wlan-adpm"); + expected_block_add_key(b, expected_key_new("inet", "dhcp")); + expected_add_block(e, b); + b = expected_block_new("iface", "wlan-default"); + expected_block_add_key(b, expected_key_new("inet", "dhcp")); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test15_trailing_space(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test15"); + + e = expected_new(); + b = expected_block_new("iface", "bnep0"); + expected_block_add_key(b, expected_key_new("inet", "static")); + expected_add_block(e, b); + + compare_expected_to_ifparser(parser, e); +} + +static void +test16_missing_newline(void) +{ + nm_auto_free_expected Expected *e = NULL; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test16"); + + e = expected_new(); + expected_add_block(e, expected_block_new("mapping", "eth0")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test17_read_static_ipv4(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip4; + NMSettingWired * s_wired; + NMIPAddress * ip4_addr; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test17-wired-static-verify-ip4"); + + connection = _connection_first_from_parser(parser); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "Ifupdown (eth0)"); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "10.0.0.3"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 8); + + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "10.0.0.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "10.0.0.2"); + + g_assert_cmpint(nm_setting_ip_config_get_num_dns_searches(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip4, 0), ==, "example.com"); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip4, 1), ==, "foo.example.com"); +} + +static void +test18_read_static_ipv6(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip6; + NMSettingWired * s_wired; + NMIPAddress * ip6_addr; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test18-wired-static-verify-ip6"); + + connection = _connection_first_from_parser(parser); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "Ifupdown (myip6tunnel)"); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv6 SETTING ===== */ + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL); + + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip6), ==, 1); + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 0); + g_assert(ip6_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "fc00::1"); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 64); + + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "fc00::2"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 1), ==, "fc00::3"); + + g_assert_cmpint(nm_setting_ip_config_get_num_dns_searches(s_ip6), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip6, 0), ==, "example.com"); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip6, 1), ==, "foo.example.com"); +} + +static void +test19_read_static_ipv4_plen(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingIPConfig * s_ip4; + NMIPAddress * ip4_addr; + nm_auto_ifparser if_parser *parser = + init_ifparser_with_file("test19-wired-static-verify-ip4-plen"); + + connection = _connection_first_from_parser(parser); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "10.0.0.3"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 8); +} + +static void +test20_source_stanza(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test20-source-stanza"); + + e = expected_new(); + + b = expected_block_new("auto", "eth0"); + expected_add_block(e, b); + b = expected_block_new("iface", "eth0"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "dhcp")); + + b = expected_block_new("auto", "eth1"); + expected_add_block(e, b); + b = expected_block_new("iface", "eth1"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "dhcp")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test21_source_dir_stanza(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test21-source-dir-stanza"); + + e = expected_new(); + + b = expected_block_new("auto", "eth0"); + expected_add_block(e, b); + b = expected_block_new("iface", "eth0"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "dhcp")); + + compare_expected_to_ifparser(parser, e); +} + +static void +test22_duplicate_stanzas(void) +{ + nm_auto_free_expected Expected *e = NULL; + ExpectedBlock * b; + nm_auto_ifparser if_parser *parser = init_ifparser_with_file("test22-duplicate-stanzas"); + + e = expected_new(); + + b = expected_block_new("iface", "br10"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "manual")); + expected_block_add_key(b, expected_key_new("bridge-ports", "enp6s0.15")); + expected_block_add_key(b, expected_key_new("bridge-stp", "off")); + expected_block_add_key(b, expected_key_new("bridge-maxwait", "0")); + expected_block_add_key(b, expected_key_new("bridge-fd", "0")); + b = expected_block_new("iface", "br10"); + expected_add_block(e, b); + expected_block_add_key(b, expected_key_new("inet", "auto")); + expected_block_add_key(b, expected_key_new("bridge-ports", "enp6s0.15")); + + compare_expected_to_ifparser(parser, e); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_assert_logging(&argc, &argv, "WARN", "DEFAULT"); + + (void) dump_blocks; + + g_test_add_func("/ifupdate/ignore_line_before_first_block", + test1_ignore_line_before_first_block); + g_test_add_func("/ifupdate/wrapped_line", test2_wrapped_line); + g_test_add_func("/ifupdate/wrapped_multiline_multiarg", test3_wrapped_multiline_multiarg); + g_test_add_func("/ifupdate/allow_auto_is_auto", test4_allow_auto_is_auto); + g_test_add_func("/ifupdate/allow_auto_multiarg", test5_allow_auto_multiarg); + g_test_add_func("/ifupdate/mixed_whitespace", test6_mixed_whitespace); + g_test_add_func("/ifupdate/long_line", test7_long_line); + g_test_add_func("/ifupdate/long_line_wrapped", test8_long_line_wrapped); + g_test_add_func("/ifupdate/wrapped_lines_in_block", test9_wrapped_lines_in_block); + g_test_add_func("/ifupdate/complex_wrap", test11_complex_wrap); + g_test_add_func("/ifupdate/complex_wrap_split_word", test12_complex_wrap_split_word); + g_test_add_func("/ifupdate/more_mixed_whitespace", test13_more_mixed_whitespace); + g_test_add_func("/ifupdate/mixed_whitespace_block_start", test14_mixed_whitespace_block_start); + g_test_add_func("/ifupdate/trailing_space", test15_trailing_space); + g_test_add_func("/ifupdate/missing_newline", test16_missing_newline); + g_test_add_func("/ifupdate/read_static_ipv4", test17_read_static_ipv4); + g_test_add_func("/ifupdate/read_static_ipv6", test18_read_static_ipv6); + g_test_add_func("/ifupdate/read_static_ipv4_plen", test19_read_static_ipv4_plen); + g_test_add_func("/ifupdate/source_stanza", test20_source_stanza); + g_test_add_func("/ifupdate/source_dir_stanza", test21_source_dir_stanza); + g_test_add_func("/ifupdate/test22-duplicate-stanzas", test22_duplicate_stanzas); + + return g_test_run(); +} diff --git a/src/core/settings/plugins/ifupdown/tests/test1 b/src/core/settings/plugins/ifupdown/tests/test1 new file mode 100644 index 00000000..74c23b45 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test1 @@ -0,0 +1,6 @@ +# case 1: line before 1st block (must be ignored) +address 10.250.2.3 + +auto eth0 +iface eth0 inet dhcp + diff --git a/src/core/settings/plugins/ifupdown/tests/test11 b/src/core/settings/plugins/ifupdown/tests/test11 new file mode 100644 index 00000000..89561dd7 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test11 @@ -0,0 +1,5 @@ +iface pppoe inet manual +# case 11: wrapped line (without leading space on the wrapped part, wrap within a multi-word value) + pre-up /sbin/ifconfig \ +eth0 up + diff --git a/src/core/settings/plugins/ifupdown/tests/test12 b/src/core/settings/plugins/ifupdown/tests/test12 new file mode 100644 index 00000000..6096842e --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test12 @@ -0,0 +1,5 @@ +iface pppoe inet manual +# case 12: wrapped line, splitting a word (must be joined again) + up ifup ppp0\ +=dsl + diff --git a/src/core/settings/plugins/ifupdown/tests/test13 b/src/core/settings/plugins/ifupdown/tests/test13 new file mode 100644 index 00000000..c001f7ef --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test13 @@ -0,0 +1,3 @@ +# case 13: variations of tabs & spaces +iface dsl inet ppp + diff --git a/src/core/settings/plugins/ifupdown/tests/test14 b/src/core/settings/plugins/ifupdown/tests/test14 new file mode 100644 index 00000000..4a153ab3 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test14 @@ -0,0 +1,5 @@ +# case 14: variations of tabs and spaces (all must be recognized as lines starting an iface block) +iface wlan0 inet manual + iface wlan-adpm inet dhcp +iface wlan-default inet dhcp + diff --git a/src/core/settings/plugins/ifupdown/tests/test15 b/src/core/settings/plugins/ifupdown/tests/test15 new file mode 100644 index 00000000..c3ceca24 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test15 @@ -0,0 +1,3 @@ +# case 15: trailing space (must be ignored) +iface bnep0 inet static + diff --git a/src/core/settings/plugins/ifupdown/tests/test16 b/src/core/settings/plugins/ifupdown/tests/test16 new file mode 100644 index 00000000..f4f74fb5 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test16 @@ -0,0 +1,2 @@ +# case 16: last line that is not followed by LF (added with 'echo -n "mapping eth0" >> /e/n/i') +mapping eth0 \ No newline at end of file diff --git a/src/core/settings/plugins/ifupdown/tests/test17-wired-static-verify-ip4 b/src/core/settings/plugins/ifupdown/tests/test17-wired-static-verify-ip4 new file mode 100644 index 00000000..9e5243a7 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test17-wired-static-verify-ip4 @@ -0,0 +1,5 @@ +iface eth0 inet static + address 10.0.0.3 + netmask 255.0.0.0 + dns-search example.com foo.example.com + dns-nameservers 10.0.0.1 10.0.0.2 diff --git a/src/core/settings/plugins/ifupdown/tests/test18-wired-static-verify-ip6 b/src/core/settings/plugins/ifupdown/tests/test18-wired-static-verify-ip6 new file mode 100644 index 00000000..29546a61 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test18-wired-static-verify-ip6 @@ -0,0 +1,6 @@ +iface myip6tunnel inet6 v4tunnel + address fc00::1 + netmask 64 + endpoint 78.35.24.124 + dns-nameservers fc00::2 fc00::3 + dns-search example.com foo.example.com diff --git a/src/core/settings/plugins/ifupdown/tests/test19-wired-static-verify-ip4-plen b/src/core/settings/plugins/ifupdown/tests/test19-wired-static-verify-ip4-plen new file mode 100644 index 00000000..441106da --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test19-wired-static-verify-ip4-plen @@ -0,0 +1,3 @@ +iface eth0 inet static + address 10.0.0.3 + netmask 8 diff --git a/src/core/settings/plugins/ifupdown/tests/test2 b/src/core/settings/plugins/ifupdown/tests/test2 new file mode 100644 index 00000000..7462b352 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test2 @@ -0,0 +1,4 @@ +# case 2: wrapped line +auto \ +lo + diff --git a/src/core/settings/plugins/ifupdown/tests/test20-source-stanza b/src/core/settings/plugins/ifupdown/tests/test20-source-stanza new file mode 100644 index 00000000..5cfe1730 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test20-source-stanza @@ -0,0 +1 @@ +source test20-source-stanza.eth* diff --git a/src/core/settings/plugins/ifupdown/tests/test20-source-stanza.eth0 b/src/core/settings/plugins/ifupdown/tests/test20-source-stanza.eth0 new file mode 100644 index 00000000..81922cea --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test20-source-stanza.eth0 @@ -0,0 +1,2 @@ +auto eth0 +iface eth0 inet dhcp diff --git a/src/core/settings/plugins/ifupdown/tests/test20-source-stanza.eth1 b/src/core/settings/plugins/ifupdown/tests/test20-source-stanza.eth1 new file mode 100644 index 00000000..b8a783f5 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test20-source-stanza.eth1 @@ -0,0 +1,2 @@ +auto eth1 +iface eth1 inet dhcp diff --git a/src/core/settings/plugins/ifupdown/tests/test21-source-dir-stanza b/src/core/settings/plugins/ifupdown/tests/test21-source-dir-stanza new file mode 100644 index 00000000..d0604ddc --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test21-source-dir-stanza @@ -0,0 +1 @@ +source-directory test21-source-dir-stanza.d diff --git a/src/core/settings/plugins/ifupdown/tests/test21-source-dir-stanza.d/test21-source-dir-stanza.eth0 b/src/core/settings/plugins/ifupdown/tests/test21-source-dir-stanza.d/test21-source-dir-stanza.eth0 new file mode 100644 index 00000000..81922cea --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test21-source-dir-stanza.d/test21-source-dir-stanza.eth0 @@ -0,0 +1,2 @@ +auto eth0 +iface eth0 inet dhcp diff --git a/src/core/settings/plugins/ifupdown/tests/test22-duplicate-stanzas b/src/core/settings/plugins/ifupdown/tests/test22-duplicate-stanzas new file mode 100644 index 00000000..c13c2e7e --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test22-duplicate-stanzas @@ -0,0 +1,8 @@ +iface br10 inet manual + bridge_ports enp6s0.15 + bridge_stp off + bridge_maxwait 0 + bridge_fd 0 + +iface br10 inet auto + bridge_ports enp6s0.15 diff --git a/src/core/settings/plugins/ifupdown/tests/test3 b/src/core/settings/plugins/ifupdown/tests/test3 new file mode 100644 index 00000000..f6293bbd --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test3 @@ -0,0 +1,5 @@ +# case 3: line wrapped over multiple lines & multi-argument allow-* +allow-hotplug eth0 \ + wlan0 \ + bnep0 + diff --git a/src/core/settings/plugins/ifupdown/tests/test4 b/src/core/settings/plugins/ifupdown/tests/test4 new file mode 100644 index 00000000..46a40bc9 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test4 @@ -0,0 +1,3 @@ +# case 4: 'allow-auto' is synonymous to 'auto' +allow-auto eth0 + diff --git a/src/core/settings/plugins/ifupdown/tests/test5 b/src/core/settings/plugins/ifupdown/tests/test5 new file mode 100644 index 00000000..b69fc42b --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test5 @@ -0,0 +1,3 @@ +# case 5: multi-argument allow-* (even worse: trailing space) +allow-hotplug eth0 wlan0 + diff --git a/src/core/settings/plugins/ifupdown/tests/test6 b/src/core/settings/plugins/ifupdown/tests/test6 new file mode 100644 index 00000000..50ac69bd --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test6 @@ -0,0 +1,3 @@ +# case 6: mix between tabs and spaces + iface lo inet loopback + diff --git a/src/core/settings/plugins/ifupdown/tests/test7 b/src/core/settings/plugins/ifupdown/tests/test7 new file mode 100644 index 00000000..03cb131a --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test7 @@ -0,0 +1,3 @@ +# case 7: over-long line (must be ignored completely) +123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 + diff --git a/src/core/settings/plugins/ifupdown/tests/test8 b/src/core/settings/plugins/ifupdown/tests/test8 new file mode 100644 index 00000000..311f7e15 --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test8 @@ -0,0 +1,5 @@ +# case 8: over-long line that wraps to consecutive lines (must be ignored completely) +123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 \ +allow-test eth0 \ +eth0 + diff --git a/src/core/settings/plugins/ifupdown/tests/test9 b/src/core/settings/plugins/ifupdown/tests/test9 new file mode 100644 index 00000000..7d94563a --- /dev/null +++ b/src/core/settings/plugins/ifupdown/tests/test9 @@ -0,0 +1,10 @@ +iface eth0 inet static +# case 9: wrapped lines inside a block (to be on the safe side) + address \ + 10.250.2.3 + netmask \ + 255.255.255.192 + + broadcast 10.250.2.63 + gateway 10.250.2.50 + |