[recovery-netstack] ip: Improve extension traits, make them public
- Add IpPacketBuilder trait, implemented by Ipv{4,6}PacketBuilder
- Use IpPacketBuilder in IP module, simplifying sending logic
- Add more documentation to IP extension traits
- Make IP extension traits public so they can be used by other
modules
Test: Existing tests pass; future CLs make use of this functionality,
and are tested
Change-Id: I6bd3f68c09795b1c0a89e24ed1b218939ef7ff9c
diff --git a/garnet/bin/recovery_netstack/core/src/ip/mod.rs b/garnet/bin/recovery_netstack/core/src/ip/mod.rs
index 77126d6..40bb405 100644
--- a/garnet/bin/recovery_netstack/core/src/ip/mod.rs
+++ b/garnet/bin/recovery_netstack/core/src/ip/mod.rs
@@ -13,16 +13,12 @@
pub use self::types::*;
use log::{debug, trace};
-use std::fmt::Debug;
use std::mem;
use packet::{BufferMut, BufferSerializer, ParsablePacket, ParseBufferMut, Serializer};
-use zerocopy::{ByteSlice, ByteSliceMut};
use crate::device::DeviceId;
use crate::ip::forwarding::{Destination, ForwardingTable};
-use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketBuilder};
-use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketBuilder};
use crate::{Context, EventDispatcher};
// default IPv4 TTL or IPv6 hops
@@ -319,96 +315,11 @@
assert!(!A::Version::LOOPBACK_SUBNET.contains(src_ip));
assert!(!A::Version::LOOPBACK_SUBNET.contains(dst_ip));
- specialize_ip_addr!(
- fn serialize<D, S>(
- ctx: &mut Context<D>, device: DeviceId, src_ip: Self, dst_ip: Self, next_hop: Self, ttl: u8, proto: IpProto, body: S
- )
- where
- D: EventDispatcher,
- S: Serializer,
- {
- Ipv4Addr => {
- let body = body.encapsulate(Ipv4PacketBuilder::new(src_ip, dst_ip, ttl, proto));
- crate::device::send_ip_frame(ctx, device, next_hop, body);
- }
- Ipv6Addr => {
- let body = body.encapsulate(Ipv6PacketBuilder::new(src_ip, dst_ip, ttl, proto));
- crate::device::send_ip_frame(ctx, device, next_hop, body);
- }
- }
- );
- A::serialize(ctx, device, src_ip, dst_ip, next_hop, DEFAULT_TTL, proto, body)
-}
-
-// An `Ip` extension trait for internal use.
-//
-// This trait adds extra associated types that are useful for our implementation
-// here, but which consumers outside of the ip module do not need to see.
-trait IpExt<B: ByteSlice>: Ip {
- type Packet: IpPacket<B, Self>;
-}
-
-impl<B: ByteSlice, I: Ip> IpExt<B> for I {
- default type Packet = !;
-}
-
-impl<B: ByteSlice> IpExt<B> for Ipv4 {
- type Packet = Ipv4Packet<B>;
-}
-
-impl<B: ByteSlice> IpExt<B> for Ipv6 {
- type Packet = Ipv6Packet<B>;
-}
-
-// `Ipv4Packet` or `Ipv6Packet`
-trait IpPacket<B: ByteSlice, I: Ip>: Sized + Debug + ParsablePacket<B, ()> {
- fn src_ip(&self) -> I::Addr;
- fn dst_ip(&self) -> I::Addr;
- fn proto(&self) -> IpProto;
- fn ttl(&self) -> u8;
- fn set_ttl(&mut self, ttl: u8)
- where
- B: ByteSliceMut;
-}
-
-impl<B: ByteSlice> IpPacket<B, Ipv4> for Ipv4Packet<B> {
- fn src_ip(&self) -> Ipv4Addr {
- Ipv4Packet::src_ip(self)
- }
- fn dst_ip(&self) -> Ipv4Addr {
- Ipv4Packet::dst_ip(self)
- }
- fn proto(&self) -> IpProto {
- Ipv4Packet::proto(self)
- }
- fn ttl(&self) -> u8 {
- Ipv4Packet::ttl(self)
- }
- fn set_ttl(&mut self, ttl: u8)
- where
- B: ByteSliceMut,
- {
- Ipv4Packet::set_ttl(self, ttl)
- }
-}
-
-impl<B: ByteSlice> IpPacket<B, Ipv6> for Ipv6Packet<B> {
- fn src_ip(&self) -> Ipv6Addr {
- Ipv6Packet::src_ip(self)
- }
- fn dst_ip(&self) -> Ipv6Addr {
- Ipv6Packet::dst_ip(self)
- }
- fn proto(&self) -> IpProto {
- Ipv6Packet::proto(self)
- }
- fn ttl(&self) -> u8 {
- Ipv6Packet::hop_limit(self)
- }
- fn set_ttl(&mut self, ttl: u8)
- where
- B: ByteSliceMut,
- {
- Ipv6Packet::set_hop_limit(self, ttl)
- }
+ let body = body.encapsulate(<A::Version as IpExt<&[u8]>>::PacketBuilder::new(
+ src_ip,
+ dst_ip,
+ DEFAULT_TTL,
+ proto,
+ ));
+ crate::device::send_ip_frame(ctx, device, next_hop, body);
}
diff --git a/garnet/bin/recovery_netstack/core/src/ip/types.rs b/garnet/bin/recovery_netstack/core/src/ip/types.rs
index 77e34c8..08e0f76 100644
--- a/garnet/bin/recovery_netstack/core/src/ip/types.rs
+++ b/garnet/bin/recovery_netstack/core/src/ip/types.rs
@@ -7,7 +7,12 @@
use std::hash::Hash;
use byteorder::{ByteOrder, NetworkEndian};
-use zerocopy::{AsBytes, FromBytes, Unaligned};
+use packet::{PacketBuilder, ParsablePacket};
+use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, Unaligned};
+
+use crate::error::ParseError;
+use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketBuilder};
+use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketBuilder};
/// An IP protocol version.
#[allow(missing_docs)]
@@ -427,6 +432,128 @@
}
}
+/// An extension trait to the `Ip` trait adding an associated `Packet` type.
+///
+/// `IpExt` extends the `Ip` trait, adding an associated `Packet` type. It
+/// cannot be part of the `Ip` trait because it requires a `B: ByteSlice`
+/// parameter (due to the requirements of `packet::ParsablePacket`).
+pub trait IpExt<B: ByteSlice>: Ip {
+ type Packet: IpPacket<B, Self, Builder = Self::PacketBuilder>;
+ type PacketBuilder: IpPacketBuilder<Self>;
+}
+
+// NOTE(joshlf): We know that this is safe because we seal the Ip trait to only
+// be implemented by Ipv4 and Ipv6.
+impl<B: ByteSlice, I: Ip> IpExt<B> for I {
+ default type Packet = !;
+ default type PacketBuilder = !;
+}
+
+impl<B: ByteSlice> IpExt<B> for Ipv4 {
+ type Packet = Ipv4Packet<B>;
+ type PacketBuilder = Ipv4PacketBuilder;
+}
+
+impl<B: ByteSlice> IpExt<B> for Ipv6 {
+ type Packet = Ipv6Packet<B>;
+ type PacketBuilder = Ipv6PacketBuilder;
+}
+
+/// An IPv4 or IPv6 packet.
+///
+/// `IpPacket` is implemented by `Ipv4Packet` and `Ipv6Packet`.
+pub trait IpPacket<B: ByteSlice, I: Ip>:
+ Sized + Debug + ParsablePacket<B, (), Error = ParseError>
+{
+ /// A builder for this packet type.
+ type Builder: IpPacketBuilder<I>;
+
+ /// The source IP address.
+ fn src_ip(&self) -> I::Addr;
+
+ /// The destination IP address.
+ fn dst_ip(&self) -> I::Addr;
+
+ /// The protocol (IPv4) or next header (IPv6) field.
+ fn proto(&self) -> IpProto;
+
+ /// The Time to Live (TTL).
+ fn ttl(&self) -> u8;
+
+ /// Set the Time to Live (TTL).
+ ///
+ /// `set_ttl` updates the packet's TTL in place.
+ fn set_ttl(&mut self, ttl: u8)
+ where
+ B: ByteSliceMut;
+}
+
+impl<B: ByteSlice> IpPacket<B, Ipv4> for Ipv4Packet<B> {
+ type Builder = Ipv4PacketBuilder;
+
+ fn src_ip(&self) -> Ipv4Addr {
+ Ipv4Packet::src_ip(self)
+ }
+ fn dst_ip(&self) -> Ipv4Addr {
+ Ipv4Packet::dst_ip(self)
+ }
+ fn proto(&self) -> IpProto {
+ Ipv4Packet::proto(self)
+ }
+ fn ttl(&self) -> u8 {
+ Ipv4Packet::ttl(self)
+ }
+ fn set_ttl(&mut self, ttl: u8)
+ where
+ B: ByteSliceMut,
+ {
+ Ipv4Packet::set_ttl(self, ttl)
+ }
+}
+
+impl<B: ByteSlice> IpPacket<B, Ipv6> for Ipv6Packet<B> {
+ type Builder = Ipv6PacketBuilder;
+
+ fn src_ip(&self) -> Ipv6Addr {
+ Ipv6Packet::src_ip(self)
+ }
+ fn dst_ip(&self) -> Ipv6Addr {
+ Ipv6Packet::dst_ip(self)
+ }
+ fn proto(&self) -> IpProto {
+ Ipv6Packet::proto(self)
+ }
+ fn ttl(&self) -> u8 {
+ Ipv6Packet::hop_limit(self)
+ }
+ fn set_ttl(&mut self, ttl: u8)
+ where
+ B: ByteSliceMut,
+ {
+ Ipv6Packet::set_hop_limit(self, ttl)
+ }
+}
+
+/// A builder for IP packets.
+///
+/// `IpPacketBuilder` is implemented by `Ipv4PacketBuilder` and
+/// `Ipv6PacketBuilder`.
+pub trait IpPacketBuilder<I: Ip>: PacketBuilder {
+ fn new(src_ip: I::Addr, dst_ip: I::Addr, ttl: u8, proto: IpProto) -> Self;
+}
+
+impl IpPacketBuilder<Ipv4> for Ipv4PacketBuilder {
+ fn new(src_ip: Ipv4Addr, dst_ip: Ipv4Addr, ttl: u8, proto: IpProto) -> Ipv4PacketBuilder {
+ Ipv4PacketBuilder::new(src_ip, dst_ip, ttl, proto)
+ }
+}
+
+impl IpPacketBuilder<Ipv6> for Ipv6PacketBuilder {
+ fn new(src_ip: Ipv6Addr, dst_ip: Ipv6Addr, ttl: u8, proto: IpProto) -> Ipv6PacketBuilder {
+ Ipv6PacketBuilder::new(src_ip, dst_ip, ttl, proto)
+ }
+}
+
/// An IPv4 header option.
///
/// An IPv4 header option comprises metadata about the option (which is stored