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