1 /// "Destination Unreachable" ICMP header for IPv4 (without the invoking packet).
2 ///
3 /// # Description in RFC 792:
4 ///
5 /// If, according to the information in the gateway's routing tables,
6 /// the network specified in the internet destination field of a
7 /// datagram is unreachable, e.g., the distance to the network is
8 /// infinity, the gateway may send a destination unreachable message
9 /// to the internet source host of the datagram.  In addition, in some
10 /// networks, the gateway may be able to determine if the internet
11 /// destination host is unreachable.  Gateways in these networks may
12 /// send destination unreachable messages to the source host when the
13 /// destination host is unreachable.
14 ///
15 /// If, in the destination host, the IP module cannot deliver the
16 /// datagram  because the indicated protocol module or process port is
17 /// not active, the destination host may send a destination
18 /// unreachable message to the source host.
19 ///
20 /// Another case is when a datagram must be fragmented to be forwarded
21 /// by a gateway yet the Don't Fragment flag is on.  In this case the
22 /// gateway must discard the datagram and may return a destination
23 /// unreachable message.
24 ///
25 /// Codes 0, 1, 4, and 5 may be received from a gateway.  Codes 2 and
26 /// 3 may be received from a host.
27 #[derive(Clone, Debug, PartialEq, Eq)]
28 pub enum DestUnreachableHeader {
29     /// Network unreachable error.
30     Network,
31     /// Host unreachable error.
32     Host,
33     /// Transport protocol not supported error.
34     Protocol,
35     /// Port unreachable error.
36     Port,
37     /// Fragmentation would be needed but the don't fragment bit is set.
38     FragmentationNeeded { next_hop_mtu: u16 },
39     /// Source Route Failed
40     SourceRouteFailed,
41     /// Destination Network Unknown (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
42     NetworkUnknown,
43     /// Destination Host Unknown (no route to host known) (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
44     HostUnknown,
45     /// Source Host Isolated - obsolete (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
46     Isolated,
47     /// Communication with Destination Network is Administratively Prohibited (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
48     NetworkProhibited,
49     /// Communication with Destination Host is Administratively Prohibited (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
50     HostProhibited,
51     /// Destination Network Unreachable for Type of Service (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
52     TosNetwork,
53     /// Destination Host Unreachable for Type of Service (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
54     TosHost,
55     /// Cannot forward because packet administratively filtered (from [RFC 1812](https://tools.ietf.org/html/rfc1812))
56     FilterProhibited,
57     /// Required level of precidence not supported (from [RFC 1812](https://tools.ietf.org/html/rfc1812))
58     HostPrecedenceViolation,
59     /// Packet was below minimum precidence (from [RFC 1812](https://tools.ietf.org/html/rfc1812))
60     PrecedenceCutoff,
61 }
62 
63 impl DestUnreachableHeader {
64     /// Tries to convert the code [`u8`] value and next_hop_mtu to a [`DestUnreachableHeader`] value.
65     ///
66     /// Returns [`None`] in case the code value is not known as a destination unreachable code.
from_values(code_u8: u8, next_hop_mtu: u16) -> Option<DestUnreachableHeader>67     pub fn from_values(code_u8: u8, next_hop_mtu: u16) -> Option<DestUnreachableHeader> {
68         use crate::icmpv4::{DestUnreachableHeader::*, *};
69         match code_u8 {
70             CODE_DST_UNREACH_NET => Some(Network),
71             CODE_DST_UNREACH_HOST => Some(Host),
72             CODE_DST_UNREACH_PROTOCOL => Some(Protocol),
73             CODE_DST_UNREACH_PORT => Some(Port),
74             CODE_DST_UNREACH_NEED_FRAG => Some(FragmentationNeeded { next_hop_mtu }),
75             CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => Some(SourceRouteFailed),
76             CODE_DST_UNREACH_NET_UNKNOWN => Some(NetworkUnknown),
77             CODE_DST_UNREACH_HOST_UNKNOWN => Some(HostUnknown),
78             CODE_DST_UNREACH_ISOLATED => Some(Isolated),
79             CODE_DST_UNREACH_NET_PROHIB => Some(NetworkProhibited),
80             CODE_DST_UNREACH_HOST_PROHIB => Some(HostProhibited),
81             CODE_DST_UNREACH_TOS_NET => Some(TosNetwork),
82             CODE_DST_UNREACH_TOS_HOST => Some(TosHost),
83             CODE_DST_UNREACH_FILTER_PROHIB => Some(FilterProhibited),
84             CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => Some(HostPrecedenceViolation),
85             CODE_DST_UNREACH_PRECEDENCE_CUTOFF => Some(PrecedenceCutoff),
86             _ => None,
87         }
88     }
89 
90     /// Returns the icmp code value of the destination unreachable packet.
91     #[inline]
code_u8(&self) -> u892     pub fn code_u8(&self) -> u8 {
93         use crate::icmpv4::{DestUnreachableHeader::*, *};
94         match self {
95             Network => CODE_DST_UNREACH_NET,
96             Host => CODE_DST_UNREACH_HOST,
97             Protocol => CODE_DST_UNREACH_PROTOCOL,
98             Port => CODE_DST_UNREACH_PORT,
99             FragmentationNeeded { next_hop_mtu: _ } => CODE_DST_UNREACH_NEED_FRAG,
100             SourceRouteFailed => CODE_DST_UNREACH_SOURCE_ROUTE_FAILED,
101             NetworkUnknown => CODE_DST_UNREACH_NET_UNKNOWN,
102             HostUnknown => CODE_DST_UNREACH_HOST_UNKNOWN,
103             Isolated => CODE_DST_UNREACH_ISOLATED,
104             NetworkProhibited => CODE_DST_UNREACH_NET_PROHIB,
105             HostProhibited => CODE_DST_UNREACH_HOST_PROHIB,
106             TosNetwork => CODE_DST_UNREACH_TOS_NET,
107             TosHost => CODE_DST_UNREACH_TOS_HOST,
108             FilterProhibited => CODE_DST_UNREACH_FILTER_PROHIB,
109             HostPrecedenceViolation => CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION,
110             PrecedenceCutoff => CODE_DST_UNREACH_PRECEDENCE_CUTOFF,
111         }
112     }
113 }
114 
115 #[cfg(test)]
116 mod test {
117     use crate::icmpv4::*;
118     use alloc::format;
119     use proptest::prelude::*;
120 
conversion_values(next_hop_mtu: u16) -> [(u8, DestUnreachableHeader); 16]121     fn conversion_values(next_hop_mtu: u16) -> [(u8, DestUnreachableHeader); 16] {
122         use DestUnreachableHeader::*;
123         [
124             (CODE_DST_UNREACH_NET, Network),
125             (CODE_DST_UNREACH_HOST, Host),
126             (CODE_DST_UNREACH_PROTOCOL, Protocol),
127             (CODE_DST_UNREACH_PORT, Port),
128             (
129                 CODE_DST_UNREACH_NEED_FRAG,
130                 FragmentationNeeded { next_hop_mtu },
131             ),
132             (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed),
133             (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown),
134             (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown),
135             (CODE_DST_UNREACH_ISOLATED, Isolated),
136             (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited),
137             (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited),
138             (CODE_DST_UNREACH_TOS_NET, TosNetwork),
139             (CODE_DST_UNREACH_TOS_HOST, TosHost),
140             (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited),
141             (
142                 CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION,
143                 HostPrecedenceViolation,
144             ),
145             (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff),
146         ]
147     }
148 
149     proptest! {
150         #[test]
151         fn from_values(
152             next_hop_mtu in any::<u16>(),
153         ) {
154             // valid values
155             {
156                 let valid_values = conversion_values(next_hop_mtu);
157                 for t in valid_values {
158                     assert_eq!(Some(t.1), DestUnreachableHeader::from_values(t.0, next_hop_mtu));
159                 }
160             }
161             // invalid values
162             for code_u8 in 16u8..=u8::MAX {
163                 assert_eq!(None, DestUnreachableHeader::from_values(code_u8, next_hop_mtu));
164             }
165         }
166     }
167 
168     proptest! {
169         #[test]
170         fn code_u8(
171             next_hop_mtu in any::<u16>(),
172         ) {
173             let valid_values = conversion_values(next_hop_mtu);
174             for t in valid_values {
175                 assert_eq!(t.0, t.1.code_u8());
176             }
177         }
178     }
179 
180     #[test]
clone_eq()181     fn clone_eq() {
182         use DestUnreachableHeader::*;
183         let values = [
184             Network,
185             Host,
186             Protocol,
187             Port,
188             FragmentationNeeded { next_hop_mtu: 0 },
189             SourceRouteFailed,
190             NetworkUnknown,
191             HostUnknown,
192             Isolated,
193             NetworkProhibited,
194             HostProhibited,
195             TosNetwork,
196             TosHost,
197             FilterProhibited,
198             HostPrecedenceViolation,
199             PrecedenceCutoff,
200         ];
201         for value in values {
202             assert_eq!(value.clone(), value);
203         }
204     }
205 
206     #[test]
debug()207     fn debug() {
208         use DestUnreachableHeader::*;
209         let tests = [
210             ("Network", Network),
211             ("Host", Host),
212             ("Protocol", Protocol),
213             ("Port", Port),
214             (
215                 "FragmentationNeeded { next_hop_mtu: 0 }",
216                 FragmentationNeeded { next_hop_mtu: 0 },
217             ),
218             ("SourceRouteFailed", SourceRouteFailed),
219             ("NetworkUnknown", NetworkUnknown),
220             ("HostUnknown", HostUnknown),
221             ("Isolated", Isolated),
222             ("NetworkProhibited", NetworkProhibited),
223             ("HostProhibited", HostProhibited),
224             ("TosNetwork", TosNetwork),
225             ("TosHost", TosHost),
226             ("FilterProhibited", FilterProhibited),
227             ("HostPrecedenceViolation", HostPrecedenceViolation),
228             ("PrecedenceCutoff", PrecedenceCutoff),
229         ];
230         for t in tests {
231             assert_eq!(t.0, format!("{:?}", t.1));
232         }
233     }
234 }
235