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.
45//! IPv6 specific functionality.
67use 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;
1415/// 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,
2021/// 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).
26Continue,
2728/// Stop processing extension headers and consider the
29 /// packet fragmented. The node must attempt to handle
30 /// the fragmented packet (attempt reassembly).
31ProcessFragment,
32}
3334/// 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.
52let mut action = Ipv6PacketAction::Continue;
53let mut iter = packet.iter_extension_hdrs();
5455if at_destination {
56// Keep looping while we are okay to just continue parsing extension
57 // headers.
58while action == Ipv6PacketAction::Continue {
59let ext_hdr = match iter.next() {
60None => break,
61Some(x) => x,
62 };
6364match 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.
93if let Some(ext_hdr) = iter.next() {
94if 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 }
105106 action
107}
108109/// 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 {
126for option in options {
127match 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.
131HopByHopOptionData::Unrecognized { .. } => {}
132// Also skip RouterAlert because router part of MLD is not
133 // implemented.
134HopByHopOptionData::RouterAlert { .. } => {}
135 }
136 }
137138 Ipv6PacketAction::Continue
139}
140141/// 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}
151152/// 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 {
169for option in options {
170match 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.
174DestinationOptionData::Unrecognized { .. } => {}
175 }
176 }
177178 Ipv6PacketAction::Continue
179}
180181#[cfg(test)]
182mod tests {
183use alloc::vec;
184185use netstack3_base::testutil::{FakeDeviceId, TEST_ADDRS_V6};
186use packet::serialize::{Buf, Serializer};
187use packet::ParseBuffer;
188use packet_formats::ip::IpProto;
189use packet_formats::ipv6::Ipv6PacketBuilder;
190191use super::*;
192193type FakeCoreCtx = netstack3_base::testutil::FakeCoreCtx<(), (), FakeDeviceId>;
194195#[test]
196fn test_no_extension_headers() {
197// Test that if we have no extension headers, we continue.
198let mut core_ctx = FakeCoreCtx::default();
199let builder = Ipv6PacketBuilder::new(
200 TEST_ADDRS_V6.remote_ip,
201 TEST_ADDRS_V6.local_ip,
20210,
203 IpProto::Tcp.into(),
204 );
205let frame_dst = FrameDestination::Individual { local: true };
206let mut buffer =
207 Buf::new(vec![1, 2, 3, 4, 5], ..).encapsulate(builder).serialize_vec_outer().unwrap();
208let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
209210assert_eq!(
211 handle_extension_headers(&mut core_ctx, &FakeDeviceId, Some(frame_dst), &packet, false),
212 Ipv6PacketAction::Continue
213 );
214 }
215}