asyncfnbpf_relay<O, IR, IW, OR, OW>( bpf: Arc<Mutex<O>>, in_conn_info: ConnInfo<IR, IW>, out_conn_info: ConnInfo<OR, OW>, ) -> anyhow::Result<()> where O: BPFOperator<K = IdxMapKey>, IR: AsyncRead + Unpin, IW: AsyncWrite + Unpin, OR: AsyncRead + Unpin, OW: AsyncWrite + Unpin, { // used for delete from idx_map and sockmap letmut inbound_addr_opt = None; letmut outbound_addr_opt = None;
// add socket and key to idx_map and sockmap for ipv4 // Note: Local port is stored in host byte order while remote port is in network byte order. // https://github.com/torvalds/linux/blob/v5.10/include/uapi/linux/bpf.h#L4110 iflet (V4(in_addr), V4(out_addr)) = (in_conn_info.addr, out_conn_info.addr) { let inbound_addr = IdxMapKey { addr: u32::to_be(u32::from(in_addr.ip().to_owned())), port: u32::to_be(in_addr.port().into()), }; let outbound_addr = IdxMapKey { addr: u32::to_be(u32::from(out_addr.ip().to_owned())), port: out_addr.port().into(), }; inbound_addr_opt = Some(inbound_addr); outbound_addr_opt = Some(outbound_addr); letmut guard = bpf.lock().unwrap(); let _ = guard.add(out_conn_info.fd, inbound_addr); let _ = guard.add(in_conn_info.fd, outbound_addr); }
// block on copy data // Note: Here we copy bidirectional manually, remove from map ASAP to // avoid outbound port reuse and packet mis-redirected. tracing::info!("Relay started");
let (mut ri, mut wi) = (in_conn_info.read_half, in_conn_info.write_half); let (mut ro, mut wo) = (out_conn_info.read_half, out_conn_info.write_half); let client_to_server = async { let _ = tokio::io::copy(&mut ri, &mut wo).await; tracing::info!("Relay inbound -> outbound finished"); let _ = wo.shutdown().await; ifletSome(addr) = inbound_addr_opt { let _ = bpf.lock().unwrap().delete(addr); } };
let server_to_client = async { let _ = tokio::io::copy(&mut ro, &mut wi).await; tracing::info!("Relay outbound -> inbound finished"); let _ = wi.shutdown().await; ifletSome(addr) = outbound_addr_opt { let _ = bpf.lock().unwrap().delete(addr); } };