netstack3_ip/raw/
protocol.rs

1// Copyright 2024 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//! Declares types related to the protocols of raw IP sockets.
6
7use net_types::ip::{GenericOverIp, Ip, IpVersion};
8use netstack3_base::IpExt;
9use packet_formats::ip::{IpProto, Ipv4Proto, Ipv6Proto};
10
11/// A witness type enforcing that the contained protocol is not the
12/// "reserved" IANA Internet protocol.
13#[derive(Clone, Copy, Debug, Eq, GenericOverIp, Ord, PartialEq, PartialOrd)]
14#[generic_over_ip(I, Ip)]
15pub struct Protocol<I: IpExt>(I::Proto);
16
17impl<I: IpExt> Protocol<I> {
18    fn new(proto: I::Proto) -> Option<Protocol<I>> {
19        I::map_ip(
20            proto,
21            |v4_proto| match v4_proto {
22                Ipv4Proto::Proto(IpProto::Reserved) => None,
23                _ => Some(Protocol(v4_proto)),
24            },
25            |v6_proto| match v6_proto {
26                Ipv6Proto::Proto(IpProto::Reserved) => None,
27                _ => Some(Protocol(v6_proto)),
28            },
29        )
30    }
31}
32
33/// The supported protocols of raw IP sockets.
34#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
35pub enum RawIpSocketProtocol<I: IpExt> {
36    /// Analogous to `IPPROTO_RAW` on Linux.
37    ///
38    /// This configures the socket as send only (no packets will be received).
39    /// When sending the provided message must include the IP header, as when
40    /// the `IP_HDRINCL` socket option is set.
41    Raw,
42    /// An IANA Internet Protocol.
43    Proto(Protocol<I>),
44}
45
46impl<I: IpExt> RawIpSocketProtocol<I> {
47    /// Construct a new [`RawIpSocketProtocol`] from the given IP protocol.
48    pub fn new(proto: I::Proto) -> RawIpSocketProtocol<I> {
49        Protocol::new(proto).map_or(RawIpSocketProtocol::Raw, RawIpSocketProtocol::Proto)
50    }
51
52    /// Extract the plain IP protocol from this [`RawIpSocketProtocol`].
53    pub fn proto(&self) -> I::Proto {
54        match self {
55            RawIpSocketProtocol::Raw => IpProto::Reserved.into(),
56            RawIpSocketProtocol::Proto(Protocol(proto)) => *proto,
57        }
58    }
59
60    /// True, if the protocol is ICMP for the associated IP version `I`.
61    pub fn is_icmp(&self) -> bool {
62        match self {
63            RawIpSocketProtocol::Raw => false,
64            RawIpSocketProtocol::Proto(Protocol(p)) => *p == I::ICMP_IP_PROTO,
65        }
66    }
67
68    /// True if the protocol requires system checksum support.
69    pub fn requires_system_checksums(&self) -> bool {
70        // Per RFC 2292, Section 3.1:
71        //
72        // The kernel will calculate and insert the ICMPv6 checksum for ICMPv6
73        // raw sockets, since this checksum is mandatory.
74        //
75        // https://www.rfc-editor.org/rfc/rfc2292#section-3.1
76        self.is_icmp() && I::VERSION == IpVersion::V6
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use super::*;
83    use ip_test_macro::ip_test;
84    use net_types::ip::{Ipv4, Ipv6};
85    use test_case::test_case;
86
87    #[ip_test(I)]
88    #[test_case(IpProto::Udp, Some(Protocol(IpProto::Udp.into())); "valid")]
89    #[test_case(IpProto::Reserved, None; "reserved")]
90    fn new_protocol<I: IpExt>(p: IpProto, expected: Option<Protocol<I>>) {
91        assert_eq!(Protocol::new(p.into()), expected);
92    }
93
94    #[ip_test(I)]
95    #[test_case(IpProto::Udp, RawIpSocketProtocol::Proto(Protocol(IpProto::Udp.into())); "valid")]
96    #[test_case(IpProto::Reserved, RawIpSocketProtocol::Raw; "reserved")]
97    fn new_raw_ip_socket_protocol<I: IpExt>(p: IpProto, expected: RawIpSocketProtocol<I>) {
98        assert_eq!(RawIpSocketProtocol::new(p.into()), expected);
99    }
100
101    #[test_case(Ipv4Proto::Icmp, true; "icmpv4")]
102    #[test_case(Ipv4Proto::Other(58), false; "icmpv6")]
103    #[test_case(Ipv4Proto::Proto(IpProto::Udp), false; "udp")]
104    #[test_case(Ipv4Proto::Proto(IpProto::Reserved), false; "reserved")]
105    fn is_icmpv4(proto: Ipv4Proto, expected_result: bool) {
106        assert_eq!(RawIpSocketProtocol::<Ipv4>::new(proto).is_icmp(), expected_result)
107    }
108
109    #[test_case(Ipv6Proto::Icmpv6, true; "icmpv6")]
110    #[test_case(Ipv6Proto::Other(1), false; "icmpv4")]
111    #[test_case(Ipv6Proto::Proto(IpProto::Udp), false; "udp")]
112    #[test_case(Ipv6Proto::Proto(IpProto::Reserved), false; "reserved")]
113    fn is_icmpv6(proto: Ipv6Proto, expected_result: bool) {
114        assert_eq!(RawIpSocketProtocol::<Ipv6>::new(proto).is_icmp(), expected_result)
115    }
116}