Commit 73e011d0 authored by Dan Winship's avatar Dan Winship
Browse files

ifcfg-rh: add support for reading and writing ifcfg alias files

parent 8fbd5625
......@@ -377,7 +377,7 @@ ifcfg_dir_changed (GFileMonitor *monitor,
gpointer user_data)
{
SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
char *path, *ifcfg_path;
char *path, *base, *ifcfg_path;
NMIfcfgConnection *connection;
path = g_file_get_path (file);
......@@ -386,8 +386,14 @@ ifcfg_dir_changed (GFileMonitor *monitor,
return;
}
/* Given any ifcfg, keys, or routes file, get the ifcfg file path */
ifcfg_path = utils_get_ifcfg_path (path);
base = g_file_get_basename (file);
if (utils_is_ifcfg_alias_file (base, NULL)) {
/* Alias file changed. Get the base ifcfg file from it */
ifcfg_path = utils_get_ifcfg_from_alias (path);
} else {
/* Given any ifcfg, keys, or routes file, get the ifcfg file path */
ifcfg_path = utils_get_ifcfg_path (path);
}
if (ifcfg_path) {
connection = find_by_path (plugin, ifcfg_path);
switch (event_type) {
......@@ -407,6 +413,7 @@ ifcfg_dir_changed (GFileMonitor *monitor,
g_free (ifcfg_path);
}
g_free (path);
g_free (base);
}
static void
......@@ -459,6 +466,8 @@ read_connections (SCPluginIfcfg *plugin)
if (utils_should_ignore_file (item, TRUE))
continue;
if (utils_is_ifcfg_alias_file (item, NULL))
continue;
full_path = g_build_filename (IFCFG_DIR, item, NULL);
if (!utils_get_ifcfg_name (full_path, TRUE))
......
......@@ -50,6 +50,7 @@
#include <nm-setting-bridge-port.h>
#include <nm-setting-dcb.h>
#include <nm-setting-generic.h>
#include <nm-util-private.h>
#include <nm-utils.h>
#include "wifi-utils.h"
......@@ -641,10 +642,9 @@ static gboolean
read_full_ip4_address (shvarFile *ifcfg,
const char *network_file,
gint32 which,
NMIP4Address **out_address,
NMIP4Address *addr,
GError **error)
{
NMIP4Address *addr;
char *ip_tag, *prefix_tag, *netmask_tag, *gw_tag;
guint32 tmp;
gboolean success = FALSE;
......@@ -654,12 +654,10 @@ read_full_ip4_address (shvarFile *ifcfg,
g_return_val_if_fail (which >= -1, FALSE);
g_return_val_if_fail (ifcfg != NULL, FALSE);
g_return_val_if_fail (network_file != NULL, FALSE);
g_return_val_if_fail (out_address != NULL, FALSE);
g_return_val_if_fail (*out_address == NULL, FALSE);
g_return_val_if_fail (addr != NULL, FALSE);
if (error)
g_return_val_if_fail (*error == NULL, FALSE);
addr = nm_ip4_address_new ();
ip_tag = get_numbered_tag ("IPADDR", which);
prefix_tag = get_numbered_tag ("PREFIX", which);
netmask_tag = get_numbered_tag ("NETMASK", which);
......@@ -668,13 +666,12 @@ read_full_ip4_address (shvarFile *ifcfg,
/* IP address */
if (!read_ip4_address (ifcfg, ip_tag, &tmp, error))
goto done;
if (!tmp) {
nm_ip4_address_unref (addr);
addr = NULL;
success = TRUE; /* done */
if (tmp)
nm_ip4_address_set_address (addr, tmp);
else if (!nm_ip4_address_get_address (addr)) {
success = TRUE;
goto done;
}
nm_ip4_address_set_address (addr, tmp);
/* Gateway */
if (!read_ip4_address (ifcfg, gw_tag, &tmp, error))
......@@ -741,13 +738,9 @@ read_full_ip4_address (shvarFile *ifcfg,
goto done;
}
*out_address = addr;
success = TRUE;
done:
if (!success && addr)
nm_ip4_address_unref (addr);
g_free (ip_tag);
g_free (prefix_tag);
g_free (netmask_tag);
......@@ -1387,9 +1380,12 @@ make_ip4_setting (shvarFile *ifcfg,
for (i = -1; i < 256; i++) {
NMIP4Address *addr = NULL;
if (!read_full_ip4_address (ifcfg, network_file, i, &addr, error))
addr = nm_ip4_address_new ();
if (!read_full_ip4_address (ifcfg, network_file, i, addr, error))
goto done;
if (!addr) {
if (!nm_ip4_address_get_address (addr)) {
nm_ip4_address_unref (addr);
/* The first mandatory variable is 2-indexed (IPADDR2)
* Variables IPADDR, IPADDR0 and IPADDR1 are optional */
if (i > 1)
......@@ -1516,6 +1512,101 @@ done:
return NULL;
}
static void
read_aliases (NMSettingIP4Config *s_ip4, const char *filename, const char *network_file)
{
GDir *dir;
char *dirname, *base;
shvarFile *parsed;
NMIP4Address *base_addr;
GError *err = NULL;
g_return_if_fail (s_ip4 != NULL);
g_return_if_fail (filename != NULL);
base_addr = nm_setting_ip4_config_get_address (s_ip4, 0);
if (!base_addr)
return;
dirname = g_path_get_dirname (filename);
g_return_if_fail (dirname != NULL);
base = g_path_get_basename (filename);
g_return_if_fail (base != NULL);
dir = g_dir_open (dirname, 0, &err);
if (dir) {
const char *item;
NMIP4Address *addr;
gboolean ok;
while ((item = g_dir_read_name (dir))) {
char *full_path, *device;
const char *p;
if (!utils_is_ifcfg_alias_file (item, base))
continue;
full_path = g_build_filename (dirname, item, NULL);
p = strchr (item, ':');
g_assert (p != NULL); /* we know this is true from utils_is_ifcfg_alias_file() */
for (p++; *p; p++) {
if (!g_ascii_isalnum (*p) && *p != '_') {
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: ignoring alias file '%s' with invalid name", full_path);
g_free (full_path);
continue;
}
}
parsed = svNewFile (full_path);
if (!parsed) {
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: couldn't parse file '%s'", full_path);
g_free (full_path);
continue;
}
device = svGetValue (parsed, "DEVICE", FALSE);
if (!device) {
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: file '%s' has no DEVICE", full_path);
svCloseFile (parsed);
g_free (full_path);
continue;
}
/* We know that item starts with IFCFG_TAG from utils_is_ifcfg_alias_file() */
if (strcmp (device, item + strlen (IFCFG_TAG)) != 0) {
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: file '%s' has invalid DEVICE (%s) for filename", full_path, device);
g_free (device);
svCloseFile (parsed);
g_free (full_path);
continue;
}
addr = nm_ip4_address_dup (base_addr);
ok = read_full_ip4_address (parsed, network_file, -1, addr, &err);
svCloseFile (parsed);
if (ok) {
if (!NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, device)))
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: duplicate IP4 address in alias file %s", item);
} else {
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: error reading IP4 address from '%s': %s", full_path, err ? err->message : "no address")
g_clear_error (&err);
}
nm_ip4_address_unref (addr);
g_free (device);
g_free (full_path);
}
g_dir_close (dir);
} else {
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: can not read directory '%s': %s", dirname, err->message);
g_error_free (err);
}
g_free (base);
g_free (dirname);
}
static NMSetting *
make_ip6_setting (shvarFile *ifcfg,
const char *network_file,
......@@ -5106,8 +5197,10 @@ connection_from_file (const char *filename,
g_object_unref (connection);
connection = NULL;
goto done;
} else
} else {
read_aliases (NM_SETTING_IP4_CONFIG (s_ip4), filename, network_file);
nm_connection_add_setting (connection, s_ip4);
}
/* There is only one DOMAIN variable and it is read and put to IPv4 config
* But if IPv4 is disabled or the config fails for some reason, we read
......
......@@ -117,8 +117,24 @@ EXTRA_DIST = \
ifcfg-test-team-port \
ifcfg-test-team-port-empty-config
# make target dependencies can't have colons in their names, which ends up
# meaning that we can't add the alias files to EXTRA_DIST
ALIAS_FILES = \
ifcfg-aliasem0 \
ifcfg-aliasem0:1 \
ifcfg-aliasem0:2 \
ifcfg-aliasem0:99 \
ifcfg-aliasem1 \
ifcfg-aliasem1:1 \
ifcfg-aliasem1:2
dist-hook:
@for f in $(ALIAS_FILES); do \
cp $(abs_srcdir)/$$f $(distdir)/; \
done
check-local:
@for f in $(EXTRA_DIST); do \
@for f in $(EXTRA_DIST) $(ALIAS_FILES); do \
chmod 0600 $(abs_srcdir)/$$f; \
done
......
TYPE=Ethernet
DEVICE=aliasem0
HWADDR=00:11:22:33:44:55
BOOTPROTO=none
ONBOOT=yes
DNS1=4.2.2.1
DNS2=4.2.2.2
IPADDR=192.168.1.5
PREFIX=24
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
IPV6INIT=no
TYPE=Ethernet
DEVICE=aliasem1
HWADDR=00:11:22:33:44:55
BOOTPROTO=none
ONBOOT=yes
DNS1=4.2.2.1
DNS2=4.2.2.2
IPADDR=192.168.1.5
PREFIX=24
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
IPV6INIT=no
# bad: wrong DEVICE
DEVICE=aliasem0:2
IPADDR=192.168.1.20
......@@ -46,6 +46,7 @@
#include <nm-setting-serial.h>
#include <nm-setting-vlan.h>
#include <nm-setting-dcb.h>
#include <nm-util-private.h>
#include "nm-test-helpers.h"
#include "NetworkManagerUtils.h"
......@@ -2829,6 +2830,271 @@ test_read_write_802_1X_subj_matches (void)
g_object_unref (reread);
}
#define TEST_IFCFG_ALIASES_GOOD TEST_IFCFG_DIR"/network-scripts/ifcfg-aliasem0"
static void
test_read_wired_aliases_good (void)
{
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingIP4Config *s_ip4;
char *unmanaged = NULL;
char *keyfile = NULL;
char *routefile = NULL;
char *route6file = NULL;
gboolean ignore_error = FALSE;
GError *error = NULL;
const char *tmp;
const char *expected_id = "System aliasem0";
int expected_num_addresses = 4, expected_prefix = 24;
const char *expected_address[4] = { "192.168.1.5", "192.168.1.6", "192.168.1.9", "192.168.1.99" };
const char *expected_label[4] = { NULL, "aliasem0:1", "aliasem0:2", "aliasem0:99" };
const char *expected_gateway[4] = { "192.168.1.1", "192.168.1.1", "192.168.1.1", "192.168.1.1" };
int i, j;
connection = connection_from_file (TEST_IFCFG_ALIASES_GOOD,
NULL,
TYPE_ETHERNET,
NULL,
&unmanaged,
&keyfile,
&routefile,
&route6file,
&error,
&ignore_error);
ASSERT (connection != NULL,
"aliases-good-read", "failed to read %s: %s", TEST_IFCFG_ALIASES_GOOD, error->message);
ASSERT (nm_connection_verify (connection, &error),
"aliases-good-verify", "failed to verify %s: %s", TEST_IFCFG_ALIASES_GOOD, error->message);
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
ASSERT (s_con != NULL,
"aliases-good-verify-connection", "failed to verify %s: missing %s setting",
TEST_IFCFG_ALIASES_GOOD,
NM_SETTING_CONNECTION_SETTING_NAME);
/* ID */
tmp = nm_setting_connection_get_id (s_con);
ASSERT (tmp != NULL,
"aliases-good-verify-connection", "failed to verify %s: missing %s / %s key",
TEST_IFCFG_ALIASES_GOOD,
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_ID);
ASSERT (strcmp (tmp, expected_id) == 0,
"aliases-good-verify-connection", "failed to verify %s: unexpected %s / %s key value",
TEST_IFCFG_ALIASES_GOOD,
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_ID);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
ASSERT (s_ip4 != NULL,
"aliases-good-verify-ip4", "failed to verify %s: missing %s setting",
TEST_IFCFG_ALIASES_GOOD,
NM_SETTING_IP4_CONFIG_SETTING_NAME);
/* Method */
tmp = nm_setting_ip4_config_get_method (s_ip4);
ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0,
"aliases-good-verify-ip4", "failed to verify %s: unexpected %s / %s key value",
TEST_IFCFG_ALIASES_GOOD,
NM_SETTING_IP4_CONFIG_SETTING_NAME,
NM_SETTING_IP4_CONFIG_METHOD);
ASSERT (nm_setting_ip4_config_get_num_addresses (s_ip4) == expected_num_addresses,
"aliases-good-verify-ip4", "failed to verify %s: unexpected %s / %s key value",
TEST_IFCFG_ALIASES_GOOD,
NM_SETTING_IP4_CONFIG_SETTING_NAME,
NM_SETTING_IP4_CONFIG_ADDRESSES);
/* Addresses */
for (i = 0; i < expected_num_addresses; i++) {
NMIP4Address *ip4_addr;
char buf[INET_ADDRSTRLEN];
struct in_addr addr;
ip4_addr = nm_setting_ip4_config_get_address (s_ip4, i);
ASSERT (ip4_addr,
"aliases-good-verify-ip4", "failed to verify %s: missing IP4 address #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
addr.s_addr = nm_ip4_address_get_address (ip4_addr);
ASSERT (inet_ntop (AF_INET, &addr, buf, sizeof (buf)) > 0,
"aliases-good-verify-ip4", "failed to verify %s: couldn't convert IP address #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
for (j = 0; j < expected_num_addresses; j++) {
if (!g_strcmp0 (buf, expected_address[j]))
break;
}
ASSERT (j < expected_num_addresses,
"aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
ASSERT (nm_ip4_address_get_prefix (ip4_addr) == expected_prefix,
"aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address prefix #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
if (expected_gateway[j]) {
ASSERT (inet_pton (AF_INET, expected_gateway[j], &addr) > 0,
"aliases-good-verify-ip4", "failed to verify %s: couldn't convert IP address gateway #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
} else
addr.s_addr = 0;
ASSERT (nm_ip4_address_get_gateway (ip4_addr) == addr.s_addr,
"aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address gateway #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
ASSERT (g_strcmp0 (NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, i)), expected_label[j]) == 0,
"aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address label #%d",
TEST_IFCFG_ALIASES_GOOD,
i);
expected_address[j] = NULL;
expected_gateway[j] = NULL;
expected_label[j] = NULL;
}
for (i = 0; i < expected_num_addresses; i++) {
ASSERT (expected_address[i] == NULL,
"aliases-good-verify-ip4", "failed to verify %s: did not find IP4 address %s",
TEST_IFCFG_ALIASES_GOOD,
expected_address[i]);
}
g_free (keyfile);
g_free (routefile);
g_free (route6file);
g_object_unref (connection);
}
#define TEST_IFCFG_ALIASES_BAD TEST_IFCFG_DIR"/network-scripts/ifcfg-aliasem1"
static void
test_read_wired_aliases_bad (void)
{
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingIP4Config *s_ip4;
char *unmanaged = NULL;
char *keyfile = NULL;
char *routefile = NULL;
char *route6file = NULL;
gboolean ignore_error = FALSE;
GError *error = NULL;
const char *tmp;
const char *expected_id = "System aliasem1";
int expected_num_addresses = 1, expected_prefix = 24;
const char *expected_address = "192.168.1.5";
const char *expected_label = NULL;
const char *expected_gateway = "192.168.1.1";
NMIP4Address *ip4_addr;
struct in_addr addr;
connection = connection_from_file (TEST_IFCFG_ALIASES_BAD,
NULL,
TYPE_ETHERNET,
NULL,
&unmanaged,
&keyfile,
&routefile,
&route6file,
&error,
&ignore_error);
ASSERT (connection != NULL,
"aliases-bad-read", "failed to read %s: %s", TEST_IFCFG_ALIASES_BAD, error->message);
ASSERT (nm_connection_verify (connection, &error),
"aliases-bad-verify", "failed to verify %s: %s", TEST_IFCFG_ALIASES_BAD, error->message);
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
ASSERT (s_con != NULL,
"aliases-bad-verify-connection", "failed to verify %s: missing %s setting",
TEST_IFCFG_ALIASES_BAD,
NM_SETTING_CONNECTION_SETTING_NAME);
/* ID */
tmp = nm_setting_connection_get_id (s_con);
ASSERT (tmp != NULL,
"aliases-bad-verify-connection", "failed to verify %s: missing %s / %s key",
TEST_IFCFG_ALIASES_BAD,
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_ID);
ASSERT (strcmp (tmp, expected_id) == 0,
"aliases-bad-verify-connection", "failed to verify %s: unexpected %s / %s key value",
TEST_IFCFG_ALIASES_BAD,
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_ID);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
ASSERT (s_ip4 != NULL,
"aliases-bad-verify-ip4", "failed to verify %s: missing %s setting",
TEST_IFCFG_ALIASES_BAD,
NM_SETTING_IP4_CONFIG_SETTING_NAME);
/* Method */
tmp = nm_setting_ip4_config_get_method (s_ip4);
ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0,
"aliases-bad-verify-ip4", "failed to verify %s: unexpected %s / %s key value",
TEST_IFCFG_ALIASES_BAD,
NM_SETTING_IP4_CONFIG_SETTING_NAME,
NM_SETTING_IP4_CONFIG_METHOD);
ASSERT (nm_setting_ip4_config_get_num_addresses (s_ip4) == expected_num_addresses,
"aliases-bad-verify-ip4", "failed to verify %s: unexpected %s / %s key value",
TEST_IFCFG_ALIASES_BAD,
NM_SETTING_IP4_CONFIG_SETTING_NAME,
NM_SETTING_IP4_CONFIG_ADDRESSES);
/* Addresses */
ip4_addr = nm_setting_ip4_config_get_address (s_ip4, 0);
ASSERT (ip4_addr,
"aliases-bad-verify-ip4", "failed to verify %s: missing IP4 address",
TEST_IFCFG_ALIASES_BAD);
ASSERT (inet_pton (AF_INET, expected_address, &addr) > 0,
"aliases-bad-verify-ip4", "failed to verify %s: couldn't convert IP address",
TEST_IFCFG_ALIASES_BAD);
ASSERT (nm_ip4_address_get_address (ip4_addr) == addr.s_addr,
"aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address",
TEST_IFCFG_ALIASES_BAD);
ASSERT (nm_ip4_address_get_prefix (ip4_addr) == expected_prefix,
"aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address prefix",
TEST_IFCFG_ALIASES_BAD);
ASSERT (inet_pton (AF_INET, expected_gateway, &addr) > 0,
"aliases-bad-verify-ip4", "failed to verify %s: couldn't convert IP address gateway",
TEST_IFCFG_ALIASES_BAD);
ASSERT (nm_ip4_address_get_gateway (ip4_addr) == addr.s_addr,
"aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address gateway",
TEST_IFCFG_ALIASES_BAD);
ASSERT (g_strcmp0 (NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)), expected_label) == 0,
"aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address label",
TEST_IFCFG_ALIASES_BAD);
g_free (keyfile);
g_free (routefile);
g_free (route6file);
g_object_unref (connection);
}
#define TEST_IFCFG_WIFI_OPEN TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open"
static void
......@@ -7799,6 +8065,208 @@ test_write_wired_8021x_tls (NMSetting8021xCKScheme scheme,
g_object_unref (reread);
}
#define TEST_SCRATCH_ALIAS_BASE TEST_SCRATCH_DIR "/network-scripts/ifcfg-alias0"
static void
test_write_wired_aliases (void)
{
NMConnection *connection;
NMConnection *reread;
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingIP4Config *s_ip4;
char *uuid;
int num_addresses = 4;
guint32 ip[] = { 0x01010101, 0x01010102, 0x01010103, 0x01010104 };
const char *label[] = { NULL, "alias0:2", NULL, "alias0:3" };
const guint32 gw = htonl (0x01010101);
const guint32 prefix = 24;
NMIP4Address *addr;
gboolean success;
GError *error = NULL;
char *testfile = NULL;
char *unmanaged = NULL;
char *keyfile = NULL;
char *routefile = NULL;