@@ -10,7 +10,7 @@ use std::net::{IpAddr, Ipv4Addr};
1010use std:: sync:: Arc ;
1111use std:: time:: Instant ;
1212
13- use holo_utils:: bgp:: RouteType ;
13+ use holo_utils:: bgp:: { RoleName , RouteType } ;
1414use holo_utils:: ibus:: IbusChannelsTx ;
1515use holo_utils:: protocol:: Protocol ;
1616use prefix_trie:: map:: PrefixMap ;
@@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize};
1919use crate :: af:: { AddressFamily , Ipv4Unicast , Ipv6Unicast } ;
2020use crate :: debug:: Debug ;
2121use crate :: ibus;
22- use crate :: neighbor:: { Neighbor , PeerType } ;
22+ use crate :: neighbor:: { Neighbor , Neighbors , PeerType } ;
2323use crate :: northbound:: configuration:: {
2424 DistanceCfg , InstanceTraceOptions , MultipathCfg , RouteSelectionCfg ,
2525} ;
@@ -145,6 +145,7 @@ pub enum RouteIneligibleReason {
145145 Originator ,
146146 Confed ,
147147 Unresolvable ,
148+ Role ,
148149}
149150
150151#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
@@ -689,6 +690,7 @@ where
689690pub ( crate ) fn best_path < A > (
690691 dest : & mut Destination ,
691692 local_asn : u32 ,
693+ neighbors : & Neighbors ,
692694 nht : & HashMap < IpAddr , NhtEntry < A > > ,
693695 selection_cfg : & RouteSelectionCfg ,
694696) -> Option < Box < Route > >
@@ -698,7 +700,7 @@ where
698700 let mut best_route = None ;
699701
700702 // Iterate over each Adj-RIB-In route for the destination.
701- for route in dest
703+ ' rib_loop : for route in dest
702704 . adj_rib
703705 . values_mut ( )
704706 // Pick the post-policy routes.
@@ -712,7 +714,37 @@ where
712714 // First, check if the route is eligible.
713715 if route. attrs . base . value . as_path . contains ( local_asn) {
714716 route. ineligible_reason = Some ( RouteIneligibleReason :: AsLoop ) ;
715- continue ;
717+ continue ' rib_loop;
718+ }
719+
720+ // Check for RoleName and OTC validity.
721+ // RFC 9234 Section 5.
722+ //
723+ // 1.
724+ // If a route with the OTC Attribute is received from a Customer or an
725+ // RS-Client, then it is a route leak and be considered ineligible.
726+ //
727+ // 2.
728+ // If a route with the OTC Attribute is received from a Peer
729+ // (i.e., remote AS with a Peer Role) and the Attribute has
730+ // a value that is not equal to the remote (i.e., Peer's) AS number,
731+ // then it is a route leak and be considered ineligible.
732+ if let RouteOrigin :: Neighbor { remote_addr, .. } = route. origin
733+ && let Some ( nbr) = neighbors. get ( & remote_addr)
734+ && let Some ( role) = nbr. remote_role
735+ {
736+ if let Some ( otc) = route. attrs . base . value . otc {
737+ let is_leak = match role {
738+ RoleName :: Customer | RoleName :: RsClient => true ,
739+ RoleName :: Peer if otc != nbr. config . peer_as => true ,
740+ _ => false ,
741+ } ;
742+
743+ if is_leak {
744+ route. ineligible_reason = Some ( RouteIneligibleReason :: Role ) ;
745+ continue ' rib_loop;
746+ }
747+ }
716748 }
717749
718750 // Get interior cost to the route's nexthop.
@@ -722,7 +754,7 @@ where
722754 if route. igp_cost . is_none ( ) {
723755 route. ineligible_reason =
724756 Some ( RouteIneligibleReason :: Unresolvable ) ;
725- continue ;
757+ continue ' rib_loop ;
726758 } ;
727759 }
728760
@@ -851,6 +883,22 @@ pub(crate) fn attrs_tx_update<A>(
851883
852884 // Remove the LOCAL_PREF attribute.
853885 attrs. base . local_pref = None ;
886+
887+ // RFC 9234 - Section 5:
888+ // If a route is to be advertised to a Customer, a Peer, or an
889+ // RS-Client (when the sender is an RS)
890+ // and the OTC Attribute is not present, then when advertising the
891+ // route, an OTC Attribute
892+ // be added with a value equal to the AS number of the local AS
893+ if let Some ( remote_role) = nbr. remote_role
894+ && matches ! (
895+ remote_role,
896+ RoleName :: Customer | RoleName :: Peer | RoleName :: Rs
897+ )
898+ && attrs. base . otc . is_none ( )
899+ {
900+ attrs. base . otc = Some ( local_asn) ;
901+ }
854902 }
855903 }
856904
0 commit comments