2828#include "in-addr-util.h"
2929#include "ndisc-internal.h"
3030#include "ndisc-router.h"
31+ #include "random-util.h"
3132#include "socket-util.h"
3233#include "string-util.h"
3334#include "util.h"
3435
35- #define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
36- #define NDISC_MAX_ROUTER_SOLICITATIONS 3U
36+ #define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
3737
3838static void ndisc_callback (sd_ndisc * ndisc , sd_ndisc_event event , sd_ndisc_router * rt ) {
3939 assert (ndisc );
@@ -129,9 +129,10 @@ static int ndisc_reset(sd_ndisc *nd) {
129129 assert (nd );
130130
131131 nd -> timeout_event_source = sd_event_source_unref (nd -> timeout_event_source );
132+ nd -> timeout_no_ra = sd_event_source_unref (nd -> timeout_no_ra );
133+ nd -> retransmit_time = 0 ;
132134 nd -> recv_event_source = sd_event_source_unref (nd -> recv_event_source );
133135 nd -> fd = safe_close (nd -> fd );
134- nd -> nd_sent = 0 ;
135136
136137 return 0 ;
137138}
@@ -264,52 +265,85 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
264265 return ndisc_handle_datagram (nd , rt );
265266}
266267
268+ static usec_t ndisc_timeout_compute_random (usec_t val ) {
269+ /* compute a time that is random within ±10% of the given value */
270+ return val - val / 10 +
271+ (random_u64 () % (2 * USEC_PER_SEC )) * val / 10 / USEC_PER_SEC ;
272+ }
273+
267274static int ndisc_timeout (sd_event_source * s , uint64_t usec , void * userdata ) {
268275 sd_ndisc * nd = userdata ;
269- usec_t time_now , next_timeout ;
276+ usec_t time_now ;
270277 int r ;
278+ char time_string [FORMAT_TIMESPAN_MAX ];
271279
272280 assert (s );
273281 assert (nd );
274282 assert (nd -> event );
275283
276- if (nd -> nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS ) {
277- nd -> timeout_event_source = sd_event_source_unref (nd -> timeout_event_source );
278- ndisc_callback (nd , SD_NDISC_EVENT_TIMEOUT , NULL );
279- return 0 ;
284+ assert_se (sd_event_now (nd -> event , clock_boottime_or_monotonic (), & time_now ) >= 0 );
285+
286+ nd -> timeout_event_source = sd_event_source_unref (nd -> timeout_event_source );
287+
288+ if (!nd -> retransmit_time )
289+ nd -> retransmit_time = ndisc_timeout_compute_random (NDISC_ROUTER_SOLICITATION_INTERVAL );
290+ else {
291+ if (nd -> retransmit_time > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 2 )
292+ nd -> retransmit_time = ndisc_timeout_compute_random (NDISC_MAX_ROUTER_SOLICITATION_INTERVAL );
293+ else
294+ nd -> retransmit_time += ndisc_timeout_compute_random (nd -> retransmit_time );
280295 }
281296
282- r = icmp6_send_router_solicitation (nd -> fd , & nd -> mac_addr );
283- if (r < 0 ) {
284- log_ndisc_errno (r , "Error sending Router Solicitation: %m" );
297+ r = sd_event_add_time (nd -> event , & nd -> timeout_event_source ,
298+ clock_boottime_or_monotonic (),
299+ time_now + nd -> retransmit_time ,
300+ 10 * USEC_PER_MSEC , ndisc_timeout , nd );
301+ if (r < 0 )
285302 goto fail ;
286- }
287303
288- log_ndisc ("Sent Router Solicitation" );
289- nd -> nd_sent ++ ;
304+ r = sd_event_source_set_priority (nd -> timeout_event_source , nd -> event_priority );
305+ if (r < 0 )
306+ goto fail ;
290307
291- assert_se (sd_event_now (nd -> event , clock_boottime_or_monotonic (), & time_now ) >= 0 );
292- next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL ;
308+ (void ) sd_event_source_set_description (nd -> timeout_event_source , "ndisc-timeout-no-ra" );
293309
294- r = sd_event_source_set_time (nd -> timeout_event_source , next_timeout );
310+ r = sd_event_source_set_enabled (nd -> timeout_event_source , SD_EVENT_ONESHOT );
295311 if (r < 0 ) {
296- log_ndisc_errno (r , "Error updating timer: %m" );
312+ log_ndisc_errno (r , "Error reenabling timer: %m" );
297313 goto fail ;
298314 }
299315
300- r = sd_event_source_set_enabled (nd -> timeout_event_source , SD_EVENT_ONESHOT );
316+ r = icmp6_send_router_solicitation (nd -> fd , & nd -> mac_addr );
301317 if (r < 0 ) {
302- log_ndisc_errno (r , "Error reenabling timer : %m" );
318+ log_ndisc_errno (r , "Error sending Router Solicitation : %m" );
303319 goto fail ;
304320 }
305321
322+ log_ndisc ("Sent Router Solicitation, next solicitation in %s" ,
323+ format_timespan (time_string , FORMAT_TIMESPAN_MAX ,
324+ nd -> retransmit_time , USEC_PER_SEC ));
325+
306326 return 0 ;
307327
308328fail :
309329 sd_ndisc_stop (nd );
310330 return 0 ;
311331}
312332
333+ static int ndisc_timeout_no_ra (sd_event_source * s , uint64_t usec , void * userdata ) {
334+ sd_ndisc * nd = userdata ;
335+
336+ assert (s );
337+ assert (nd );
338+
339+ log_ndisc ("No RA received before link confirmation timeout" );
340+
341+ nd -> timeout_no_ra = sd_event_source_unref (nd -> timeout_no_ra );
342+ ndisc_callback (nd , SD_NDISC_EVENT_TIMEOUT , NULL );
343+
344+ return 0 ;
345+ }
346+
313347_public_ int sd_ndisc_stop (sd_ndisc * nd ) {
314348 assert_return (nd , - EINVAL );
315349
@@ -324,6 +358,7 @@ _public_ int sd_ndisc_stop(sd_ndisc *nd) {
324358
325359_public_ int sd_ndisc_start (sd_ndisc * nd ) {
326360 int r ;
361+ usec_t time_now ;
327362
328363 assert_return (nd , - EINVAL );
329364 assert_return (nd -> event , - EINVAL );
@@ -335,6 +370,10 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) {
335370 assert (!nd -> recv_event_source );
336371 assert (!nd -> timeout_event_source );
337372
373+ r = sd_event_now (nd -> event , clock_boottime_or_monotonic (), & time_now );
374+ if (r < 0 )
375+ goto fail ;
376+
338377 nd -> fd = icmp6_bind_router_solicitation (nd -> ifindex );
339378 if (nd -> fd < 0 )
340379 return nd -> fd ;
@@ -359,6 +398,19 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) {
359398
360399 (void ) sd_event_source_set_description (nd -> timeout_event_source , "ndisc-timeout" );
361400
401+ r = sd_event_add_time (nd -> event , & nd -> timeout_no_ra ,
402+ clock_boottime_or_monotonic (),
403+ time_now + NDISC_TIMEOUT_NO_RA_USEC ,
404+ 10 * USEC_PER_MSEC , ndisc_timeout_no_ra , nd );
405+ if (r < 0 )
406+ goto fail ;
407+
408+ r = sd_event_source_set_priority (nd -> timeout_no_ra , nd -> event_priority );
409+ if (r < 0 )
410+ goto fail ;
411+
412+ (void ) sd_event_source_set_description (nd -> timeout_no_ra , "ndisc-timeout-no-ra" );
413+
362414 log_ndisc ("Started IPv6 Router Solicitation client" );
363415 return 1 ;
364416
0 commit comments