1212
1313#include "event-util.h"
1414#include "missing_network.h"
15+ #include "ndisc-router-internal.h"
1516#include "networkd-address-generation.h"
1617#include "networkd-address.h"
1718#include "networkd-dhcp6.h"
3536 * Not sure if the threshold is high enough. Let's adjust later if not. */
3637#define NDISC_PREF64_MAX 64U
3738
39+ static int ndisc_drop_outdated (Link * link , const struct in6_addr * router , usec_t timestamp_usec );
40+
3841bool link_ndisc_enabled (Link * link ) {
3942 assert (link );
4043
@@ -516,8 +519,6 @@ static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
516519}
517520
518521static int ndisc_redirect_verify_sender (Link * link , sd_ndisc_redirect * rd ) {
519- sd_ndisc_redirect * existing ;
520- struct in6_addr router , sender ;
521522 int r ;
522523
523524 assert (link );
@@ -527,11 +528,18 @@ static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
527528 * The IP source address of the Redirect is the same as the current first-hop router for the specified
528529 * ICMP Destination Address. */
529530
531+ struct in6_addr sender ;
530532 r = sd_ndisc_redirect_get_sender_address (rd , & sender );
531533 if (r < 0 )
532534 return r ;
533535
534- existing = set_get (link -> ndisc_redirects , rd );
536+ /* We will reuse the sender's router lifetime as the lifetime of the redirect route. Hence, if we
537+ * have not remembered an RA from the sender, refuse the Redirect message. */
538+ sd_ndisc_router * router = hashmap_get (link -> ndisc_routers_by_sender , & sender );
539+ if (!router )
540+ return false;
541+
542+ sd_ndisc_redirect * existing = set_get (link -> ndisc_redirects , rd );
535543 if (existing ) {
536544 struct in6_addr target , dest ;
537545
@@ -555,15 +563,30 @@ static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
555563 return false;
556564 }
557565
558- if (!link -> ndisc_default_router )
559- return false;
560-
561- r = sd_ndisc_router_get_sender_address (link -> ndisc_default_router , & router );
566+ /* Check if the sender is one of the known router with highest priority. */
567+ uint8_t preference ;
568+ r = sd_ndisc_router_get_preference (router , & preference );
562569 if (r < 0 )
563570 return r ;
564571
565- /* The sender must be the default router. */
566- return in6_addr_equal (& sender , & router );
572+ if (preference == SD_NDISC_PREFERENCE_HIGH )
573+ return true;
574+
575+ sd_ndisc_router * rt ;
576+ HASHMAP_FOREACH (rt , link -> ndisc_routers_by_sender ) {
577+ if (rt == router )
578+ continue ;
579+
580+ uint8_t pref ;
581+ if (sd_ndisc_router_get_preference (rt , & pref ) < 0 )
582+ continue ;
583+
584+ if (pref == SD_NDISC_PREFERENCE_HIGH ||
585+ (pref == SD_NDISC_PREFERENCE_MEDIUM && preference == SD_NDISC_PREFERENCE_LOW ))
586+ return false;
587+ }
588+
589+ return true;
567590}
568591
569592static int ndisc_redirect_handler (Link * link , sd_ndisc_redirect * rd ) {
@@ -576,6 +599,15 @@ static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
576599 if (!link -> network -> ndisc_use_redirect )
577600 return 0 ;
578601
602+ usec_t now_usec ;
603+ r = sd_event_now (link -> manager -> event , CLOCK_BOOTTIME , & now_usec );
604+ if (r < 0 )
605+ return r ;
606+
607+ r = ndisc_drop_outdated (link , /* router = */ NULL , now_usec );
608+ if (r < 0 )
609+ return r ;
610+
579611 r = ndisc_redirect_verify_sender (link , rd );
580612 if (r <= 0 )
581613 return r ;
@@ -598,6 +630,14 @@ static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
598630 if (r < 0 )
599631 return r ;
600632
633+ sd_ndisc_router * rt = hashmap_get (link -> ndisc_routers_by_sender , & route -> provider .in6 );
634+ if (!rt )
635+ return - EADDRNOTAVAIL ;
636+
637+ r = sd_ndisc_router_get_lifetime_timestamp (rt , CLOCK_BOOTTIME , & route -> lifetime_usec );
638+ if (r < 0 )
639+ return r ;
640+
601641 return ndisc_request_route (route , link );
602642}
603643
@@ -609,13 +649,10 @@ static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
609649 sd_ndisc_redirect * rd ;
610650 SET_FOREACH (rd , link -> ndisc_redirects ) {
611651 if (router ) {
612- struct in6_addr target ;
613-
614- r = sd_ndisc_redirect_get_target_address (rd , & target );
615- if (r < 0 )
616- return r ;
652+ struct in6_addr a ;
617653
618- if (!in6_addr_equal (& target , router ))
654+ if (!(sd_ndisc_redirect_get_sender_address (rd , & a ) >= 0 && in6_addr_equal (& a , router )) &&
655+ !(sd_ndisc_redirect_get_target_address (rd , & a ) >= 0 && in6_addr_equal (& a , router )))
619656 continue ;
620657 }
621658
@@ -782,124 +819,105 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
782819 return 0 ;
783820}
784821
785- static int update_default_router_address (Link * link , const struct in6_addr * original_address , const struct in6_addr * current_address ) {
786- struct in6_addr a ;
822+ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR (
823+ ndisc_router_hash_ops ,
824+ struct in6_addr ,
825+ in6_addr_hash_func ,
826+ in6_addr_compare_func ,
827+ sd_ndisc_router ,
828+ sd_ndisc_router_unref );
829+
830+ static int ndisc_update_router_address (Link * link , const struct in6_addr * original_address , const struct in6_addr * current_address ) {
831+ _cleanup_ (sd_ndisc_router_unrefp ) sd_ndisc_router * rt = NULL ;
787832 int r ;
788833
789834 assert (link );
790835 assert (original_address );
791836 assert (current_address );
792837
793- if (!link -> ndisc_default_router )
838+ rt = hashmap_remove (link -> ndisc_routers_by_sender , original_address );
839+ if (!rt )
794840 return 0 ;
795841
796- r = sd_ndisc_router_get_sender_address (link -> ndisc_default_router , & a );
797- if (r < 0 )
798- return r ;
799-
800- if (!in6_addr_equal (& a , original_address ))
842+ /* If we already received an RA from the new address, then forget the RA from the old address. */
843+ if (hashmap_contains (link -> ndisc_routers_by_sender , current_address ))
801844 return 0 ;
802845
803- return sd_ndisc_router_set_sender_address (link -> ndisc_default_router , current_address );
804- }
805-
806- static int drop_default_router (Link * link , const struct in6_addr * router , usec_t timestamp_usec ) {
807- usec_t lifetime_usec ;
808- int r ;
809-
810- assert (link );
811-
812- if (!link -> ndisc_default_router )
813- return 0 ;
814-
815- if (router ) {
816- struct in6_addr a ;
817-
818- r = sd_ndisc_router_get_sender_address (link -> ndisc_default_router , & a );
819- if (r < 0 )
820- return r ;
821-
822- if (!in6_addr_equal (& a , router ))
823- return 0 ;
824- }
825-
826- r = sd_ndisc_router_get_lifetime_timestamp (link -> ndisc_default_router , CLOCK_BOOTTIME , & lifetime_usec );
846+ /* Otherwise, update the sender address of the previously received RA. */
847+ r = sd_ndisc_router_set_sender_address (rt , current_address );
827848 if (r < 0 )
828849 return r ;
829850
830- if (lifetime_usec > timestamp_usec )
831- return 0 ;
851+ r = hashmap_put (link -> ndisc_routers_by_sender , & rt -> packet -> sender_address , rt );
852+ if (r < 0 )
853+ return r ;
832854
833- link -> ndisc_default_router = sd_ndisc_router_unref ( link -> ndisc_default_router );
855+ TAKE_PTR ( rt );
834856 return 0 ;
835857}
836858
837- static int accept_default_router ( sd_ndisc_router * new_router , sd_ndisc_router * existing_router ) {
859+ static int ndisc_drop_router_one ( Link * link , sd_ndisc_router * rt , usec_t timestamp_usec ) {
838860 usec_t lifetime_usec ;
839- struct in6_addr a , b ;
840- uint8_t p , q ;
841861 int r ;
842862
843- assert (new_router );
863+ assert (link );
864+ assert (rt );
865+ assert (rt -> packet );
844866
845- r = sd_ndisc_router_get_lifetime ( new_router , & lifetime_usec );
867+ r = sd_ndisc_router_get_lifetime_timestamp ( rt , CLOCK_BOOTTIME , & lifetime_usec );
846868 if (r < 0 )
847869 return r ;
848870
849- if (lifetime_usec == 0 )
850- return false; /* Received a new RA about revoking the router, ignoring. */
851-
852- if (!existing_router )
853- return true;
871+ if (lifetime_usec > timestamp_usec )
872+ return 0 ;
854873
855- /* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
874+ r = ndisc_drop_redirect ( link , & rt -> packet -> sender_address );
856875
857- r = sd_ndisc_router_get_sender_address (new_router , & a );
858- if (r < 0 )
859- return r ;
876+ sd_ndisc_router_unref (hashmap_remove (link -> ndisc_routers_by_sender , & rt -> packet -> sender_address ));
860877
861- r = sd_ndisc_router_get_sender_address (existing_router , & b );
862- if (r < 0 )
863- return r ;
864-
865- if (in6_addr_equal (& a , & b ))
866- return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
878+ return r ;
879+ }
867880
868- r = sd_ndisc_router_get_preference ( new_router , & p );
869- if ( r < 0 )
870- return r ;
881+ static int ndisc_drop_routers ( Link * link , const struct in6_addr * router , usec_t timestamp_usec ) {
882+ sd_ndisc_router * rt ;
883+ int ret = 0 ;
871884
872- r = sd_ndisc_router_get_preference (existing_router , & q );
873- if (r < 0 )
874- return r ;
885+ assert (link );
875886
876- if (p == q )
877- return true;
887+ if (router ) {
888+ rt = hashmap_get (link -> ndisc_routers_by_sender , router );
889+ if (!rt )
890+ return 0 ;
878891
879- if ( p == SD_NDISC_PREFERENCE_HIGH )
880- return true;
892+ return ndisc_drop_router_one ( link , rt , timestamp_usec );
893+ }
881894
882- if ( p == SD_NDISC_PREFERENCE_MEDIUM && q == SD_NDISC_PREFERENCE_LOW )
883- return true ;
895+ HASHMAP_FOREACH_KEY ( rt , router , link -> ndisc_routers_by_sender )
896+ RET_GATHER ( ret , ndisc_drop_router_one ( link , rt , timestamp_usec )) ;
884897
885- return false ;
898+ return ret ;
886899}
887900
888- static int ndisc_remember_default_router (Link * link , sd_ndisc_router * rt ) {
901+ static int ndisc_remember_router (Link * link , sd_ndisc_router * rt ) {
889902 int r ;
890903
891904 assert (link );
892905 assert (rt );
906+ assert (rt -> packet );
907+
908+ sd_ndisc_router_unref (hashmap_remove (link -> ndisc_routers_by_sender , & rt -> packet -> sender_address ));
893909
894- r = accept_default_router (rt , link -> ndisc_default_router );
910+ /* Remember RAs with non-zero lifetime. */
911+ r = sd_ndisc_router_get_lifetime (rt , NULL );
895912 if (r <= 0 )
896913 return r ;
897914
898- sd_ndisc_router_ref ( rt );
899- sd_ndisc_router_unref ( link -> ndisc_default_router );
900- link -> ndisc_default_router = rt ;
915+ r = hashmap_ensure_put ( & link -> ndisc_routers_by_sender , & ndisc_router_hash_ops , & rt -> packet -> sender_address , rt );
916+ if ( r < 0 )
917+ return r ;
901918
902- return 1 ; /* The received router advertisement is from the default router. */
919+ sd_ndisc_router_ref (rt );
920+ return 0 ;
903921}
904922
905923static int ndisc_router_process_reachable_time (Link * link , sd_ndisc_router * rt ) {
@@ -1842,7 +1860,7 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
18421860 * valid lifetimes to improve the reaction of SLAAC to renumbering events.
18431861 * See draft-ietf-6man-slaac-renum-02, section 4.2. */
18441862
1845- r = drop_default_router (link , router , timestamp_usec );
1863+ r = ndisc_drop_routers (link , router , timestamp_usec );
18461864 if (r < 0 )
18471865 RET_GATHER (ret , log_link_warning_errno (link , r , "Failed to drop outdated default router, ignoring: %m" ));
18481866
@@ -1961,6 +1979,16 @@ static int ndisc_setup_expire(Link *link) {
19611979 assert (link );
19621980 assert (link -> manager );
19631981
1982+ sd_ndisc_router * rt ;
1983+ HASHMAP_FOREACH (rt , link -> ndisc_routers_by_sender ) {
1984+ usec_t t ;
1985+
1986+ if (sd_ndisc_router_get_lifetime_timestamp (rt , CLOCK_BOOTTIME , & t ) < 0 )
1987+ continue ;
1988+
1989+ lifetime_usec = MIN (lifetime_usec , t );
1990+ }
1991+
19641992 SET_FOREACH (route , link -> manager -> routes ) {
19651993 if (route -> source != NETWORK_CONFIG_SOURCE_NDISC )
19661994 continue ;
@@ -2096,7 +2124,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
20962124 if (r < 0 )
20972125 return r ;
20982126
2099- r = ndisc_remember_default_router (link , rt );
2127+ r = ndisc_remember_router (link , rt );
21002128 if (r < 0 )
21012129 return r ;
21022130
@@ -2195,7 +2223,7 @@ static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *n
21952223 if (in6_addr_equal (& current_address , & original_address ))
21962224 return 0 ; /* the router address is not changed */
21972225
2198- r = update_default_router_address (link , & original_address , & current_address );
2226+ r = ndisc_update_router_address (link , & original_address , & current_address );
21992227 if (r < 0 )
22002228 return r ;
22012229
@@ -2447,6 +2475,7 @@ void ndisc_flush(Link *link) {
24472475 (void ) ndisc_drop_outdated (link , /* router = */ NULL , /* timestamp_usec = */ USEC_INFINITY );
24482476 (void ) ndisc_drop_redirect (link , /* router = */ NULL );
24492477
2478+ link -> ndisc_routers_by_sender = hashmap_free (link -> ndisc_routers_by_sender );
24502479 link -> ndisc_rdnss = set_free (link -> ndisc_rdnss );
24512480 link -> ndisc_dnssl = set_free (link -> ndisc_dnssl );
24522481 link -> ndisc_captive_portals = set_free (link -> ndisc_captive_portals );
0 commit comments