Commit d9f143df authored by Thomas Haller's avatar Thomas Haller

platform: merge branch 'th/route-fixes-bgo741871-v2'

https://bugzilla.gnome.org/show_bug.cgi?id=741871
parents 529591d8 96c099de
......@@ -1076,6 +1076,8 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
continue;
if (item->plen != route.plen)
continue;
if (item->metric != metric)
continue;
memcpy (item, &route, sizeof (route));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
......@@ -1097,6 +1099,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
NMPlatformIP6Route route;
guint i;
metric = nm_utils_ip6_route_metric_normalize (metric);
memset (&route, 0, sizeof (route));
route.source = NM_IP_CONFIG_SOURCE_KERNEL;
route.ifindex = ifindex;
......@@ -1116,6 +1120,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
continue;
if (item->plen != route.plen)
continue;
if (item->metric != metric)
continue;
memcpy (item, &route, sizeof (route));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
......@@ -1153,6 +1159,8 @@ ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int p
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
int i;
metric = nm_utils_ip6_route_metric_normalize (metric);
for (i = 0; i < priv->ip6_routes->len; i++) {
NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
......
......@@ -1668,6 +1668,9 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor
{
NMPlatformIP4Route route;
if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)
return;
if (!_route_match ((struct rtnl_route *) object, AF_INET, 0, FALSE)) {
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 route %s", to_string_ip4_route ((struct rtnl_route *) object));
return;
......@@ -1680,6 +1683,9 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor
{
NMPlatformIP6Route route;
if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)
return;
if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0, FALSE)) {
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 route %s", to_string_ip6_route ((struct rtnl_route *) object));
return;
......@@ -3919,7 +3925,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
*
* Instead, re-fetch the route from kernel, and if that fails, there is nothing to do.
* On success, there is still a race that we might end up deleting the wrong route. */
if (!refresh_object (platform, (struct nl_object *) route, FALSE, NM_PLATFORM_REASON_INTERNAL)) {
if (!refresh_object (platform, (struct nl_object *) route, FALSE, _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)) {
rtnl_route_put ((struct rtnl_route *) route);
return TRUE;
}
......
......@@ -69,7 +69,10 @@ typedef enum {
/* Event came from the kernel. */
NM_PLATFORM_REASON_EXTERNAL,
/* Event is a result of cache checking and cleanups. */
NM_PLATFORM_REASON_CACHE_CHECK
NM_PLATFORM_REASON_CACHE_CHECK,
/* Internal reason to suppress announcing change events */
_NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL,
} NMPlatformReason;
#define __NMPlatformObject_COMMON \
......
......@@ -125,6 +125,113 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl
g_error ("Added/changed link not found in the local cache.");
}
gboolean
ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric)
{
gs_free char *arg_network = NULL;
const char *argv[] = {
NULL,
"route",
"list",
"dev",
ifname,
"exact",
NULL,
NULL,
};
int exit_status;
gs_free char *std_out = NULL, *std_err = NULL;
char *out;
gboolean success;
gs_free_error GError *error = NULL;
gs_free char *metric_pattern = NULL;
g_assert (ifname && nm_utils_iface_valid_name (ifname));
g_assert (!strstr (ifname, " metric "));
g_assert (plen >= 0 && plen <= 32);
if (!NM_IS_LINUX_PLATFORM (nm_platform_get ())) {
/* If we don't test against linux-platform, we don't actually configure any
* routes in the system. */
return -1;
}
argv[0] = nm_utils_file_search_in_paths ("ip", NULL,
(const char *[]) { "/sbin", "/usr/sbin", NULL },
G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, NULL);
argv[6] = arg_network = g_strdup_printf ("%s/%d", nm_utils_inet4_ntop (network, NULL), plen);
if (!argv[0]) {
/* Hm. There is no 'ip' binary. Return *unknown* */
return -1;
}
success = g_spawn_sync (NULL,
(char **) argv,
(char *[]) { NULL },
0,
NULL,
NULL,
&std_out,
&std_err,
&exit_status,
&error);
g_assert_no_error (error);
g_assert (success);
g_assert_cmpstr (std_err, ==, "");
g_assert (std_out);
metric_pattern = g_strdup_printf (" metric %u", metric);
out = std_out;
while (out) {
char *eol = strchr (out, '\n');
gs_free char *line = eol ? g_strndup (out, eol - out) : g_strdup (out);
const char *p;
out = eol ? &eol[1] : NULL;
if (!line[0])
continue;
if (metric == 0) {
if (!strstr (line, " metric "))
return TRUE;
}
p = strstr (line, metric_pattern);
if (p && NM_IN_SET (p[strlen (metric_pattern)], ' ', '\0'))
return TRUE;
}
return FALSE;
}
void
_assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric)
{
int ifindex;
gboolean exists_checked;
/* Check for existance of the route by spawning iproute2. Do this because platform
* code might be entirely borked, but we expect ip-route to give a correct result.
* If the ip command cannot be found, we accept this as success. */
exists_checked = ip4_route_exists (ifname, network, plen, metric);
if (exists_checked != -1 && !exists_checked != !exists) {
g_error ("[%s:%u] %s(): We expect the ip4 route %s/%d metric %u %s, but it %s",
file, line, func,
nm_utils_inet4_ntop (network, NULL), plen, metric,
exists ? "to exist" : "not to exist",
exists ? "doesn't" : "does");
}
ifindex = nm_platform_link_get_ifindex (ifname);
g_assert (ifindex > 0);
if (!nm_platform_ip4_route_exists (ifindex, network, plen, metric) != !exists) {
g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s",
file, line, func,
nm_utils_inet4_ntop (network, NULL), plen, metric,
exists ? "exists" : "does not exist",
exists ? "it doesn't" : "it does");
}
}
void
run_command (const char *format, ...)
{
......
......@@ -34,6 +34,11 @@ void accept_signal (SignalData *data);
void wait_signal (SignalData *data);
void free_signal (SignalData *data);
gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric);
void _assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric);
#define assert_ip4_route_exists(exists, ifname, network, plen, metric) _assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, exists, ifname, network, plen, metric)
void link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data);
void run_command (const char *format, ...);
......
......@@ -50,6 +50,75 @@ ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *recei
data->received = TRUE;
}
static void
test_ip4_route_metric0 (void)
{
int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback);
SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback);
SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback);
in_addr_t network = nmtst_inet4_from_string ("192.0.2.5"); /* from 192.0.2.0/24 (TEST-NET-1) (rfc5737) */
int plen = 32;
int metric = 22987;
int mss = 1000;
/* No routes initially */
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric);
/* add the first route */
g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss));
no_error ();
accept_signal (route_added);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric);
/* Deleting route with metric 0 does nothing */
g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0));
no_error ();
g_assert (!route_removed->received);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric);
/* add the second route */
g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss));
no_error ();
accept_signal (route_added);
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric);
/* Delete route with metric 0 */
g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0));
no_error ();
accept_signal (route_removed);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric);
/* Delete route with metric 0 again (we expect nothing to happen) */
g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0));
no_error ();
g_assert (!route_removed->received);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric);
/* Delete the other route */
g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric));
no_error ();
accept_signal (route_removed);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0);
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric);
free_signal (route_added);
free_signal (route_changed);
free_signal (route_removed);
}
static void
test_ip4_route (void)
{
......@@ -75,11 +144,11 @@ test_ip4_route (void)
accept_signal (route_added);
/* Add route */
g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, metric));
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric);
no_error ();
g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss));
no_error ();
g_assert (nm_platform_ip4_route_exists (ifindex, network, plen, metric));
assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric);
no_error ();
accept_signal (route_added);
......@@ -89,11 +158,11 @@ test_ip4_route (void)
accept_signal (route_changed);
/* Add default route */
g_assert (!nm_platform_ip4_route_exists (ifindex, 0, 0, metric));
assert_ip4_route_exists (FALSE, DEVICE_NAME, 0, 0, metric);
no_error ();
g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss));
no_error ();
g_assert (nm_platform_ip4_route_exists (ifindex, 0, 0, metric));
assert_ip4_route_exists (TRUE, DEVICE_NAME, 0, 0, metric);
no_error ();
accept_signal (route_added);
......@@ -134,7 +203,7 @@ test_ip4_route (void)
/* Remove route */
g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric));
no_error ();
g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, metric));
assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric);
accept_signal (route_removed);
/* Remove route again */
......@@ -257,4 +326,5 @@ setup_tests (void)
g_test_add_func ("/route/ip4", test_ip4_route);
g_test_add_func ("/route/ip6", test_ip6_route);
g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment