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