mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
chore(relay): log when encountering unsupported channel mappings (#8617)
Currently, the relays eBPF module only supports routing from IPv4 to IPv4 as well as IPv6 to IPv6. In general, TURN servers can also route from IPv4 to IPv6 and vice versa. Our userspace routing supports that but doing the same in the eBPF code is a bit more involved. We'd need to move around the headers a bit more (IPv4 and IPv6 headers are different in size), as well as configure the respective "source" address for each interface. Currently, we simply take the destination address of the incoming packet as the new source address. When routing across IP versions, that doesn't work. To gain some more insight into how often this happens, we add these additional maps and populate them. This allows us to emit a dedicated log message whenever we encounter a packet for such a mapping. First, we always do check for an entry in the maps that we can handle. If we can't we check the other map and special-case the error. Otherwise, we fall back to the previous "no entry" error. We shouldn't really see these "no entry" errors anymore now, unless someone starts probing our relays for active channels.
This commit is contained in:
@@ -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<NonZeroUsize> {
|
||||
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",
|
||||
|
||||
@@ -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<ClientAndChannelV4, PortAndPeerV4> =
|
||||
HashMap::with_max_entries(NUM_ENTRIES, 0);
|
||||
@@ -53,6 +53,18 @@ static CHAN_TO_UDP_66: HashMap<ClientAndChannelV6, PortAndPeerV6> =
|
||||
#[map]
|
||||
static UDP_TO_CHAN_66: HashMap<PortAndPeerV6, ClientAndChannelV6> =
|
||||
HashMap::with_max_entries(NUM_ENTRIES, 0);
|
||||
#[map]
|
||||
static CHAN_TO_UDP_46: HashMap<ClientAndChannelV4, PortAndPeerV6> =
|
||||
HashMap::with_max_entries(NUM_ENTRIES, 0);
|
||||
#[map]
|
||||
static UDP_TO_CHAN_46: HashMap<PortAndPeerV4, ClientAndChannelV6> =
|
||||
HashMap::with_max_entries(NUM_ENTRIES, 0);
|
||||
#[map]
|
||||
static CHAN_TO_UDP_64: HashMap<ClientAndChannelV6, PortAndPeerV4> =
|
||||
HashMap::with_max_entries(NUM_ENTRIES, 0);
|
||||
#[map]
|
||||
static UDP_TO_CHAN_64: HashMap<PortAndPeerV6, ClientAndChannelV4> =
|
||||
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();
|
||||
|
||||
@@ -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<HashMap<&mut MapData, ClientAndChannelV4, PortAndPeerV6>> {
|
||||
self.hash_map_mut("CHAN_TO_UDP_46")
|
||||
}
|
||||
|
||||
fn udp_to_chan_46_map_mut(
|
||||
&mut self,
|
||||
) -> Result<HashMap<&mut MapData, PortAndPeerV4, ClientAndChannelV6>> {
|
||||
self.hash_map_mut("UDP_TO_CHAN_46")
|
||||
}
|
||||
|
||||
fn chan_to_udp_64_map_mut(
|
||||
&mut self,
|
||||
) -> Result<HashMap<&mut MapData, ClientAndChannelV6, PortAndPeerV4>> {
|
||||
self.hash_map_mut("CHAN_TO_UDP_64")
|
||||
}
|
||||
|
||||
fn udp_to_chan_64_map_mut(
|
||||
&mut self,
|
||||
) -> Result<HashMap<&mut MapData, PortAndPeerV6, ClientAndChannelV4>> {
|
||||
self.hash_map_mut("UDP_TO_CHAN_64")
|
||||
}
|
||||
|
||||
fn config_array_mut(&mut self) -> Result<Array<&mut MapData, Config>> {
|
||||
self.array_mut("CONFIG")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user