sd-ndisc: stop solicitations only after an RS and non-zero-lifetime RA
RFC 4861 section 6.3.7 states that a host should send at least one Router Solicitation even if it receives an unsolicited Router Advertisement before sending its first solicitation, and should stop soliciting only after receiving a valid Router Advertisement with a non-zero Router Lifetime. Keep the RS timer active until both conditions are met, and add a regression test for a zero-lifetime RA arriving before the initial RS. Fixes #43205.
This commit is contained in:
@@ -219,7 +219,10 @@ static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) event_source_disable(nd->timeout_event_source);
|
||||
/* Stop sending RS once we have sent at least one RS and received an RA with a non-zero lifetime. */
|
||||
if (nd->retransmit_time > 0 && sd_ndisc_router_get_lifetime(rt, /* ret= */ NULL) > 0)
|
||||
(void) event_source_disable(nd->timeout_event_source);
|
||||
|
||||
(void) event_source_disable(nd->timeout_no_ra);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
@@ -466,6 +469,8 @@ static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata
|
||||
|
||||
log_ndisc(nd, "No RA received before link confirmation timeout");
|
||||
|
||||
/* Also stop sending RS here, by disabling timeout_event_source. */
|
||||
(void) event_source_disable(nd->timeout_event_source);
|
||||
(void) event_source_disable(nd->timeout_no_ra);
|
||||
ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
|
||||
|
||||
|
||||
@@ -295,6 +295,15 @@ static void test_callback_count(
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
static int on_recv_rs_exit(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
|
||||
|
||||
ASSERT_OK(icmp6_packet_receive(fd, &packet));
|
||||
ASSERT_EQ(icmp6_packet_get_type(packet), ND_ROUTER_SOLICIT);
|
||||
|
||||
return ASSERT_OK(sd_event_exit(ASSERT_PTR(userdata), 0));
|
||||
}
|
||||
|
||||
static int on_recv_rs(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
|
||||
assert_se(icmp6_packet_receive(fd, &packet) >= 0);
|
||||
@@ -323,6 +332,39 @@ TEST(rs_ifindex_mismatch) {
|
||||
test_fd[1] = safe_close(test_fd[1]);
|
||||
}
|
||||
|
||||
TEST(rs_after_zero_lifetime_ra) {
|
||||
static const struct nd_router_advert advertisement = {
|
||||
.nd_ra_type = ND_ROUTER_ADVERT,
|
||||
};
|
||||
|
||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
|
||||
unsigned count = 0;
|
||||
|
||||
ASSERT_OK(sd_event_new(&e));
|
||||
ASSERT_OK(sd_ndisc_new(&nd));
|
||||
ASSERT_OK(sd_ndisc_attach_event(nd, e, 0));
|
||||
ASSERT_OK(sd_ndisc_set_ifindex(nd, 42));
|
||||
ASSERT_OK(sd_ndisc_set_callback(nd, test_callback_count, &count));
|
||||
ASSERT_OK(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
|
||||
30 * USEC_PER_SEC, 0,
|
||||
NULL, INT_TO_PTR(-ETIMEDOUT)));
|
||||
ASSERT_OK(sd_ndisc_start(nd));
|
||||
|
||||
ASSERT_EQ(write(test_fd[1], &advertisement, sizeof(advertisement)), (ssize_t) sizeof(advertisement));
|
||||
ASSERT_OK_POSITIVE(sd_event_run(e, UINT64_MAX));
|
||||
ASSERT_EQ(count, 1U);
|
||||
ASSERT_OK_POSITIVE(sd_event_source_get_enabled(nd->timeout_event_source, NULL));
|
||||
|
||||
ASSERT_OK(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_exit, e));
|
||||
ASSERT_OK(sd_event_source_set_io_fd_own(s, true));
|
||||
ASSERT_OK(sd_event_source_set_time(nd->timeout_event_source, 0));
|
||||
ASSERT_OK(sd_event_loop(e));
|
||||
|
||||
test_fd[1] = -EBADF;
|
||||
}
|
||||
|
||||
TEST(rs) {
|
||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
|
||||
Reference in New Issue
Block a user