/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-default.h"
#include "nm-l3cfg.h"
#include "nm-l3-ipv4ll.h"
#include "nm-netns.h"
#include "platform/nm-platform.h"
#include "platform/tests/test-common.h"
/*****************************************************************************/
static NML3Cfg *
_netns_access_l3cfg(NMNetns *netns, int ifindex)
{
NML3Cfg *l3cfg;
g_assert(NM_IS_NETNS(netns));
g_assert(ifindex > 0);
g_assert(!nm_netns_get_l3cfg(netns, ifindex));
l3cfg = nm_netns_access_l3cfg(netns, ifindex);
g_assert(NM_IS_L3CFG(l3cfg));
return l3cfg;
}
/*****************************************************************************/
typedef struct {
int test_idx;
NMPlatform * platform;
NMNetns * netns;
NMDedupMultiIndex *multiidx;
const char * ifname0;
const char * ifname1;
NMPLinkAddress hwaddr0;
NMPLinkAddress hwaddr1;
int ifindex0;
int ifindex1;
} TestFixture1;
static const TestFixture1 *
_test_fixture_1_setup(TestFixture1 *f, int test_idx)
{
const NMPlatformLink *l0;
const NMPlatformLink *l1;
const NMEtherAddr addr0 = NM_ETHER_ADDR_INIT(0xAA, 0xAA, test_idx, 0x00, 0x00, 0x00);
const NMEtherAddr addr1 = NM_ETHER_ADDR_INIT(0xAA, 0xAA, test_idx, 0x00, 0x00, 0x11);
g_assert_cmpint(test_idx, >, 0);
g_assert_cmpint(f->test_idx, ==, 0);
f->test_idx = test_idx;
f->ifname0 = "nm-test-veth0";
f->ifname1 = "nm-test-veth1";
f->platform = g_object_ref(NM_PLATFORM_GET);
f->multiidx = nm_dedup_multi_index_ref(nm_platform_get_multi_idx(f->platform));
f->netns = nm_netns_new(f->platform);
nmtstp_link_veth_add(f->platform, -1, f->ifname0, f->ifname1);
l0 = nmtstp_link_get_typed(f->platform, -1, f->ifname0, NM_LINK_TYPE_VETH);
l1 = nmtstp_link_get_typed(f->platform, -1, f->ifname1, NM_LINK_TYPE_VETH);
f->ifindex0 = l0->ifindex;
f->ifindex1 = l1->ifindex;
g_assert_cmpint(nm_platform_link_set_address(f->platform, f->ifindex0, &addr0, sizeof(addr0)),
==,
0);
g_assert_cmpint(nm_platform_link_set_address(f->platform, f->ifindex1, &addr1, sizeof(addr1)),
==,
0);
l0 = nmtstp_link_get_typed(f->platform, f->ifindex0, f->ifname0, NM_LINK_TYPE_VETH);
l1 = nmtstp_link_get_typed(f->platform, f->ifindex1, f->ifname1, NM_LINK_TYPE_VETH);
f->hwaddr0 = l0->l_address;
f->hwaddr1 = l1->l_address;
g_assert(nm_platform_link_set_up(f->platform, f->ifindex0, NULL));
g_assert(nm_platform_link_set_up(f->platform, f->ifindex1, NULL));
return f;
}
static void
_test_fixture_1_teardown(TestFixture1 *f)
{
g_assert(f);
if (f->test_idx == 0)
return;
_LOGD("test teatdown");
nmtstp_link_delete(f->platform, -1, f->ifindex0, f->ifname0, TRUE);
g_assert(!nm_platform_link_get(f->platform, f->ifindex0));
g_assert(!nm_platform_link_get(f->platform, f->ifindex1));
g_assert(!nm_platform_link_get_by_ifname(f->platform, f->ifname0));
g_assert(!nm_platform_link_get_by_ifname(f->platform, f->ifname1));
g_object_unref(f->netns);
g_object_unref(f->platform);
nm_dedup_multi_index_unref(f->multiidx);
*f = (TestFixture1){
.test_idx = 0,
};
}
/*****************************************************************************/
typedef enum {
TEST_L3CFG_NOTIFY_TYPE_NONE,
TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL,
TEST_L3CFG_NOTIFY_TYPE_COMMIT_1,
TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1,
} TestL3cfgNotifyType;
typedef struct {
const TestFixture1 *f;
bool has_addr4_101 : 1;
bool add_addr4_101 : 1;
guint32 acd_timeout_msec_a;
NML3AcdDefendType acd_defend_type_a;
TestL3cfgNotifyType notify_type;
guint post_commit_event_count;
guint general_event_count;
guint general_event_flags;
union {
struct {
int cb_count;
bool expected_probe_result : 1;
bool acd_event_ready_45 : 1;
bool acd_event_ready_101 : 1;
} wait_for_acd_ready_1;
} notify_result;
} TestL3cfgData;
static void
_test_l3cfg_data_set_notify_type(TestL3cfgData *tdata, TestL3cfgNotifyType notify_type)
{
g_assert(tdata);
tdata->notify_type = notify_type;
tdata->post_commit_event_count = 0;
tdata->general_event_count = 0;
tdata->general_event_flags = 0;
memset(&tdata->notify_result, 0, sizeof(tdata->notify_result));
}
static void
_test_l3cfg_signal_notify(NML3Cfg * l3cfg,
const NML3ConfigNotifyData *notify_data,
TestL3cfgData * tdata)
{
guint i;
g_assert(NM_IS_L3CFG(l3cfg));
g_assert(tdata);
g_assert(notify_data);
g_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type));
g_assert(notify_data->notify_type < _NM_L3_CONFIG_NOTIFY_TYPE_NUM);
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE)
g_assert(notify_data->platform_change_on_idle.obj_type_flags != 0u);
else if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE) {
g_assert(NMP_OBJECT_IS_VALID(notify_data->platform_change.obj));
g_assert(notify_data->platform_change.change_type != 0);
} else if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT) {
g_assert_cmpint(notify_data->acd_event.info.n_track_infos, >=, 1);
g_assert(notify_data->acd_event.info.track_infos);
for (i = 0; i < notify_data->acd_event.info.n_track_infos; i++) {
const NML3AcdAddrTrackInfo *ti = ¬ify_data->acd_event.info.track_infos[i];
nm_assert(NMP_OBJECT_GET_TYPE(ti->obj) == NMP_OBJECT_TYPE_IP4_ADDRESS);
nm_assert(NMP_OBJECT_CAST_IP4_ADDRESS(ti->obj)->address
== notify_data->acd_event.info.addr);
nm_assert(NM_IS_L3_CONFIG_DATA(ti->l3cd));
nm_assert(ti->tag);
}
}
switch (tdata->notify_type) {
case TEST_L3CFG_NOTIFY_TYPE_NONE:
g_assert_not_reached();
break;
case TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL:
if (NM_IN_SET(notify_data->notify_type,
NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE,
NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE))
return;
g_assert_not_reached();
return;
case TEST_L3CFG_NOTIFY_TYPE_COMMIT_1:
g_assert_cmpint(tdata->post_commit_event_count, ==, 0);
switch (notify_data->notify_type) {
case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT:
tdata->post_commit_event_count++;
return;
case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT:
switch (tdata->f->test_idx) {
case 2:
case 3:
nmtst_assert_ip4_address(notify_data->acd_event.info.addr, "192.168.133.45");
if (tdata->f->test_idx == 2)
g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_DEFENDING);
else
g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_PROBING);
g_assert(tdata->general_event_count == 0);
tdata->general_event_count++;
return;
case 4:
if (notify_data->acd_event.info.addr == nmtst_inet4_from_string("192.168.133.45")) {
g_assert(!NM_FLAGS_HAS(tdata->general_event_flags, 0x1u));
tdata->general_event_flags |= 0x1u;
g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_PROBING);
tdata->general_event_count++;
} else if (notify_data->acd_event.info.addr
== nmtst_inet4_from_string("192.168.133.101")) {
g_assert(!NM_FLAGS_HAS(tdata->general_event_flags, 0x4u));
tdata->general_event_flags |= 0x4u;
g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_PROBING);
tdata->general_event_count++;
} else
g_assert_not_reached();
return;
default:
g_assert_not_reached();
return;
}
case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE:
return;
default:
g_assert_not_reached();
return;
}
case TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1:
{
int num_acd_completed_events =
1 + 2 + (tdata->add_addr4_101 ? (tdata->has_addr4_101 ? 1 : 3) : 0);
if (NM_IN_SET(notify_data->notify_type,
NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE,
NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE))
return;
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT) {
if (notify_data->acd_event.info.addr == nmtst_inet4_from_string("192.168.133.45")) {
g_assert(NM_IN_SET(notify_data->acd_event.info.state,
NM_L3_ACD_ADDR_STATE_READY,
NM_L3_ACD_ADDR_STATE_DEFENDING));
tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_45 = TRUE;
} else if (notify_data->acd_event.info.addr
== nmtst_inet4_from_string("192.168.133.101")) {
if (tdata->has_addr4_101) {
g_assert(
NM_IN_SET(notify_data->acd_event.info.state, NM_L3_ACD_ADDR_STATE_USED));
} else {
g_assert(NM_IN_SET(notify_data->acd_event.info.state,
NM_L3_ACD_ADDR_STATE_READY,
NM_L3_ACD_ADDR_STATE_DEFENDING));
tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_101 = TRUE;
}
} else
g_assert_not_reached();
g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count,
<,
num_acd_completed_events);
tdata->notify_result.wait_for_acd_ready_1.cb_count++;
return;
}
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT) {
g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, >, 0);
g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count,
<,
num_acd_completed_events);
tdata->notify_result.wait_for_acd_ready_1.cb_count++;
nmtstp_platform_ip_addresses_assert(
tdata->f->platform,
tdata->f->ifindex0,
TRUE,
TRUE,
TRUE,
tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_45 ? "192.168.133.45"
: NULL,
tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_101 ? "192.168.133.101"
: NULL,
"1:2:3:4::45");
return;
}
g_assert_not_reached();
return;
}
}
g_assert_not_reached();
}
static void
test_l3cfg(gconstpointer test_data)
{
const int TEST_IDX = GPOINTER_TO_INT(test_data);
const guint32 ACD_TIMEOUT_BASE_MSEC = 1000;
nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {};
const TestFixture1 * f;
NML3CfgCommitTypeHandle * commit_type_1;
NML3CfgCommitTypeHandle * commit_type_2;
gs_unref_object NML3Cfg *l3cfg0 = NULL;
nm_auto_unref_l3cd const NML3ConfigData *l3cd_a = NULL;
TestL3cfgData tdata_stack = {
.f = NULL,
};
TestL3cfgData *const tdata = &tdata_stack;
_LOGD("test start (/l3cfg/%d)", TEST_IDX);
if (nmtst_test_quick()) {
gs_free char *msg =
g_strdup_printf("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n",
g_get_prgname() ?: "test-l3cfg");
g_test_skip(msg);
return;
}
f = _test_fixture_1_setup(&test_fixture, TEST_IDX);
tdata->f = f;
tdata->has_addr4_101 = (f->test_idx == 4 && nmtst_get_rand_bool());
tdata->add_addr4_101 = (f->test_idx == 4 && nmtst_get_rand_bool());
tdata->acd_timeout_msec_a = NM_IN_SET(f->test_idx, 3, 4) ? ACD_TIMEOUT_BASE_MSEC : 0u;
tdata->acd_defend_type_a = NM_IN_SET(f->test_idx, 4)
? nmtst_rand_select(NM_L3_ACD_DEFEND_TYPE_NEVER,
NM_L3_ACD_DEFEND_TYPE_ONCE,
NM_L3_ACD_DEFEND_TYPE_ALWAYS)
: NM_L3_ACD_DEFEND_TYPE_NEVER;
if (tdata->has_addr4_101) {
nmtstp_ip4_address_add(f->platform,
-1,
f->ifindex1,
nmtst_inet4_from_string("192.168.133.101"),
24,
nmtst_inet4_from_string("192.168.133.101"),
100000,
0,
0,
NULL);
}
l3cfg0 = _netns_access_l3cfg(f->netns, f->ifindex0);
g_signal_connect(l3cfg0, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_test_l3cfg_signal_notify), tdata);
commit_type_1 = nm_l3cfg_commit_type_register(l3cfg0, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL);
if (!nmtst_get_rand_one_case_in(4)) {
commit_type_2 =
nm_l3cfg_commit_type_register(l3cfg0,
nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE,
NM_L3_CFG_COMMIT_TYPE_ASSUME,
NM_L3_CFG_COMMIT_TYPE_UPDATE),
NULL);
} else
commit_type_2 = NULL;
switch (f->test_idx) {
case 1:
break;
case 2:
case 3:
case 4:
{
nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
l3cd = nm_l3_config_data_new(f->multiidx, f->ifindex0);
nm_l3_config_data_add_address_4(
l3cd,
NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.168.133.45"),
.peer_address = nmtst_inet4_from_string("192.168.133.45"),
.plen = 24, ));
if (tdata->add_addr4_101) {
nm_l3_config_data_add_address_4(
l3cd,
NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.168.133.101"),
.peer_address =
nmtst_inet4_from_string("192.168.133.101"),
.plen = 24, ));
}
nm_l3_config_data_add_address_6(
l3cd,
NM_PLATFORM_IP6_ADDRESS_INIT(.address = *nmtst_inet6_from_string("1:2:3:4::45"),
.plen = 64, ));
if (nmtst_get_rand_one_case_in(2))
nm_l3_config_data_seal(l3cd);
l3cd_a = g_steal_pointer(&l3cd);
break;
}
}
nm_l3_config_data_log(l3cd_a, "l3cd_a", "platform-test: l3cd_a: ", LOGL_DEBUG, LOGD_PLATFORM);
if (l3cd_a) {
nm_l3cfg_add_config(l3cfg0,
GINT_TO_POINTER('a'),
nmtst_get_rand_bool(),
l3cd_a,
'a',
0,
0,
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4,
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
0,
0,
tdata->acd_defend_type_a,
tdata->acd_timeout_msec_a,
NM_L3_CONFIG_MERGE_FLAGS_NONE);
}
nm_l3_config_data_log(nm_l3cfg_get_combined_l3cd(l3cfg0, FALSE),
"test",
"platform-test: l3cfg0: ",
LOGL_DEBUG,
LOGD_PLATFORM);
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_COMMIT_1);
nm_l3cfg_commit(l3cfg0, NM_L3_CFG_COMMIT_TYPE_REAPPLY);
g_assert_cmpint(tdata->post_commit_event_count, ==, 1);
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE);
nmtstp_platform_ip_addresses_assert(tdata->f->platform,
tdata->f->ifindex0,
TRUE,
TRUE,
TRUE,
NM_IN_SET(f->test_idx, 2) ? "192.168.133.45" : NULL,
NM_IN_SET(f->test_idx, 2, 3, 4) ? "1:2:3:4::45" : NULL);
if (NM_IN_SET(f->test_idx, 1, 2)) {
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL);
_LOGT("poll 1 start");
nmtst_main_context_iterate_until(NULL,
nmtst_get_rand_uint32() % (ACD_TIMEOUT_BASE_MSEC * 5u),
FALSE);
_LOGT("poll 1 end");
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE);
}
if (NM_IN_SET(f->test_idx, 3, 4)) {
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1);
tdata->notify_result.wait_for_acd_ready_1.expected_probe_result = TRUE;
_LOGT("poll 2 start");
nmtst_main_context_iterate_until(
NULL,
ACD_TIMEOUT_BASE_MSEC * 3u / 2u
+ (nmtst_get_rand_uint32() % (2u * ACD_TIMEOUT_BASE_MSEC)),
FALSE);
_LOGT("poll 2 end");
g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count,
==,
1 + 2 + (tdata->add_addr4_101 ? (tdata->has_addr4_101 ? 1 : 3) : 0));
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE);
}
g_signal_handlers_disconnect_by_func(l3cfg0, G_CALLBACK(_test_l3cfg_signal_notify), tdata);
nm_l3cfg_commit_type_unregister(l3cfg0, commit_type_1);
nm_l3cfg_commit_type_unregister(l3cfg0, commit_type_2);
if (nmtst_get_rand_one_case_in(3))
_test_fixture_1_teardown(&test_fixture);
nm_l3cfg_remove_config_all(l3cfg0, GINT_TO_POINTER('a'), FALSE);
if (nmtst_get_rand_one_case_in(3))
_test_fixture_1_teardown(&test_fixture);
_LOGD("test end (/l3cfg/%d)", TEST_IDX);
}
/*****************************************************************************/
#define L3IPV4LL_ACD_TIMEOUT_MSEC 1500u
typedef struct {
const TestFixture1 * f;
NML3CfgCommitTypeHandle *l3cfg_commit_type_1;
guint acd_timeout_msec;
NML3IPv4LL * l3ipv4ll;
bool has_addr4_101;
gint8 ready_seen;
gint8 addr_commit;
in_addr_t addr_commit_addr;
bool add_conflict_checked : 1;
bool add_conflict_done;
} TestL3IPv4LLData;
static gconstpointer
TEST_L3_IPV4LL_TAG(const TestL3IPv4LLData *tdata, guint offset)
{
return (&(((const char *) tdata)[offset]));
}
static void
_test_l3_ipv4ll_maybe_add_addr_4(const TestL3IPv4LLData *tdata,
int ifindex,
guint one_case_in_num,
bool * has_addr,
const char * addr)
{
if (has_addr) {
if (*has_addr || !nmtst_get_rand_one_case_in(one_case_in_num))
return;
*has_addr = TRUE;
}
if (ifindex == 0)
ifindex = tdata->f->ifindex0;
g_assert_cmpint(ifindex, >, 0);
_LOGT("add test address: %s on ifindex=%d", addr, ifindex);
nmtstp_ip4_address_add(tdata->f->platform,
-1,
ifindex,
nmtst_inet4_from_string(addr),
24,
nmtst_inet4_from_string(addr),
100000,
0,
0,
NULL);
}
static void
_test_l3_ipv4ll_signal_notify(NML3Cfg * l3cfg,
const NML3ConfigNotifyData *notify_data,
TestL3IPv4LLData * tdata)
{
char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
g_assert(NM_IS_L3CFG(l3cfg));
g_assert(tdata);
g_assert(notify_data);
g_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type));
g_assert(notify_data->notify_type < _NM_L3_CONFIG_NOTIFY_TYPE_NUM);
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT) {
g_assert(tdata->l3ipv4ll == notify_data->ipv4ll_event.ipv4ll);
g_assert(NM_IN_SET(tdata->ready_seen, 0, 1));
g_assert(NM_IN_SET(tdata->addr_commit, 0, 1));
if (nm_l3_ipv4ll_get_state(tdata->l3ipv4ll) == NM_L3_IPV4LL_STATE_READY) {
g_assert_cmpint(tdata->ready_seen, ==, 0);
g_assert_cmpint(tdata->addr_commit, ==, 0);
tdata->ready_seen++;
if (tdata->f->test_idx == 2 && nmtst_get_rand_bool()) {
tdata->addr_commit++;
tdata->addr_commit_addr = nm_l3_ipv4ll_get_addr(tdata->l3ipv4ll);
g_assert(nm_utils_ip4_address_is_link_local(tdata->addr_commit_addr));
_LOGT("add address %s that passed ACD",
_nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr));
if (!nm_l3cfg_add_config(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll),
TEST_L3_IPV4LL_TAG(tdata, 1),
nmtst_get_rand_bool(),
nm_l3_ipv4ll_get_l3cd(tdata->l3ipv4ll),
NM_L3CFG_CONFIG_PRIORITY_IPV4LL,
0,
0,
104,
105,
0,
0,
NM_L3_ACD_DEFEND_TYPE_ONCE,
nmtst_get_rand_bool() ? tdata->acd_timeout_msec : 0u,
NM_L3_CONFIG_MERGE_FLAGS_NONE))
g_assert_not_reached();
nm_l3cfg_commit_on_idle_schedule(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll));
tdata->l3cfg_commit_type_1 =
nm_l3cfg_commit_type_register(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll),
NM_L3_CFG_COMMIT_TYPE_UPDATE,
tdata->l3cfg_commit_type_1);
}
} else if (nm_l3_ipv4ll_get_state(tdata->l3ipv4ll) != NM_L3_IPV4LL_STATE_DEFENDING
&& tdata->ready_seen > 0) {
g_assert_cmpint(tdata->ready_seen, ==, 1);
tdata->ready_seen--;
if (tdata->addr_commit > 0) {
g_assert_cmpint(tdata->addr_commit, ==, 1);
tdata->addr_commit--;
g_assert(nm_utils_ip4_address_is_link_local(tdata->addr_commit_addr));
_LOGT("remove address %s that previously passed ACD",
_nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr));
if (!nm_l3cfg_remove_config_all(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll),
TEST_L3_IPV4LL_TAG(tdata, 1),
FALSE))
g_assert_not_reached();
nm_l3cfg_commit_on_idle_schedule(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll));
nm_l3cfg_commit_type_unregister(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll),
g_steal_pointer(&tdata->l3cfg_commit_type_1));
}
}
return;
}
}
static void
test_l3_ipv4ll(gconstpointer test_data)
{
const int TEST_IDX = GPOINTER_TO_INT(test_data);
nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {};
const TestFixture1 * f;
gs_unref_object NML3Cfg *l3cfg0 = NULL;
TestL3IPv4LLData tdata_stack = {
.f = NULL,
};
TestL3IPv4LLData *const tdata = &tdata_stack;
NMTstpAcdDefender * acd_defender_1 = NULL;
NMTstpAcdDefender * acd_defender_2 = NULL;
nm_auto_unref_l3ipv4ll NML3IPv4LL * l3ipv4ll = NULL;
gint64 start_time_msec;
gint64 total_poll_time_msec;
nm_auto_remove_l3ipv4ll_registration NML3IPv4LLRegistration *l3ipv4ll_reg = NULL;
char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
_LOGD("test start (/l3-ipv4ll/%d)", TEST_IDX);
if (nmtst_test_quick()) {
gs_free char *msg =
g_strdup_printf("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n",
g_get_prgname() ?: "test-l3-ipv4ll");
g_test_skip(msg);
return;
}
f = _test_fixture_1_setup(&test_fixture, TEST_IDX);
tdata->f = f;
if (tdata->f->test_idx == 1)
tdata->acd_timeout_msec = 0;
else
tdata->acd_timeout_msec = L3IPV4LL_ACD_TIMEOUT_MSEC;
_test_l3_ipv4ll_maybe_add_addr_4(tdata, 0, 4, &tdata->has_addr4_101, "192.168.133.101");
l3cfg0 = _netns_access_l3cfg(f->netns, f->ifindex0);
g_signal_connect(l3cfg0,
NM_L3CFG_SIGNAL_NOTIFY,
G_CALLBACK(_test_l3_ipv4ll_signal_notify),
tdata);
l3ipv4ll = nm_l3_ipv4ll_new(l3cfg0);
tdata->l3ipv4ll = l3ipv4ll;
g_assert_cmpint(nm_l3_ipv4ll_get_ifindex(l3ipv4ll), ==, f->ifindex0);
g_assert_cmpint(nm_l3_ipv4ll_get_state(l3ipv4ll), ==, NM_L3_IPV4LL_STATE_DISABLED);
g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll), ==, 0u);
if (tdata->f->test_idx == 1) {
if (nmtst_get_rand_one_case_in(2))
l3ipv4ll_reg = nm_l3_ipv4ll_register_new(l3ipv4ll, tdata->acd_timeout_msec);
} else
l3ipv4ll_reg = nm_l3_ipv4ll_register_new(l3ipv4ll, tdata->acd_timeout_msec);
g_assert(tdata->acd_timeout_msec == 0 || l3ipv4ll_reg);
g_assert(!l3ipv4ll_reg || l3ipv4ll == nm_l3_ipv4ll_register_get_instance(l3ipv4ll_reg));
if (tdata->acd_timeout_msec == 0) {
g_assert_cmpint(nm_l3_ipv4ll_get_state(l3ipv4ll), ==, NM_L3_IPV4LL_STATE_DISABLED);
g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll), ==, 0u);
} else {
g_assert_cmpint(nm_l3_ipv4ll_get_state(l3ipv4ll), ==, NM_L3_IPV4LL_STATE_PROBING);
if (f->test_idx == 1) {
g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll),
==,
nmtst_inet4_from_string("169.254.30.158"));
} else {
g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll),
==,
nmtst_inet4_from_string("169.254.17.45"));
}
g_assert(nm_l3_ipv4ll_get_l3cd(l3ipv4ll));
}
_test_l3_ipv4ll_maybe_add_addr_4(tdata, 0, 4, &tdata->has_addr4_101, "192.168.133.101");
if (tdata->f->test_idx == 2 && nmtst_get_rand_one_case_in(3)) {
in_addr_t a = nm_l3_ipv4ll_get_addr(l3ipv4ll);
g_assert(nm_utils_ip4_address_is_link_local(a));
_test_l3_ipv4ll_maybe_add_addr_4(tdata,
tdata->f->ifindex1,
2,
&tdata->add_conflict_done,
_nm_utils_inet4_ntop(a, sbuf_addr));
g_assert_cmpint(tdata->f->hwaddr1.len, ==, sizeof(NMEtherAddr));
acd_defender_2 =
nmtstp_acd_defender_new(tdata->f->ifindex1, a, &tdata->f->hwaddr1.ether_addr);
}
start_time_msec = nm_utils_get_monotonic_timestamp_msec();
total_poll_time_msec =
(L3IPV4LL_ACD_TIMEOUT_MSEC * 3 / 2) + (nmtst_get_rand_uint32() % L3IPV4LL_ACD_TIMEOUT_MSEC);
_LOGT("poll 1 start (wait %" G_GINT64_FORMAT " msec)", total_poll_time_msec);
while (TRUE) {
gint64 next_timeout_msec;
next_timeout_msec =
start_time_msec + total_poll_time_msec - nm_utils_get_monotonic_timestamp_msec();
if (next_timeout_msec <= 0)
break;
next_timeout_msec = NM_MIN(next_timeout_msec, nmtst_get_rand_uint32() % 1000u);
nmtst_main_context_iterate_until(NULL, next_timeout_msec, FALSE);
_LOGT("poll 1 intermezzo");
_test_l3_ipv4ll_maybe_add_addr_4(tdata,
0,
1 + total_poll_time_msec / 1000,
&tdata->has_addr4_101,
"192.168.133.101");
if (tdata->addr_commit == 1 && !tdata->add_conflict_checked) {
tdata->add_conflict_checked = TRUE;
_test_l3_ipv4ll_maybe_add_addr_4(
tdata,
tdata->f->ifindex1,
2,
&tdata->add_conflict_done,
_nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr));
if (tdata->add_conflict_done)
total_poll_time_msec += L3IPV4LL_ACD_TIMEOUT_MSEC / 2;
g_assert_cmpint(tdata->f->hwaddr1.len, ==, sizeof(NMEtherAddr));
acd_defender_2 = nmtstp_acd_defender_new(tdata->f->ifindex1,
tdata->addr_commit_addr,
&tdata->f->hwaddr1.ether_addr);
}
}
_LOGT("poll 1 end");
if (tdata->addr_commit || nmtst_get_rand_bool()) {
nm_l3cfg_remove_config_all(nm_l3_ipv4ll_get_l3cfg(l3ipv4ll),
TEST_L3_IPV4LL_TAG(tdata, 1),
FALSE);
}
nmtstp_acd_defender_destroy(g_steal_pointer(&acd_defender_1));
nmtstp_acd_defender_destroy(g_steal_pointer(&acd_defender_2));
nm_l3cfg_commit_type_unregister(l3cfg0, g_steal_pointer(&tdata->l3cfg_commit_type_1));
g_signal_handlers_disconnect_by_func(l3cfg0, G_CALLBACK(_test_l3_ipv4ll_signal_notify), tdata);
}
/*****************************************************************************/
NMTstpSetupFunc const _nmtstp_setup_platform_func = nm_linux_platform_setup;
void
_nmtstp_init_tests(int *argc, char ***argv)
{
nmtst_init_with_logging(argc, argv, "ERR", "ALL");
}
void
_nmtstp_setup_tests(void)
{
g_test_add_data_func("/l3cfg/1", GINT_TO_POINTER(1), test_l3cfg);
g_test_add_data_func("/l3cfg/2", GINT_TO_POINTER(2), test_l3cfg);
g_test_add_data_func("/l3cfg/3", GINT_TO_POINTER(3), test_l3cfg);
g_test_add_data_func("/l3cfg/4", GINT_TO_POINTER(4), test_l3cfg);
g_test_add_data_func("/l3-ipv4ll/1", GINT_TO_POINTER(1), test_l3_ipv4ll);
g_test_add_data_func("/l3-ipv4ll/2", GINT_TO_POINTER(2), test_l3_ipv4ll);
}