l3cfg: rework ACD handling in NML3Cfg to support handling conflicts

Heavily rework NML3Cfg's ACD handling.

- the (user facing) API changed, so that we can ask the current ACD
  state of an address with nm_l3cfg_get_acd_addr_info(). So, the
  acd-event signal is only to notify when the state changes, it does
  not carry information that you couldn't fetch anytime.

- add clearer ACD states (NML3AcdAddrState). The current (ACD) state
  of an address is important and becomes part of the information that
  we expose.

- add new ACD state "USED", when ACD fails. This blocks the address from
  being used. Usually the caller would either remove the (used) address
  or force reconfigure it (by setting acd_timeout_msec to zero).

- add new ACD state "CONFLICT". Previously conflicts were not handled.
  Now the API allows to specify the defend policy. A conflicted address
  also gets blocked from being used.

- add new ACD state "EXTERNAL_REMOVED". This happens when we have an
  address we wanted to configure, but then the address is no longer
  on the interface. For example because the user removed it from the
  interface. This also leaves the device indefinitely blocked, and
  is important to stop announcing the address.

- add a new ACD state "READY". This indicates that the address is ready
  to be configured, but not yet actually configured on the device. This
  is the step before "DEFENDING".
This commit is contained in:
Thomas Haller
2020-09-28 18:07:51 +02:00
parent 79664aa106
commit b8f9d7b5dd
3 changed files with 1381 additions and 952 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -18,10 +18,47 @@
#define NM_L3CFG_SIGNAL_NOTIFY "l3cfg-notify" #define NM_L3CFG_SIGNAL_NOTIFY "l3cfg-notify"
typedef enum _nm_packed {
NM_L3_ACD_DEFEND_TYPE_NONE,
NM_L3_ACD_DEFEND_TYPE_NEVER,
NM_L3_ACD_DEFEND_TYPE_ONCE,
NM_L3_ACD_DEFEND_TYPE_ALWAYS,
} NML3AcdDefendType;
typedef enum _nm_packed {
NM_L3_ACD_ADDR_STATE_INIT,
NM_L3_ACD_ADDR_STATE_PROBING,
NM_L3_ACD_ADDR_STATE_USED,
NM_L3_ACD_ADDR_STATE_READY,
NM_L3_ACD_ADDR_STATE_DEFENDING,
NM_L3_ACD_ADDR_STATE_CONFLICT,
NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED,
} NML3AcdAddrState;
typedef struct {
const NMPObject * obj;
const NML3ConfigData *l3cd;
gconstpointer tag;
char _padding[sizeof(struct {
guint32 a;
NML3AcdDefendType b;
guint8 c;
})];
} NML3AcdAddrTrackInfo;
typedef struct {
in_addr_t addr;
guint n_track_infos;
NML3AcdAddrState state;
NML3Cfg * l3cfg;
const NML3AcdAddrTrackInfo *track_infos;
} NML3AcdAddrInfo;
typedef enum { typedef enum {
NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED, NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED,
NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED, NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT,
/* emitted at the end of nm_l3cfg_platform_commit(). */ /* emitted at the end of nm_l3cfg_platform_commit(). */
NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT, NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT,
@@ -41,21 +78,12 @@ typedef enum {
_NM_L3_CONFIG_NOTIFY_TYPE_NUM, _NM_L3_CONFIG_NOTIFY_TYPE_NUM,
} NML3ConfigNotifyType; } NML3ConfigNotifyType;
typedef struct {
const NMPObject * obj;
const NML3ConfigData *l3cd;
gconstpointer tag;
} NML3ConfigNotifyPayloadAcdFailedSource;
typedef struct { typedef struct {
NML3ConfigNotifyType notify_type; NML3ConfigNotifyType notify_type;
union { union {
struct { struct {
in_addr_t addr; NML3AcdAddrInfo info;
guint sources_len; } acd_event;
bool probe_result : 1;
const NML3ConfigNotifyPayloadAcdFailedSource *sources;
} acd_completed;
struct { struct {
const NMPObject * obj; const NMPObject * obj;
@@ -79,7 +107,6 @@ struct _NML3Cfg {
const NMPObject * plobj; const NMPObject * plobj;
const NMPObject * plobj_next; const NMPObject * plobj_next;
int ifindex; int ifindex;
bool changed_configs : 1;
} priv; } priv;
}; };
@@ -205,6 +232,7 @@ gboolean nm_l3cfg_add_config(NML3Cfg * self,
guint32 default_route_metric_6, guint32 default_route_metric_6,
guint32 default_route_penalty_4, guint32 default_route_penalty_4,
guint32 default_route_penalty_6, guint32 default_route_penalty_6,
NML3AcdDefendType acd_defend_type,
guint32 acd_timeout_msec, guint32 acd_timeout_msec,
NML3ConfigMergeFlags merge_flags); NML3ConfigMergeFlags merge_flags);
@@ -250,6 +278,10 @@ void nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self);
/*****************************************************************************/ /*****************************************************************************/
const NML3AcdAddrInfo *nm_l3cfg_get_acd_addr_info(NML3Cfg *self, in_addr_t addr);
/*****************************************************************************/
NML3CfgCommitType nm_l3cfg_commit_type_get(NML3Cfg *self); NML3CfgCommitType nm_l3cfg_commit_type_get(NML3Cfg *self);
typedef struct _NML3CfgCommitTypeHandle NML3CfgCommitTypeHandle; typedef struct _NML3CfgCommitTypeHandle NML3CfgCommitTypeHandle;

View File

@@ -93,6 +93,9 @@ typedef enum {
typedef struct { typedef struct {
const TestFixture1 *f; const TestFixture1 *f;
guint32 acd_timeout_msec_a;
NML3AcdDefendType acd_defend_type_a;
TestL3cfgNotifyType notify_type; TestL3cfgNotifyType notify_type;
guint post_commit_event_count; guint post_commit_event_count;
guint general_event_count; guint general_event_count;
@@ -101,7 +104,7 @@ typedef struct {
int cb_count; int cb_count;
bool expected_probe_result : 1; bool expected_probe_result : 1;
} wait_for_acd_ready_1; } wait_for_acd_ready_1;
} notify_data; } notify_result;
} TestL3cfgData; } TestL3cfgData;
static void static void
@@ -112,7 +115,7 @@ _test_l3cfg_data_set_notify_type(TestL3cfgData *tdata, TestL3cfgNotifyType notif
tdata->notify_type = notify_type; tdata->notify_type = notify_type;
tdata->post_commit_event_count = 0; tdata->post_commit_event_count = 0;
tdata->general_event_count = 0; tdata->general_event_count = 0;
memset(&tdata->notify_data, 0, sizeof(tdata->notify_data)); memset(&tdata->notify_result, 0, sizeof(tdata->notify_result));
} }
static void static void
@@ -120,6 +123,8 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
const NML3ConfigNotifyData *notify_data, const NML3ConfigNotifyData *notify_data,
TestL3cfgData * tdata) TestL3cfgData * tdata)
{ {
guint i;
g_assert(NM_IS_L3CFG(l3cfg)); g_assert(NM_IS_L3CFG(l3cfg));
g_assert(tdata); g_assert(tdata);
g_assert(notify_data); g_assert(notify_data);
@@ -131,6 +136,18 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
else if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE) { 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(NMP_OBJECT_IS_VALID(notify_data->platform_change.obj));
g_assert(notify_data->platform_change.change_type != 0); 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 = &notify_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) { switch (tdata->notify_type) {
@@ -150,11 +167,15 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT: case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT:
tdata->post_commit_event_count++; tdata->post_commit_event_count++;
return; return;
case NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED: case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT:
switch (tdata->f->test_idx) { switch (tdata->f->test_idx) {
case 2: case 2:
nmtst_assert_ip4_address(notify_data->acd_completed.addr, "192.167.133.45"); case 3:
g_assert(notify_data->acd_completed.probe_result); 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); g_assert(tdata->general_event_count == 0);
tdata->general_event_count++; tdata->general_event_count++;
return; return;
@@ -169,30 +190,46 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
return; return;
} }
case TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1: case TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1:
{
int num_acd_completed_events = 1;
if (NM_IN_SET(notify_data->notify_type, if (NM_IN_SET(notify_data->notify_type,
NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE, NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE,
NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE)) NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE))
return; return;
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED) { if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT) {
g_assert(tdata->notify_data.wait_for_acd_ready_1.cb_count == 0); if (notify_data->acd_event.info.addr == nmtst_inet4_from_string("192.168.133.45")) {
tdata->notify_data.wait_for_acd_ready_1.cb_count++; g_assert(NM_IN_SET(notify_data->acd_event.info.state,
NM_L3_ACD_ADDR_STATE_READY,
NM_L3_ACD_ADDR_STATE_DEFENDING));
} else
g_assert_not_reached();
g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count,
<,
2 * num_acd_completed_events);
tdata->notify_result.wait_for_acd_ready_1.cb_count++;
return; return;
} }
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT) { if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT) {
g_assert(tdata->notify_data.wait_for_acd_ready_1.cb_count == 1); g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, >, 0);
tdata->notify_data.wait_for_acd_ready_1.cb_count++; g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count,
==,
2 * num_acd_completed_events);
tdata->notify_result.wait_for_acd_ready_1.cb_count++;
nmtstp_platform_ip_addresses_assert(tdata->f->platform, nmtstp_platform_ip_addresses_assert(tdata->f->platform,
tdata->f->ifindex0, tdata->f->ifindex0,
TRUE, TRUE,
TRUE, TRUE,
TRUE, TRUE,
"192.167.133.45", "192.168.133.45",
"1:2:3:4::45"); "1:2:3:4::45");
return; return;
} }
g_assert_not_reached(); g_assert_not_reached();
return; return;
} }
}
g_assert_not_reached(); g_assert_not_reached();
} }
@@ -200,19 +237,20 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
static void static void
test_l3cfg(gconstpointer test_data) 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 = {}; nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {};
const TestFixture1 * f; const TestFixture1 * f;
NML3CfgCommitTypeHandle * commit_type_1; NML3CfgCommitTypeHandle * commit_type_1;
NML3CfgCommitTypeHandle * commit_type_2; NML3CfgCommitTypeHandle * commit_type_2;
gs_unref_object NML3Cfg *l3cfg0 = NULL; gs_unref_object NML3Cfg *l3cfg0 = NULL;
nm_auto_unref_l3cd const NML3ConfigData *l3cd_a = NULL; nm_auto_unref_l3cd const NML3ConfigData *l3cd_a = NULL;
guint32 acd_timeout_msec = 0;
TestL3cfgData tdata_stack = { TestL3cfgData tdata_stack = {
.f = NULL, .f = NULL,
}; };
TestL3cfgData *const tdata = &tdata_stack; TestL3cfgData *const tdata = &tdata_stack;
_LOGD("test start (/l3cfg/%d)", GPOINTER_TO_INT(test_data)); _LOGD("test start (/l3cfg/%d)", TEST_IDX);
if (nmtst_test_quick()) { if (nmtst_test_quick()) {
gs_free char *msg = gs_free char *msg =
@@ -223,10 +261,13 @@ test_l3cfg(gconstpointer test_data)
return; return;
} }
f = _test_fixture_1_setup(&test_fixture, GPOINTER_TO_INT(test_data)); f = _test_fixture_1_setup(&test_fixture, TEST_IDX);
tdata->f = f; tdata->f = f;
tdata->acd_timeout_msec_a = NM_IN_SET(f->test_idx, 3) ? ACD_TIMEOUT_BASE_MSEC : 0u;
tdata->acd_defend_type_a = NM_L3_ACD_DEFEND_TYPE_NEVER;
l3cfg0 = nm_netns_access_l3cfg(f->netns, f->ifindex0); l3cfg0 = nm_netns_access_l3cfg(f->netns, f->ifindex0);
g_assert(NM_IS_L3CFG(l3cfg0)); g_assert(NM_IS_L3CFG(l3cfg0));
@@ -256,8 +297,8 @@ test_l3cfg(gconstpointer test_data)
nm_l3_config_data_add_address_4( nm_l3_config_data_add_address_4(
l3cd, l3cd,
NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.167.133.45"), NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.168.133.45"),
.peer_address = nmtst_inet4_from_string("192.167.133.45"), .peer_address = nmtst_inet4_from_string("192.168.133.45"),
.plen = 24, )); .plen = 24, ));
nm_l3_config_data_add_address_6( nm_l3_config_data_add_address_6(
@@ -272,7 +313,7 @@ test_l3cfg(gconstpointer test_data)
} }
} }
acd_timeout_msec = (f->test_idx == 3) ? 2000u : 0u; nm_l3_config_data_log(l3cd_a, "l3cd_a", "platform-test: l3cd_a: ", LOGL_DEBUG, LOGD_PLATFORM);
if (l3cd_a) { if (l3cd_a) {
nm_l3cfg_add_config(l3cfg0, nm_l3cfg_add_config(l3cfg0,
@@ -286,7 +327,8 @@ test_l3cfg(gconstpointer test_data)
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
0, 0,
0, 0,
acd_timeout_msec, tdata->acd_defend_type_a,
tdata->acd_timeout_msec_a,
NM_L3_CONFIG_MERGE_FLAGS_NONE); NM_L3_CONFIG_MERGE_FLAGS_NONE);
} }
@@ -306,24 +348,30 @@ test_l3cfg(gconstpointer test_data)
TRUE, TRUE,
TRUE, TRUE,
TRUE, TRUE,
NM_IN_SET(f->test_idx, 2) ? "192.167.133.45" : NULL, NM_IN_SET(f->test_idx, 2) ? "192.168.133.45" : NULL,
NM_IN_SET(f->test_idx, 2, 3) ? "1:2:3:4::45" : NULL); NM_IN_SET(f->test_idx, 2, 3) ? "1:2:3:4::45" : NULL);
if (NM_IN_SET(f->test_idx, 1, 2)) { if (NM_IN_SET(f->test_idx, 1, 2)) {
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL); _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL);
_LOGT("poll 1 start"); _LOGT("poll 1 start");
nmtst_main_context_iterate_until(NULL, nmtst_get_rand_uint32() % 5000u, FALSE); nmtst_main_context_iterate_until(NULL,
nmtst_get_rand_uint32() % (ACD_TIMEOUT_BASE_MSEC * 5u),
FALSE);
_LOGT("poll 1 end"); _LOGT("poll 1 end");
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE); _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE);
} }
if (NM_IN_SET(f->test_idx, 3)) { if (NM_IN_SET(f->test_idx, 3)) {
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1); _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1);
tdata->notify_data.wait_for_acd_ready_1.expected_probe_result = TRUE; tdata->notify_result.wait_for_acd_ready_1.expected_probe_result = TRUE;
_LOGT("poll 2 start"); _LOGT("poll 2 start");
nmtst_main_context_iterate_until(NULL, 2500u + (nmtst_get_rand_uint32() % 4000u), FALSE); 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"); _LOGT("poll 2 end");
g_assert_cmpint(tdata->notify_data.wait_for_acd_ready_1.cb_count, ==, 2); g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, ==, 3);
_test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE); _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE);
} }
@@ -340,7 +388,7 @@ test_l3cfg(gconstpointer test_data)
if ((nmtst_get_rand_uint32() % 3) == 0) if ((nmtst_get_rand_uint32() % 3) == 0)
_test_fixture_1_teardown(&test_fixture); _test_fixture_1_teardown(&test_fixture);
_LOGD("test end (/l3cfg/%d)", f->test_idx); _LOGD("test end (/l3cfg/%d)", TEST_IDX);
} }
/*****************************************************************************/ /*****************************************************************************/