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