diff --git a/README.md b/README.md index 8118361c..3b7653ff 100644 --- a/README.md +++ b/README.md @@ -194,8 +194,8 @@ Holo supports the following IETF RFCs and Internet drafts: | ietf-bfd@2022-09-22 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-bfd.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 | 0.00% | 100.00% | - | - | [18.75%](http://westphal.com.br/holo/ietf-ipv4-unicast-routing.html) | -| ietf-ipv6-unicast-routing@2018-03-13 | 0.00% | 100.00% | - | - | [8.57%](http://westphal.com.br/holo/ietf-ipv6-unicast-routing.html) | +| ietf-ipv4-unicast-routing@2018-03-13 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-ipv4-unicast-routing.html) | +| ietf-ipv6-unicast-routing@2018-03-13 | 40.62% | 100.00% | - | - | [45.71%](http://westphal.com.br/holo/ietf-ipv6-unicast-routing.html) | | ietf-key-chain@2017-04-18 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-key-chain.html) | | ietf-mpls-ldp@2022-03-14 | 86.96% | 92.31% | 100.00% | 100.00% | [92.38%](http://westphal.com.br/holo/ietf-mpls-ldp.html) | | ietf-mpls@2020-12-18 | 0.00% | 57.14% | - | - | [35.29%](http://westphal.com.br/holo/ietf-mpls.html) | @@ -204,7 +204,7 @@ Holo supports the following IETF RFCs and Internet drafts: | ietf-ospfv3-extended-lsa@2023-08-21 | 50.00% | 85.28% | - | - | [84.85%](http://westphal.com.br/holo/ietf-ospfv3-extended-lsa.html) | | ietf-rip@2020-02-20 | 27.91% | 93.33% | 100.00% | - | [55.41%](http://westphal.com.br/holo/ietf-rip.html) | | ietf-routing-policy@2021-10-11 | 100.00% | 0.00% | - | - | [98.11%](http://westphal.com.br/holo/ietf-routing-policy.html) | -| ietf-routing@2018-03-13 | 91.67% | 85.71% | - | - | [88.46%](http://westphal.com.br/holo/ietf-routing.html) | +| ietf-routing@2018-03-13 | 100.00% | 85.71% | - | - | [92.31%](http://westphal.com.br/holo/ietf-routing.html) | | ietf-segment-routing-mpls@2021-05-26 | 62.50% | 0.00% | - | 23.53% | [32.76%](http://westphal.com.br/holo/ietf-segment-routing-mpls.html) | | ietf-segment-routing@2021-05-26 | 100.00% | - | - | - | [100.00%](http://westphal.com.br/holo/ietf-segment-routing.html) | diff --git a/holo-routing/src/lib.rs b/holo-routing/src/lib.rs index 36a5aa27..8a3b2249 100644 --- a/holo-routing/src/lib.rs +++ b/holo-routing/src/lib.rs @@ -24,10 +24,11 @@ use holo_utils::ibus::{IbusMsg, IbusReceiver, IbusSender}; use holo_utils::protocol::Protocol; use holo_utils::sr::SrCfg; use holo_utils::Database; +use ipnetwork::IpNetwork; use tokio::sync::mpsc; use tracing::Instrument; -use crate::rib::Rib; +use crate::rib::{Rib, StaticRoute}; pub struct Master { // Northbound Tx channel. @@ -42,6 +43,8 @@ pub struct Master { pub netlink_handle: rtnetlink::Handle, // RIB. pub rib: Rib, + // Static routes. + pub static_routes: BTreeMap, // SR configuration data. pub sr_config: SrCfg, // Protocol instances. @@ -176,6 +179,7 @@ pub fn start( event_recorder_config, netlink_handle: netlink::init(), rib: Default::default(), + static_routes: Default::default(), sr_config: Default::default(), instances: Default::default(), }; diff --git a/holo-routing/src/northbound/configuration.rs b/holo-routing/src/northbound/configuration.rs index 6b7cd26c..692c8f6c 100644 --- a/holo-routing/src/northbound/configuration.rs +++ b/holo-routing/src/northbound/configuration.rs @@ -4,7 +4,7 @@ // SPDX-License-Identifier: MIT // -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use std::sync::{Arc, LazyLock as Lazy}; use async_trait::async_trait; @@ -22,12 +22,14 @@ use holo_utils::ibus::{IbusMsg, SrCfgEvent}; use holo_utils::ip::{AddressFamily, IpNetworkKind}; use holo_utils::mpls::LabelRange; use holo_utils::protocol::Protocol; +use holo_utils::southbound::{Nexthop, NexthopSpecial, RouteKeyMsg, RouteMsg}; use holo_utils::sr::{IgpAlgoType, SidLastHopBehavior, SrCfgPrefixSid}; use holo_utils::yang::DataNodeRefExt; use holo_yang::TryFromYang; use ipnetwork::IpNetwork; use crate::northbound::REGEX_PROTOCOLS; +use crate::rib::{StaticRoute, StaticRouteNexthop}; use crate::{InstanceId, Master}; static VALIDATION_CALLBACKS: Lazy = @@ -40,6 +42,8 @@ pub enum ListEntry { #[default] None, ProtocolInstance(InstanceId), + StaticRoute(IpNetwork), + StaticRouteNexthop(IpNetwork, String), SrCfgPrefixSid(IpNetwork, IgpAlgoType), } @@ -51,6 +55,8 @@ pub enum Resource { #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum Event { InstanceStart { protocol: Protocol, name: String }, + StaticRouteInstall(IpNetwork), + StaticRouteUninstall(IpNetwork), SrCfgUpdate, SrCfgLabelRangeUpdate, SrCfgPrefixSidUpdate(AddressFamily), @@ -122,6 +128,318 @@ fn load_callbacks() -> Callbacks { .delete_apply(|_master, _args| { // Nothing to do. }) + .path(control_plane_protocol::static_routes::ipv4::route::PATH) + .create_apply(|master, args| { + let prefix = args.dnode.get_prefix_relative("./destination-prefix").unwrap(); + + master.static_routes.insert(prefix, StaticRoute::default()); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + + master.static_routes.remove(&prefix); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteUninstall(prefix)); + }) + .lookup(|_master, _list_entry, dnode| { + let prefix = dnode.get_prefix_relative("./destination-prefix").unwrap(); + ListEntry::StaticRoute(prefix) + }) + .path(control_plane_protocol::static_routes::ipv4::route::description::PATH) + .modify_apply(|_master, _args| { + // Nothing to do. + }) + .delete_apply(|_master, _args| { + // Nothing to do. + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::outgoing_interface::PATH) + .modify_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let ifname = args.dnode.get_string(); + route.nexthop_single.ifname = Some(ifname); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_single.ifname = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::ipv4_next_hop_address::PATH) + .modify_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let addr = args.dnode.get_ip(); + route.nexthop_single.addr = Some(addr); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_single.addr = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::special_next_hop::PATH) + .modify_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let special = args.dnode.get_string(); + let special = NexthopSpecial::try_from_yang(&special).unwrap(); + route.nexthop_special = Some(special); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_special = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::next_hop_list::next_hop::PATH) + .create_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let index = args.dnode.get_string_relative("./index").unwrap(); + route.nexthop_list.insert(index, StaticRouteNexthop::default()); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_list.remove(&nh_index); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .lookup(|_master, list_entry, dnode| { + let prefix = list_entry.into_static_route().unwrap(); + + let index = dnode.get_string_relative("./index").unwrap(); + ListEntry::StaticRouteNexthop(prefix, index) + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::next_hop_list::next_hop::outgoing_interface::PATH) + .modify_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + let ifname = args.dnode.get_string(); + nexthop.ifname = Some(ifname); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + nexthop.ifname = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::next_hop_list::next_hop::ipv4_next_hop_address::PATH) + .modify_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + let addr = args.dnode.get_ip(); + nexthop.addr = Some(addr); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + nexthop.addr = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv6::route::PATH) + .create_apply(|master, args| { + let prefix = args.dnode.get_prefix_relative("./destination-prefix").unwrap(); + + master.static_routes.insert(prefix, StaticRoute::default()); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + + master.static_routes.remove(&prefix); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteUninstall(prefix)); + }) + .lookup(|_master, _list_entry, dnode| { + let prefix = dnode.get_prefix_relative("./destination-prefix").unwrap(); + ListEntry::StaticRoute(prefix) + }) + .path(control_plane_protocol::static_routes::ipv6::route::description::PATH) + .modify_apply(|_master, _args| { + // Nothing to do. + }) + .delete_apply(|_master, _args| { + // Nothing to do. + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::outgoing_interface::PATH) + .modify_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let ifname = args.dnode.get_string(); + route.nexthop_single.ifname = Some(ifname); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_single.ifname = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::ipv6_next_hop_address::PATH) + .modify_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let addr = args.dnode.get_ip(); + route.nexthop_single.addr = Some(addr); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_single.addr = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::special_next_hop::PATH) + .modify_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let special = args.dnode.get_string(); + let special = NexthopSpecial::try_from_yang(&special).unwrap(); + route.nexthop_special = Some(special); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_special = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::next_hop_list::next_hop::PATH) + .create_apply(|master, args| { + let prefix = args.list_entry.into_static_route().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + let index = args.dnode.get_string_relative("./index").unwrap(); + route.nexthop_list.insert(index, StaticRouteNexthop::default()); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + + route.nexthop_list.remove(&nh_index); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .lookup(|_master, list_entry, dnode| { + let prefix = list_entry.into_static_route().unwrap(); + + let index = dnode.get_string_relative("./index").unwrap(); + ListEntry::StaticRouteNexthop(prefix, index) + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::next_hop_list::next_hop::outgoing_interface::PATH) + .modify_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + let ifname = args.dnode.get_string(); + nexthop.ifname = Some(ifname); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + nexthop.ifname = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::next_hop_list::next_hop::ipv6_next_hop_address::PATH) + .modify_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + let addr = args.dnode.get_ip(); + nexthop.addr = Some(addr); + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) + .delete_apply(|master, args| { + let (prefix, nh_index) = args.list_entry.into_static_route_nexthop().unwrap(); + let route = master.static_routes.get_mut(&prefix).unwrap(); + let nexthop = route.nexthop_list.get_mut(&nh_index).unwrap(); + + nexthop.addr = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::StaticRouteInstall(prefix)); + }) .path(sr_mpls::bindings::connected_prefix_sid_map::connected_prefix_sid::PATH) .create_apply(|master, args| { let prefix = args.dnode.get_prefix_relative("./prefix").unwrap(); @@ -465,12 +783,61 @@ impl Provider for Master { Some(event_recorder_config), ) } + Protocol::STATIC => { + // Nothing to do. + return; + } }; // Keep track of northbound channel associated to the protocol // type and name. self.instances.insert(instance_id, nb_daemon_tx); } + Event::StaticRouteInstall(prefix) => { + let route = self.static_routes.get(&prefix).unwrap(); + + // Get nexthops. + let mut nexthops = BTreeSet::default(); + if let Some(nexthop) = static_nexthop_get(&route.nexthop_single) + { + nexthops.insert(nexthop); + } + if let Some(special) = route.nexthop_special { + nexthops.insert(Nexthop::Special(special)); + } + for nexthop in route + .nexthop_list + .values() + .filter_map(|nexthop| static_nexthop_get(nexthop)) + { + nexthops.insert(nexthop); + } + + // Prepare message. + let msg = RouteMsg { + protocol: Protocol::STATIC, + prefix, + distance: 1, + metric: 0, + tag: None, + nexthops, + }; + + // Send message. + let msg = IbusMsg::RouteIpAdd(msg); + let _ = self.ibus_tx.send(msg); + } + Event::StaticRouteUninstall(prefix) => { + // Prepare message. + let msg = RouteKeyMsg { + protocol: Protocol::STATIC, + prefix, + }; + + // Send message. + let msg = IbusMsg::RouteIpDel(msg); + let _ = self.ibus_tx.send(msg); + } Event::SrCfgUpdate => { // Update the shared SR configuration by creating a new reference-counted copy. self.shared.sr_config = Arc::new(self.sr_config.clone()); @@ -495,3 +862,16 @@ impl Provider for Master { } } } + +// ===== helper functions ===== + +fn static_nexthop_get(nexthop: &StaticRouteNexthop) -> Option { + match (&nexthop.ifname, nexthop.addr) { + (Some(_), Some(addr)) => Some(Nexthop::Address { + ifindex: 3, + addr, + labels: Default::default(), + }), + _ => None, + } +} diff --git a/holo-routing/src/northbound/state.rs b/holo-routing/src/northbound/state.rs index 496ecd61..7a936e13 100644 --- a/holo-routing/src/northbound/state.rs +++ b/holo-routing/src/northbound/state.rs @@ -72,6 +72,26 @@ fn load_callbacks() -> Callbacks { .map(ListEntry::ProtocolInstance); Some(Box::new(iter)) }) + .path(control_plane_protocol::static_routes::ipv4::route::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(control_plane_protocol::static_routes::ipv4::route::next_hop::next_hop_list::next_hop::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(control_plane_protocol::static_routes::ipv6::route::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(control_plane_protocol::static_routes::ipv6::route::next_hop::next_hop_list::next_hop::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) .path(sr_mpls::bindings::connected_prefix_sid_map::connected_prefix_sid::PATH) .get_iterate(|_master, _args| { // No operational data under this list. diff --git a/holo-routing/src/rib.rs b/holo-routing/src/rib.rs index 2e6b66e7..fb44d5b6 100644 --- a/holo-routing/src/rib.rs +++ b/holo-routing/src/rib.rs @@ -4,7 +4,8 @@ // SPDX-License-Identifier: MIT // -use std::collections::{btree_map, BTreeMap, BTreeSet}; +use std::collections::{btree_map, BTreeMap, BTreeSet, HashMap}; +use std::net::IpAddr; use bitflags::bitflags; use chrono::{DateTime, Utc}; @@ -15,7 +16,7 @@ use holo_utils::mpls::Label; use holo_utils::protocol::Protocol; use holo_utils::southbound::{ AddressFlags, AddressMsg, LabelInstallMsg, LabelUninstallMsg, Nexthop, - RouteKeyMsg, RouteMsg, + NexthopSpecial, RouteKeyMsg, RouteMsg, }; use holo_utils::{UnboundedReceiver, UnboundedSender}; use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; @@ -53,6 +54,19 @@ bitflags! { } } +#[derive(Debug, Default)] +pub struct StaticRoute { + pub nexthop_single: StaticRouteNexthop, + pub nexthop_special: Option, + pub nexthop_list: HashMap, +} + +#[derive(Clone, Debug, Default)] +pub struct StaticRouteNexthop { + pub ifname: Option, + pub addr: Option, +} + // ===== impl Rib ===== impl Rib { diff --git a/holo-tools/src/replay.rs b/holo-tools/src/replay.rs index 1a3e250d..b144a876 100644 --- a/holo-tools/src/replay.rs +++ b/holo-tools/src/replay.rs @@ -74,7 +74,7 @@ async fn main() { Protocol::RIPNG => { replay::>(filename).await } - Protocol::DIRECT => { + Protocol::DIRECT | Protocol::STATIC => { eprintln!("Unsupported protocol type"); std::process::exit(1); } diff --git a/holo-utils/src/protocol.rs b/holo-utils/src/protocol.rs index c3d5c4b4..6789568e 100644 --- a/holo-utils/src/protocol.rs +++ b/holo-utils/src/protocol.rs @@ -22,6 +22,7 @@ pub enum Protocol { OSPFV3, RIPV2, RIPNG, + STATIC, } // ===== impl Protocol ===== @@ -36,6 +37,7 @@ impl std::fmt::Display for Protocol { Protocol::OSPFV3 => write!(f, "ospfv3"), Protocol::RIPV2 => write!(f, "ripv2"), Protocol::RIPNG => write!(f, "ripng"), + Protocol::STATIC => write!(f, "static"), } } } @@ -46,7 +48,7 @@ impl FromStr for Protocol { fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "bfd" => Ok(Protocol::BFD), - "direct" => Ok(Protocol::DIRECT), + "static" => Ok(Protocol::STATIC), "ldp" => Ok(Protocol::LDP), "ospfv2" => Ok(Protocol::OSPFV2), "ospfv3" => Ok(Protocol::OSPFV3), @@ -67,6 +69,7 @@ impl ToYang for Protocol { Protocol::OSPFV3 => "ietf-ospf:ospfv3".to_owned(), Protocol::RIPV2 => "ietf-rip:ripv2".to_owned(), Protocol::RIPNG => "ietf-rip:ripng".to_owned(), + Protocol::STATIC => "ietf-routing:static".to_owned(), } } } @@ -81,6 +84,7 @@ impl TryFromYang for Protocol { "ietf-ospf:ospfv3" => Some(Protocol::OSPFV3), "ietf-rip:ripv2" => Some(Protocol::RIPV2), "ietf-rip:ripng" => Some(Protocol::RIPNG), + "ietf-routing:static" => Some(Protocol::STATIC), _ => None, } } diff --git a/holo-utils/src/southbound.rs b/holo-utils/src/southbound.rs index cf356967..6d2ae96b 100644 --- a/holo-utils/src/southbound.rs +++ b/holo-utils/src/southbound.rs @@ -8,7 +8,7 @@ use std::collections::BTreeSet; use std::net::IpAddr; use bitflags::bitflags; -use holo_yang::ToYang; +use holo_yang::{ToYang, TryFromYang}; use ipnetwork::IpNetwork; use serde::{Deserialize, Serialize}; @@ -48,7 +48,7 @@ pub enum Nexthop { Special(NexthopSpecial), } -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[derive(Deserialize, Serialize)] pub enum NexthopSpecial { Blackhole, @@ -181,3 +181,14 @@ impl ToYang for NexthopSpecial { } } } + +impl TryFromYang for NexthopSpecial { + fn try_from_yang(value: &str) -> Option { + match value { + "blackhole" => Some(NexthopSpecial::Blackhole), + "unreachable" => Some(NexthopSpecial::Unreachable), + "prohibit" => Some(NexthopSpecial::Prohibit), + _ => None, + } + } +} diff --git a/holo-yang/modules/deviations/ietf-routing-holo-deviations.yang b/holo-yang/modules/deviations/ietf-routing-holo-deviations.yang index 34381f64..d26fe5f3 100644 --- a/holo-yang/modules/deviations/ietf-routing-holo-deviations.yang +++ b/holo-yang/modules/deviations/ietf-routing-holo-deviations.yang @@ -22,10 +22,6 @@ module ietf-routing-holo-deviations { deviate not-supported; } - deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rt:static-routes" { - deviate not-supported; - } - deviation "/rt:routing-state" { deviate not-supported; }