Skip to main content

netstack3_core/
transport.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! The transport layer.
6//!
7//! # Listeners and connections
8//!
9//! Some transport layer protocols (notably TCP and UDP) follow a common pattern
10//! with respect to registering listeners and connections. There are some
11//! subtleties here that are worth pointing out.
12//!
13//! ## Connections
14//!
15//! A connection has simpler semantics than a listener. It is bound to a single
16//! local address and port and a single remote address and port. By virtue of
17//! being bound to a local address, it is also bound to a local interface. This
18//! means that, regardless of the entries in the forwarding table, all traffic
19//! on that connection will always egress over the same interface. [^1] This
20//! also means that, if the interface's address changes, any connections bound
21//! to it are severed.
22//!
23//! ## Listeners
24//!
25//! A listener, on the other hand, can be bound to any number of local addresses
26//! (although it is still always bound to a particular port). From the
27//! perspective of this crate, there are two ways of registering a listener:
28//! - By specifying one or more local addresses, the listener will be bound to
29//!   each of those local addresses.
30//! - By specifying zero local addresses, the listener will be bound to all
31//!   addresses. These are referred to in our documentation as "wildcard
32//!   listeners".
33//!
34//! The algorithm for figuring out what listener to deliver a packet to is as
35//! follows: If there is any listener bound to the specific local address and
36//! port addressed in the packet, deliver the packet to that listener.
37//! Otherwise, if there is a wildcard listener bound the port addressed in the
38//! packet, deliver the packet to that listener. This implies that if a listener
39//! is removed which was bound to a particular local address, it can "uncover" a
40//! wildcard listener bound to the same port, allowing traffic which would
41//! previously have been delivered to the normal listener to now be delivered to
42//! the wildcard listener.
43//!
44//! If desired, clients of this crate can implement a different mechanism for
45//! registering listeners on all local addresses - enumerate every local
46//! address, and then specify all of the local addresses when registering the
47//! listener. This approach will not support shadowing, as a different listener
48//! binding to the same port will explicitly conflict with the existing
49//! listener, and will thus be rejected. In other words, from the perspective of
50//! this crate's API, such listeners will appear like normal listeners that just
51//! happen to bind all of the addresses, rather than appearing like wildcard
52//! listeners.
53//!
54//! [^1]: It is an open design question as to whether incoming traffic on the
55//!       connection will be accepted from a different interface. This is part
56//!       of the "weak host model" vs "strong host model" discussion.
57
58mod integration;
59
60use derivative::Derivative;
61use net_types::ip::{Ip, Ipv4, Ipv6};
62use netstack3_base::socket::SocketInfo;
63use netstack3_base::{
64    ChecksumOffloadResult, CoreTxMetadataContext, HandleableTimer, TimerHandler, TxMetadata,
65};
66use netstack3_datagram as datagram;
67use netstack3_device::WeakDeviceId;
68use netstack3_icmp_echo::{IcmpSocketTxMetadata, IcmpSockets};
69use netstack3_tcp::{
70    self as tcp, TcpCountersWithSocket, TcpCountersWithoutSocket, TcpSocketTxMetadata, TcpState,
71    TcpTimerId,
72};
73use netstack3_udp::{
74    UdpCountersWithSocket, UdpCountersWithoutSocket, UdpSocketTxMetadata, UdpState,
75};
76
77use crate::{BindingsContext, BindingsTypes, CoreCtx, IpExt};
78
79/// A builder for transport layer state.
80#[derive(Default, Clone)]
81pub struct TransportStateBuilder;
82
83impl TransportStateBuilder {
84    pub(crate) fn build_with_ctx<BC: BindingsContext>(
85        self,
86        bindings_ctx: &mut BC,
87    ) -> TransportLayerState<BC> {
88        let now = bindings_ctx.now();
89        let mut rng = bindings_ctx.rng();
90        TransportLayerState {
91            udpv4: Default::default(),
92            udpv6: Default::default(),
93            tcpv4: TcpState::new(now, &mut rng),
94            tcpv6: TcpState::new(now, &mut rng),
95            icmp_echo_v4: Default::default(),
96            icmp_echo_v6: Default::default(),
97        }
98    }
99}
100
101/// The state associated with the transport layer.
102pub struct TransportLayerState<BT: BindingsTypes> {
103    udpv4: UdpState<Ipv4, WeakDeviceId<BT>, BT>,
104    udpv6: UdpState<Ipv6, WeakDeviceId<BT>, BT>,
105    tcpv4: TcpState<Ipv4, WeakDeviceId<BT>, BT>,
106    tcpv6: TcpState<Ipv6, WeakDeviceId<BT>, BT>,
107    icmp_echo_v4: IcmpSockets<Ipv4, WeakDeviceId<BT>, BT>,
108    icmp_echo_v6: IcmpSockets<Ipv6, WeakDeviceId<BT>, BT>,
109}
110
111impl<BT: BindingsTypes> TransportLayerState<BT> {
112    fn tcp_state<I: tcp::DualStackIpExt>(&self) -> &TcpState<I, WeakDeviceId<BT>, BT> {
113        I::map_ip((), |()| &self.tcpv4, |()| &self.tcpv6)
114    }
115
116    fn udp_state<I: datagram::IpExt>(&self) -> &UdpState<I, WeakDeviceId<BT>, BT> {
117        I::map_ip((), |()| &self.udpv4, |()| &self.udpv6)
118    }
119
120    pub(crate) fn icmp_echo_state<I: datagram::IpExt>(
121        &self,
122    ) -> &IcmpSockets<I, WeakDeviceId<BT>, BT> {
123        I::map_ip((), |()| &self.icmp_echo_v4, |()| &self.icmp_echo_v6)
124    }
125
126    pub(crate) fn udp_counters_with_socket<I: Ip>(&self) -> &UdpCountersWithSocket<I> {
127        I::map_ip((), |()| &self.udpv4.counters_with_socket, |()| &self.udpv6.counters_with_socket)
128    }
129
130    pub(crate) fn udp_counters_without_socket<I: Ip>(&self) -> &UdpCountersWithoutSocket<I> {
131        I::map_ip(
132            (),
133            |()| &self.udpv4.counters_without_socket,
134            |()| &self.udpv6.counters_without_socket,
135        )
136    }
137
138    pub(crate) fn tcp_counters_with_socket<I: Ip>(&self) -> &TcpCountersWithSocket<I> {
139        I::map_ip((), |()| &self.tcpv4.counters_with_socket, |()| &self.tcpv6.counters_with_socket)
140    }
141
142    pub(crate) fn tcp_counters_without_socket<I: Ip>(&self) -> &TcpCountersWithoutSocket<I> {
143        I::map_ip(
144            (),
145            |()| &self.tcpv4.counters_without_socket,
146            |()| &self.tcpv6.counters_without_socket,
147        )
148    }
149}
150
151/// The identifier for timer events in the transport layer.
152#[derive(Derivative)]
153#[derivative(
154    Clone(bound = ""),
155    Eq(bound = ""),
156    PartialEq(bound = ""),
157    Hash(bound = ""),
158    Debug(bound = "")
159)]
160pub(crate) enum TransportLayerTimerId<BT: BindingsTypes> {
161    Tcp(TcpTimerId<WeakDeviceId<BT>, BT>),
162}
163
164impl<CC, BT> HandleableTimer<CC, BT> for TransportLayerTimerId<BT>
165where
166    BT: BindingsTypes,
167    CC: TimerHandler<BT, TcpTimerId<WeakDeviceId<BT>, BT>>,
168{
169    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BT, timer: BT::UniqueTimerId) {
170        match self {
171            TransportLayerTimerId::Tcp(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
172        }
173    }
174}
175
176impl<BT: BindingsTypes> From<TcpTimerId<WeakDeviceId<BT>, BT>> for TransportLayerTimerId<BT> {
177    fn from(id: TcpTimerId<WeakDeviceId<BT>, BT>) -> Self {
178        TransportLayerTimerId::Tcp(id)
179    }
180}
181
182/// The frame metadata type for frames traversing the stack.
183#[derive(Derivative)]
184#[derivative(Debug = "transparent", Debug(bound = ""), Default(bound = ""))]
185#[cfg_attr(any(test, feature = "testutils"), derivative(PartialEq(bound = "")))]
186pub struct CoreTxMetadata<BT: BindingsTypes>(TxMetadataInner<BT>);
187
188/// The internal metadata type.
189///
190/// This is split from [`TxMetadata`] so the outer type is opaque to bindings.
191#[derive(Derivative)]
192#[derivative(Debug(bound = ""), Default(bound = ""))]
193#[cfg_attr(any(test, feature = "testutils"), derivative(PartialEq(bound = "")))]
194enum TxMetadataInner<BT: BindingsTypes> {
195    #[derivative(Default)]
196    None,
197    #[derivative(Debug = "transparent")]
198    Udpv4(UdpSocketTxMetadata<Ipv4, WeakDeviceId<BT>, BT>),
199    #[derivative(Debug = "transparent")]
200    Udpv6(UdpSocketTxMetadata<Ipv6, WeakDeviceId<BT>, BT>),
201    #[derivative(Debug = "transparent")]
202    Icmpv4(IcmpSocketTxMetadata<Ipv4, WeakDeviceId<BT>, BT>),
203    #[derivative(Debug = "transparent")]
204    Icmpv6(IcmpSocketTxMetadata<Ipv6, WeakDeviceId<BT>, BT>),
205    #[derivative(Debug = "transparent")]
206    Tcpv4(TcpSocketTxMetadata<Ipv4, WeakDeviceId<BT>, BT>),
207    #[derivative(Debug = "transparent")]
208    Tcpv6(TcpSocketTxMetadata<Ipv6, WeakDeviceId<BT>, BT>),
209}
210
211impl<I: IpExt, L, BT: BindingsTypes>
212    CoreTxMetadataContext<UdpSocketTxMetadata<I, WeakDeviceId<BT>, BT>, BT> for CoreCtx<'_, BT, L>
213{
214    fn convert_tx_meta(
215        &self,
216        tx_meta: UdpSocketTxMetadata<I, WeakDeviceId<BT>, BT>,
217    ) -> CoreTxMetadata<BT> {
218        CoreTxMetadata(I::map_ip_in(tx_meta, TxMetadataInner::Udpv4, TxMetadataInner::Udpv6))
219    }
220}
221
222impl<I: IpExt, L, BT: BindingsTypes>
223    CoreTxMetadataContext<IcmpSocketTxMetadata<I, WeakDeviceId<BT>, BT>, BT>
224    for CoreCtx<'_, BT, L>
225{
226    fn convert_tx_meta(
227        &self,
228        tx_meta: IcmpSocketTxMetadata<I, WeakDeviceId<BT>, BT>,
229    ) -> CoreTxMetadata<BT> {
230        CoreTxMetadata(I::map_ip_in(tx_meta, TxMetadataInner::Icmpv4, TxMetadataInner::Icmpv6))
231    }
232}
233
234impl<I: IpExt, L, BT: BindingsTypes>
235    CoreTxMetadataContext<TcpSocketTxMetadata<I, WeakDeviceId<BT>, BT>, BT> for CoreCtx<'_, BT, L>
236{
237    fn convert_tx_meta(
238        &self,
239        tx_meta: TcpSocketTxMetadata<I, WeakDeviceId<BT>, BT>,
240    ) -> CoreTxMetadata<BT> {
241        CoreTxMetadata(I::map_ip_in(tx_meta, TxMetadataInner::Tcpv4, TxMetadataInner::Tcpv6))
242    }
243}
244
245impl<BT: BindingsTypes> TxMetadata for CoreTxMetadata<BT> {
246    fn socket_info(&self) -> Option<SocketInfo> {
247        let CoreTxMetadata(inner) = self;
248        match inner {
249            // TODO(https://fxbug.dev/417224088): Handle Raw and Packet sockets.
250            TxMetadataInner::None => None,
251            TxMetadataInner::Tcpv4(tx_metadata) => {
252                tx_metadata.socket().upgrade().map(|s| s.socket_info())
253            }
254            TxMetadataInner::Tcpv6(tx_metadata) => {
255                tx_metadata.socket().upgrade().map(|s| s.socket_info())
256            }
257            TxMetadataInner::Udpv4(tx_metadata) => {
258                tx_metadata.socket().upgrade().map(|s| s.socket_info())
259            }
260            TxMetadataInner::Udpv6(tx_metadata) => {
261                tx_metadata.socket().upgrade().map(|s| s.socket_info())
262            }
263            TxMetadataInner::Icmpv4(tx_metadata) => {
264                tx_metadata.socket().upgrade().map(|s| s.socket_info())
265            }
266            TxMetadataInner::Icmpv6(tx_metadata) => {
267                tx_metadata.socket().upgrade().map(|s| s.socket_info())
268            }
269        }
270    }
271
272    fn checksum_offload_result(&self) -> Option<ChecksumOffloadResult> {
273        let CoreTxMetadata(inner) = self;
274        match inner {
275            TxMetadataInner::None => None,
276            TxMetadataInner::Tcpv4(tx_metadata) => tx_metadata.checksum_offload_result(),
277            TxMetadataInner::Tcpv6(tx_metadata) => tx_metadata.checksum_offload_result(),
278            TxMetadataInner::Udpv4(tx_metadata) => tx_metadata.checksum_offload_result(),
279            TxMetadataInner::Udpv6(tx_metadata) => tx_metadata.checksum_offload_result(),
280            TxMetadataInner::Icmpv4(tx_metadata) => tx_metadata.checksum_offload_result(),
281            TxMetadataInner::Icmpv6(tx_metadata) => tx_metadata.checksum_offload_result(),
282        }
283    }
284
285    fn set_checksum_offload_result(&mut self, result: Option<ChecksumOffloadResult>) {
286        let CoreTxMetadata(inner) = self;
287        match inner {
288            TxMetadataInner::None => {}
289            TxMetadataInner::Tcpv4(tx_metadata) => tx_metadata.set_checksum_offload_result(result),
290            TxMetadataInner::Tcpv6(tx_metadata) => tx_metadata.set_checksum_offload_result(result),
291            TxMetadataInner::Udpv4(tx_metadata) => tx_metadata.set_checksum_offload_result(result),
292            TxMetadataInner::Udpv6(tx_metadata) => tx_metadata.set_checksum_offload_result(result),
293            TxMetadataInner::Icmpv4(tx_metadata) => tx_metadata.set_checksum_offload_result(result),
294            TxMetadataInner::Icmpv6(tx_metadata) => tx_metadata.set_checksum_offload_result(result),
295        }
296    }
297}