Skip to main content

packet_formats/
ip.rs

1// Copyright 2020 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//! IP protocol types.
6
7// TODO(https://fxbug.dev/326330182): this import seems actually necessary. Is this a bug on the
8// lint?
9#[allow(unused_imports)]
10use alloc::vec::Vec;
11use core::cmp::PartialEq;
12use core::convert::Infallible as Never;
13use core::fmt::{Debug, Display};
14use core::hash::Hash;
15use core::marker::PhantomData;
16
17use net_types::ip::{GenericOverIp, Ip, IpAddr, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
18use packet::{
19    BufferViewMut, NoOpSerializationContext, PacketBuilder, ParsablePacket, ParseMetadata,
20    PartialPacketBuilder, SerializationContext,
21};
22use zerocopy::{
23    FromBytes, Immutable, IntoBytes, KnownLayout, SplitByteSlice, SplitByteSliceMut, Unaligned,
24};
25
26use crate::error::{IpParseResult, Ipv6ParseError, ParseError};
27use crate::ethernet::EthernetIpExt;
28use crate::icmp::IcmpIpExt;
29use crate::ipv4::{IPV4_MIN_HDR_LEN, Ipv4Header, Ipv4Packet, Ipv4PacketBuilder, Ipv4PacketRaw};
30use crate::ipv6::{IPV6_FIXED_HDR_LEN, Ipv6Header, Ipv6Packet, Ipv6PacketBuilder, Ipv6PacketRaw};
31use crate::private::Sealed;
32
33/// An [`Ip`] extension trait adding an associated type for the IP protocol
34/// number.
35pub trait IpProtoExt: Ip {
36    /// The type representing an IPv4 or IPv6 protocol number.
37    ///
38    /// For IPv4, this is [`Ipv4Proto`], and for IPv6, this is [`Ipv6Proto`].
39    type Proto: IpProtocol
40        + GenericOverIp<Self, Type = Self::Proto>
41        + GenericOverIp<Ipv4, Type = Ipv4Proto>
42        + GenericOverIp<Ipv6, Type = Ipv6Proto>
43        + Copy
44        + Clone
45        + Hash
46        + Debug
47        + Display
48        + PartialEq
49        + Eq
50        + PartialOrd
51        + Ord;
52}
53
54impl IpProtoExt for Ipv4 {
55    type Proto = Ipv4Proto;
56}
57
58impl IpProtoExt for Ipv6 {
59    type Proto = Ipv6Proto;
60}
61
62/// IP packet context relevant to serialization.
63pub struct IpEnvelope<I: IpExt> {
64    /// True if the IP packet has options (IPv4) or extension headers (IPv6).
65    pub has_options: bool,
66    _marker: PhantomData<I>,
67}
68
69impl<I: IpExt> IpEnvelope<I> {
70    /// Creates a new `IpEnvelope`.
71    pub fn new(has_options: bool) -> Self {
72        Self { has_options, _marker: PhantomData }
73    }
74}
75
76/// A trait for IP serialization contexts.
77pub trait IpSerializationContext<I: IpExt>: SerializationContext {
78    /// Converts an `IpEnvelope` into the serialization context's state.
79    fn envelope_to_state(envelope: IpEnvelope<I>) -> Self::ContextState;
80}
81
82impl<I: IpExt> IpSerializationContext<I> for NoOpSerializationContext {
83    fn envelope_to_state(_envelope: IpEnvelope<I>) -> Self::ContextState {
84        ()
85    }
86}
87
88/// An extension trait to the `Ip` trait adding associated types relevant for
89/// packet parsing and serialization.
90pub trait IpExt: EthernetIpExt + IcmpIpExt {
91    /// The error type returned when parsing a packet of this IP version fails.
92    type PacketParseError: From<ParseError> + Debug + PartialEq + Send + Sync;
93
94    /// An IP packet type for this IP version.
95    type Packet<B: SplitByteSlice>: IpPacket<B, Self>
96        + GenericOverIp<Self, Type = Self::Packet<B>>
97        + GenericOverIp<Ipv4, Type = Ipv4Packet<B>>
98        + GenericOverIp<Ipv6, Type = Ipv6Packet<B>>;
99    /// A raw IP packet type for this IP version.
100    type PacketRaw<B: SplitByteSlice>: IpPacketRaw<B, Self>
101        + GenericOverIp<Self, Type = Self::PacketRaw<B>>
102        + GenericOverIp<Ipv4, Type = Ipv4PacketRaw<B>>
103        + GenericOverIp<Ipv6, Type = Ipv6PacketRaw<B>>;
104    /// An IP packet builder type for the IP version.
105    type PacketBuilder<C: IpSerializationContext<Self>>: IpPacketBuilder<C, Self> + Eq;
106    /// Minimal IP header size.
107    const MIN_HEADER_LENGTH: usize;
108}
109
110impl IpExt for Ipv4 {
111    type PacketParseError = ParseError;
112    type Packet<B: SplitByteSlice> = Ipv4Packet<B>;
113    type PacketRaw<B: SplitByteSlice> = Ipv4PacketRaw<B>;
114    type PacketBuilder<C: IpSerializationContext<Self>> = Ipv4PacketBuilder;
115
116    const MIN_HEADER_LENGTH: usize = IPV4_MIN_HDR_LEN;
117}
118
119impl IpExt for Ipv6 {
120    type PacketParseError = Ipv6ParseError;
121    type Packet<B: SplitByteSlice> = Ipv6Packet<B>;
122    type PacketRaw<B: SplitByteSlice> = Ipv6PacketRaw<B>;
123    type PacketBuilder<C: IpSerializationContext<Self>> = Ipv6PacketBuilder;
124
125    const MIN_HEADER_LENGTH: usize = IPV6_FIXED_HDR_LEN;
126}
127
128/// An error encountered during NAT64 translation.
129#[derive(Debug)]
130pub enum Nat64Error {
131    /// Support not yet implemented in the library.
132    NotImplemented,
133}
134
135/// The result of NAT64 translation.
136#[derive(Debug)]
137pub enum Nat64TranslationResult<S, E> {
138    /// Forward the packet encoded in `S`.
139    Forward(S),
140    /// Silently drop the packet.
141    Drop,
142    /// An error was encountered.
143    Err(E),
144}
145
146/// Combines Differentiated Services Code Point (DSCP) and Explicit Congestion
147/// Notification (ECN) values into one. Internally the 2 fields are stored
148/// using the same layout as the Traffic Class field in IPv6 and the Type Of
149/// Service field in IPv4: 6 higher bits for DSCP and 2 lower bits for ECN.
150#[derive(
151    Default,
152    Debug,
153    Clone,
154    Copy,
155    PartialEq,
156    Eq,
157    KnownLayout,
158    FromBytes,
159    IntoBytes,
160    Immutable,
161    Unaligned,
162)]
163#[repr(C)]
164pub struct DscpAndEcn(u8);
165
166const DSCP_OFFSET: u8 = 2;
167const DSCP_MAX: u8 = (1 << (8 - DSCP_OFFSET)) - 1;
168const ECN_MAX: u8 = (1 << DSCP_OFFSET) - 1;
169
170impl DscpAndEcn {
171    /// Returns the default value. Implemented separately from the `Default`
172    /// trait to make it `const`.
173    pub const fn default() -> Self {
174        Self(0)
175    }
176
177    /// Creates a new `DscpAndEcn` instance with the specified DSCP and ECN
178    /// values.
179    pub const fn new(dscp: u8, ecn: u8) -> Self {
180        debug_assert!(dscp <= DSCP_MAX);
181        debug_assert!(ecn <= ECN_MAX);
182        Self((dscp << DSCP_OFFSET) + ecn)
183    }
184
185    /// Constructs a new `DspAndEcn` from a raw value, i.e., both fields packet
186    /// into one byte.
187    pub const fn new_with_raw(value: u8) -> Self {
188        Self(value)
189    }
190
191    /// Returns the Differentiated Services Code Point value.
192    pub fn dscp(self) -> u8 {
193        let Self(v) = self;
194        v >> 2
195    }
196
197    /// Returns the Explicit Congestion Notification value.
198    pub fn ecn(self) -> u8 {
199        let Self(v) = self;
200        v & 0x3
201    }
202
203    /// Returns the raw value, i.e. both fields packed into one byte.
204    pub fn raw(self) -> u8 {
205        let Self(value) = self;
206        value
207    }
208}
209
210impl From<u8> for DscpAndEcn {
211    fn from(value: u8) -> Self {
212        Self::new_with_raw(value)
213    }
214}
215
216/// An IPv4 or IPv6 packet.
217///
218/// `IpPacket` is implemented by `Ipv4Packet` and `Ipv6Packet`.
219pub trait IpPacket<B: SplitByteSlice, I: IpExt>:
220    Sized + Debug + ParsablePacket<B, (), Error = I::PacketParseError>
221{
222    /// A builder for this packet type.
223    type Builder<C: IpSerializationContext<I>>: IpPacketBuilder<C, I>;
224
225    /// The source IP address.
226    fn src_ip(&self) -> I::Addr;
227
228    /// The destination IP address.
229    fn dst_ip(&self) -> I::Addr;
230
231    /// The protocol number.
232    fn proto(&self) -> I::Proto;
233
234    /// The Time to Live (TTL) (IPv4) or Hop Limit (IPv6) field.
235    fn ttl(&self) -> u8;
236
237    /// The Differentiated Services Code Point (DSCP) and the Explicit
238    /// Congestion Notification (ECN).
239    fn dscp_and_ecn(&self) -> DscpAndEcn;
240
241    /// Set the Time to Live (TTL) (IPv4) or Hop Limit (IPv6) field.
242    ///
243    /// `set_ttl` updates the packet's TTL/Hop Limit in place.
244    fn set_ttl(&mut self, ttl: u8)
245    where
246        B: SplitByteSliceMut;
247
248    /// The length of the header in bytes, including options/extension headers.
249    fn header_len(&self) -> usize;
250
251    /// Get the body.
252    fn body(&self) -> &[u8];
253
254    /// Consume the packet and return some metadata.
255    ///
256    /// Consume the packet and return the source address, destination address,
257    /// protocol, and `ParseMetadata`.
258    fn into_metadata(self) -> (I::Addr, I::Addr, I::Proto, ParseMetadata) {
259        let src_ip = self.src_ip();
260        let dst_ip = self.dst_ip();
261        let proto = self.proto();
262        let meta = self.parse_metadata();
263        (src_ip, dst_ip, proto, meta)
264    }
265
266    /// Converts a packet reference into a dynamically-typed reference.
267    fn as_ip_addr_ref(&self) -> IpAddr<&'_ Ipv4Packet<B>, &'_ Ipv6Packet<B>>;
268
269    /// Reassembles a fragmented packet into a parsed IP packet.
270    ///
271    /// # Panics
272    ///
273    /// Panics if the provided header is too small to hold a valid header.
274    fn reassemble_fragmented_packet<'a, BV: BufferViewMut<B>, IT: Iterator<Item = &'a [u8]>>(
275        buffer: BV,
276        header: &[u8],
277        body_fragments: IT,
278    ) -> IpParseResult<I, ()>
279    where
280        B: SplitByteSliceMut;
281
282    /// Copies the full packet into a `Vec`.
283    fn to_vec(&self) -> Vec<u8>;
284
285    /// Constructs a builder with the same contents as this packet's header.
286    fn builder<C: IpSerializationContext<I>>(&self) -> Self::Builder<C>;
287}
288
289impl<B: SplitByteSlice> IpPacket<B, Ipv4> for Ipv4Packet<B> {
290    type Builder<C: IpSerializationContext<Ipv4>> = Ipv4PacketBuilder;
291
292    fn src_ip(&self) -> Ipv4Addr {
293        Ipv4Header::src_ip(self)
294    }
295    fn dst_ip(&self) -> Ipv4Addr {
296        Ipv4Header::dst_ip(self)
297    }
298    fn proto(&self) -> Ipv4Proto {
299        Ipv4Header::proto(self)
300    }
301    fn dscp_and_ecn(&self) -> DscpAndEcn {
302        Ipv4Header::dscp_and_ecn(self)
303    }
304    fn ttl(&self) -> u8 {
305        Ipv4Header::ttl(self)
306    }
307    fn set_ttl(&mut self, ttl: u8)
308    where
309        B: SplitByteSliceMut,
310    {
311        Ipv4Packet::set_ttl(self, ttl)
312    }
313    fn header_len(&self) -> usize {
314        Ipv4Packet::header_len(self)
315    }
316    fn body(&self) -> &[u8] {
317        Ipv4Packet::body(self)
318    }
319
320    fn as_ip_addr_ref(&self) -> IpAddr<&'_ Self, &'_ Ipv6Packet<B>> {
321        IpAddr::V4(self)
322    }
323
324    fn reassemble_fragmented_packet<'a, BV: BufferViewMut<B>, IT: Iterator<Item = &'a [u8]>>(
325        buffer: BV,
326        header: &[u8],
327        body_fragments: IT,
328    ) -> IpParseResult<Ipv4, ()>
329    where
330        B: SplitByteSliceMut,
331    {
332        crate::ipv4::reassemble_fragmented_packet(buffer, header, body_fragments)
333    }
334
335    fn to_vec(&self) -> Vec<u8> {
336        self.to_vec()
337    }
338
339    fn builder<C: IpSerializationContext<Ipv4>>(&self) -> Self::Builder<C> {
340        Ipv4Header::builder(self)
341    }
342}
343
344impl<B: SplitByteSlice> IpPacket<B, Ipv6> for Ipv6Packet<B> {
345    type Builder<C: IpSerializationContext<Ipv6>> = Ipv6PacketBuilder;
346
347    fn src_ip(&self) -> Ipv6Addr {
348        Ipv6Header::src_ip(self)
349    }
350    fn dst_ip(&self) -> Ipv6Addr {
351        Ipv6Header::dst_ip(self)
352    }
353    fn proto(&self) -> Ipv6Proto {
354        Ipv6Packet::proto(self)
355    }
356    fn dscp_and_ecn(&self) -> DscpAndEcn {
357        Ipv6Header::dscp_and_ecn(self)
358    }
359    fn ttl(&self) -> u8 {
360        Ipv6Header::hop_limit(self)
361    }
362    fn set_ttl(&mut self, ttl: u8)
363    where
364        B: SplitByteSliceMut,
365    {
366        Ipv6Packet::set_hop_limit(self, ttl)
367    }
368    fn header_len(&self) -> usize {
369        Ipv6Packet::header_len(self)
370    }
371    fn body(&self) -> &[u8] {
372        Ipv6Packet::body(self)
373    }
374
375    fn as_ip_addr_ref(&self) -> IpAddr<&'_ Ipv4Packet<B>, &'_ Self> {
376        IpAddr::V6(self)
377    }
378    fn reassemble_fragmented_packet<'a, BV: BufferViewMut<B>, IT: Iterator<Item = &'a [u8]>>(
379        buffer: BV,
380        header: &[u8],
381        body_fragments: IT,
382    ) -> IpParseResult<Ipv6, ()>
383    where
384        B: SplitByteSliceMut,
385    {
386        crate::ipv6::reassemble_fragmented_packet(buffer, header, body_fragments)
387    }
388
389    fn to_vec(&self) -> Vec<u8> {
390        self.to_vec()
391    }
392
393    fn builder<C: IpSerializationContext<Ipv6>>(&self) -> Self::Builder<C> {
394        self.builder()
395    }
396}
397
398/// A raw IPv4 or IPv6 packet.
399///
400/// `IpPacketRaw` is implemented by `Ipv4PacketRaw` and `Ipv6PacketRaw`.
401pub trait IpPacketRaw<B: SplitByteSlice, I: IpExt>:
402    Sized + ParsablePacket<B, (), Error = I::PacketParseError>
403{
404}
405
406impl<B: SplitByteSlice> IpPacketRaw<B, Ipv4> for Ipv4PacketRaw<B> {}
407impl<B: SplitByteSlice, I: IpExt> GenericOverIp<I> for Ipv4PacketRaw<B> {
408    type Type = <I as IpExt>::PacketRaw<B>;
409}
410
411impl<B: SplitByteSlice> IpPacketRaw<B, Ipv6> for Ipv6PacketRaw<B> {}
412impl<B: SplitByteSlice, I: IpExt> GenericOverIp<I> for Ipv6PacketRaw<B> {
413    type Type = <I as IpExt>::PacketRaw<B>;
414}
415
416/// A builder for IP packets.
417pub trait IpPacketBuilder<C: SerializationContext, I: IpExt>:
418    PacketBuilder<C> + PartialPacketBuilder<C> + Clone + Debug
419{
420    /// Returns a new packet builder for an associated IP version with the given
421    /// given source and destination IP addresses, TTL (IPv4)/Hop Limit (IPv4)
422    /// and Protocol Number.
423    fn new(src_ip: I::Addr, dst_ip: I::Addr, ttl: u8, proto: I::Proto) -> Self;
424
425    /// Returns the source IP address for the builder.
426    fn src_ip(&self) -> I::Addr;
427
428    /// Sets the source IP address for the builder.
429    fn set_src_ip(&mut self, addr: I::Addr);
430
431    /// Returns the destination IP address for the builder.
432    fn dst_ip(&self) -> I::Addr;
433
434    /// Sets the destination IP address for the builder.
435    fn set_dst_ip(&mut self, addr: I::Addr);
436
437    /// Returns the IP protocol number for the builder.
438    fn proto(&self) -> I::Proto;
439
440    /// Set DSCP & ECN fields.
441    fn set_dscp_and_ecn(&mut self, dscp_and_ecn: DscpAndEcn);
442}
443
444/// An IPv4 or IPv6 protocol number.
445pub trait IpProtocol: From<IpProto> + From<u8> + Sealed + Send + Sync + 'static {}
446
447impl Sealed for Never {}
448
449create_protocol_enum!(
450    /// An IPv4 or IPv6 protocol number.
451    ///
452    /// `IpProto` encodes the protocol numbers whose values are the same for
453    /// both IPv4 and IPv6.
454    ///
455    /// The protocol numbers are maintained [by IANA][protocol-numbers].
456    ///
457    /// [protocol-numbers]: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
458    #[allow(missing_docs)]
459    #[derive(Copy, Clone, Hash, Eq, Ord, PartialEq, PartialOrd)]
460    pub enum IpProto: u8 {
461        Tcp, 6, "TCP";
462        Udp, 17, "UDP";
463        Reserved, 255, "IANA-RESERVED";
464    }
465);
466
467create_protocol_enum!(
468    /// An IPv4 protocol number.
469    ///
470    /// The protocol numbers are maintained [by IANA][protocol-numbers].
471    ///
472    /// [protocol-numbers]: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
473    #[allow(missing_docs)]
474    #[derive(Copy, Clone, Hash, Eq, Ord, PartialEq, PartialOrd)]
475    pub enum Ipv4Proto: u8 {
476        Icmp, 1, "ICMP";
477        Igmp, 2, "IGMP";
478        + Proto(IpProto);
479        _, "IPv4 protocol {}";
480    }
481);
482
483impl IpProtocol for Ipv4Proto {}
484impl<I: Ip + IpProtoExt> GenericOverIp<I> for Ipv4Proto {
485    type Type = I::Proto;
486}
487impl Sealed for Ipv4Proto {}
488
489create_protocol_enum!(
490    /// An IPv6 protocol number.
491    ///
492    /// The protocol numbers are maintained [by IANA][protocol-numbers].
493    ///
494    /// [protocol-numbers]: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
495    #[allow(missing_docs)]
496    #[derive(Copy, Clone, Hash, Eq, Ord, PartialEq, PartialOrd)]
497    pub enum Ipv6Proto: u8 {
498        Icmpv6, 58, "ICMPv6";
499        NoNextHeader, 59, "NO NEXT HEADER";
500        + Proto(IpProto);
501        _, "IPv6 protocol {}";
502    }
503);
504
505impl IpProtocol for Ipv6Proto {}
506impl<I: Ip + IpProtoExt> GenericOverIp<I> for Ipv6Proto {
507    type Type = I::Proto;
508}
509impl Sealed for Ipv6Proto {}
510
511create_protocol_enum!(
512    /// An IPv6 extension header.
513    ///
514    /// These are next header values that encode for extension header types.
515    /// This enum does not include upper layer protocol numbers even though they
516    /// may be valid next header values.
517    #[allow(missing_docs)]
518    #[derive(Copy, Clone, Hash, Eq, PartialEq)]
519    pub enum Ipv6ExtHdrType: u8 {
520        HopByHopOptions, 0, "IPv6 HOP-BY-HOP OPTIONS HEADER";
521        Routing, 43, "IPv6 ROUTING HEADER";
522        Fragment, 44, "IPv6 FRAGMENT HEADER";
523        EncapsulatingSecurityPayload, 50, "ENCAPSULATING SECURITY PAYLOAD";
524        Authentication, 51, "AUTHENTICATION HEADER";
525        DestinationOptions, 60, "IPv6 DESTINATION OPTIONS HEADER";
526        _,  "IPv6 EXTENSION HEADER {}";
527    }
528);
529
530/// An IP fragment offset.
531///
532/// Represents a fragment offset found in an IP header. The offset is expressed
533/// in units of 8 octets and must be smaller than `1 << 13`.
534///
535/// This is valid for both IPv4 ([RFC 791 Section 3.1]) and IPv6 ([RFC 8200
536/// Section 4.5]) headers.
537///
538/// [RFC 791 Section 3.1]: https://datatracker.ietf.org/doc/html/rfc791#section-3.1
539/// [RFC 8200 Section 4.5]: https://datatracker.ietf.org/doc/html/rfc8200#section-4.5
540#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
541pub struct FragmentOffset(u16);
542
543impl FragmentOffset {
544    /// The zero fragment offset.
545    pub const ZERO: FragmentOffset = FragmentOffset(0);
546
547    /// Creates a new offset from a raw u16 value.
548    ///
549    /// Returns `None` if `offset` is not smaller than `1 << 13`.
550    pub const fn new(offset: u16) -> Option<Self> {
551        if offset < 1 << 13 { Some(Self(offset)) } else { None }
552    }
553
554    /// Creates a new offset from a raw u16 value masking to only the lowest 13
555    /// bits.
556    pub(crate) fn new_with_lsb(offset: u16) -> Self {
557        Self(offset & 0x1FFF)
558    }
559
560    /// Creates a new offset from a raw u16 value masking to only the highest 13
561    /// bits.
562    pub(crate) fn new_with_msb(offset: u16) -> Self {
563        Self(offset >> 3)
564    }
565
566    /// Creates a new offset from a raw bytes value.
567    ///
568    /// Returns `None` if `offset_bytes` is not a multiple of `8`.
569    pub const fn new_with_bytes(offset_bytes: u16) -> Option<Self> {
570        if offset_bytes & 0x7 == 0 {
571            // NOTE: check for length above ensures this fits in a u16.
572            Some(Self(offset_bytes >> 3))
573        } else {
574            None
575        }
576    }
577
578    /// Consumes `self` returning the raw offset value in 8-octets multiples.
579    pub const fn into_raw(self) -> u16 {
580        self.0
581    }
582
583    /// Consumes `self` returning the total number of bytes represented by this
584    /// offset.
585    ///
586    /// Equal to 8 times the raw offset value.
587    pub fn into_bytes(self) -> u16 {
588        // NB: Shift can't overflow because `FragmentOffset` is guaranteed to
589        // fit in 13 bits.
590        self.0 << 3
591    }
592}
593
594#[cfg(test)]
595mod tests {
596    use super::*;
597
598    #[test]
599    fn fragment_offset_raw() {
600        assert_eq!(FragmentOffset::new(1), Some(FragmentOffset(1)));
601        assert_eq!(FragmentOffset::new(1 << 13), None);
602    }
603
604    #[test]
605    fn fragment_offset_bytes() {
606        assert_eq!(FragmentOffset::new_with_bytes(0), Some(FragmentOffset(0)));
607        for i in 1..=7 {
608            assert_eq!(FragmentOffset::new_with_bytes(i), None);
609        }
610        assert_eq!(FragmentOffset::new_with_bytes(8), Some(FragmentOffset(1)));
611        assert_eq!(FragmentOffset::new_with_bytes(u16::MAX), None);
612        assert_eq!(
613            FragmentOffset::new_with_bytes(u16::MAX & !0x7),
614            Some(FragmentOffset((1 << 13) - 1)),
615        );
616    }
617}