blob: f4a0f3770fef203f9fb4ab07309232933441f838 [file] [log] [blame] [edit]
// Copyright 2019 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.
//! IPv6 specific functionality.
use core::iter::Iterator;
use packet_formats::ipv6::ext_hdrs::{
DestinationOptionData, ExtensionHeaderOption, FragmentData, HopByHopOptionData,
Ipv6ExtensionHeaderData, RoutingData,
};
use packet_formats::ipv6::Ipv6Packet;
use zerocopy::ByteSlice;
use crate::device::{DeviceId, FrameDestination};
use crate::{Context, EventDispatcher};
/// What to do with an IPv6 packet after parsing an extension header.
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum Ipv6PacketAction {
/// Discard the packet.
_Discard,
/// Continue processing the next extension header (if any are
/// available and the processing node is the destination node)
/// or continue processing the packet (if the extension headers
/// have been exhausted or the processing node is not the
/// destination node).
Continue,
/// Stop processing extension headers and consider the
/// packet fragmented. The node must attempt to handle
/// the fragmented packet (attempt reassembly).
ProcessFragment,
}
/// Handle IPv6 extension headers.
///
/// What this function does depends on whether or not the `at_destination` flag
/// is set. If it is `true`, then we will attempt to process all the extension
/// headers in `packet`. Otherwise, we will only attempt to process the hop-by-hop
/// extension header (which MUST be the first extension header if present) as
/// per RFC 8200 section 4.
pub(crate) fn handle_extension_headers<D: EventDispatcher, B: ByteSlice>(
ctx: &mut Context<D>,
device: DeviceId,
frame_dst: FrameDestination,
packet: &Ipv6Packet<B>,
at_destination: bool,
) -> Ipv6PacketAction {
// The next action we need to do after processing an extension header.
//
// Initialized to `Ipv6PacketAction::Continue` so we start off processing
// extension headers.
let mut action = Ipv6PacketAction::Continue;
let mut iter = packet.iter_extension_hdrs();
if at_destination {
// Keep looping while we are okay to just continue parsing extension headers.
while action == Ipv6PacketAction::Continue {
let ext_hdr = match iter.next() {
None => break,
Some(x) => x,
};
match ext_hdr.data() {
Ipv6ExtensionHeaderData::HopByHopOptions { options } => {
action = handle_hop_by_hop_options_ext_hdr(
ctx,
device,
frame_dst,
packet,
options.iter(),
);
}
Ipv6ExtensionHeaderData::Fragment { fragment_data } => {
action = handle_fragment_ext_hdr(ctx, device, frame_dst, packet, fragment_data);
}
Ipv6ExtensionHeaderData::DestinationOptions { options } => {
action = handle_destination_options_ext_hdr(
ctx,
device,
frame_dst,
packet,
options.iter(),
);
}
}
}
} else {
// Packet is not yet at the destination, so only process the hop-by-hop
// options extension header (which MUST be the first extension header if
// it is present) as per RFC 8200 section 4.
if let Some(ext_hdr) = iter.next() {
if let Ipv6ExtensionHeaderData::HopByHopOptions { options } = ext_hdr.data() {
action = handle_hop_by_hop_options_ext_hdr(
ctx,
device,
frame_dst,
packet,
options.iter(),
);
}
}
}
action
}
/// Handles a Hop By Hop extension header for a `packet`.
// For now, we do not support any options. If parsing succeeds we are
// guaranteed that the options present are safely skippable. If they
// aren't safely skippable, we must have resulted in a parsing error
// when parsing the packet, and so this function will never be called.
fn handle_hop_by_hop_options_ext_hdr<
'a,
D: EventDispatcher,
B: ByteSlice,
I: Iterator<Item = ExtensionHeaderOption<HopByHopOptionData<'a>>>,
>(
_ctx: &mut Context<D>,
_device: DeviceId,
_frame_dst: FrameDestination,
_packet: &Ipv6Packet<B>,
options: I,
) -> Ipv6PacketAction {
for option in options {
match option.data {
// Safely skip and continue, as we know that if we parsed an unrecognized
// option, the option's action was set to skip and continue.
HopByHopOptionData::Unrecognized { .. } => {}
// Also skip RouterAlert because router part of MLD is not implemented.
HopByHopOptionData::RouterAlert { .. } => {}
}
}
Ipv6PacketAction::Continue
}
/// Handles a routing extension header for a `packet`.
// TODO(rheacock): Remove `_` prefix when this is used
fn _handle_routing_ext_hdr<'a, D: EventDispatcher, B: ByteSlice>(
_ctx: &mut Context<D>,
_device: DeviceId,
_frame_dst: FrameDestination,
_packet: &Ipv6Packet<B>,
_routing_data: &RoutingData<'a>,
) -> Ipv6PacketAction {
// We should never end up here because we do not support parsing any routing header
// type yet. We should have errored out while parsing the extension header if
// there is a routing header we would normally have to act on.
unreachable!("We should not end up here because no routing type is supported yet");
}
/// Handles a fragment extension header for a `packet`.
fn handle_fragment_ext_hdr<'a, D: EventDispatcher, B: ByteSlice>(
_ctx: &mut Context<D>,
_device: DeviceId,
_frame_dst: FrameDestination,
_packet: &Ipv6Packet<B>,
_fragment_data: &FragmentData<'a>,
) -> Ipv6PacketAction {
Ipv6PacketAction::ProcessFragment
}
/// Handles a destination extension header for a `packet`.
// For now, we do not support any options. If parsing succeeds we are
// guaranteed that the options present are safely skippable. If they
// aren't safely skippable, we must have resulted in a parsing error
// when parsing the packet, and so this function will never be called.
fn handle_destination_options_ext_hdr<
'a,
D: EventDispatcher,
B: ByteSlice,
I: Iterator<Item = ExtensionHeaderOption<DestinationOptionData<'a>>>,
>(
_ctx: &mut Context<D>,
_device: DeviceId,
_frame_dst: FrameDestination,
_packet: &Ipv6Packet<B>,
options: I,
) -> Ipv6PacketAction {
for option in options {
match option.data {
// Safely skip and continue, as we know that if we parsed an unrecognized
// option, the option's action was set to skip and continue.
DestinationOptionData::Unrecognized { .. } => {}
}
}
Ipv6PacketAction::Continue
}
#[cfg(test)]
mod tests {
use super::*;
use packet::serialize::{Buf, Serializer};
use packet::ParseBuffer;
use packet_formats::ip::IpProto;
use packet_formats::ipv6::{Ipv6Packet, Ipv6PacketBuilder};
use crate::testutil::{DummyEventDispatcher, DummyEventDispatcherBuilder, DUMMY_CONFIG_V6};
#[test]
fn test_no_extension_headers() {
//
// Test that if we have no extension headers, we continue
//
let mut ctx = DummyEventDispatcherBuilder::from_config(DUMMY_CONFIG_V6)
.build::<DummyEventDispatcher>();
let builder = Ipv6PacketBuilder::new(
DUMMY_CONFIG_V6.remote_ip,
DUMMY_CONFIG_V6.local_ip,
10,
IpProto::Tcp,
);
let device_id = DeviceId::new_ethernet(0);
let frame_dst = FrameDestination::Unicast;
let mut buffer =
Buf::new(vec![1, 2, 3, 4, 5], ..).encapsulate(builder).serialize_vec_outer().unwrap();
let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
assert_eq!(
handle_extension_headers(&mut ctx, device_id, frame_dst, &packet, false),
Ipv6PacketAction::Continue
);
}
}