@@ -328,6 +328,7 @@ void route_hash_func(const Route *route, struct siphash *state) {
328328 siphash24_compress (& route -> initrwnd , sizeof (route -> initrwnd ), state );
329329
330330 siphash24_compress (& route -> advmss , sizeof (route -> advmss ), state );
331+ siphash24_compress (& route -> nexthop_id , sizeof (route -> nexthop_id ), state );
331332
332333 break ;
333334 default :
@@ -416,6 +417,10 @@ int route_compare_func(const Route *a, const Route *b) {
416417 if (r != 0 )
417418 return r ;
418419
420+ r = CMP (a -> nexthop_id , b -> nexthop_id );
421+ if (r != 0 )
422+ return r ;
423+
419424 return 0 ;
420425 default :
421426 /* treat any other address family as AF_UNSPEC */
@@ -479,7 +484,7 @@ static int route_get(const Manager *manager, const Link *link, const Route *in,
479484 return - ENOENT ;
480485}
481486
482- static void route_copy (Route * dest , const Route * src , const MultipathRoute * m ) {
487+ static void route_copy (Route * dest , const Route * src , const MultipathRoute * m , const NextHop * nh ) {
483488 assert (dest );
484489 assert (src );
485490
@@ -498,9 +503,14 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
498503 dest -> initcwnd = src -> initcwnd ;
499504 dest -> initrwnd = src -> initrwnd ;
500505 dest -> lifetime = src -> lifetime ;
501- dest -> advmss = src -> advmss ;
506+ dest -> advmss = src -> advmss ;
507+ dest -> nexthop_id = src -> nexthop_id ;
502508
503- if (m ) {
509+ if (nh ) {
510+ dest -> gw_family = nh -> family ;
511+ dest -> gw = nh -> gw ;
512+ dest -> gw_weight = src -> gw_weight ;
513+ } else if (m ) {
504514 dest -> gw_family = m -> gateway .family ;
505515 dest -> gw = m -> gateway .address ;
506516 dest -> gw_weight = m -> weight ;
@@ -523,7 +533,7 @@ static int route_add_internal(Manager *manager, Link *link, Set **routes, const
523533 if (r < 0 )
524534 return r ;
525535
526- route_copy (route , in , NULL );
536+ route_copy (route , in , NULL , NULL );
527537
528538 r = set_ensure_put (routes , & route_hash_ops , route );
529539 if (r < 0 )
@@ -547,7 +557,7 @@ static int route_add_foreign(Manager *manager, Link *link, const Route *in, Rout
547557 return route_add_internal (manager , link , link ? & link -> routes_foreign : & manager -> routes_foreign , in , ret );
548558}
549559
550- static int route_add (Manager * manager , Link * link , const Route * in , const MultipathRoute * m , Route * * ret ) {
560+ static int route_add (Manager * manager , Link * link , const Route * in , const MultipathRoute * m , const NextHop * nh , Route * * ret ) {
551561 _cleanup_ (route_freep ) Route * tmp = NULL ;
552562 bool is_new = false;
553563 Route * route ;
@@ -556,14 +566,21 @@ static int route_add(Manager *manager, Link *link, const Route *in, const Multip
556566 assert (manager || link );
557567 assert (in );
558568
559- if (m ) {
569+ if (nh ) {
570+ r = route_new (& tmp );
571+ if (r < 0 )
572+ return r ;
573+
574+ route_copy (tmp , in , NULL , nh );
575+ in = tmp ;
576+ } else if (m ) {
560577 assert (link && (m -> ifindex == 0 || m -> ifindex == link -> ifindex ));
561578
562579 r = route_new (& tmp );
563580 if (r < 0 )
564581 return r ;
565582
566- route_copy (tmp , in , m );
583+ route_copy (tmp , in , m , NULL );
567584 in = tmp ;
568585 }
569586
@@ -722,14 +739,20 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
722739 if (r < 0 )
723740 return log_link_error_errno (link , r , "Could not set route type: %m" );
724741
725- if (!route_type_is_reject (route )) {
742+ if (!route_type_is_reject (route ) && route -> nexthop_id == 0 ) {
726743 assert (link ); /* Those routes must be attached to a specific link */
727744
728745 r = sd_netlink_message_append_u32 (req , RTA_OIF , link -> ifindex );
729746 if (r < 0 )
730747 return log_link_error_errno (link , r , "Could not append RTA_OIF attribute: %m" );
731748 }
732749
750+ if (route -> nexthop_id > 0 ) {
751+ r = sd_netlink_message_append_u32 (req , RTA_NH_ID , route -> nexthop_id );
752+ if (r < 0 )
753+ return log_link_error_errno (link , r , "Could not append RTA_NH_ID attribute: %m" );
754+ }
755+
733756 r = sd_netlink_message_append_u8 (req , RTA_PREF , route -> pref );
734757 if (r < 0 )
735758 return log_link_error_errno (link , r , "Could not append RTA_PREF attribute: %m" );
@@ -893,7 +916,7 @@ int link_drop_foreign_routes(Link *link) {
893916 continue ;
894917
895918 if (link_has_route (link , route ))
896- k = route_add (NULL , link , route , NULL , NULL );
919+ k = route_add (NULL , link , route , NULL , NULL , NULL );
897920 else
898921 k = route_remove (route , NULL , link , NULL );
899922 if (k < 0 && r >= 0 )
@@ -947,24 +970,28 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
947970
948971static int route_add_and_setup_timer (Link * link , const Route * route , const MultipathRoute * m , Route * * ret ) {
949972 _cleanup_ (sd_event_source_unrefp ) sd_event_source * expire = NULL ;
973+ NextHop * nh = NULL ;
950974 Route * nr ;
951975 int r , k ;
952976
953977 assert (link );
978+ assert (link -> manager );
954979 assert (route );
955980
981+ (void ) manager_get_nexthop_by_id (link -> manager , route -> nexthop_id , & nh );
982+
956983 if (route_type_is_reject (route ))
957- k = route_add (link -> manager , NULL , route , NULL , & nr );
984+ k = route_add (link -> manager , NULL , route , NULL , NULL , & nr );
958985 else if (!m || m -> ifindex == 0 || m -> ifindex == link -> ifindex )
959- k = route_add (NULL , link , route , m , & nr );
986+ k = route_add (NULL , link , route , m , nh , & nr );
960987 else {
961988 Link * link_gw ;
962989
963990 r = link_get (link -> manager , m -> ifindex , & link_gw );
964991 if (r < 0 )
965992 return log_link_error_errno (link , r , "Failed to get link with ifindex %d: %m" , m -> ifindex );
966993
967- k = route_add (NULL , link_gw , route , m , & nr );
994+ k = route_add (NULL , link_gw , route , m , NULL , & nr );
968995 }
969996 if (k < 0 )
970997 return log_link_error_errno (link , k , "Could not add route: %m" );
@@ -1258,6 +1285,9 @@ static bool route_has_gateway(const Route *route) {
12581285 if (!ordered_set_isempty (route -> multipath_routes ))
12591286 return true;
12601287
1288+ if (route -> nexthop_id > 0 )
1289+ return true;
1290+
12611291 return false;
12621292}
12631293
@@ -1356,13 +1386,30 @@ int link_set_routes(Link *link) {
13561386static int process_route_one (Manager * manager , Link * link , uint16_t type , const Route * tmp , const MultipathRoute * m ) {
13571387 _cleanup_ (route_freep ) Route * nr = NULL ;
13581388 Route * route = NULL ;
1389+ NextHop * nh = NULL ;
13591390 int r ;
13601391
13611392 assert (manager );
13621393 assert (tmp );
13631394 assert (IN_SET (type , RTM_NEWROUTE , RTM_DELROUTE ));
13641395
1365- if (m ) {
1396+ (void ) manager_get_nexthop_by_id (manager , tmp -> nexthop_id , & nh );
1397+
1398+ if (nh ) {
1399+ if (link && link != nh -> link )
1400+ return log_link_warning_errno (link , SYNTHETIC_ERRNO (EINVAL ),
1401+ "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring." );
1402+
1403+ link = nh -> link ;
1404+
1405+ r = route_new (& nr );
1406+ if (r < 0 )
1407+ return log_oom ();
1408+
1409+ route_copy (nr , tmp , NULL , nh );
1410+
1411+ tmp = nr ;
1412+ } else if (m ) {
13661413 if (link )
13671414 return log_link_warning_errno (link , SYNTHETIC_ERRNO (EINVAL ),
13681415 "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring." );
@@ -1381,7 +1428,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const
13811428 if (r < 0 )
13821429 return log_oom ();
13831430
1384- route_copy (nr , tmp , m );
1431+ route_copy (nr , tmp , m , NULL );
13851432
13861433 tmp = nr ;
13871434 }
@@ -1573,6 +1620,12 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
15731620 return 0 ;
15741621 }
15751622
1623+ r = sd_netlink_message_read_u32 (message , RTA_NH_ID , & tmp -> nexthop_id );
1624+ if (r < 0 && r != - ENODATA ) {
1625+ log_link_warning_errno (link , r , "rtnl: received route message with invalid nexthop id, ignoring: %m" );
1626+ return 0 ;
1627+ }
1628+
15761629 r = sd_netlink_message_enter_container (message , RTA_METRICS );
15771630 if (r < 0 && r != - ENODATA ) {
15781631 log_link_error_errno (link , r , "rtnl: Could not enter RTA_METRICS container: %m" );
@@ -1966,6 +2019,59 @@ int config_parse_route_scope(
19662019 return 0 ;
19672020}
19682021
2022+ int config_parse_route_nexthop (
2023+ const char * unit ,
2024+ const char * filename ,
2025+ unsigned line ,
2026+ const char * section ,
2027+ unsigned section_line ,
2028+ const char * lvalue ,
2029+ int ltype ,
2030+ const char * rvalue ,
2031+ void * data ,
2032+ void * userdata ) {
2033+
2034+ Network * network = userdata ;
2035+ _cleanup_ (route_free_or_set_invalidp ) Route * n = NULL ;
2036+ uint32_t id ;
2037+ int r ;
2038+
2039+ assert (filename );
2040+ assert (section );
2041+ assert (lvalue );
2042+ assert (rvalue );
2043+ assert (data );
2044+
2045+ r = route_new_static (network , filename , section_line , & n );
2046+ if (r == - ENOMEM )
2047+ return log_oom ();
2048+ if (r < 0 ) {
2049+ log_syntax (unit , LOG_WARNING , filename , line , r ,
2050+ "Failed to allocate route, ignoring assignment: %m" );
2051+ return 0 ;
2052+ }
2053+
2054+ if (isempty (rvalue )) {
2055+ n -> nexthop_id = 0 ;
2056+ TAKE_PTR (n );
2057+ return 0 ;
2058+ }
2059+
2060+ r = safe_atou32 (rvalue , & id );
2061+ if (r < 0 ) {
2062+ log_syntax (unit , LOG_WARNING , filename , line , r , "Failed to parse nexthop ID, ignoring assignment: %s" , rvalue );
2063+ return 0 ;
2064+ }
2065+ if (id == 0 ) {
2066+ log_syntax (unit , LOG_WARNING , filename , line , 0 , "Invalid nexthop ID, ignoring assignment: %s" , rvalue );
2067+ return 0 ;
2068+ }
2069+
2070+ n -> nexthop_id = id ;
2071+ TAKE_PTR (n );
2072+ return 0 ;
2073+ }
2074+
19692075int config_parse_route_table (
19702076 const char * unit ,
19712077 const char * filename ,
@@ -2656,6 +2762,14 @@ static int route_section_verify(Route *route, Network *network) {
26562762 route -> section -> filename , route -> section -> line );
26572763 }
26582764
2765+ if (route -> nexthop_id > 0 &&
2766+ (in_addr_is_set (route -> gw_family , & route -> gw ) ||
2767+ !ordered_set_isempty (route -> multipath_routes )))
2768+ return log_warning_errno (SYNTHETIC_ERRNO (EINVAL ),
2769+ "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
2770+ "Ignoring [Route] section from line %u." ,
2771+ route -> section -> filename , route -> section -> line );
2772+
26592773 return 0 ;
26602774}
26612775
0 commit comments