netstack3_ip/
ipv6.rs

1// Copyright 2019 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//! IPv6 specific functionality.
6
7use netstack3_base::{AnyDevice, DeviceIdContext, FrameDestination};
8use packet_formats::ipv6::ext_hdrs::{
9    DestinationOptionData, ExtensionHeaderOption, FragmentData, HopByHopOptionData,
10    Ipv6ExtensionHeaderData,
11};
12use packet_formats::ipv6::Ipv6Packet;
13use zerocopy::SplitByteSlice;
14
15/// What to do with an IPv6 packet after parsing an extension header.
16#[derive(Debug, PartialEq, Eq)]
17pub(crate) enum Ipv6PacketAction {
18    /// Discard the packet.
19    _Discard,
20
21    /// Continue processing the next extension header (if any are
22    /// available and the processing node is the destination node)
23    /// or continue processing the packet (if the extension headers
24    /// have been exhausted or the processing node is not the
25    /// destination node).
26    Continue,
27
28    /// Stop processing extension headers and consider the
29    /// packet fragmented. The node must attempt to handle
30    /// the fragmented packet (attempt reassembly).
31    ProcessFragment,
32}
33
34/// Handle IPv6 extension headers.
35///
36/// What this function does depends on whether or not the `at_destination` flag
37/// is set. If it is `true`, then we will attempt to process all the extension
38/// headers in `packet`. Otherwise, we will only attempt to process the
39/// hop-by-hop extension header (which MUST be the first extension header if
40/// present) as per RFC 8200 section 4.
41pub(crate) fn handle_extension_headers<CC: DeviceIdContext<AnyDevice>, B: SplitByteSlice>(
42    core_ctx: &mut CC,
43    device: &CC::DeviceId,
44    frame_dst: Option<FrameDestination>,
45    packet: &Ipv6Packet<B>,
46    at_destination: bool,
47) -> Ipv6PacketAction {
48    // The next action we need to do after processing an extension header.
49    //
50    // Initialized to `Ipv6PacketAction::Continue` so we start off processing
51    // extension headers.
52    let mut action = Ipv6PacketAction::Continue;
53    let mut iter = packet.iter_extension_hdrs();
54
55    if at_destination {
56        // Keep looping while we are okay to just continue parsing extension
57        // headers.
58        while action == Ipv6PacketAction::Continue {
59            let ext_hdr = match iter.next() {
60                None => break,
61                Some(x) => x,
62            };
63
64            match ext_hdr.data() {
65                Ipv6ExtensionHeaderData::HopByHopOptions { options } => {
66                    action = handle_hop_by_hop_options_ext_hdr(
67                        core_ctx,
68                        device,
69                        frame_dst,
70                        packet,
71                        options.iter(),
72                    );
73                }
74                Ipv6ExtensionHeaderData::Fragment { fragment_data } => {
75                    action =
76                        handle_fragment_ext_hdr(core_ctx, device, frame_dst, packet, fragment_data);
77                }
78                Ipv6ExtensionHeaderData::DestinationOptions { options } => {
79                    action = handle_destination_options_ext_hdr(
80                        core_ctx,
81                        device,
82                        frame_dst,
83                        packet,
84                        options.iter(),
85                    );
86                }
87            }
88        }
89    } else {
90        // Packet is not yet at the destination, so only process the hop-by-hop
91        // options extension header (which MUST be the first extension header if
92        // it is present) as per RFC 8200 section 4.
93        if let Some(ext_hdr) = iter.next() {
94            if let Ipv6ExtensionHeaderData::HopByHopOptions { options } = ext_hdr.data() {
95                action = handle_hop_by_hop_options_ext_hdr(
96                    core_ctx,
97                    device,
98                    frame_dst,
99                    packet,
100                    options.iter(),
101                );
102            }
103        }
104    }
105
106    action
107}
108
109/// Handles a Hop By Hop extension header for a `packet`.
110// For now, we do not support any options. If parsing succeeds we are guaranteed
111// that the options present are safely skippable. If they aren't safely
112// skippable, we must have resulted in a parsing error when parsing the packet,
113// and so this function will never be called.
114fn handle_hop_by_hop_options_ext_hdr<
115    'a,
116    CC: DeviceIdContext<AnyDevice>,
117    B: SplitByteSlice,
118    I: Iterator<Item = ExtensionHeaderOption<HopByHopOptionData<'a>>>,
119>(
120    _bindings_ctx: &mut CC,
121    _device: &CC::DeviceId,
122    _frame_dst: Option<FrameDestination>,
123    _packet: &Ipv6Packet<B>,
124    options: I,
125) -> Ipv6PacketAction {
126    for option in options {
127        match option.data {
128            // Safely skip and continue, as we know that if we parsed an
129            // unrecognized option, the option's action was set to skip and
130            // continue.
131            HopByHopOptionData::Unrecognized { .. } => {}
132            // Also skip RouterAlert because router part of MLD is not
133            // implemented.
134            HopByHopOptionData::RouterAlert { .. } => {}
135        }
136    }
137
138    Ipv6PacketAction::Continue
139}
140
141/// Handles a fragment extension header for a `packet`.
142fn handle_fragment_ext_hdr<'a, CC: DeviceIdContext<AnyDevice>, B: SplitByteSlice>(
143    _bindings_ctx: &mut CC,
144    _device: &CC::DeviceId,
145    _frame_dst: Option<FrameDestination>,
146    _packet: &Ipv6Packet<B>,
147    _fragment_data: &FragmentData<'a>,
148) -> Ipv6PacketAction {
149    Ipv6PacketAction::ProcessFragment
150}
151
152/// Handles a destination extension header for a `packet`.
153// For now, we do not support any options. If parsing succeeds we are guaranteed
154// that the options present are safely skippable. If they aren't safely
155// skippable, we must have resulted in a parsing error when parsing the packet,
156// and so this function will never be called.
157fn handle_destination_options_ext_hdr<
158    'a,
159    CC: DeviceIdContext<AnyDevice>,
160    B: SplitByteSlice,
161    I: Iterator<Item = ExtensionHeaderOption<DestinationOptionData<'a>>>,
162>(
163    _bindings_ctx: &mut CC,
164    _device: &CC::DeviceId,
165    _frame_dst: Option<FrameDestination>,
166    _packet: &Ipv6Packet<B>,
167    options: I,
168) -> Ipv6PacketAction {
169    for option in options {
170        match option.data {
171            // Safely skip and continue, as we know that if we parsed an
172            // unrecognized option, the option's action was set to skip and
173            // continue.
174            DestinationOptionData::Unrecognized { .. } => {}
175        }
176    }
177
178    Ipv6PacketAction::Continue
179}
180
181#[cfg(test)]
182mod tests {
183    use alloc::vec;
184
185    use netstack3_base::testutil::{FakeDeviceId, TEST_ADDRS_V6};
186    use packet::serialize::{Buf, Serializer};
187    use packet::ParseBuffer;
188    use packet_formats::ip::IpProto;
189    use packet_formats::ipv6::Ipv6PacketBuilder;
190
191    use super::*;
192
193    type FakeCoreCtx = netstack3_base::testutil::FakeCoreCtx<(), (), FakeDeviceId>;
194
195    #[test]
196    fn test_no_extension_headers() {
197        // Test that if we have no extension headers, we continue.
198        let mut core_ctx = FakeCoreCtx::default();
199        let builder = Ipv6PacketBuilder::new(
200            TEST_ADDRS_V6.remote_ip,
201            TEST_ADDRS_V6.local_ip,
202            10,
203            IpProto::Tcp.into(),
204        );
205        let frame_dst = FrameDestination::Individual { local: true };
206        let mut buffer =
207            Buf::new(vec![1, 2, 3, 4, 5], ..).encapsulate(builder).serialize_vec_outer().unwrap();
208        let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
209
210        assert_eq!(
211            handle_extension_headers(&mut core_ctx, &FakeDeviceId, Some(frame_dst), &packet, false),
212            Ipv6PacketAction::Continue
213        );
214    }
215}