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:
2171
src/nm-l3cfg.c
2171
src/nm-l3cfg.c
File diff suppressed because it is too large
Load Diff
@@ -18,10 +18,47 @@
|
||||
|
||||
#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 {
|
||||
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(). */
|
||||
NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT,
|
||||
@@ -41,21 +78,12 @@ typedef enum {
|
||||
_NM_L3_CONFIG_NOTIFY_TYPE_NUM,
|
||||
} NML3ConfigNotifyType;
|
||||
|
||||
typedef struct {
|
||||
const NMPObject * obj;
|
||||
const NML3ConfigData *l3cd;
|
||||
gconstpointer tag;
|
||||
} NML3ConfigNotifyPayloadAcdFailedSource;
|
||||
|
||||
typedef struct {
|
||||
NML3ConfigNotifyType notify_type;
|
||||
union {
|
||||
struct {
|
||||
in_addr_t addr;
|
||||
guint sources_len;
|
||||
bool probe_result : 1;
|
||||
const NML3ConfigNotifyPayloadAcdFailedSource *sources;
|
||||
} acd_completed;
|
||||
NML3AcdAddrInfo info;
|
||||
} acd_event;
|
||||
|
||||
struct {
|
||||
const NMPObject * obj;
|
||||
@@ -79,7 +107,6 @@ struct _NML3Cfg {
|
||||
const NMPObject * plobj;
|
||||
const NMPObject * plobj_next;
|
||||
int ifindex;
|
||||
bool changed_configs : 1;
|
||||
} priv;
|
||||
};
|
||||
|
||||
@@ -205,6 +232,7 @@ gboolean nm_l3cfg_add_config(NML3Cfg * self,
|
||||
guint32 default_route_metric_6,
|
||||
guint32 default_route_penalty_4,
|
||||
guint32 default_route_penalty_6,
|
||||
NML3AcdDefendType acd_defend_type,
|
||||
guint32 acd_timeout_msec,
|
||||
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);
|
||||
|
||||
typedef struct _NML3CfgCommitTypeHandle NML3CfgCommitTypeHandle;
|
||||
|
@@ -93,6 +93,9 @@ typedef enum {
|
||||
typedef struct {
|
||||
const TestFixture1 *f;
|
||||
|
||||
guint32 acd_timeout_msec_a;
|
||||
NML3AcdDefendType acd_defend_type_a;
|
||||
|
||||
TestL3cfgNotifyType notify_type;
|
||||
guint post_commit_event_count;
|
||||
guint general_event_count;
|
||||
@@ -101,7 +104,7 @@ typedef struct {
|
||||
int cb_count;
|
||||
bool expected_probe_result : 1;
|
||||
} wait_for_acd_ready_1;
|
||||
} notify_data;
|
||||
} notify_result;
|
||||
} TestL3cfgData;
|
||||
|
||||
static void
|
||||
@@ -112,7 +115,7 @@ _test_l3cfg_data_set_notify_type(TestL3cfgData *tdata, TestL3cfgNotifyType notif
|
||||
tdata->notify_type = notify_type;
|
||||
tdata->post_commit_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
|
||||
@@ -120,6 +123,8 @@ _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);
|
||||
@@ -131,6 +136,18 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
|
||||
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) {
|
||||
@@ -150,11 +167,15 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
|
||||
case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT:
|
||||
tdata->post_commit_event_count++;
|
||||
return;
|
||||
case NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED:
|
||||
case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT:
|
||||
switch (tdata->f->test_idx) {
|
||||
case 2:
|
||||
nmtst_assert_ip4_address(notify_data->acd_completed.addr, "192.167.133.45");
|
||||
g_assert(notify_data->acd_completed.probe_result);
|
||||
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;
|
||||
@@ -169,30 +190,46 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
|
||||
return;
|
||||
}
|
||||
case TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1:
|
||||
{
|
||||
int num_acd_completed_events = 1;
|
||||
|
||||
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_COMPLETED) {
|
||||
g_assert(tdata->notify_data.wait_for_acd_ready_1.cb_count == 0);
|
||||
tdata->notify_data.wait_for_acd_ready_1.cb_count++;
|
||||
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));
|
||||
} 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;
|
||||
}
|
||||
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);
|
||||
tdata->notify_data.wait_for_acd_ready_1.cb_count++;
|
||||
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,
|
||||
==,
|
||||
2 * 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,
|
||||
"192.167.133.45",
|
||||
"192.168.133.45",
|
||||
"1:2:3:4::45");
|
||||
return;
|
||||
}
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
@@ -200,19 +237,20 @@ _test_l3cfg_signal_notify(NML3Cfg * l3cfg,
|
||||
static void
|
||||
test_l3cfg(gconstpointer test_data)
|
||||
{
|
||||
nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {};
|
||||
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;
|
||||
guint32 acd_timeout_msec = 0;
|
||||
TestL3cfgData tdata_stack = {
|
||||
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)", GPOINTER_TO_INT(test_data));
|
||||
_LOGD("test start (/l3cfg/%d)", TEST_IDX);
|
||||
|
||||
if (nmtst_test_quick()) {
|
||||
gs_free char *msg =
|
||||
@@ -223,10 +261,13 @@ test_l3cfg(gconstpointer test_data)
|
||||
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->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);
|
||||
g_assert(NM_IS_L3CFG(l3cfg0));
|
||||
|
||||
@@ -256,8 +297,8 @@ test_l3cfg(gconstpointer test_data)
|
||||
|
||||
nm_l3_config_data_add_address_4(
|
||||
l3cd,
|
||||
NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.167.133.45"),
|
||||
.peer_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.168.133.45"),
|
||||
.plen = 24, ));
|
||||
|
||||
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) {
|
||||
nm_l3cfg_add_config(l3cfg0,
|
||||
@@ -286,7 +327,8 @@ test_l3cfg(gconstpointer test_data)
|
||||
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
|
||||
0,
|
||||
0,
|
||||
acd_timeout_msec,
|
||||
tdata->acd_defend_type_a,
|
||||
tdata->acd_timeout_msec_a,
|
||||
NM_L3_CONFIG_MERGE_FLAGS_NONE);
|
||||
}
|
||||
|
||||
@@ -306,24 +348,30 @@ test_l3cfg(gconstpointer test_data)
|
||||
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);
|
||||
|
||||
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() % 5000u, FALSE);
|
||||
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)) {
|
||||
_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");
|
||||
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");
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -340,7 +388,7 @@ test_l3cfg(gconstpointer test_data)
|
||||
if ((nmtst_get_rand_uint32() % 3) == 0)
|
||||
_test_fixture_1_teardown(&test_fixture);
|
||||
|
||||
_LOGD("test end (/l3cfg/%d)", f->test_idx);
|
||||
_LOGD("test end (/l3cfg/%d)", TEST_IDX);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
Reference in New Issue
Block a user