Skip to content

Commit

Permalink
bgp: implement the "clear" action for neighbors
Browse files Browse the repository at this point in the history
Signed-off-by: Renato Westphal <[email protected]>
  • Loading branch information
rwestphal committed May 2, 2024
1 parent f27bbaf commit d095a53
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 76 deletions.
69 changes: 4 additions & 65 deletions holo-bgp/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,10 @@ fn process_nbr_route_refresh(

match (afi, safi) {
(Afi::Ipv4, Safi::Unicast) => {
process_nbr_route_refresh_af::<Ipv4Unicast>(instance, nbr)
nbr.resend_adj_rib_out::<Ipv4Unicast>(instance);
}
(Afi::Ipv6, Safi::Unicast) => {
process_nbr_route_refresh_af::<Ipv6Unicast>(instance, nbr)
nbr.resend_adj_rib_out::<Ipv6Unicast>(instance);
}
_ => {
// Ignore unsupported AFI/SAFI combination.
Expand All @@ -417,36 +417,6 @@ fn process_nbr_route_refresh(
Ok(())
}

fn process_nbr_route_refresh_af<A>(
instance: &mut InstanceUpView<'_>,
nbr: &mut Neighbor,
) where
A: AddressFamily,
{
let table = A::table(&mut instance.state.rib.tables);
for (prefix, dest) in &table.prefixes {
let Some(adj_rib) = dest.adj_rib.get(&nbr.remote_addr) else {
continue;
};
let Some(route) = adj_rib.out_post() else {
continue;
};

// Update route's attributes before transmission.
let mut attrs = route.attrs.get();
attrs_tx_update::<A>(
nbr,
instance.config.asn,
route.origin.is_local(),
&mut attrs,
);

// Update neighbor's Tx queue.
let update_queue = A::update_queue(&mut nbr.update_queues);
update_queue.reach.entry(attrs).or_default().insert(*prefix);
}
}

// ===== neighbor expired timeout =====

pub(crate) fn process_nbr_timer(
Expand Down Expand Up @@ -592,11 +562,11 @@ where

// Update route's attributes before transmission.
let mut attrs = rpinfo.attrs;
attrs_tx_update::<A>(
rib::attrs_tx_update::<A>(
&mut attrs,
nbr,
instance.config.asn,
rpinfo.origin.is_local(),
&mut attrs,
);

// Update neighbor's Tx queue.
Expand All @@ -623,37 +593,6 @@ where
Ok(())
}

fn attrs_tx_update<A>(
nbr: &Neighbor,
local_asn: u32,
local: bool,
attrs: &mut Attrs,
) where
A: AddressFamily,
{
match nbr.peer_type {
PeerType::Internal => {
// Attach LOCAL_PREF with default value if it's missing.
if attrs.base.local_pref.is_none() {
attrs.base.local_pref = Some(rib::DFLT_LOCAL_PREF);
}
}
PeerType::External => {
// Prepend local AS number.
attrs.base.as_path.prepend(local_asn);

// Do not propagate the MULTI_EXIT_DISC attribute.
attrs.base.med = None;

// Remove the LOCAL_PREF attribute.
attrs.base.local_pref = None;
}
}

// Update the next-hop attribute based on the address family if necessary.
A::nexthop_tx_change(nbr, local, &mut attrs.base);
}

// ===== redistribute policy import result =====

pub(crate) fn process_redistribute_policy_import<A>(
Expand Down
95 changes: 92 additions & 3 deletions holo-bgp/src/neighbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use holo_utils::ibus::IbusSender;
use holo_utils::socket::{TcpConnInfo, TcpStream, TTL_MAX};
use holo_utils::task::{IntervalTask, Task, TimeoutTask};
use holo_utils::{Sender, UnboundedSender};
use num_traits::FromPrimitive;
use num_traits::{FromPrimitive, ToPrimitive};
use tokio::sync::mpsc;

use crate::af::{AddressFamily, Ipv4Unicast, Ipv6Unicast};
Expand All @@ -26,13 +26,14 @@ use crate::error::Error;
use crate::instance::{Instance, InstanceUpView};
use crate::northbound::configuration::{InstanceCfg, NeighborCfg};
use crate::northbound::notification;
use crate::northbound::rpc::ClearType;
use crate::packet::attribute::Attrs;
use crate::packet::consts::{
Afi, ErrorCode, FsmErrorSubcode, Safi, AS_TRANS, BGP_VERSION,
Afi, CeaseSubcode, ErrorCode, FsmErrorSubcode, Safi, AS_TRANS, BGP_VERSION,
};
use crate::packet::message::{
Capability, DecodeCxt, EncodeCxt, KeepaliveMsg, Message,
NegotiatedCapability, NotificationMsg, OpenMsg,
NegotiatedCapability, NotificationMsg, OpenMsg, RouteRefreshMsg,
};
use crate::rib::{Rib, Route, RouteOrigin};
use crate::tasks::messages::input::{NbrRxMsg, NbrTimerMsg, TcpConnectMsg};
Expand Down Expand Up @@ -929,6 +930,37 @@ impl Neighbor {
);
}

// Re-send the current Adj-RIB-Out.
pub(crate) fn resend_adj_rib_out<A>(
&mut self,
instance: &mut InstanceUpView<'_>,
) where
A: AddressFamily,
{
let table = A::table(&mut instance.state.rib.tables);
for (prefix, dest) in &table.prefixes {
let Some(adj_rib) = dest.adj_rib.get(&self.remote_addr) else {
continue;
};
let Some(route) = adj_rib.out_post() else {
continue;
};

// Update route's attributes before transmission.
let mut attrs = route.attrs.get();
rib::attrs_tx_update::<A>(
&mut attrs,
self,
instance.config.asn,
route.origin.is_local(),
);

// Update neighbor's Tx queue.
let update_queue = A::update_queue(&mut self.update_queues);
update_queue.reach.entry(attrs).or_default().insert(*prefix);
}
}

// Clears the Adj-RIB-In and Adj-RIB-Out for the given address family.
fn clear_routes<A>(&mut self, rib: &mut Rib, ibus_tx: &IbusSender)
where
Expand Down Expand Up @@ -959,6 +991,63 @@ impl Neighbor {
}
}

// Clears the neighbor session.
pub(crate) fn clear_session(
&mut self,
instance: &mut InstanceUpView<'_>,
clear_type: ClearType,
) {
match clear_type {
ClearType::Admin => {
// Close the session with the "Administrative Reset" subcode.
let msg = NotificationMsg::new(
ErrorCode::Cease,
CeaseSubcode::AdministrativeReset,
);
self.fsm_event(instance, fsm::Event::Stop(Some(msg)));
}
ClearType::Hard => {
// Close the session with the "Hard Reset" subcode.
let msg = NotificationMsg::new(
ErrorCode::Cease,
CeaseSubcode::HardReset,
);
self.fsm_event(instance, fsm::Event::Stop(Some(msg)));
}
ClearType::Soft => {
// Re-send the current Adj-RIB-Out to this neighbor.
self.resend_adj_rib_out::<Ipv4Unicast>(instance);
self.resend_adj_rib_out::<Ipv6Unicast>(instance);
let msg_list = self.update_queues.build_updates();
if !msg_list.is_empty() {
self.message_list_send(msg_list);
}
}
ClearType::SoftInbound => {
// Request the Adj-RIB-In for this neighbor to be re-sent.
for (afi, safi) in self
.capabilities_nego
.iter()
.filter_map(|cap| {
if let NegotiatedCapability::MultiProtocol {
afi,
safi,
} = cap
{
Some((afi.to_u16().unwrap(), safi.to_u8().unwrap()))
} else {
None
}
})
.collect::<Vec<_>>()
{
let msg = RouteRefreshMsg { afi, safi };
self.message_send(Message::RouteRefresh(msg));
}
}
}
}

// Determines whether the given route is eligible for distribution.
pub(crate) fn distribute_filter(&self, route: &Route) -> bool {
// Suppress advertisements to peers if their AS number is present
Expand Down
50 changes: 46 additions & 4 deletions holo-bgp/src/northbound/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,60 @@
use std::sync::LazyLock as Lazy;

use holo_northbound::rpc::{Callbacks, CallbacksBuilder, Provider};
use holo_northbound::yang::control_plane_protocol::bgp;
use holo_utils::yang::DataNodeRefExt;
use yang2::data::Data;

//use holo_utils::yang::DataNodeRefExt;
//use yang2::data::Data;
use crate::instance::Instance;

pub static CALLBACKS: Lazy<Callbacks<Instance>> = Lazy::new(load_callbacks);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ClearType {
Admin,
Hard,
Soft,
SoftInbound,
}

// ===== callbacks =====

fn load_callbacks() -> Callbacks<Instance> {
// TODO: YANG actions are not supported yet.
CallbacksBuilder::<Instance>::default().build()
CallbacksBuilder::<Instance>::default()
.path(bgp::neighbors::clear::PATH)
.rpc(|instance, args| {
Box::pin(async move {
let rpc = args.data.find_path(args.rpc_path).unwrap();

// Parse input parameters.
let remote_addr = rpc.get_ip_relative("./remote-addr");
let clear_type = if rpc.exists("./hard") {
ClearType::Hard
} else if rpc.exists("./soft") {
ClearType::Soft
} else if rpc.exists("./soft-inbound") {
ClearType::SoftInbound
} else {
ClearType::Admin
};

// Clear peers.
let Some((mut instance, neighbors)) = instance.as_up() else {
return Ok(());
};
if let Some(remote_addr) = remote_addr {
let nbr = neighbors.get_mut(&remote_addr).unwrap();
nbr.clear_session(&mut instance, clear_type);
} else {
for nbr in neighbors.values_mut() {
nbr.clear_session(&mut instance, clear_type);
}
}

Ok(())
})
})
.build()
}

// ===== impl Instance =====
Expand Down
32 changes: 32 additions & 0 deletions holo-bgp/src/rib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize};

use crate::af::{AddressFamily, Ipv4Unicast, Ipv6Unicast};
use crate::debug::Debug;
use crate::neighbor::{Neighbor, PeerType};
use crate::northbound::configuration::{
DistanceCfg, MultipathCfg, RouteSelectionCfg,
};
Expand Down Expand Up @@ -819,6 +820,37 @@ pub(crate) fn loc_rib_update<A>(
}
}

pub(crate) fn attrs_tx_update<A>(
attrs: &mut Attrs,
nbr: &Neighbor,
local_asn: u32,
local: bool,
) where
A: AddressFamily,
{
match nbr.peer_type {
PeerType::Internal => {
// Attach LOCAL_PREF with default value if it's missing.
if attrs.base.local_pref.is_none() {
attrs.base.local_pref = Some(DFLT_LOCAL_PREF);
}
}
PeerType::External => {
// Prepend local AS number.
attrs.base.as_path.prepend(local_asn);

// Do not propagate the MULTI_EXIT_DISC attribute.
attrs.base.med = None;

// Remove the LOCAL_PREF attribute.
attrs.base.local_pref = None;
}
}

// Update the next-hop attribute based on the address family if necessary.
A::nexthop_tx_change(nbr, local, &mut attrs.base);
}

pub(crate) fn nexthop_track<A>(
nht: &mut HashMap<IpAddr, NhtEntry<A>>,
prefix: A::IpNetwork,
Expand Down
7 changes: 6 additions & 1 deletion holo-routing/src/northbound/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//

use holo_northbound::rpc::Provider;
use holo_northbound::yang::control_plane_protocol;
use holo_northbound::{CallbackKey, NbDaemonSender};
use holo_utils::protocol::Protocol;
use holo_utils::yang::DataNodeRefExt;
Expand Down Expand Up @@ -69,7 +70,11 @@ fn find_instance(
) -> Result<(Protocol, Option<String>), String> {
let (protocol, name) = match rpc.schema().module().name() {
"ietf-bgp" => {
todo!()
let protocol = Protocol::BGP;
let name = rpc.get_string_relative(
control_plane_protocol::name::PATH.as_ref(),
);
(protocol, name)
}
"ietf-mpls-ldp" => {
let protocol = Protocol::LDP;
Expand Down
1 change: 1 addition & 0 deletions holo-tools/src/yang_callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ fn main() {

// Load base YANG modules that define features used by other modules.
yang::load_module(&mut yang_ctx, "ietf-bfd-types");
yang::load_module(&mut yang_ctx, "iana-bgp-types");

// Load provided YANG module.
yang::load_module(&mut yang_ctx, module_name);
Expand Down
6 changes: 5 additions & 1 deletion holo-tools/src/yang_deviations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ fn main() {
// Initialize YANG context.
let mut yang_ctx = yang::new_context();

// Load YANG module.
// Load base YANG modules that define features used by other modules.
yang::load_module(&mut yang_ctx, "ietf-bfd-types");
yang::load_module(&mut yang_ctx, "iana-bgp-types");

// Load requested YANG module.
yang::load_module(&mut yang_ctx, module_name);
let module = yang_ctx
.get_module_latest(module_name)
Expand Down
Loading

0 comments on commit d095a53

Please sign in to comment.