From 719738d1335b8c41ebcd20b8eec11804beb70e43 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 3 May 2010 04:53:13 -0700 Subject: [PATCH] keyfile: fix for IPv6 address gateways --- system-settings/plugins/keyfile/io/reader.c | 44 ++- system-settings/plugins/keyfile/io/writer.c | 34 ++- .../keyfile/tests/keyfiles/Makefile.am | 3 +- .../tests/keyfiles/Test_Wired_Connection_IP6 | 20 ++ .../plugins/keyfile/tests/test-keyfile.c | 277 +++++++++++++++++- 5 files changed, 362 insertions(+), 16 deletions(-) create mode 100644 system-settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection_IP6 diff --git a/system-settings/plugins/keyfile/io/reader.c b/system-settings/plugins/keyfile/io/reader.c index 3a4ec42e84..a71c05c577 100644 --- a/system-settings/plugins/keyfile/io/reader.c +++ b/system-settings/plugins/keyfile/io/reader.c @@ -337,13 +337,30 @@ split_prefix (char *addr) return slash; } +static char * +split_gw (char *str) +{ + char *comma; + + g_return_val_if_fail (str != NULL, NULL); + + /* Find the prefix and split the string */ + comma = strchr (str, ','); + if (comma && comma > str) { + comma++; + *(comma - 1) = '\0'; + return comma; + } + return NULL; +} + static GPtrArray * read_ip6_addresses (GKeyFile *file, const char *setting_name, const char *key) { GPtrArray *addresses; - struct in6_addr addr; + struct in6_addr addr, gw; guint32 prefix; int i = 0; @@ -351,10 +368,11 @@ read_ip6_addresses (GKeyFile *file, /* Look for individual addresses */ while (i++ < 1000) { - char *tmp, *key_name, *str_prefix; + char *tmp, *key_name, *str_prefix, *str_gw; int ret; GValueArray *values; GByteArray *address; + GByteArray *gateway; GValue value = { 0 }; key_name = g_strdup_printf ("%s%d", key, i); @@ -377,6 +395,7 @@ read_ip6_addresses (GKeyFile *file, g_value_array_free (values); goto next; } + address = g_byte_array_new (); g_byte_array_append (address, (guint8 *) addr.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); @@ -401,6 +420,26 @@ read_ip6_addresses (GKeyFile *file, g_value_array_append (values, &value); g_value_unset (&value); + /* Gateway (optional) */ + str_gw = split_gw (str_prefix); + if (str_gw) { + ret = inet_pton (AF_INET6, str_gw, &gw); + if (ret <= 0) { + g_warning ("%s: ignoring invalid IPv6 %s gateway '%s'", __func__, key_name, tmp); + g_value_array_free (values); + goto next; + } + + if (!IN6_IS_ADDR_UNSPECIFIED (&gw)) { + gateway = g_byte_array_new (); + g_byte_array_append (gateway, (guint8 *) gw.s6_addr, 16); + g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); + g_value_take_boxed (&value, gateway); + g_value_array_append (values, &value); + g_value_unset (&value); + } + } + g_ptr_array_add (addresses, values); next: @@ -422,7 +461,6 @@ ip6_addr_parser (NMSetting *setting, const char *key, GKeyFile *keyfile) const char *setting_name = nm_setting_get_name (setting); addresses = read_ip6_addresses (keyfile, setting_name, key); - if (addresses) { g_object_set (setting, key, addresses, NULL); g_ptr_array_foreach (addresses, free_one_ip6_address, NULL); diff --git a/system-settings/plugins/keyfile/io/writer.c b/system-settings/plugins/keyfile/io/writer.c index 2e194072e3..355d624c19 100644 --- a/system-settings/plugins/keyfile/io/writer.c +++ b/system-settings/plugins/keyfile/io/writer.c @@ -226,18 +226,27 @@ ip6_dns_writer (GKeyFile *file, } static gboolean -ip6_array_to_addr (GValueArray *values, guint32 idx, char *buf, size_t buflen) +ip6_array_to_addr (GValueArray *values, + guint32 idx, + char *buf, + size_t buflen, + gboolean *out_is_unspec) { GByteArray *byte_array; GValue *addr_val; + struct in6_addr *addr; g_return_val_if_fail (buflen >= INET6_ADDRSTRLEN, FALSE); - /* address */ addr_val = g_value_array_get_nth (values, idx); byte_array = g_value_get_boxed (addr_val); + addr = (struct in6_addr *) byte_array->data; + + if (out_is_unspec && IN6_IS_ADDR_UNSPECIFIED (addr)) + *out_is_unspec = TRUE; + errno = 0; - if (!inet_ntop (AF_INET6, (struct in6_addr *) byte_array->data, buf, buflen)) { + if (!inet_ntop (AF_INET6, addr, buf, buflen)) { GString *ip6_str = g_string_sized_new (INET6_ADDRSTRLEN + 10); /* error converting the address */ @@ -259,18 +268,24 @@ ip6_array_to_addr_prefix (GValueArray *values) GValue *prefix_val; char *ret = NULL; GString *ip6_str; - char buf[INET6_ADDRSTRLEN]; + char buf[INET6_ADDRSTRLEN + 1]; + gboolean is_unspec = FALSE; /* address */ - if (ip6_array_to_addr (values, 0, buf, sizeof (buf))) { + if (ip6_array_to_addr (values, 0, buf, sizeof (buf), NULL)) { /* Enough space for the address, '/', and the prefix */ - ip6_str = g_string_sized_new (INET6_ADDRSTRLEN + 5); + ip6_str = g_string_sized_new ((INET6_ADDRSTRLEN * 2) + 5); /* prefix */ g_string_append (ip6_str, buf); prefix_val = g_value_array_get_nth (values, 1); g_string_append_printf (ip6_str, "/%u", g_value_get_uint (prefix_val)); + if (ip6_array_to_addr (values, 2, buf, sizeof (buf), &is_unspec)) { + if (!is_unspec) + g_string_append_printf (ip6_str, ",%s", buf); + } + ret = ip6_str->str; g_string_free (ip6_str, FALSE); } @@ -337,7 +352,8 @@ ip6_route_writer (GKeyFile *file, GValueArray *values = g_ptr_array_index (array, i); char *key_name; guint32 int_val; - char buf[INET6_ADDRSTRLEN]; + char buf[INET6_ADDRSTRLEN + 1]; + gboolean is_unspec = FALSE; memset (list, 0, sizeof (list)); @@ -347,7 +363,9 @@ ip6_route_writer (GKeyFile *file, continue; /* Next Hop */ - if (!ip6_array_to_addr (values, 2, buf, sizeof (buf))) + if (!ip6_array_to_addr (values, 2, buf, sizeof (buf), &is_unspec)) + continue; + if (is_unspec) continue; list[1] = g_strdup (buf); diff --git a/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am b/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am index a4ba7bf01d..bf5a5ab060 100644 --- a/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am +++ b/system-settings/plugins/keyfile/tests/keyfiles/Makefile.am @@ -2,7 +2,8 @@ EXTRA_DIST = \ Test_Wired_Connection \ Test_GSM_Connection \ Test_Wireless_Connection \ - Test_Wired_Connection_MAC_Case + Test_Wired_Connection_MAC_Case \ + Test_Wired_Connection_IP6 check-local: @for f in $(EXTRA_DIST); do \ diff --git a/system-settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection_IP6 b/system-settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection_IP6 new file mode 100644 index 0000000000..a42dd5d2a5 --- /dev/null +++ b/system-settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection_IP6 @@ -0,0 +1,20 @@ + +[connection] +id=Test Wired Connection IP6 +uuid=4e80a56d-c99f-4aad-a6dd-b449bc398c57 +type=802-3-ethernet +autoconnect=true +timestamp=6654332 + +[802-3-ethernet] +auto-negotiate=true +mtu=1400 + +[ipv4] +method=disabled + +[ipv6] +method=manual +addresses1=abcd:1234:ffff::cdde/64,abcd:1234:ffff::cdd1 +dns=1111:dddd::aaaa;1::cafe; + diff --git a/system-settings/plugins/keyfile/tests/test-keyfile.c b/system-settings/plugins/keyfile/tests/test-keyfile.c index 29fe19d2fd..aae823642b 100644 --- a/system-settings/plugins/keyfile/tests/test-keyfile.c +++ b/system-settings/plugins/keyfile/tests/test-keyfile.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. */ #include @@ -510,7 +510,8 @@ add_one_ip4_route (NMSettingIP4Config *s_ip4, static void add_one_ip6_address (NMSettingIP6Config *s_ip6, const char *addr, - guint32 prefix) + guint32 prefix, + const char *gw) { struct in6_addr tmp; NMIP6Address *ip6_addr; @@ -521,6 +522,11 @@ add_one_ip6_address (NMSettingIP6Config *s_ip6, inet_pton (AF_INET6, addr, &tmp); nm_ip6_address_set_address (ip6_addr, &tmp); + if (gw) { + inet_pton (AF_INET6, gw, &tmp); + nm_ip6_address_set_gateway (ip6_addr, &tmp); + } + nm_setting_ip6_config_add_address (s_ip6, ip6_addr); nm_ip6_address_unref (ip6_addr); } @@ -666,8 +672,8 @@ test_write_wired_connection (void) NULL); /* Addresses */ - add_one_ip6_address (s_ip6, address6_1, 64); - add_one_ip6_address (s_ip6, address6_2, 56); + add_one_ip6_address (s_ip6, address6_1, 64, NULL); + add_one_ip6_address (s_ip6, address6_2, 56, NULL); /* Routes */ add_one_ip6_route (s_ip6, route6_1, route6_1_nh, 64, 3); @@ -708,6 +714,266 @@ test_write_wired_connection (void) g_object_unref (connection); } +#define TEST_WIRED_IP6_FILE TEST_KEYFILES_DIR"/Test_Wired_Connection_IP6" + +static void +test_read_ip6_wired_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + GError *error = NULL; + const char *tmp; + const char *expected_id = "Test Wired Connection IP6"; + const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57"; + struct in6_addr addr6; + const char *expected6_address1 = "abcd:1234:ffff::cdde"; + const char *expected6_gw1 = "abcd:1234:ffff::cdd1"; + NMIP6Address *ip6_addr; + + connection = connection_from_file (TEST_WIRED_IP6_FILE); + ASSERT (connection != NULL, + "connection-read", "failed to read %s", TEST_WIRED_IP6_FILE); + + ASSERT (nm_connection_verify (connection, &error), + "connection-verify", "failed to verify %s: %s", TEST_WIRED_IP6_FILE, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "connection-verify-connection", "failed to verify %s: missing %s setting", + TEST_WIRED_IP6_FILE, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "connection-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_WIRED_IP6_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRED_IP6_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* UUID */ + tmp = nm_setting_connection_get_uuid (s_con); + ASSERT (tmp != NULL, + "connection-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_WIRED_IP6_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_UUID); + ASSERT (strcmp (tmp, expected_uuid) == 0, + "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRED_IP6_FILE, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_UUID); + + /* ===== WIRED SETTING ===== */ + + s_wired = NM_SETTING_WIRED (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED)); + ASSERT (s_wired != NULL, + "connection-verify-wired", "failed to verify %s: missing %s setting", + TEST_WIRED_IP6_FILE, + NM_SETTING_WIRED_SETTING_NAME); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + ASSERT (s_ip4 != NULL, + "connection-verify-ip4", "failed to verify %s: missing %s setting", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP4_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0, + "connection-verify-wired", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + + ASSERT (nm_setting_ip4_config_get_num_addresses (s_ip4) == 0, + "connection-verify-wired", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_DNS); + + /* ===== IPv6 SETTING ===== */ + + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG)); + ASSERT (s_ip6 != NULL, + "connection-verify-ip6", "failed to verify %s: missing %s setting", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip6_config_get_method (s_ip6); + ASSERT (strcmp (tmp, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0, + "connection-verify-wired", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_METHOD); + + ASSERT (nm_setting_ip6_config_get_num_addresses (s_ip6) == 1, + "connection-verify-wired", "failed to verify %s: unexpected %s / %s key value", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_DNS); + + /* Address #1 */ + ip6_addr = nm_setting_ip6_config_get_address (s_ip6, 0); + ASSERT (ip6_addr, + "connection-verify-wired", "failed to verify %s: missing IP6 address #1", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_ADDRESSES); + + ASSERT (nm_ip6_address_get_prefix (ip6_addr) == 64, + "connection-verify-wired", "failed to verify %s: unexpected IP6 address #1 prefix", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_ADDRESSES); + + ASSERT (inet_pton (AF_INET6, expected6_address1, &addr6) > 0, + "connection-verify-wired", "failed to verify %s: couldn't convert IP address #1", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_ADDRESSES); + ASSERT (IN6_ARE_ADDR_EQUAL (nm_ip6_address_get_address (ip6_addr), &addr6), + "connection-verify-wired", "failed to verify %s: unexpected IP4 address #1", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_ADDRESSES); + + ASSERT (inet_pton (AF_INET6, expected6_gw1, &addr6) > 0, + "connection-verify-wired", "failed to verify %s: couldn't convert GW address #1", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_ADDRESSES); + ASSERT (IN6_ARE_ADDR_EQUAL (nm_ip6_address_get_gateway (ip6_addr), &addr6), + "connection-verify-wired", "failed to verify %s: unexpected IP4 address #1", + TEST_WIRED_IP6_FILE, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_ADDRESSES); + + g_object_unref (connection); +} + +static void +test_write_ip6_wired_connection (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *uuid; + gboolean success; + NMConnection *reread; + char *testfile = NULL; + GError *error = NULL; + pid_t owner_grp; + uid_t owner_uid; + struct in6_addr addr6; + const char *dns = "1::cafe"; + const char *address = "abcd::beef"; + const char *gw = "dcba::beef"; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "connection-write", "failed to allocate new connection"); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + ASSERT (s_con != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Work Wired IP6", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wired setting */ + + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + ASSERT (s_wired != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_WIRED_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + ASSERT (s_ip4 != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL); + + /* IP6 setting */ + + s_ip6 = NM_SETTING_IP6_CONFIG (nm_setting_ip6_config_new ()); + ASSERT (s_ip6 != NULL, + "connection-write", "failed to allocate new %s setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL); + + /* Addresses */ + add_one_ip6_address (s_ip6, address, 64, gw); + + /* DNS servers */ + inet_pton (AF_INET6, dns, &addr6); + nm_setting_ip6_config_add_dns (s_ip6, &addr6); + + /* DNS searches */ + nm_setting_ip6_config_add_dns_search (s_ip6, "wallaceandgromit.com"); + + /* Write out the connection */ + owner_uid = geteuid (); + owner_grp = getegid (); + success = write_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error); + ASSERT (success == TRUE, + "connection-write", "failed to allocate write keyfile: %s", + error ? error->message : "(none)"); + + ASSERT (testfile != NULL, + "connection-write", "didn't get keyfile name back after writing connection"); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = connection_from_file (testfile); + ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "connection-write", "written and re-read connection weren't the same"); + + g_clear_error (&error); + unlink (testfile); + g_free (testfile); + + g_object_unref (reread); + g_object_unref (connection); +} + #define TEST_WIRED_MAC_CASE_FILE TEST_KEYFILES_DIR"/Test_Wired_Connection_MAC_Case" static void @@ -1042,6 +1308,9 @@ int main (int argc, char **argv) test_read_valid_wired_connection (); test_write_wired_connection (); + test_read_ip6_wired_connection (); + test_write_ip6_wired_connection (); + test_read_wired_mac_case (); test_read_valid_wireless_connection ();