diff --git a/dataplane/.cargo/config.toml b/dataplane/.cargo/config.toml index f0ccbc9a..029b94e5 100644 --- a/dataplane/.cargo/config.toml +++ b/dataplane/.cargo/config.toml @@ -1,2 +1,6 @@ [alias] -xtask = "run --package xtask --" \ No newline at end of file +xtask = "run --package xtask --" + +## Needed to build blixt's programs with BTF + custom bpf-linker build. +[build] +rustflags = ["-C", "debuginfo=2"] \ No newline at end of file diff --git a/dataplane/Makefile b/dataplane/Makefile index 76a6443a..100e0d9f 100644 --- a/dataplane/Makefile +++ b/dataplane/Makefile @@ -26,3 +26,25 @@ build.image: load.image: build.image kind load docker-image $(IMAGE):$(TAG) --name $(KIND_CLUSTER) && \ kubectl -n blixt-system rollout restart daemonset blixt-dataplane + +.PHONY: build.bytecode.images +build.bytecode.images: build + docker build \ + --build-arg PROGRAM_NAME=blixt-tc-ingress \ + --build-arg BPF_FUNCTION_NAME=tc_ingress \ + --build-arg PROGRAM_TYPE=tc \ + --build-arg BYTECODE_FILENAME=loader \ + -f https://raw.githubusercontent.com/bpfd-dev/bpfd/main/packaging/container-deployment/Containerfile.bytecode \ + ./target/bpfel-unknown-none/debug -t quay.io/bpfd-bytecode/blixt-tc-ingress:latest + docker build \ + --build-arg PROGRAM_NAME=blixt-tc-egress \ + --build-arg BPF_FUNCTION_NAME=tc_egress \ + --build-arg PROGRAM_TYPE=tc \ + --build-arg BYTECODE_FILENAME=loader \ + -f https://raw.githubusercontent.com/bpfd-dev/bpfd/main/packaging/container-deployment/Containerfile.bytecode \ + ./target/bpfel-unknown-none/debug -t quay.io/bpfd-bytecode/blixt-tc-egress:latest + +.PHONY: push.bytecode.images +push.bytecode.images: build.bytecode.images + docker push quay.io/bpfd-bytecode/blixt-tc-egress:latest + docker push quay.io/bpfd-bytecode/blixt-tc-ingress:latest diff --git a/dataplane/api-server/src/backends.rs b/dataplane/api-server/src/backends.rs index 5571964e..25b70150 100644 --- a/dataplane/api-server/src/backends.rs +++ b/dataplane/api-server/src/backends.rs @@ -45,8 +45,8 @@ pub struct InterfaceIndexConfirmation { /// Generated client implementations. pub mod backends_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; use tonic::codegen::http::Uri; + use tonic::codegen::*; #[derive(Debug, Clone)] pub struct BackendsClient { inner: tonic::client::Grpc, @@ -90,9 +90,8 @@ pub mod backends_client { >::ResponseBody, >, >, - , - >>::Error: Into + Send + Sync, + >>::Error: + Into + Send + Sync, { BackendsClient::new(InterceptedService::new(inner, interceptor)) } @@ -130,23 +129,16 @@ pub mod backends_client { pub async fn get_interface_index( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/backends.backends/GetInterfaceIndex", - ); + let path = http::uri::PathAndQuery::from_static("/backends.backends/GetInterfaceIndex"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("backends.backends", "GetInterfaceIndex")); @@ -156,38 +148,34 @@ pub mod backends_client { &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/backends.backends/Update"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("backends.backends", "Update")); + req.extensions_mut() + .insert(GrpcMethod::new("backends.backends", "Update")); self.inner.unary(req, path, codec).await } pub async fn delete( &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/backends.backends/Delete"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("backends.backends", "Delete")); + req.extensions_mut() + .insert(GrpcMethod::new("backends.backends", "Delete")); self.inner.unary(req, path, codec).await } } @@ -202,10 +190,7 @@ pub mod backends_server { async fn get_interface_index( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn update( &self, request: tonic::Request, @@ -238,10 +223,7 @@ pub mod backends_server { max_encoding_message_size: None, } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where F: tonic::service::Interceptor, { @@ -297,21 +279,12 @@ pub mod backends_server { "/backends.backends/GetInterfaceIndex" => { #[allow(non_camel_case_types)] struct GetInterfaceIndexSvc(pub Arc); - impl tonic::server::UnaryService - for GetInterfaceIndexSvc { + impl tonic::server::UnaryService for GetInterfaceIndexSvc { type Response = super::InterfaceIndexConfirmation; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - (*inner).get_interface_index(request).await - }; + let fut = async move { (*inner).get_interface_index(request).await }; Box::pin(fut) } } @@ -341,13 +314,9 @@ pub mod backends_server { "/backends.backends/Update" => { #[allow(non_camel_case_types)] struct UpdateSvc(pub Arc); - impl tonic::server::UnaryService - for UpdateSvc { + impl tonic::server::UnaryService for UpdateSvc { type Response = super::Confirmation; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -383,17 +352,10 @@ pub mod backends_server { "/backends.backends/Delete" => { #[allow(non_camel_case_types)] struct DeleteSvc(pub Arc); - impl tonic::server::UnaryService - for DeleteSvc { + impl tonic::server::UnaryService for DeleteSvc { type Response = super::Confirmation; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { + type Future = BoxFuture, tonic::Status>; + fn call(&mut self, request: tonic::Request) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { (*inner).delete(request).await }; Box::pin(fut) @@ -422,18 +384,14 @@ pub mod backends_server { }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } diff --git a/dataplane/ebpf/.cargo/config.toml b/dataplane/ebpf/.cargo/config.toml index 5d7e5915..997f47a2 100644 --- a/dataplane/ebpf/.cargo/config.toml +++ b/dataplane/ebpf/.cargo/config.toml @@ -1,6 +1,7 @@ [build] target-dir = "../target" target = "bpfel-unknown-none" +rustflags = ["-C", "save-temps"] [unstable] build-std = ["core"] \ No newline at end of file diff --git a/dataplane/ebpf/Cargo.toml b/dataplane/ebpf/Cargo.toml index 1805d00c..e162a6cd 100644 --- a/dataplane/ebpf/Cargo.toml +++ b/dataplane/ebpf/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [profile.dev] opt-level = 3 -debug = false +debug = true debug-assertions = false overflow-checks = false lto = true @@ -29,6 +29,7 @@ rpath = false lto = true panic = "abort" codegen-units = 1 +debug-assertions = false [workspace] members = [] diff --git a/dataplane/ebpf/src/main.rs b/dataplane/ebpf/src/main.rs index e16ec0ac..e4b3b8b4 100644 --- a/dataplane/ebpf/src/main.rs +++ b/dataplane/ebpf/src/main.rs @@ -126,5 +126,5 @@ fn try_tc_egress(ctx: TcContext) -> Result { #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { core::hint::unreachable_unchecked() } + loop {} } diff --git a/dataplane/loader/src/main.rs b/dataplane/loader/src/main.rs index 6d044748..88db3e08 100644 --- a/dataplane/loader/src/main.rs +++ b/dataplane/loader/src/main.rs @@ -4,11 +4,11 @@ Copyright 2023 The Kubernetes Authors. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -use std::net::Ipv4Addr; +use std::{net::Ipv4Addr, path::Path}; use anyhow::Context; use api_server::start as start_api_server; -use aya::maps::HashMap; +use aya::maps::{HashMap, Map, MapData}; use aya::programs::{tc, SchedClassifier, TcAttachType}; use aya::{include_bytes_aligned, Bpf}; use aya_log::BpfLogger; @@ -26,48 +26,73 @@ struct Opt { async fn main() -> Result<(), anyhow::Error> { let opt = Opt::parse(); + // TODO(astoycos) Let's determine a better way to let processes know bpfd is up and running, + // Maybe if we're not running as a privileged deployment ALWAYS wait for bpfd?. + std::thread::sleep(std::time::Duration::from_secs(5)); env_logger::init(); - info!("loading ebpf programs"); - - #[cfg(debug_assertions)] - let mut bpf = Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/debug/loader" - ))?; - #[cfg(not(debug_assertions))] - let mut bpf = Bpf::load(include_bytes_aligned!( - "../../target/bpfel-unknown-none/release/loader" - ))?; - if let Err(e) = BpfLogger::init(&mut bpf) { - warn!("failed to initialize eBPF logger: {}", e); - } + // If bpfd loaded the programs just load the maps. + let bpfd_maps = Path::new("/run/bpfd/fs/maps"); + + if bpfd_maps.exists() { + info!("programs loaded via bpfd"); + let backends: HashMap<_, BackendKey, BackendList> = Map::HashMap( + MapData::from_pin(bpfd_maps.join("BACKENDS")).expect("no maps named BACKENDS"), + ) + .try_into()?; + + let gateway_indexes: HashMap<_, BackendKey, u16> = Map::HashMap( + MapData::from_pin(bpfd_maps.join("GATEWAY_INDEXES")) + .expect("no maps named GATEWAY_INDEXES"), + ) + .try_into()?; + + info!("starting api server"); + start_api_server(Ipv4Addr::new(0, 0, 0, 0), 9874, backends, gateway_indexes).await?; + } else { + info!("loading ebpf programs"); + + #[cfg(debug_assertions)] + let mut bpf = Bpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/debug/loader" + ))?; + #[cfg(not(debug_assertions))] + let mut bpf = Bpf::load(include_bytes_aligned!( + "../../target/bpfel-unknown-none/release/loader" + ))?; + if let Err(e) = BpfLogger::init(&mut bpf) { + warn!("failed to initialize eBPF logger: {}", e); + } + + info!("attaching tc_ingress program to {}", &opt.iface); - info!("attaching tc_ingress program to {}", &opt.iface); - - let _ = tc::qdisc_add_clsact(&opt.iface); - let ingress_program: &mut SchedClassifier = - bpf.program_mut("tc_ingress").unwrap().try_into()?; - ingress_program.load()?; - ingress_program - .attach(&opt.iface, TcAttachType::Ingress) - .context("failed to attach the ingress TC program")?; - - info!("attaching tc_egress program to {}", &opt.iface); - - let egress_program: &mut SchedClassifier = bpf.program_mut("tc_egress").unwrap().try_into()?; - egress_program.load()?; - egress_program - .attach(&opt.iface, TcAttachType::Egress) - .context("failed to attach the egress TC program")?; - - info!("starting api server"); - let backends: HashMap<_, BackendKey, BackendList> = - HashMap::try_from(bpf.take_map("BACKENDS").expect("no maps named BACKENDS"))?; - let gateway_indexes: HashMap<_, BackendKey, u16> = HashMap::try_from( - bpf.take_map("GATEWAY_INDEXES") - .expect("no maps named GATEWAY_INDEXES"), - )?; - start_api_server(Ipv4Addr::new(0, 0, 0, 0), 9874, backends, gateway_indexes).await?; + let _ = tc::qdisc_add_clsact(&opt.iface); + let ingress_program: &mut SchedClassifier = + bpf.program_mut("tc_ingress").unwrap().try_into()?; + ingress_program.load()?; + ingress_program + .attach(&opt.iface, TcAttachType::Ingress) + .context("failed to attach the ingress TC program")?; + + info!("attaching tc_egress program to {}", &opt.iface); + + let egress_program: &mut SchedClassifier = + bpf.program_mut("tc_egress").unwrap().try_into()?; + egress_program.load()?; + egress_program + .attach(&opt.iface, TcAttachType::Egress) + .context("failed to attach the egress TC program")?; + + info!("starting api server"); + let backends: HashMap<_, BackendKey, BackendList> = + HashMap::try_from(bpf.take_map("BACKENDS").expect("no maps named BACKENDS"))?; + let gateway_indexes: HashMap<_, BackendKey, u16> = HashMap::try_from( + bpf.take_map("GATEWAY_INDEXES") + .expect("no maps named GATEWAY_INDEXES"), + )?; + + start_api_server(Ipv4Addr::new(0, 0, 0, 0), 9874, backends, gateway_indexes).await?; + } info!("Exiting...");