From 03b0dcff0bd2cb74e9cc916720eacc3ef090abb1 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Mon, 4 Dec 2023 15:38:40 -0300 Subject: [PATCH] BGP WIP implementation Signed-off-by: Renato Westphal --- Cargo.toml | 2 + README.md | 26 + holo-bgp/Cargo.toml | 54 + holo-bgp/LICENSE | 19 + holo-bgp/benches/msg_decoding.rs | 37 + holo-bgp/benches/msg_encoding.rs | 52 + holo-bgp/src/debug.rs | 147 + holo-bgp/src/error.rs | 220 + holo-bgp/src/events.rs | 388 ++ holo-bgp/src/instance.rs | 454 ++ holo-bgp/src/lib.rs | 24 + holo-bgp/src/neighbor.rs | 818 +++ holo-bgp/src/network.rs | 296 + holo-bgp/src/northbound/configuration.rs | 1501 +++++ holo-bgp/src/northbound/mod.rs | 36 + holo-bgp/src/northbound/rpc.rs | 30 + holo-bgp/src/northbound/state.rs | 1797 ++++++ holo-bgp/src/northbound/yang.rs | 330 ++ holo-bgp/src/packet/attribute.rs | 1323 +++++ holo-bgp/src/packet/consts.rs | 338 ++ holo-bgp/src/packet/error.rs | 166 + holo-bgp/src/packet/message.rs | 999 ++++ holo-bgp/src/packet/mod.rs | 10 + holo-bgp/src/policy.rs | 361 ++ holo-bgp/src/rib.rs | 162 + holo-bgp/src/southbound/mod.rs | 8 + holo-bgp/src/southbound/rx.rs | 19 + holo-bgp/src/southbound/tx.rs | 5 + holo-bgp/src/tasks.rs | 447 ++ holo-bgp/tests/mod.rs | 9 + holo-bgp/tests/packet/keepalive.rs | 33 + holo-bgp/tests/packet/mod.rs | 40 + holo-bgp/tests/packet/notification.rs | 39 + holo-bgp/tests/packet/open.rs | 122 + holo-bgp/tests/packet/route_refresh.rs | 38 + holo-bgp/tests/packet/update.rs | 167 + holo-daemon/Cargo.toml | 4 +- holo-daemon/src/northbound/yang.rs | 14 +- holo-policy/src/northbound/configuration.rs | 534 +- holo-policy/src/northbound/mod.rs | 2 +- holo-policy/src/northbound/state.rs | 95 + holo-routing/Cargo.toml | 1 + holo-routing/src/northbound/configuration.rs | 13 + holo-routing/src/northbound/rpc.rs | 4 + holo-routing/src/northbound/state.rs | 1 + holo-tools/yang-coverage.sh | 2 + holo-utils/Cargo.toml | 1 + holo-utils/src/bgp.rs | 177 + holo-utils/src/lib.rs | 1 + holo-utils/src/policy.rs | 371 +- holo-utils/src/protocol.rs | 5 + holo-yang/modules/augmentations/holo-bgp.yang | 78 + .../deviations/ietf-bgp-holo-deviations.yang | 4832 +++++++++++++++++ holo-yang/src/lib.rs | 14 + 54 files changed, 16608 insertions(+), 58 deletions(-) create mode 100644 holo-bgp/Cargo.toml create mode 100644 holo-bgp/LICENSE create mode 100644 holo-bgp/benches/msg_decoding.rs create mode 100644 holo-bgp/benches/msg_encoding.rs create mode 100644 holo-bgp/src/debug.rs create mode 100644 holo-bgp/src/error.rs create mode 100644 holo-bgp/src/events.rs create mode 100644 holo-bgp/src/instance.rs create mode 100644 holo-bgp/src/lib.rs create mode 100644 holo-bgp/src/neighbor.rs create mode 100644 holo-bgp/src/network.rs create mode 100644 holo-bgp/src/northbound/configuration.rs create mode 100644 holo-bgp/src/northbound/mod.rs create mode 100644 holo-bgp/src/northbound/rpc.rs create mode 100644 holo-bgp/src/northbound/state.rs create mode 100644 holo-bgp/src/northbound/yang.rs create mode 100644 holo-bgp/src/packet/attribute.rs create mode 100644 holo-bgp/src/packet/consts.rs create mode 100644 holo-bgp/src/packet/error.rs create mode 100644 holo-bgp/src/packet/message.rs create mode 100644 holo-bgp/src/packet/mod.rs create mode 100644 holo-bgp/src/policy.rs create mode 100644 holo-bgp/src/rib.rs create mode 100644 holo-bgp/src/southbound/mod.rs create mode 100644 holo-bgp/src/southbound/rx.rs create mode 100644 holo-bgp/src/southbound/tx.rs create mode 100644 holo-bgp/src/tasks.rs create mode 100644 holo-bgp/tests/mod.rs create mode 100644 holo-bgp/tests/packet/keepalive.rs create mode 100644 holo-bgp/tests/packet/mod.rs create mode 100644 holo-bgp/tests/packet/notification.rs create mode 100644 holo-bgp/tests/packet/open.rs create mode 100644 holo-bgp/tests/packet/route_refresh.rs create mode 100644 holo-bgp/tests/packet/update.rs create mode 100644 holo-utils/src/bgp.rs create mode 100644 holo-yang/modules/augmentations/holo-bgp.yang create mode 100644 holo-yang/modules/deviations/ietf-bgp-holo-deviations.yang diff --git a/Cargo.toml b/Cargo.toml index b82ae945..01a15879 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "holo-bfd", + "holo-bgp", "holo-cli", "holo-daemon", "holo-interface", @@ -35,6 +36,7 @@ check_keyword = "0.2" clap = "2.33" chrono = { version = "0.4", features = ["serde"] } criterion = "0.4" +crossbeam-channel = "0.5" derive-new = "0.5" enum-as-inner = "0.6" futures = "0.3" diff --git a/README.md b/README.md index 745a1169..748e75cd 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,30 @@ Holo supports the following IETF RFCs and Internet drafts: * RFC 5882 - Generic Application of Bidirectional Forwarding Detection (BFD) * RFC 5883 - Bidirectional Forwarding Detection (BFD) for Multihop Paths +##### BGP + +* RFC 1997 - BGP Communities Attribute +* RFC 2385 - Protection of BGP Sessions via the TCP MD5 Signature Option +* RFC 2545 - Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing +* RFC 2918 - Route Refresh Capability for BGP-4 +* RFC 4271 - A Border Gateway Protocol 4 (BGP-4) +* RFC 4360 - BGP Extended Communities Attribute +* RFC 4486 - Subcodes for BGP Cease Notification Message +* RFC 4760 - Multiprotocol Extensions for BGP-4 +* RFC 5082 - The Generalized TTL Security Mechanism (GTSM) +* RFC 5492 - Capabilities Advertisement with BGP-4 +* RFC 5668 - 4-Octet AS Specific BGP Extended Community +* RFC 5701 - IPv6 Address Specific BGP Extended Community Attribute +* RFC 6286 - Autonomous-System-Wide Unique BGP Identifier for BGP-4 +* RFC 6608 - Subcodes for BGP Finite State Machine Error +* RFC 6793 - BGP Support for Four-Octet Autonomous System (AS) Number Space +* RFC 7606 - Revised Error Handling for BGP UPDATE Messages +* RFC 7607 - Codification of AS 0 Processing +* RFC 8092 - BGP Large Communities Attribute +* RFC 8212 - Default External BGP (EBGP) Route Propagation Behavior without Policies +* RFC 8642 - Policy Behavior for Well-Known BGP Communities +* RFC 9072 - Extended Optional Parameters Length for BGP OPEN Message + ##### MPLS LDP * RFC 5036 - LDP Specification @@ -192,6 +216,8 @@ Holo supports the following IETF RFCs and Internet drafts: | ietf-bfd-ip-mh@2022-09-22 | 100.00% | 100.00% | - | 100.00% | [100.00%](http://westphal.com.br/holo/ietf-bfd-ip-mh.html) | | ietf-bfd-ip-sh@2022-09-22 | 100.00% | 100.00% | - | 100.00% | [100.00%](http://westphal.com.br/holo/ietf-bfd-ip-sh.html) | | ietf-bfd@2022-09-22 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-bfd.html) | +| ietf-bgp-policy@2023-07-05 | 100.00% | - | - | - | [100.00%](http://westphal.com.br/holo/ietf-bgp-policy.html) | +| ietf-bgp@2023-07-05 | 39.43% | 93.33% | - | - | [67.62%](http://westphal.com.br/holo/ietf-bgp.html) | | ietf-interfaces@2018-01-09 | 100.00% | 0.00% | - | - | [22.22%](http://westphal.com.br/holo/ietf-interfaces.html) | | ietf-ip@2018-01-09 | 17.39% | 0.00% | - | - | [13.33%](http://westphal.com.br/holo/ietf-ip.html) | | ietf-ipv4-unicast-routing@2018-03-13 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-ipv4-unicast-routing.html) | diff --git a/holo-bgp/Cargo.toml b/holo-bgp/Cargo.toml new file mode 100644 index 00000000..566c34f7 --- /dev/null +++ b/holo-bgp/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "holo-bgp" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +async-trait.workspace = true +bitflags.workspace = true +bytes.workspace = true +chrono.workspace = true +crossbeam-channel.workspace = true +derive-new.workspace = true +enum-as-inner.workspace = true +generational-arena.workspace = true +ipnetwork.workspace = true +itertools.workspace = true +libc.workspace = true +num-derive.workspace = true +num-traits.workspace = true +rand.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio.workspace = true +tracing.workspace = true +yang2.workspace = true + +holo-northbound = { path = "../holo-northbound" } +holo-protocol = { path = "../holo-protocol" } +holo-utils = { path = "../holo-utils" } +holo-yang = { path = "../holo-yang" } + +[dev-dependencies] +criterion.workspace = true + +holo-bgp = { path = ".", features = ["testing"] } +holo-protocol = { path = "../holo-protocol", features = ["testing"] } +holo-utils = { path = "../holo-utils", features = ["testing"] } + +[lints] +workspace = true + +[features] +default = [] +testing = [] + +[[bench]] +name = "msg_encoding" +harness = false + +[[bench]] +name = "msg_decoding" +harness = false diff --git a/holo-bgp/LICENSE b/holo-bgp/LICENSE new file mode 100644 index 00000000..4481fc10 --- /dev/null +++ b/holo-bgp/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2023 The Holo Core Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/holo-bgp/benches/msg_decoding.rs b/holo-bgp/benches/msg_decoding.rs new file mode 100644 index 00000000..98f89016 --- /dev/null +++ b/holo-bgp/benches/msg_decoding.rs @@ -0,0 +1,37 @@ +#![feature(lazy_cell)] + +use std::hint::black_box; + +use criterion::{criterion_group, criterion_main, Criterion}; +use holo_bgp::neighbor::PeerType; +use holo_bgp::packet::message::{Capability, DecodeCxt, Message}; + +fn msg_decode(n: u64) { + let cxt = DecodeCxt { + peer_type: PeerType::Internal, + peer_as: n as u32, + capabilities: [Capability::FourOctetAsNumber { asn: n as u32 }].into(), + }; + + let bytes = vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x3d, 0x01, 0x04, 0x00, 0x01, 0x00, 0xb4, + 0x01, 0x01, 0x01, 0x01, 0x20, 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, + 0x01, 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, + 0x00, 0x02, 0x06, 0x41, 0x04, 0x00, 0x01, 0x00, 0x0e, 0x02, 0x02, 0x46, + 0x00, + ]; + + for _ in 0..n { + let _msg = Message::decode(&bytes, &cxt).unwrap(); + } +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("Message decode", |b| { + b.iter(|| msg_decode(black_box(10000))) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/holo-bgp/benches/msg_encoding.rs b/holo-bgp/benches/msg_encoding.rs new file mode 100644 index 00000000..a070ddce --- /dev/null +++ b/holo-bgp/benches/msg_encoding.rs @@ -0,0 +1,52 @@ +#![feature(lazy_cell)] + +use std::hint::black_box; +use std::net::Ipv4Addr; +use std::str::FromStr; +use std::sync::LazyLock as Lazy; + +use criterion::{criterion_group, criterion_main, Criterion}; +use holo_bgp::packet::consts::{Afi, Safi, BGP_VERSION}; +use holo_bgp::packet::message::{Capability, EncodeCxt, Message, OpenMsg}; + +static MESSAGE: Lazy = Lazy::new(|| { + Message::Open(OpenMsg { + version: BGP_VERSION, + my_as: 1, + holdtime: 180, + identifier: Ipv4Addr::from_str("1.1.1.1").unwrap(), + capabilities: [ + Capability::MultiProtocol { + afi: Afi::Ipv4, + safi: Safi::Unicast, + }, + Capability::MultiProtocol { + afi: Afi::Ipv6, + safi: Safi::Unicast, + }, + Capability::FourOctetAsNumber { asn: 65550 }, + Capability::RouteRefresh, + Capability::EnhancedRouteRefresh, + ] + .into(), + }) +}); + +fn msg_encode(n: u64) { + let cxt = EncodeCxt { + capabilities: [Capability::FourOctetAsNumber { asn: n as u32 }].into(), + }; + + for _ in 0..n { + MESSAGE.encode(&cxt); + } +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("Message encode", |b| { + b.iter(|| msg_encode(black_box(10000))) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/holo-bgp/src/debug.rs b/holo-bgp/src/debug.rs new file mode 100644 index 00000000..25893250 --- /dev/null +++ b/holo-bgp/src/debug.rs @@ -0,0 +1,147 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; + +use tracing::{debug, debug_span}; + +use crate::neighbor::fsm; +use crate::packet::consts::AttrType; +use crate::packet::error::AttrError; +use crate::packet::message::Message; + +// BGP debug messages. +#[derive(Debug)] +pub enum Debug<'a> { + InstanceCreate, + InstanceDelete, + InstanceStart, + InstanceStop(InstanceInactiveReason), + InstanceStatusCheck(&'a str), + NbrFsmEvent(&'a IpAddr, &'a fsm::Event), + NbrFsmTransition(&'a IpAddr, &'a fsm::State, &'a fsm::State), + NbrMsgRx(&'a IpAddr, &'a Message), + NbrMsgTx(&'a IpAddr, &'a Message), + NbrAttrError(AttrType, AttrError), +} + +// Reason why an BGP instance is inactive. +#[derive(Debug)] +pub enum InstanceInactiveReason { + AdminDown, + MissingRouterId, +} + +// ===== impl Debug ===== + +impl<'a> Debug<'a> { + // Log debug message using the tracing API. + pub(crate) fn log(&self) { + match self { + Debug::InstanceCreate + | Debug::InstanceDelete + | Debug::InstanceStart => { + // Parent span(s): bgp-instance + debug!("{}", self); + } + Debug::InstanceStop(reason) => { + // Parent span(s): bgp-instance + debug!(%reason, "{}", self); + } + Debug::InstanceStatusCheck(status) => { + // Parent span(s): bgp-instance + debug!(%status, "{}", self); + } + Debug::NbrFsmEvent(nbr_addr, event) => { + // Parent span(s): bgp-instance + debug_span!("neighbor", %nbr_addr).in_scope(|| { + debug_span!("fsm").in_scope(|| { + debug!(?event, "{}", self); + }) + }); + } + Debug::NbrFsmTransition(nbr_addr, old_state, new_state) => { + // Parent span(s): bgp-instance + debug_span!("neighbor", %nbr_addr).in_scope(|| { + debug_span!("fsm").in_scope(|| { + debug!(?old_state, ?new_state, "{}", self); + }) + }); + } + Debug::NbrMsgRx(nbr_addr, msg) => { + // Parent span(s): bgp-instance + debug_span!("neighbor", %nbr_addr).in_scope(|| { + debug_span!("input").in_scope(|| { + let data = serde_json::to_string(&msg).unwrap(); + debug!(%data, "{}", self); + }) + }); + } + Debug::NbrMsgTx(nbr_addr, msg) => { + // Parent span(s): bgp-instance + debug_span!("neighbor", %nbr_addr).in_scope(|| { + debug_span!("output").in_scope(|| { + let data = serde_json::to_string(&msg).unwrap(); + debug!(%data, "{}", self); + }) + }); + } + Debug::NbrAttrError(attr_type, action) => { + // Parent span(s): bgp-instance + debug!(?attr_type, ?action, "{}", self); + } + } + } +} + +impl<'a> std::fmt::Display for Debug<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Debug::InstanceCreate => { + write!(f, "instance created") + } + Debug::InstanceDelete => { + write!(f, "instance deleted") + } + Debug::InstanceStart => { + write!(f, "starting instance") + } + Debug::InstanceStop(..) => { + write!(f, "stopping instance") + } + Debug::InstanceStatusCheck(..) => { + write!(f, "checking instance status") + } + Debug::NbrFsmEvent(..) => { + write!(f, "event") + } + Debug::NbrFsmTransition(..) => { + write!(f, "state transition") + } + Debug::NbrMsgRx(..) | Debug::NbrMsgTx(..) => { + write!(f, "message") + } + Debug::NbrAttrError(..) => { + write!(f, "malformed attribute") + } + } + } +} + +// ===== impl InstanceInactiveReason ===== + +impl std::fmt::Display for InstanceInactiveReason { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InstanceInactiveReason::AdminDown => { + write!(f, "administrative status down") + } + InstanceInactiveReason::MissingRouterId => { + write!(f, "missing router-id") + } + } + } +} diff --git a/holo-bgp/src/error.rs b/holo-bgp/src/error.rs new file mode 100644 index 00000000..82f091fc --- /dev/null +++ b/holo-bgp/src/error.rs @@ -0,0 +1,220 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::{IpAddr, Ipv4Addr}; + +use serde::{Deserialize, Serialize}; +use tracing::{error, warn, warn_span}; + +use crate::packet::error::DecodeError; + +// BGP errors. +#[derive(Debug)] +pub enum Error { + // I/O errors + IoError(IoError), + // Network input + NbrRxError(NbrRxError), + // Message processing + NbrBadAs(IpAddr, u32, u32), + NbrBadIdentifier(IpAddr, Ipv4Addr), + // Other + InstanceStartError(Box), +} + +// BGP I/O errors. +#[derive(Debug)] +pub enum IoError { + TcpSocketError(std::io::Error), + TcpAcceptError(std::io::Error), + TcpConnectError(std::io::Error), + TcpInfoError(std::io::Error), + TcpAuthError(std::io::Error), + TcpRecvError(std::io::Error), + TcpSendError(std::io::Error), +} + +// Neighbor RX errors. +#[derive(Debug)] +#[derive(Deserialize, Serialize)] +pub enum NbrRxError { + TcpConnClosed(IpAddr), + MsgDecodeError(IpAddr, DecodeError), +} + +// ===== impl Error ===== + +impl Error { + pub(crate) fn log(&self) { + match self { + Error::IoError(error) => { + error.log(); + } + Error::NbrRxError(error) => { + error.log(); + } + Error::NbrBadAs(addr, received, expected) => { + warn_span!("neighbor", %addr).in_scope(|| { + warn!(%received, %expected, "{}", self); + }); + } + Error::NbrBadIdentifier(addr, identifier) => { + warn_span!("neighbor", %addr).in_scope(|| { + warn!(%identifier, "{}", self); + }); + } + Error::InstanceStartError(error) => { + error!(error = %with_source(error), "{}", self); + } + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::IoError(error) => error.fmt(f), + Error::NbrRxError(error) => error.fmt(f), + Error::NbrBadAs(..) => { + write!(f, "bad peer AS") + } + Error::NbrBadIdentifier(..) => { + write!(f, "BGP identifier conflict") + } + Error::InstanceStartError(..) => { + write!(f, "failed to start instance") + } + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::IoError(error) => Some(error), + Error::NbrRxError(error) => Some(error), + Error::InstanceStartError(error) => Some(error), + _ => None, + } + } +} + +impl From for Error { + fn from(error: IoError) -> Error { + Error::IoError(error) + } +} + +// ===== impl IoError ===== + +impl IoError { + pub(crate) fn log(&self) { + match self { + IoError::TcpSocketError(error) + | IoError::TcpAcceptError(error) + | IoError::TcpConnectError(error) + | IoError::TcpAuthError(error) + | IoError::TcpInfoError(error) + | IoError::TcpRecvError(error) + | IoError::TcpSendError(error) => { + warn!(error = %with_source(error), "{}", self); + } + } + } +} + +impl std::fmt::Display for IoError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IoError::TcpSocketError(..) => { + write!(f, "failed to create TCP socket") + } + IoError::TcpAcceptError(..) => { + write!(f, "failed to accept connection request") + } + IoError::TcpConnectError(..) => { + write!(f, "failed to establish TCP connection") + } + IoError::TcpAuthError(..) => { + write!(f, "failed to set TCP authentication option") + } + IoError::TcpInfoError(..) => { + write!(f, "failed to fetch address and port information from the socket") + } + IoError::TcpRecvError(..) => { + write!(f, "failed to read TCP data") + } + IoError::TcpSendError(..) => { + write!(f, "failed to send TCP data") + } + } + } +} + +impl std::error::Error for IoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + IoError::TcpSocketError(error) + | IoError::TcpAcceptError(error) + | IoError::TcpConnectError(error) + | IoError::TcpAuthError(error) + | IoError::TcpInfoError(error) + | IoError::TcpRecvError(error) + | IoError::TcpSendError(error) => Some(error), + } + } +} + +// ===== impl NbrRxError ===== + +impl NbrRxError { + pub(crate) fn log(&self) { + match self { + NbrRxError::TcpConnClosed(addr) => { + warn_span!("neighbor", %addr).in_scope(|| { + warn!("{}", self); + }); + } + NbrRxError::MsgDecodeError(addr, error) => { + warn_span!("neighbor", %addr).in_scope(|| { + warn!(error = %with_source(error), "{}", self); + }); + } + } + } +} + +impl std::fmt::Display for NbrRxError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NbrRxError::TcpConnClosed(..) => { + write!(f, "connection closed by remote end") + } + NbrRxError::MsgDecodeError(..) => { + write!(f, "failed to decode BGP message") + } + } + } +} + +impl std::error::Error for NbrRxError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + NbrRxError::MsgDecodeError(_, error) => Some(error), + _ => None, + } + } +} + +// ===== global functions ===== + +fn with_source(error: E) -> String { + if let Some(source) = error.source() { + format!("{} ({})", error, with_source(source)) + } else { + error.to_string() + } +} diff --git a/holo-bgp/src/events.rs b/holo-bgp/src/events.rs new file mode 100644 index 00000000..77fe4856 --- /dev/null +++ b/holo-bgp/src/events.rs @@ -0,0 +1,388 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::BTreeMap; +use std::net::IpAddr; +use std::sync::Arc; + +use chrono::Utc; +use holo_utils::bgp::AfiSafi; +use holo_utils::ip::{IpAddrKind, IpNetworkKind}; +use holo_utils::policy::{MatchSets, PolicyResult}; +use holo_utils::socket::{TcpConnInfo, TcpStream}; +use ipnetwork::IpNetwork; + +use crate::debug::Debug; +use crate::error::{Error, IoError, NbrRxError}; +use crate::instance::{InstanceUpView, PolicyApplyTasks}; +use crate::neighbor::{fsm, Neighbor, Neighbors}; +use crate::network; +use crate::packet::attribute::Attrs; +//use crate::packet::consts::Safi; +use crate::packet::message::{ + Message, MpReachNlri, MpUnreachNlri, RouteRefreshMsg, UpdateMsg, +}; +use crate::rib::{AttrSetsCtx, Destination, Route}; +use crate::tasks::messages::output::PolicyApplyMsg; + +// ===== TCP connection request ===== + +pub(crate) fn process_tcp_accept( + instance: &mut InstanceUpView<'_>, + neighbors: &mut Neighbors, + stream: TcpStream, + conn_info: TcpConnInfo, +) -> Result<(), Error> { + // Lookup neighbor. + let Some(nbr) = neighbors.get_mut(&conn_info.remote_addr) else { + return Ok(()); + }; + + // Initialize the accepted stream. + network::accepted_stream_init( + &stream, + nbr.remote_addr.address_family(), + nbr.tx_ttl(), + nbr.config.transport.ttl_security, + nbr.config.transport.tcp_mss, + ) + .map_err(IoError::TcpSocketError)?; + + // Invoke FSM event. + nbr.fsm_event(instance, fsm::Event::Connected(stream, conn_info)); + + Ok(()) +} + +// ===== TCP connection established ===== + +pub(crate) fn process_tcp_connect( + instance: &mut InstanceUpView<'_>, + neighbors: &mut Neighbors, + stream: TcpStream, + conn_info: TcpConnInfo, +) -> Result<(), Error> { + // Lookup neighbor. + let Some(nbr) = neighbors.get_mut(&conn_info.remote_addr) else { + return Ok(()); + }; + nbr.tasks.connect = None; + + // Invoke FSM event. + nbr.fsm_event(instance, fsm::Event::Connected(stream, conn_info)); + + Ok(()) +} + +// ===== neighbor message receipt ===== + +pub(crate) fn process_nbr_msg( + instance: &mut InstanceUpView<'_>, + neighbors: &mut Neighbors, + nbr_addr: IpAddr, + msg: Result, +) -> Result<(), Error> { + // Lookup neighbor. + let Some(nbr) = neighbors.get_mut(&nbr_addr) else { + return Ok(()); + }; + + // Process received message. + match msg { + Ok(msg) => { + Debug::NbrMsgRx(&nbr.remote_addr, &msg).log(); + + // Update statistics. + nbr.statistics.msgs_rcvd.update(&msg); + + match msg { + Message::Open(msg) => { + nbr.fsm_event(instance, fsm::Event::RcvdOpen(msg)); + } + Message::Update(msg) => { + nbr.fsm_event(instance, fsm::Event::RcvdUpdate); + process_nbr_update(instance, nbr, msg)?; + } + Message::Notification(msg) => { + nbr.fsm_event(instance, fsm::Event::RcvdNotif(msg.clone())); + // Keep track of the last received notification. + nbr.notification_rcvd = Some((Utc::now(), msg)); + } + Message::Keepalive(_) => { + nbr.fsm_event(instance, fsm::Event::RcvdKalive); + } + Message::RouteRefresh(msg) => { + process_nbr_route_refresh(instance, nbr, msg)?; + } + } + } + Err(error) => match error { + NbrRxError::TcpConnClosed(_) => { + nbr.fsm_event(instance, fsm::Event::ConnFail); + } + NbrRxError::MsgDecodeError(_, error) => { + nbr.fsm_event(instance, fsm::Event::RcvdError(error)); + } + }, + } + + Ok(()) +} + +fn process_nbr_update( + instance: &mut InstanceUpView<'_>, + nbr: &mut Neighbor, + msg: UpdateMsg, +) -> Result<(), Error> { + let rib = &mut instance.state.rib; + let match_sets = &instance.shared.policy_match_sets; + + // Process IPv4 reachable NLRIs. + // + // Use nexthop from the NEXTHOP attribute. + if let Some(reach) = msg.reach { + if let Some(attrs) = &msg.attrs { + // Update the pre-policy Adj-RIB-In. + let mut attrs = attrs.clone(); + attrs.base.nexthop = Some(reach.nexthop.into()); + process_nbr_reach_prefixes( + &mut rib.ipv4_unicast, + &mut rib.attr_sets, + match_sets, + &instance.state.policy_apply_tasks, + nbr, + reach.prefixes.clone(), + attrs, + ); + } else { + // Treat as withdraw. + process_nbr_unreach_prefixes( + &mut rib.ipv4_unicast, + nbr, + reach.prefixes, + ); + } + } + + // Process multiprotocol reachable NLRIs. + // + // Use nexthop(s) from the MP_REACH_NLRI attribute. + if let Some(mp_reach) = msg.mp_reach { + if let Some(mut attrs) = msg.attrs { + match mp_reach { + MpReachNlri::Ipv4 { + prefixes, nexthop, .. + } => { + attrs.base.nexthop = Some(nexthop.into()); + process_nbr_reach_prefixes( + &mut rib.ipv4_unicast, + &mut rib.attr_sets, + match_sets, + &instance.state.policy_apply_tasks, + nbr, + prefixes, + attrs, + ); + } + MpReachNlri::Ipv6 { + prefixes, + nexthop, + ll_nexthop, + .. + } => { + attrs.base.nexthop = Some(nexthop.into()); + attrs.base.ll_nexthop = ll_nexthop; + process_nbr_reach_prefixes( + &mut rib.ipv6_unicast, + &mut rib.attr_sets, + match_sets, + &instance.state.policy_apply_tasks, + nbr, + prefixes, + attrs, + ); + } + } + } else { + // Treat as withdraw. + match mp_reach { + MpReachNlri::Ipv4 { prefixes, .. } => { + process_nbr_unreach_prefixes( + &mut rib.ipv4_unicast, + nbr, + prefixes, + ); + } + MpReachNlri::Ipv6 { prefixes, .. } => { + process_nbr_unreach_prefixes( + &mut rib.ipv6_unicast, + nbr, + prefixes, + ); + } + } + } + } + + // Process IPv4 unreachable NLRIs. + if let Some(unreach) = msg.unreach { + process_nbr_unreach_prefixes( + &mut rib.ipv4_unicast, + nbr, + unreach.prefixes, + ); + } + + // Process multiprotocol unreachable NLRIs. + if let Some(mp_unreach) = msg.mp_unreach { + match mp_unreach { + MpUnreachNlri::Ipv4 { prefixes, .. } => { + process_nbr_unreach_prefixes( + &mut rib.ipv4_unicast, + nbr, + prefixes, + ); + } + MpUnreachNlri::Ipv6 { prefixes, .. } => { + process_nbr_unreach_prefixes( + &mut rib.ipv6_unicast, + nbr, + prefixes, + ); + } + } + } + + Ok(()) +} + +fn process_nbr_reach_prefixes( + rib: &mut BTreeMap, + attr_sets: &mut AttrSetsCtx, + match_sets: &MatchSets, + policy_apply_tasks: &PolicyApplyTasks, + nbr: &Neighbor, + nlri_prefixes: Vec, + attrs: Attrs, +) where + I: IpAddrKind, + N: IpNetworkKind, +{ + // Update pre-policy Adj-RIB-In routes. + let route_attrs = attr_sets.get_route_attr_sets(attrs.clone()); + for prefix in nlri_prefixes + .clone() + .into_iter() + // Ignore semantically incorrect NLRI prefixes. + .filter(|prefix| prefix.is_routable()) + { + let destination = rib.entry(prefix).or_default(); + let route = Route::new(route_attrs.clone()); + destination.adj_in_pre.insert(nbr.remote_addr, route); + } + + // Enqueue import policy application. + if let Some(afi_safi) = &nbr.config.afi_safi.get(&AfiSafi::Ipv4Unicast) { + let _ = policy_apply_tasks.tx.send(PolicyApplyMsg::NeighborImport { + nbr_addr: nbr.remote_addr, + prefixes: nlri_prefixes + .into_iter() + .map(|prefix| prefix.into()) + .collect(), + attrs, + // TODO + policies: Arc::new(Default::default()), + match_sets: Arc::new(match_sets.clone()), + default_policy: afi_safi.apply_policy.default_import_policy, + }); + } +} + +fn process_nbr_unreach_prefixes( + rib: &mut BTreeMap, + nbr: &Neighbor, + nlri_prefixes: Vec, +) where + I: IpAddrKind, + N: IpNetworkKind, +{ + // Remove routes from Adj-RIB-In. + for prefix in nlri_prefixes { + let destination = rib.entry(prefix).or_default(); + destination.adj_in_pre.remove(&nbr.remote_addr); + destination.adj_in_post.remove(&nbr.remote_addr); + } +} + +fn process_nbr_route_refresh( + _instance: &mut InstanceUpView<'_>, + _nbr: &mut Neighbor, + _msg: RouteRefreshMsg, +) -> Result<(), Error> { + // TODO + + Ok(()) +} + +// ===== neighbor expired timeout ===== + +pub(crate) fn process_nbr_timer( + instance: &mut InstanceUpView<'_>, + neighbors: &mut Neighbors, + nbr_addr: IpAddr, + timer: fsm::Timer, +) -> Result<(), Error> { + // Lookup neighbor. + let Some(nbr) = neighbors.get_mut(&nbr_addr) else { + return Ok(()); + }; + + // Invoke FSM event. + nbr.fsm_event(instance, fsm::Event::Timer(timer)); + + Ok(()) +} + +// ===== neighbor policy import result ===== + +pub(crate) fn process_nbr_policy_import( + instance: &mut InstanceUpView<'_>, + neighbors: &mut Neighbors, + nbr_addr: IpAddr, + prefixes: Vec<(IpNetwork, PolicyResult)>, +) -> Result<(), Error> { + // Lookup neighbor. + let Some(nbr) = neighbors.get_mut(&nbr_addr) else { + return Ok(()); + }; + + let rib = &mut instance.state.rib; + for (prefix, result) in prefixes { + // Get RIB destination. + let destination = match prefix { + IpNetwork::V4(prefix) => { + rib.ipv4_unicast.entry(prefix).or_default() + } + IpNetwork::V6(prefix) => { + rib.ipv6_unicast.entry(prefix).or_default() + } + }; + + // Update post-policy Adj-RIB-In routes. + match result { + PolicyResult::Accept(attrs) => { + let route_attrs = rib.attr_sets.get_route_attr_sets(attrs); + let route = Route::new(route_attrs.clone()); + destination.adj_in_post.insert(nbr.remote_addr, route); + } + PolicyResult::Reject => { + destination.adj_in_post.remove(&nbr.remote_addr); + } + } + } + + Ok(()) +} diff --git a/holo-bgp/src/instance.rs b/holo-bgp/src/instance.rs new file mode 100644 index 00000000..ef5ec4d8 --- /dev/null +++ b/holo-bgp/src/instance.rs @@ -0,0 +1,454 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; +use std::sync::Arc; + +use async_trait::async_trait; +use holo_protocol::{ + InstanceChannelsTx, InstanceShared, MessageReceiver, ProtocolInstance, +}; +use holo_utils::ibus::IbusMsg; +use holo_utils::ip::AddressFamily; +use holo_utils::protocol::Protocol; +use holo_utils::socket::TcpListener; +use holo_utils::task::Task; +use holo_utils::{Receiver, Sender, UnboundedReceiver, UnboundedSender}; +use tokio::sync::mpsc; + +use crate::debug::{Debug, InstanceInactiveReason}; +use crate::error::{Error, IoError}; +use crate::neighbor::{fsm, Neighbors}; +use crate::northbound::configuration::InstanceCfg; +use crate::packet::consts::{CeaseSubcode, ErrorCode}; +use crate::packet::message::NotificationMsg; +use crate::rib::Rib; +use crate::tasks::messages::input::{ + NbrRxMsg, NbrTimerMsg, PolicyResultMsg, TcpAcceptMsg, TcpConnectMsg, +}; +use crate::tasks::messages::output::PolicyApplyMsg; +use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; +use crate::{events, network, southbound, tasks}; + +#[derive(Debug)] +pub struct Instance { + // Instance name. + pub name: String, + // Instance system data. + pub system: InstanceSys, + // Instance configuration data. + pub config: InstanceCfg, + // Instance state data. + pub state: Option, + // Instance neighbors. + pub neighbors: Neighbors, + // Instance Tx channels. + pub tx: InstanceChannelsTx, + // Shared data. + pub shared: InstanceShared, +} + +#[derive(Debug, Default)] +pub struct InstanceSys { + pub router_id: Option, +} + +#[derive(Debug)] +pub struct InstanceState { + // Instance Router ID. + pub router_id: Ipv4Addr, + // TCP listening sockets. + pub listening_sockets: Vec, + // Policy tasks. + pub policy_apply_tasks: PolicyApplyTasks, + // RIB. + pub rib: Rib, +} + +#[derive(Debug)] +pub struct TcpListenerTask { + pub af: AddressFamily, + pub socket: Arc, + _task: Task<()>, +} + +#[derive(Debug)] +pub struct PolicyApplyTasks { + pub tx: crossbeam_channel::Sender, + _tasks: Vec>, +} + +#[derive(Clone, Debug)] +pub struct ProtocolInputChannelsTx { + // TCP accept event. + pub tcp_accept: Sender, + // TCP connect event. + pub tcp_connect: Sender, + // TCP neighbor message. + pub nbr_msg_rx: Sender, + // Neighbor timeout event. + pub nbr_timer: Sender, + // Policy result message. + pub policy_result: UnboundedSender, +} + +#[derive(Debug)] +pub struct ProtocolInputChannelsRx { + // TCP accept event. + pub tcp_accept: Receiver, + // TCP connect event. + pub tcp_connect: Receiver, + // TCP neighbor message. + pub nbr_msg_rx: Receiver, + // Neighbor timeout event. + pub nbr_timer: Receiver, + // Policy result message. + pub policy_result: UnboundedReceiver, +} + +pub struct InstanceUpView<'a> { + pub name: &'a str, + pub system: &'a InstanceSys, + pub config: &'a InstanceCfg, + pub state: &'a mut InstanceState, + pub tx: &'a InstanceChannelsTx, + pub shared: &'a InstanceShared, +} + +// ===== impl Instance ===== + +impl Instance { + // Checks if the instance needs to be started or stopped in response to a + // northbound or southbound event. + // + // Note: Router-ID updates are ignored if the instance is already active. + pub(crate) async fn update(&mut self) { + let router_id = self.get_router_id(); + + match self.is_ready(router_id) { + Ok(()) if !self.is_active() => { + self.start(router_id.unwrap()).await; + } + Err(reason) if self.is_active() => { + self.stop(reason); + } + _ => (), + } + } + + // Starts the BGP instance. + async fn start(&mut self, router_id: Ipv4Addr) { + Debug::InstanceStart.log(); + + match InstanceState::new(router_id, &self.tx).await { + Ok(state) => { + // Store instance initial state. + self.state = Some(state); + } + Err(error) => { + Error::InstanceStartError(Box::new(error)).log(); + } + } + } + + // Stops the BGP instance. + fn stop(&mut self, reason: InstanceInactiveReason) { + let Some((mut instance, neighbors)) = self.as_up() else { + return; + }; + + Debug::InstanceStop(reason).log(); + + // Stop neighbors. + let error_code = ErrorCode::Cease; + let error_subcode = CeaseSubcode::AdministrativeShutdown; + for nbr in neighbors.values_mut() { + let msg = NotificationMsg::new(error_code, error_subcode); + nbr.fsm_event(&mut instance, fsm::Event::Stop(Some(msg))); + } + + // Clear instance state. + self.state = None; + } + + // Returns whether the BGP instance is operational. + fn is_active(&self) -> bool { + self.state.is_some() + } + + // Returns whether the instance is ready for BGP operation. + fn is_ready( + &self, + router_id: Option, + ) -> Result<(), InstanceInactiveReason> { + if router_id.is_none() { + return Err(InstanceInactiveReason::MissingRouterId); + } + + Ok(()) + } + + // Retrieves the Router ID from configuration or system information. + // Prioritizes the configured Router ID, using the system's Router ID as a + // fallback. + fn get_router_id(&self) -> Option { + self.config.identifier.or(self.system.router_id) + } + + // Returns a view struct for the instance if it is operational. + pub(crate) fn as_up( + &mut self, + ) -> Option<(InstanceUpView<'_>, &mut Neighbors)> { + if let Some(state) = &mut self.state { + let instance = InstanceUpView { + name: &self.name, + system: &self.system, + config: &self.config, + state, + tx: &self.tx, + shared: &self.shared, + }; + Some((instance, &mut self.neighbors)) + } else { + None + } + } +} + +#[async_trait] +impl ProtocolInstance for Instance { + const PROTOCOL: Protocol = Protocol::BGP; + + type ProtocolInputMsg = ProtocolInputMsg; + type ProtocolOutputMsg = ProtocolOutputMsg; + type ProtocolInputChannelsTx = ProtocolInputChannelsTx; + type ProtocolInputChannelsRx = ProtocolInputChannelsRx; + + async fn new( + name: String, + shared: InstanceShared, + tx: InstanceChannelsTx, + ) -> Instance { + Debug::InstanceCreate.log(); + + Instance { + name, + system: Default::default(), + config: Default::default(), + state: None, + neighbors: Default::default(), + tx, + shared, + } + } + + async fn shutdown(mut self) { + // Ensure instance is disabled before exiting. + self.stop(InstanceInactiveReason::AdminDown); + Debug::InstanceDelete.log(); + } + + async fn process_ibus_msg(&mut self, msg: IbusMsg) { + if let Err(error) = process_ibus_msg(self, msg).await { + error.log(); + } + } + + fn process_protocol_msg(&mut self, msg: ProtocolInputMsg) { + // Ignore event if the instance isn't active. + if let Some((mut instance, neighbors)) = self.as_up() { + if let Err(error) = + process_protocol_msg(&mut instance, neighbors, msg) + { + error.log(); + } + } + } + + fn protocol_input_channels( + ) -> (ProtocolInputChannelsTx, ProtocolInputChannelsRx) { + let (tcp_acceptp, tcp_acceptc) = mpsc::channel(4); + let (tcp_connectp, tcp_connectc) = mpsc::channel(4); + let (nbr_msg_rxp, nbr_msg_rxc) = mpsc::channel(4); + let (nbr_timerp, nbr_timerc) = mpsc::channel(4); + let (policy_resultp, policy_resultc) = mpsc::unbounded_channel(); + + let tx = ProtocolInputChannelsTx { + tcp_accept: tcp_acceptp, + tcp_connect: tcp_connectp, + nbr_msg_rx: nbr_msg_rxp, + nbr_timer: nbr_timerp, + policy_result: policy_resultp, + }; + let rx = ProtocolInputChannelsRx { + tcp_accept: tcp_acceptc, + tcp_connect: tcp_connectc, + nbr_msg_rx: nbr_msg_rxc, + nbr_timer: nbr_timerc, + policy_result: policy_resultc, + }; + + (tx, rx) + } + + #[cfg(feature = "testing")] + fn test_dir() -> String { + format!("{}/tests/conformance", env!("CARGO_MANIFEST_DIR"),) + } +} + +// ===== impl InstanceState ===== + +impl InstanceState { + async fn new( + router_id: Ipv4Addr, + instance_tx: &InstanceChannelsTx, + ) -> Result { + let mut listening_sockets = Vec::new(); + + // Create TCP listeners. + for af in [AddressFamily::Ipv4, AddressFamily::Ipv6] { + let socket = network::listen_socket(af) + .map(Arc::new) + .map_err(IoError::TcpSocketError)?; + let task = tasks::tcp_listener( + &socket, + &instance_tx.protocol_input.tcp_accept, + ); + listening_sockets.push(TcpListenerTask { + af, + socket, + _task: task, + }); + } + + // Create routing policy tasks. + let (policy_tx, policy_rx) = crossbeam_channel::unbounded(); + let num_cpus = std::thread::available_parallelism().unwrap().get(); + let tasks = (0..num_cpus) + .map(|_| { + tasks::policy_apply( + policy_rx.clone(), + &instance_tx.protocol_input.policy_result, + #[cfg(feature = "testing")] + &instance_tx.protocol_output, + ) + }) + .collect(); + let policy_apply_tasks = PolicyApplyTasks { + tx: policy_tx, + _tasks: tasks, + }; + + Ok(InstanceState { + router_id, + listening_sockets, + policy_apply_tasks, + rib: Default::default(), + }) + } +} + +// ===== impl ProtocolInputChannelsRx ===== + +#[async_trait] +impl MessageReceiver for ProtocolInputChannelsRx { + async fn recv(&mut self) -> Option { + tokio::select! { + msg = self.tcp_accept.recv() => { + msg.map(ProtocolInputMsg::TcpAccept) + } + msg = self.tcp_connect.recv() => { + msg.map(ProtocolInputMsg::TcpConnect) + } + msg = self.nbr_msg_rx.recv() => { + msg.map(ProtocolInputMsg::NbrRx) + } + msg = self.nbr_timer.recv() => { + msg.map(ProtocolInputMsg::NbrTimer) + } + msg = self.policy_result.recv() => { + msg.map(ProtocolInputMsg::PolicyResult) + } + } + } +} + +// ===== helper functions ===== + +async fn process_ibus_msg( + instance: &mut Instance, + msg: IbusMsg, +) -> Result<(), Error> { + match msg { + // Router ID update notification. + IbusMsg::RouterIdUpdate(router_id) => { + southbound::rx::process_router_id_update(instance, router_id).await; + } + // Ignore other events. + _ => {} + } + + Ok(()) +} + +fn process_protocol_msg( + instance: &mut InstanceUpView<'_>, + neighbors: &mut Neighbors, + msg: ProtocolInputMsg, +) -> Result<(), Error> { + match msg { + // Accepted TCP connection request. + ProtocolInputMsg::TcpAccept(mut msg) => { + events::process_tcp_accept( + instance, + neighbors, + msg.stream(), + msg.conn_info, + )?; + } + // Established TCP connection. + ProtocolInputMsg::TcpConnect(mut msg) => { + events::process_tcp_connect( + instance, + neighbors, + msg.stream(), + msg.conn_info, + )?; + } + // Received message from neighbor. + ProtocolInputMsg::NbrRx(msg) => { + events::process_nbr_msg( + instance, + neighbors, + msg.nbr_addr, + msg.msg, + )?; + } + // Neighbor's timeout has expired. + ProtocolInputMsg::NbrTimer(msg) => { + events::process_nbr_timer( + instance, + neighbors, + msg.nbr_addr, + msg.timer, + )?; + } + // Policy result. + ProtocolInputMsg::PolicyResult(msg) => { + tracing::debug!("XXX policy result: {:?}", msg); + match msg { + PolicyResultMsg::NeighborImport { nbr_addr, prefixes } => { + events::process_nbr_policy_import( + instance, neighbors, nbr_addr, prefixes, + )?; + } + } + } + } + + Ok(()) +} diff --git a/holo-bgp/src/lib.rs b/holo-bgp/src/lib.rs new file mode 100644 index 00000000..4cb79cac --- /dev/null +++ b/holo-bgp/src/lib.rs @@ -0,0 +1,24 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +#![cfg_attr( + feature = "testing", + allow(dead_code, unused_variables, unused_imports) +)] +#![feature(let_chains, lazy_cell)] + +pub mod debug; +pub mod error; +pub mod events; +pub mod instance; +pub mod neighbor; +pub mod network; +pub mod northbound; +pub mod packet; +pub mod policy; +pub mod rib; +pub mod southbound; +pub mod tasks; diff --git a/holo-bgp/src/neighbor.rs b/holo-bgp/src/neighbor.rs new file mode 100644 index 00000000..eecc7a78 --- /dev/null +++ b/holo-bgp/src/neighbor.rs @@ -0,0 +1,818 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::{BTreeMap, BTreeSet}; +use std::net::{IpAddr, Ipv4Addr}; +use std::sync::atomic::{self, AtomicU32}; +use std::sync::Arc; +use std::time::Duration; + +use chrono::{DateTime, Utc}; +use holo_utils::bgp::AfiSafi; +use holo_utils::socket::{TcpConnInfo, TcpStream, TTL_MAX}; +use holo_utils::task::{IntervalTask, Task, TimeoutTask}; +use holo_utils::{Sender, UnboundedSender}; +use tokio::sync::mpsc; + +use crate::debug::Debug; +use crate::error::Error; +use crate::instance::InstanceUpView; +use crate::northbound::configuration::{InstanceCfg, NeighborCfg}; +use crate::packet::consts::{ + Afi, ErrorCode, FsmErrorSubcode, Safi, AS_TRANS, BGP_VERSION, +}; +use crate::packet::message::{ + Capability, DecodeCxt, EncodeCxt, KeepaliveMsg, Message, NotificationMsg, + OpenMsg, +}; +use crate::tasks; +use crate::tasks::messages::input::{NbrRxMsg, NbrTimerMsg, TcpConnectMsg}; +use crate::tasks::messages::output::NbrTxMsg; +#[cfg(feature = "testing")] +use crate::tasks::messages::ProtocolOutputMsg; + +// Large hold-time used during session initialization. +const LARGE_HOLDTIME: u16 = 240; + +// BGP neighbor. +#[derive(Debug)] +pub struct Neighbor { + pub remote_addr: IpAddr, + pub config: NeighborCfg, + pub state: fsm::State, + pub peer_type: PeerType, + pub conn_info: Option, + pub identifier: Option, + pub holdtime_nego: Option, + pub capabilities_adv: BTreeSet, + pub capabilities_rcvd: BTreeSet, + pub capabilities_nego: BTreeSet, + pub notification_sent: Option<(DateTime, NotificationMsg)>, + pub notification_rcvd: Option<(DateTime, NotificationMsg)>, + pub last_established: Option>, + pub statistics: NeighborStatistics, + pub tasks: NeighborTasks, + pub msg_txp: Option>, +} + +// BGP peer type. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PeerType { + Internal, + External, +} + +// Neighbor statistics. +#[derive(Debug, Default)] +pub struct NeighborStatistics { + pub established_transitions: u32, + pub msgs_rcvd: MessageStatistics, + pub msgs_sent: MessageStatistics, + pub erroneous_updates_withdrawn: u32, + pub erroneous_updates_attribute_discarded: u32, + pub in_update_elapsed_time: Duration, +} + +// Inbound and outbound message counters. +#[derive(Debug, Default)] +pub struct MessageStatistics { + pub total: Arc, + pub updates: u32, + pub notifications: u32, + pub route_refreshes: u32, +} + +// Neighbor tasks. +#[derive(Debug, Default)] +pub struct NeighborTasks { + pub autostart: Option, + pub connect: Option>, + pub connect_retry: Option, + pub tcp_rx: Option>, + pub keepalive: Option, + pub holdtime: Option, +} + +// Type aliases. +pub type Neighbors = BTreeMap; + +// Finite State Machine. +pub mod fsm { + use holo_utils::socket::{TcpConnInfo, TcpStream}; + use serde::{Deserialize, Serialize}; + + use crate::packet::error::DecodeError; + use crate::packet::message::{NotificationMsg, OpenMsg}; + + // FSM states. + #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] + pub enum State { + Idle, + Connect, + Active, + OpenSent, + OpenConfirm, + Established, + } + + // FSM events. + // + // The original RFC FSM events are listed above each event for clarity. + #[derive(Debug)] + pub enum Event { + // ManualStart + // ManualStart_with_PassiveTcpEstablishment + Start, + // ManualStop + Stop(Option), + // Tcp_CR_Acked + // TcpConnectionConfirmed + Connected(TcpStream, TcpConnInfo), + // TcpConnectionFails + ConnFail, + // BGPHeaderErr + // BGPOpenMsgErr + // UpdateMsgErr + RcvdError(DecodeError), + // BGPOpen + RcvdOpen(OpenMsg), + // NotifMsg + RcvdNotif(NotificationMsg), + // KeepAliveMsg + RcvdKalive, + // UpdateMsg + RcvdUpdate, + // ConnectRetryTimer_Expires + // HoldTimer_Expires + // AutomaticStart + // AutomaticStart_with_PassiveTcpEstablishment + Timer(Timer), + } + + // BGP timers. + // + // Note: KEEPALIVE messages are sent independently, separate from the FSM. + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Deserialize, Serialize)] + pub enum Timer { + ConnectRetry, + Hold, + AutoStart, + } +} + +// ===== impl Neighbor ===== + +impl Neighbor { + // Creates a new neighbor in the Idle state with default configuration. + pub(crate) fn new(remote_addr: IpAddr, peer_type: PeerType) -> Neighbor { + Neighbor { + remote_addr, + config: Default::default(), + state: fsm::State::Idle, + peer_type, + conn_info: None, + identifier: None, + holdtime_nego: None, + capabilities_adv: Default::default(), + capabilities_rcvd: Default::default(), + capabilities_nego: Default::default(), + notification_sent: None, + notification_rcvd: None, + last_established: None, + statistics: Default::default(), + tasks: Default::default(), + msg_txp: None, + } + } + + // Injects an event into the neighbor's FSM. + pub(crate) fn fsm_event( + &mut self, + instance: &mut InstanceUpView<'_>, + event: fsm::Event, + ) { + Debug::NbrFsmEvent(&self.remote_addr, &event).log(); + + // Process FSM event. + let next_state = match self.state { + // Idle state + fsm::State::Idle => match event { + fsm::Event::Start + | fsm::Event::Timer(fsm::Timer::AutoStart) => { + self.connect_retry_start( + &instance.tx.protocol_input.nbr_timer, + ); + if self.config.transport.passive_mode { + Some(fsm::State::Active) + } else { + self.connect(&instance.tx.protocol_input.tcp_connect); + Some(fsm::State::Connect) + } + } + _ => None, + }, + // Connect state + fsm::State::Connect => match event { + fsm::Event::Start => None, + fsm::Event::Stop(_) => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::Connected(stream, conn_info) => { + self.connect_retry_stop(); + self.connection_setup( + stream, + conn_info, + &instance.tx.protocol_input.nbr_msg_rx, + #[cfg(feature = "testing")] + &instance.tx.protocol_output, + ); + self.open_send(instance.config, instance.state.router_id); + self.holdtime_start( + LARGE_HOLDTIME, + &instance.tx.protocol_input.nbr_timer, + ); + Some(fsm::State::OpenSent) + } + fsm::Event::ConnFail => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::RcvdError(error) => { + let msg = NotificationMsg::from(error); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + fsm::Event::Timer(fsm::Timer::ConnectRetry) => { + self.connect(&instance.tx.protocol_input.tcp_connect); + self.connect_retry_start( + &instance.tx.protocol_input.nbr_timer, + ); + None + } + _ => { + // FSM error. + self.session_close(None); + Some(fsm::State::Idle) + } + }, + // Active state + fsm::State::Active => match event { + fsm::Event::Start => None, + fsm::Event::Stop(_) => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::Connected(stream, conn_info) => { + self.connect_retry_stop(); + self.connection_setup( + stream, + conn_info, + &instance.tx.protocol_input.nbr_msg_rx, + #[cfg(feature = "testing")] + &instance.tx.protocol_output, + ); + self.open_send(instance.config, instance.state.router_id); + self.holdtime_start( + LARGE_HOLDTIME, + &instance.tx.protocol_input.nbr_timer, + ); + Some(fsm::State::OpenSent) + } + fsm::Event::ConnFail => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::RcvdError(error) => { + let msg = NotificationMsg::from(error); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + fsm::Event::Timer(fsm::Timer::ConnectRetry) => { + self.connect(&instance.tx.protocol_input.tcp_connect); + self.connect_retry_start( + &instance.tx.protocol_input.nbr_timer, + ); + Some(fsm::State::Connect) + } + _ => { + // FSM error. + self.session_close(None); + Some(fsm::State::Idle) + } + }, + // OpenSent state + fsm::State::OpenSent => match event { + fsm::Event::Start => None, + fsm::Event::Stop(msg) => { + self.session_close(msg); + Some(fsm::State::Idle) + } + fsm::Event::ConnFail => { + self.session_close(None); + self.connect_retry_start( + &instance.tx.protocol_input.nbr_timer, + ); + Some(fsm::State::Active) + } + fsm::Event::RcvdError(error) => { + let msg = NotificationMsg::from(error); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + fsm::Event::RcvdOpen(msg) => { + let next_state = self.open_process(instance, msg); + Some(next_state) + } + fsm::Event::Timer(fsm::Timer::Hold) => { + let error_code = ErrorCode::HoldTimerExpired; + let error_subcode = 0; + let msg = NotificationMsg::new(error_code, error_subcode); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + _ => { + // FSM error. + let error_code = ErrorCode::FiniteStateMachineError; + let error_subcode = + FsmErrorSubcode::UnexpectedMessageInOpenSent; + let msg = NotificationMsg::new(error_code, error_subcode); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + }, + // OpenConfirm state + fsm::State::OpenConfirm => match event { + fsm::Event::Start => None, + fsm::Event::Stop(msg) => { + self.session_close(msg); + Some(fsm::State::Idle) + } + fsm::Event::ConnFail => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::RcvdError(error) => { + let msg = NotificationMsg::from(error); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + fsm::Event::RcvdOpen(_msg) => { + // TODO: collision detection + Some(fsm::State::Idle) + } + fsm::Event::RcvdNotif(_) => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::RcvdKalive => { + self.holdtime_restart(); + Some(fsm::State::Established) + } + fsm::Event::Timer(fsm::Timer::Hold) => { + let error_code = ErrorCode::HoldTimerExpired; + let error_subcode = 0; + let msg = NotificationMsg::new(error_code, error_subcode); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + _ => { + // FSM error. + let error_code = ErrorCode::FiniteStateMachineError; + let error_subcode = + FsmErrorSubcode::UnexpectedMessageInOpenConfirm; + let msg = NotificationMsg::new(error_code, error_subcode); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + }, + // Established state + fsm::State::Established => match event { + fsm::Event::Start => None, + fsm::Event::Stop(msg) => { + self.session_close(msg); + Some(fsm::State::Idle) + } + fsm::Event::ConnFail => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::RcvdError(error) => { + let msg = NotificationMsg::from(error); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + fsm::Event::RcvdNotif(_) => { + self.session_close(None); + Some(fsm::State::Idle) + } + fsm::Event::RcvdKalive | fsm::Event::RcvdUpdate => { + self.holdtime_restart(); + None + } + fsm::Event::Timer(fsm::Timer::Hold) => { + let error_code = ErrorCode::HoldTimerExpired; + let error_subcode = 0; + let msg = NotificationMsg::new(error_code, error_subcode); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + _ => { + // FSM error. + let error_code = ErrorCode::FiniteStateMachineError; + let error_subcode = + FsmErrorSubcode::UnexpectedMessageInEstablished; + let msg = NotificationMsg::new(error_code, error_subcode); + self.session_close(Some(msg)); + Some(fsm::State::Idle) + } + }, + }; + + // Change to next FSM state when applicable. + if let Some(next_state) = next_state + && self.state != next_state + { + // Schedule auto-start unless the peer has been manually disabled. + if next_state == fsm::State::Idle && self.config.enabled { + self.autostart_start(&instance.tx.protocol_input.nbr_timer); + } else { + self.autostart_stop(); + } + + self.fsm_state_change(next_state); + } + } + + // Updates the neighbor's FSM state. + fn fsm_state_change(&mut self, next_state: fsm::State) { + Debug::NbrFsmTransition(&self.remote_addr, &self.state, &next_state) + .log(); + + // Keep track of the time that the BGP session last transitioned in or + // out of the Established state. + if self.state == fsm::State::Established + || next_state == fsm::State::Established + { + self.last_established = Some(Utc::now()); + } + + if next_state == fsm::State::Established { + // Update statistics. + self.statistics.established_transitions += 1; + + // Compute the negotiated capabilities. + self.capabilities_nego = self + .capabilities_adv + .intersection(&self.capabilities_rcvd) + .cloned() + .collect(); + + // Update the Tx task with the negotiated capabilities. + let msg = + NbrTxMsg::UpdateCapabilities(self.capabilities_nego.clone()); + let _ = self.msg_txp.as_ref().unwrap().send(msg); + } + + self.state = next_state; + } + + // Sets up the connection for the BGP neighbor, spawning necessary tasks for + // TCP communication. + fn connection_setup( + &mut self, + stream: TcpStream, + conn_info: TcpConnInfo, + nbr_msg_rxp: &Sender, + #[cfg(feature = "testing")] proto_output_tx: &Sender, + ) { + // Store TCP connection information. + self.conn_info = Some(conn_info); + + // Split TCP stream into two halves. + let (read_half, write_half) = stream.into_split(); + + // Spawn neighbor TCP Tx task. + let (msg_txp, msg_txc) = mpsc::unbounded_channel(); + let cxt = EncodeCxt { + capabilities: Default::default(), + }; + let mut tx_task = tasks::nbr_tx( + self, + cxt, + write_half, + msg_txc, + #[cfg(feature = "testing")] + proto_output_tx, + ); + self.msg_txp = Some(msg_txp); + + // Spawn neighbor TCP Rx task. + let cxt = DecodeCxt { + peer_type: self.peer_type, + peer_as: self.config.peer_as, + capabilities: Default::default(), + }; + let tcp_rx_task = tasks::nbr_rx(self, cxt, read_half, nbr_msg_rxp); + self.tasks.tcp_rx = Some(tcp_rx_task); + + // No need to keep track of the Tx task since it gracefully exits as + // soon as the tx end of its mpsc channel is dropped. This ensures that + // messages sent during neighbor shutdown will be delivered. + tx_task.detach(); + } + + // Closes the BGP session, performing necessary cleanup and releasing resources. + fn session_close(&mut self, send_notif: Option) { + // Send a notification message. + if self.state >= fsm::State::OpenSent + && let Some(msg) = send_notif + { + self.message_send(Message::Notification(msg)); + } + + // Set the ConnectRetryTimer to zero. + self.connect_retry_stop(); + + // Release all resources. + self.conn_info = None; + self.identifier = None; + self.holdtime_nego = None; + self.capabilities_adv.clear(); + self.capabilities_rcvd.clear(); + self.capabilities_nego.clear(); + self.tasks = Default::default(); + self.msg_txp = None; + } + + // Enqueues a BGP message for transmission. + fn message_send(&mut self, msg: Message) { + Debug::NbrMsgTx(&self.remote_addr, &msg).log(); + + // Update statistics. + self.statistics.msgs_sent.update(&msg); + + // Keep track of the last sent notification. + if let Message::Notification(msg) = &msg { + self.notification_sent = Some((Utc::now(), msg.clone())); + } + + // Ignore any possible error as the connection might have gone down + // already. + let nbr_addr = self.remote_addr; + let msg = NbrTxMsg::SendMessage { nbr_addr, msg }; + let _ = self.msg_txp.as_ref().unwrap().send(msg); + } + + // Sends a BGP OPEN message based on the local configuration. + fn open_send(&mut self, instance_cfg: &InstanceCfg, identifier: Ipv4Addr) { + // Base capabilities. + let mut capabilities: BTreeSet<_> = [ + Capability::RouteRefresh, + Capability::FourOctetAsNumber { + asn: instance_cfg.asn, + }, + ] + .into(); + + // Multiprotocol capabilities. + if let Some(afi_safi) = self.config.afi_safi.get(&AfiSafi::Ipv4Unicast) + && afi_safi.enabled + { + capabilities.insert(Capability::MultiProtocol { + afi: Afi::Ipv4, + safi: Safi::Unicast, + }); + } + if let Some(afi_safi) = self.config.afi_safi.get(&AfiSafi::Ipv6Unicast) + && afi_safi.enabled + { + capabilities.insert(Capability::MultiProtocol { + afi: Afi::Ipv6, + safi: Safi::Unicast, + }); + } + + // Keep track of the advertised capabilities. + self.capabilities_adv = capabilities.clone(); + + // Fill-in and send message. + let msg = Message::Open(OpenMsg { + version: BGP_VERSION, + my_as: instance_cfg.asn.try_into().unwrap_or(AS_TRANS), + holdtime: self.config.timers.holdtime, + identifier, + capabilities, + }); + self.message_send(msg); + } + + // Processes the received OPEN message while in the OpenSent state. + fn open_process( + &mut self, + instance: &InstanceUpView<'_>, + msg: OpenMsg, + ) -> fsm::State { + use crate::packet::consts::OpenMessageErrorSubcode as ErrorSubcode; + + // Validate the received message. + if let Err(error) = self.open_validate(instance, &msg) { + error.log(); + + // Close the session. + let msg = match error { + Error::NbrBadAs(..) => { + let error_code = ErrorCode::OpenMessageError; + let error_subcode = ErrorSubcode::BadPeerAs; + let msg = NotificationMsg::new(error_code, error_subcode); + Some(msg) + } + Error::NbrBadIdentifier(..) => { + let error_code = ErrorCode::OpenMessageError; + let error_subcode = ErrorSubcode::BadBgpIdentifier; + let msg = NotificationMsg::new(error_code, error_subcode); + Some(msg) + } + _ => None, + }; + self.session_close(msg); + + // Transition to the Idle state. + return fsm::State::Idle; + } + + // Calculate negotiated hold-time. + let holdtime_nego = + std::cmp::min(msg.holdtime, self.config.timers.holdtime); + + // Set the ConnectRetryTimer to zero. + self.connect_retry_stop(); + + // Send Keepalive message. + self.message_send(Message::Keepalive(KeepaliveMsg {})); + + // Start Keepalive interval and session hold timer. + if holdtime_nego != 0 { + self.keepalive_interval_start(holdtime_nego); + self.holdtime_start( + holdtime_nego, + &instance.tx.protocol_input.nbr_timer, + ); + } else { + self.holdtime_stop(); + } + + // Keep track of the received data. + self.identifier = Some(msg.identifier); + self.holdtime_nego = (holdtime_nego != 0).then_some(holdtime_nego); + self.capabilities_rcvd = msg.capabilities; + + // TODO: collision detection + + // Transition to the OpenConfirm state. + fsm::State::OpenConfirm + } + + // Performs semantic validation of the received BGP OPEN message. + // Syntactic errors are detected during the decoding phase. + fn open_validate( + &self, + instance: &InstanceUpView<'_>, + msg: &OpenMsg, + ) -> Result<(), Error> { + // Validate ASN. + if self.config.peer_as != msg.real_as() { + return Err(Error::NbrBadAs( + self.remote_addr, + msg.real_as(), + self.config.peer_as, + )); + } + + // Validate BGP identifier for internal peers. + if self.peer_type == PeerType::Internal + && msg.identifier == instance.state.router_id + { + return Err(Error::NbrBadIdentifier( + self.remote_addr, + msg.identifier, + )); + } + + Ok(()) + } + + // Returns the neighbor's Tx-TTL value based on the peer type and + // configuration. + pub(crate) fn tx_ttl(&self) -> u8 { + match self.peer_type { + PeerType::Internal => TTL_MAX, + PeerType::External => { + if self.config.transport.ttl_security.is_some() { + TTL_MAX + } else if self.config.transport.ebgp_multihop_enabled + && let Some(ttl) = self.config.transport.ebgp_multihop_ttl + { + ttl + } else { + 1 + } + } + } + } + + // Starts the auto-start timer. + fn autostart_start(&mut self, nbr_timerp: &Sender) { + let idle_hold_time = 1; + let task = tasks::nbr_timer( + self, + fsm::Timer::AutoStart, + idle_hold_time, + nbr_timerp, + ); + self.tasks.autostart = Some(task); + } + + // Stops the auto-start timer. + fn autostart_stop(&mut self) { + self.tasks.autostart = None; + } + + // Starts a TCP connection task to the neighbor's remote address. + fn connect(&mut self, tcp_connectp: &Sender) { + let task = tasks::tcp_connect(self, tcp_connectp); + self.tasks.connect = Some(task); + } + + // Starts the Keepalive Tx interval. + fn keepalive_interval_start(&mut self, holdtime_nego: u16) { + let interval = + self.config.timers.keepalive.unwrap_or(holdtime_nego / 3); + let task = tasks::nbr_kalive_interval(self, interval); + self.tasks.keepalive = Some(task); + } + + // Starts the session hold timer. + fn holdtime_start( + &mut self, + seconds: u16, + nbr_timerp: &Sender, + ) { + let task = + tasks::nbr_timer(self, fsm::Timer::Hold, seconds, nbr_timerp); + self.tasks.holdtime = Some(task); + } + + // Restarts the session hold timer if the negotiated HoldTime value is + // non-zero. + fn holdtime_restart(&mut self) { + if let Some(holdtime) = self.tasks.holdtime.as_mut() { + holdtime.reset(None); + } + } + + // Stops the session hold timer. + fn holdtime_stop(&mut self) { + self.tasks.holdtime = None; + } + + // Starts the connect retry timer. + fn connect_retry_start(&mut self, nbr_timerp: &Sender) { + let task = tasks::nbr_timer( + self, + fsm::Timer::ConnectRetry, + self.config.timers.connect_retry_interval, + nbr_timerp, + ); + self.tasks.connect_retry = Some(task); + } + + // Stops the connect retry timer. + fn connect_retry_stop(&mut self) { + self.tasks.connect_retry = None; + } +} + +// ===== impl MessageStatistics ===== + +impl MessageStatistics { + pub(crate) fn update(&mut self, msg: &Message) { + self.total.fetch_add(1, atomic::Ordering::Relaxed); + match msg { + Message::Update(_) => { + self.updates += 1; + } + Message::Notification(_) => { + self.notifications += 1; + } + Message::RouteRefresh(_) => { + self.route_refreshes += 1; + } + _ => {} + } + } +} diff --git a/holo-bgp/src/network.rs b/holo-bgp/src/network.rs new file mode 100644 index 00000000..c93c961b --- /dev/null +++ b/holo-bgp/src/network.rs @@ -0,0 +1,296 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::{IpAddr, SocketAddr}; +use std::sync::Arc; + +use holo_utils::ip::{AddressFamily, IpAddrExt, IpAddrKind}; +use holo_utils::socket::{ + OwnedReadHalf, OwnedWriteHalf, SocketExt, TcpConnInfo, TcpListener, + TcpSocket, TcpSocketExt, TcpStream, TcpStreamExt, TTL_MAX, +}; +use holo_utils::{capabilities, Sender, UnboundedReceiver}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::sync::mpsc::error::SendError; + +use crate::error::{Error, IoError, NbrRxError}; +use crate::packet::message::{DecodeCxt, EncodeCxt, Message}; +use crate::tasks::messages::input::{NbrRxMsg, TcpAcceptMsg}; +use crate::tasks::messages::output::NbrTxMsg; + +const BGP_PORT: u16 = 179; + +// ===== global functions ===== + +pub(crate) fn listen_socket( + af: AddressFamily, +) -> Result { + #[cfg(not(feature = "testing"))] + { + // Create TCP socket. + let socket = socket(af)?; + + // Bind socket. + let sockaddr = SocketAddr::from((IpAddr::unspecified(af), BGP_PORT)); + socket.set_reuseaddr(true)?; + capabilities::raise(|| socket.bind(sockaddr))?; + + // GTSM Procedure: set TTL to max for outgoing packets. + match af { + AddressFamily::Ipv4 => { + socket.set_ipv4_ttl(TTL_MAX)?; + } + AddressFamily::Ipv6 => { + socket.set_ipv6_unicast_hops(TTL_MAX)?; + } + } + + // Convert the socket into a TcpListener. + let socket = socket.listen(4096)?; + + Ok(socket) + } + #[cfg(feature = "testing")] + { + Ok(TcpListener {}) + } +} + +pub(crate) fn listen_socket_md5sig_update( + socket: &TcpListener, + nbr_addr: &IpAddr, + password: Option<&str>, +) { + #[cfg(not(feature = "testing"))] + { + if let Err(error) = socket.set_md5sig(nbr_addr, password) { + IoError::TcpAuthError(error).log(); + } + } +} + +#[cfg(not(feature = "testing"))] +pub(crate) async fn listen_loop( + listener: Arc, + tcp_acceptp: Sender, +) -> Result<(), SendError> { + loop { + match listener.accept().await { + Ok((stream, _)) => match stream.conn_info() { + Ok(conn_info) => { + let msg = TcpAcceptMsg { + stream: Some(stream), + conn_info, + }; + tcp_acceptp.send(msg).await?; + } + Err(error) => { + IoError::TcpInfoError(error).log(); + } + }, + Err(error) => { + IoError::TcpAcceptError(error).log(); + } + } + } +} + +pub(crate) fn accepted_stream_init( + stream: &TcpStream, + af: AddressFamily, + ttl: u8, + ttl_security: Option, + tcp_mss: Option, +) -> Result<(), std::io::Error> { + #[cfg(not(feature = "testing"))] + { + // Set TTL. + match af { + AddressFamily::Ipv4 => stream.set_ipv4_ttl(ttl)?, + AddressFamily::Ipv6 => stream.set_ipv6_unicast_hops(ttl)?, + } + + // Set TTL security check. + if let Some(ttl_security_hops) = ttl_security { + let ttl = TTL_MAX - ttl_security_hops + 1; + match af { + AddressFamily::Ipv4 => stream.set_ipv4_minttl(ttl)?, + AddressFamily::Ipv6 => stream.set_ipv6_min_hopcount(ttl)?, + } + } + + // Set the TCP Maximum Segment Size. + if let Some(tcp_mss) = tcp_mss { + stream.set_mss(tcp_mss.into())?; + } + } + + Ok(()) +} + +#[cfg(not(feature = "testing"))] +pub(crate) async fn connect( + remote_addr: IpAddr, + local_addr: Option, + ttl: u8, + ttl_security: Option, + tcp_mss: Option, + tcp_password: &Option, +) -> Result<(TcpStream, TcpConnInfo), Error> { + let af = remote_addr.address_family(); + + // Create TCP socket. + let socket = socket(af).map_err(IoError::TcpSocketError)?; + + // Bind socket. + if let Some(local_addr) = local_addr { + let sockaddr = SocketAddr::from((local_addr, 0)); + socket + .set_reuseaddr(true) + .map_err(IoError::TcpSocketError)?; + capabilities::raise(|| socket.bind(sockaddr)) + .map_err(IoError::TcpSocketError)?; + } + + // Set TTL. + match af { + AddressFamily::Ipv4 => socket.set_ipv4_ttl(ttl), + AddressFamily::Ipv6 => socket.set_ipv6_unicast_hops(ttl), + } + .map_err(IoError::TcpSocketError)?; + + // Set TTL security check. + if let Some(ttl_security_hops) = ttl_security { + let ttl = TTL_MAX - ttl_security_hops + 1; + match af { + AddressFamily::Ipv4 => socket.set_ipv4_minttl(ttl), + AddressFamily::Ipv6 => socket.set_ipv6_min_hopcount(ttl), + } + .map_err(IoError::TcpSocketError)?; + } + + // Set the TCP Maximum Segment Size. + if let Some(tcp_mss) = tcp_mss { + socket + .set_mss(tcp_mss.into()) + .map_err(IoError::TcpSocketError)?; + } + + // Set the TCP MD5 password. + if let Some(tcp_password) = tcp_password { + socket + .set_md5sig(&remote_addr, Some(tcp_password)) + .map_err(IoError::TcpAuthError)?; + } + + // Connect to remote address on the BGP port. + let sockaddr = SocketAddr::from((remote_addr, BGP_PORT)); + let stream = socket + .connect(sockaddr) + .await + .map_err(IoError::TcpConnectError)?; + + // Obtain TCP connection address/port information. + let conn_info = stream.conn_info().map_err(IoError::TcpInfoError)?; + + Ok((stream, conn_info)) +} + +#[cfg(not(feature = "testing"))] +pub(crate) async fn nbr_write_loop( + mut stream: OwnedWriteHalf, + mut cxt: EncodeCxt, + mut nbr_msg_txc: UnboundedReceiver, +) { + while let Some(msg) = nbr_msg_txc.recv().await { + match msg { + // Send message to the peer. + NbrTxMsg::SendMessage { msg, .. } => { + let buf = msg.encode(&cxt); + if let Err(error) = stream.write_all(&buf).await { + IoError::TcpSendError(error).log(); + } + } + // Update negotiated capabilities. + NbrTxMsg::UpdateCapabilities(caps) => cxt.capabilities = caps, + } + } +} + +#[cfg(not(feature = "testing"))] +pub(crate) async fn nbr_read_loop( + mut stream: OwnedReadHalf, + nbr_addr: IpAddr, + mut cxt: DecodeCxt, + nbr_msg_rxp: Sender, +) -> Result<(), SendError> { + const BUF_SIZE: usize = 65535; + let mut buf = [0; BUF_SIZE]; + let mut data = Vec::with_capacity(BUF_SIZE); + + loop { + // Read data from the network. + match stream.read(&mut buf).await { + Ok(0) => { + // Notify that the connection was closed by the remote end. + let msg = NbrRxMsg { + nbr_addr, + msg: Err(NbrRxError::TcpConnClosed(nbr_addr)), + }; + nbr_msg_rxp.send(msg).await?; + return Ok(()); + } + Ok(num_bytes) => data.extend_from_slice(&buf[..num_bytes]), + Err(error) => { + IoError::TcpRecvError(error).log(); + continue; + } + }; + + // Decode message(s). + while let Some(msg_size) = Message::get_message_len(&data) { + let msg = Message::decode(&data[0..msg_size], &cxt) + .map_err(|error| NbrRxError::MsgDecodeError(nbr_addr, error)); + data.drain(..msg_size); + + // Keep track of received capabilities as they influence how some + // messages should be decoded. + if let Ok(Message::Open(msg)) = &msg { + cxt.capabilities = msg.capabilities.clone(); + } + + // Notify that the BGP message was received. + let msg = NbrRxMsg { nbr_addr, msg }; + nbr_msg_rxp.send(msg).await?; + } + } +} + +// ===== helper functions ===== + +#[cfg(not(feature = "testing"))] +fn socket(af: AddressFamily) -> Result { + let socket = match af { + AddressFamily::Ipv4 => TcpSocket::new_v4()?, + AddressFamily::Ipv6 => { + let socket = TcpSocket::new_v6()?; + socket.set_ipv6_only(true)?; + socket + } + }; + + // Set socket options. + match af { + AddressFamily::Ipv4 => { + socket.set_ipv4_tos(libc::IPTOS_PREC_INTERNETCONTROL)?; + } + AddressFamily::Ipv6 => { + socket.set_ipv6_tclass(libc::IPTOS_PREC_INTERNETCONTROL)?; + } + } + + Ok(socket) +} diff --git a/holo-bgp/src/northbound/configuration.rs b/holo-bgp/src/northbound/configuration.rs new file mode 100644 index 00000000..d279dbe4 --- /dev/null +++ b/holo-bgp/src/northbound/configuration.rs @@ -0,0 +1,1501 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +#![allow(clippy::derivable_impls)] + +use std::collections::BTreeMap; +use std::net::{IpAddr, Ipv4Addr}; +use std::sync::LazyLock as Lazy; + +use async_trait::async_trait; +use enum_as_inner::EnumAsInner; +use holo_northbound::configuration::{ + Callbacks, CallbacksBuilder, Provider, ValidationCallbacks, + ValidationCallbacksBuilder, +}; +use holo_northbound::paths::control_plane_protocol::bgp; +use holo_utils::bgp::AfiSafi; +use holo_utils::ip::IpAddrKind; +use holo_utils::policy::{ApplyPolicyCfg, DefaultPolicyType}; +use holo_utils::yang::DataNodeRefExt; +use holo_yang::TryFromYang; + +use crate::instance::Instance; +use crate::neighbor::{fsm, Neighbor, PeerType}; +use crate::network; +use crate::packet::consts::{CeaseSubcode, ErrorCode}; +use crate::packet::message::NotificationMsg; + +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry { + #[default] + None, + AfiSafi(AfiSafi), + Neighbor(IpAddr), + NeighborAfiSafi(IpAddr, AfiSafi), +} + +#[derive(Debug)] +pub enum Resource {} + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum Event { + InstanceUpdate, + NeighborUpdate(IpAddr), + NeighborReset(IpAddr, NotificationMsg), + NeighborUpdateAuth(IpAddr), +} + +pub static VALIDATION_CALLBACKS: Lazy = + Lazy::new(load_validation_callbacks); +pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); + +// ===== configuration structs ===== + +#[derive(Debug)] +pub struct InstanceCfg { + pub asn: u32, + pub identifier: Option, + pub distance_external: u8, + pub distance_internal: u8, + pub multipath: InstanceMultipathCfg, + pub route_selection: RouteSelectionCfg, + pub apply_policy: ApplyPolicyCfg, + pub afi_safi: BTreeMap, +} + +#[derive(Debug)] +pub struct InstanceMultipathCfg { + pub enabled: bool, + pub ebgp_allow_multiple_as: bool, + pub ebgp_maximum_paths: u32, + pub ibgp_maximum_paths: u32, +} + +#[derive(Debug)] +pub struct InstanceAfiSafiCfg { + pub enabled: bool, + pub multipath: InstanceMultipathCfg, + pub route_selection: RouteSelectionCfg, + pub prefix_limit: PrefixLimitCfg, + pub send_default_route: bool, + pub apply_policy: ApplyPolicyCfg, +} + +#[derive(Debug)] +pub struct NeighborCfg { + pub enabled: bool, + pub peer_as: u32, + pub local_as: Option, + pub private_as_remove: Option, + pub timers: NeighborTimersCfg, + pub transport: NeighborTransportCfg, + pub treat_as_withdraw: bool, + pub log_neighbor_state_changes: bool, + pub as_path_options: AsPathOptions, + pub multipath: NeighborMultipathCfg, + pub apply_policy: ApplyPolicyCfg, + pub prefix_limit: PrefixLimitCfg, + pub afi_safi: BTreeMap, +} + +#[derive(Debug)] +pub struct NeighborMultipathCfg { + pub enabled: bool, + pub ebgp_allow_multiple_as: bool, +} + +#[derive(Debug)] +pub struct NeighborTimersCfg { + pub connect_retry_interval: u16, + pub holdtime: u16, + pub keepalive: Option, + pub min_as_orig_interval: Option, + pub min_route_adv_interval: Option, +} + +#[derive(Debug)] +pub struct NeighborTransportCfg { + // TODO: this can be an interface name too. + pub local_addr: Option, + pub tcp_mss: Option, + pub ebgp_multihop_enabled: bool, + pub ebgp_multihop_ttl: Option, + pub passive_mode: bool, + pub ttl_security: Option, + pub secure_session_enabled: bool, + pub md5_key: Option, +} + +#[derive(Debug)] +pub struct NeighborAfiSafiCfg { + pub enabled: bool, + pub multipath: NeighborMultipathCfg, + pub prefix_limit: PrefixLimitCfg, + pub send_default_route: bool, + pub apply_policy: ApplyPolicyCfg, +} + +#[derive(Debug)] +pub struct RouteSelectionCfg { + pub always_compare_med: bool, + pub ignore_as_path_length: bool, + pub external_compare_router_id: bool, + pub ignore_next_hop_igp_metric: bool, + pub enable_med: bool, +} + +#[derive(Debug)] +pub struct PrefixLimitCfg { + pub max_prefixes: Option, + pub warning_threshold_pct: Option, + pub teardown: bool, + pub idle_time: Option, +} + +#[derive(Debug)] +pub struct AsPathOptions { + pub allow_own_as: u8, + pub replace_peer_as: bool, + pub disable_peer_as_filter: bool, +} + +#[derive(Debug)] +pub enum PrivateAsRemove { + RemoveAll, + ReplaceAll, +} + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + CallbacksBuilder::::default() + .path(bgp::global::PATH) + .create_apply(|_instance, _args| { + }) + .delete_apply(|_instance, _args| { + }) + .path(bgp::global::r#as::PATH) + .modify_apply(|instance, args| { + let asn = args.dnode.get_u32(); + instance.config.asn = asn; + + let event_queue = args.event_queue; + event_queue.insert(Event::InstanceUpdate); + }) + .path(bgp::global::identifier::PATH) + .modify_apply(|instance, args| { + let identifier = args.dnode.get_ipv4(); + instance.config.identifier = Some(identifier); + + let event_queue = args.event_queue; + event_queue.insert(Event::InstanceUpdate); + }) + .delete_apply(|instance, args| { + instance.config.identifier = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::InstanceUpdate); + }) + .path(bgp::global::distance::external::PATH) + .modify_apply(|instance, args| { + let distance = args.dnode.get_u8(); + instance.config.distance_external = distance; + }) + .path(bgp::global::distance::internal::PATH) + .modify_apply(|instance, args| { + let distance = args.dnode.get_u8(); + instance.config.distance_internal = distance; + }) + .path(bgp::global::use_multiple_paths::enabled::PATH) + .modify_apply(|instance, args| { + let enabled = args.dnode.get_bool(); + instance.config.multipath.enabled = enabled; + }) + .path(bgp::global::use_multiple_paths::ebgp::allow_multiple_as::PATH) + .modify_apply(|instance, args| { + let allow = args.dnode.get_bool(); + instance.config.multipath.ebgp_allow_multiple_as = allow; + }) + .path(bgp::global::use_multiple_paths::ebgp::maximum_paths::PATH) + .modify_apply(|instance, args| { + let max = args.dnode.get_u32(); + instance.config.multipath.ebgp_maximum_paths = max; + }) + .path(bgp::global::use_multiple_paths::ibgp::maximum_paths::PATH) + .modify_apply(|instance, args| { + let max = args.dnode.get_u32(); + instance.config.multipath.ibgp_maximum_paths = max; + }) + .path(bgp::global::route_selection_options::always_compare_med::PATH) + .modify_apply(|instance, args| { + let compare = args.dnode.get_bool(); + instance.config.route_selection.always_compare_med = compare; + }) + .path(bgp::global::route_selection_options::ignore_as_path_length::PATH) + .modify_apply(|instance, args| { + let ignore = args.dnode.get_bool(); + instance.config.route_selection.ignore_as_path_length = ignore; + }) + .path(bgp::global::route_selection_options::external_compare_router_id::PATH) + .modify_apply(|instance, args| { + let compare = args.dnode.get_bool(); + instance.config.route_selection.external_compare_router_id = compare; + }) + .path(bgp::global::route_selection_options::ignore_next_hop_igp_metric::PATH) + .modify_apply(|instance, args| { + let ignore = args.dnode.get_bool(); + instance.config.route_selection.ignore_next_hop_igp_metric = ignore; + }) + .path(bgp::global::route_selection_options::enable_med::PATH) + .modify_apply(|instance, args| { + let enable = args.dnode.get_bool(); + instance.config.route_selection.enable_med = enable; + }) + .path(bgp::global::afi_safis::afi_safi::PATH) + .create_apply(|instance, args| { + let afi_safi = args.dnode.get_string_relative("./name").unwrap(); + let afi_safi = AfiSafi::try_from_yang(&afi_safi).unwrap(); + instance.config.afi_safi.insert(afi_safi, Default::default()); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + + instance.config.afi_safi.remove(&afi_safi); + }) + .lookup(|_instance, _list_entry, dnode| { + let afi_safi = dnode.get_string_relative("./name").unwrap(); + let afi_safi = AfiSafi::try_from_yang(&afi_safi).unwrap(); + ListEntry::AfiSafi(afi_safi) + }) + .path(bgp::global::afi_safis::afi_safi::enabled::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let enabled = args.dnode.get_bool(); + afi_safi.enabled = enabled; + }) + .path(bgp::global::afi_safis::afi_safi::route_selection_options::always_compare_med::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let compare = args.dnode.get_bool(); + afi_safi.route_selection.always_compare_med = compare; + }) + .path(bgp::global::afi_safis::afi_safi::route_selection_options::ignore_as_path_length::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let ignore = args.dnode.get_bool(); + afi_safi.route_selection.ignore_as_path_length = ignore; + }) + .path(bgp::global::afi_safis::afi_safi::route_selection_options::external_compare_router_id::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let compare = args.dnode.get_bool(); + afi_safi.route_selection.external_compare_router_id = compare; + }) + .path(bgp::global::afi_safis::afi_safi::route_selection_options::ignore_next_hop_igp_metric::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let ignore = args.dnode.get_bool(); + afi_safi.route_selection.ignore_next_hop_igp_metric = ignore; + }) + .path(bgp::global::afi_safis::afi_safi::route_selection_options::enable_med::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let enable = args.dnode.get_bool(); + afi_safi.route_selection.enable_med = enable; + }) + .path(bgp::global::afi_safis::afi_safi::use_multiple_paths::enabled::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let enabled = args.dnode.get_bool(); + afi_safi.multipath.enabled = enabled; + }) + .path(bgp::global::afi_safis::afi_safi::use_multiple_paths::ebgp::allow_multiple_as::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let allow = args.dnode.get_bool(); + afi_safi.multipath.ebgp_allow_multiple_as = allow; + }) + .path(bgp::global::afi_safis::afi_safi::use_multiple_paths::ebgp::maximum_paths::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let max = args.dnode.get_u32(); + afi_safi.multipath.ebgp_maximum_paths = max; + }) + .path(bgp::global::afi_safis::afi_safi::use_multiple_paths::ibgp::maximum_paths::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let max = args.dnode.get_u32(); + afi_safi.multipath.ibgp_maximum_paths = max; + }) + .path(bgp::global::afi_safis::afi_safi::apply_policy::import_policy::PATH) + .create_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.import_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.import_policy.remove(&policy); + }) + .path(bgp::global::afi_safis::afi_safi::apply_policy::default_import_policy::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + afi_safi.apply_policy.default_import_policy = default; + }) + .path(bgp::global::afi_safis::afi_safi::apply_policy::export_policy::PATH) + .create_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.export_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.export_policy.remove(&policy); + }) + .path(bgp::global::afi_safis::afi_safi::apply_policy::default_export_policy::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + afi_safi.apply_policy.default_export_policy = default; + }) + .path(bgp::global::afi_safis::afi_safi::ipv4_unicast::prefix_limit::max_prefixes::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let max = args.dnode.get_u32(); + afi_safi.prefix_limit.max_prefixes = Some(max); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.max_prefixes = None; + }) + .path(bgp::global::afi_safis::afi_safi::ipv4_unicast::prefix_limit::warning_threshold_pct::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let threshold = args.dnode.get_u8(); + afi_safi.prefix_limit.warning_threshold_pct = Some(threshold); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.warning_threshold_pct = None; + }) + .path(bgp::global::afi_safis::afi_safi::ipv4_unicast::prefix_limit::teardown::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let teardown = args.dnode.get_bool(); + afi_safi.prefix_limit.teardown = teardown; + }) + .path(bgp::global::afi_safis::afi_safi::ipv4_unicast::prefix_limit::idle_time::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let idle_time = args.dnode.get_string(); + let idle_time: u32 = idle_time.parse().unwrap(); + afi_safi.prefix_limit.idle_time = Some(idle_time); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.idle_time = None; + }) + .path(bgp::global::afi_safis::afi_safi::ipv4_unicast::send_default_route::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let send = args.dnode.get_bool(); + afi_safi.send_default_route = send; + }) + .path(bgp::global::afi_safis::afi_safi::ipv6_unicast::prefix_limit::max_prefixes::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let max = args.dnode.get_u32(); + afi_safi.prefix_limit.max_prefixes = Some(max); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.max_prefixes = None; + }) + .path(bgp::global::afi_safis::afi_safi::ipv6_unicast::prefix_limit::warning_threshold_pct::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let threshold = args.dnode.get_u8(); + afi_safi.prefix_limit.warning_threshold_pct = Some(threshold); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.warning_threshold_pct = None; + }) + .path(bgp::global::afi_safis::afi_safi::ipv6_unicast::prefix_limit::teardown::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let teardown = args.dnode.get_bool(); + afi_safi.prefix_limit.teardown = teardown; + }) + .path(bgp::global::afi_safis::afi_safi::ipv6_unicast::prefix_limit::idle_time::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let idle_time = args.dnode.get_string(); + let idle_time: u32 = idle_time.parse().unwrap(); + afi_safi.prefix_limit.idle_time = Some(idle_time); + }) + .delete_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.idle_time = None; + }) + .path(bgp::global::afi_safis::afi_safi::ipv6_unicast::send_default_route::PATH) + .modify_apply(|instance, args| { + let afi_safi = args.list_entry.into_afi_safi().unwrap(); + let afi_safi = instance.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let send = args.dnode.get_bool(); + afi_safi.send_default_route = send; + }) + .path(bgp::global::apply_policy::import_policy::PATH) + .create_apply(|instance, args| { + let policy = args.dnode.get_string(); + instance.config.apply_policy.import_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let policy = args.dnode.get_string(); + instance.config.apply_policy.import_policy.remove(&policy); + }) + .path(bgp::global::apply_policy::default_import_policy::PATH) + .modify_apply(|instance, args| { + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + instance.config.apply_policy.default_import_policy = default; + }) + .path(bgp::global::apply_policy::export_policy::PATH) + .create_apply(|instance, args| { + let policy = args.dnode.get_string(); + instance.config.apply_policy.export_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let policy = args.dnode.get_string(); + instance.config.apply_policy.export_policy.remove(&policy); + }) + .path(bgp::global::apply_policy::default_export_policy::PATH) + .modify_apply(|instance, args| { + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + instance.config.apply_policy.default_export_policy = default; + }) + .path(bgp::neighbors::neighbor::PATH) + .create_apply(|instance, args| { + let nbr_addr = args.dnode.get_ip_relative("./remote-address").unwrap(); + let peer_as = args.dnode.get_u32_relative("./peer-as").unwrap(); + + let peer_type = if instance.config.asn == peer_as { + PeerType::Internal + } else { + PeerType::External + }; + let nbr = Neighbor::new(nbr_addr, peer_type); + instance.neighbors.insert(nbr_addr, nbr); + + let event_queue = args.event_queue; + event_queue.insert(Event::NeighborUpdate(nbr_addr)); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + + instance.neighbors.remove(&nbr_addr); + + // TODO: neighbor delete event + // TODO: unset md5 password + }) + .lookup(|_instance, _list_entry, dnode| { + let nbr_addr = dnode.get_ip_relative("./remote-address").unwrap(); + ListEntry::Neighbor(nbr_addr) + }) + .path(bgp::neighbors::neighbor::enabled::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let enabled = args.dnode.get_bool(); + nbr.config.enabled = enabled; + }) + .path(bgp::neighbors::neighbor::peer_as::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let asn = args.dnode.get_u32(); + nbr.config.peer_as = asn; + nbr.peer_type = if instance.config.asn == nbr.config.peer_as { + PeerType::Internal + } else { + PeerType::External + }; + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .path(bgp::neighbors::neighbor::local_as::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let asn = args.dnode.get_u32(); + nbr.config.local_as = Some(asn); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.local_as = None; + }) + .path(bgp::neighbors::neighbor::remove_private_as::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let private_as_remove = args.dnode.get_string(); + let private_as_remove = PrivateAsRemove::try_from_yang(&private_as_remove).unwrap(); + nbr.config.private_as_remove = Some(private_as_remove); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.private_as_remove = None; + }) + .path(bgp::neighbors::neighbor::description::PATH) + .modify_apply(|_instance, _args| { + // Nothing to do. + }) + .delete_apply(|_instance, _args| { + // Nothing to do. + }) + .path(bgp::neighbors::neighbor::timers::connect_retry_interval::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let interval = args.dnode.get_u16(); + nbr.config.timers.connect_retry_interval = interval; + }) + .path(bgp::neighbors::neighbor::timers::hold_time::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let holdtime = args.dnode.get_u16(); + nbr.config.timers.holdtime = holdtime; + }) + .path(bgp::neighbors::neighbor::timers::keepalive::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let keepalive = args.dnode.get_u16(); + nbr.config.timers.keepalive = Some(keepalive); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.timers.keepalive = None; + }) + .path(bgp::neighbors::neighbor::timers::min_as_origination_interval::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let interval = args.dnode.get_u16(); + nbr.config.timers.min_as_orig_interval = Some(interval); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.timers.min_as_orig_interval = None; + }) + .path(bgp::neighbors::neighbor::timers::min_route_advertisement_interval::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let interval = args.dnode.get_u16(); + nbr.config.timers.min_route_adv_interval = Some(interval); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.timers.min_route_adv_interval = None; + }) + .path(bgp::neighbors::neighbor::transport::local_address::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let addr = args.dnode.get_ip(); + nbr.config.transport.local_addr = Some(addr); + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.transport.local_addr = None; + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .path(bgp::neighbors::neighbor::transport::tcp_mss::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let tcp_mss = args.dnode.get_u16(); + nbr.config.transport.tcp_mss = Some(tcp_mss); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.transport.tcp_mss = None; + }) + .path(bgp::neighbors::neighbor::transport::ebgp_multihop::enabled::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let enabled = args.dnode.get_bool(); + nbr.config.transport.ebgp_multihop_enabled = enabled; + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .path(bgp::neighbors::neighbor::transport::ebgp_multihop::multihop_ttl::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let ttl = args.dnode.get_u8(); + nbr.config.transport.ebgp_multihop_ttl = Some(ttl); + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.transport.ebgp_multihop_ttl = None; + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .path(bgp::neighbors::neighbor::transport::passive_mode::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let passive_mode = args.dnode.get_bool(); + nbr.config.transport.passive_mode = passive_mode; + }) + .path(bgp::neighbors::neighbor::transport::ttl_security::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let ttl_security = args.dnode.get_u8(); + nbr.config.transport.ttl_security = Some(ttl_security); + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + }) + .path(bgp::neighbors::neighbor::transport::secure_session::enabled::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let enabled = args.dnode.get_bool(); + nbr.config.transport.secure_session_enabled = enabled; + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + event_queue.insert(Event::NeighborUpdateAuth(nbr.remote_addr)); + }) + .path(bgp::neighbors::neighbor::transport::secure_session::options::md5_key_string::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let keychain = args.dnode.get_string(); + nbr.config.transport.md5_key = Some(keychain); + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + event_queue.insert(Event::NeighborUpdateAuth(nbr.remote_addr)); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.transport.md5_key = None; + + let event_queue = args.event_queue; + let msg = NotificationMsg::new( + ErrorCode::Cease, + CeaseSubcode::OtherConfigurationChange, + ); + event_queue.insert(Event::NeighborReset(nbr.remote_addr, msg)); + event_queue.insert(Event::NeighborUpdateAuth(nbr.remote_addr)); + }) + .path(bgp::neighbors::neighbor::treat_as_withdraw::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let treat_as_withdraw = args.dnode.get_bool(); + nbr.config.treat_as_withdraw = treat_as_withdraw; + }) + .path(bgp::neighbors::neighbor::logging_options::log_neighbor_state_changes::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let log = args.dnode.get_bool(); + nbr.config.log_neighbor_state_changes = log; + }) + .path(bgp::neighbors::neighbor::as_path_options::allow_own_as::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let allow = args.dnode.get_u8(); + nbr.config.as_path_options.allow_own_as = allow; + }) + .path(bgp::neighbors::neighbor::as_path_options::replace_peer_as::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let replace = args.dnode.get_bool(); + nbr.config.as_path_options.replace_peer_as = replace; + }) + .path(bgp::neighbors::neighbor::as_path_options::disable_peer_as_filter::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let disable = args.dnode.get_bool(); + nbr.config.as_path_options.disable_peer_as_filter = disable; + }) + .path(bgp::neighbors::neighbor::use_multiple_paths::enabled::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let enabled = args.dnode.get_bool(); + nbr.config.multipath.enabled = enabled; + }) + .path(bgp::neighbors::neighbor::use_multiple_paths::ebgp::allow_multiple_as::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let allow = args.dnode.get_bool(); + nbr.config.multipath.ebgp_allow_multiple_as = allow; + }) + .path(bgp::neighbors::neighbor::apply_policy::import_policy::PATH) + .create_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let policy = args.dnode.get_string(); + nbr.config.apply_policy.import_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let policy = args.dnode.get_string(); + nbr.config.apply_policy.import_policy.remove(&policy); + }) + .path(bgp::neighbors::neighbor::apply_policy::default_import_policy::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + nbr.config.apply_policy.default_import_policy = default; + }) + .path(bgp::neighbors::neighbor::apply_policy::export_policy::PATH) + .create_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let policy = args.dnode.get_string(); + nbr.config.apply_policy.export_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let policy = args.dnode.get_string(); + nbr.config.apply_policy.export_policy.remove(&policy); + }) + .path(bgp::neighbors::neighbor::apply_policy::default_export_policy::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + nbr.config.apply_policy.default_export_policy = default; + }) + .path(bgp::neighbors::neighbor::prefix_limit::max_prefixes::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let max = args.dnode.get_u32(); + nbr.config.prefix_limit.max_prefixes = Some(max); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.prefix_limit.max_prefixes = None; + }) + .path(bgp::neighbors::neighbor::prefix_limit::warning_threshold_pct::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let threshold = args.dnode.get_u8(); + nbr.config.prefix_limit.warning_threshold_pct = Some(threshold); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.prefix_limit.warning_threshold_pct = None; + }) + .path(bgp::neighbors::neighbor::prefix_limit::teardown::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let teardown = args.dnode.get_bool(); + nbr.config.prefix_limit.teardown = teardown; + }) + .path(bgp::neighbors::neighbor::prefix_limit::idle_time::PATH) + .modify_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let idle_time = args.dnode.get_string(); + let idle_time: u32 = idle_time.parse().unwrap(); + nbr.config.prefix_limit.idle_time = Some(idle_time); + }) + .delete_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.config.prefix_limit.idle_time = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::PATH) + .create_apply(|instance, args| { + let nbr_addr = args.list_entry.into_neighbor().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + + let afi_safi = args.dnode.get_string_relative("./name").unwrap(); + let afi_safi = AfiSafi::try_from_yang(&afi_safi).unwrap(); + nbr.config.afi_safi.insert(afi_safi, Default::default()); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + nbr.config.afi_safi.remove(&afi_safi); + }) + .lookup(|_instance, list_entry, dnode| { + let nbr_addr = list_entry.into_neighbor().unwrap(); + let afi_safi = dnode.get_string_relative("./name").unwrap(); + let afi_safi = AfiSafi::try_from_yang(&afi_safi).unwrap(); + ListEntry::NeighborAfiSafi(nbr_addr, afi_safi) + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::enabled::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let enabled = args.dnode.get_bool(); + afi_safi.enabled = enabled; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::apply_policy::import_policy::PATH) + .create_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.import_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.import_policy.remove(&policy); + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::apply_policy::default_import_policy::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + afi_safi.apply_policy.default_import_policy = default; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::apply_policy::export_policy::PATH) + .create_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.export_policy.insert(policy); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let policy = args.dnode.get_string(); + afi_safi.apply_policy.export_policy.remove(&policy); + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::apply_policy::default_export_policy::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let default = args.dnode.get_string(); + let default = DefaultPolicyType::try_from_yang(&default).unwrap(); + afi_safi.apply_policy.default_export_policy = default; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv4_unicast::prefix_limit::max_prefixes::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let max = args.dnode.get_u32(); + afi_safi.prefix_limit.max_prefixes = Some(max); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.max_prefixes = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv4_unicast::prefix_limit::warning_threshold_pct::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let threshold = args.dnode.get_u8(); + afi_safi.prefix_limit.warning_threshold_pct = Some(threshold); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.warning_threshold_pct = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv4_unicast::prefix_limit::teardown::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let teardown = args.dnode.get_bool(); + afi_safi.prefix_limit.teardown = teardown; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv4_unicast::prefix_limit::idle_time::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let idle_time = args.dnode.get_string(); + let idle_time: u32 = idle_time.parse().unwrap(); + afi_safi.prefix_limit.idle_time = Some(idle_time); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.idle_time = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv4_unicast::send_default_route::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let send = args.dnode.get_bool(); + afi_safi.send_default_route = send; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv6_unicast::prefix_limit::max_prefixes::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let max = args.dnode.get_u32(); + afi_safi.prefix_limit.max_prefixes = Some(max); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.max_prefixes = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv6_unicast::prefix_limit::warning_threshold_pct::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let threshold = args.dnode.get_u8(); + afi_safi.prefix_limit.warning_threshold_pct = Some(threshold); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.warning_threshold_pct = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv6_unicast::prefix_limit::teardown::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let teardown = args.dnode.get_bool(); + afi_safi.prefix_limit.teardown = teardown; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv6_unicast::prefix_limit::idle_time::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let idle_time = args.dnode.get_string(); + let idle_time: u32 = idle_time.parse().unwrap(); + afi_safi.prefix_limit.idle_time = Some(idle_time); + }) + .delete_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + afi_safi.prefix_limit.idle_time = None; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv6_unicast::send_default_route::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let send = args.dnode.get_bool(); + afi_safi.send_default_route = send; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::use_multiple_paths::enabled::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let enabled = args.dnode.get_bool(); + afi_safi.multipath.enabled = enabled; + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::use_multiple_paths::ebgp::allow_multiple_as::PATH) + .modify_apply(|instance, args| { + let (nbr_addr, afi_safi) = args.list_entry.into_neighbor_afi_safi().unwrap(); + let nbr = instance.neighbors.get_mut(&nbr_addr).unwrap(); + let afi_safi = nbr.config.afi_safi.get_mut(&afi_safi).unwrap(); + + let allow = args.dnode.get_bool(); + afi_safi.multipath.ebgp_allow_multiple_as = allow; + }) + .build() +} + +fn load_validation_callbacks() -> ValidationCallbacks { + ValidationCallbacksBuilder::default().build() +} + +// ===== impl Instance ===== + +#[async_trait] +impl Provider for Instance { + type ListEntry = ListEntry; + type Event = Event; + type Resource = Resource; + + fn validation_callbacks() -> Option<&'static ValidationCallbacks> { + Some(&VALIDATION_CALLBACKS) + } + + fn callbacks() -> Option<&'static Callbacks> { + Some(&CALLBACKS) + } + + async fn process_event(&mut self, event: Event) { + match event { + Event::InstanceUpdate => self.update().await, + Event::NeighborUpdate(nbr_addr) => { + let Some((mut instance, neighbors)) = self.as_up() else { + return; + }; + let nbr = neighbors.get_mut(&nbr_addr).unwrap(); + + if nbr.config.enabled { + nbr.fsm_event(&mut instance, fsm::Event::Start); + } else { + let error_code = ErrorCode::Cease; + let error_subcode = CeaseSubcode::AdministrativeShutdown; + let msg = NotificationMsg::new(error_code, error_subcode); + nbr.fsm_event(&mut instance, fsm::Event::Stop(Some(msg))); + } + } + Event::NeighborReset(nbr_addr, msg) => { + let Some((mut instance, neighbors)) = self.as_up() else { + return; + }; + let nbr = neighbors.get_mut(&nbr_addr).unwrap(); + + nbr.fsm_event(&mut instance, fsm::Event::Stop(Some(msg))); + } + Event::NeighborUpdateAuth(nbr_addr) => { + let Some((instance, neighbors)) = self.as_up() else { + return; + }; + let nbr = neighbors.get_mut(&nbr_addr).unwrap(); + + // Get neighbor password. + let key = if nbr.config.transport.secure_session_enabled + && let Some(key) = &nbr.config.transport.md5_key + { + Some(key.clone()) + } else { + None + }; + + // Set/unset password in the listening sockets. + for listener in + instance.state.listening_sockets.iter().filter(|listener| { + listener.af == nbr_addr.address_family() + }) + { + network::listen_socket_md5sig_update( + &listener.socket, + &nbr_addr, + key.as_deref(), + ); + } + } + } + } +} + +// ===== configuration defaults ===== + +impl Default for InstanceCfg { + fn default() -> InstanceCfg { + let distance_external = bgp::global::distance::external::DFLT; + let distance_internal = bgp::global::distance::internal::DFLT; + + InstanceCfg { + asn: 0, + identifier: None, + distance_external, + distance_internal, + multipath: Default::default(), + route_selection: Default::default(), + apply_policy: Default::default(), + afi_safi: Default::default(), + } + } +} + +impl Default for InstanceMultipathCfg { + fn default() -> InstanceMultipathCfg { + let enabled = bgp::global::use_multiple_paths::enabled::DFLT; + let ebgp_allow_multiple_as = + bgp::global::use_multiple_paths::ebgp::allow_multiple_as::DFLT; + let ebgp_maximum_paths = + bgp::global::use_multiple_paths::ebgp::maximum_paths::DFLT; + let ibgp_maximum_paths = + bgp::global::use_multiple_paths::ibgp::maximum_paths::DFLT; + + InstanceMultipathCfg { + enabled, + ebgp_allow_multiple_as, + ebgp_maximum_paths, + ibgp_maximum_paths, + } + } +} + +impl Default for InstanceAfiSafiCfg { + fn default() -> InstanceAfiSafiCfg { + // TODO: fetch defaults from YANG module + InstanceAfiSafiCfg { + enabled: false, + multipath: Default::default(), + route_selection: Default::default(), + prefix_limit: Default::default(), + send_default_route: false, + apply_policy: Default::default(), + } + } +} + +impl Default for NeighborCfg { + fn default() -> NeighborCfg { + let enabled = bgp::neighbors::neighbor::enabled::DFLT; + let treat_as_withdraw = + bgp::neighbors::neighbor::treat_as_withdraw::DFLT; + let log_neighbor_state_changes = + bgp::neighbors::neighbor::logging_options::log_neighbor_state_changes::DFLT; + + NeighborCfg { + enabled, + peer_as: 0, + local_as: None, + private_as_remove: None, + timers: Default::default(), + transport: Default::default(), + treat_as_withdraw, + log_neighbor_state_changes, + as_path_options: Default::default(), + multipath: Default::default(), + apply_policy: Default::default(), + prefix_limit: Default::default(), + afi_safi: Default::default(), + } + } +} + +impl Default for NeighborMultipathCfg { + fn default() -> NeighborMultipathCfg { + // TODO: fetch defaults from YANG module + NeighborMultipathCfg { + enabled: false, + ebgp_allow_multiple_as: false, + } + } +} + +impl Default for NeighborTimersCfg { + fn default() -> NeighborTimersCfg { + let connect_retry_interval = + bgp::neighbors::neighbor::timers::connect_retry_interval::DFLT; + let holdtime = bgp::neighbors::neighbor::timers::hold_time::DFLT; + + NeighborTimersCfg { + connect_retry_interval, + holdtime, + keepalive: None, + min_as_orig_interval: None, + min_route_adv_interval: None, + } + } +} + +impl Default for NeighborTransportCfg { + fn default() -> NeighborTransportCfg { + let ebgp_multihop_enabled = + bgp::neighbors::neighbor::transport::ebgp_multihop::enabled::DFLT; + let passive_mode = + bgp::neighbors::neighbor::transport::passive_mode::DFLT; + let secure_session_enabled = + bgp::neighbors::neighbor::transport::secure_session::enabled::DFLT; + + NeighborTransportCfg { + local_addr: None, + tcp_mss: None, + ebgp_multihop_enabled, + ebgp_multihop_ttl: None, + passive_mode, + ttl_security: None, + secure_session_enabled, + md5_key: None, + } + } +} + +impl Default for NeighborAfiSafiCfg { + fn default() -> NeighborAfiSafiCfg { + let enabled = + bgp::neighbors::neighbor::afi_safis::afi_safi::enabled::DFLT; + + NeighborAfiSafiCfg { + enabled, + multipath: Default::default(), + prefix_limit: Default::default(), + send_default_route: false, + apply_policy: Default::default(), + } + } +} + +impl Default for RouteSelectionCfg { + fn default() -> RouteSelectionCfg { + // TODO: fetch defaults from YANG module + RouteSelectionCfg { + always_compare_med: false, + ignore_as_path_length: false, + external_compare_router_id: true, + ignore_next_hop_igp_metric: false, + enable_med: false, + } + } +} + +impl Default for PrefixLimitCfg { + fn default() -> PrefixLimitCfg { + // TODO: fetch defaults from YANG module + PrefixLimitCfg { + max_prefixes: None, + warning_threshold_pct: None, + teardown: false, + idle_time: None, + } + } +} + +impl Default for AsPathOptions { + fn default() -> AsPathOptions { + // TODO: fetch defaults from YANG module + AsPathOptions { + allow_own_as: 0, + replace_peer_as: false, + disable_peer_as_filter: false, + } + } +} diff --git a/holo-bgp/src/northbound/mod.rs b/holo-bgp/src/northbound/mod.rs new file mode 100644 index 00000000..0dfe9b63 --- /dev/null +++ b/holo-bgp/src/northbound/mod.rs @@ -0,0 +1,36 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +pub mod configuration; +pub mod rpc; +pub mod state; +pub mod yang; + +use holo_northbound::ProviderBase; +use holo_utils::protocol::Protocol; +use holo_yang::ToYang; +use tracing::{debug_span, Span}; + +use crate::instance::Instance; + +// ===== impl Instance ===== + +impl ProviderBase for Instance { + fn yang_modules() -> &'static [&'static str] { + &["ietf-bgp", "holo-bgp"] + } + + fn top_level_node(&self) -> String { + format!( + "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='{}'][name='main']/ietf-bgp:bgp", + Protocol::BGP.to_yang(), + ) + } + + fn debug_span(name: &str) -> Span { + debug_span!("bgp-instance", %name) + } +} diff --git a/holo-bgp/src/northbound/rpc.rs b/holo-bgp/src/northbound/rpc.rs new file mode 100644 index 00000000..6cdb875f --- /dev/null +++ b/holo-bgp/src/northbound/rpc.rs @@ -0,0 +1,30 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::sync::LazyLock as Lazy; + +use holo_northbound::rpc::{Callbacks, CallbacksBuilder, Provider}; + +//use holo_utils::yang::DataNodeRefExt; +//use yang2::data::Data; +use crate::instance::Instance; + +pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + // TODO: YANG actions are not supported yet. + CallbacksBuilder::::default().build() +} + +// ===== impl Instance ===== + +impl Provider for Instance { + fn callbacks() -> Option<&'static Callbacks> { + Some(&CALLBACKS) + } +} diff --git a/holo-bgp/src/northbound/state.rs b/holo-bgp/src/northbound/state.rs new file mode 100644 index 00000000..17c2cba1 --- /dev/null +++ b/holo-bgp/src/northbound/state.rs @@ -0,0 +1,1797 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; +use std::sync::{atomic, Arc, LazyLock as Lazy}; + +use enum_as_inner::EnumAsInner; +use holo_northbound::paths::control_plane_protocol::bgp; +use holo_northbound::state::{ + Callbacks, CallbacksBuilder, ListEntryKind, NodeAttributes, Provider, +}; +use holo_utils::bgp::AfiSafi; +use holo_yang::ToYang; +use ipnetwork::{Ipv4Network, Ipv6Network}; +use itertools::Itertools; + +use crate::instance::Instance; +use crate::neighbor::Neighbor; +use crate::packet::attribute::{ + AsPathSegment, BaseAttrs, Comm, Comms, ExtComm, ExtComms, Extv6Comm, + Extv6Comms, LargeComm, LargeComms, UnknownAttr, +}; +use crate::packet::consts::{Afi, AttrFlags, Safi}; +use crate::packet::message::{AddPathTuple, Capability}; +use crate::rib::{AttrSet, Route}; + +pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); + +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry<'a> { + #[default] + None, + Neighbor(&'a Neighbor), + CapabilityAdv(usize, &'a Capability), + CapabilityRcvd(usize, &'a Capability), + CapabilityNego(String), + AddPathTuple(&'a AddPathTuple), + Rib(AfiSafi), + RibBaseAttrs(&'a Arc>), + RibComms(&'a Arc>), + RibComm(&'a Comm), + RibExtComms(&'a Arc>), + RibExtComm(&'a ExtComm), + RibExtv6Comms(&'a Arc>), + RibExtv6Comm(&'a Extv6Comm), + RibLargeComms(&'a Arc>), + RibLargeComm(&'a LargeComm), + RibAsPathSegment(&'a AsPathSegment), + RibAsPathSegmentMember(u32), + RibClusterList(Ipv4Addr), + RibNeighbor(AfiSafi, &'a Neighbor), + RibV4AdjInPreRoute(&'a Ipv4Network, &'a Route), + RibV6AdjInPreRoute(&'a Ipv6Network, &'a Route), + RibV4AdjInPostRoute(&'a Ipv4Network, &'a Route), + RibV6AdjInPostRoute(&'a Ipv6Network, &'a Route), + RouteUnknownAttr(&'a UnknownAttr), +} + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + { + {} + } + CallbacksBuilder::::default() + .path(bgp::global::afi_safis::afi_safi::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::global::afi_safis::afi_safi::statistics::total_paths::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::global::afi_safis::afi_safi::statistics::total_prefixes::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::global::afi_safis::afi_safi::apply_policy::import_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::global::afi_safis::afi_safi::apply_policy::export_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::global::afi_safis::afi_safi::ipv4_unicast::prefix_limit::prefix_limit_exceeded::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::global::afi_safis::afi_safi::ipv6_unicast::prefix_limit::prefix_limit_exceeded::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::global::apply_policy::import_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::global::apply_policy::export_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::global::statistics::total_paths::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::global::statistics::total_prefixes::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::PATH) + .get_iterate(|instance, _args| { + let iter = instance + .neighbors + .values() + .map(ListEntry::Neighbor); + Some(Box::new(iter)) + }) + .path(bgp::neighbors::neighbor::local_address::PATH) + .get_element_ip(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.conn_info.as_ref().map(|conn_info| conn_info.local_addr) + }) + .path(bgp::neighbors::neighbor::local_port::PATH) + .get_element_u16(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.conn_info.as_ref().map(|conn_info| conn_info.local_port) + }) + .path(bgp::neighbors::neighbor::remote_port::PATH) + .get_element_u16(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.conn_info.as_ref().map(|conn_info| conn_info.remote_port) + }) + .path(bgp::neighbors::neighbor::peer_type::PATH) + .get_element_string(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.peer_type.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::identifier::PATH) + .get_element_ipv4(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.identifier + }) + .path(bgp::neighbors::neighbor::dynamically_configured::PATH) + .get_element_empty(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::timers::negotiated_hold_time::PATH) + .get_element_u16(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.holdtime_nego + }) + .path(bgp::neighbors::neighbor::apply_policy::import_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::neighbors::neighbor::apply_policy::export_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::neighbors::neighbor::prefix_limit::prefix_limit_exceeded::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::active::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::prefixes::received::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::prefixes::sent::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::prefixes::installed::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::apply_policy::import_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::apply_policy::export_policy::PATH) + .get_iterate(|_instance, _args| { + // No operational data under this list. + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv4_unicast::prefix_limit::prefix_limit_exceeded::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::afi_safis::afi_safi::ipv6_unicast::prefix_limit::prefix_limit_exceeded::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::last_established::PATH) + .attributes(NodeAttributes::TIME) + .get_element_date_and_time(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.last_established + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::PATH) + .get_iterate(|_instance, args| { + let nbr = args.parent_list_entry.as_neighbor().unwrap(); + let iter = nbr + .capabilities_adv + .iter() + .enumerate() + .map(|(index, cap)| ListEntry::CapabilityAdv(index, cap)); + Some(Box::new(iter)) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::name::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_adv().unwrap(); + Some(cap.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::mpbgp::afi::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_adv().unwrap(); + cap.as_multi_protocol().map(|(afi, _)| afi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::mpbgp::safi::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_adv().unwrap(); + cap.as_multi_protocol().map(|(_, safi)| safi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::mpbgp::name::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_adv().unwrap(); + cap.as_multi_protocol() + .and_then(|(afi, safi)| afi_safi_tuple(*afi, *safi)) + .map(|afi_safi| afi_safi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::asn32::r#as::PATH) + .get_element_u32(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_adv().unwrap(); + cap.as_four_octet_as_number().copied() + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::add_paths::afi_safis::PATH) + .get_iterate(|_instance, args| { + let (_, cap) = args.parent_list_entry.as_capability_adv().unwrap(); + if let Capability::AddPath(cap) = cap { + let iter = cap.iter().map(ListEntry::AddPathTuple); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::add_paths::afi_safis::afi::PATH) + .get_element_string(|_instance, args| { + let ap = args.list_entry.as_add_path_tuple().unwrap(); + Some(ap.afi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::add_paths::afi_safis::safi::PATH) + .get_element_string(|_instance, args| { + let ap = args.list_entry.as_add_path_tuple().unwrap(); + Some(ap.safi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::advertised_capabilities::value::add_paths::afi_safis::mode::PATH) + .get_element_string(|_instance, args| { + let ap = args.list_entry.as_add_path_tuple().unwrap(); + Some(ap.mode.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::PATH) + .get_iterate(|_instance, args| { + let nbr = args.parent_list_entry.as_neighbor().unwrap(); + let iter = nbr + .capabilities_rcvd + .iter() + .enumerate() + .map(|(index, cap)| ListEntry::CapabilityRcvd(index, cap)); + Some(Box::new(iter)) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::name::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_rcvd().unwrap(); + Some(cap.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::mpbgp::afi::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_rcvd().unwrap(); + cap.as_multi_protocol().map(|(afi, _)| afi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::mpbgp::safi::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_rcvd().unwrap(); + cap.as_multi_protocol().map(|(_, safi)| safi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::mpbgp::name::PATH) + .get_element_string(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_rcvd().unwrap(); + cap.as_multi_protocol() + .and_then(|(afi, safi)| afi_safi_tuple(*afi, *safi)) + .map(|afi_safi| afi_safi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::asn32::r#as::PATH) + .get_element_u32(|_instance, args| { + let (_, cap) = args.list_entry.as_capability_rcvd().unwrap(); + cap.as_four_octet_as_number().copied() + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::add_paths::afi_safis::PATH) + .get_iterate(|_instance, args| { + let (_, cap) = args.parent_list_entry.as_capability_rcvd().unwrap(); + if let Capability::AddPath(cap) = cap { + let iter = cap.iter().map(ListEntry::AddPathTuple); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::add_paths::afi_safis::afi::PATH) + .get_element_string(|_instance, args| { + let ap = args.list_entry.as_add_path_tuple().unwrap(); + Some(ap.afi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::add_paths::afi_safis::safi::PATH) + .get_element_string(|_instance, args| { + let ap = args.list_entry.as_add_path_tuple().unwrap(); + Some(ap.safi.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::received_capabilities::value::add_paths::afi_safis::mode::PATH) + .get_element_string(|_instance, args| { + let ap = args.list_entry.as_add_path_tuple().unwrap(); + Some(ap.mode.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::capabilities::negotiated_capabilities::PATH) + .get_iterate(|_instance, args| { + let nbr = args.parent_list_entry.as_neighbor().unwrap(); + let iter = nbr + .capabilities_adv + .iter() + .map(|cap| cap.to_yang().into()) + .dedup() + .map(ListEntry::CapabilityNego); + Some(Box::new(iter)) + }) + .get_element_string(|_instance, args| { + let cap = args.list_entry.as_capability_nego().unwrap(); + Some(cap.clone()) + }) + .path(bgp::neighbors::neighbor::errors::received::last_notification::PATH) + .attributes(NodeAttributes::TIME) + .get_element_date_and_time(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_rcvd.as_ref().map(|(time, _)| *time) + }) + .path(bgp::neighbors::neighbor::errors::received::last_error::PATH) + .get_element_string(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_rcvd.as_ref().map(|(_, notif)| notif.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::errors::received::last_error_code::PATH) + .get_element_u8(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_rcvd.as_ref().map(|(_, notif)| notif.error_code) + }) + .path(bgp::neighbors::neighbor::errors::received::last_error_subcode::PATH) + .get_element_u8(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_rcvd.as_ref().map(|(_, notif)| notif.error_subcode) + }) + .path(bgp::neighbors::neighbor::errors::received::last_error_data::PATH) + .get_element_binary(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_rcvd.as_ref().map(|(_, notif)| notif.data.clone()) + }) + .path(bgp::neighbors::neighbor::errors::sent::last_notification::PATH) + .attributes(NodeAttributes::TIME) + .get_element_date_and_time(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_sent.as_ref().map(|(time, _)| *time) + }) + .path(bgp::neighbors::neighbor::errors::sent::last_error::PATH) + .get_element_string(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_sent.as_ref().map(|(_, notif)| notif.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::errors::sent::last_error_code::PATH) + .get_element_u8(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_sent.as_ref().map(|(_, notif)| notif.error_code) + }) + .path(bgp::neighbors::neighbor::errors::sent::last_error_subcode::PATH) + .get_element_u8(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_sent.as_ref().map(|(_, notif)| notif.error_code) + }) + .path(bgp::neighbors::neighbor::errors::sent::last_error_data::PATH) + .get_element_binary(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + nbr.notification_sent.as_ref().map(|(_, notif)| notif.data.clone()) + }) + .path(bgp::neighbors::neighbor::session_state::PATH) + .get_element_string(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.state.to_yang().into()) + }) + .path(bgp::neighbors::neighbor::statistics::established_transitions::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.established_transitions) + }) + .path(bgp::neighbors::neighbor::statistics::messages::total_received::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_rcvd.total.load(atomic::Ordering::Relaxed)) + }) + .path(bgp::neighbors::neighbor::statistics::messages::total_sent::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_sent.total.load(atomic::Ordering::Relaxed)) + }) + .path(bgp::neighbors::neighbor::statistics::messages::updates_received::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_rcvd.updates) + }) + .path(bgp::neighbors::neighbor::statistics::messages::updates_sent::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_sent.updates) + }) + .path(bgp::neighbors::neighbor::statistics::messages::erroneous_updates_withdrawn::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.erroneous_updates_withdrawn) + }) + .path(bgp::neighbors::neighbor::statistics::messages::erroneous_updates_attribute_discarded::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.erroneous_updates_attribute_discarded) + }) + .path(bgp::neighbors::neighbor::statistics::messages::in_update_elapsed_time::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.in_update_elapsed_time.as_secs() as u32) + }) + .path(bgp::neighbors::neighbor::statistics::messages::notifications_received::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_rcvd.notifications) + }) + .path(bgp::neighbors::neighbor::statistics::messages::notifications_sent::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_sent.notifications) + }) + .path(bgp::neighbors::neighbor::statistics::messages::route_refreshes_received::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_rcvd.route_refreshes) + }) + .path(bgp::neighbors::neighbor::statistics::messages::route_refreshes_sent::PATH) + .attributes(NodeAttributes::COUNTER) + .get_element_u32(|_instance, args| { + let nbr = args.list_entry.as_neighbor().unwrap(); + Some(nbr.statistics.msgs_sent.route_refreshes) + }) + .path(bgp::neighbors::neighbor::statistics::queues::input::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::neighbors::neighbor::statistics::queues::output::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::attr_sets::attr_set::PATH) + .get_iterate(|instance, _args| { + if let Some(state) = &instance.state { + let iter = state + .rib + .attr_sets + .base + .tree + .values() + .map(ListEntry::RibBaseAttrs); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::attr_sets::attr_set::attributes::origin::PATH) + .get_element_string(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + Some(attr_set.value.origin.to_yang().into()) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::as_path::segment::PATH) + .get_iterate(|_instance, args| { + let attr_set = args.parent_list_entry.as_rib_base_attrs().unwrap(); + let iter = attr_set + .value + .as_path + .segments + .iter() + .map(ListEntry::RibAsPathSegment); + Some(Box::new(iter)) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::as_path::segment::r#type::PATH) + .get_element_string(|_instance, args| { + let aspath_seg = args.list_entry.as_rib_as_path_segment().unwrap(); + Some(aspath_seg.seg_type.to_yang().into()) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::as_path::segment::member::PATH) + .get_iterate(|_instance, args| { + let aspath_seg = + args.parent_list_entry.as_rib_as_path_segment().unwrap(); + let iter = aspath_seg + .members + .iter() + .copied() + .map(ListEntry::RibAsPathSegmentMember); + Some(Box::new(iter)) + }) + .get_element_u32(|_instance, args| { + let asn = args.list_entry.as_rib_as_path_segment_member().unwrap(); + Some(*asn) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::next_hop::PATH) + .get_element_ip(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.nexthop + }) + .path(bgp::rib::attr_sets::attr_set::attributes::link_local_next_hop::PATH) + .get_element_ipv6(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.ll_nexthop + }) + .path(bgp::rib::attr_sets::attr_set::attributes::med::PATH) + .get_element_u32(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.med + }) + .path(bgp::rib::attr_sets::attr_set::attributes::local_pref::PATH) + .get_element_u32(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.local_pref + }) + .path(bgp::rib::attr_sets::attr_set::attributes::as4_path::segment::PATH) + .get_iterate(|_instance, args| { + let attr_set = args.parent_list_entry.as_rib_base_attrs().unwrap(); + if let Some(as4_path) = &attr_set.value.as4_path { + let iter = as4_path + .segments + .iter() + .map(ListEntry::RibAsPathSegment); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::attr_sets::attr_set::attributes::as4_path::segment::r#type::PATH) + .get_element_string(|_instance, args| { + let aspath_seg = args.list_entry.as_rib_as_path_segment().unwrap(); + Some(aspath_seg.seg_type.to_yang().into()) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::as4_path::segment::member::PATH) + .get_iterate(|_instance, args| { + let aspath_seg = + args.parent_list_entry.as_rib_as_path_segment().unwrap(); + let iter = aspath_seg + .members + .iter() + .copied() + .map(ListEntry::RibAsPathSegmentMember); + Some(Box::new(iter)) + }) + .get_element_u32(|_instance, args| { + let asn = args.list_entry.as_rib_as_path_segment_member().unwrap(); + Some(*asn) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::aggregator::r#as::PATH) + .get_element_u32(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.aggregator.as_ref().map(|aggregator| aggregator.asn) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::aggregator::identifier::PATH) + .get_element_ipv4(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.aggregator.as_ref().map(|aggregator| aggregator.identifier) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::aggregator4::as4::PATH) + .get_element_u32(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.as4_aggregator.as_ref().map(|aggregator| aggregator.asn) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::aggregator4::identifier::PATH) + .get_element_ipv4(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.as4_aggregator.as_ref().map(|aggregator| aggregator.identifier) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::atomic_aggregate::PATH) + .get_element_bool(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + Some(attr_set.value.atomic_aggregate) + }) + .path(bgp::rib::attr_sets::attr_set::attributes::originator_id::PATH) + .get_element_ipv4(|_instance, args| { + let attr_set = args.list_entry.as_rib_base_attrs().unwrap(); + attr_set.value.originator_id + }) + .path(bgp::rib::attr_sets::attr_set::attributes::cluster_list::PATH) + .get_iterate(|_instance, args| { + let attr_set = args.parent_list_entry.as_rib_base_attrs().unwrap(); + if let Some(cluster_list) = &attr_set.value.cluster_list { + let iter = cluster_list.0 + .iter() + .copied() + .map(ListEntry::RibClusterList); + Some(Box::new(iter)) + } else { + None + } + }) + .get_element_ipv4(|_instance, args| { + let addr = args.list_entry.as_rib_cluster_list().unwrap(); + Some(*addr) + }) + .path(bgp::rib::communities::community::PATH) + .get_iterate(|instance, _args| { + if let Some(state) = &instance.state { + let iter = state + .rib + .attr_sets + .comm + .tree + .values() + .map(ListEntry::RibComms); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::communities::community::community::PATH) + .get_iterate(|_instance, args| { + let comms = args.parent_list_entry.as_rib_comms().unwrap(); + let iter = comms.value.0.iter().map(ListEntry::RibComm); + Some(Box::new(iter)) + }) + .get_element_string(|_instance, args| { + let comm = args.list_entry.as_rib_comm().unwrap(); + Some(comm.to_yang().into()) + }) + .path(bgp::rib::ext_communities::ext_community::PATH) + .get_iterate(|instance, _args| { + if let Some(state) = &instance.state { + let iter = state + .rib + .attr_sets + .ext_comm + .tree + .values() + .map(ListEntry::RibExtComms); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::ext_communities::ext_community::ext_community::PATH) + .get_iterate(|_instance, args| { + let comms = args.parent_list_entry.as_rib_ext_comms().unwrap(); + let iter = comms.value.0.iter().map(ListEntry::RibExtComm); + Some(Box::new(iter)) + }) + .get_element_string(|_instance, args| { + let comm = args.list_entry.as_rib_ext_comm().unwrap(); + Some(comm.to_yang().into()) + }) + .path(bgp::rib::ext_communities::ext_community::ext_community_raw::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::ipv6_ext_communities::ipv6_ext_community::PATH) + .get_iterate(|instance, _args| { + if let Some(state) = &instance.state { + let iter = state + .rib + .attr_sets + .extv6_comm + .tree + .values() + .map(ListEntry::RibExtv6Comms); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::ipv6_ext_communities::ipv6_ext_community::ipv6_ext_community::PATH) + .get_iterate(|_instance, args| { + let comms = args.parent_list_entry.as_rib_extv6_comms().unwrap(); + let iter = comms.value.0.iter().map(ListEntry::RibExtv6Comm); + Some(Box::new(iter)) + }) + .get_element_string(|_instance, args| { + let comm = args.list_entry.as_rib_extv6_comm().unwrap(); + Some(comm.to_yang().into()) + }) + .path(bgp::rib::ipv6_ext_communities::ipv6_ext_community::ipv6_ext_community_raw::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::large_communities::large_community::PATH) + .get_iterate(|instance, _args| { + if let Some(state) = &instance.state { + let iter = state + .rib + .attr_sets + .large_comm + .tree + .values() + .map(ListEntry::RibLargeComms); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::large_communities::large_community::large_community::PATH) + .get_iterate(|_instance, args| { + let comms = args.parent_list_entry.as_rib_large_comms().unwrap(); + let iter = comms.value.0.iter().map(ListEntry::RibLargeComm); + Some(Box::new(iter)) + }) + .get_element_string(|_instance, args| { + let comm = args.list_entry.as_rib_large_comm().unwrap(); + Some(comm.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::PATH) + .get_iterate(|instance, _args| { + if instance.state.is_some() { + let iter = [AfiSafi::Ipv4Unicast, AfiSafi::Ipv6Unicast] + .into_iter() + .map(ListEntry::Rib); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::attr_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::ext_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::large_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::last_modified::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::loc_rib::routes::route::reject_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::PATH) + .get_iterate(|instance, args| { + let afi_safi = *args.parent_list_entry.as_rib().unwrap(); + if afi_safi == AfiSafi::Ipv4Unicast { + let iter = instance + .neighbors + .values() + .map(move |nbr| ListEntry::RibNeighbor(afi_safi, nbr)); + Some(Box::new(iter)) + } else { + None + } + }) + + + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::PATH) + .get_iterate(|instance, args| { + let (_, nbr) = args.parent_list_entry.as_rib_neighbor().unwrap(); + if let Some(state) = &instance.state { + let iter = state.rib.ipv4_unicast.iter().filter_map( + |(prefix, dest)| { + dest.adj_in_pre.get(&nbr.remote_addr).map(|route| { + ListEntry::RibV4AdjInPreRoute(prefix, route) + }) + }, + ); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::attr_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + Some(route.attrs.base.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + route.attrs.comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::ext_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + route.attrs.ext_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::large_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + route.attrs.large_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::last_modified::PATH) + .get_element_timeticks(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + Some(route.last_modified) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + Some(route.eligible) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + route.ineligible_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, args| { + let (_, route) = args.parent_list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + let iter = route.attrs.unknown.iter().map(ListEntry::RouteUnknownAttr); + Some(Box::new(iter)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::OPTIONAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::TRANSITIVE)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::PARTIAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::EXTENDED)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.length) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_binary(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.value.to_vec()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::reject_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_pre_route().unwrap(); + route.reject_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::PATH) + .get_iterate(|instance, args| { + let (_, nbr) = args.parent_list_entry.as_rib_neighbor().unwrap(); + if let Some(state) = &instance.state { + let iter = state.rib.ipv4_unicast.iter().filter_map( + |(prefix, dest)| { + dest.adj_in_post.get(&nbr.remote_addr).map(|route| { + ListEntry::RibV4AdjInPostRoute(prefix, route) + }) + }, + ); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::attr_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + Some(route.attrs.base.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + route.attrs.comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::ext_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + route.attrs.ext_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::large_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + route.attrs.large_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::last_modified::PATH) + .get_element_timeticks(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + Some(route.last_modified) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + Some(route.eligible) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + route.ineligible_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::best_path::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, args| { + let (_, route) = args.parent_list_entry.as_rib_v4_adj_in_post_route().unwrap(); + let iter = route.attrs.unknown.iter().map(ListEntry::RouteUnknownAttr); + Some(Box::new(iter)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::OPTIONAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::TRANSITIVE)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::PARTIAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::EXTENDED)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.length) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_binary(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.value.to_vec()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::reject_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v4_adj_in_post_route().unwrap(); + route.reject_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::attr_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::ext_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::large_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::last_modified::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::reject_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::attr_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::ext_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::large_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::last_modified::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::reject_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::attr_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::ext_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::large_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::last_modified::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::loc_rib::routes::route::reject_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::PATH) + .get_iterate(|instance, args| { + let afi_safi = *args.parent_list_entry.as_rib().unwrap(); + if afi_safi == AfiSafi::Ipv6Unicast { + let iter = instance + .neighbors + .values() + .map(move |nbr| ListEntry::RibNeighbor(afi_safi, nbr)); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::PATH) + .get_iterate(|instance, args| { + let (_, nbr) = args.parent_list_entry.as_rib_neighbor().unwrap(); + if let Some(state) = &instance.state { + let iter = state.rib.ipv6_unicast.iter().filter_map( + |(prefix, dest)| { + dest.adj_in_post.get(&nbr.remote_addr).map(|route| { + ListEntry::RibV6AdjInPreRoute(prefix, route) + }) + }, + ); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::attr_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + Some(route.attrs.base.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + route.attrs.comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::ext_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + route.attrs.ext_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::large_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + route.attrs.large_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::last_modified::PATH) + .get_element_timeticks(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + Some(route.last_modified) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + Some(route.eligible) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + route.ineligible_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, args| { + let (_, route) = args.parent_list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + let iter = route.attrs.unknown.iter().map(ListEntry::RouteUnknownAttr); + Some(Box::new(iter)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::OPTIONAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::TRANSITIVE)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::PARTIAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::EXTENDED)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.length) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_binary(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.value.to_vec()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::reject_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_pre_route().unwrap(); + route.reject_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::PATH) + .get_iterate(|instance, args| { + let (_, nbr) = args.parent_list_entry.as_rib_neighbor().unwrap(); + if let Some(state) = &instance.state { + let iter = state.rib.ipv6_unicast.iter().filter_map( + |(prefix, dest)| { + dest.adj_in_post.get(&nbr.remote_addr).map(|route| { + ListEntry::RibV6AdjInPostRoute(prefix, route) + }) + }, + ); + Some(Box::new(iter)) + } else { + None + } + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::attr_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + Some(route.attrs.base.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + route.attrs.comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::ext_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + route.attrs.ext_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::large_community_index::PATH) + .get_element_u64(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + route.attrs.large_comm.as_ref().map(|c| c.index) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::last_modified::PATH) + .get_element_timeticks(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + Some(route.last_modified) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + Some(route.eligible) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + route.ineligible_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::best_path::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, args| { + let (_, route) = args.parent_list_entry.as_rib_v6_adj_in_post_route().unwrap(); + let iter = route.attrs.unknown.iter().map(ListEntry::RouteUnknownAttr); + Some(Box::new(iter)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::OPTIONAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::TRANSITIVE)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::PARTIAL)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.flags.contains(AttrFlags::EXTENDED)) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.length) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_binary(|_instance, args| { + let attr = args.list_entry.as_route_unknown_attr().unwrap(); + Some(attr.value.to_vec()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::reject_reason::PATH) + .get_element_string(|_instance, args| { + let (_, route) = args.list_entry.as_rib_v6_adj_in_post_route().unwrap(); + route.reject_reason.as_ref().map(|r| r.to_yang().into()) + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::attr_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::ext_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::large_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::last_modified::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_pre::routes::route::reject_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::attr_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::ext_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::large_community_index::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::last_modified::PATH) + .get_element_u32(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::eligible_route::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::ineligible_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::optional::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::transitive::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::partial::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::extended::PATH) + .get_element_bool(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::attr_len::PATH) + .get_element_u16(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::unknown_attributes::unknown_attribute::attr_value::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .path(bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_out_post::routes::route::reject_reason::PATH) + .get_element_string(|_instance, _args| { + // TODO: implement me! + None + }) + .build() +} + +// ===== impl Instance ===== + +impl Provider for Instance { + const STATE_PATH: &'static str = "/ietf-routing:routing/control-plane-protocols/control-plane-protocol[type='ietf-bgp:bgp'][name='test']/ietf-bgp:bgp"; + + type ListEntry<'a> = ListEntry<'a>; + + fn callbacks() -> Option<&'static Callbacks> { + Some(&CALLBACKS) + } +} + +// ===== impl ListEntry ===== + +impl<'a> ListEntryKind for ListEntry<'a> { + fn get_keys(&self) -> Option { + match self { + ListEntry::None => None, + ListEntry::Neighbor(nbr) => { + use bgp::neighbors::neighbor::list_keys; + let keys = list_keys(nbr.remote_addr); + Some(keys) + } + ListEntry::CapabilityAdv(index, cap) => { + use bgp::neighbors::neighbor::capabilities::advertised_capabilities::list_keys; + let keys = list_keys(cap.code() as u8, index); + Some(keys) + } + ListEntry::CapabilityRcvd(index, cap) => { + use bgp::neighbors::neighbor::capabilities::received_capabilities::list_keys; + let keys = list_keys(cap.code() as u8, index); + Some(keys) + } + ListEntry::Rib(afi_safi) => { + use bgp::rib::afi_safis::afi_safi::list_keys; + let keys = list_keys(afi_safi.to_yang()); + Some(keys) + } + ListEntry::RibBaseAttrs(attr_set) => { + use bgp::rib::attr_sets::attr_set::list_keys; + let keys = list_keys(attr_set.index); + Some(keys) + } + ListEntry::RibComms(attr_set) => { + use bgp::rib::communities::community::list_keys; + let keys = list_keys(attr_set.index); + Some(keys) + } + ListEntry::RibExtComms(attr_set) => { + use bgp::rib::ext_communities::ext_community::list_keys; + let keys = list_keys(attr_set.index); + Some(keys) + } + ListEntry::RibExtv6Comms(attr_set) => { + use bgp::rib::ipv6_ext_communities::ipv6_ext_community::list_keys; + let keys = list_keys(attr_set.index); + Some(keys) + } + ListEntry::RibLargeComms(attr_set) => { + use bgp::rib::large_communities::large_community::list_keys; + let keys = list_keys(attr_set.index); + Some(keys) + } + ListEntry::RibNeighbor(_afi_safi, nbr) => { + use bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::list_keys; + let keys = list_keys(nbr.remote_addr); + Some(keys) + } + ListEntry::RibV4AdjInPreRoute(prefix, _route) => { + use bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::list_keys; + let keys = list_keys(prefix, 0); + Some(keys) + } + ListEntry::RibV6AdjInPreRoute(prefix, _route) => { + use bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_pre::routes::route::list_keys; + let keys = list_keys(prefix, 0); + Some(keys) + } + ListEntry::RibV4AdjInPostRoute(prefix, _route) => { + use bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::list_keys; + let keys = list_keys(prefix, 0); + Some(keys) + } + ListEntry::RibV6AdjInPostRoute(prefix, _route) => { + use bgp::rib::afi_safis::afi_safi::ipv6_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::list_keys; + let keys = list_keys(prefix, 0); + Some(keys) + } + ListEntry::RouteUnknownAttr(attr) => { + use bgp::rib::afi_safis::afi_safi::ipv4_unicast::neighbors::neighbor::adj_rib_in_post::routes::route::unknown_attributes::unknown_attribute::list_keys; + let keys = list_keys(attr.attr_type); + Some(keys) + } + ListEntry::CapabilityNego(_) + | ListEntry::AddPathTuple(_) + | ListEntry::RibComm(_) + | ListEntry::RibExtComm(_) + | ListEntry::RibExtv6Comm(_) + | ListEntry::RibLargeComm(_) + | ListEntry::RibAsPathSegment(_) + | ListEntry::RibAsPathSegmentMember(_) + | ListEntry::RibClusterList(_) => { + // Keyless lists. + None + } + } + } +} + +// ===== helper functions ===== + +fn afi_safi_tuple(afi: Afi, safi: Safi) -> Option { + match (afi, safi) { + (Afi::Ipv4, Safi::Unicast) => Some(AfiSafi::Ipv4Unicast), + (Afi::Ipv6, Safi::Unicast) => Some(AfiSafi::Ipv6Unicast), + _ => None, + } +} diff --git a/holo-bgp/src/northbound/yang.rs b/holo-bgp/src/northbound/yang.rs new file mode 100644 index 00000000..6cc7cb2d --- /dev/null +++ b/holo-bgp/src/northbound/yang.rs @@ -0,0 +1,330 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::borrow::Cow; + +use holo_yang::{ToYang, TryFromYang}; +use num_traits::FromPrimitive; + +use crate::neighbor::{fsm, PeerType}; +use crate::northbound::configuration::PrivateAsRemove; +use crate::packet::consts::{ + AddPathMode, AsPathSegmentType, CeaseSubcode, ErrorCode, FsmErrorSubcode, + MessageHeaderErrorSubcode, OpenMessageErrorSubcode, + RouteRefreshErrorSubcode, Safi, UpdateMessageErrorSubcode, +}; +use crate::packet::message::{Capability, NotificationMsg}; +use crate::rib::{RouteIneligibleReason, RouteRejectReason}; + +// ===== ToYang implementations ===== + +impl ToYang for Safi { + fn to_yang(&self) -> Cow<'static, str> { + match self { + Safi::Unicast => "unicast-safi".into(), + Safi::Multicast => "multicast-safi".into(), + Safi::LabeledUnicast => "labeled-unicast-safi".into(), + Safi::MulticastVpn => "multicast-vpn-safi".into(), + Safi::Pseudowire => "pseudowire-safi".into(), + Safi::TunnelEncap => "tunnel-encap-safi".into(), + Safi::McastVpls => "mcast-vpls-safi".into(), + Safi::Tunnel => "tunnel-safi".into(), + Safi::Vpls => "vpls-safi".into(), + Safi::Mdt => "mdt-safi".into(), + Safi::V4OverV6 => "v4-over-v6-safi".into(), + Safi::V6OverV4 => "v6-over-v4-safi".into(), + Safi::L1VpnAutoDiscovery => "l1-vpn-auto-discovery-safi".into(), + Safi::Evpn => "evpn-safi".into(), + Safi::BgpLs => "bgp-ls-safi".into(), + Safi::BgpLsVpn => "bgp-ls-vpn-safi".into(), + Safi::SrTe => "sr-te-safi".into(), + Safi::SdWanCapabilities => "sd-wan-capabilities-safi".into(), + Safi::LabeledVpn => "labeled-vpn-safi".into(), + Safi::MulticastMplsVpn => "multicast-mpls-vpn-safi".into(), + Safi::RouteTarget => "route-target-safi".into(), + Safi::Ipv4FlowSpec => "ipv4-flow-spec-safi".into(), + Safi::Vpnv4FlowSpec => "vpnv4-flow-spec-safi".into(), + Safi::VpnAutoDiscovery => "vpn-auto-discovery-safi".into(), + } + } +} + +impl ToYang for AddPathMode { + fn to_yang(&self) -> Cow<'static, str> { + match self { + AddPathMode::Receive => "receive".into(), + AddPathMode::Send => "send".into(), + AddPathMode::ReceiveSend => "receive-send".into(), + } + } +} + +impl ToYang for Capability { + fn to_yang(&self) -> Cow<'static, str> { + match self { + Capability::MultiProtocol { .. } => "iana-bgp-types:mp-bgp".into(), + Capability::FourOctetAsNumber { .. } => { + "iana-bgp-types:asn32".into() + } + Capability::AddPath { .. } => "holo-bgp:add-paths".into(), + Capability::RouteRefresh => "iana-bgp-types:route-refresh".into(), + Capability::EnhancedRouteRefresh => { + "holo-bgp:enhanced-route-refresh".into() + } + } + } +} + +impl ToYang for NotificationMsg { + fn to_yang(&self) -> Cow<'static, str> { + let Some(error_code) = ErrorCode::from_u8(self.error_code) else { + return "holo-bgp:unknown-error".into(); + }; + let identity = match error_code { + ErrorCode::MessageHeaderError => { + use MessageHeaderErrorSubcode as ErrorSubcode; + match ErrorSubcode::from_u8(self.error_subcode) { + Some(ErrorSubcode::Unspecific) => { + "message-header-unspecific" + } + Some(ErrorSubcode::ConnectionNotSynchronized) => { + "message-header-connection-not-synchronized" + } + Some(ErrorSubcode::BadMessageLength) => { + "message-header-bad-message-length" + } + Some(ErrorSubcode::BadMessageType) => { + "message-header-bad-message-type" + } + None => "message-header-error", + } + } + ErrorCode::OpenMessageError => { + use OpenMessageErrorSubcode as ErrorSubcode; + match ErrorSubcode::from_u8(self.error_subcode) { + Some(ErrorSubcode::Unspecific) => "open-message-unspecific", + Some(ErrorSubcode::UnsupportedVersionNumber) => { + "open-unsupported-version-number" + } + Some(ErrorSubcode::BadPeerAs) => "open-bad-peer-as", + Some(ErrorSubcode::BadBgpIdentifier) => "open-bad-bgp-id", + Some(ErrorSubcode::UnsupportedOptParam) => { + "open-unsupported-optional-parameter" + } + Some(ErrorSubcode::UnacceptableHoldTime) => { + "open-unacceptable-hold-time" + } + Some(ErrorSubcode::UnsupportedCapability) => { + "open-unsupported-capability" + } + Some(ErrorSubcode::RoleMismatch) => "open-role-mismatch", + None => "open-message-error", + } + } + ErrorCode::UpdateMessageError => { + use UpdateMessageErrorSubcode as ErrorSubcode; + match ErrorSubcode::from_u8(self.error_subcode) { + Some(ErrorSubcode::Unspecific) => "update-unspecific", + Some(ErrorSubcode::MalformedAttributeList) => { + "update-malformed-attribute-list" + } + Some(ErrorSubcode::UnrecognizedWellKnownAttribute) => { + "update-unrecognized-well-known-attribute" + } + Some(ErrorSubcode::MissingWellKnownAttribute) => { + "update-missing-well-known-attribute" + } + Some(ErrorSubcode::AttributeFlagsError) => { + "update-attribute-flags-error" + } + Some(ErrorSubcode::AttributeLengthError) => { + "update-attribute-length-error" + } + Some(ErrorSubcode::InvalidOriginAttribute) => { + "update-invalid-origin-attribute" + } + Some(ErrorSubcode::InvalidNexthopAttribute) => { + "update-invalid-next-hop-attribute" + } + Some(ErrorSubcode::OptionalAttributeError) => { + "open-optional-attribute-error" + } + Some(ErrorSubcode::InvalidNetworkField) => { + "open-invalid-network-field" + } + Some(ErrorSubcode::MalformedAsPath) => { + "open-malformed-as-path" + } + None => "update-message-error", + } + } + ErrorCode::HoldTimerExpired => "hold-timer-expired-error", + ErrorCode::FiniteStateMachineError => { + use FsmErrorSubcode as ErrorSubcode; + match ErrorSubcode::from_u8(self.error_subcode) { + Some(ErrorSubcode::UnexpectedMessageInOpenSent) => { + "fsm-error-unexpected-in-opensent" + } + Some(ErrorSubcode::UnexpectedMessageInOpenConfirm) => { + "fsm-error-unexpected-in-openconfirm" + } + Some(ErrorSubcode::UnexpectedMessageInEstablished) => { + "fsm-error-unexpected-in-established" + } + None => "fsm-error", + } + } + ErrorCode::Cease => { + use CeaseSubcode as ErrorSubcode; + match ErrorSubcode::from_u8(self.error_subcode) { + Some(ErrorSubcode::MaximumNumberofPrefixesReached) => { + "cease-max-prefixes" + } + Some(ErrorSubcode::AdministrativeShutdown) => { + "cease-admin-shutdown" + } + Some(ErrorSubcode::PeerDeConfigured) => { + "cease-peer-deconfigured" + } + Some(ErrorSubcode::AdministrativeReset) => { + "cease-admin-reset" + } + Some(ErrorSubcode::ConnectionRejected) => { + "cease-connection-rejected" + } + Some(ErrorSubcode::OtherConfigurationChange) => { + "cease-other-configuration-change" + } + Some(ErrorSubcode::ConnectionCollisionResolution) => { + "cease-connection-collision" + } + Some(ErrorSubcode::OutOfResources) => { + "cease-out-of-resources" + } + Some(ErrorSubcode::HardReset) => "cease-hard-reset", + Some(ErrorSubcode::BfdDown) => "cease-bfd-down", + None => "cease", + } + } + ErrorCode::RouteRefreshMessageError => { + use RouteRefreshErrorSubcode as ErrorSubcode; + match ErrorSubcode::from_u8(self.error_subcode) { + Some(ErrorSubcode::InvalidMessageLength) => { + "route-refresh-invalid-message-length" + } + None => "route-refresh-message-error", + } + } + }; + format!("iana-bgp-notification:{}", identity).into() + } +} + +impl ToYang for fsm::State { + fn to_yang(&self) -> Cow<'static, str> { + match self { + fsm::State::Idle => "idle".into(), + fsm::State::Connect => "connect".into(), + fsm::State::Active => "active".into(), + fsm::State::OpenSent => "opensent".into(), + fsm::State::OpenConfirm => "openconfirm".into(), + fsm::State::Established => "established".into(), + } + } +} + +impl ToYang for PeerType { + fn to_yang(&self) -> Cow<'static, str> { + match self { + PeerType::Internal => "internal".into(), + PeerType::External => "external".into(), + } + } +} + +impl ToYang for AsPathSegmentType { + fn to_yang(&self) -> Cow<'static, str> { + match self { + AsPathSegmentType::Set => "iana-bgp-types:as-set".into(), + AsPathSegmentType::Sequence => "iana-bgp-types:as-sequence".into(), + AsPathSegmentType::ConfedSequence => { + "iana-bgp-types:as-confed-sequence".into() + } + AsPathSegmentType::ConfedSet => { + "iana-bgp-types:as-confed-set".into() + } + } + } +} + +impl ToYang for RouteIneligibleReason { + fn to_yang(&self) -> Cow<'static, str> { + match self { + RouteIneligibleReason::ClusterLoop => { + "iana-bgp-rib-types:ineligible-cluster-loop".into() + } + RouteIneligibleReason::AsLoop => { + "iana-bgp-rib-types:ineligible-as-loop".into() + } + RouteIneligibleReason::Originator => { + "iana-bgp-rib-types:ineligible-originator".into() + } + RouteIneligibleReason::Confed => { + "iana-bgp-rib-types:ineligible-confed".into() + } + } + } +} + +impl ToYang for RouteRejectReason { + fn to_yang(&self) -> Cow<'static, str> { + match self { + RouteRejectReason::LocalPrefLower => { + "iana-bgp-rib-types:local-pref-lower".into() + } + RouteRejectReason::AsPathLonger => { + "iana-bgp-rib-types:as-path-longer".into() + } + RouteRejectReason::OriginTypeHigher => { + "iana-bgp-rib-types:origin-type-higher".into() + } + RouteRejectReason::MedHigher => { + "iana-bgp-rib-types:med-higher".into() + } + RouteRejectReason::PreferExternal => { + "iana-bgp-rib-types:prefer-external".into() + } + RouteRejectReason::NexthopCostHigher => { + "iana-bgp-rib-types:nexthop-cost-higher".into() + } + RouteRejectReason::HigherRouterId => { + "iana-bgp-rib-types:higher-router-id".into() + } + RouteRejectReason::HigherPeerAddress => { + "iana-bgp-rib-types:higher-peer-address".into() + } + RouteRejectReason::RejectedImportPolicy => { + "iana-bgp-rib-types:rejected-import-policy".into() + } + } + } +} + +// ===== TryFromYang implementations ===== + +impl TryFromYang for PrivateAsRemove { + fn try_from_yang(value: &str) -> Option { + match value { + "iana-bgp-types:private-as-remove-all" => { + Some(PrivateAsRemove::RemoveAll) + } + "iana-bgp-types:private-as-replace-all" => { + Some(PrivateAsRemove::ReplaceAll) + } + _ => None, + } + } +} diff --git a/holo-bgp/src/packet/attribute.rs b/holo-bgp/src/packet/attribute.rs new file mode 100644 index 00000000..f67f04ec --- /dev/null +++ b/holo-bgp/src/packet/attribute.rs @@ -0,0 +1,1323 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::{BTreeSet, HashSet, VecDeque}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use derive_new::new; +use holo_utils::bytes::{BytesExt, BytesMutExt}; +use holo_utils::ip::{Ipv4AddrExt, Ipv6AddrExt}; +use num_traits::FromPrimitive; +use serde::{Deserialize, Serialize}; + +use crate::debug::Debug; +use crate::neighbor::PeerType; +use crate::packet::consts::{ + Afi, AsPathSegmentType, AttrFlags, AttrType, Origin, Safi, +}; +use crate::packet::error::{AttrError, UpdateMessageError}; +use crate::packet::message::{ + decode_ipv4_prefix, decode_ipv6_prefix, encode_ipv4_prefix, + encode_ipv6_prefix, DecodeCxt, EncodeCxt, MpReachNlri, MpUnreachNlri, + ReachNlri, +}; + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct Attrs { + pub base: BaseAttrs, + pub comm: Option, + pub ext_comm: Option, + pub extv6_comm: Option, + pub large_comm: Option, + pub unknown: Vec, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct BaseAttrs { + pub origin: Origin, + pub as_path: AsPath, + pub as4_path: Option, + pub nexthop: Option, + pub ll_nexthop: Option, + pub med: Option, + pub local_pref: Option, + pub aggregator: Option, + pub as4_aggregator: Option, + pub atomic_aggregate: bool, + pub originator_id: Option, + pub cluster_list: Option, +} + +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct AsPath { + pub segments: VecDeque, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct AsPathSegment { + pub seg_type: AsPathSegmentType, + pub members: VecDeque, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct Aggregator { + pub asn: u32, + pub identifier: Ipv4Addr, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct ClusterList(pub BTreeSet); + +// Re-exports for convenience. +pub type Comm = holo_utils::bgp::Comm; +pub type ExtComm = holo_utils::bgp::ExtComm; +pub type Extv6Comm = holo_utils::bgp::Extv6Comm; +pub type LargeComm = holo_utils::bgp::LargeComm; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct CommList(pub BTreeSet); + +pub trait CommType: + Clone + std::fmt::Debug + Eq + Ord + PartialEq + PartialOrd +{ + const TYPE: AttrType; + const LENGTH: usize; + + fn encode(&self, buf: &mut BytesMut); + fn decode(buf: &mut Bytes) -> Self; +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(new)] +#[derive(Deserialize, Serialize)] +pub struct UnknownAttr { + pub attr_type: u8, + pub flags: AttrFlags, + pub length: u16, + pub value: Bytes, +} + +// Useful type definitions. +pub type Comms = CommList; +pub type ExtComms = CommList; +pub type Extv6Comms = CommList; +pub type LargeComms = CommList; + +// ===== impl Attrs ===== + +impl Attrs { + pub(crate) fn encode( + &self, + buf: &mut BytesMut, + reach: &Option, + mp_reach: &Option, + mp_unreach: &Option, + cxt: &EncodeCxt, + ) { + // Check whether the 4-octet AS number capability has been negotiated. + let four_byte_asn_cap = cxt + .capabilities + .iter() + .any(|cap| cap.is_four_octet_as_number()); + + // RFC 7606 - Section 5.1: + // "The MP_REACH_NLRI or MP_UNREACH_NLRI attribute (if present) SHALL + // be encoded as the very first path attribute in an UPDATE message". + if let Some(mp_reach) = mp_reach { + mp_reach.encode(buf); + } + if let Some(mp_unreach) = mp_unreach { + mp_unreach.encode(buf); + } + + // RFC 4271 - Section 5: + // "The sender of an UPDATE message SHOULD order path attributes within + // the UPDATE message in ascending order of attribute type". + + // ORIGIN attribute. + origin::encode(self.base.origin, buf); + + // AS_PATH attribute. + self.base.as_path.encode( + buf, + AttrFlags::TRANSITIVE, + AttrType::AsPath, + four_byte_asn_cap, + ); + + // NEXT_HOP attribute. + if let Some(reach) = reach { + nexthop::encode(reach.nexthop, buf); + } + + // MULTI_EXIT_DISC attribute. + if let Some(metric) = self.base.med { + med::encode(metric, buf); + } + + // LOCAL_PREF attribute. + if let Some(local_pref) = self.base.local_pref { + local_pref::encode(local_pref, buf); + } + + // ATOMIC_AGGREGATE attribute. + if self.base.atomic_aggregate { + atomic_aggregate::encode(buf); + } + + // AGGREGATOR attribute. + if let Some(aggregator) = &self.base.aggregator { + aggregator.encode( + buf, + AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL, + AttrType::Aggregator, + four_byte_asn_cap, + ); + } + + // COMMUNITIES attribute. + if let Some(comm) = &self.comm { + comm.encode(buf); + } + + // ORIGINATOR_ID attribute. + if let Some(originator_id) = self.base.originator_id { + originator_id::encode(originator_id, buf); + } + + // CLUSTER_LIST attribute. + if let Some(cluster_list) = &self.base.cluster_list { + cluster_list.encode(buf); + } + + // EXTENDED COMMUNITIES attribute. + if let Some(ext_comm) = &self.ext_comm { + ext_comm.encode(buf); + } + + // AS4_PATH attribute. + if let Some(as4_path) = &self.base.as4_path { + as4_path.encode( + buf, + AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL, + AttrType::As4Path, + true, + ); + } + + // AS4_AGGREGATOR attribute. + if let Some(as4_aggregator) = &self.base.as4_aggregator { + as4_aggregator.encode( + buf, + AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL, + AttrType::As4Aggregator, + true, + ); + } + + // IPv6 Address Specific Extended Community attribute. + if let Some(extv6_comm) = &self.extv6_comm { + extv6_comm.encode(buf); + } + + // LARGE_COMMUNITY attribute. + if let Some(large_comm) = &self.large_comm { + large_comm.encode(buf); + } + } + + pub(crate) fn decode( + buf: &mut Bytes, + cxt: &DecodeCxt, + nexthop: &mut Option, + nlri_present: bool, + mp_unreach: &mut Option, + mp_reach: &mut Option, + ) -> Result, UpdateMessageError> { + let mut origin = None; + let mut as_path = None; + let mut as4_path = None; + let mut med = None; + let mut local_pref = None; + let mut aggregator = None; + let mut as4_aggregator = None; + let mut atomic_aggregate = false; + let mut originator_id = None; + let mut cluster_list = None; + let mut comm = None; + let mut ext_comm = None; + let mut extv6_comm = None; + let mut large_comm = None; + let mut unknown = vec![]; + let mut withdraw = false; + + // Check whether the 4-octet AS number capability has been negotiated. + let four_byte_asn_cap = cxt + .capabilities + .iter() + .any(|cap| cap.is_four_octet_as_number()); + + // List of parsed attributes. + let mut attr_list = HashSet::new(); + + // Parse attributes. + while buf.remaining() > 0 { + if buf.remaining() < 2 { + withdraw = true; + break; + } + + // Parse attribute flags. + let attr_flags = buf.get_u8(); + let mut attr_flags = AttrFlags::from_bits_truncate(attr_flags); + + // Parse attribute type. + let attr_type_raw = buf.get_u8(); + let attr_type = AttrType::from_u8(attr_type_raw); + + // Parse attribute length. + let attr_len = if attr_flags.contains(AttrFlags::EXTENDED) { + if buf.remaining() < 2 { + withdraw = true; + break; + } + buf.get_u16() as usize + } else { + if buf.remaining() < 1 { + withdraw = true; + break; + } + buf.get_u8() as usize + }; + if attr_len > buf.remaining() { + withdraw = true; + break; + } + let mut buf = buf.copy_to_bytes(attr_len); + + // RFC 7606 - Section 3.c: + // "If the value of either the Optional or Transitive bits in the + // Attribute Flags is in conflict with their specified values, then + // the attribute MUST be treated as malformed and the + // "treat-as-withdraw" approach used". + if let Some(attr_type) = attr_type + && (attr_flags & (AttrFlags::OPTIONAL | AttrFlags::TRANSITIVE)) + != attribute_flags(attr_type) + { + withdraw = true; + continue; + } + + // RFC 7606 - Section 3.g: + // "If the MP_REACH_NLRI attribute or the MP_UNREACH_NLRI attribute + // appears more than once in the UPDATE message, then a NOTIFICATION + // message MUST be sent with the Error Subcode "Malformed Attribute + // List". If any other attribute (whether recognized or + // unrecognized) appears more than once in an UPDATE message, then + // all the occurrences of the attribute other than the first one + // SHALL be discarded and the UPDATE message will continue to be + // processed". + if !attr_list.insert(attr_type_raw) { + if matches!( + attr_type, + Some(AttrType::MpReachNlri | AttrType::MpUnreachNlri) + ) { + return Err(UpdateMessageError::MalformedAttributeList); + } else { + continue; + } + } + + // Parse attribute value. + match attr_type { + // Known attribute. + Some(attr_type) => { + if let Err(error) = match attr_type { + AttrType::Origin => { + origin::decode(&mut buf, &mut origin) + } + AttrType::AsPath => AsPath::decode( + &mut buf, + cxt, + attr_type, + four_byte_asn_cap, + &mut as_path, + ), + AttrType::Nexthop => nexthop::decode(&mut buf, nexthop), + AttrType::Med => med::decode(&mut buf, &mut med), + AttrType::LocalPref => { + local_pref::decode(&mut buf, cxt, &mut local_pref) + } + AttrType::AtomicAggregate => atomic_aggregate::decode( + &mut buf, + &mut atomic_aggregate, + ), + AttrType::Aggregator => Aggregator::decode( + &mut buf, + attr_type, + four_byte_asn_cap, + &mut aggregator, + ), + AttrType::Communities => { + Comms::decode(&mut buf, &mut comm) + } + AttrType::OriginatorId => originator_id::decode( + &mut buf, + cxt, + &mut originator_id, + ), + AttrType::ClusterList => ClusterList::decode( + &mut buf, + cxt, + &mut cluster_list, + ), + AttrType::MpReachNlri => { + MpReachNlri::decode(&mut buf, mp_reach) + } + AttrType::MpUnreachNlri => { + MpUnreachNlri::decode(&mut buf, mp_unreach) + } + AttrType::ExtCommunities => { + ExtComms::decode(&mut buf, &mut ext_comm) + } + AttrType::As4Path => AsPath::decode( + &mut buf, + cxt, + attr_type, + four_byte_asn_cap, + &mut as4_path, + ), + AttrType::As4Aggregator => Aggregator::decode( + &mut buf, + attr_type, + four_byte_asn_cap, + &mut as4_aggregator, + ), + AttrType::Extv6Community => { + Extv6Comms::decode(&mut buf, &mut extv6_comm) + } + AttrType::LargeCommunity => { + LargeComms::decode(&mut buf, &mut large_comm) + } + } { + // Log malformed attribute. + Debug::NbrAttrError(attr_type, error).log(); + + // Process malformed attribute. + match error { + AttrError::Discard => continue, + AttrError::Withdraw => withdraw = true, + AttrError::Reset => { + return Err( + UpdateMessageError::OptionalAttributeError, + ) + } + } + } + } + // Unknown attribute. + None => { + // RFC 4271 - Section 6.3: + // "If any of the well-known mandatory attributes are not + // recognized, then the Error Subcode MUST be set to + // Unrecognized Well-known Attribute. The Data field MUST + // contain the unrecognized attribute (type, length, and + // value)". + if !attr_flags.contains(AttrFlags::OPTIONAL) { + return Err( + UpdateMessageError::UnrecognizedWellKnownAttribute, + ); + } + + // RFC 4271 - Section 9: + // "If an optional non-transitive attribute is unrecognized, + // it is quietly ignored". + if !attr_flags.contains(AttrFlags::TRANSITIVE) { + continue; + } + + // RFC 4271 - Section 9: + // "If an optional transitive attribute is unrecognized, the + // Partial bit in the attribute flags octet is set to 1, and + // the attribute is retained for propagation to other BGP + // speakers". + attr_flags.insert(AttrFlags::PARTIAL); + let attr_value = buf.copy_to_bytes(attr_len); + unknown.push(UnknownAttr::new( + attr_type_raw, + attr_flags, + attr_len as u16, + attr_value, + )); + } + } + } + + // Check for missing well-known attributes. + let mut attrs = None; + if !withdraw + && let Some(origin) = origin + && let Some(as_path) = as_path + && (local_pref.is_some() || cxt.peer_type == PeerType::External) + && (nexthop.is_some() || !nlri_present) + { + attrs = Some(Attrs { + base: BaseAttrs { + origin, + as_path, + as4_path, + nexthop: None, + ll_nexthop: None, + med, + local_pref, + aggregator, + as4_aggregator, + atomic_aggregate, + originator_id, + cluster_list, + }, + comm, + ext_comm, + extv6_comm, + large_comm, + unknown, + }); + } + Ok(attrs) + } +} + +// ===== ORIGIN attribute ===== + +mod origin { + use super::*; + const LEN: u8 = 1; + + pub(super) fn encode(origin: Origin, buf: &mut BytesMut) { + buf.put_u8(AttrFlags::TRANSITIVE.bits()); + buf.put_u8(AttrType::Origin as u8); + buf.put_u8(LEN); + buf.put_u8(origin as u8); + } + + pub(super) fn decode( + buf: &mut Bytes, + origin: &mut Option, + ) -> Result<(), AttrError> { + if buf.remaining() != LEN as usize { + return Err(AttrError::Withdraw); + } + + let value = buf.get_u8(); + match Origin::from_u8(value) { + Some(value) => { + *origin = Some(value); + Ok(()) + } + None => Err(AttrError::Withdraw), + } + } +} + +// ===== impl AsPath ===== + +impl AsPath { + fn encode( + &self, + buf: &mut BytesMut, + mut attr_flags: AttrFlags, + attr_type: AttrType, + four_byte_asns: bool, + ) { + attr_flags.insert(AttrFlags::EXTENDED); + buf.put_u8(attr_flags.bits()); + buf.put_u8(attr_type as u8); + + // The length field will be initialized later. + let start_pos = buf.len(); + buf.put_u16(0); + + // Encode attribute data. + for segment in &self.segments { + segment.encode(buf, four_byte_asns); + } + + // Rewrite attribute length. + let attr_len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&attr_len.to_be_bytes()); + } + + fn decode( + buf: &mut Bytes, + cxt: &DecodeCxt, + attr_type: AttrType, + four_byte_asn_cap: bool, + as_path: &mut Option, + ) -> Result<(), AttrError> { + if attr_type == AttrType::As4Path && four_byte_asn_cap { + return Err(AttrError::Discard); + } + + let four_byte_asns = + four_byte_asn_cap || attr_type == AttrType::As4Path; + + // Decode AS Path segments. + let mut segments = VecDeque::new(); + while buf.remaining() > 0 { + let segment = + AsPathSegment::decode(buf, attr_type, four_byte_asns)?; + segments.push_back(segment); + } + let value = AsPath { segments }; + + // First AS check for eBGP peers. + if cxt.peer_type == PeerType::External + && value.iter(Some(AsPathSegmentType::Sequence)).next() + != Some(cxt.peer_as) + { + if attr_type == AttrType::AsPath { + return Err(AttrError::Withdraw); + } else { + return Err(AttrError::Discard); + } + } + + *as_path = Some(value); + Ok(()) + } + + pub(crate) fn iter( + &self, + filter: Option, + ) -> impl Iterator + '_ { + self.segments + .iter() + .filter(move |segment| { + if let Some(filter) = filter { + segment.seg_type == filter + } else { + true + } + }) + .flat_map(|segment| segment.members.iter().copied()) + } + + pub(crate) fn prepend(&mut self, asn: u32) { + if let Some(segment) = self.segments.front_mut() + && segment.seg_type == AsPathSegmentType::Sequence + && segment.members.len() < 255 + { + segment.members.push_front(asn); + } else { + self.segments.push_front(AsPathSegment { + seg_type: AsPathSegmentType::Sequence, + members: [asn].into(), + }); + } + } + + pub(crate) fn contains(&self, asn: u32) -> bool { + self.segments.iter().any(|segment| segment.contains(asn)) + } +} + +impl AsPathSegment { + fn encode(&self, buf: &mut BytesMut, four_byte_asns: bool) { + buf.put_u8(self.seg_type as u8); + buf.put_u8(self.members.len() as u8); + for member in &self.members { + encode_asn(buf, *member, four_byte_asns); + } + } + + fn decode( + buf: &mut Bytes, + attr_type: AttrType, + four_byte_asns: bool, + ) -> Result { + // Decode segment type. + let seg_type = buf.get_u8(); + let Some(seg_type) = AsPathSegmentType::from_u8(seg_type) else { + if attr_type == AttrType::AsPath { + return Err(AttrError::Withdraw); + } else { + return Err(AttrError::Discard); + } + }; + + // Decode segment length. + let seg_len = buf.get_u8(); + if seg_len == 0 { + if attr_type == AttrType::AsPath { + return Err(AttrError::Withdraw); + } else { + return Err(AttrError::Discard); + } + } + + // Decode segment members. + let members = (0..seg_len as usize) + .map(|_| decode_asn(buf, four_byte_asns)) + .collect(); + let segment = AsPathSegment { seg_type, members }; + + // RFC 7607's AS 0 processing. + if segment.contains(0) { + if attr_type == AttrType::AsPath { + return Err(AttrError::Withdraw); + } else { + return Err(AttrError::Discard); + } + } + + Ok(segment) + } + + fn contains(&self, asn: u32) -> bool { + self.members.iter().any(|member| asn == *member) + } +} + +// ===== NEXT_HOP attribute ===== + +mod nexthop { + use super::*; + const LEN: u8 = 4; + + pub(super) fn encode(addr: Ipv4Addr, buf: &mut BytesMut) { + buf.put_u8(AttrFlags::TRANSITIVE.bits()); + buf.put_u8(AttrType::Nexthop as u8); + buf.put_u8(LEN); + buf.put_ipv4(&addr); + } + + pub(super) fn decode( + buf: &mut Bytes, + nexthop: &mut Option, + ) -> Result<(), AttrError> { + if buf.remaining() != LEN as usize { + return Err(AttrError::Withdraw); + } + + let value = buf.get_ipv4(); + *nexthop = Some(value); + Ok(()) + } +} + +// ===== MULTI_EXIT_DISC attribute ===== + +mod med { + use super::*; + const LEN: u8 = 4; + + pub(super) fn encode(metric: u32, buf: &mut BytesMut) { + buf.put_u8(AttrFlags::OPTIONAL.bits()); + buf.put_u8(AttrType::Med as u8); + buf.put_u8(LEN); + buf.put_u32(metric); + } + + pub(super) fn decode( + buf: &mut Bytes, + med: &mut Option, + ) -> Result<(), AttrError> { + if buf.remaining() != LEN as usize { + return Err(AttrError::Withdraw); + } + + let value = buf.get_u32(); + *med = Some(value); + Ok(()) + } +} + +// ===== LOCAL_PREF attribute ===== + +mod local_pref { + use super::*; + const LEN: u8 = 4; + + pub(super) fn encode(local_pref: u32, buf: &mut BytesMut) { + buf.put_u8(AttrFlags::TRANSITIVE.bits()); + buf.put_u8(AttrType::LocalPref as u8); + buf.put_u8(LEN); + buf.put_u32(local_pref); + } + + pub(super) fn decode( + buf: &mut Bytes, + cxt: &DecodeCxt, + local_pref: &mut Option, + ) -> Result<(), AttrError> { + if cxt.peer_type == PeerType::External { + return Err(AttrError::Discard); + } + + if buf.remaining() != LEN as usize { + return Err(AttrError::Withdraw); + } + + let value = buf.get_u32(); + *local_pref = Some(value); + Ok(()) + } +} + +// ===== ATOMIC_AGGREGATE attribute ===== + +mod atomic_aggregate { + use super::*; + const LEN: u8 = 0; + + pub(super) fn encode(buf: &mut BytesMut) { + buf.put_u8(AttrFlags::TRANSITIVE.bits()); + buf.put_u8(AttrType::AtomicAggregate as u8); + buf.put_u8(LEN); + } + + pub(super) fn decode( + buf: &mut Bytes, + atomic_aggregate: &mut bool, + ) -> Result<(), AttrError> { + if buf.remaining() != LEN as usize { + return Err(AttrError::Discard); + } + + *atomic_aggregate = true; + Ok(()) + } +} + +// ===== impl Aggregator ===== + +impl Aggregator { + fn encode( + &self, + buf: &mut BytesMut, + attr_flags: AttrFlags, + attr_type: AttrType, + four_byte_asns: bool, + ) { + buf.put_u8(attr_flags.bits()); + buf.put_u8(attr_type as u8); + + // The length field will be initialized later. + let start_pos = buf.len(); + buf.put_u8(0); + + // Encode attribute data. + encode_asn(buf, self.asn, four_byte_asns); + buf.put_ipv4(&self.identifier); + + // Rewrite attribute length. + let attr_len = buf.len() - start_pos - 1; + buf[start_pos] = attr_len as u8; + } + + fn decode( + buf: &mut Bytes, + attr_type: AttrType, + four_byte_asn_cap: bool, + aggregator: &mut Option, + ) -> Result<(), AttrError> { + if attr_type == AttrType::As4Aggregator && four_byte_asn_cap { + return Err(AttrError::Discard); + } + + let four_byte_asns = + four_byte_asn_cap || attr_type == AttrType::As4Aggregator; + let len = if four_byte_asns { 8 } else { 6 }; + if buf.remaining() != len { + return Err(AttrError::Discard); + } + + let asn = decode_asn(buf, four_byte_asns); + let identifier = buf.get_ipv4(); + + // RFC 7607's AS 0 processing. + if asn == 0 { + return Err(AttrError::Discard); + } + + *aggregator = Some(Aggregator { asn, identifier }); + Ok(()) + } +} + +// ===== ORIGINATOR_ID attribute ===== + +mod originator_id { + use super::*; + const LEN: u8 = 4; + + pub(super) fn encode(originator_id: Ipv4Addr, buf: &mut BytesMut) { + buf.put_u8(AttrFlags::OPTIONAL.bits()); + buf.put_u8(AttrType::OriginatorId as u8); + buf.put_u8(LEN); + buf.put_ipv4(&originator_id); + } + + pub(super) fn decode( + buf: &mut Bytes, + cxt: &DecodeCxt, + originator_id: &mut Option, + ) -> Result<(), AttrError> { + if cxt.peer_type == PeerType::External { + return Err(AttrError::Discard); + } + + if buf.remaining() != LEN as usize { + return Err(AttrError::Withdraw); + } + + let value = buf.get_ipv4(); + *originator_id = Some(value); + Ok(()) + } +} + +// ===== impl ClusterList ===== + +impl ClusterList { + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8((AttrFlags::OPTIONAL | AttrFlags::EXTENDED).bits()); + buf.put_u8(AttrType::ClusterList as u8); + + // The length field will be initialized later. + let start_pos = buf.len(); + buf.put_u16(0); + + // Encode attribute data. + for cluster_id in &self.0 { + buf.put_ipv4(cluster_id); + } + + // Rewrite attribute length. + let attr_len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&attr_len.to_be_bytes()); + } + + fn decode( + buf: &mut Bytes, + cxt: &DecodeCxt, + cluster_list: &mut Option, + ) -> Result<(), AttrError> { + if cxt.peer_type == PeerType::External { + return Err(AttrError::Discard); + } + + if buf.remaining() == 0 || buf.remaining() % 4 != 0 { + return Err(AttrError::Withdraw); + } + + let mut list = BTreeSet::new(); + while buf.remaining() > 0 { + let cluster_id = buf.get_ipv4(); + list.insert(cluster_id); + } + + *cluster_list = Some(ClusterList(list)); + Ok(()) + } +} + +// ===== impl MpReachNlri ===== + +impl MpReachNlri { + pub const MIN_LEN: u16 = 5; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8((AttrFlags::OPTIONAL | AttrFlags::EXTENDED).bits()); + buf.put_u8(AttrType::MpReachNlri as u8); + + // The length field will be initialized later. + let start_pos = buf.len(); + buf.put_u16(0); + + // Encode attribute data. + match self { + MpReachNlri::Ipv4 { + safi, + prefixes, + nexthop, + } => { + buf.put_u16(Afi::Ipv4 as u16); + buf.put_u8(*safi as u8); + buf.put_u8(Ipv4Addr::LENGTH as u8); + buf.put_ipv4(nexthop); + buf.put_u8(0); + for prefix in prefixes { + encode_ipv4_prefix(buf, prefix); + } + } + MpReachNlri::Ipv6 { + safi, + prefixes, + nexthop, + ll_nexthop, + } => { + buf.put_u16(Afi::Ipv6 as u16); + buf.put_u8(*safi as u8); + if let Some(ll_nexthop) = ll_nexthop { + buf.put_u8((Ipv6Addr::LENGTH * 2) as u8); + buf.put_ipv6(nexthop); + buf.put_ipv6(ll_nexthop); + } else { + buf.put_u8(Ipv6Addr::LENGTH as u8); + buf.put_ipv6(nexthop); + } + buf.put_u8(0); + for prefix in prefixes { + encode_ipv6_prefix(buf, prefix); + } + } + } + + // Rewrite attribute length. + let attr_len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&attr_len.to_be_bytes()); + } + + fn decode( + buf: &mut Bytes, + mp_reach: &mut Option, + ) -> Result<(), AttrError> { + if buf.remaining() < Self::MIN_LEN as usize { + return Err(AttrError::Reset); + } + + // Parse AFI. + let afi = buf.get_u16(); + let Some(afi) = Afi::from_u16(afi) else { + // Ignore unknown AFI. + return Err(AttrError::Discard); + }; + + // Parse SAFI. + let safi = buf.get_u8(); + let Some(safi) = Safi::from_u8(safi) else { + // Ignore unknown SAFI. + return Err(AttrError::Discard); + }; + + match afi { + Afi::Ipv4 => { + let mut prefixes = Vec::new(); + + // Parse nexthop. + let nexthop_len = buf.get_u8(); + if nexthop_len as usize != Ipv4Addr::LENGTH + || nexthop_len as usize > buf.remaining() + { + return Err(AttrError::Reset); + } + let nexthop = buf.get_ipv4(); + + // Parse prefixes. + let _reserved = buf.get_u8(); + while buf.remaining() > 0 { + let prefix = decode_ipv4_prefix(buf) + .map_err(|_| AttrError::Reset)?; + prefixes.push(prefix); + } + + *mp_reach = Some(MpReachNlri::Ipv4 { + safi, + prefixes, + nexthop, + }); + } + Afi::Ipv6 => { + let mut prefixes = Vec::new(); + let mut ll_nexthop = None; + + // Parse nexthops(s). + let nexthop_len = buf.get_u8() as usize; + if (nexthop_len != Ipv6Addr::LENGTH + && nexthop_len != Ipv6Addr::LENGTH * 2) + || nexthop_len > buf.remaining() + { + return Err(AttrError::Reset); + } + let nexthop = buf.get_ipv6(); + if nexthop_len == Ipv6Addr::LENGTH * 2 { + ll_nexthop = Some(buf.get_ipv6()); + } + + // Parse prefixes. + let _reserved = buf.get_u8(); + while buf.remaining() > 0 { + let prefix = decode_ipv6_prefix(buf) + .map_err(|_| AttrError::Reset)?; + prefixes.push(prefix); + } + + *mp_reach = Some(MpReachNlri::Ipv6 { + safi, + prefixes, + nexthop, + ll_nexthop, + }); + } + } + + Ok(()) + } +} + +// ===== impl MpUnreachNlri ===== + +impl MpUnreachNlri { + pub const MIN_LEN: u16 = 3; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8((AttrFlags::OPTIONAL | AttrFlags::EXTENDED).bits()); + buf.put_u8(AttrType::MpUnreachNlri as u8); + + // The length field will be initialized later. + let start_pos = buf.len(); + buf.put_u16(0); + + // Encode attribute data. + match self { + MpUnreachNlri::Ipv4 { safi, prefixes } => { + buf.put_u16(Afi::Ipv4 as u16); + buf.put_u8(*safi as u8); + for prefix in prefixes { + encode_ipv4_prefix(buf, prefix); + } + } + MpUnreachNlri::Ipv6 { safi, prefixes } => { + buf.put_u16(Afi::Ipv6 as u16); + buf.put_u8(*safi as u8); + for prefix in prefixes { + encode_ipv6_prefix(buf, prefix); + } + } + } + + // Rewrite attribute length. + let attr_len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&attr_len.to_be_bytes()); + } + + fn decode( + buf: &mut Bytes, + mp_unreach: &mut Option, + ) -> Result<(), AttrError> { + if buf.remaining() < Self::MIN_LEN as usize { + return Err(AttrError::Reset); + } + + // Parse AFI. + let afi = buf.get_u16(); + let Some(afi) = Afi::from_u16(afi) else { + // Ignore unknown AFI. + return Err(AttrError::Discard); + }; + + // Parse SAFI. + let safi = buf.get_u8(); + let Some(safi) = Safi::from_u8(safi) else { + // Ignore unknown SAFI. + return Err(AttrError::Discard); + }; + + // Parse prefixes. + match afi { + Afi::Ipv4 => { + let mut prefixes = Vec::new(); + + while buf.remaining() > 0 { + let prefix = decode_ipv4_prefix(buf) + .map_err(|_| AttrError::Reset)?; + prefixes.push(prefix); + } + + *mp_unreach = Some(MpUnreachNlri::Ipv4 { safi, prefixes }); + } + Afi::Ipv6 => { + let mut prefixes = Vec::new(); + + while buf.remaining() > 0 { + let prefix = decode_ipv6_prefix(buf) + .map_err(|_| AttrError::Reset)?; + prefixes.push(prefix); + } + + *mp_unreach = Some(MpUnreachNlri::Ipv6 { safi, prefixes }); + } + } + + Ok(()) + } +} + +// ===== impl Comm ===== + +impl CommType for Comm { + const TYPE: AttrType = AttrType::Communities; + const LENGTH: usize = 4; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u32(self.0); + } + + fn decode(buf: &mut Bytes) -> Self { + let value = buf.get_u32(); + Self(value) + } +} + +// ===== impl ExtComm ===== + +impl CommType for ExtComm { + const TYPE: AttrType = AttrType::ExtCommunities; + const LENGTH: usize = 8; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_slice(&self.0); + } + + fn decode(buf: &mut Bytes) -> Self { + let mut value = [0; 8]; + buf.copy_to_slice(&mut value); + Self(value) + } +} + +// ===== impl Extv6Comm ===== + +impl CommType for Extv6Comm { + const TYPE: AttrType = AttrType::Extv6Community; + const LENGTH: usize = 20; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_ipv6(&self.0); + buf.put_u32(self.1); + } + + fn decode(buf: &mut Bytes) -> Self { + let addr = buf.get_ipv6(); + let local = buf.get_u32(); + Self(addr, local) + } +} + +// ===== impl LargeComm ===== + +impl CommType for LargeComm { + const TYPE: AttrType = AttrType::LargeCommunity; + const LENGTH: usize = 12; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_slice(&self.0); + } + + fn decode(buf: &mut Bytes) -> Self { + let mut value = [0; 12]; + buf.copy_to_slice(&mut value); + Self(value) + } +} + +// ===== impl CommList ===== + +impl CommList { + fn encode(&self, buf: &mut BytesMut) { + let attr_flags = + AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL | AttrFlags::EXTENDED; + buf.put_u8(attr_flags.bits()); + buf.put_u8(T::TYPE as u8); + + // The length field will be initialized later. + let start_pos = buf.len(); + buf.put_u16(0); + + // Encode attribute data. + for value in &self.0 { + value.encode(buf); + } + + // Rewrite attribute length. + let attr_len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&attr_len.to_be_bytes()); + } + + fn decode( + buf: &mut Bytes, + comm: &mut Option, + ) -> Result<(), AttrError> { + if buf.remaining() == 0 || buf.remaining() % T::LENGTH != 0 { + return Err(AttrError::Withdraw); + } + + let mut list = BTreeSet::new(); + while buf.remaining() >= T::LENGTH { + let value = T::decode(buf); + list.insert(value); + } + + *comm = Some(CommList(list)); + Ok(()) + } +} + +// ===== helper functions ===== + +fn attribute_flags(attr_type: AttrType) -> AttrFlags { + match attr_type { + // Well-known. + AttrType::Origin + | AttrType::AsPath + | AttrType::Nexthop + | AttrType::LocalPref + | AttrType::AtomicAggregate => AttrFlags::TRANSITIVE, + + // Optional non-transitive. + AttrType::Med + | AttrType::OriginatorId + | AttrType::ClusterList + | AttrType::MpReachNlri + | AttrType::MpUnreachNlri => AttrFlags::OPTIONAL, + + // Optional transitive. + AttrType::Aggregator + | AttrType::Communities + | AttrType::ExtCommunities + | AttrType::As4Path + | AttrType::As4Aggregator + | AttrType::Extv6Community + | AttrType::LargeCommunity => { + AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL + } + } +} + +fn encode_asn(buf: &mut BytesMut, asn: u32, four_byte_asns: bool) { + if four_byte_asns { + buf.put_u32(asn) + } else { + buf.put_u16(asn as u16) + } +} + +fn decode_asn(buf: &mut Bytes, four_byte_asns: bool) -> u32 { + if four_byte_asns { + buf.get_u32() + } else { + buf.get_u16() as u32 + } +} diff --git a/holo-bgp/src/packet/consts.rs b/holo-bgp/src/packet/consts.rs new file mode 100644 index 00000000..6d5f22f6 --- /dev/null +++ b/holo-bgp/src/packet/consts.rs @@ -0,0 +1,338 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use bitflags::bitflags; +use holo_utils::ip::AddressFamily; +use num_derive::{FromPrimitive, ToPrimitive}; +use serde::{Deserialize, Serialize}; + +pub const BGP_VERSION: u8 = 4; +pub const AS_TRANS: u16 = 23456; + +// BGP Message Types. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-1 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum MessageType { + Open = 1, + Update = 2, + Notification = 3, + Keepalive = 4, + // RFC 2918 + RouteRefresh = 5, +} + +// BGP OPEN Optional Parameter Types. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-11 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum OpenParamType { + // RFC5492 + Capabilities = 2, +} + +// Capability Codes. +// +// IANA registry: +// https://www.iana.org/assignments/capability-codes/capability-codes.xhtml#capability-codes-2 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum CapabilityCode { + // RFC 2858 + MultiProtocol = 1, + // RFC 2918 + RouteRefresh = 2, + // RFC 5291 + OutboundRouteFiltering = 3, + // RFC 8950 + ExtendedNextHop = 5, + // RFC 8654 + ExtendedMessage = 6, + // RFC 8205 + BgpSec = 7, + // RFC 8277 + MultipleLabels = 8, + // RFC 9234 + BgpRole = 9, + // RFC 4724 + GracefulRestart = 64, + // RFC 6793 + FourOctetAsNumber = 65, + // RFC7911 + AddPath = 69, + // RFC7313 + EnhancedRouteRefresh = 70, +} + +// Send/Receive value for a per-AFI/SAFI instance of the ADD-PATH Capability. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum AddPathMode { + Receive = 1, + Send = 2, + ReceiveSend = 3, +} + +// BGP Error (Notification) Codes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum ErrorCode { + MessageHeaderError = 1, + OpenMessageError = 2, + UpdateMessageError = 3, + HoldTimerExpired = 4, + FiniteStateMachineError = 5, + Cease = 6, + // RFC 7313 + RouteRefreshMessageError = 7, +} + +// Message Header Error subcodes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-5 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum MessageHeaderErrorSubcode { + Unspecific = 0, + ConnectionNotSynchronized = 1, + BadMessageLength = 2, + BadMessageType = 3, +} + +// OPEN Message Error subcodes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-6 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum OpenMessageErrorSubcode { + Unspecific = 0, + UnsupportedVersionNumber = 1, + BadPeerAs = 2, + BadBgpIdentifier = 3, + UnsupportedOptParam = 4, + UnacceptableHoldTime = 6, + // RFC 5492 + UnsupportedCapability = 7, + // RFC 9234 + RoleMismatch = 11, +} + +// UPDATE Message Error subcodes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-7 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum UpdateMessageErrorSubcode { + Unspecific = 0, + MalformedAttributeList = 1, + UnrecognizedWellKnownAttribute = 2, + MissingWellKnownAttribute = 3, + AttributeFlagsError = 4, + AttributeLengthError = 5, + InvalidOriginAttribute = 6, + InvalidNexthopAttribute = 8, + OptionalAttributeError = 9, + InvalidNetworkField = 10, + MalformedAsPath = 11, +} + +// BGP Finite State Machine Error Subcodes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-finite-state-machine-error-subcodes +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum FsmErrorSubcode { + UnexpectedMessageInOpenSent = 1, + UnexpectedMessageInOpenConfirm = 2, + UnexpectedMessageInEstablished = 3, +} + +// BGP Cease NOTIFICATION message subcodes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-8 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum CeaseSubcode { + MaximumNumberofPrefixesReached = 1, + AdministrativeShutdown = 2, + PeerDeConfigured = 3, + AdministrativeReset = 4, + ConnectionRejected = 5, + OtherConfigurationChange = 6, + ConnectionCollisionResolution = 7, + OutOfResources = 8, + // RFC 8538 + HardReset = 9, + // RFC 9384 + BfdDown = 10, +} + +// BGP ROUTE-REFRESH Message Error subcodes. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#route-refresh-error-subcodes +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum RouteRefreshErrorSubcode { + InvalidMessageLength = 1, +} + +// Address Family identifiers (AFI). +pub type Afi = AddressFamily; + +// Subsequent Address Family Identifiers (SAFI). +// +// IANA registry: +// https://www.iana.org/assignments/safi-namespace/safi-namespace.xhtml#safi-namespace-2 +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum Safi { + Unicast = 1, + Multicast = 2, + LabeledUnicast = 4, + MulticastVpn = 5, + Pseudowire = 6, + TunnelEncap = 7, + McastVpls = 8, + Tunnel = 64, + Vpls = 65, + Mdt = 66, + V4OverV6 = 67, + V6OverV4 = 68, + L1VpnAutoDiscovery = 69, + Evpn = 70, + BgpLs = 71, + BgpLsVpn = 72, + SrTe = 73, + SdWanCapabilities = 74, + LabeledVpn = 128, + MulticastMplsVpn = 129, + RouteTarget = 132, + Ipv4FlowSpec = 133, + Vpnv4FlowSpec = 134, + VpnAutoDiscovery = 140, +} + +// BGP Path Attribute Flags. +bitflags! { + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] + #[serde(transparent)] + pub struct AttrFlags: u8 { + const OPTIONAL = 0x80; + const TRANSITIVE = 0x40; + const PARTIAL = 0x20; + const EXTENDED = 0x10; + } +} + +// BGP Path Attribute Types. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-2 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum AttrType { + Origin = 1, + AsPath = 2, + Nexthop = 3, + Med = 4, + LocalPref = 5, + AtomicAggregate = 6, + Aggregator = 7, + // RFC 1997 + Communities = 8, + // RFC 4456 + OriginatorId = 9, + ClusterList = 10, + // RFC 4760 + MpReachNlri = 14, + MpUnreachNlri = 15, + // RFC 4360 + ExtCommunities = 16, + // RFC 6793 + As4Path = 17, + As4Aggregator = 18, + // RFC 6514 + //PmsiTunnel = 22, + // RFC 9012 + //TunnelEncap = 23, + // RFC 5543 + //TrafficEngineering = 24, + // RFC 5701 + Extv6Community = 25, + // RFC 7311 + //Aigp = 26, + // RFC 6514 + //PeDistinguisherLabels = 27, + // RFC-ietf-idr-rfc7752bis-16 + //BgpLs = 29, + // RFC 8092 + LargeCommunity = 32, + // RFC 8205 + //BgpSecPath = 33, + // RFC 9234 + //Otc = 35, + // RFC 9015 + //Sfp = 37, + // RFC 9026 + //BfdDiscriminator = 38, + // RFC 8669 + //BgpPrefixSid = 40, + // RFC6 368 + //AttrSet = 128, +} + +// BGP Origin. +pub type Origin = holo_utils::bgp::Origin; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum AsPathSegmentType { + Set = 1, + Sequence = 2, + ConfedSequence = 3, + ConfedSet = 4, +} + +// Re-exports for convenience. +pub type WellKnownCommunities = holo_utils::bgp::WellKnownCommunities; + +// BGP AIGP Attribute Types. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-aigp +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum AigpType { + Aigp = 1, +} diff --git a/holo-bgp/src/packet/error.rs b/holo-bgp/src/packet/error.rs new file mode 100644 index 00000000..4c4c170a --- /dev/null +++ b/holo-bgp/src/packet/error.rs @@ -0,0 +1,166 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use serde::{Deserialize, Serialize}; + +// Type aliases. +pub type DecodeResult = Result; + +// BGP message decoding errors. +#[derive(Debug)] +#[derive(Deserialize, Serialize)] +pub enum DecodeError { + MessageHeader(MessageHeaderError), + OpenMessage(OpenMessageError), + UpdateMessage(UpdateMessageError), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum MessageHeaderError { + ConnectionNotSynchronized, + BadMessageLength(u16), + BadMessageType(u8), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum OpenMessageError { + UnsupportedVersion(u8), + BadPeerAs, + BadBgpIdentifier, + UnsupportedOptParam, + UnacceptableHoldTime, + UnsupportedCapability, + MalformedOptParam, +} + +// UPDATE message errors. +// +// NOTE: many of the errors originally specified by RFC 4271 were made obsolete +// by RFC 7606. +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum UpdateMessageError { + MalformedAttributeList, + UnrecognizedWellKnownAttribute, + OptionalAttributeError, + InvalidNetworkField, +} + +// Attribute errors. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum AttrError { + Discard, + Withdraw, + Reset, +} + +// ===== impl DecodeError ===== + +impl std::fmt::Display for DecodeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DecodeError::MessageHeader(error) => error.fmt(f), + DecodeError::OpenMessage(error) => error.fmt(f), + DecodeError::UpdateMessage(error) => error.fmt(f), + } + } +} + +impl std::error::Error for DecodeError {} + +impl From for DecodeError { + fn from(error: MessageHeaderError) -> DecodeError { + DecodeError::MessageHeader(error) + } +} + +impl From for DecodeError { + fn from(error: OpenMessageError) -> DecodeError { + DecodeError::OpenMessage(error) + } +} + +impl From for DecodeError { + fn from(error: UpdateMessageError) -> DecodeError { + DecodeError::UpdateMessage(error) + } +} + +// ===== impl MessageHeaderError ===== + +impl std::fmt::Display for MessageHeaderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MessageHeaderError::ConnectionNotSynchronized => { + write!(f, "Connection not synchronized") + } + MessageHeaderError::BadMessageLength(len) => { + write!(f, "Invalid message length: {}", len) + } + MessageHeaderError::BadMessageType(msg_type) => { + write!(f, "Invalid message type: {}", msg_type) + } + } + } +} + +// ===== impl OpenMessageError ===== + +impl std::fmt::Display for OpenMessageError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "OPEN message error: ")?; + + match self { + OpenMessageError::UnsupportedVersion(version) => { + write!(f, "unsupported version number: {}", version) + } + OpenMessageError::BadPeerAs => { + write!(f, "bad peer AS") + } + OpenMessageError::BadBgpIdentifier => { + write!(f, "bad BGP identifier") + } + OpenMessageError::UnsupportedOptParam => { + write!(f, "unsupported optional parameter") + } + OpenMessageError::UnacceptableHoldTime => { + write!(f, "unacceptable hold time") + } + OpenMessageError::UnsupportedCapability => { + write!(f, "unsupported capability") + } + OpenMessageError::MalformedOptParam => { + write!(f, "malformed optional parameter") + } + } + } +} + +// ===== impl UpdateMessageError ===== + +impl std::fmt::Display for UpdateMessageError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "UPDATE message error: ")?; + + match self { + UpdateMessageError::MalformedAttributeList => { + write!(f, "malformed attribute list") + } + UpdateMessageError::UnrecognizedWellKnownAttribute => { + write!(f, "unrecognized well-known attribute") + } + UpdateMessageError::OptionalAttributeError => { + write!(f, "optional attribute error") + } + UpdateMessageError::InvalidNetworkField => { + write!(f, "invalid network field") + } + } + } +} diff --git a/holo-bgp/src/packet/message.rs b/holo-bgp/src/packet/message.rs new file mode 100644 index 00000000..ccf0d3e5 --- /dev/null +++ b/holo-bgp/src/packet/message.rs @@ -0,0 +1,999 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::BTreeSet; +use std::net::{Ipv4Addr, Ipv6Addr}; + +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use enum_as_inner::EnumAsInner; +use holo_utils::bytes::{BytesExt, BytesMutExt, TLS_BUF}; +use holo_utils::ip::{ + Ipv4AddrExt, Ipv4NetworkExt, Ipv6AddrExt, Ipv6NetworkExt, +}; +use ipnetwork::{Ipv4Network, Ipv6Network}; +use num_traits::{FromPrimitive, ToPrimitive}; +use serde::{Deserialize, Serialize}; + +use crate::neighbor::PeerType; +use crate::packet::attribute::Attrs; +use crate::packet::consts::{ + AddPathMode, Afi, CapabilityCode, ErrorCode, MessageHeaderErrorSubcode, + MessageType, OpenMessageErrorSubcode, OpenParamType, Safi, + UpdateMessageErrorSubcode, BGP_VERSION, +}; +use crate::packet::error::{ + DecodeError, DecodeResult, MessageHeaderError, OpenMessageError, + UpdateMessageError, +}; + +// +// BGP message. +// +// Encoding format (message header): +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + + +// | | +// + + +// | Marker | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Length | Type | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum Message { + Open(OpenMsg), + Update(UpdateMsg), + Notification(NotificationMsg), + Keepalive(KeepaliveMsg), + RouteRefresh(RouteRefreshMsg), +} + +// +// OPEN Message. +// +// Encoding format (message body): +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+ +// | Version | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | My Autonomous System | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Hold Time | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BGP Identifier | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Opt Parm Len | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// | Optional Parameters (variable) | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Encoding format (optional parameter): +// +// 0 1 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-... +// | Parm. Type | Parm. Length | Parameter Value (variable) +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-... +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct OpenMsg { + pub version: u8, + pub my_as: u16, + pub holdtime: u16, + pub identifier: Ipv4Addr, + pub capabilities: BTreeSet, +} +// +// Capabilities Optional Parameter. +// +// Encoding format: +// +// +------------------------------+ +// | Capability Code (1 octet) | +// +------------------------------+ +// | Capability Length (1 octet) | +// +------------------------------+ +// | Capability Value (variable) | +// ~ ~ +// +------------------------------+ +// +#[derive(Clone, Debug, EnumAsInner, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum Capability { + MultiProtocol { afi: Afi, safi: Safi }, + FourOctetAsNumber { asn: u32 }, + AddPath(BTreeSet), + RouteRefresh, + EnhancedRouteRefresh, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct AddPathTuple { + pub afi: Afi, + pub safi: Safi, + pub mode: AddPathMode, +} + +// +// UPDATE Message. +// +// Encoding format (message body): +// +// +-----------------------------------------------------+ +// | Withdrawn Routes Length (2 octets) | +// +-----------------------------------------------------+ +// | Withdrawn Routes (variable) | +// +-----------------------------------------------------+ +// | Total Path Attribute Length (2 octets) | +// +-----------------------------------------------------+ +// | Path Attributes (variable) | +// +-----------------------------------------------------+ +// | Network Layer Reachability Information (variable) | +// +-----------------------------------------------------+ +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct UpdateMsg { + pub reach: Option, + pub unreach: Option, + pub mp_reach: Option, + pub mp_unreach: Option, + pub attrs: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct ReachNlri { + pub prefixes: Vec, + pub nexthop: Ipv4Addr, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct UnreachNlri { + pub prefixes: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum MpReachNlri { + Ipv4 { + safi: Safi, + prefixes: Vec, + nexthop: Ipv4Addr, + }, + Ipv6 { + safi: Safi, + prefixes: Vec, + nexthop: Ipv6Addr, + ll_nexthop: Option, + }, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum MpUnreachNlri { + Ipv4 { + safi: Safi, + prefixes: Vec, + }, + Ipv6 { + safi: Safi, + prefixes: Vec, + }, +} + +// +// NOTIFICATION Message. +// +// Encoding format (message body): +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Error code | Error subcode | Data (variable) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct NotificationMsg { + pub error_code: u8, + pub error_subcode: u8, + pub data: Vec, +} + +// +// KEEPALIVE Message. +// +// A KEEPALIVE message consists of only the message header and has a length of +// 19 octets. +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct KeepaliveMsg {} + +// +// Route-REFRESH Message. +// +// Encoding format (message body): +// +// 0 7 15 23 31 +// +-------+-------+-------+-------+ +// | AFI | Res. | SAFI | +// +-------+-------+-------+-------+ +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct RouteRefreshMsg { + pub afi: u16, + pub safi: u8, +} + +// BGP message decoding context. +pub struct EncodeCxt { + pub capabilities: BTreeSet, +} + +// BGP message decoding context. +pub struct DecodeCxt { + pub peer_type: PeerType, + pub peer_as: u32, + pub capabilities: BTreeSet, +} + +// ===== impl Message ===== + +impl Message { + pub const MIN_LEN: u16 = 19; + pub const MAX_LEN: u16 = 4096; + const MSG_LEN_POS: std::ops::Range = 16..18; + + // Encodes BGP message into a bytes buffer. + pub fn encode(&self, cxt: &EncodeCxt) -> Bytes { + TLS_BUF.with(|buf| { + let mut buf = buf.borrow_mut(); + buf.clear(); + + // Marker field. + buf.put_u128(u128::MAX); + // The length field will be initialized later. + buf.put_u16(0); + + // Message type and body. + match self { + Message::Open(msg) => msg.encode(&mut buf), + Message::Update(msg) => msg.encode(&mut buf, cxt), + Message::Notification(msg) => msg.encode(&mut buf), + Message::Keepalive(msg) => msg.encode(&mut buf), + Message::RouteRefresh(msg) => msg.encode(&mut buf), + } + + // Rewrite message length. + let msg_len = buf.len() as u16; + buf[Self::MSG_LEN_POS].copy_from_slice(&msg_len.to_be_bytes()); + + buf.clone().freeze() + }) + } + + // Decode buffer into a BGP message. + // + // This function panics if the provided buffer doesn't contain an entire + // message. + pub fn decode(data: &[u8], cxt: &DecodeCxt) -> DecodeResult { + let mut buf = Bytes::copy_from_slice(data); + + // Parse and validate marker. + let marker = buf.get_u128(); + if marker != u128::MAX { + return Err(MessageHeaderError::ConnectionNotSynchronized.into()); + } + + // Parse and validate message length. + let msg_len = buf.get_u16(); + if msg_len < Self::MIN_LEN || msg_len > Self::MAX_LEN { + return Err(MessageHeaderError::BadMessageLength(msg_len).into()); + } + + // Parse message type. + let msg_type = buf.get_u8(); + + // Parse message body. + match MessageType::from_u8(msg_type) { + Some(MessageType::Open) => { + let msg = OpenMsg::decode(&mut buf, msg_len)?; + Ok(Message::Open(msg)) + } + Some(MessageType::Update) => { + let msg = UpdateMsg::decode(&mut buf, msg_len, cxt)?; + Ok(Message::Update(msg)) + } + Some(MessageType::Notification) => { + let msg = NotificationMsg::decode(&mut buf, msg_len)?; + Ok(Message::Notification(msg)) + } + Some(MessageType::Keepalive) => { + let msg = KeepaliveMsg::decode(&mut buf, msg_len)?; + Ok(Message::Keepalive(msg)) + } + Some(MessageType::RouteRefresh) => { + let msg = RouteRefreshMsg::decode(&mut buf, msg_len)?; + Ok(Message::RouteRefresh(msg)) + } + None => Err(MessageHeaderError::BadMessageType(msg_type).into()), + } + } + + // Parses the given buffer to determine if it contains a complete BGP + // message, and returns the length of the message if successful. + pub fn get_message_len(data: &[u8]) -> Option { + // Validate that the buffer contains sufficient space for at least the + // message header. + let buf_size = data.len(); + if buf_size < Self::MIN_LEN as usize { + return None; + } + + // Ensure the buffer is big enough to hold the entire message. + let mut buf = Bytes::copy_from_slice(&data[0..Self::MIN_LEN as usize]); + let _marker = buf.get_u128(); + let msg_len = buf.get_u16(); + if msg_len < Self::MIN_LEN || msg_len as usize > buf_size { + return None; + } + + // Return the message size. + Some(msg_len as usize) + } +} + +// ===== impl OpenMsg ===== + +impl OpenMsg { + const MIN_LEN: u16 = 29; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8(MessageType::Open as u8); + buf.put_u8(self.version); + buf.put_u16(self.my_as); + buf.put_u16(self.holdtime); + buf.put_ipv4(&self.identifier); + + // Capabilities. + let opt_param_len_pos = buf.len(); + buf.put_u8(0); + for capability in &self.capabilities { + buf.put_u8(OpenParamType::Capabilities as u8); + + // The "Parm. Length" field will be initialized later. + let param_len_pos = buf.len(); + buf.put_u8(0); + + // Encode individual capability. + capability.encode(buf); + + // Rewrite the "Parm. Length" field. + let param_len = buf.len() - param_len_pos - 1; + buf[param_len_pos] = param_len as u8; + } + + // Rewrite the "Opt Parm Len" field. + let opt_param_len = buf.len() - opt_param_len_pos - 1; + buf[opt_param_len_pos] = opt_param_len as u8; + } + + fn decode(buf: &mut Bytes, msg_len: u16) -> DecodeResult { + if msg_len < Self::MIN_LEN { + return Err(MessageHeaderError::BadMessageLength(msg_len).into()); + } + + // Parse and validate BGP version. + let version = buf.get_u8(); + if version != BGP_VERSION { + return Err( + OpenMessageError::UnsupportedVersion(BGP_VERSION).into() + ); + } + + // Parse and validate ASN. + let my_as = buf.get_u16(); + if my_as == 0 { + return Err(OpenMessageError::BadPeerAs.into()); + } + + // Parse and validate hold time. + let holdtime = buf.get_u16(); + if holdtime == 1 || holdtime == 2 { + return Err(OpenMessageError::UnacceptableHoldTime.into()); + } + + // Parse and validate BGP identifier. + let identifier = buf.get_ipv4(); + if identifier.is_unspecified() + || identifier.is_multicast() + || identifier.is_broadcast() + { + return Err(OpenMessageError::BadBgpIdentifier.into()); + } + + // Parse and validate optional parameters. + let mut capabilities = BTreeSet::new(); + let opt_param_len = buf.get_u8(); + if opt_param_len as usize > buf.remaining() { + return Err(OpenMessageError::MalformedOptParam.into()); + } + let mut buf_opts = buf.copy_to_bytes(opt_param_len as usize); + while buf_opts.remaining() > 0 { + if buf_opts.remaining() < 2 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + let param_type = buf_opts.get_u8(); + let param_len = buf_opts.get_u8(); + if param_len as usize > buf_opts.remaining() { + return Err(OpenMessageError::MalformedOptParam.into()); + } + let mut buf_param_value = + buf_opts.copy_to_bytes(param_len as usize); + + // Parse and validate capabilities. + match OpenParamType::from_u8(param_type) { + Some(OpenParamType::Capabilities) => { + while buf_param_value.remaining() > 0 { + if let Some(cap) = + Capability::decode(&mut buf_param_value)? + { + capabilities.insert(cap); + } + } + } + None => { + return Err(OpenMessageError::UnsupportedOptParam.into()); + } + } + } + + Ok(OpenMsg { + version, + my_as, + holdtime, + identifier, + capabilities, + }) + } + + pub fn real_as(&self) -> u32 { + self.capabilities + .iter() + .find_map(|cap| { + if let Capability::FourOctetAsNumber { asn } = cap { + Some(*asn) + } else { + None + } + }) + .unwrap_or(self.my_as as u32) + } +} + +// ===== impl Capability ===== + +impl Capability { + fn encode(&self, buf: &mut BytesMut) { + let start_pos = buf.len(); + + match self { + Capability::MultiProtocol { afi, safi } => { + buf.put_u8(CapabilityCode::MultiProtocol as u8); + buf.put_u8(0); + buf.put_u16(*afi as u16); + buf.put_u8(0); + buf.put_u8(*safi as u8); + } + Capability::FourOctetAsNumber { asn } => { + buf.put_u8(CapabilityCode::FourOctetAsNumber as u8); + buf.put_u8(0); + buf.put_u32(*asn); + } + Capability::AddPath(tuples) => { + buf.put_u8(CapabilityCode::AddPath as u8); + buf.put_u8(0); + for tuple in tuples { + buf.put_u16(tuple.afi as u16); + buf.put_u8(tuple.safi as u8); + buf.put_u8(tuple.mode as u8); + } + } + Capability::RouteRefresh => { + buf.put_u8(CapabilityCode::RouteRefresh as u8); + buf.put_u8(0); + } + Capability::EnhancedRouteRefresh => { + buf.put_u8(CapabilityCode::EnhancedRouteRefresh as u8); + buf.put_u8(0); + } + } + + // Rewrite the "Capability Length" field. + let cap_len = buf.len() - start_pos - 2; + buf[start_pos + 1] = cap_len as u8; + } + + fn decode(buf: &mut Bytes) -> DecodeResult> { + if buf.remaining() < 2 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + let cap_type = buf.get_u8(); + let cap_len = buf.get_u8(); + if cap_len as usize > buf.remaining() { + return Err(OpenMessageError::MalformedOptParam.into()); + } + + let mut buf_cap = buf.copy_to_bytes(cap_len as usize); + let cap = match CapabilityCode::from_u8(cap_type) { + Some(CapabilityCode::MultiProtocol) => { + if cap_len != 4 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + + let afi = buf_cap.get_u16(); + let Some(afi) = Afi::from_u16(afi) else { + // Ignore unknown AFI. + return Ok(None); + }; + let _reserved = buf_cap.get_u8(); + let safi = buf_cap.get_u8(); + let Some(safi) = Safi::from_u8(safi) else { + // Ignore unknown SAFI. + return Ok(None); + }; + + Capability::MultiProtocol { afi, safi } + } + Some(CapabilityCode::FourOctetAsNumber) => { + if cap_len != 4 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + + let asn = buf_cap.get_u32(); + Capability::FourOctetAsNumber { asn } + } + Some(CapabilityCode::AddPath) => { + if cap_len % 4 != 0 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + + let mut tuples = BTreeSet::new(); + while buf_cap.remaining() > 0 { + let afi = buf_cap.get_u16(); + let Some(afi) = Afi::from_u16(afi) else { + // Ignore unknown AFI. + return Ok(None); + }; + let safi = buf_cap.get_u8(); + let Some(safi) = Safi::from_u8(safi) else { + // Ignore unknown SAFI. + return Ok(None); + }; + let mode = buf_cap.get_u8(); + let Some(mode) = AddPathMode::from_u8(mode) else { + // Ignore unknown value. + return Ok(None); + }; + tuples.insert(AddPathTuple { afi, safi, mode }); + } + Capability::AddPath(tuples) + } + Some(CapabilityCode::RouteRefresh) => { + if cap_len != 0 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + + Capability::RouteRefresh + } + Some(CapabilityCode::EnhancedRouteRefresh) => { + if cap_len != 0 { + return Err(OpenMessageError::MalformedOptParam.into()); + } + + Capability::EnhancedRouteRefresh + } + _ => { + // Ignore unknown capability. + return Ok(None); + } + }; + + Ok(Some(cap)) + } + + pub fn code(&self) -> CapabilityCode { + match self { + Capability::MultiProtocol { .. } => CapabilityCode::MultiProtocol, + Capability::FourOctetAsNumber { .. } => { + CapabilityCode::FourOctetAsNumber + } + Capability::AddPath { .. } => CapabilityCode::AddPath, + Capability::RouteRefresh => CapabilityCode::RouteRefresh, + Capability::EnhancedRouteRefresh => { + CapabilityCode::EnhancedRouteRefresh + } + } + } +} + +// ===== impl UpdateMsg ===== + +impl UpdateMsg { + const MIN_LEN: u16 = 23; + + fn encode(&self, buf: &mut BytesMut, cxt: &EncodeCxt) { + buf.put_u8(MessageType::Update as u8); + + // Withdrawn Routes. + let start_pos = buf.len(); + buf.put_u16(0); + if let Some(unreach) = &self.unreach { + // Encode prefixes. + for prefix in &unreach.prefixes { + let plen = prefix.prefix(); + let prefix_bytes = prefix.ip().octets(); + let plen_wire = prefix_wire_len(plen); + buf.put_u8(plen); + buf.put(&prefix_bytes[0..plen_wire]); + } + + // Rewrite the "Withdrawn Routes Length" field. + let len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&len.to_be_bytes()); + } + + // Path Attributes. + let start_pos = buf.len(); + buf.put_u16(0); + if let Some(attrs) = &self.attrs { + // Encode path attributes. + attrs.encode( + buf, + &self.reach, + &self.mp_reach, + &self.mp_unreach, + cxt, + ); + + // Rewrite the "Total Path Attribute Length" field. + let len = (buf.len() - start_pos - 2) as u16; + buf[start_pos..start_pos + 2].copy_from_slice(&len.to_be_bytes()); + } + + // Network Layer Reachability Information. + if let Some(reach) = &self.reach { + // Encode prefixes. + for prefix in &reach.prefixes { + let plen = prefix.prefix(); + let prefix_bytes = prefix.ip().octets(); + let plen_wire = prefix_wire_len(plen); + buf.put_u8(plen); + buf.put(&prefix_bytes[0..plen_wire]); + } + } + } + + fn decode( + buf: &mut Bytes, + msg_len: u16, + cxt: &DecodeCxt, + ) -> DecodeResult { + if msg_len < Self::MIN_LEN { + return Err(MessageHeaderError::BadMessageLength(msg_len).into()); + } + + let mut reach = None; + let mut unreach = None; + let mut mp_reach = None; + let mut mp_unreach = None; + let mut attrs = None; + let mut nexthop = None; + + // Withdrawn Routes Length. + let wdraw_len = buf.get_u16(); + if wdraw_len as usize > buf.remaining() { + return Err(UpdateMessageError::MalformedAttributeList.into()); + } + + // Withdrawn Routes. + let mut buf_wdraw = buf.copy_to_bytes(wdraw_len as usize); + let mut prefixes = Vec::new(); + while buf_wdraw.remaining() > 0 { + let prefix = decode_ipv4_prefix(&mut buf_wdraw)?; + prefixes.push(prefix); + } + if !prefixes.is_empty() { + unreach = Some(UnreachNlri { prefixes }); + } + + // Total Path Attribute Length. + if buf.remaining() < 2 { + return Err(UpdateMessageError::MalformedAttributeList.into()); + } + let attr_len = buf.get_u16(); + if attr_len as usize > buf.remaining() { + return Err(UpdateMessageError::MalformedAttributeList.into()); + } + + // Path Attributes. + if attr_len != 0 { + let mut buf_attr = buf.copy_to_bytes(attr_len as usize); + let nlri_present = (msg_len - wdraw_len - attr_len) > 0; + attrs = Attrs::decode( + &mut buf_attr, + cxt, + &mut nexthop, + nlri_present, + &mut mp_unreach, + &mut mp_reach, + )?; + } + + // Network Layer Reachability Information. + let mut prefixes = Vec::new(); + while buf.remaining() > 0 { + let prefix = decode_ipv4_prefix(buf)?; + prefixes.push(prefix); + } + if !prefixes.is_empty() { + reach = Some(ReachNlri { + prefixes, + nexthop: nexthop.unwrap(), + }); + } + + Ok(UpdateMsg { + reach, + unreach, + mp_reach, + mp_unreach, + attrs, + }) + } +} + +// ===== impl NotificationMsg ===== + +impl NotificationMsg { + const MIN_LEN: u16 = 21; + + pub(crate) fn new( + error_code: impl ToPrimitive, + error_subcode: impl ToPrimitive, + ) -> Self { + NotificationMsg { + error_code: error_code.to_u8().unwrap(), + error_subcode: error_subcode.to_u8().unwrap(), + data: Default::default(), + } + } + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8(MessageType::Notification as u8); + buf.put_u8(self.error_code); + buf.put_u8(self.error_subcode); + buf.put_slice(&self.data); + } + + fn decode(buf: &mut Bytes, msg_len: u16) -> DecodeResult { + if msg_len < Self::MIN_LEN { + return Err(MessageHeaderError::BadMessageLength(msg_len).into()); + } + + let error_code = buf.get_u8(); + let error_subcode = buf.get_u8(); + let data_len = msg_len - Self::MIN_LEN; + let data = buf.copy_to_bytes(data_len as usize).to_vec(); + + Ok(NotificationMsg { + error_code, + error_subcode, + data, + }) + } +} + +impl From for NotificationMsg { + fn from(error: DecodeError) -> NotificationMsg { + let error_code; + let error_subcode; + let data = vec![]; + + match error { + DecodeError::MessageHeader(error) => { + error_code = ErrorCode::MessageHeaderError as u8; + error_subcode = match error { + MessageHeaderError::ConnectionNotSynchronized => { + MessageHeaderErrorSubcode::ConnectionNotSynchronized + } + MessageHeaderError::BadMessageLength(..) => { + MessageHeaderErrorSubcode::BadMessageLength + } + MessageHeaderError::BadMessageType(..) => { + MessageHeaderErrorSubcode::BadMessageType + } + } as u8; + } + DecodeError::OpenMessage(error) => { + error_code = ErrorCode::OpenMessageError as u8; + error_subcode = match error { + OpenMessageError::UnsupportedVersion(..) => { + OpenMessageErrorSubcode::UnsupportedVersionNumber + } + OpenMessageError::BadPeerAs => { + OpenMessageErrorSubcode::BadPeerAs + } + OpenMessageError::BadBgpIdentifier => { + OpenMessageErrorSubcode::BadBgpIdentifier + } + OpenMessageError::UnsupportedOptParam => { + OpenMessageErrorSubcode::UnsupportedOptParam + } + OpenMessageError::UnacceptableHoldTime => { + OpenMessageErrorSubcode::UnacceptableHoldTime + } + OpenMessageError::UnsupportedCapability => { + OpenMessageErrorSubcode::UnsupportedCapability + } + OpenMessageError::MalformedOptParam => { + OpenMessageErrorSubcode::Unspecific + } + } as u8; + } + DecodeError::UpdateMessage(error) => { + error_code = ErrorCode::UpdateMessageError as u8; + error_subcode = match error { + UpdateMessageError::MalformedAttributeList => { + UpdateMessageErrorSubcode::MalformedAttributeList + } + UpdateMessageError::UnrecognizedWellKnownAttribute => { + UpdateMessageErrorSubcode::UnrecognizedWellKnownAttribute + } + UpdateMessageError::OptionalAttributeError => { + UpdateMessageErrorSubcode::OptionalAttributeError + } + UpdateMessageError::InvalidNetworkField => { + UpdateMessageErrorSubcode::InvalidNetworkField + } + } as u8; + } + } + + // TODO: set notification data. + + NotificationMsg { + error_code, + error_subcode, + data, + } + } +} + +// ===== impl KeepaliveMsg ===== + +impl KeepaliveMsg { + const LEN: u16 = 19; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8(MessageType::Keepalive as u8); + } + + fn decode(_buf: &mut Bytes, msg_len: u16) -> DecodeResult { + if msg_len != Self::LEN { + return Err(MessageHeaderError::BadMessageLength(msg_len).into()); + } + + // A KEEPALIVE message consists of only the message header. + Ok(KeepaliveMsg {}) + } +} + +// ===== impl RouteRefreshMsg ===== + +impl RouteRefreshMsg { + const LEN: u16 = 23; + + fn encode(&self, buf: &mut BytesMut) { + buf.put_u8(MessageType::RouteRefresh as u8); + buf.put_u16(self.afi); + buf.put_u8(0); + buf.put_u8(self.safi); + } + + fn decode(buf: &mut Bytes, msg_len: u16) -> DecodeResult { + if msg_len != Self::LEN { + return Err(MessageHeaderError::BadMessageLength(msg_len).into()); + } + + let afi = buf.get_u16(); + let _reserved = buf.get_u8(); + let safi = buf.get_u8(); + Ok(RouteRefreshMsg { afi, safi }) + } +} + +// ===== helper functions ===== + +pub(crate) fn encode_ipv4_prefix(buf: &mut BytesMut, prefix: &Ipv4Network) { + // Encode prefix length. + let plen = prefix.prefix(); + buf.put_u8(plen); + + // Encode prefix address (variable length). + let prefix_bytes = prefix.ip().octets(); + let plen_wire = prefix_wire_len(plen); + buf.put(&prefix_bytes[0..plen_wire]); +} + +pub(crate) fn encode_ipv6_prefix(buf: &mut BytesMut, prefix: &Ipv6Network) { + // Encode prefix length. + let plen = prefix.prefix(); + buf.put_u8(plen); + + // Encode prefix address (variable length). + let prefix_bytes = prefix.ip().octets(); + let plen_wire = prefix_wire_len(plen); + buf.put(&prefix_bytes[0..plen_wire]); +} + +pub(crate) fn decode_ipv4_prefix(buf: &mut Bytes) -> DecodeResult { + // Parse prefix length. + let plen = buf.get_u8(); + let plen_wire = prefix_wire_len(plen); + if plen_wire > buf.remaining() || plen > Ipv4Network::MAX_PREFIXLEN { + return Err(UpdateMessageError::InvalidNetworkField.into()); + } + + // Parse prefix address (variable length). + let mut prefix_bytes = vec![0; plen_wire]; + buf.copy_to_slice(&mut prefix_bytes); + let prefix = Ipv4Addr::from_slice(&prefix_bytes); + let prefix = Ipv4Network::new(prefix, plen) + .map(|prefix| prefix.apply_mask()) + .map_err(|_| UpdateMessageError::InvalidNetworkField)?; + Ok(prefix) +} + +pub(crate) fn decode_ipv6_prefix(buf: &mut Bytes) -> DecodeResult { + // Parse prefix length. + let plen = buf.get_u8(); + let plen_wire = prefix_wire_len(plen); + if plen_wire > buf.remaining() || plen > Ipv6Network::MAX_PREFIXLEN { + return Err(UpdateMessageError::InvalidNetworkField.into()); + } + + // Parse prefix address (variable length). + let mut prefix_bytes = vec![0; plen_wire]; + buf.copy_to_slice(&mut prefix_bytes); + let prefix = Ipv6Addr::from_slice(&prefix_bytes); + let prefix = Ipv6Network::new(prefix, plen) + .map(|prefix| prefix.apply_mask()) + .map_err(|_| UpdateMessageError::InvalidNetworkField)?; + Ok(prefix) +} + +// Calculates the number of bytes required to encode a prefix. +fn prefix_wire_len(len: u8) -> usize { + (len as usize + 7) / 8 +} diff --git a/holo-bgp/src/packet/mod.rs b/holo-bgp/src/packet/mod.rs new file mode 100644 index 00000000..6aab95ab --- /dev/null +++ b/holo-bgp/src/packet/mod.rs @@ -0,0 +1,10 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +pub mod attribute; +pub mod consts; +pub mod error; +pub mod message; diff --git a/holo-bgp/src/policy.rs b/holo-bgp/src/policy.rs new file mode 100644 index 00000000..c09b5c2a --- /dev/null +++ b/holo-bgp/src/policy.rs @@ -0,0 +1,361 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::{BTreeMap, BTreeSet}; +use std::net::IpAddr; +use std::sync::Arc; + +use holo_utils::bgp::AfiSafi; +use holo_utils::ip::{AddressFamily, IpNetworkKind}; +use holo_utils::policy::{ + BgpNexthop, BgpPolicyAction, BgpPolicyCondition, BgpSetCommMethod, + BgpSetCommOptions, BgpSetMed, DefaultPolicyType, MatchSets, + MetricModification, Policy, PolicyAction, PolicyCondition, PolicyResult, +}; +use holo_utils::UnboundedSender; +use ipnetwork::IpNetwork; + +use crate::packet::attribute::{Attrs, CommList, CommType}; +use crate::tasks::messages::input::PolicyResultMsg; + +// ===== global functions ===== + +pub(crate) fn neighbor_import_apply( + nbr_addr: IpAddr, + prefixes: Vec, + attrs: Attrs, + policies: Arc>>, + match_sets: &MatchSets, + default_policy: DefaultPolicyType, + policy_resultp: &UnboundedSender, +) { + let prefixes = prefixes + .into_iter() + .map(|prefix| { + let mut matches = false; + let mut attrs = attrs.clone(); + + for stmt in policies.iter().flat_map(|policy| policy.stmts.values()) + { + // Policy conditions. + if !stmt.conditions.values().all(|condition| { + process_stmt_condition( + &prefix, &attrs, condition, match_sets, + ) + }) { + continue; + } + + matches = true; + + // Policy actions. + for action in stmt.actions.values() { + if !process_stmt_action( + &prefix, &mut attrs, action, match_sets, + ) { + return (prefix, PolicyResult::Reject); + } + } + } + + // Check default policy if no definition in the policy chain was + // satisfied. + if !matches && default_policy == DefaultPolicyType::RejectRoute { + return (prefix, PolicyResult::Reject); + } + + (prefix, PolicyResult::Accept(attrs)) + }) + .collect(); + + let _ = policy_resultp + .send(PolicyResultMsg::NeighborImport { nbr_addr, prefixes }); +} + +// ===== helper functions ===== + +fn process_stmt_condition( + prefix: &IpNetwork, + attrs: &Attrs, + condition: &PolicyCondition, + match_sets: &MatchSets, +) -> bool { + match condition { + // "match-interface" + PolicyCondition::MatchInterface(_value) => { + // TODO + true + } + // "match-prefix-set" + PolicyCondition::MatchPrefixSet(_value) => { + // TODO + true + } + // "match-neighbor-set" + PolicyCondition::MatchNeighborSet(_value) => { + // TODO + true + } + // "bgp-conditions" + PolicyCondition::Bgp(condition) => match condition { + // "local-pref" + BgpPolicyCondition::LocalPref { value, op } => { + match attrs.base.local_pref { + Some(local_pref) => op.compare(value, &local_pref), + None => false, + } + } + // "med" + BgpPolicyCondition::Med { value, op } => match attrs.base.med { + Some(med) => op.compare(value, &med), + None => false, + }, + // "origin-eq" + BgpPolicyCondition::Origin(origin) => attrs.base.origin == *origin, + // "match-afi-safi" + BgpPolicyCondition::MatchAfiSafi { values, match_type } => { + let afi_safi = match prefix.address_family() { + AddressFamily::Ipv4 => AfiSafi::Ipv4Unicast, + AddressFamily::Ipv6 => AfiSafi::Ipv6Unicast, + }; + match_type.compare(values, &afi_safi) + } + // "match-neighbor" + BgpPolicyCondition::MatchNeighbor { + value: _, + match_type: _, + } => { + // TODO + true + } + // "route-type" + BgpPolicyCondition::RouteType(_route_type) => { + // TODO + true + } + // "community-count" + BgpPolicyCondition::CommCount { value, op } => match &attrs.comm { + Some(comm) => op.compare(value, &(comm.0.len() as u32)), + None => false, + }, + // "as-path-length" + BgpPolicyCondition::AsPathLen { value, op } => op.compare( + value, + &(attrs.base.as_path.iter(None).count() as u32), + ), + // "match-community-set" + BgpPolicyCondition::MatchCommSet { value, match_type } => { + if let Some(comm) = &attrs.comm { + let set = match_sets.bgp.comms.get(value).unwrap(); + match_type.compare(&set, &comm.0) + } else { + false + } + } + // "match-ext-community-set" + BgpPolicyCondition::MatchExtCommSet { value, match_type } => { + if let Some(ext_comm) = &attrs.ext_comm { + let set = match_sets.bgp.ext_comms.get(value).unwrap(); + match_type.compare(&set, &ext_comm.0) + } else { + false + } + } + // "match-ipv6-ext-community-set" + BgpPolicyCondition::MatchExtv6CommSet { value, match_type } => { + if let Some(extv6_comm) = &attrs.extv6_comm { + let set = match_sets.bgp.extv6_comms.get(value).unwrap(); + match_type.compare(&set, &extv6_comm.0) + } else { + false + } + } + // "match-large-community-set" + BgpPolicyCondition::MatchLargeCommSet { value, match_type } => { + if let Some(large_comm) = &attrs.large_comm { + let set = match_sets.bgp.large_comms.get(value).unwrap(); + match_type.compare(&set, &large_comm.0) + } else { + false + } + } + // "match-as-path-set" + BgpPolicyCondition::MatchAsPathSet { value, match_type } => { + let set = match_sets.bgp.as_paths.get(value).unwrap(); + let asns = attrs.base.as_path.iter(None).collect(); + match_type.compare(&set, &asns) + } + // "match-next-hop-set" + BgpPolicyCondition::MatchNexthopSet { value, match_type } => { + let nexthop = match attrs.base.nexthop { + Some(nexthop) => BgpNexthop::Addr(nexthop), + None => BgpNexthop::NexthopSelf, + }; + let set = match_sets.bgp.nexthops.get(value).unwrap(); + match_type.compare(&set, &nexthop) + } + }, + // Ignore unsupported conditions. + _ => true, + } +} + +fn process_stmt_action( + _prefix: &IpNetwork, + attrs: &mut Attrs, + action: &PolicyAction, + match_sets: &MatchSets, +) -> bool { + match action { + // "policy-result" + PolicyAction::Accept(accept) => { + return *accept; + } + // "set-metric" + PolicyAction::SetMetric { value, mod_type } => match mod_type { + MetricModification::Set => { + attrs.base.med = Some(*value); + } + MetricModification::Add => { + if let Some(med) = &mut attrs.base.med { + *med = med.saturating_add(*value); + } + } + MetricModification::Subtract => { + if let Some(med) = &mut attrs.base.med { + *med = med.saturating_sub(*value); + } + } + }, + // "bgp-actions" + PolicyAction::Bgp(action) => match action { + // "set-route-origin" + BgpPolicyAction::SetRouteOrigin(origin) => { + attrs.base.origin = *origin + } + // "set-local-pref" + BgpPolicyAction::SetLocalPref(local_pref) => { + attrs.base.local_pref = Some(*local_pref); + } + // "set-next-hop" + BgpPolicyAction::SetNexthop(set_nexthop) => { + attrs.base.nexthop = match set_nexthop { + BgpNexthop::Addr(addr) => Some(*addr), + BgpNexthop::NexthopSelf => None, + }; + } + // "set-med" + BgpPolicyAction::SetMed(set_med) => match set_med { + BgpSetMed::Add(value) => { + if let Some(med) = &mut attrs.base.med { + *med = med.saturating_add(*value); + } + } + BgpSetMed::Subtract(value) => { + if let Some(med) = &mut attrs.base.med { + *med = med.saturating_sub(*value); + } + } + BgpSetMed::Set(value) => { + attrs.base.med = Some(*value); + } + BgpSetMed::Igp => { + // TODO + } + BgpSetMed::MedPlusIgp => { + // TODO + } + }, + // "set-as-path-prepend" + BgpPolicyAction::SetAsPathPrepent { asn, repeat } => { + for _ in 0..repeat.unwrap_or(1) { + attrs.base.as_path.prepend(*asn); + } + } + // "set-community" + BgpPolicyAction::SetComm { options, method } => { + action_set_comm( + options, + method, + &match_sets.bgp.comms, + &mut attrs.comm, + ); + } + // "set-ext-community" + BgpPolicyAction::SetExtComm { options, method } => { + action_set_comm( + options, + method, + &match_sets.bgp.ext_comms, + &mut attrs.ext_comm, + ); + } + // "set-ipv6-ext-community" + BgpPolicyAction::SetExtv6Comm { options, method } => { + action_set_comm( + options, + method, + &match_sets.bgp.extv6_comms, + &mut attrs.extv6_comm, + ); + } + // "set-large-community" + BgpPolicyAction::SetLargeComm { options, method } => { + action_set_comm( + options, + method, + &match_sets.bgp.large_comms, + &mut attrs.large_comm, + ); + } + }, + // Ignore unsupported actions. + _ => {} + } + + true +} + +fn action_set_comm( + options: &BgpSetCommOptions, + method: &BgpSetCommMethod, + comm_sets: &BTreeMap>, + comm_list: &mut Option>, +) where + T: CommType, +{ + // Get list of communities. + let comms = match method { + BgpSetCommMethod::Inline(comms) => comms, + BgpSetCommMethod::Reference(set) => comm_sets.get(set).unwrap(), + }; + + // Add, remove or replace communities. + match options { + BgpSetCommOptions::Add => { + if let Some(comm_list) = comm_list { + comm_list.0.extend(comms.clone()); + } else { + *comm_list = Some(CommList(comms.clone())); + } + } + BgpSetCommOptions::Remove => { + if let Some(comm_list) = comm_list { + comm_list.0.retain(|c| !comms.contains(c)) + } + } + BgpSetCommOptions::Replace => { + *comm_list = Some(CommList(comms.clone())); + } + } + + // Remove the community list if it exists and is empty. + if let Some(list) = comm_list.as_ref() + && list.0.is_empty() + { + *comm_list = None; + } +} diff --git a/holo-bgp/src/rib.rs b/holo-bgp/src/rib.rs new file mode 100644 index 00000000..cf9f1b3b --- /dev/null +++ b/holo-bgp/src/rib.rs @@ -0,0 +1,162 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::BTreeMap; +use std::net::IpAddr; +use std::sync::Arc; +use std::time::Instant; + +use holo_utils::protocol::Protocol; +use ipnetwork::{Ipv4Network, Ipv6Network}; + +use crate::packet::attribute::{ + Attrs, BaseAttrs, Comms, ExtComms, Extv6Comms, LargeComms, UnknownAttr, +}; + +#[derive(Debug, Default)] +pub struct Rib { + pub attr_sets: AttrSetsCtx, + pub ipv4_unicast: BTreeMap, + pub ipv6_unicast: BTreeMap, +} + +#[derive(Debug, Default)] +pub struct AttrSetsCtx { + pub base: AttrSets, + pub comm: AttrSets, + pub ext_comm: AttrSets, + pub extv6_comm: AttrSets, + pub large_comm: AttrSets, +} + +#[derive(Debug)] +pub struct AttrSets { + pub tree: BTreeMap>>, + next_index: u64, +} + +#[derive(Debug)] +pub struct AttrSet { + pub index: u64, + pub value: T, +} + +#[derive(Debug, Default)] +pub struct Destination { + pub local: Option, + pub adj_in_pre: BTreeMap, + pub adj_in_post: BTreeMap, + pub adj_out_pre: BTreeMap, + pub adj_out_post: BTreeMap, +} + +#[derive(Clone, Debug)] +pub struct Route { + pub origin: Option, + pub attrs: RouteAttrs, + pub last_modified: Instant, + pub eligible: bool, + pub ineligible_reason: Option, + pub reject_reason: Option, +} + +#[derive(Clone, Copy, Debug)] +pub enum RouteOrigin { + // Route learned from a neighbor. + Neighbor(IpAddr), + // Route was injected or redistributed from another protocol. + Protocol(Protocol), +} + +#[derive(Clone, Debug)] +pub struct RouteAttrs { + pub base: Arc>, + pub comm: Option>>, + pub ext_comm: Option>>, + pub extv6_comm: Option>>, + pub large_comm: Option>>, + pub unknown: Vec, +} + +#[derive(Clone, Copy, Debug)] +pub enum RouteIneligibleReason { + ClusterLoop, + AsLoop, + Originator, + Confed, +} + +#[derive(Clone, Copy, Debug)] +pub enum RouteRejectReason { + LocalPrefLower, + AsPathLonger, + OriginTypeHigher, + MedHigher, + PreferExternal, + NexthopCostHigher, + HigherRouterId, + HigherPeerAddress, + RejectedImportPolicy, +} + +// ===== impl AttrSetsCtx ===== + +impl AttrSetsCtx { + pub(crate) fn get_route_attr_sets(&mut self, attrs: Attrs) -> RouteAttrs { + RouteAttrs { + base: self.base.get(attrs.base), + comm: attrs.comm.map(|comm| self.comm.get(comm)), + ext_comm: attrs.ext_comm.map(|c| self.ext_comm.get(c)), + extv6_comm: attrs.extv6_comm.map(|c| self.extv6_comm.get(c)), + large_comm: attrs.large_comm.map(|c| self.large_comm.get(c)), + unknown: attrs.unknown, + } + } +} + +// ===== impl AttrSets ===== + +impl AttrSets +where + T: Clone + Eq + Ord + PartialEq + PartialOrd, +{ + fn get(&mut self, attr: T) -> Arc> { + self.tree + .entry(attr.clone()) + .or_insert_with(|| { + self.next_index += 1; + Arc::new(AttrSet { + index: self.next_index, + value: attr, + }) + }) + .clone() + } +} + +impl Default for AttrSets { + fn default() -> AttrSets { + AttrSets { + tree: Default::default(), + next_index: 0, + } + } +} + +// ===== impl Route ===== + +impl Route { + pub(crate) fn new(attrs: RouteAttrs) -> Route { + Route { + origin: None, + attrs, + last_modified: Instant::now(), + eligible: true, + ineligible_reason: None, + reject_reason: None, + } + } +} diff --git a/holo-bgp/src/southbound/mod.rs b/holo-bgp/src/southbound/mod.rs new file mode 100644 index 00000000..8bd8abf6 --- /dev/null +++ b/holo-bgp/src/southbound/mod.rs @@ -0,0 +1,8 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +pub mod rx; +pub mod tx; diff --git a/holo-bgp/src/southbound/rx.rs b/holo-bgp/src/southbound/rx.rs new file mode 100644 index 00000000..bb652e1c --- /dev/null +++ b/holo-bgp/src/southbound/rx.rs @@ -0,0 +1,19 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; + +use crate::instance::Instance; + +// ===== global functions ===== + +pub(crate) async fn process_router_id_update( + instance: &mut Instance, + router_id: Option, +) { + instance.system.router_id = router_id; + instance.update().await; +} diff --git a/holo-bgp/src/southbound/tx.rs b/holo-bgp/src/southbound/tx.rs new file mode 100644 index 00000000..18b53243 --- /dev/null +++ b/holo-bgp/src/southbound/tx.rs @@ -0,0 +1,5 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// diff --git a/holo-bgp/src/tasks.rs b/holo-bgp/src/tasks.rs new file mode 100644 index 00000000..a0d84908 --- /dev/null +++ b/holo-bgp/src/tasks.rs @@ -0,0 +1,447 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::sync::{atomic, Arc}; +use std::time::Duration; + +use holo_utils::socket::{OwnedReadHalf, OwnedWriteHalf, TcpListener}; +use holo_utils::task::{IntervalTask, Task, TimeoutTask}; +use holo_utils::{Sender, UnboundedReceiver, UnboundedSender}; +use tokio::time::sleep; +use tracing::{debug_span, Instrument}; + +use crate::debug::Debug; +use crate::neighbor::{fsm, Neighbor}; +use crate::packet::message::{DecodeCxt, EncodeCxt, KeepaliveMsg, Message}; +use crate::{network, policy}; + +// +// BGP tasks diagram: +// +--------------+ +// | northbound | +// +--------------+ +// | ^ +// | | +// northbound_rx (1x) V | (1x) northbound_tx +// +--------------+ +// | | +// tcp_listener (1x) -> | | +// tcp_connect (Nx) -> | | -> (Nx) nbr_tx +// nbr_rx (Nx) -> | | -> (Nx) nbr_kalive_interval +// nbr_timer (Nx) -> | instance | +// | | +// policy_apply (Nx) -> | | -> (Nx) policy_apply +// | | +// +--------------+ +// ibus_tx (1x) | ^ (1x) ibus_rx +// | | +// V | +// +--------------+ +// | ibus | +// +--------------+ +// + +// BGP inter-task message types. +pub mod messages { + use std::collections::BTreeSet; + use std::net::IpAddr; + use std::sync::Arc; + + use holo_utils::policy::{ + DefaultPolicyType, MatchSets, Policy, PolicyResult, + }; + use holo_utils::socket::{TcpConnInfo, TcpStream}; + use ipnetwork::IpNetwork; + use serde::{Deserialize, Serialize}; + + use crate::error::NbrRxError; + use crate::neighbor::fsm; + use crate::packet::attribute::Attrs; + use crate::packet::message::{Capability, Message}; + + // Type aliases. + pub type ProtocolInputMsg = input::ProtocolMsg; + pub type ProtocolOutputMsg = output::ProtocolMsg; + + // Input messages (child task -> main task). + pub mod input { + use super::*; + + #[derive(Debug, Deserialize, Serialize)] + pub enum ProtocolMsg { + TcpAccept(TcpAcceptMsg), + TcpConnect(TcpConnectMsg), + NbrRx(NbrRxMsg), + NbrTimer(NbrTimerMsg), + PolicyResult(PolicyResultMsg), + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct TcpAcceptMsg { + #[serde(skip)] + pub stream: Option, + pub conn_info: TcpConnInfo, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct TcpConnectMsg { + #[serde(skip)] + pub stream: Option, + pub conn_info: TcpConnInfo, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct NbrRxMsg { + pub nbr_addr: IpAddr, + pub msg: Result, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct NbrTimerMsg { + pub nbr_addr: IpAddr, + pub timer: fsm::Timer, + } + + #[derive(Debug, Deserialize, Serialize)] + pub enum PolicyResultMsg { + NeighborImport { + nbr_addr: IpAddr, + prefixes: Vec<(IpNetwork, PolicyResult)>, + }, + } + + impl TcpAcceptMsg { + pub(crate) fn stream(&mut self) -> TcpStream { + #[cfg(not(feature = "testing"))] + { + self.stream.take().unwrap() + } + #[cfg(feature = "testing")] + { + Default::default() + } + } + } + + impl TcpConnectMsg { + pub(crate) fn stream(&mut self) -> TcpStream { + #[cfg(not(feature = "testing"))] + { + self.stream.take().unwrap() + } + #[cfg(feature = "testing")] + { + Default::default() + } + } + } + } + + // Output messages (main task -> child task). + pub mod output { + use super::*; + + #[derive(Debug, Serialize)] + pub enum ProtocolMsg { + NbrTx(NbrTxMsg), + PolicyApply(PolicyApplyMsg), + } + + #[derive(Debug, Serialize)] + pub enum NbrTxMsg { + SendMessage { nbr_addr: IpAddr, msg: Message }, + UpdateCapabilities(BTreeSet), + } + + #[derive(Debug, Serialize)] + pub enum PolicyApplyMsg { + NeighborImport { + nbr_addr: IpAddr, + prefixes: Vec, + attrs: Attrs, + policies: Arc>>, + match_sets: Arc, + default_policy: DefaultPolicyType, + }, + } + } +} + +// ===== BGP tasks ===== + +// TCP listening task. +pub(crate) fn tcp_listener( + session_socket: &Arc, + tcp_acceptp: &Sender, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let span1 = debug_span!("session"); + let _span1_guard = span1.enter(); + let span2 = debug_span!("input"); + let _span2_guard = span2.enter(); + + let session_socket = session_socket.clone(); + let tcp_acceptp = tcp_acceptp.clone(); + Task::spawn( + async move { + let _ = network::listen_loop(session_socket, tcp_acceptp).await; + } + .in_current_span(), + ) + } + #[cfg(feature = "testing")] + { + Task::spawn(async move { std::future::pending().await }) + } +} + +// TCP connect task. +pub(crate) fn tcp_connect( + nbr: &Neighbor, + tcp_connectp: &Sender, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let span = debug_span!("neighbor", addr = %nbr.remote_addr); + let _span_guard = span.enter(); + + let remote_addr = nbr.remote_addr; + let local_addr = nbr.config.transport.local_addr; + let ttl = nbr.tx_ttl(); + let ttl_security = nbr.config.transport.ttl_security; + let tcp_mss = nbr.config.transport.tcp_mss; + let tcp_password = nbr.config.transport.md5_key.clone(); + let tcp_connectp = tcp_connectp.clone(); + Task::spawn( + async move { + loop { + let result = network::connect( + remote_addr, + local_addr, + ttl, + ttl_security, + tcp_mss, + &tcp_password, + ) + .await; + + match result { + Ok((stream, conn_info)) => { + // Send message to the parent BGP task. + let msg = messages::input::TcpConnectMsg { + stream: Some(stream), + conn_info, + }; + let _ = tcp_connectp.send(msg).await; + return; + } + Err(error) => { + error.log(); + // Wait one second before trying again. + sleep(Duration::from_secs(1)).await; + } + } + } + } + .in_current_span(), + ) + } + #[cfg(feature = "testing")] + { + Task::spawn(async move { std::future::pending().await }) + } +} + +// Neighbor TCP Rx task. +pub(crate) fn nbr_rx( + nbr: &Neighbor, + cxt: DecodeCxt, + read_half: OwnedReadHalf, + nbr_msg_rxp: &Sender, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let span1 = debug_span!("neighbor", addr = %nbr.remote_addr); + let _span1_guard = span1.enter(); + let span2 = debug_span!("input"); + let _span2_guard = span2.enter(); + + let nbr_addr = nbr.remote_addr; + let nbr_msg_rxp = nbr_msg_rxp.clone(); + Task::spawn( + async move { + let _ = network::nbr_read_loop( + read_half, + nbr_addr, + cxt, + nbr_msg_rxp, + ) + .await; + } + .in_current_span(), + ) + } + #[cfg(feature = "testing")] + { + Task::spawn(async move { std::future::pending().await }) + } +} + +// Neighbor TCP Tx task. +#[cfg_attr(not(feature = "testing"), allow(unused_mut))] +pub(crate) fn nbr_tx( + nbr: &Neighbor, + cxt: EncodeCxt, + write_half: OwnedWriteHalf, + mut msg_txc: UnboundedReceiver, + #[cfg(feature = "testing")] proto_output_tx: &Sender< + messages::ProtocolOutputMsg, + >, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let span1 = debug_span!("neighbor", addr = %nbr.remote_addr); + let _span1_guard = span1.enter(); + let span2 = debug_span!("output"); + let _span2_guard = span2.enter(); + + Task::spawn( + async move { + network::nbr_write_loop(write_half, cxt, msg_txc).await; + } + .in_current_span(), + ) + } + #[cfg(feature = "testing")] + { + let proto_output_tx = proto_output_tx.clone(); + Task::spawn(async move { + // Relay message to the test framework. + while let Some(msg) = msg_txc.recv().await { + let msg = messages::ProtocolOutputMsg::NbrTx(msg); + let _ = proto_output_tx.send(msg).await; + } + }) + } +} + +// Neighbor timer task. +pub(crate) fn nbr_timer( + nbr: &Neighbor, + timer: fsm::Timer, + seconds: u16, + nbr_timerp: &Sender, +) -> TimeoutTask { + #[cfg(not(feature = "testing"))] + { + let nbr_timerp = nbr_timerp.clone(); + let nbr_addr = nbr.remote_addr; + + TimeoutTask::new( + Duration::from_secs(seconds.into()), + move || async move { + let msg = messages::input::NbrTimerMsg { nbr_addr, timer }; + let _ = nbr_timerp.send(msg).await; + }, + ) + } + #[cfg(feature = "testing")] + { + TimeoutTask {} + } +} + +// Send periodic keepalive messages. +pub(crate) fn nbr_kalive_interval( + nbr: &Neighbor, + interval: u16, +) -> IntervalTask { + #[cfg(not(feature = "testing"))] + { + let msg_txp = nbr.msg_txp.as_ref().unwrap().clone(); + let nbr_addr = nbr.remote_addr; + let msg_counter = nbr.statistics.msgs_sent.total.clone(); + + IntervalTask::new( + Duration::from_secs(interval.into()), + false, + move || { + let msg_txp = msg_txp.clone(); + let msg_counter = msg_counter.clone(); + + async move { + let msg = Message::Keepalive(KeepaliveMsg {}); + Debug::NbrMsgTx(&nbr_addr, &msg).log(); + + let msg = messages::output::NbrTxMsg::SendMessage { + nbr_addr, + msg, + }; + let _ = msg_txp.send(msg); + msg_counter.fetch_add(1, atomic::Ordering::Relaxed); + } + }, + ) + } + #[cfg(feature = "testing")] + { + IntervalTask {} + } +} + +// Policy processing task. +#[cfg_attr(not(feature = "testing"), allow(unused_mut))] +pub(crate) fn policy_apply( + policy_applyc: crossbeam_channel::Receiver< + messages::output::PolicyApplyMsg, + >, + policy_resultp: &UnboundedSender, + #[cfg(feature = "testing")] proto_output_tx: &Sender< + messages::ProtocolOutputMsg, + >, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let policy_resultp = policy_resultp.clone(); + Task::spawn_blocking(move || { + while let Ok(msg) = policy_applyc.recv() { + tracing::debug!("XXX policy apply request: {:?}", msg); + match msg { + messages::output::PolicyApplyMsg::NeighborImport { + nbr_addr, + prefixes, + attrs, + policies, + match_sets, + default_policy, + } => { + policy::neighbor_import_apply( + nbr_addr, + prefixes, + attrs, + policies, + &match_sets, + default_policy, + &policy_resultp, + ); + } + } + } + }) + } + #[cfg(feature = "testing")] + { + let proto_output_tx = proto_output_tx.clone(); + Task::spawn(async move { + // Relay message to the test framework. + while let Ok(msg) = policy_applyc.recv() { + let msg = messages::ProtocolOutputMsg::PolicyApply(msg); + let _ = proto_output_tx.send(msg).await; + } + }) + } +} diff --git a/holo-bgp/tests/mod.rs b/holo-bgp/tests/mod.rs new file mode 100644 index 00000000..982a3849 --- /dev/null +++ b/holo-bgp/tests/mod.rs @@ -0,0 +1,9 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +#![feature(lazy_cell)] + +mod packet; diff --git a/holo-bgp/tests/packet/keepalive.rs b/holo-bgp/tests/packet/keepalive.rs new file mode 100644 index 00000000..9218733b --- /dev/null +++ b/holo-bgp/tests/packet/keepalive.rs @@ -0,0 +1,33 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::sync::LazyLock as Lazy; + +use holo_bgp::packet::message::{KeepaliveMsg, Message}; + +use super::{test_decode_msg, test_encode_msg}; + +static KEEPALIVE1: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x13, 0x04, + ], + Message::Keepalive(KeepaliveMsg {}), + ) +}); + +#[test] +fn test_encode_keepalive1() { + let (ref bytes, ref msg) = *KEEPALIVE1; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_keepalive1() { + let (ref bytes, ref msg) = *KEEPALIVE1; + test_decode_msg(bytes, msg); +} diff --git a/holo-bgp/tests/packet/mod.rs b/holo-bgp/tests/packet/mod.rs new file mode 100644 index 00000000..6a90350a --- /dev/null +++ b/holo-bgp/tests/packet/mod.rs @@ -0,0 +1,40 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +mod keepalive; +mod notification; +mod open; +mod route_refresh; +mod update; + +use holo_bgp::neighbor::PeerType; +use holo_bgp::packet::message::{Capability, DecodeCxt, EncodeCxt, Message}; + +// +// Helper functions. +// + +fn test_encode_msg(bytes_expected: &[u8], msg: &Message) { + let cxt = EncodeCxt { + capabilities: [Capability::FourOctetAsNumber { asn: 65550 }].into(), + }; + + let bytes_actual = msg.encode(&cxt); + let data = format!("{:#04x?}", bytes_actual.as_ref()); + let _ = std::fs::write("/tmp/test-data", data); + assert_eq!(bytes_expected, bytes_actual.as_ref()); +} + +fn test_decode_msg(bytes: &[u8], msg_expected: &Message) { + let cxt = DecodeCxt { + peer_type: PeerType::Internal, + peer_as: 65550, + capabilities: [Capability::FourOctetAsNumber { asn: 65550 }].into(), + }; + + let msg_actual = Message::decode(&bytes, &cxt).unwrap(); + assert_eq!(*msg_expected, msg_actual); +} diff --git a/holo-bgp/tests/packet/notification.rs b/holo-bgp/tests/packet/notification.rs new file mode 100644 index 00000000..54e835d1 --- /dev/null +++ b/holo-bgp/tests/packet/notification.rs @@ -0,0 +1,39 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::sync::LazyLock as Lazy; + +use holo_bgp::packet::consts::{ErrorCode, MessageHeaderErrorSubcode}; +use holo_bgp::packet::message::{Message, NotificationMsg}; + +use super::{test_decode_msg, test_encode_msg}; + +static NOTIFICATION1: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x17, 0x03, 0x01, 0x02, 0xff, + 0xff, + ], + Message::Notification(NotificationMsg { + error_code: ErrorCode::MessageHeaderError as u8, + error_subcode: MessageHeaderErrorSubcode::BadMessageLength as u8, + data: vec![0xff, 0xff], + }), + ) +}); + +#[test] +fn test_encode_notification1() { + let (ref bytes, ref msg) = *NOTIFICATION1; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_notification1() { + let (ref bytes, ref msg) = *NOTIFICATION1; + test_decode_msg(bytes, msg); +} diff --git a/holo-bgp/tests/packet/open.rs b/holo-bgp/tests/packet/open.rs new file mode 100644 index 00000000..6e2b24cb --- /dev/null +++ b/holo-bgp/tests/packet/open.rs @@ -0,0 +1,122 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; +use std::str::FromStr; +use std::sync::LazyLock as Lazy; + +use holo_bgp::packet::consts::{Afi, Safi, BGP_VERSION}; +use holo_bgp::packet::message::{Capability, Message, OpenMsg}; + +use super::{test_decode_msg, test_encode_msg}; + +static OPEN1: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x00, 0x01, + 0x00, 0xb4, 0x01, 0x01, 0x01, 0x01, 0x00, + ], + Message::Open(OpenMsg { + version: BGP_VERSION, + my_as: 1, + holdtime: 180, + identifier: Ipv4Addr::from_str("1.1.1.1").unwrap(), + capabilities: [].into(), + }), + ) +}); + +static OPEN2: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x25, 0x01, 0x04, 0x00, 0x01, + 0x00, 0xb4, 0x01, 0x01, 0x01, 0x01, 0x08, 0x02, 0x06, 0x01, 0x04, + 0x00, 0x01, 0x00, 0x01, + ], + Message::Open(OpenMsg { + version: BGP_VERSION, + my_as: 1, + holdtime: 180, + identifier: Ipv4Addr::from_str("1.1.1.1").unwrap(), + capabilities: [Capability::MultiProtocol { + afi: Afi::Ipv4, + safi: Safi::Unicast, + }] + .into(), + }), + ) +}); + +static OPEN3: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3d, 0x01, 0x04, 0x00, 0x01, + 0x00, 0xb4, 0x01, 0x01, 0x01, 0x01, 0x20, 0x02, 0x06, 0x01, 0x04, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x02, 0x06, 0x41, 0x04, 0x00, 0x01, 0x00, 0x0e, 0x02, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x46, 0x00, + ], + Message::Open(OpenMsg { + version: BGP_VERSION, + my_as: 1, + holdtime: 180, + identifier: Ipv4Addr::from_str("1.1.1.1").unwrap(), + capabilities: [ + Capability::MultiProtocol { + afi: Afi::Ipv4, + safi: Safi::Unicast, + }, + Capability::MultiProtocol { + afi: Afi::Ipv6, + safi: Safi::Unicast, + }, + Capability::FourOctetAsNumber { asn: 65550 }, + Capability::RouteRefresh, + Capability::EnhancedRouteRefresh, + ] + .into(), + }), + ) +}); + +#[test] +fn test_encode_open1() { + let (ref bytes, ref msg) = *OPEN1; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_open1() { + let (ref bytes, ref msg) = *OPEN1; + test_decode_msg(bytes, msg); +} + +#[test] +fn test_encode_open2() { + let (ref bytes, ref msg) = *OPEN2; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_open2() { + let (ref bytes, ref msg) = *OPEN2; + test_decode_msg(bytes, msg); +} + +#[test] +fn test_encode_open3() { + let (ref bytes, ref msg) = *OPEN3; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_open3() { + let (ref bytes, ref msg) = *OPEN3; + test_decode_msg(bytes, msg); +} diff --git a/holo-bgp/tests/packet/route_refresh.rs b/holo-bgp/tests/packet/route_refresh.rs new file mode 100644 index 00000000..57e3851b --- /dev/null +++ b/holo-bgp/tests/packet/route_refresh.rs @@ -0,0 +1,38 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::sync::LazyLock as Lazy; + +use holo_bgp::packet::consts::{Afi, Safi}; +use holo_bgp::packet::message::{Message, RouteRefreshMsg}; + +use super::{test_decode_msg, test_encode_msg}; + +static ROUTE_REFRESH1: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x17, 0x05, 0x00, 0x01, 0x00, + 0x01, + ], + Message::RouteRefresh(RouteRefreshMsg { + afi: Afi::Ipv4 as u16, + safi: Safi::Unicast as u8, + }), + ) +}); + +#[test] +fn test_encode_route_refresh1() { + let (ref bytes, ref msg) = *ROUTE_REFRESH1; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_route_refresh1() { + let (ref bytes, ref msg) = *ROUTE_REFRESH1; + test_decode_msg(bytes, msg); +} diff --git a/holo-bgp/tests/packet/update.rs b/holo-bgp/tests/packet/update.rs new file mode 100644 index 00000000..516cf25b --- /dev/null +++ b/holo-bgp/tests/packet/update.rs @@ -0,0 +1,167 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::{Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; +use std::sync::LazyLock as Lazy; + +use holo_bgp::packet::attribute::{ + Aggregator, AsPath, AsPathSegment, Attrs, BaseAttrs, ClusterList, Comm, + CommList, ExtComm, Extv6Comm, LargeComm, +}; +use holo_bgp::packet::consts::{AsPathSegmentType, Origin, Safi}; +use holo_bgp::packet::message::{ + Message, MpReachNlri, MpUnreachNlri, ReachNlri, UnreachNlri, UpdateMsg, +}; +use ipnetwork::{Ipv4Network, Ipv6Network}; + +use super::{test_decode_msg, test_encode_msg}; + +static UPDATE1: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x17, 0x02, 0x00, 0x00, 0x00, + 0x00, + ], + Message::Update(UpdateMsg { + reach: None, + unreach: None, + mp_reach: None, + mp_unreach: None, + attrs: None, + }), + ) +}); + +static UPDATE2: Lazy<(Vec, Message)> = Lazy::new(|| { + ( + vec![ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x29, 0x02, 0x00, 0x08, 0x18, + 0x0a, 0x00, 0x01, 0x18, 0x0a, 0x00, 0x02, 0x01, 0x00, 0x90, 0x0e, + 0x00, 0x47, 0x00, 0x02, 0x01, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x07, 0xbd, + 0x19, 0x11, 0x1c, 0x84, 0x11, 0x00, 0x80, 0x20, 0x01, 0x0d, 0xb8, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x90, 0x0f, 0x00, 0x25, + 0x00, 0x02, 0x01, 0x80, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x20, + 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x40, 0x01, 0x01, 0x00, 0x50, 0x02, 0x00, + 0x0e, 0x02, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, 0x40, 0x03, 0x04, 0x01, 0x01, 0x01, 0x01, + 0x80, 0x04, 0x04, 0x00, 0x00, 0x01, 0xf4, 0x40, 0x05, 0x04, 0x00, + 0x00, 0x01, 0xf4, 0x40, 0x06, 0x00, 0xc0, 0x07, 0x08, 0x00, 0x00, + 0x03, 0xe8, 0x02, 0x02, 0x02, 0x02, 0xd0, 0x08, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x80, 0x09, 0x04, 0x01, 0x01, 0x01, 0x01, 0x90, 0x0a, 0x00, 0x04, + 0x03, 0x03, 0x03, 0x03, 0xd0, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x19, 0x00, 0x14, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x20, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x0a, 0x00, 0xff, 0x01, 0x20, 0x0a, 0x00, 0xff, 0x02, + ], + Message::Update(UpdateMsg { + reach: Some(ReachNlri { + prefixes: vec![ + Ipv4Network::from_str("10.0.255.1/32").unwrap(), + Ipv4Network::from_str("10.0.255.2/32").unwrap(), + ], + nexthop: Ipv4Addr::from_str("1.1.1.1").unwrap(), + }), + unreach: Some(UnreachNlri { + prefixes: vec![ + Ipv4Network::from_str("10.0.1.0/24").unwrap(), + Ipv4Network::from_str("10.0.2.0/24").unwrap(), + ], + }), + mp_reach: Some(MpReachNlri::Ipv6 { + safi: Safi::Unicast, + prefixes: vec![ + Ipv6Network::from_str("2001:db8:1::1/128").unwrap(), + Ipv6Network::from_str("2001:db8:1::2/128").unwrap(), + ], + nexthop: Ipv6Addr::from_str("3000::1").unwrap(), + ll_nexthop: Some( + Ipv6Addr::from_str("fe80::4207:bd19:111c:8411").unwrap(), + ), + }), + mp_unreach: Some(MpUnreachNlri::Ipv6 { + safi: Safi::Unicast, + prefixes: vec![ + Ipv6Network::from_str("2001:db8:2::1/128").unwrap(), + Ipv6Network::from_str("2001:db8:2::2/128").unwrap(), + ], + }), + attrs: Some(Attrs { + base: BaseAttrs { + origin: Origin::Igp, + as_path: AsPath { + segments: vec![AsPathSegment { + seg_type: AsPathSegmentType::Sequence, + members: vec![1, 2, 3], + }], + }, + as4_path: None, + nexthop: None, + ll_nexthop: None, + med: Some(500), + local_pref: Some(500), + aggregator: Some(Aggregator { + asn: 1000, + identifier: Ipv4Addr::from_str("2.2.2.2").unwrap(), + }), + as4_aggregator: None, + atomic_aggregate: true, + originator_id: Some(Ipv4Addr::from_str("1.1.1.1").unwrap()), + cluster_list: Some(ClusterList( + [Ipv4Addr::from_str("3.3.3.3").unwrap()].into(), + )), + }, + comm: Some(CommList([Comm(1), Comm(2), Comm(3)].into())), + ext_comm: Some(CommList( + [ExtComm([0, 0, 0, 1, 0, 0, 0, 1])].into(), + )), + extv6_comm: Some(CommList( + [Extv6Comm(Ipv6Addr::from_str("2001:db8::1").unwrap(), 1)] + .into(), + )), + large_comm: Some(CommList( + [LargeComm([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1])].into(), + )), + unknown: vec![], + }), + }), + ) +}); + +#[test] +fn test_encode_update1() { + let (ref bytes, ref msg) = *UPDATE1; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_update1() { + let (ref bytes, ref msg) = *UPDATE1; + test_decode_msg(bytes, msg); +} + +#[test] +fn test_encode_update2() { + let (ref bytes, ref msg) = *UPDATE2; + test_encode_msg(bytes, msg); +} + +#[test] +fn test_decode_update2() { + let (ref bytes, ref msg) = *UPDATE2; + test_decode_msg(bytes, msg); +} diff --git a/holo-daemon/Cargo.toml b/holo-daemon/Cargo.toml index c37dd2c1..8521cb69 100644 --- a/holo-daemon/Cargo.toml +++ b/holo-daemon/Cargo.toml @@ -32,6 +32,7 @@ yang2.workspace = true holo-interface = { path = "../holo-interface" } holo-bfd = { path = "../holo-bfd", optional = true } +holo-bgp = { path = "../holo-bgp", optional = true } holo-keychain = { path = "../holo-keychain" } holo-ldp = { path = "../holo-ldp", optional = true } holo-northbound = { path = "../holo-northbound" } @@ -54,8 +55,9 @@ name = "holod" path = "src/main.rs" [features] -default = ["bfd", "ldp", "ospf", "rip"] +default = ["bfd", "bgp", "ldp", "ospf", "rip"] bfd = ["holo-bfd"] +bgp = ["holo-bgp"] ldp = ["holo-ldp"] ospf = ["holo-ospf"] rip = ["holo-rip"] diff --git a/holo-daemon/src/northbound/yang.rs b/holo-daemon/src/northbound/yang.rs index dc02f1e9..32ceaca7 100644 --- a/holo-daemon/src/northbound/yang.rs +++ b/holo-daemon/src/northbound/yang.rs @@ -18,8 +18,13 @@ pub(crate) fn create_context() { let mut modules = Vec::new(); // Add data type modules. - for module_name in ["iana-if-type", "ietf-routing-types", "ietf-bfd-types"] - { + for module_name in [ + "iana-if-type", + "iana-bgp-notification", + "iana-bgp-types", + "ietf-routing-types", + "ietf-bfd-types", + ] { modules.push(module_name); } @@ -35,6 +40,11 @@ pub(crate) fn create_context() { use holo_bfd::master::Master; modules_add::(&mut modules); } + #[cfg(feature = "bgp")] + { + use holo_bgp::instance::Instance; + modules_add::(&mut modules); + } #[cfg(feature = "ldp")] { use holo_ldp::instance::Instance; diff --git a/holo-policy/src/northbound/configuration.rs b/holo-policy/src/northbound/configuration.rs index cb7553a6..6a701267 100644 --- a/holo-policy/src/northbound/configuration.rs +++ b/holo-policy/src/northbound/configuration.rs @@ -16,10 +16,9 @@ use holo_northbound::paths::routing_policy; use holo_utils::ibus::IbusMsg; use holo_utils::ip::AddressFamily; use holo_utils::policy::{ - IpPrefixRange, MatchSetRestrictedType, MatchSetType, MetricModification, - MetricType, NeighborSet, Policy, PolicyAction, PolicyActionType, - PolicyCondition, PolicyConditionType, PolicyStmt, PrefixSet, RouteLevel, - RouteType, TagSet, + IpPrefixRange, MatchSetRestrictedType, MatchSetType, MetricType, + NeighborSet, Policy, PolicyAction, PolicyActionType, PolicyCondition, + PolicyConditionType, PolicyStmt, PrefixSet, RouteLevel, RouteType, TagSet, }; use holo_utils::protocol::Protocol; use holo_utils::yang::DataNodeRefExt; @@ -210,6 +209,114 @@ fn load_callbacks() -> Callbacks { let event_queue = args.event_queue; event_queue.insert(Event::MatchSetsUpdate); }) + .path(routing_policy::defined_sets::bgp_defined_sets::as_path_sets::as_path_set::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .lookup(|_master, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(routing_policy::defined_sets::bgp_defined_sets::as_path_sets::as_path_set::member::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::defined_sets::bgp_defined_sets::community_sets::community_set::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .lookup(|_master, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(routing_policy::defined_sets::bgp_defined_sets::community_sets::community_set::member::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ext_community_sets::ext_community_set::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .lookup(|_master, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ext_community_sets::ext_community_set::member::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ipv6_ext_community_sets::ipv6_ext_community_set::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .lookup(|_master, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ipv6_ext_community_sets::ipv6_ext_community_set::member::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::defined_sets::bgp_defined_sets::large_community_sets::large_community_set::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .lookup(|_master, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(routing_policy::defined_sets::bgp_defined_sets::large_community_sets::large_community_set::member::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::defined_sets::bgp_defined_sets::next_hop_sets::next_hop_set::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .lookup(|_master, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(routing_policy::defined_sets::bgp_defined_sets::next_hop_sets::next_hop_set::next_hop::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) .path(routing_policy::policy_definitions::policy_definition::PATH) .create_apply(|master, args| { let name = args.dnode.get_string_relative("./name").unwrap(); @@ -455,6 +562,252 @@ fn load_callbacks() -> Callbacks { let event_queue = args.event_queue; event_queue.insert(Event::PolicyChange(policy.name.clone())); }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::local_pref::value::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::local_pref::eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::local_pref::lt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::local_pref::gt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::med::value::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::med::eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::med::lt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::med::gt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::origin_eq::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_afi_safi::afi_safi_in::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_afi_safi::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_neighbor::neighbor_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_neighbor::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::route_type::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::community_count::community_count::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::community_count::eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::community_count::lt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::community_count::gt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::as_path_length::as_path_length::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::as_path_length::eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::as_path_length::lt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::as_path_length::gt_or_eq::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_community_set::community_set::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_community_set::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_ext_community_set::ext_community_set::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_ext_community_set::ext_community_match_kind::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_ext_community_set::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_ipv6_ext_community_set::ipv6_ext_community_set::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_ipv6_ext_community_set::ipv6_ext_community_match_kind::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_ipv6_ext_community_set::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_large_community_set::large_community_set::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_large_community_set::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_as_path_set::as_path_set::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_as_path_set::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_next_hop_set::next_hop_set::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_next_hop_set::match_set_options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::policy_result::PATH) .modify_apply(|master, args| { let (policy_name, stmt_name) = args.list_entry.into_policy_stmt().unwrap(); @@ -479,49 +832,18 @@ fn load_callbacks() -> Callbacks { event_queue.insert(Event::PolicyChange(policy.name.clone())); }) .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::set_metric::metric_modification::PATH) - .modify_apply(|master, args| { - let (policy_name, stmt_name) = args.list_entry.into_policy_stmt().unwrap(); - let policy = master.policies.get_mut(&policy_name).unwrap(); - let stmt = policy.stmts.get_mut(&stmt_name).unwrap(); - - let metric_mod = args.dnode.get_string(); - let metric_mod = MetricModification::try_from_yang(&metric_mod).unwrap(); - stmt.action_add(PolicyAction::SetMetricMod(metric_mod)); - - let event_queue = args.event_queue; - event_queue.insert(Event::PolicyChange(policy.name.clone())); + .modify_apply(|_master, _args| { + // TODO: implement me! }) - .delete_apply(|master, args| { - let (policy_name, stmt_name) = args.list_entry.into_policy_stmt().unwrap(); - let policy = master.policies.get_mut(&policy_name).unwrap(); - let stmt = policy.stmts.get_mut(&stmt_name).unwrap(); - - stmt.action_remove(PolicyActionType::SetMetricMod); - - let event_queue = args.event_queue; - event_queue.insert(Event::PolicyChange(policy.name.clone())); + .delete_apply(|_master, _args| { + // TODO: implement me! }) .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::set_metric::metric::PATH) - .modify_apply(|master, args| { - let (policy_name, stmt_name) = args.list_entry.into_policy_stmt().unwrap(); - let policy = master.policies.get_mut(&policy_name).unwrap(); - let stmt = policy.stmts.get_mut(&stmt_name).unwrap(); - - let metric = args.dnode.get_u32(); - stmt.action_add(PolicyAction::SetMetric(metric)); - - let event_queue = args.event_queue; - event_queue.insert(Event::PolicyChange(policy.name.clone())); + .modify_apply(|_master, _args| { + // TODO: implement me! }) - .delete_apply(|master, args| { - let (policy_name, stmt_name) = args.list_entry.into_policy_stmt().unwrap(); - let policy = master.policies.get_mut(&policy_name).unwrap(); - let stmt = policy.stmts.get_mut(&stmt_name).unwrap(); - - stmt.action_remove(PolicyActionType::SetMetric); - - let event_queue = args.event_queue; - event_queue.insert(Event::PolicyChange(policy.name.clone())); + .delete_apply(|_master, _args| { + // TODO: implement me! }) .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::set_metric_type::metric_type::PATH) .modify_apply(|master, args| { @@ -637,6 +959,132 @@ fn load_callbacks() -> Callbacks { let event_queue = args.event_queue; event_queue.insert(Event::PolicyChange(policy.name.clone())); }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_route_origin::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_local_pref::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_next_hop::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_med::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_as_path_prepend::repeat_n::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_as_path_prepend::asn::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_community::options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_community::communities::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_community::community_set_ref::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ext_community::options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ext_community::communities::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ext_community::ext_community_set_ref::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ipv6_ext_community::options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ipv6_ext_community::communities::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ipv6_ext_community::ipv6_ext_community_set_ref::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_large_community::options::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_large_community::communities::PATH) + .create_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_large_community::large_community_set_ref::PATH) + .modify_apply(|_master, _args| { + // TODO: implement me! + }) + .delete_apply(|_master, _args| { + // TODO: implement me! + }) .build() } diff --git a/holo-policy/src/northbound/mod.rs b/holo-policy/src/northbound/mod.rs index 322ebe1e..e9251e1f 100644 --- a/holo-policy/src/northbound/mod.rs +++ b/holo-policy/src/northbound/mod.rs @@ -17,7 +17,7 @@ use crate::Master; impl ProviderBase for Master { fn yang_modules() -> &'static [&'static str] { - &["ietf-routing-policy"] + &["ietf-routing-policy", "ietf-bgp-policy"] } fn top_level_node(&self) -> String { diff --git a/holo-policy/src/northbound/state.rs b/holo-policy/src/northbound/state.rs index 94e2fdb7..fd578557 100644 --- a/holo-policy/src/northbound/state.rs +++ b/holo-policy/src/northbound/state.rs @@ -56,6 +56,66 @@ fn load_callbacks() -> Callbacks { // No operational data under this list. None }) + .path(routing_policy::defined_sets::bgp_defined_sets::as_path_sets::as_path_set::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::as_path_sets::as_path_set::member::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::community_sets::community_set::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::community_sets::community_set::member::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ext_community_sets::ext_community_set::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ext_community_sets::ext_community_set::member::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ipv6_ext_community_sets::ipv6_ext_community_set::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::ipv6_ext_community_sets::ipv6_ext_community_set::member::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::large_community_sets::large_community_set::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::large_community_sets::large_community_set::member::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::next_hop_sets::next_hop_set::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::defined_sets::bgp_defined_sets::next_hop_sets::next_hop_set::next_hop::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) .path(routing_policy::policy_definitions::policy_definition::PATH) .get_iterate(|_master, _args| { // No operational data under this list. @@ -71,6 +131,41 @@ fn load_callbacks() -> Callbacks { // No operational data under this list. None }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_afi_safi::afi_safi_in::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::conditions::bgp_conditions::match_neighbor::neighbor_eq::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_as_path_prepend::asn::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_community::communities::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ext_community::communities::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_ipv6_ext_community::communities::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(routing_policy::policy_definitions::policy_definition::statements::statement::actions::bgp_actions::set_large_community::communities::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) .build() } diff --git a/holo-routing/Cargo.toml b/holo-routing/Cargo.toml index 5209204c..cca0e03f 100644 --- a/holo-routing/Cargo.toml +++ b/holo-routing/Cargo.toml @@ -22,6 +22,7 @@ tracing.workspace = true yang2.workspace = true holo-bfd = { path = "../holo-bfd" } +holo-bgp = { path = "../holo-bgp" } holo-ldp = { path = "../holo-ldp" } holo-northbound = { path = "../holo-northbound" } holo-ospf = { path = "../holo-ospf" } diff --git a/holo-routing/src/northbound/configuration.rs b/holo-routing/src/northbound/configuration.rs index 99f271be..9a0ce0b8 100644 --- a/holo-routing/src/northbound/configuration.rs +++ b/holo-routing/src/northbound/configuration.rs @@ -660,6 +660,7 @@ impl Provider for Master { fn nested_callbacks() -> Option> { let keys = [ holo_bfd::northbound::configuration::CALLBACKS.keys(), + holo_bgp::northbound::configuration::CALLBACKS.keys(), holo_ldp::northbound::configuration::CALLBACKS.keys(), holo_ospf::northbound::configuration::CALLBACKS_OSPFV2.keys(), holo_ospf::northbound::configuration::CALLBACKS_OSPFV3.keys(), @@ -717,6 +718,18 @@ impl Provider for Master { // Nothing to do, the BFD task runs permanently. return; } + Protocol::BGP => { + use holo_bgp::instance::Instance; + + spawn_protocol_task::( + name, + &self.nb_tx, + &self.ibus_tx, + Default::default(), + self.shared.clone(), + Some(event_recorder_config), + ) + } Protocol::DIRECT => { // This protocol type can not be configured. unreachable!() diff --git a/holo-routing/src/northbound/rpc.rs b/holo-routing/src/northbound/rpc.rs index c15368b9..93068a27 100644 --- a/holo-routing/src/northbound/rpc.rs +++ b/holo-routing/src/northbound/rpc.rs @@ -15,6 +15,7 @@ use crate::Master; impl Provider for Master { fn nested_callbacks() -> Option> { let keys = [ + holo_bgp::northbound::rpc::CALLBACKS.keys(), holo_ldp::northbound::rpc::CALLBACKS.keys(), holo_ospf::northbound::rpc::CALLBACKS_OSPFV2.keys(), holo_ospf::northbound::rpc::CALLBACKS_OSPFV3.keys(), @@ -62,6 +63,9 @@ fn find_instance( rpc: DataNodeRef<'_>, ) -> Result<(Protocol, Option), String> { let (protocol, name) = match rpc.schema().module().name() { + "ietf-bgp" => { + todo!() + } "ietf-mpls-ldp" => { let protocol = Protocol::LDP; let name = match rpc.path().as_ref() { diff --git a/holo-routing/src/northbound/state.rs b/holo-routing/src/northbound/state.rs index 723824f3..fa6ee04c 100644 --- a/holo-routing/src/northbound/state.rs +++ b/holo-routing/src/northbound/state.rs @@ -393,6 +393,7 @@ impl Provider for Master { fn nested_callbacks() -> Option> { let keys = [ holo_bfd::northbound::state::CALLBACKS.keys(), + holo_bgp::northbound::state::CALLBACKS.keys(), holo_ldp::northbound::state::CALLBACKS.keys(), holo_ospf::northbound::state::CALLBACKS_OSPFV2.keys(), holo_ospf::northbound::state::CALLBACKS_OSPFV3.keys(), diff --git a/holo-tools/yang-coverage.sh b/holo-tools/yang-coverage.sh index 1a368d3e..622a9e13 100755 --- a/holo-tools/yang-coverage.sh +++ b/holo-tools/yang-coverage.sh @@ -14,6 +14,8 @@ cargo run --bin yang_coverage --\ -m ietf-bfd\ -m ietf-bfd-ip-mh\ -m ietf-bfd-ip-sh\ + -m ietf-bgp\ + -m ietf-bgp-policy\ -m ietf-mpls-ldp\ -m ietf-ospf\ -m ietf-ospf-sr\ diff --git a/holo-utils/Cargo.toml b/holo-utils/Cargo.toml index e892c512..87dac0e3 100644 --- a/holo-utils/Cargo.toml +++ b/holo-utils/Cargo.toml @@ -16,6 +16,7 @@ chrono.workspace = true derive-new.workspace = true enum-as-inner.workspace = true ipnetwork.workspace = true +itertools.workspace = true libc.workspace = true nix.workspace = true num-derive.workspace = true diff --git a/holo-utils/src/bgp.rs b/holo-utils/src/bgp.rs new file mode 100644 index 00000000..765d8ba7 --- /dev/null +++ b/holo-utils/src/bgp.rs @@ -0,0 +1,177 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +//! This file contains BGP definitions that are common to both `holo-bgp` and +//! `holo-policy`. In the future, the northbound layer should be restructured +//! so that `holo-bgp` can handle the BGP-specific policy definitions itself, +//! eliminating the need for shared definitions. + +use std::borrow::Cow; +use std::net::Ipv6Addr; + +use holo_yang::{ToYang, TryFromYang}; +use itertools::Itertools; +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::FromPrimitive; +use serde::{Deserialize, Serialize}; + +// Configurable (AFI,SAFI) tuples. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum AfiSafi { + Ipv4Unicast, + Ipv6Unicast, +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum Origin { + Igp = 0, + Egp = 1, + Incomplete = 2, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct Comm(pub u32); + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct ExtComm(pub [u8; 8]); + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct Extv6Comm(pub Ipv6Addr, pub u32); + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub struct LargeComm(pub [u8; 12]); + +// BGP Well-known Communities. +// +// IANA registry: +// https://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xhtml +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(FromPrimitive, ToPrimitive)] +#[derive(Deserialize, Serialize)] +pub enum WellKnownCommunities { + NoExport = 0xFFFFFF01, + NoAdvertise = 0xFFFFFF02, + NoExportSubconfed = 0xFFFFFF03, +} + +// ===== impl AfiSafi ===== + +impl ToYang for AfiSafi { + fn to_yang(&self) -> Cow<'static, str> { + match self { + AfiSafi::Ipv4Unicast => "iana-bgp-types:ipv4-unicast".into(), + AfiSafi::Ipv6Unicast => "iana-bgp-types:ipv6-unicast".into(), + } + } +} + +impl TryFromYang for AfiSafi { + fn try_from_yang(value: &str) -> Option { + match value { + "iana-bgp-types:ipv4-unicast" => Some(AfiSafi::Ipv4Unicast), + "iana-bgp-types:ipv6-unicast" => Some(AfiSafi::Ipv6Unicast), + _ => None, + } + } +} + +// ===== impl Origin ===== + +impl ToYang for Origin { + fn to_yang(&self) -> Cow<'static, str> { + match self { + Origin::Igp => "igp".into(), + Origin::Egp => "egp".into(), + Origin::Incomplete => "incomplete".into(), + } + } +} + +// ===== impl Comm ===== + +impl ToYang for Comm { + fn to_yang(&self) -> Cow<'static, str> { + match WellKnownCommunities::from_u32(self.0) { + Some(WellKnownCommunities::NoExport) => { + "iana-bgp-community-types:no-export".into() + } + Some(WellKnownCommunities::NoAdvertise) => { + "iana-bgp-community-types:no-advertise".into() + } + Some(WellKnownCommunities::NoExportSubconfed) => { + "iana-bgp-community-types:no-export-subconfed".into() + } + None => { + let asn = self.0 >> 16; + let local = self.0 & 0xFFFF; + format!("{}:{}", asn, local).into() + } + } + } +} + +// ===== impl ExtComm ===== + +impl ToYang for ExtComm { + fn to_yang(&self) -> Cow<'static, str> { + // TODO: cover other cases instead of always using the raw format. + format!( + "raw:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", + self.0[0], + self.0[1], + self.0[2], + self.0[3], + self.0[4], + self.0[5], + self.0[6], + self.0[7] + ) + .into() + } +} + +// ===== impl Extv6Comm ===== + +impl ToYang for Extv6Comm { + fn to_yang(&self) -> Cow<'static, str> { + // TODO: cover other cases instead of always using the raw format. + let addr = self + .0 + .segments() + .into_iter() + .map(|s| format!("{:02x}", s)) + .join(":"); + let local = self + .1 + .to_be_bytes() + .into_iter() + .map(|s| format!("{:02x}", s)) + .join(":"); + format!("ipv6-raw:{}:{}", addr, local,).into() + } +} + +// ===== impl LargeComm ===== + +impl ToYang for LargeComm { + fn to_yang(&self) -> Cow<'static, str> { + format!( + "{}:{}:{}", + u32::from_be_bytes(self.0[0..4].try_into().unwrap()), + u32::from_be_bytes(self.0[4..8].try_into().unwrap()), + u32::from_be_bytes(self.0[8..12].try_into().unwrap()), + ) + .into() + } +} diff --git a/holo-utils/src/lib.rs b/holo-utils/src/lib.rs index 27029f1f..f6f32101 100644 --- a/holo-utils/src/lib.rs +++ b/holo-utils/src/lib.rs @@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex}; use pickledb::PickleDb; pub mod bfd; +pub mod bgp; pub mod bytes; pub mod capabilities; pub mod crypto; diff --git a/holo-utils/src/policy.rs b/holo-utils/src/policy.rs index 706620ee..4d94b097 100644 --- a/holo-utils/src/policy.rs +++ b/holo-utils/src/policy.rs @@ -13,12 +13,40 @@ use holo_yang::TryFromYang; use ipnetwork::IpNetwork; use serde::{Deserialize, Serialize}; +use crate::bgp::{AfiSafi, Comm, ExtComm, Extv6Comm, LargeComm, Origin}; use crate::ip::AddressFamily; use crate::protocol::Protocol; // Type aliases. pub type Policies = BTreeMap>; +// Routing policy configuration. +#[derive(Clone, Debug, Default)] +pub struct ApplyPolicyCfg { + // TODO: "ordered-by user" + pub import_policy: BTreeSet, + pub default_import_policy: DefaultPolicyType, + // TODO: "ordered-by user" + pub export_policy: BTreeSet, + pub default_export_policy: DefaultPolicyType, +} + +#[derive(Debug)] +#[derive(Deserialize, Serialize)] +pub enum PolicyResult { + Accept(T), + Reject, +} + +// Default policy type. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum DefaultPolicyType { + AcceptRoute, + #[default] + RejectRoute, +} + // Route type. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[derive(Deserialize, Serialize)] @@ -40,9 +68,9 @@ pub enum RouteType { #[derive(Clone, Copy, Debug)] #[derive(Deserialize, Serialize)] pub enum MetricModification { - SetMetric, - AddMetric, - SubtractMetric, + Set, + Add, + Subtract, } // Route metric types. @@ -105,6 +133,7 @@ pub struct MatchSets { pub prefixes: BTreeMap<(String, AddressFamily), PrefixSet>, pub neighbors: BTreeMap, pub tags: BTreeMap, + pub bgp: BgpMatchSets, } // List of IPv4 or IPv6 prefixes that are matched as part of a policy. @@ -132,6 +161,18 @@ pub struct TagSet { pub tags: BTreeSet, } +// BGP sets of attributes used in policy match statements. +#[derive(Clone, Debug, Default)] +#[derive(Deserialize, Serialize)] +pub struct BgpMatchSets { + pub as_paths: BTreeMap>, + pub comms: BTreeMap>, + pub ext_comms: BTreeMap>, + pub extv6_comms: BTreeMap>, + pub large_comms: BTreeMap>, + pub nexthops: BTreeMap>, +} + // Policy definition. #[derive(Clone, Debug)] #[derive(Deserialize, Serialize)] @@ -139,6 +180,7 @@ pub struct Policy { // Name of the policy. pub name: String, // List of statements. + // TODO: "ordered-by user" pub stmts: BTreeMap, } @@ -164,6 +206,7 @@ pub enum PolicyConditionType { MatchNeighborSet, MatchTagSet, MatchRouteType, + Bgp(BgpPolicyConditionType), } // Policy condition statement. @@ -177,6 +220,83 @@ pub enum PolicyCondition { MatchNeighborSet(String), MatchTagSet(String), MatchRouteType(BTreeSet), + Bgp(BgpPolicyCondition), +} + +// BGP policy condition statement type. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum BgpPolicyConditionType { + LocalPref, + Med, + Origin, + MatchAfiSafi, + MatchNeighbor, + RouteType, + CommCount, + AsPathLen, + MatchCommSet, + MatchExtCommSet, + MatchExtv6CommSet, + MatchLargeCommSet, + MatchAsPathSet, + MatchNexthopSet, +} + +// BGP policy condition statement. +#[derive(Clone, Debug, EnumAsInner)] +#[derive(Deserialize, Serialize)] +pub enum BgpPolicyCondition { + LocalPref { + value: u32, + op: BgpEqOperator, + }, + Med { + value: u32, + op: BgpEqOperator, + }, + Origin(Origin), + MatchAfiSafi { + values: BTreeSet, + match_type: MatchSetRestrictedType, + }, + MatchNeighbor { + value: BTreeSet, + match_type: MatchSetRestrictedType, + }, + RouteType(BgpRouteType), + CommCount { + value: u32, + op: BgpEqOperator, + }, + AsPathLen { + value: u32, + op: BgpEqOperator, + }, + MatchCommSet { + value: String, + match_type: MatchSetType, + }, + MatchExtCommSet { + value: String, + match_type: MatchSetType, + }, + MatchExtv6CommSet { + value: String, + match_type: MatchSetType, + }, + MatchLargeCommSet { + value: String, + match_type: MatchSetType, + }, + MatchAsPathSet { + value: String, + match_type: MatchSetType, + }, + MatchNexthopSet { + value: String, + match_type: MatchSetRestrictedType, + }, } // Policy action statement type. @@ -185,12 +305,12 @@ pub enum PolicyCondition { pub enum PolicyActionType { Accept, SetMetric, - SetMetricMod, SetMetricType, SetRouteLevel, SetRoutePref, SetTag, SetAppTag, + Bgp(BgpPolicyActionType), } // Policy action statement. @@ -198,13 +318,120 @@ pub enum PolicyActionType { #[derive(Deserialize, Serialize)] pub enum PolicyAction { Accept(bool), - SetMetric(u32), - SetMetricMod(MetricModification), + SetMetric { + value: u32, + mod_type: MetricModification, + }, SetMetricType(MetricType), SetRouteLevel(RouteLevel), SetRoutePref(u16), SetTag(u32), SetAppTag(u32), + Bgp(BgpPolicyAction), +} + +// BGP policy action statement type. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum BgpPolicyActionType { + SetRouteOrigin, + SetLocalPref, + SetNexthop, + SetMed, + SetAsPathPrepent, + SetComm, + SetExtComm, + SetExtv6Comm, + SetLargeComm, +} + +// BGP policy action statement. +#[derive(Clone, Debug)] +#[derive(Deserialize, Serialize)] +pub enum BgpPolicyAction { + SetRouteOrigin(Origin), + SetLocalPref(u32), + SetNexthop(BgpNexthop), + SetMed(BgpSetMed), + SetAsPathPrepent { + asn: u32, + repeat: Option, + }, + SetComm { + options: BgpSetCommOptions, + method: BgpSetCommMethod, + }, + SetExtComm { + options: BgpSetCommOptions, + method: BgpSetCommMethod, + }, + SetExtv6Comm { + options: BgpSetCommOptions, + method: BgpSetCommMethod, + }, + SetLargeComm { + options: BgpSetCommOptions, + method: BgpSetCommMethod, + }, +} + +#[derive(Clone, Copy, Debug)] +#[derive(Deserialize, Serialize)] +pub enum BgpEqOperator { + Equal, + LessThanOrEqual, + GreaterThanOrEqual, +} + +#[derive(Clone, Copy, Debug)] +#[derive(Deserialize, Serialize)] +pub enum BgpRouteType { + Internal, + External, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum BgpNexthop { + Addr(IpAddr), + NexthopSelf, +} + +#[derive(Clone, Debug)] +#[derive(Deserialize, Serialize)] +pub enum BgpSetMed { + Add(u32), + Subtract(u32), + Set(u32), + Igp, + MedPlusIgp, +} + +#[derive(Clone, Copy, Debug)] +#[derive(Deserialize, Serialize)] +pub enum BgpSetCommOptions { + Add, + Remove, + Replace, +} + +#[derive(Clone, Debug)] +#[derive(Deserialize, Serialize)] +pub enum BgpSetCommMethod { + Inline(BTreeSet), + Reference(String), +} + +// ===== impl DefaultPolicyType ===== + +impl TryFromYang for DefaultPolicyType { + fn try_from_yang(value: &str) -> Option { + match value { + "accept-route" => Some(DefaultPolicyType::AcceptRoute), + "reject-route" => Some(DefaultPolicyType::RejectRoute), + _ => None, + } + } } // ===== impl RouteType ===== @@ -249,9 +476,9 @@ impl TryFromYang for RouteType { impl TryFromYang for MetricModification { fn try_from_yang(value: &str) -> Option { match value { - "set-metric" => Some(MetricModification::SetMetric), - "add-metric" => Some(MetricModification::AddMetric), - "subtract-metric" => Some(MetricModification::SubtractMetric), + "set-metric" => Some(MetricModification::Set), + "add-metric" => Some(MetricModification::Add), + "subtract-metric" => Some(MetricModification::Subtract), _ => None, } } @@ -303,6 +530,19 @@ impl TryFromYang for RouteLevel { // ===== impl MatchSetType ===== +impl MatchSetType { + pub fn compare(&self, a: &BTreeSet, b: &BTreeSet) -> bool + where + T: Eq + Ord + PartialEq + PartialOrd, + { + match self { + MatchSetType::Any => !a.is_disjoint(b), + MatchSetType::All => a.is_superset(b), + MatchSetType::Invert => a.is_disjoint(b), + } + } +} + impl TryFromYang for MatchSetType { fn try_from_yang(identity: &str) -> Option { match identity { @@ -316,6 +556,18 @@ impl TryFromYang for MatchSetType { // ===== impl MatchSetRestrictedType ===== +impl MatchSetRestrictedType { + pub fn compare(&self, a: &BTreeSet, b: &T) -> bool + where + T: Eq + Ord + PartialEq + PartialOrd, + { + match self { + MatchSetRestrictedType::Any => a.contains(b), + MatchSetRestrictedType::Invert => !a.contains(b), + } + } +} + impl TryFromYang for MatchSetRestrictedType { fn try_from_yang(identity: &str) -> Option { match identity { @@ -384,6 +636,9 @@ impl PolicyCondition { PolicyCondition::MatchRouteType(..) => { PolicyConditionType::MatchRouteType } + PolicyCondition::Bgp(cond) => { + PolicyConditionType::Bgp(cond.as_type()) + } } } } @@ -394,13 +649,107 @@ impl PolicyAction { fn as_type(&self) -> PolicyActionType { match self { PolicyAction::Accept(..) => PolicyActionType::Accept, - PolicyAction::SetMetric(..) => PolicyActionType::SetMetric, - PolicyAction::SetMetricMod(..) => PolicyActionType::SetMetricMod, + PolicyAction::SetMetric { .. } => PolicyActionType::SetMetric, PolicyAction::SetMetricType(..) => PolicyActionType::SetMetricType, PolicyAction::SetRouteLevel(..) => PolicyActionType::SetRouteLevel, PolicyAction::SetRoutePref(..) => PolicyActionType::SetRoutePref, PolicyAction::SetTag(..) => PolicyActionType::SetTag, PolicyAction::SetAppTag(..) => PolicyActionType::SetAppTag, + PolicyAction::Bgp(action) => { + PolicyActionType::Bgp(action.as_type()) + } + } + } +} + +// ===== impl BgpPolicyCondition ===== + +impl BgpPolicyCondition { + fn as_type(&self) -> BgpPolicyConditionType { + match self { + BgpPolicyCondition::LocalPref { .. } => { + BgpPolicyConditionType::LocalPref + } + BgpPolicyCondition::Med { .. } => BgpPolicyConditionType::Med, + BgpPolicyCondition::Origin(..) => BgpPolicyConditionType::Origin, + BgpPolicyCondition::MatchAfiSafi { .. } => { + BgpPolicyConditionType::MatchAfiSafi + } + BgpPolicyCondition::MatchNeighbor { .. } => { + BgpPolicyConditionType::MatchNeighbor + } + BgpPolicyCondition::RouteType(..) => { + BgpPolicyConditionType::RouteType + } + BgpPolicyCondition::CommCount { .. } => { + BgpPolicyConditionType::CommCount + } + BgpPolicyCondition::AsPathLen { .. } => { + BgpPolicyConditionType::AsPathLen + } + BgpPolicyCondition::MatchCommSet { .. } => { + BgpPolicyConditionType::MatchCommSet + } + BgpPolicyCondition::MatchExtCommSet { .. } => { + BgpPolicyConditionType::MatchExtCommSet + } + BgpPolicyCondition::MatchExtv6CommSet { .. } => { + BgpPolicyConditionType::MatchExtv6CommSet + } + BgpPolicyCondition::MatchLargeCommSet { .. } => { + BgpPolicyConditionType::MatchLargeCommSet + } + BgpPolicyCondition::MatchAsPathSet { .. } => { + BgpPolicyConditionType::MatchAsPathSet + } + BgpPolicyCondition::MatchNexthopSet { .. } => { + BgpPolicyConditionType::MatchNexthopSet + } + } + } +} + +// ===== impl BgpPolicyAction ===== + +impl BgpPolicyAction { + fn as_type(&self) -> BgpPolicyActionType { + match self { + BgpPolicyAction::SetRouteOrigin(..) => { + BgpPolicyActionType::SetRouteOrigin + } + BgpPolicyAction::SetLocalPref(..) => { + BgpPolicyActionType::SetLocalPref + } + BgpPolicyAction::SetNexthop(..) => BgpPolicyActionType::SetNexthop, + BgpPolicyAction::SetMed(..) => BgpPolicyActionType::SetMed, + BgpPolicyAction::SetAsPathPrepent { .. } => { + BgpPolicyActionType::SetAsPathPrepent + } + BgpPolicyAction::SetComm { .. } => BgpPolicyActionType::SetComm, + BgpPolicyAction::SetExtComm { .. } => { + BgpPolicyActionType::SetExtComm + } + BgpPolicyAction::SetExtv6Comm { .. } => { + BgpPolicyActionType::SetExtv6Comm + } + BgpPolicyAction::SetLargeComm { .. } => { + BgpPolicyActionType::SetLargeComm + } + } + } +} + +// ===== impl BgpEqOperator ===== + +impl BgpEqOperator { + pub fn compare(&self, a: &T, b: &T) -> bool + where + T: Eq + Ord + PartialEq + PartialOrd, + { + match self { + BgpEqOperator::Equal => *a == *b, + BgpEqOperator::LessThanOrEqual => *a <= *b, + BgpEqOperator::GreaterThanOrEqual => *a >= *b, } } } diff --git a/holo-utils/src/protocol.rs b/holo-utils/src/protocol.rs index f25096de..15d48073 100644 --- a/holo-utils/src/protocol.rs +++ b/holo-utils/src/protocol.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; #[serde(rename_all = "lowercase")] pub enum Protocol { BFD, + BGP, DIRECT, LDP, OSPFV2, @@ -32,6 +33,7 @@ impl std::fmt::Display for Protocol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Protocol::BFD => write!(f, "bfd"), + Protocol::BGP => write!(f, "bgp"), Protocol::DIRECT => write!(f, "direct"), Protocol::LDP => write!(f, "ldp"), Protocol::OSPFV2 => write!(f, "ospfv2"), @@ -49,6 +51,7 @@ impl FromStr for Protocol { fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "bfd" => Ok(Protocol::BFD), + "bgp" => Ok(Protocol::BGP), "direct" => Ok(Protocol::DIRECT), "ldp" => Ok(Protocol::LDP), "ospfv2" => Ok(Protocol::OSPFV2), @@ -65,6 +68,7 @@ impl ToYang for Protocol { fn to_yang(&self) -> Cow<'static, str> { match self { Protocol::BFD => "ietf-bfd-types:bfdv1".into(), + Protocol::BGP => "ietf-bgp:bgp".into(), Protocol::DIRECT => "ietf-routing:direct".into(), Protocol::LDP => "ietf-mpls-ldp:mpls-ldp".into(), Protocol::OSPFV2 => "ietf-ospf:ospfv2".into(), @@ -80,6 +84,7 @@ impl TryFromYang for Protocol { fn try_from_yang(identity: &str) -> Option { match identity { "ietf-bfd-types:bfdv1" => Some(Protocol::BFD), + "ietf-bgp:bgp" => Some(Protocol::BGP), "ietf-routing:direct" => Some(Protocol::DIRECT), "ietf-mpls-ldp:mpls-ldp" => Some(Protocol::LDP), "ietf-ospf:ospfv2" => Some(Protocol::OSPFV2), diff --git a/holo-yang/modules/augmentations/holo-bgp.yang b/holo-yang/modules/augmentations/holo-bgp.yang new file mode 100644 index 00000000..96ccb62d --- /dev/null +++ b/holo-yang/modules/augmentations/holo-bgp.yang @@ -0,0 +1,78 @@ +module holo-bgp { + yang-version 1.1; + namespace "http://holo-routing.org/yang/holo-bgp"; + prefix holo-bgp; + + import ietf-routing { + prefix rt; + } + + import iana-bgp-types { + prefix bt; + } + + import iana-bgp-notification { + prefix bn; + } + + import ietf-bgp { + prefix bgp; + } + + organization + "Holo Routing Stack"; + + description + "This module defines augment statements for the ietf-bgp + module."; + + /* + * Identities. + */ + + identity unknown-error { + base bn:bgp-notification; + description + "Unknown error code"; + } + + identity graceful-restart { + base bt:bgp-capability; + description + "Graceful restart functionality"; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + identity add-paths { + base bt:bgp-capability; + description + "Advertisement of multiple paths for the same address prefix + without the new paths implicitly replacing any previous + ones."; + reference + "RFC 7911: Advertisement of Multiple Paths in BGP."; + } + + identity enhanced-route-refresh { + base bt:bgp-capability; + description + "The BGP enhanced route-refresh functionality"; + reference + "RFC 7313: Enhanced Route Refresh Capability for BGP-4"; + } + + /* + * Augmentations. + */ + + augment "/rt:routing/rt:control-plane-protocols/" + + "rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/" + + "bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:md5" { + leaf md5-key-string { + type string; + description + "Key string in ASCII format."; + } + } +} diff --git a/holo-yang/modules/deviations/ietf-bgp-holo-deviations.yang b/holo-yang/modules/deviations/ietf-bgp-holo-deviations.yang new file mode 100644 index 00000000..4f2fbf68 --- /dev/null +++ b/holo-yang/modules/deviations/ietf-bgp-holo-deviations.yang @@ -0,0 +1,4832 @@ +module ietf-bgp-holo-deviations { + yang-version 1.1; + namespace "http://holo-routing.org/yang/ietf-bgp-holo-deviations"; + prefix ietf-bgp-holo-deviations; + + import ietf-routing { + prefix rt; + } + + import ietf-bgp { + prefix bgp; + } + + organization + "Holo Routing Stack"; + + description + "This module defines deviation statements for the ietf-bgp + module."; + + /* + * Default values + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:distance/bgp:external" { + deviate add { + default "20"; + } + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:distance/bgp:internal" { + deviate add { + default "200"; + } + } + + /* + * Other deviations + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:peer-as" { + deviate add { + mandatory "true"; + } + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:session-state" { + deviate add { + config "false"; + } + } + + /* + * Not supported nodes + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:identifier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:distance" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:distance/bgp:external" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:distance/bgp:internal" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:confederation" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:confederation/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:confederation/bgp:identifier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:confederation/bgp:member-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths/bgp:ebgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths/bgp:ebgp/bgp:allow-multiple-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths/bgp:ebgp/bgp:maximum-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths/bgp:ibgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:use-multiple-paths/bgp:ibgp/bgp:maximum-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:always-compare-med" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:ignore-as-path-length" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:external-compare-router-id" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:advertise-inactive-routes" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:enable-aigp" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:ignore-next-hop-igp-metric" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:enable-med" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:med-plus-igp" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:med-plus-igp/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:med-plus-igp/bgp:igp-multiplier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:route-selection-options/bgp:med-plus-igp/bgp:med-multiplier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:statistics" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:statistics/bgp:total-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:statistics/bgp:total-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:always-compare-med" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:ignore-as-path-length" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:external-compare-router-id" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:advertise-inactive-routes" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:enable-aigp" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:ignore-next-hop-igp-metric" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:enable-med" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:med-plus-igp" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:med-plus-igp/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:med-plus-igp/bgp:igp-multiplier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:route-selection-options/bgp:med-plus-igp/bgp:med-multiplier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp/bgp:allow-multiple-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp/bgp:maximum-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ibgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ibgp/bgp:maximum-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:default-import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:default-export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:send-default-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:send-default-route" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:apply-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:apply-policy/bgp:import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:apply-policy/bgp:default-import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:apply-policy/bgp:export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:apply-policy/bgp:default-export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:statistics" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:statistics/bgp:total-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:global/bgp:statistics/bgp:total-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:remote-address" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:peer-group" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:local-address" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:local-port" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:remote-port" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:peer-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:identifier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:dynamically-configured" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:local-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:remove-private-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:description" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers/bgp:connect-retry-interval" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers/bgp:hold-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers/bgp:negotiated-hold-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers/bgp:keepalive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers/bgp:min-as-origination-interval" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:timers/bgp:min-route-advertisement-interval" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:local-address" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:tcp-mss" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:mtu-discovery" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:ebgp-multihop" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:ebgp-multihop/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:ebgp-multihop/bgp:multihop-ttl" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:passive-mode" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ao" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ao/bgp:ao-keychain" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:md5" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:md5/bgp:md5-keychain" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ipsec" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ipsec/bgp:sa" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:treat-as-withdraw" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:logging-options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:logging-options/bgp:log-neighbor-state-changes" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:route-reflector" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:route-reflector/bgp:cluster-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:route-reflector/bgp:client" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:as-path-options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:as-path-options/bgp:allow-own-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:as-path-options/bgp:replace-peer-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:as-path-options/bgp:disable-peer-as-filter" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:use-multiple-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:use-multiple-paths/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:use-multiple-paths/bgp:ebgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:use-multiple-paths/bgp:ebgp/bgp:allow-multiple-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:apply-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:apply-policy/bgp:import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:apply-policy/bgp:default-import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:apply-policy/bgp:export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:apply-policy/bgp:default-export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:active" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:prefixes/bgp:received" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:prefixes/bgp:sent" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:prefixes/bgp:installed" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:default-import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:default-export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:send-default-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:send-default-route" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp/bgp:allow-multiple-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:last-established" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:code" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:mpbgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:mpbgp/bgp:afi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:mpbgp/bgp:safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:mpbgp/bgp:name" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:unknown-flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:restart-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:afi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:afi-safi-flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:afi-safi-unknown-flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:asn32" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:asn32/bgp:as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:add-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis/bgp:afi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis/bgp:safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:advertised-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis/bgp:mode" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:code" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:mpbgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:mpbgp/bgp:afi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:mpbgp/bgp:safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:mpbgp/bgp:name" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:unknown-flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:restart-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:afi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:afi-safi-flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:graceful-restart/bgp:afi-safis/bgp:afi-safi-unknown-flags" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:asn32" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:asn32/bgp:as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:add-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis/bgp:afi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis/bgp:safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:received-capabilities/bgp:value/bgp:add-paths/bgp:afi-safis/bgp:mode" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:capabilities/bgp:negotiated-capabilities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-notification" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-error" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-error-code" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-error-subcode" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-encapsulated-error" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-encapsulated-error-code" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-encapsulated-error-subcode" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:received/bgp:last-error-data" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-notification" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-error" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-error-code" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-error-subcode" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-encapsulated-error" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-encapsulated-error-code" { + deviate not-supported; + } + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-encapsulated-error-subcode" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:errors/bgp:sent/bgp:last-error-data" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:established-transitions" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:total-received" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:total-sent" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:updates-received" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:updates-sent" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:erroneous-updates-withdrawn" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:erroneous-updates-attribute-discarded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:in-update-elapsed-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:notifications-received" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:notifications-sent" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:route-refreshes-received" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:messages/bgp:route-refreshes-sent" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:queues" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:queues/bgp:input" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:neighbors/bgp:neighbor/bgp:statistics/bgp:queues/bgp:output" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:peer-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:local-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:remove-private-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:description" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers/bgp:connect-retry-interval" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers/bgp:hold-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers/bgp:negotiated-hold-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers/bgp:keepalive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers/bgp:min-as-origination-interval" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:timers/bgp:min-route-advertisement-interval" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:local-address" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:tcp-mss" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:mtu-discovery" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:ebgp-multihop" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:ebgp-multihop/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:ebgp-multihop/bgp:multihop-ttl" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:passive-mode" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ao" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ao/bgp:ao-keychain" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:md5" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:md5/bgp:md5-keychain" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ipsec" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:transport/bgp:secure-session/bgp:options/bgp:option/bgp:ipsec/bgp:sa" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:treat-as-withdraw" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:logging-options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:logging-options/bgp:log-neighbor-state-changes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:route-reflector" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:route-reflector/bgp:cluster-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:route-reflector/bgp:client" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:as-path-options" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:as-path-options/bgp:allow-own-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:as-path-options/bgp:replace-peer-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:as-path-options/bgp:disable-peer-as-filter" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:use-multiple-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:use-multiple-paths/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:use-multiple-paths/bgp:ebgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:use-multiple-paths/bgp:ebgp/bgp:allow-multiple-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:apply-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:apply-policy/bgp:import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:apply-policy/bgp:default-import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:apply-policy/bgp:export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:apply-policy/bgp:default-export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:dynamic-peers" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:dynamic-peers/bgp:dynamic-peer-list" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:dynamic-peers/bgp:dynamic-peer-list/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:use-multiple-paths/bgp:ebgp/bgp:allow-multiple-as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:default-import-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:apply-policy/bgp:default-export-policy" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:send-default-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:send-default-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-labeled-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-labeled-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-unicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv4-multicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l3vpn-ipv6-multicast/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-vpls/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:max-prefixes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:warning-threshold-pct" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:teardown" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:idle-time" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:peer-groups/bgp:peer-group/bgp:afi-safis/bgp:afi-safi/bgp:l2vpn-evpn/bgp:prefix-limit/bgp:prefix-limit-exceeded" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:origin" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as-path" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as-path/bgp:segment" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as-path/bgp:segment/bgp:type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as-path/bgp:segment/bgp:member" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:next-hop" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:link-local-next-hop" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:med" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:local-pref" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as4-path" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as4-path/bgp:segment" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as4-path/bgp:segment/bgp:type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:as4-path/bgp:segment/bgp:member" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aggregator" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aggregator/bgp:as" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aggregator/bgp:identifier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aggregator4" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aggregator4/bgp:as4" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aggregator4/bgp:identifier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:atomic-aggregate" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:originator-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:cluster-list" { + deviate not-supported; + } + */ + + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:attr-sets/bgp:attr-set/bgp:attributes/bgp:aigp-metric" { + deviate not-supported; + } + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:communities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:communities/bgp:community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:communities/bgp:community/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:communities/bgp:community/bgp:community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ext-communities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ext-communities/bgp:ext-community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ext-communities/bgp:ext-community/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ext-communities/bgp:ext-community/bgp:ext-community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ext-communities/bgp:ext-community/bgp:ext-community-raw" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ipv6-ext-communities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ipv6-ext-communities/bgp:ipv6-ext-community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ipv6-ext-communities/bgp:ipv6-ext-community/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ipv6-ext-communities/bgp:ipv6-ext-community/bgp:ipv6-ext-community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:ipv6-ext-communities/bgp:ipv6-ext-community/bgp:ipv6-ext-community-raw" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:large-communities" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:large-communities/bgp:large-community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:large-communities/bgp:large-community/bgp:index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:large-communities/bgp:large-community/bgp:large-community" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:name" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:origin" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:neighbor-address" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:best-path" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv4-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:origin" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:loc-rib/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:neighbor-address" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-pre/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:best-path" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-in-post/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-pre/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:path-id" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:attr-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:ext-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:large-community-index" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:last-modified" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:eligible-route" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:ineligible-reason" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-type" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:optional" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:transitive" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:partial" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:extended" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-len" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:unknown-attributes/bgp:unknown-attribute/bgp:attr-value" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/bgp:bgp/bgp:rib/bgp:afi-safis/bgp:afi-safi/bgp:ipv6-unicast/bgp:neighbors/bgp:neighbor/bgp:adj-rib-out-post/bgp:routes/bgp:route/bgp:reject-reason" { + deviate not-supported; + } + */ +} diff --git a/holo-yang/src/lib.rs b/holo-yang/src/lib.rs index 5b7d3e49..5c2247c5 100644 --- a/holo-yang/src/lib.rs +++ b/holo-yang/src/lib.rs @@ -115,11 +115,15 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { EmbeddedModuleKey::new("ietf-tcp-common", Some("2023-04-17"), None, None) => include_str!("../modules/ietf/ietf-tcp-common@2023-04-17.yang"), // IETF Holo augmentations + EmbeddedModuleKey::new("holo-bgp", None, None, None) => + include_str!("../modules/augmentations/holo-bgp.yang"), EmbeddedModuleKey::new("holo-ospf", None, None, None) => include_str!("../modules/augmentations/holo-ospf.yang"), EmbeddedModuleKey::new("holo-ospf-dev", None, None, None) => include_str!("../modules/augmentations/holo-ospf-dev.yang"), // IETF Holo deviations + EmbeddedModuleKey::new("ietf-bgp-holo-deviations", None, None, None) => + include_str!("../modules/deviations/ietf-bgp-holo-deviations.yang"), EmbeddedModuleKey::new("ietf-mpls-ldp-holo-deviations", None, None, None) => include_str!("../modules/deviations/ietf-mpls-ldp-holo-deviations.yang"), EmbeddedModuleKey::new("ietf-interfaces-holo-deviations", None, None, None) => @@ -157,10 +161,14 @@ pub static YANG_IMPLEMENTED_MODULES: Lazy> = Lazy::new(|| { vec![ "iana-if-type", + "iana-bgp-notification", + "iana-bgp-types", "ietf-bfd-ip-mh", "ietf-bfd-ip-sh", "ietf-bfd-types", "ietf-bfd", + "ietf-bgp", + "ietf-bgp-policy", "ietf-routing-types", "ietf-interfaces", "ietf-ip", @@ -178,6 +186,8 @@ pub static YANG_IMPLEMENTED_MODULES: Lazy> = "ietf-ospf-sr-mpls", "ietf-ospfv3-extended-lsa", "ietf-rip", + "ietf-tcp", + "holo-bgp", "holo-ospf", "holo-ospf-dev", ] @@ -187,6 +197,10 @@ pub static YANG_IMPLEMENTED_MODULES: Lazy> = pub static YANG_FEATURES: Lazy>> = Lazy::new(|| { hashmap! { + "iana-bgp-types" => vec![ + "route-refresh", + "ttl-security", + ], "ietf-bfd-types" => vec![ "client-base-cfg-parms", "single-minimum-interval",