From 7e47592f1a52760947d8a2d07d0d1974441e7b85 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sun, 28 Apr 2024 11:54:13 -0700 Subject: [PATCH] vlan plumbing --- Cargo.lock | 56 ++++++++++++++++++------------------- Cargo.toml | 4 +-- bgp/src/session.rs | 4 +++ ddm/src/sys.rs | 1 + mg-lower/src/dendrite.rs | 10 +++++++ mgadm/src/bgp.rs | 3 ++ mgadm/src/static_routing.rs | 3 ++ mgd/src/bgp_admin.rs | 2 ++ mgd/src/bgp_param.rs | 4 +++ mgd/src/main.rs | 3 +- mgd/src/static_admin.rs | 6 ++-- openapi/mg-admin.json | 12 ++++++++ rdb/src/bestpath.rs | 3 ++ rdb/src/db.rs | 2 ++ rdb/src/types.rs | 6 +++- 15 files changed, 85 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ac7253f..4f3677db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,7 +118,7 @@ checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "api_identity" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#bd40fc89c053c8b56fe4a60f70b44e3258dfa3aa" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#50cd41c4f3e613a97b2139e64ac6751f46083163" dependencies = [ "omicron-workspace-hack", "proc-macro2", @@ -562,7 +562,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dendrite?branch=main#183f9b8c26e08344ddbf9bc384f806f712d204b9" +source = "git+https://github.com/oxidecomputer/dendrite?branch=vlan#bc73cc7f2b0401c2c72c9408347c79687b0bab5c" dependencies = [ "anyhow", "chrono", @@ -1007,7 +1007,7 @@ dependencies = [ [[package]] name = "dpd-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dendrite?branch=main#183f9b8c26e08344ddbf9bc384f806f712d204b9" +source = "git+https://github.com/oxidecomputer/dendrite?branch=vlan#bc73cc7f2b0401c2c72c9408347c79687b0bab5c" dependencies = [ "async-trait", "chrono", @@ -1029,7 +1029,7 @@ dependencies = [ [[package]] name = "dropshot" version = "0.10.1-dev" -source = "git+https://github.com/oxidecomputer/dropshot?branch=main#17e8fee4c5753c617b27510e57048710ffd421cd" +source = "git+https://github.com/oxidecomputer/dropshot?branch=main#33e158ba518a9b42aa036a0f6b605d73f11d8b23" dependencies = [ "async-stream", "async-trait", @@ -1074,7 +1074,7 @@ dependencies = [ [[package]] name = "dropshot_endpoint" version = "0.10.1-dev" -source = "git+https://github.com/oxidecomputer/dropshot?branch=main#17e8fee4c5753c617b27510e57048710ffd421cd" +source = "git+https://github.com/oxidecomputer/dropshot?branch=main#33e158ba518a9b42aa036a0f6b605d73f11d8b23" dependencies = [ "proc-macro2", "quote", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "omicron-common" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#bd40fc89c053c8b56fe4a60f70b44e3258dfa3aa" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#50cd41c4f3e613a97b2139e64ac6751f46083163" dependencies = [ "anyhow", "api_identity", @@ -2662,7 +2662,7 @@ dependencies = [ [[package]] name = "omicron-uuid-kinds" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#bd40fc89c053c8b56fe4a60f70b44e3258dfa3aa" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#50cd41c4f3e613a97b2139e64ac6751f46083163" dependencies = [ "newtype-uuid", "paste", @@ -2830,7 +2830,7 @@ dependencies = [ [[package]] name = "oximeter" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#bd40fc89c053c8b56fe4a60f70b44e3258dfa3aa" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#50cd41c4f3e613a97b2139e64ac6751f46083163" dependencies = [ "bytes", "chrono", @@ -2850,7 +2850,7 @@ dependencies = [ [[package]] name = "oximeter-macro-impl" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#bd40fc89c053c8b56fe4a60f70b44e3258dfa3aa" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#50cd41c4f3e613a97b2139e64ac6751f46083163" dependencies = [ "omicron-workspace-hack", "proc-macro2", @@ -3181,7 +3181,7 @@ dependencies = [ [[package]] name = "progenitor" version = "0.6.0" -source = "git+https://github.com/oxidecomputer/progenitor?branch=main#b0fdd988378204813b104ab5aadc0d4b92b41381" +source = "git+https://github.com/oxidecomputer/progenitor?branch=main#0f0b1062f471f33d4d42273c03c4079f6ebf4ad9" dependencies = [ "progenitor-client", "progenitor-impl", @@ -3192,7 +3192,7 @@ dependencies = [ [[package]] name = "progenitor-client" version = "0.6.0" -source = "git+https://github.com/oxidecomputer/progenitor?branch=main#b0fdd988378204813b104ab5aadc0d4b92b41381" +source = "git+https://github.com/oxidecomputer/progenitor?branch=main#0f0b1062f471f33d4d42273c03c4079f6ebf4ad9" dependencies = [ "bytes", "futures-core", @@ -3206,7 +3206,7 @@ dependencies = [ [[package]] name = "progenitor-impl" version = "0.6.0" -source = "git+https://github.com/oxidecomputer/progenitor?branch=main#b0fdd988378204813b104ab5aadc0d4b92b41381" +source = "git+https://github.com/oxidecomputer/progenitor?branch=main#0f0b1062f471f33d4d42273c03c4079f6ebf4ad9" dependencies = [ "getopts", "heck 0.5.0", @@ -3228,7 +3228,7 @@ dependencies = [ [[package]] name = "progenitor-macro" version = "0.6.0" -source = "git+https://github.com/oxidecomputer/progenitor?branch=main#b0fdd988378204813b104ab5aadc0d4b92b41381" +source = "git+https://github.com/oxidecomputer/progenitor?branch=main#0f0b1062f471f33d4d42273c03c4079f6ebf4ad9" dependencies = [ "openapiv3", "proc-macro2", @@ -3765,9 +3765,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" dependencies = [ "bytes", "chrono", @@ -3781,14 +3781,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -3867,9 +3867,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] @@ -3885,9 +3885,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -3896,13 +3896,13 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -4984,7 +4984,7 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typify" version = "0.0.16" -source = "git+https://github.com/oxidecomputer/typify#3dc9e213e0488901df42ac3c72e29eb5f05fe38e" +source = "git+https://github.com/oxidecomputer/typify#f7442947ca90eb97af6446a90e73600a7adc191b" dependencies = [ "typify-impl", "typify-macro", @@ -4993,7 +4993,7 @@ dependencies = [ [[package]] name = "typify-impl" version = "0.0.16" -source = "git+https://github.com/oxidecomputer/typify#3dc9e213e0488901df42ac3c72e29eb5f05fe38e" +source = "git+https://github.com/oxidecomputer/typify#f7442947ca90eb97af6446a90e73600a7adc191b" dependencies = [ "heck 0.5.0", "log", @@ -5010,7 +5010,7 @@ dependencies = [ [[package]] name = "typify-macro" version = "0.0.16" -source = "git+https://github.com/oxidecomputer/typify#3dc9e213e0488901df42ac3c72e29eb5f05fe38e" +source = "git+https://github.com/oxidecomputer/typify#f7442947ca90eb97af6446a90e73600a7adc191b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 3ca099aa..d4f80f20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,12 +100,12 @@ rev = "7ee353a470ea59529ee1b34729681da887aa88ce" [workspace.dependencies.dpd-client] git = "https://github.com/oxidecomputer/dendrite" -branch = "main" +branch = "vlan" package = "dpd-client" [workspace.dependencies.dendrite-common] git = "https://github.com/oxidecomputer/dendrite" -branch = "main" +branch = "vlan" package = "common" [patch."https://github.com/oxidecomputer/netadm-sys"] diff --git a/bgp/src/session.rs b/bgp/src/session.rs index 06ce626e..9785a4b8 100644 --- a/bgp/src/session.rs +++ b/bgp/src/session.rs @@ -378,6 +378,9 @@ pub struct SessionInfo { /// Policy governing exported routes. pub allow_export: ImportExportPolicy, + + /// Vlan tag to assign to data plane routes created by this session. + pub vlan_id: Option, } impl SessionInfo { @@ -2074,6 +2077,7 @@ impl SessionRunner { id, as_path, }), + vlan_id: lock!(self.session).vlan_id, }; if let Err(e) = diff --git a/ddm/src/sys.rs b/ddm/src/sys.rs index ccde37e3..da4a4fc2 100644 --- a/ddm/src/sys.rs +++ b/ddm/src/sys.rs @@ -220,6 +220,7 @@ pub fn add_routes_dendrite( port_id, link_id, tgt_ip: gw, + vlan_id: None, }; let route_set = types::RouteSet { cidr: cidr.into(), diff --git a/mg-lower/src/dendrite.rs b/mg-lower/src/dendrite.rs index 57bf0586..48a62a6e 100644 --- a/mg-lower/src/dendrite.rs +++ b/mg-lower/src/dendrite.rs @@ -34,6 +34,7 @@ pub(crate) struct RouteHash { pub(crate) port_id: PortId, pub(crate) link_id: types::LinkId, pub(crate) nexthop: IpAddr, + pub(crate) vlan_id: Option, } impl RouteHash { @@ -42,6 +43,7 @@ impl RouteHash { port_id: PortId, link_id: types::LinkId, nexthop: IpAddr, + vlan_id: Option, ) -> Result { match (cidr, nexthop) { (Cidr::V4(_), IpAddr::V4(_)) | (Cidr::V6(_), IpAddr::V6(_)) => { @@ -50,6 +52,7 @@ impl RouteHash { port_id, link_id, nexthop, + vlan_id, }) } _ => Err("mismatched subnet and target"), @@ -77,6 +80,7 @@ impl RouteHash { port_id, link_id, nexthop: path.nexthop, + vlan_id: path.vlan_id, }; Ok(rh) @@ -166,6 +170,7 @@ where let tag = dpd.inner().tag.clone(); let port_id = r.port_id; let link_id = r.link_id; + let vlan_id = r.vlan_id; let target = match (r.cidr, r.nexthop) { (Cidr::V4(c), IpAddr::V4(tgt_ip)) => { @@ -191,6 +196,7 @@ where port_id, link_id, tgt_ip, + vlan_id, } .into() } @@ -217,6 +223,7 @@ where port_id, link_id, tgt_ip, + vlan_id, } .into() } @@ -360,6 +367,7 @@ pub(crate) fn get_routes_for_prefix( r.port_id, r.link_id, r.tgt_ip.into(), + r.vlan_id, ) { Ok(rh) => result.push(rh), Err(e) => { @@ -380,6 +388,7 @@ pub(crate) fn get_routes_for_prefix( r.port_id, r.link_id, r.tgt_ip.into(), + r.vlan_id, ) .unwrap() }) @@ -402,6 +411,7 @@ pub(crate) fn get_routes_for_prefix( r.port_id, r.link_id, r.tgt_ip.into(), + r.vlan_id, ) .unwrap() }) diff --git a/mgadm/src/bgp.rs b/mgadm/src/bgp.rs index 71755fdc..0702535f 100644 --- a/mgadm/src/bgp.rs +++ b/mgadm/src/bgp.rs @@ -405,6 +405,9 @@ pub struct Neighbor { #[arg(long)] pub enforce_first_as: bool, + #[arg(long)] + pub vlan_id: u16, + /// Autonomous system number for the router to add the neighbor to. #[clap(env)] pub asn: u32, diff --git a/mgadm/src/static_routing.rs b/mgadm/src/static_routing.rs index f5733c62..5d182be7 100644 --- a/mgadm/src/static_routing.rs +++ b/mgadm/src/static_routing.rs @@ -33,6 +33,7 @@ pub enum Ipv4PrefixParseError { pub struct StaticRoute4 { pub destination: Ipv4Prefix, pub nexthop: Ipv4Addr, + pub vlan_id: Option, } #[derive(Debug, Clone, Copy)] @@ -72,6 +73,7 @@ pub async fn commands(command: Commands, client: Client) -> Result<()> { length: route.destination.len, }, nexthop: route.nexthop, + vlan_id: route.vlan_id, }], }, }; @@ -86,6 +88,7 @@ pub async fn commands(command: Commands, client: Client) -> Result<()> { length: route.destination.len, }, nexthop: route.nexthop, + vlan_id: route.vlan_id, }], }, }; diff --git a/mgd/src/bgp_admin.rs b/mgd/src/bgp_admin.rs index aedaf3f2..5e3b8dd5 100644 --- a/mgd/src/bgp_admin.rs +++ b/mgd/src/bgp_admin.rs @@ -739,6 +739,7 @@ pub(crate) mod helpers { enforce_first_as: rq.enforce_first_as, allow_import: rq.allow_import.clone(), allow_export: rq.allow_export.clone(), + vlan_id: rq.vlan_id, ..Default::default() }; @@ -785,6 +786,7 @@ pub(crate) mod helpers { enforce_first_as: rq.enforce_first_as, allow_import: rq.allow_import.clone(), allow_export: rq.allow_export.clone(), + vlan_id: rq.vlan_id, })?; if start_session { diff --git a/mgd/src/bgp_param.rs b/mgd/src/bgp_param.rs index cd112660..36cc6a6a 100644 --- a/mgd/src/bgp_param.rs +++ b/mgd/src/bgp_param.rs @@ -63,6 +63,7 @@ pub struct Neighbor { pub enforce_first_as: bool, pub allow_import: ImportExportPolicy, pub allow_export: ImportExportPolicy, + pub vlan_id: Option, } impl From for PeerConfig { @@ -107,6 +108,7 @@ impl Neighbor { enforce_first_as: rq.enforce_first_as, allow_import: rq.allow_import, allow_export: rq.allow_export, + vlan_id: rq.vlan_id, } } @@ -132,6 +134,7 @@ impl Neighbor { enforce_first_as: rq.enforce_first_as, allow_import: rq.allow_import.clone(), allow_export: rq.allow_export.clone(), + vlan_id: rq.vlan_id, } } } @@ -281,6 +284,7 @@ pub struct BgpPeerConfig { pub enforce_first_as: bool, pub allow_import: ImportExportPolicy, pub allow_export: ImportExportPolicy, + pub vlan_id: Option, } #[derive(Debug, Deserialize, JsonSchema, Clone)] diff --git a/mgd/src/main.rs b/mgd/src/main.rs index ceb66b47..184fcefa 100644 --- a/mgd/src/main.rs +++ b/mgd/src/main.rs @@ -256,6 +256,7 @@ fn start_bgp_routers( enforce_first_as: nbr.enforce_first_as, allow_import: nbr.allow_import.clone(), allow_export: nbr.allow_export.clone(), + vlan_id: nbr.vlan_id, }, true, ) @@ -279,7 +280,7 @@ fn initialize_static_routes(db: &rdb::Db) { .get_static4() .expect("failed to get static routes from db"); for route in &routes { - let path = Path::for_static(route.nexthop); + let path = Path::for_static(route.nexthop, route.vlan_id); db.add_prefix_path(route.prefix, path, true) .unwrap_or_else(|e| { panic!("failed to initialize static route {route:#?}: {e}") diff --git a/mgd/src/static_admin.rs b/mgd/src/static_admin.rs index ac857ee0..b07860cf 100644 --- a/mgd/src/static_admin.rs +++ b/mgd/src/static_admin.rs @@ -31,6 +31,7 @@ pub struct StaticRoute4List { pub struct StaticRoute4 { pub prefix: Prefix4, pub nexthop: Ipv4Addr, + pub vlan_id: Option, } impl From for StaticRouteKey { @@ -38,6 +39,7 @@ impl From for StaticRouteKey { StaticRouteKey { prefix: val.prefix.into(), nexthop: val.nexthop.into(), + vlan_id: val.vlan_id, } } } @@ -61,7 +63,7 @@ pub async fn static_add_v4_route( .map(Into::into) .collect(); for r in routes { - let path = Path::for_static(r.nexthop); + let path = Path::for_static(r.nexthop, r.vlan_id); ctx.context() .db .add_prefix_path(r.prefix, path, true) @@ -83,7 +85,7 @@ pub async fn static_remove_v4_route( .map(Into::into) .collect(); for r in routes { - let path = Path::for_static(r.nexthop); + let path = Path::for_static(r.nexthop, r.vlan_id); ctx.context() .db .remove_prefix_path(r.prefix, path, true) diff --git a/openapi/mg-admin.json b/openapi/mg-admin.json index 4db1d00c..90db600d 100644 --- a/openapi/mg-admin.json +++ b/openapi/mg-admin.json @@ -2557,6 +2557,12 @@ }, "shutdown": { "type": "boolean" + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 } }, "required": [ @@ -3000,6 +3006,12 @@ }, "prefix": { "$ref": "#/components/schemas/Prefix4" + }, + "vlan_id": { + "nullable": true, + "type": "integer", + "format": "uint16", + "minimum": 0 } }, "required": [ diff --git a/rdb/src/bestpath.rs b/rdb/src/bestpath.rs index af99b23c..6f7fbcbb 100644 --- a/rdb/src/bestpath.rs +++ b/rdb/src/bestpath.rs @@ -112,6 +112,7 @@ mod test { stale: None, as_path: vec![64500, 64501, 64502], }), + vlan_id: None, }; rib.insert(target.into(), BTreeSet::from([path1.clone()])); @@ -131,6 +132,7 @@ mod test { stale: None, as_path: vec![64500, 64501, 64502], }), + vlan_id: None, }; rib.get_mut(&Prefix::V4(target)) .unwrap() @@ -153,6 +155,7 @@ mod test { stale: None, as_path: vec![64500, 64501, 64502], }), + vlan_id: None, }; rib.get_mut(&Prefix::V4(target)) .unwrap() diff --git a/rdb/src/db.rs b/rdb/src/db.rs index 4ca4af5f..14cead37 100644 --- a/rdb/src/db.rs +++ b/rdb/src/db.rs @@ -416,6 +416,7 @@ impl Db { let srk = StaticRouteKey { prefix, nexthop: path.nexthop, + vlan_id: path.vlan_id, }; let key = serde_json::to_string(&srk)?; tree.insert(key.as_str(), "")?; @@ -530,6 +531,7 @@ impl Db { let srk = StaticRouteKey { prefix, nexthop: path.nexthop, + vlan_id: path.vlan_id, }; let key = serde_json::to_string(&srk)?; tree.remove(key.as_str())?; diff --git a/rdb/src/types.rs b/rdb/src/types.rs index 90d19f85..6842b361 100644 --- a/rdb/src/types.rs +++ b/rdb/src/types.rs @@ -20,6 +20,7 @@ pub struct Path { pub shutdown: bool, pub local_pref: Option, pub bgp: Option, + pub vlan_id: Option, } // Define a basic ordering on paths so bestpath selection is deterministic @@ -44,9 +45,10 @@ impl Ord for Path { } impl Path { - pub fn for_static(nexthop: IpAddr) -> Self { + pub fn for_static(nexthop: IpAddr, vlan_id: Option) -> Self { Self { nexthop, + vlan_id, shutdown: false, local_pref: None, bgp: None, @@ -99,6 +101,7 @@ impl Ord for BgpPathProperties { pub struct StaticRouteKey { pub prefix: Prefix, pub nexthop: IpAddr, + pub vlan_id: Option, } #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -447,6 +450,7 @@ pub struct BgpNeighborInfo { pub enforce_first_as: bool, pub allow_import: ImportExportPolicy, pub allow_export: ImportExportPolicy, + pub vlan_id: Option, } #[derive(Debug, Copy, Clone, Deserialize, Serialize, JsonSchema)]