Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions holo-bgp/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ pub(crate) fn process_tcp_accept(
return Ok(());
};

// Workaround to prevent connection collision until collision resolution
// is implemented.
if nbr.conn_info.is_some() {
return Ok(());
// Flags existence of a collision.
if let Some(local_conn_info) = &nbr.conn_info {
nbr.in_collision = collision_detection(local_conn_info, &conn_info);
}

// Initialize the accepted stream.
Expand Down Expand Up @@ -80,10 +79,9 @@ pub(crate) fn process_tcp_connect(
};
nbr.tasks.connect = None;

// Workaround to prevent connection collision until collision resolution
// is implemented.
if nbr.conn_info.is_some() {
return Ok(());
// Flags existence of a collision.
if let Some(peer_conn_info) = &nbr.conn_info {
nbr.in_collision = collision_detection(&conn_info, peer_conn_info);
}

// Invoke FSM event.
Expand All @@ -92,6 +90,17 @@ pub(crate) fn process_tcp_connect(
Ok(())
}

// ===== Flags for Collision existence ====

fn collision_detection(
local_conn_info: &TcpConnInfo,
peer_conn_info: &TcpConnInfo,
) -> bool {
// Returns true if a collision has happened.
(local_conn_info.local_addr == peer_conn_info.local_addr)
&& (local_conn_info.remote_addr == peer_conn_info.remote_addr)
}

// ===== neighbor message receipt =====

pub(crate) fn process_nbr_msg(
Expand All @@ -117,6 +126,28 @@ pub(crate) fn process_nbr_msg(

match msg {
Message::Open(msg) => {
// Collision Prevention.
let peer_id = nbr.identifier;
let _ = nbr;
let collision_resolved = if nbr.in_collision {
Neighbor::collision_prevention(
peer_id,
msg.identifier,
instance,
neighbors,
)
} else {
true
};
let Some(nbr) = neighbors.get_mut(&nbr_addr) else {
return Ok(());
};

if collision_resolved {
nbr.in_collision = false;
}

// Make sure to clarify we are no longer in collision.
nbr.fsm_event(instance, fsm::Event::RcvdOpen(msg));
}
Message::Update(msg) => {
Expand Down Expand Up @@ -286,10 +317,14 @@ fn process_nbr_reach_prefixes<A>(
}

// Initialize route origin and type.
let Some(identifier) = nbr.identifier else {
return;
};
let origin = RouteOrigin::Neighbor {
identifier: nbr.identifier.unwrap(),
identifier,
remote_addr: nbr.remote_addr,
};

let route_type = match nbr.peer_type {
PeerType::Internal => RouteType::Internal,
PeerType::External => RouteType::External,
Expand Down
67 changes: 67 additions & 0 deletions holo-bgp/src/neighbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub struct Neighbor {
pub tasks: NeighborTasks,
pub update_queues: NeighborUpdateQueues,
pub msg_txp: Option<UnboundedSender<NbrTxMsg>>,
pub in_collision: bool,
}

// BGP peer type.
Expand Down Expand Up @@ -214,9 +215,57 @@ impl Neighbor {
tasks: Default::default(),
update_queues: Default::default(),
msg_txp: None,
in_collision: false,
}
}

/// Carries out the collision prevention procedure.
///
/// # Arguments
/// * `current_peer_id` - peer ID that we have saved locally.
/// * `incoming_peer_id` - peer ID from the incoming OpenMsg.
/// * `instance`
/// * `neighbors` - list of neighbors. Needed when filtering through those with matching characteristics
///
/// * Returns
///
/// Returns true if the collision prevention procedure led to the closure of a connection
/// binded to a neighbor that is not the one sending the OPEN Message.
///
/// Returns false if the collision has not yet been handled, and the session to be
/// closed is the one that has just sent this OPEN message. This will be handled in
/// fsm_event()
pub(crate) fn collision_prevention(
current_peer_id: Option<Ipv4Addr>,
incoming_peer_id: Ipv4Addr,
instance: &mut InstanceUpView<'_>,
neighbors: &mut Neighbors,
) -> bool {
if let (Some(local_id), Some(current_peer_id)) =
(instance.config.identifier, current_peer_id)
&& current_peer_id == incoming_peer_id
&& local_id < current_peer_id
&& let Some((_, nbr)) = neighbors.iter_mut().find(|(_, n)| {
matches!(
n.state,
fsm::State::OpenConfirm | fsm::State::OpenSent
)
})
{
let msg = NotificationMsg::new(
ErrorCode::Cease,
CeaseSubcode::ConnectionCollisionResolution,
);

nbr.in_collision = false;
nbr.session_close(&mut instance.state.rib, instance.tx, Some(msg));
nbr.state = fsm::State::Idle;
return true;
}

false
}

// Injects an event into the neighbor's FSM.
pub(crate) fn fsm_event(
&mut self,
Expand Down Expand Up @@ -381,6 +430,14 @@ impl Neighbor {
Some(fsm::State::Idle)
}
fsm::Event::RcvdOpen(_msg) => {
if self.in_collision {
let error_code = ErrorCode::Cease;
let error_subcode =
CeaseSubcode::ConnectionCollisionResolution;
let msg =
NotificationMsg::new(error_code, error_subcode);
self.session_close(rib, instance.tx, Some(msg));
}
// TODO: collision detection
Some(fsm::State::Idle)
}
Expand Down Expand Up @@ -716,6 +773,16 @@ impl Neighbor {
instance: &mut InstanceUpView<'_>,
msg: OpenMsg,
) -> fsm::State {
// Check for a collision.
if self.in_collision {
let error_code = ErrorCode::Cease;
let error_subcode = CeaseSubcode::ConnectionCollisionResolution;
let msg = NotificationMsg::new(error_code, error_subcode);
let rib = &mut instance.state.rib;
self.session_close(rib, instance.tx, Some(msg));
return fsm::State::Idle;
}

use crate::packet::iana::OpenMessageErrorSubcode as ErrorSubcode;

// Validate the received message.
Expand Down
47 changes: 40 additions & 7 deletions holo-bgp/tests/conformance/topologies/topo1-1/rt1/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@
"ietf-interfaces:interfaces": {
"interface": [
{
"name": "lo",
"name": "eth1",
"type": "iana-if-type:ethernetCsmacd",
"ietf-ip:ipv4": {}
"ietf-ip:ipv4": {},
"ietf-ip:ipv6": {}
},
{
"name": "eth-rt2",
"name": "eth2",
"type": "iana-if-type:ethernetCsmacd",
"ietf-ip:ipv4": {}
"ietf-ip:ipv4": {},
"ietf-ip:ipv6": {}
},
{
"name": "eth-rt3",
"name": "lo",
"type": "iana-if-type:ethernetCsmacd",
"ietf-ip:ipv4": {}
"ietf-ip:ipv4": {},
"ietf-ip:ipv6": {}
}
]
},
Expand All @@ -23,7 +26,7 @@
"control-plane-protocol": [
{
"type": "ietf-bgp:bgp",
"name": "test",
"name": "main",
"ietf-bgp:bgp": {
"global": {
"as": 65100,
Expand All @@ -44,6 +47,20 @@
}
]
}
},
{
"name": "iana-bgp-types:ipv6-unicast",
"enabled": true,
"apply-policy": {
"default-import-policy": "accept-route"
},
"ipv6-unicast": {
"holo-bgp:redistribution": [
{
"type": "ietf-routing:direct"
}
]
}
}
]
}
Expand All @@ -62,6 +79,14 @@
"default-import-policy": "accept-route",
"default-export-policy": "accept-route"
}
},
{
"name": "iana-bgp-types:ipv6-unicast",
"enabled": true,
"apply-policy": {
"default-import-policy": "accept-route",
"default-export-policy": "accept-route"
}
}
]
}
Expand All @@ -78,6 +103,14 @@
"default-import-policy": "accept-route",
"default-export-policy": "accept-route"
}
},
{
"name": "iana-bgp-types:ipv6-unicast",
"enabled": true,
"apply-policy": {
"default-import-policy": "accept-route",
"default-export-policy": "accept-route"
}
}
]
}
Expand Down
Loading