blob: 323c838cc5355d770a7b97a932246596ab0ca9e7 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The User Datagram Protocol (UDP).
use std::hash::Hash;
use std::num::NonZeroU16;
use packet::{BufferMut, BufferSerializer, Serializer};
use zerocopy::ByteSlice;
use crate::ip::{Ip, IpAddr, IpProto, Ipv4Addr, Ipv6Addr};
use crate::transport::{ConnAddrMap, ListenerAddrMap};
use crate::wire::udp::{UdpPacket, UdpPacketBuilder, UdpParseArgs};
use crate::{Context, EventDispatcher, StackState};
/// The state associated with the UDP protocol.
pub struct UdpState<D: EventDispatcher> {
ipv4: UdpStateInner<D, Ipv4Addr>,
ipv6: UdpStateInner<D, Ipv6Addr>,
}
impl<D: EventDispatcher> Default for UdpState<D> {
fn default() -> UdpState<D> {
UdpState {
ipv4: UdpStateInner {
conns: ConnAddrMap::default(),
listeners: ListenerAddrMap::default(),
wildcard_listeners: ListenerAddrMap::default(),
},
ipv6: UdpStateInner {
conns: ConnAddrMap::default(),
listeners: ListenerAddrMap::default(),
wildcard_listeners: ListenerAddrMap::default(),
},
}
}
}
struct UdpStateInner<D: EventDispatcher, A: IpAddr> {
conns: ConnAddrMap<D::UdpConn, Conn<A>>,
listeners: ListenerAddrMap<D::UdpListener, Listener<A>>,
wildcard_listeners: ListenerAddrMap<D::UdpListener, NonZeroU16>,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct Conn<A: IpAddr> {
local_addr: A,
local_port: NonZeroU16,
remote_addr: A,
remote_port: NonZeroU16,
}
impl<A: IpAddr> Conn<A> {
/// Construct a `Conn` from an incoming packet.
///
/// The source is treated as the remote address/port, and the destination is
/// treated as the local address/port. If there is no source port, then the
/// packet cannot correspond to a connection, and so None is returned.
fn from_packet<B: ByteSlice>(src_ip: A, dst_ip: A, packet: &UdpPacket<B>) -> Option<Conn<A>> {
Some(Conn {
local_addr: dst_ip,
local_port: packet.dst_port(),
remote_addr: src_ip,
remote_port: packet.src_port()?,
})
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct Listener<A: IpAddr> {
addr: A,
port: NonZeroU16,
}
impl<A: IpAddr> Listener<A> {
/// Construct a `Listener` from an incoming packet.
///
/// The destination is treated as the local address/port.
fn from_packet<B: ByteSlice>(dst_ip: A, packet: &UdpPacket<B>) -> Listener<A> {
Listener {
addr: dst_ip,
port: packet.dst_port(),
}
}
}
/// An event dispatcher for the UDP layer.
///
/// See the `EventDispatcher` trait in the crate root for more details.
pub trait UdpEventDispatcher {
/// A key identifying a UDP connection.
///
/// A `UdpConn` is an opaque identifier which uniquely identifies a
/// particular UDP connection. When registering a new connection, a new
/// `UdpConn` must be provided. When the stack invokes methods on this trait
/// related to a connection, the corresponding `UdpConn` will be provided.
type UdpConn: Clone + Eq + Hash;
/// A key identifying a UDP listener.
///
/// A `UdpListener` is an opaque identifier which uniquely identifies a
/// particular UDP listener. When registering a new listener, a new
/// `UdpListener` must be provided. When the stack invokes methods on this
/// trait related to a listener, the corresponding `UdpListener` will be
/// provided.
type UdpListener: Clone + Eq + Hash;
/// Receive a UDP packet for a connection.
fn receive_udp_from_conn(&mut self, conn: &Self::UdpConn, body: &[u8]) {
log_unimplemented!(
(),
"UdpEventDispatcher::receive_udp_from_conn: not implemented"
);
}
/// Receive a UDP packet for a listener.
fn receive_udp_from_listen<A: IpAddr>(
&mut self, listener: &Self::UdpListener, src_ip: A, dst_ip: A,
src_port: Option<NonZeroU16>, body: &[u8],
) {
log_unimplemented!(
(),
"UdpEventDispatcher::receive_udp_from_listen: not implemented"
);
}
}
/// Receive a UDP packet in an IP packet.
pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: BufferMut>(
ctx: &mut Context<D>, src_ip: A, dst_ip: A, mut buffer: B,
) {
println!("received udp packet: {:x?}", buffer.as_mut());
let packet = if let Ok(packet) =
buffer.parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(src_ip, dst_ip))
{
packet
} else {
// TODO(joshlf): Do something with ICMP here?
return;
};
let (state, dispatcher) = ctx.state_and_dispatcher();
let state = get_inner_state(state);
if let Some(conn) =
Conn::from_packet(src_ip, dst_ip, &packet).and_then(|conn| state.conns.get_by_addr(&conn))
{
dispatcher.receive_udp_from_conn(conn, packet.body());
} else if let Some(listener) = state
.listeners
.get_by_addr(&Listener::from_packet(dst_ip, &packet))
.or_else(|| state.wildcard_listeners.get_by_addr(&packet.dst_port()))
{
dispatcher.receive_udp_from_listen(
listener,
src_ip,
dst_ip,
packet.src_port(),
packet.body(),
);
}
}
/// Send a UDP packet on an existing connection.
///
/// # Panics
///
/// `send_udp_conn` panics if `conn` is not associated with a connection for this IP version.
pub fn send_udp_conn<D: EventDispatcher, I: Ip, B: BufferMut>(
ctx: &mut Context<D>, conn: &D::UdpConn, body: B,
) {
let state = get_inner_state::<_, I::Addr>(ctx.state());
let Conn {
local_addr,
local_port,
remote_addr,
remote_port,
} = state
.conns
.get_by_conn(conn)
.expect("transport::udp::send_udp_conn: no such conn")
.clone();
crate::ip::send_ip_packet_from(
ctx,
local_addr,
remote_addr,
IpProto::Udp,
BufferSerializer::new_vec(body).encapsulate(UdpPacketBuilder::new(
local_addr,
remote_addr,
Some(local_port),
remote_port,
)),
);
}
/// Send a UDP packet on an existing listener.
///
/// `send_udp_listener` sends a UDP packet on an existing listener. The caller
/// must specify the local address in order to disambiguate in case the listener
/// is bound to multiple local addresses. If the listener is not bound to the
/// local address provided, `send_udp_listener` will fail.
///
/// # Panics
///
/// `send_udp_listener` panics if `listener` is not associated with a listener
/// for this IP version.
pub fn send_udp_listener<D: EventDispatcher, A: IpAddr, B: BufferMut>(
ctx: &mut Context<D>, listener: &D::UdpListener, local_addr: A, remote_addr: A,
remote_port: NonZeroU16, body: B,
) {
if !crate::ip::is_local_addr(ctx, local_addr) {
// TODO(joshlf): Return error.
panic!("transport::udp::send_udp::listener: invalid local addr");
}
let state = get_inner_state::<_, A>(ctx.state());
let local_port: Result<_, ()> = state
.listeners
.get_by_listener(listener)
.map(|addrs| {
// We found the listener. Make sure at least one of the addresses
// associated with it is the local_addr the caller passed. Return a
// result with Ok if one of the addresses matched, and Err
// otherwise.
addrs
.iter()
.find_map(|addr| {
if addr.addr == local_addr {
Some(addr.port)
} else {
None
}
})
.ok_or_else(|| unimplemented!())
})
.or_else(|| {
// We didn't find the listener in state.listeners. Maybe it's a
// wildcard listener. Wildcard listeners are only associated with
// ports, so if we find it, we can return Ok immediately to match
// the result that we produce if we find the listener in
// state.listeners. This is OK since we already check that
// local_addr is a local address in the if block above (we would do
// it here, but it results in conflicting lifetimes).
state
.wildcard_listeners
.get_by_listener(listener)
.map(|ports| Ok(ports[0]))
// We didn't find the listener in either map, so we panic.
})
.expect("transport::udp::send_udp_listener: no such listener");
// TODO(joshlf): Return an error rather than panicking.
let local_port = local_port.unwrap();
crate::ip::send_ip_packet_from(
ctx,
local_addr,
remote_addr,
IpProto::Udp,
BufferSerializer::new_vec(body).encapsulate(UdpPacketBuilder::new(
local_addr,
remote_addr,
Some(local_port),
remote_port,
)),
);
}
/// Create a UDP connection.
///
/// `connect_udp` binds `conn` as a connection to the remote address and port.
/// It is also bound to the local address and port, meaning that packets sent on
/// this connection will always come from that address and port. If `local_addr`
/// is `None`, then the local address will be chosen based on the route to the
/// remote address. If `local_port` is `None`, then one will be chosen from the
/// available local ports.
///
/// If both `local_addr` and `local_port` are specified, but conflict with an
/// existing connection or listener, `connect_udp` will fail. If one or both are
/// left unspecified, but there is still no way to satisfy the request (e.g.,
/// `local_addr` is specified, but there are no available local ports for that
/// address), `connect_udp` will fail. If there is no route to `remote_addr`,
/// `connect_udp` will fail.
///
/// # Panics
///
/// `connect_udp` panics if `conn` is already in use.
pub fn connect_udp<D: EventDispatcher, A: IpAddr>(
ctx: &mut Context<D>, conn: D::UdpConn, local_addr: Option<A>, local_port: Option<NonZeroU16>,
remote_addr: A, remote_port: NonZeroU16,
) {
let (ipv4_state, ipv6_state) = get_inner_states(ctx.state());
if ipv4_state.conns.get_by_conn(&conn).is_some()
|| ipv6_state.conns.get_by_conn(&conn).is_some()
{
panic!("transport::udp::connect_udp: conn already in use");
}
let default_local = if let Some(local) = crate::ip::local_address_for_remote(ctx, remote_addr) {
local
} else {
// TODO(joshlf): There's no route to the remote, so return an error.
return;
};
let local_addr = local_addr.unwrap_or(default_local);
if let Some(local_port) = local_port {
let c = Conn {
local_addr,
local_port,
remote_addr,
remote_port,
};
let listener = Listener {
addr: local_addr,
port: local_port,
};
let state = get_inner_state(ctx.state());
if state.conns.get_by_addr(&c).is_some() || state.listeners.get_by_addr(&listener).is_some()
{
// TODO(joshlf): Return error
return;
}
state.conns.insert(conn, c);
} else {
unimplemented!();
}
}
/// Listen on for incoming UDP packets.
///
/// `listen_udp` registers `listener` as a listener for incoming UDP packets on
/// the given `port`. If `addrs` is empty, the listener is a "wildcard
/// listener", and is bound to all local addresses. See the `transport` module
/// documentation for more details.
///
/// If `addrs` is not empty, and any of the addresses in `addrs` is already
/// bound on the given port (either by a listener or a connection), `listen_udp`
/// will fail. If `addrs` is empty, and a wildcard listener is already bound to
/// the given port, `listen_udp` will fail.
///
/// # Panics
///
/// `listen_udp` panics if `listener` is already in use.
pub fn listen_udp<D: EventDispatcher, A: IpAddr>(
ctx: &mut Context<D>, listener: D::UdpListener, addrs: Vec<A>, port: NonZeroU16,
) {
let (ipv4_state, ipv6_state) = get_inner_states(ctx.state());
if ipv4_state.listeners.get_by_listener(&listener).is_some()
|| ipv4_state
.wildcard_listeners
.get_by_listener(&listener)
.is_some()
|| ipv6_state.listeners.get_by_listener(&listener).is_some()
|| ipv6_state
.wildcard_listeners
.get_by_listener(&listener)
.is_some()
{
panic!("transport::udp::listen_udp: listener already in use");
}
let state = get_inner_state(ctx.state());
if addrs.is_empty() {
if state.wildcard_listeners.get_by_addr(&port).is_some() {
// TODO(joshlf): Return error
return;
}
// TODO(joshlf): Check for connections bound to this IP:port.
state.wildcard_listeners.insert(listener, vec![port]);
} else {
for addr in &addrs {
let listener = Listener { addr: *addr, port };
if state.listeners.get_by_addr(&listener).is_some() {
// TODO(joshlf): Return error
return;
}
}
state.listeners.insert(
listener,
addrs
.into_iter()
.map(|addr| Listener { addr, port })
.collect(),
);
}
}
fn get_inner_state<D: EventDispatcher, A: IpAddr>(
state: &mut StackState<D>,
) -> &mut UdpStateInner<D, A> {
specialize_ip_addr!(
fn get_inner_state<D>(state: &mut UdpState<D>) -> &mut UdpStateInner<D, Self>
where
D: EventDispatcher,
{
Ipv4Addr => { &mut state.ipv4 }
Ipv6Addr => { &mut state.ipv6 }
}
);
A::get_inner_state(&mut state.transport.udp)
}
fn get_inner_states<D: EventDispatcher>(
state: &mut StackState<D>,
) -> (
&mut UdpStateInner<D, Ipv4Addr>,
&mut UdpStateInner<D, Ipv6Addr>,
) {
let state = &mut state.transport.udp;
(&mut state.ipv4, &mut state.ipv6)
}