diff --git a/rust/relay/ebpf-turn-router/src/error.rs b/rust/relay/ebpf-turn-router/src/error.rs index ed65b3851..dae5068a9 100644 --- a/rust/relay/ebpf-turn-router/src/error.rs +++ b/rust/relay/ebpf-turn-router/src/error.rs @@ -10,12 +10,29 @@ pub enum Error { Ipv4PacketWithOptions, NotAChannelDataMessage, BadChannelDataLength, - NoChannelBinding, + NoEntry(SupportedChannel), + UnsupportedChannel(UnsupportedChannel), XdpLoadBytesFailed, XdpAdjustHeadFailed, XdpStoreBytesFailed, } +#[derive(Debug, Clone, Copy)] +pub enum SupportedChannel { + UdpToChan44, + ChanToUdp44, + UdpToChan66, + ChanToUdp66, +} + +#[derive(Debug, Clone, Copy)] +pub enum UnsupportedChannel { + UdpToChan46, + ChanToUdp46, + UdpToChan64, + ChanToUdp64, +} + impl aya_log_ebpf::WriteToBuf for Error { fn write(self, buf: &mut [u8]) -> Option { let msg = match self { @@ -27,7 +44,30 @@ impl aya_log_ebpf::WriteToBuf for Error { Error::Ipv4PacketWithOptions => "IPv4 packet has options", Error::NotAChannelDataMessage => "Not a channel data message", Error::BadChannelDataLength => "Channel data length does not match packet length", - Error::NoChannelBinding => "No channel binding", + Error::NoEntry(SupportedChannel::UdpToChan44) => { + "No entry in UDPv4 to channel IPv4 map" + } + Error::NoEntry(SupportedChannel::ChanToUdp44) => { + "No entry in channel IPv4 to UDPv4 map" + } + Error::NoEntry(SupportedChannel::UdpToChan66) => { + "No entry in UDPv6 to channel IPv6 map" + } + Error::NoEntry(SupportedChannel::ChanToUdp66) => { + "No entry in channel IPv6 to UDPv6 map" + } + Error::UnsupportedChannel(UnsupportedChannel::UdpToChan46) => { + "Relaying UDPv4 to channel IPv6 is not supported" + } + Error::UnsupportedChannel(UnsupportedChannel::ChanToUdp46) => { + "Relaying channel IPv4 to UDPv6 is not supported" + } + Error::UnsupportedChannel(UnsupportedChannel::UdpToChan64) => { + "Relaying UDPv6 to channel IPv4 is not supported" + } + Error::UnsupportedChannel(UnsupportedChannel::ChanToUdp64) => { + "Relaying channel IPv6 to UDPv4 is not supported" + } Error::XdpLoadBytesFailed => "Failed to load bytes", Error::XdpAdjustHeadFailed => "Failed to adjust head", Error::XdpStoreBytesFailed => "Failed to store bytes", diff --git a/rust/relay/ebpf-turn-router/src/main.rs b/rust/relay/ebpf-turn-router/src/main.rs index ee035a315..f4db51ca2 100644 --- a/rust/relay/ebpf-turn-router/src/main.rs +++ b/rust/relay/ebpf-turn-router/src/main.rs @@ -11,6 +11,7 @@ use aya_ebpf::{ use aya_log_ebpf::*; use channel_data::{CdHdr, ChannelData}; use ebpf_shared::{ClientAndChannelV4, ClientAndChannelV6, PortAndPeerV4, PortAndPeerV6}; +use error::{SupportedChannel, UnsupportedChannel}; use eth::Eth; use ip4::{Ip4, Ipv4Hdr}; use ip6::Ip6; @@ -38,9 +39,8 @@ mod udp; const NUM_ENTRIES: u32 = 0x10000; -/// Channel mappings from an IPv4 socket + channel number to an IPv4 socket + port. -/// -/// TODO: Update flags to `BPF_F_NO_PREALLOC` to guarantee atomicity? Needs research. +// TODO: Update flags to `BPF_F_NO_PREALLOC` to guarantee atomicity? Needs research. + #[map] static CHAN_TO_UDP_44: HashMap = HashMap::with_max_entries(NUM_ENTRIES, 0); @@ -53,6 +53,18 @@ static CHAN_TO_UDP_66: HashMap = #[map] static UDP_TO_CHAN_66: HashMap = HashMap::with_max_entries(NUM_ENTRIES, 0); +#[map] +static CHAN_TO_UDP_46: HashMap = + HashMap::with_max_entries(NUM_ENTRIES, 0); +#[map] +static UDP_TO_CHAN_46: HashMap = + HashMap::with_max_entries(NUM_ENTRIES, 0); +#[map] +static CHAN_TO_UDP_64: HashMap = + HashMap::with_max_entries(NUM_ENTRIES, 0); +#[map] +static UDP_TO_CHAN_64: HashMap = + HashMap::with_max_entries(NUM_ENTRIES, 0); #[xdp] pub fn handle_turn(ctx: XdpContext) -> u32 { @@ -68,7 +80,8 @@ pub fn handle_turn(ctx: XdpContext) -> u32 { | Error::XdpLoadBytesFailed | Error::PacketTooShort | Error::NoMacAddress - | Error::NoChannelBinding => { + | Error::UnsupportedChannel(_) + | Error::NoEntry(_) => { debug!(&ctx, "Passing packet to userspace: {}", e); xdp_action::XDP_PASS @@ -147,10 +160,16 @@ fn try_handle_ipv4_channel_data_to_udp( ) -> Result<(), Error> { let cd = ChannelData::parse(ctx, Ipv4Hdr::LEN)?; + let key = ClientAndChannelV4::new(ipv4.src(), udp.src(), cd.number()); + // SAFETY: ??? - let port_and_peer = - unsafe { CHAN_TO_UDP_44.get(&ClientAndChannelV4::new(ipv4.src(), udp.src(), cd.number())) } - .ok_or(Error::NoChannelBinding)?; + let port_and_peer = unsafe { CHAN_TO_UDP_44.get(&key) }.ok_or_else(|| { + if unsafe { CHAN_TO_UDP_46.get(&key) }.is_some() { + return Error::UnsupportedChannel(UnsupportedChannel::ChanToUdp46); + } + + Error::NoEntry(SupportedChannel::ChanToUdp44) + })?; let new_src = ipv4.dst(); // The IP we received the packet on will be the new source IP. let new_dst = port_and_peer.peer_ip(); @@ -182,9 +201,15 @@ fn try_handle_ipv4_udp_to_channel_data( ipv4: Ip4, udp: Udp, ) -> Result<(), Error> { - let client_and_channel = - unsafe { UDP_TO_CHAN_44.get(&PortAndPeerV4::new(ipv4.src(), udp.dst(), udp.src())) } - .ok_or(Error::NoChannelBinding)?; + let key = PortAndPeerV4::new(ipv4.src(), udp.dst(), udp.src()); + + let client_and_channel = unsafe { UDP_TO_CHAN_44.get(&key) }.ok_or_else(|| { + if unsafe { UDP_TO_CHAN_46.get(&key) }.is_some() { + return Error::UnsupportedChannel(UnsupportedChannel::UdpToChan46); + } + + Error::NoEntry(SupportedChannel::UdpToChan44) + })?; let new_src = ipv4.dst(); // The IP we received the packet on will be the new source IP. let new_dst = client_and_channel.client_ip(); @@ -266,9 +291,15 @@ fn try_handle_ipv6_udp_to_channel_data( ipv6: Ip6, udp: Udp, ) -> Result<(), Error> { - let client_and_channel = - unsafe { UDP_TO_CHAN_66.get(&PortAndPeerV6::new(ipv6.src(), udp.dst(), udp.src())) } - .ok_or(Error::NoChannelBinding)?; + let key = PortAndPeerV6::new(ipv6.src(), udp.dst(), udp.src()); + + let client_and_channel = unsafe { UDP_TO_CHAN_66.get(&key) }.ok_or_else(|| { + if unsafe { UDP_TO_CHAN_64.get(&key) }.is_some() { + return Error::UnsupportedChannel(UnsupportedChannel::UdpToChan64); + } + + Error::NoEntry(SupportedChannel::UdpToChan66) + })?; let new_src = ipv6.dst(); // The IP we received the packet on will be the new source IP. let new_dst = client_and_channel.client_ip(); @@ -311,10 +342,16 @@ fn try_handle_ipv6_channel_data_to_udp( ) -> Result<(), Error> { let cd = ChannelData::parse(ctx, Ipv6Hdr::LEN)?; + let key = ClientAndChannelV6::new(ipv6.src(), udp.src(), cd.number()); + // SAFETY: ??? - let port_and_peer = - unsafe { CHAN_TO_UDP_66.get(&ClientAndChannelV6::new(ipv6.src(), udp.src(), cd.number())) } - .ok_or(Error::NoChannelBinding)?; + let port_and_peer = unsafe { CHAN_TO_UDP_66.get(&key) }.ok_or_else(|| { + if unsafe { CHAN_TO_UDP_64.get(&key) }.is_some() { + return Error::UnsupportedChannel(UnsupportedChannel::ChanToUdp64); + } + + Error::NoEntry(SupportedChannel::ChanToUdp66) + })?; let new_src = ipv6.dst(); // The IP we received the packet on will be the new source IP. let new_dst = port_and_peer.peer_ip(); diff --git a/rust/relay/server/src/ebpf/linux.rs b/rust/relay/server/src/ebpf/linux.rs index 218723bce..3c8ddd6ab 100644 --- a/rust/relay/server/src/ebpf/linux.rs +++ b/rust/relay/server/src/ebpf/linux.rs @@ -135,8 +135,25 @@ impl Program { self.udp_to_chan_66_map_mut()? .insert(port_and_peer, client_and_channel, 0)?; } - (SocketAddr::V4(_), SocketAddr::V6(_)) | (SocketAddr::V6(_), SocketAddr::V4(_)) => { - // Relaying between IPv4 and IPv6 is not supported in the eBPF kernel. + (SocketAddr::V4(client), SocketAddr::V6(peer)) => { + let client_and_channel = + ClientAndChannelV4::from_socket(client, channel_number.value()); + let port_and_peer = PortAndPeerV6::from_socket(peer, allocation_port.value()); + + self.chan_to_udp_46_map_mut()? + .insert(client_and_channel, port_and_peer, 0)?; + self.udp_to_chan_64_map_mut()? + .insert(port_and_peer, client_and_channel, 0)?; + } + (SocketAddr::V6(client), SocketAddr::V4(peer)) => { + let client_and_channel = + ClientAndChannelV6::from_socket(client, channel_number.value()); + let port_and_peer = PortAndPeerV4::from_socket(peer, allocation_port.value()); + + self.chan_to_udp_64_map_mut()? + .insert(client_and_channel, port_and_peer, 0)?; + self.udp_to_chan_46_map_mut()? + .insert(port_and_peer, client_and_channel, 0)?; } } @@ -170,8 +187,21 @@ impl Program { self.chan_to_udp_66_map_mut()?.remove(&client_and_channel)?; self.udp_to_chan_66_map_mut()?.remove(&port_and_peer)?; } - (SocketAddr::V4(_), SocketAddr::V6(_)) | (SocketAddr::V6(_), SocketAddr::V4(_)) => { - // Relaying between IPv4 and IPv6 is not supported in the eBPF kernel. + (SocketAddr::V4(client), SocketAddr::V6(peer)) => { + let client_and_channel = + ClientAndChannelV4::from_socket(client, channel_number.value()); + let port_and_peer = PortAndPeerV6::from_socket(peer, allocation_port.value()); + + self.chan_to_udp_46_map_mut()?.remove(&client_and_channel)?; + self.udp_to_chan_64_map_mut()?.remove(&port_and_peer)?; + } + (SocketAddr::V6(client), SocketAddr::V4(peer)) => { + let client_and_channel = + ClientAndChannelV6::from_socket(client, channel_number.value()); + let port_and_peer = PortAndPeerV4::from_socket(peer, allocation_port.value()); + + self.chan_to_udp_64_map_mut()?.remove(&client_and_channel)?; + self.udp_to_chan_46_map_mut()?.remove(&port_and_peer)?; } } @@ -208,6 +238,30 @@ impl Program { self.hash_map_mut("UDP_TO_CHAN_66") } + fn chan_to_udp_46_map_mut( + &mut self, + ) -> Result> { + self.hash_map_mut("CHAN_TO_UDP_46") + } + + fn udp_to_chan_46_map_mut( + &mut self, + ) -> Result> { + self.hash_map_mut("UDP_TO_CHAN_46") + } + + fn chan_to_udp_64_map_mut( + &mut self, + ) -> Result> { + self.hash_map_mut("CHAN_TO_UDP_64") + } + + fn udp_to_chan_64_map_mut( + &mut self, + ) -> Result> { + self.hash_map_mut("UDP_TO_CHAN_64") + } + fn config_array_mut(&mut self) -> Result> { self.array_mut("CONFIG") }