etherparse/transport/icmpv6_type.rs
1use crate::{
2 err::{ValueTooBigError, ValueType},
3 icmpv6::RouterAdvertisementHeader,
4 *,
5};
6
7/// Different kinds of ICMPv6 messages.
8///
9/// The data stored in this enum corresponds to the statically sized data
10/// at the start of an ICMPv6 packet without the checksum. If you also need
11/// the checksum you can package and [`Icmpv6Type`] value in an [`Icmpv6Header`]
12/// struct.
13///
14/// # Decoding Example (complete packet):
15///
16/// ```
17/// # use etherparse::{PacketBuilder};
18/// # let mut builder = PacketBuilder::
19/// # ethernet2([0;6], [0;6])
20/// # .ipv6([0;16], [0;16], 20)
21/// # .icmpv6_echo_request(1, 2);
22/// # let payload = [1,2,3,4];
23/// # let mut packet = Vec::<u8>::with_capacity(builder.size(payload.len()));
24/// # builder.write(&mut packet, &payload);
25/// use etherparse::PacketHeaders;
26///
27/// let headers = PacketHeaders::from_ethernet_slice(&packet).unwrap();
28///
29/// use etherparse::TransportHeader::*;
30/// match headers.transport {
31/// Some(Icmpv6(icmp)) => {
32/// use etherparse::Icmpv6Type::*;
33/// match icmp.icmp_type {
34/// // Unknown is used when further decoding is currently not supported for the icmp type & code.
35/// // You can still further decode the packet on your own by using the raw data in this enum
36/// // together with `headers.payload` (contains the packet data after the 8th byte)
37/// Unknown{ type_u8, code_u8, bytes5to8 } => println!("Unknown{{ type_u8: {}, code_u8: {}, bytes5to8: {:?} }}", type_u8, code_u8, bytes5to8),
38/// DestinationUnreachable(header) => println!("{:?}", header),
39/// PacketTooBig { mtu } => println!("TimeExceeded{{ mtu: {} }}", mtu),
40/// TimeExceeded(code) => println!("{:?}", code),
41/// ParameterProblem(header) => println!("{:?}", header),
42/// EchoRequest(header) => println!("{:?}", header),
43/// EchoReply(header) => println!("{:?}", header),
44/// RouterSolicitation => println!("RouterSolicitation"),
45/// RouterAdvertisement(header) => println!("{:?}", header),
46/// NeighborSolicitation => println!("NeighborSolicitation"),
47/// NeighborAdvertisement(header) => println!("{:?}", header),
48/// Redirect => println!("Redirect"),
49/// }
50/// },
51/// _ => {},
52/// }
53/// ```
54///
55/// # Encoding Example (only ICMPv6 part)
56///
57/// To get the on wire bytes of an Icmpv6Type it needs to get packaged
58/// into a [`Icmpv6Header`] so the checksum gets calculated.
59///
60/// ```
61/// # use etherparse::Ipv6Header;
62/// # let ip_header: Ipv6Header = Default::default();
63/// # let invoking_packet : [u8;0] = [];
64///
65/// use etherparse::{Icmpv6Type, icmpv6::DestUnreachableCode};
66/// let t = Icmpv6Type::DestinationUnreachable(
67/// DestUnreachableCode::Address
68/// );
69///
70/// // to calculate the checksum the ip header and the payload
71/// // (in case of dest unreachable the invoking packet) are needed
72/// let header = t.to_header(ip_header.source, ip_header.destination, &invoking_packet).unwrap();
73///
74/// // an ICMPv6 packet is composed of the header and payload
75/// let mut packet = Vec::with_capacity(header.header_len() + invoking_packet.len());
76/// packet.extend_from_slice(&header.to_bytes());
77/// packet.extend_from_slice(&invoking_packet);
78/// #
79/// # {
80/// # let checksum_be = header.checksum.to_be_bytes();
81/// # assert_eq!(
82/// # &packet,
83/// # &[
84/// # header.icmp_type.type_u8(),
85/// # header.icmp_type.code_u8(),
86/// # checksum_be[0],
87/// # checksum_be[1],
88/// # 0,0,0,0
89/// # ]
90/// # );
91/// # }
92/// ```
93#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
94pub enum Icmpv6Type {
95 /// In case of an unknown icmp type is received the header elements of
96 /// the first 8 bytes/octets are stored raw in this enum value.
97 ///
98 /// # What is part of the header for `Icmpv6Type::Unknown`?
99 ///
100 /// For unknown ICMPv6 type & code combination the first 8 bytes are stored
101 /// in the [`Icmpv6Header`] and the rest is stored in the payload
102 /// ([`Icmpv6Slice::payload`] or [`PacketHeaders::payload`]).
103 ///
104 ///
105 /// ```text
106 /// 0 1 2 3 4
107 /// +---------------------------------------------------------------+ -
108 /// | type_u8 | code_u8 | checksum (in Icmpv6Header) | |
109 /// +---------------------------------------------------------------+ | part of header & type
110 /// | bytes5to8 | ↓
111 /// +---------------------------------------------------------------+ -
112 /// | | |
113 /// ... ... ... | part of payload
114 /// | | ↓
115 /// +---------------------------------------------------------------+ -
116 /// ```
117 Unknown {
118 /// ICMPv6 type (present in the first byte of the ICMPv6 packet).
119 type_u8: u8,
120 /// ICMPv6 code (present in the 2nd byte of the ICMPv6 packet).
121 code_u8: u8,
122 /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet.
123 bytes5to8: [u8; 4],
124 },
125
126 /// Message sent to inform the client that the destination is unreachable for
127 /// some reason.
128 ///
129 /// # What is part of the header for `Icmpv6Type::DestinationUnreachable`?
130 ///
131 /// For the `Icmpv6Type::DestinationUnreachable` type the first 8 bytes/octets of the ICMPv6
132 /// packet are part of the header. The `unused` part is not stored and droped.
133 /// The offending packet is stored in the payload part of the packet
134 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
135 /// the [`Icmpv6Header`].
136 ///
137 /// ```text
138 /// 0 1 2 3 4
139 /// +---------------------------------------------------------------+ -
140 /// | 1 | [value as u8] | checksum (in Icmpv6Header) | |
141 /// +---------------------------------------------------------------+ | part of header & type
142 /// | <unused> | ↓
143 /// +---------------------------------------------------------------+ -
144 /// | | |
145 /// | <As much of invoking packet as possible without | | part of payload
146 /// ... the ICMPv6 packet exceeding the minimum IPv6 MTU> ... |
147 /// | | ↓
148 /// +---------------------------------------------------------------+ -
149 /// ```
150 ///
151 /// # RFC 4443 Description
152 ///
153 /// A Destination Unreachable message SHOULD be generated by a router, or
154 /// by the IPv6 layer in the originating node, in response to a packet
155 /// that cannot be delivered to its destination address for reasons other
156 /// than congestion. (An ICMPv6 message MUST NOT be generated if a
157 /// packet is dropped due to congestion.)
158 DestinationUnreachable(icmpv6::DestUnreachableCode),
159
160 /// Sent if a packet to too big to be forwarded.
161 ///
162 /// # What is part of the header for `Icmpv6Type::PacketTooBig`?
163 ///
164 /// For the `Icmpv6Type::PacketTooBig` type the first 8 bytes/octets of the ICMPv6
165 /// packet are part of the header. The offending packet is stored in the payload part of the packet
166 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
167 /// the [`Icmpv6Header`].
168 ///
169 /// ```text
170 /// 0 1 2 3 4
171 /// +---------------------------------------------------------------+ -
172 /// | 2 | 0 | checksum (in Icmpv6Header) | |
173 /// +---------------------------------------------------------------+ | part of header & type
174 /// | mtu | ↓
175 /// +---------------------------------------------------------------+ -
176 /// | | |
177 /// | <As much of invoking packet as possible without | | part of payload
178 /// ... the ICMPv6 packet exceeding the minimum IPv6 MTU> ... |
179 /// | | ↓
180 /// +---------------------------------------------------------------+ -
181 /// ```
182 ///
183 /// # RFC 4443 Description
184 ///
185 /// A Packet Too Big MUST be sent by a router in response to a packet
186 /// that it cannot forward because the packet is larger than the MTU of
187 /// the outgoing link. The information in this message is used as part
188 /// of the Path MTU Discovery process.
189 PacketTooBig {
190 /// The Maximum Transmission Unit of the next-hop link.
191 mtu: u32,
192 },
193
194 /// Generated when a datagram had to be discarded due to the hop limit field
195 /// reaching zero.
196 ///
197 /// # What is part of the header for `Icmpv6Type::TimeExceeded`?
198 ///
199 /// For the `Icmpv6Type::TimeExceeded` type the first 8 bytes/octets of the ICMPv6
200 /// packet are part of the header. The `unused` part is not stored and droped.
201 /// The offending packet is stored in the payload part of the packet
202 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
203 /// the [`Icmpv6Header`].
204 ///
205 /// ```text
206 /// 0 1 2 3 4
207 /// +---------------------------------------------------------------+ -
208 /// | 3 | [value as u8] | checksum (in Icmpv6Header) | |
209 /// +---------------------------------------------------------------+ | part of header & type
210 /// | <unused> | ↓
211 /// +---------------------------------------------------------------+ -
212 /// | | |
213 /// | <As much of invoking packet as possible without | | part of payload
214 /// ... the ICMPv6 packet exceeding the minimum IPv6 MTU> ... |
215 /// | | ↓
216 /// +---------------------------------------------------------------+ -
217 /// ```
218 ///
219 /// # RFC 4443 Description
220 ///
221 /// If a router receives a packet with a Hop Limit of zero, or if a
222 /// router decrements a packet's Hop Limit to zero, it MUST discard the
223 /// packet and originate an ICMPv6 Time Exceeded message with Code 0 to
224 /// the source of the packet. This indicates either a routing loop or
225 /// too small an initial Hop Limit value.
226 ///
227 /// An ICMPv6 Time Exceeded message with Code 1 is used to report
228 /// fragment reassembly timeout, as specified in [IPv6, Section 4.5].
229 TimeExceeded(icmpv6::TimeExceededCode),
230
231 /// Sent if there is a problem with a parameter in a received packet.
232 ///
233 /// # What is part of the header for `Icmpv6Type::ParameterProblem`?
234 ///
235 /// For the `Icmpv6Type::ParameterProblem` type the first 8 bytes/octets of the ICMPv6
236 /// packet are part of the header. The `unused` part is not stored and droped.
237 /// The offending packet is stored in the payload part of the packet
238 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
239 /// the [`Icmpv6Header`].
240 ///
241 /// ```text
242 /// 0 1 2 3 4
243 /// +---------------------------------------------------------------+ -
244 /// | 4 | [value].code | checksum (in Icmpv6Header) | |
245 /// +---------------------------------------------------------------+ | part of header & type
246 /// | [value].pointer | ↓
247 /// +---------------------------------------------------------------+ -
248 /// | | |
249 /// | <As much of invoking packet as possible without | | part of payload
250 /// ... the ICMPv6 packet exceeding the minimum IPv6 MTU> ... |
251 /// | | ↓
252 /// +---------------------------------------------------------------+ -
253 /// ```
254 ///
255 /// # RFC 4443 Description
256 ///
257 /// If an IPv6 node processing a packet finds a problem with a field in
258 /// the IPv6 header or extension headers such that it cannot complete
259 /// processing the packet, it MUST discard the packet and SHOULD
260 /// originate an ICMPv6 Parameter Problem message to the packet's source,
261 /// indicating the type and location of the problem.
262 ParameterProblem(icmpv6::ParameterProblemHeader),
263
264 /// Requesting an `EchoReply` from the receiver.
265 ///
266 /// # What is part of the header for `Icmpv6Type::EchoRequest`?
267 ///
268 /// For the [`Icmpv6Type::EchoRequest`] type the first 8 bytes/octets of the
269 /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
270 /// fields. The data part of the ICMP echo request packet is part of the payload
271 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
272 /// [`Icmpv6Header`].
273 ///
274 /// ```text
275 /// 0 1 2 3 4
276 /// +---------------------------------------------------------------+ -
277 /// | 128 | 0 | checksum (in Icmpv6Header) | |
278 /// +---------------------------------------------------------------+ | part of header & type
279 /// | [value].id | [value].seq | ↓
280 /// +---------------------------------------------------------------+ -
281 /// | | |
282 /// ... <data> ... | part of payload
283 /// | | ↓
284 /// +---------------------------------------------------------------+ -
285 /// ```
286 ///
287 /// # RFC 4443 Description
288 ///
289 /// Every node MUST implement an ICMPv6 Echo responder function that
290 /// receives Echo Requests and originates corresponding Echo Replies. A
291 /// node SHOULD also implement an application-layer interface for
292 /// originating Echo Requests and receiving Echo Replies, for diagnostic
293 /// purposes.
294 EchoRequest(IcmpEchoHeader),
295 /// Response to an `EchoRequest` message.
296 ///
297 /// # What is part of the header for `Icmpv6Type::EchoReply`?
298 ///
299 /// For the [`Icmpv6Type::EchoReply`] type the first 8 bytes/octets of the
300 /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
301 /// fields. The data part of the ICMP echo request packet is part of the payload
302 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
303 /// [`Icmpv6Header`].
304 ///
305 /// ```text
306 /// 0 1 2 3 4
307 /// +---------------------------------------------------------------+ -
308 /// | 129 | 0 | checksum (in Icmpv6Header) | |
309 /// +---------------------------------------------------------------+ | part of header & type
310 /// | [value].id | [value].seq | ↓
311 /// +---------------------------------------------------------------+ -
312 /// | | |
313 /// ... <data> ... | part of payload
314 /// | | ↓
315 /// +---------------------------------------------------------------+ -
316 /// ```
317 ///
318 /// # RFC 4443 Description
319 ///
320 /// Every node MUST implement an ICMPv6 Echo responder function that
321 /// receives Echo Requests and originates corresponding Echo Replies. A
322 /// node SHOULD also implement an application-layer interface for
323 /// originating Echo Requests and receiving Echo Replies, for diagnostic
324 /// purposes.
325 ///
326 /// The source address of an Echo Reply sent in response to a unicast
327 /// Echo Request message MUST be the same as the destination address of
328 /// that Echo Request message.
329 ///
330 /// An Echo Reply SHOULD be sent in response to an Echo Request message
331 /// sent to an IPv6 multicast or anycast address. In this case, the
332 /// source address of the reply MUST be a unicast address belonging to
333 /// the interface on which the Echo Request message was received.
334 ///
335 /// The data received in the ICMPv6 Echo Request message MUST be returned
336 /// entirely and unmodified in the ICMPv6 Echo Reply message.
337 EchoReply(IcmpEchoHeader),
338
339 /// Router Solicitation message header (part of "Neighbor Discovery Protocol"
340 /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
341 ///
342 /// # What is part of the header for `Icmpv6Type::RouterSolicitation`?
343 ///
344 /// For the [`Icmpv6Type::RouterSolicitation`] type the first 8 bytes/octets
345 /// of the ICMPv6 packet are part of the header.
346 ///
347 /// The options part of the ICMP Router Solicitation packet is part of the payload
348 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
349 /// [`Icmpv6Header`].
350 /// ```text
351 /// 0 1 2 3 4
352 /// +---------------------------------------------------------------+ -
353 /// | 133 | 0 | checksum (in Icmpv6Header) | |
354 /// +---------------------------------------------------------------+ | part of header & type
355 /// | <unused> | ↓
356 /// +---------------------------------------------------------------+ -
357 /// | Options ... | | part of payload
358 /// | | ↓
359 /// +----------------- -
360 /// ```
361 ///
362 /// # RFC 4861 Description
363 ///
364 /// Hosts send Router Solicitations in order to prompt routers to
365 /// generate Router Advertisements quickly.
366 RouterSolicitation,
367
368 /// Router Advertisement message header (part of "Neighbor Discovery Protocol"
369 /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
370 ///
371 /// # What is part of the header for `Icmpv6Type::RouterAdvertisement`?
372 ///
373 /// For the [`Icmpv6Type::RouterAdvertisement`] type the first 8 bytes/octets
374 /// of the ICMPv6 packet are part of the header.
375 ///
376 /// The options part of the ICMP Router Advertisement packet is part of the payload
377 /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
378 /// [`Icmpv6Header`].
379 /// ```text
380 /// 0 1 2 3 4
381 /// +---------------------------------------------------------------+ -
382 /// | 134 | 0 | checksum (in Icmpv6Header) | |
383 /// +---------------------------------------------------------------+ | part of header & type
384 /// | Cur Hop Limit |M|O| Reserved | Router Lifetime | ↓
385 /// +---------------------------------------------------------------+ -
386 /// | Reachable Time | |
387 /// +---------------------------------------------------------------+ |
388 /// | Retrans Timer | | part of payload
389 /// +---------------------------------------------------------------+ |
390 /// | Options ... | ↓
391 /// +----------------- -
392 /// ```
393 ///
394 /// # RFC 4861 Description
395 ///
396 /// Routers send out Router Advertisement messages periodically, or in
397 /// response to Router Solicitations.
398 RouterAdvertisement(RouterAdvertisementHeader),
399
400 /// Requesting the link-layer address of a target node (part of "Neighbor Discovery Protocol"
401 /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
402 ///
403 /// # What is part of the header for `Icmpv6Type::NeighborSolicitation`?
404 ///
405 /// For the [`Icmpv6Type::NeighborSolicitation`] type the first 8 bytes/octets
406 /// of the ICMPv6 packet are part of the header.
407 ///
408 /// The target address & options of the ICMP Neighbor Solicitation packet is part
409 /// of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not
410 /// part of the [`Icmpv6Header`].
411 /// ```text
412 /// 0 1 2 3 4
413 /// +---------------------------------------------------------------+ -
414 /// | 135 | 0 | checksum (in Icmpv6Header) | |
415 /// +---------------------------------------------------------------+ | part of header & type
416 /// | <unused> | ↓
417 /// +---------------------------------------------------------------+ -
418 /// | | |
419 /// | Target Address | |
420 /// | | | part of payload
421 /// +---------------------------------------------------------------+ |
422 /// | Options ... ↓
423 /// +----------------- -
424 /// ```
425 ///
426 /// # RFC 4861 Description
427 ///
428 /// Nodes send Neighbor Solicitations to request the link-layer address
429 /// of a target node while also providing their own link-layer address to
430 /// the target. Neighbor Solicitations are multicast when the node needs
431 /// to resolve an address and unicast when the node seeks to verify the
432 /// reachability of a neighbor.
433 NeighborSolicitation,
434
435 /// Header of "Neighbor Advertisement" message (part of "Neighbor Discovery Protocol"
436 /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
437 ///
438 /// # What is part of the header for `Icmpv6Type::NeighborAdvertisement`?
439 ///
440 /// For the [`Icmpv6Type::NeighborAdvertisement`] type the first 8 bytes/octets
441 /// of the ICMPv6 packet are part of the header. This includes 3 bits for
442 /// - router
443 /// - solicited
444 /// - override
445 ///
446 /// The target address & options of the ICMP Neighbor Advertisement packet is part
447 /// of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not
448 /// part of the [`Icmpv6Header`].
449 ///
450 /// ```text
451 /// 0 1 2 3 4
452 /// +---------------------------------------------------------------+ -
453 /// | 136 | 0 | checksum (in Icmpv6Header) | |
454 /// +---------------------------------------------------------------+ | part of header & type
455 /// |R|S|O| <unused> | ↓
456 /// +---------------------------------------------------------------+ -
457 /// | | |
458 /// | Target Address | |
459 /// | | | part of payload
460 /// +---------------------------------------------------------------+ |
461 /// | Options ... ↓
462 /// +----------------- -
463 /// ```
464 ///
465 /// # RFC 4861 Description
466 ///
467 /// A node sends Neighbor Advertisements in response to Neighbor
468 /// Solicitations and sends unsolicited Neighbor Advertisements in order
469 /// to (unreliably) propagate new information quickly.
470 ///
471 /// R Router flag. When set, the R-bit indicates that
472 /// the sender is a router. The R-bit is used by
473 /// Neighbor Unreachability Detection to detect a
474 /// router that changes to a host.
475 ///
476 /// S Solicited flag. When set, the S-bit indicates that
477 /// the advertisement was sent in response to a
478 /// Neighbor Solicitation from the Destination address.
479 /// The S-bit is used as a reachability confirmation
480 /// for Neighbor Unreachability Detection. It MUST NOT
481 /// be set in multicast advertisements or in
482 /// unsolicited unicast advertisements.
483 ///
484 /// O Override flag. When set, the O-bit indicates that
485 /// the advertisement should override an existing cache
486 /// entry and update the cached link-layer address.
487 /// When it is not set the advertisement will not
488 /// update a cached link-layer address though it will
489 /// update an existing Neighbor Cache entry for which
490 /// no link-layer address is known. It SHOULD NOT be
491 /// set in solicited advertisements for anycast
492 /// addresses and in solicited proxy advertisements.
493 /// It SHOULD be set in other solicited advertisements
494 /// and in unsolicited advertisements.
495 NeighborAdvertisement(icmpv6::NeighborAdvertisementHeader),
496
497 /// Header of "Redirect" message (part of "Neighbor Discovery Protocol"
498 /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
499 ///
500 /// # What is part of the header for `Icmpv6Type::Redirect`?
501 ///
502 /// For the [`Icmpv6Type::Redirect`] type the first 8 bytes/octets
503 /// of the ICMPv6 packet are part of the header.
504 ///
505 /// The "target address", "destination address" & options part of the ICMP Redirect
506 /// packet is part of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`])
507 /// and not part of the [`Icmpv6Header`].
508 /// ```text
509 /// 0 1 2 3 4
510 /// +---------------------------------------------------------------+ -
511 /// | 137 | 0 | checksum (in Icmpv6Header) | |
512 /// +---------------------------------------------------------------+ | part of header & type
513 /// | <unused> | ↓
514 /// +---------------------------------------------------------------+ -
515 /// | | |
516 /// | Target Address | |
517 /// | | |
518 /// +---------------------------------------------------------------+ |
519 /// | | | part of payload
520 /// | Destination Address | |
521 /// | | |
522 /// +---------------------------------------------------------------+ |
523 /// | Options ... ↓
524 /// +----------------- - -
525 /// ```
526 ///
527 /// # RFC 4861 Description
528 ///
529 /// Routers send Redirect packets to inform a host of a better first-hop
530 /// node on the path to a destination. Hosts can be redirected to a
531 /// better first-hop router but can also be informed by a redirect that
532 /// the destination is in fact a neighbor. The latter is accomplished by
533 /// setting the ICMP Target Address equal to the ICMP Destination
534 /// Address.
535 Redirect,
536}
537
538impl Icmpv6Type {
539 /// Returns the type value (first byte of the ICMPv6 header) of this type.
540 #[inline]
541 pub fn type_u8(&self) -> u8 {
542 use crate::{icmpv6::*, Icmpv6Type::*};
543 match self {
544 Unknown {
545 type_u8,
546 code_u8: _,
547 bytes5to8: _,
548 } => *type_u8,
549 DestinationUnreachable(_) => TYPE_DST_UNREACH,
550 PacketTooBig { mtu: _ } => TYPE_PACKET_TOO_BIG,
551 TimeExceeded(_) => TYPE_TIME_EXCEEDED,
552 ParameterProblem(_) => TYPE_PARAMETER_PROBLEM,
553 EchoRequest(_) => TYPE_ECHO_REQUEST,
554 EchoReply(_) => TYPE_ECHO_REPLY,
555 RouterSolicitation => TYPE_ROUTER_SOLICITATION,
556 RouterAdvertisement(_) => TYPE_ROUTER_ADVERTISEMENT,
557 NeighborSolicitation => TYPE_NEIGHBOR_SOLICITATION,
558 NeighborAdvertisement(_) => TYPE_NEIGHBOR_ADVERTISEMENT,
559 Redirect => TYPE_REDIRECT_MESSAGE,
560 }
561 }
562
563 /// Returns the code value (second byte of the ICMPv6 header) of this type.
564 #[inline]
565 pub fn code_u8(&self) -> u8 {
566 use Icmpv6Type::*;
567 match self {
568 Unknown {
569 type_u8: _,
570 code_u8,
571 bytes5to8: _,
572 } => *code_u8,
573 DestinationUnreachable(code) => code.code_u8(),
574 PacketTooBig { mtu: _ } => 0,
575 TimeExceeded(code) => code.code_u8(),
576 ParameterProblem(header) => header.code.code_u8(),
577 EchoRequest(_) => 0,
578 EchoReply(_) => 0,
579 RouterSolicitation => 0,
580 RouterAdvertisement(_) => 0,
581 NeighborSolicitation => 0,
582 NeighborAdvertisement(_) => 0,
583 Redirect => 0,
584 }
585 }
586
587 /// Calculates the checksum of the ICMPv6 header.
588 ///
589 /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
590 /// <strong>Warning:</strong> Don't use this method to verfy if a checksum of a
591 /// received packet is correct. This method assumes that all unused bytes are
592 /// filled with zeros. If this is not the case the computed checksum value will
593 /// will be incorrect for a received packet.
594 ///
595 /// If you want to verify that a received packet has a correct checksum use
596 /// [`Icmpv6Slice::is_checksum_valid`] instead.
597 /// </p>
598 pub fn calc_checksum(
599 &self,
600 source_ip: [u8; 16],
601 destination_ip: [u8; 16],
602 payload: &[u8],
603 ) -> Result<u16, ValueTooBigError<usize>> {
604 // check that the total length fits into the field
605 //
606 // Note according to RFC 2460 the "Upper-Layer Packet Length" used
607 // in the checksum calculation, for protocols that don't contain
608 // their own length information (like ICMPv6), is "the Payload Length
609 // from the IPv6 header, minus the length of any extension headers present
610 // between the IPv6 header and the upper-layer header."
611 let max_payload_len: usize = (u32::MAX as usize) - self.header_len();
612 if max_payload_len < payload.len() {
613 return Err(ValueTooBigError {
614 actual: payload.len(),
615 max_allowed: max_payload_len,
616 value_type: ValueType::Icmpv6PayloadLength,
617 });
618 }
619
620 let msg_len = payload.len() + self.header_len();
621
622 // calculate the checksum
623 // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
624 // unlike Icmp4
625 let pseudo_sum = checksum::Sum16BitWords::new()
626 .add_16bytes(source_ip)
627 .add_16bytes(destination_ip)
628 .add_2bytes([0, ip_number::IPV6_ICMP.0])
629 .add_4bytes((msg_len as u32).to_be_bytes());
630
631 use crate::{icmpv6::*, Icmpv6Type::*};
632 Ok(match self {
633 Unknown {
634 type_u8,
635 code_u8,
636 bytes5to8,
637 } => pseudo_sum
638 .add_2bytes([*type_u8, *code_u8])
639 .add_4bytes(*bytes5to8),
640 DestinationUnreachable(header) => {
641 pseudo_sum.add_2bytes([TYPE_DST_UNREACH, header.code_u8()])
642 }
643 PacketTooBig { mtu } => pseudo_sum
644 .add_2bytes([TYPE_PACKET_TOO_BIG, 0])
645 .add_4bytes(mtu.to_be_bytes()),
646 TimeExceeded(code) => pseudo_sum.add_2bytes([TYPE_TIME_EXCEEDED, code.code_u8()]),
647 ParameterProblem(header) => pseudo_sum
648 .add_2bytes([TYPE_PARAMETER_PROBLEM, header.code.code_u8()])
649 .add_4bytes(header.pointer.to_be_bytes()),
650 EchoRequest(echo) => pseudo_sum
651 .add_2bytes([TYPE_ECHO_REQUEST, 0])
652 .add_4bytes(echo.to_bytes()),
653 EchoReply(echo) => pseudo_sum
654 .add_2bytes([TYPE_ECHO_REPLY, 0])
655 .add_4bytes(echo.to_bytes()),
656 RouterSolicitation => pseudo_sum
657 .add_2bytes([TYPE_ROUTER_SOLICITATION, 0])
658 .add_4bytes([0; 4]),
659 RouterAdvertisement(header) => pseudo_sum
660 .add_2bytes([TYPE_ROUTER_ADVERTISEMENT, 0])
661 .add_4bytes(header.to_bytes()),
662 NeighborSolicitation => pseudo_sum
663 .add_2bytes([TYPE_NEIGHBOR_SOLICITATION, 0])
664 .add_4bytes([0; 4]),
665 NeighborAdvertisement(header) => pseudo_sum
666 .add_2bytes([TYPE_NEIGHBOR_ADVERTISEMENT, 0])
667 .add_4bytes(header.to_bytes()),
668 Redirect => pseudo_sum
669 .add_2bytes([TYPE_REDIRECT_MESSAGE, 0])
670 .add_4bytes([0; 4]),
671 }
672 .add_slice(payload)
673 .ones_complement()
674 .to_be())
675 }
676
677 /// Creates a header with the correct checksum.
678 pub fn to_header(
679 self,
680 source_ip: [u8; 16],
681 destination_ip: [u8; 16],
682 payload: &[u8],
683 ) -> Result<Icmpv6Header, ValueTooBigError<usize>> {
684 Ok(Icmpv6Header {
685 checksum: self.calc_checksum(source_ip, destination_ip, payload)?,
686 icmp_type: self,
687 })
688 }
689
690 /// Decode a structured payload based on this ICMPv6 type.
691 #[inline]
692 pub fn payload_slice<'a>(
693 &self,
694 payload: &'a [u8],
695 ) -> Result<icmpv6::Icmpv6PayloadSlice<'a>, err::LenError> {
696 icmpv6::Icmpv6PayloadSlice::from_slice(self, payload)
697 }
698
699 /// Convert a structured payload slice into an owned payload if supported.
700 ///
701 /// The returned [`icmpv6::Icmpv6Payload`] variants contain only fixed-part
702 /// message fields (for example `target_address`) and intentionally exclude
703 /// NDP options. For payload types with variable trailing data, the second
704 /// tuple element contains the remaining bytes that must be parsed
705 /// separately (for example via [`icmpv6::Icmpv6PayloadSlice`] option
706 /// accessors/iterators).
707 #[inline]
708 pub fn payload_from_slice<'a>(
709 &self,
710 payload: &'a [u8],
711 ) -> Result<Option<(icmpv6::Icmpv6Payload, &'a [u8])>, err::LenError> {
712 self.payload_slice(payload).map(|value| value.to_payload())
713 }
714
715 /// Serialized length of the header in bytes/octets.
716 ///
717 /// Note that this size is not the size of the entire
718 /// ICMPv6 packet but only the header.
719 pub fn header_len(&self) -> usize {
720 use Icmpv6Type::*;
721 match self {
722 Unknown {
723 type_u8: _,
724 code_u8: _,
725 bytes5to8: _,
726 }
727 | DestinationUnreachable(_)
728 | PacketTooBig { mtu: _ }
729 | TimeExceeded(_)
730 | ParameterProblem(_)
731 | EchoRequest(_)
732 | EchoReply(_)
733 | RouterSolicitation
734 | RouterAdvertisement(_)
735 | NeighborSolicitation
736 | NeighborAdvertisement(_)
737 | Redirect => 8,
738 }
739 }
740
741 /// If the ICMP type has a fixed size returns the number of
742 /// bytes that should be present after the header of this type.
743 #[inline]
744 pub fn fixed_payload_size(&self) -> Option<usize> {
745 use Icmpv6Type::*;
746 match self {
747 Unknown {
748 type_u8: _,
749 code_u8: _,
750 bytes5to8: _,
751 }
752 | DestinationUnreachable(_)
753 | PacketTooBig { mtu: _ }
754 | TimeExceeded(_)
755 | ParameterProblem(_)
756 | EchoRequest(_)
757 | EchoReply(_)
758 | RouterSolicitation
759 | RouterAdvertisement(_)
760 | NeighborSolicitation
761 | NeighborAdvertisement(_)
762 | Redirect => None,
763 }
764 }
765}
766
767#[cfg(test)]
768mod test {
769 use crate::{
770 err::{ValueTooBigError, ValueType},
771 icmpv6::*,
772 test_gens::*,
773 Icmpv6Type::*,
774 *,
775 };
776 use alloc::format;
777 use proptest::prelude::*;
778
779 proptest! {
780 #[test]
781 fn type_u8(
782 code_u8 in any::<u8>(),
783 bytes5to8 in any::<[u8;4]>(),
784 ) {
785 {
786 let type_u8_type_pair = [
787 (TYPE_DST_UNREACH, DestinationUnreachable(DestUnreachableCode::SourceAddressFailedPolicy)),
788 (TYPE_PACKET_TOO_BIG, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }),
789 (TYPE_TIME_EXCEEDED, TimeExceeded(TimeExceededCode::HopLimitExceeded)),
790 (TYPE_PARAMETER_PROBLEM, ParameterProblem(ParameterProblemHeader{ code: ParameterProblemCode::UnrecognizedNextHeader, pointer: u32::from_be_bytes(bytes5to8)})),
791 (TYPE_ECHO_REQUEST, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))),
792 (TYPE_ECHO_REPLY, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))),
793 (TYPE_ROUTER_SOLICITATION, RouterSolicitation),
794 (TYPE_ROUTER_ADVERTISEMENT, RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8))),
795 (TYPE_NEIGHBOR_SOLICITATION, NeighborSolicitation),
796 (TYPE_NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8))),
797 (TYPE_REDIRECT_MESSAGE, Redirect),
798 ];
799 for test in type_u8_type_pair {
800 assert_eq!(test.0, test.1.type_u8());
801 }
802 }
803
804 for t in 0..=u8::MAX {
805 assert_eq!(
806 t,
807 Unknown{
808 type_u8: t,
809 code_u8,
810 bytes5to8,
811 }.type_u8()
812 );
813 }
814 }
815 }
816
817 proptest! {
818 #[test]
819 fn code_u8(
820 code_u8 in any::<u8>(),
821 bytes5to8 in any::<[u8;4]>(),
822 ) {
823 // types with 0 as code
824 {
825 let code_type_pair = [
826 (0, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }),
827 (0, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))),
828 (0, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))),
829 (0, RouterSolicitation),
830 (0, RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8))),
831 (0, NeighborSolicitation),
832 (0, NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8))),
833 (0, Redirect),
834 ];
835 for test in code_type_pair {
836 assert_eq!(test.0, test.1.code_u8());
837 }
838 }
839
840 // destination unreachable
841 for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
842 assert_eq!(code_u8, DestinationUnreachable(code).code_u8());
843 }
844
845 // time exceeded
846 for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
847 assert_eq!(code_u8, TimeExceeded(code).code_u8());
848 }
849
850 // parameter problem
851 for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
852 assert_eq!(
853 code_u8,
854 ParameterProblem(
855 ParameterProblemHeader{
856 code,
857 pointer: u32::from_be_bytes(bytes5to8),
858 }
859 ).code_u8()
860 );
861 }
862
863 // unknown
864 for t in 0..=u8::MAX {
865 assert_eq!(
866 code_u8,
867 Unknown{
868 type_u8: t,
869 code_u8,
870 bytes5to8,
871 }.code_u8()
872 );
873 }
874 }
875 }
876
877 proptest! {
878 #[test]
879 #[cfg(not(target_pointer_width = "32"))]
880 fn calc_checksum(
881 ip_header in ipv6_any(),
882 icmpv6_type in icmpv6_type_any(),
883 type_u8 in any::<u8>(),
884 code_u8 in any::<u8>(),
885 bytes5to8 in any::<[u8;4]>(),
886 // max length is u32::MAX - header_len (7)
887 bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
888 payload in proptest::collection::vec(any::<u8>(), 0..64)
889 ) {
890 use Icmpv6Type::*;
891
892 // size error case
893 {
894 // SAFETY: In case the error is not triggered
895 // a segmentation fault will be triggered.
896 let too_big_slice = unsafe {
897 //NOTE: The pointer must be initialized with a non null value
898 // otherwise a key constraint of slices is not fulfilled
899 // which can lead to crashes in release mode.
900 use core::ptr::NonNull;
901 core::slice::from_raw_parts(
902 NonNull::<u8>::dangling().as_ptr(),
903 bad_len
904 )
905 };
906 assert_eq!(
907 icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, too_big_slice),
908 Err(ValueTooBigError{
909 actual: bad_len,
910 max_allowed: (core::u32::MAX - 8) as usize,
911 value_type: ValueType::Icmpv6PayloadLength
912 })
913 );
914 }
915
916 // normal cases
917 {
918 let test_checksum_calc = |icmp_type: Icmpv6Type| {
919 let expected_checksum = {
920 crate::checksum::Sum16BitWords::new()
921 .add_16bytes(ip_header.source)
922 .add_16bytes(ip_header.destination)
923 .add_2bytes([0, ip_number::IPV6_ICMP.0])
924 .add_4bytes((
925 payload.len() as u32 + icmpv6_type.header_len() as u32
926 ).to_be_bytes())
927 .add_slice(&Icmpv6Header {
928 icmp_type: icmp_type.clone(),
929 checksum: 0 // use zero so the checksum gets correct calculated
930 }.to_bytes())
931 .add_slice(&payload)
932 .ones_complement()
933 .to_be()
934 };
935 assert_eq!(
936 expected_checksum,
937 icmp_type.calc_checksum(
938 ip_header.source,
939 ip_header.destination,
940 &payload
941 ).unwrap()
942 );
943 };
944
945 // unknown
946 test_checksum_calc(
947 Unknown{
948 type_u8, code_u8, bytes5to8
949 }
950 );
951
952 // destination unreachable
953 for (code, _) in dest_unreachable_code_test_consts::VALID_VALUES {
954 test_checksum_calc(DestinationUnreachable(code));
955 }
956
957 // packet too big
958 test_checksum_calc(PacketTooBig{
959 mtu: u32::from_be_bytes(bytes5to8)
960 });
961
962 // time exceeded
963 for (code, _) in time_exceeded_code_test_consts::VALID_VALUES {
964 test_checksum_calc(TimeExceeded(code));
965 }
966
967 // parameter problem
968 for (code, _) in parameter_problem_code_test_consts::VALID_VALUES {
969 test_checksum_calc(ParameterProblem(
970 ParameterProblemHeader{
971 code,
972 pointer: u32::from_be_bytes(bytes5to8)
973 }
974 ));
975 }
976
977 // echo request
978 test_checksum_calc(EchoRequest(
979 IcmpEchoHeader::from_bytes(bytes5to8)
980 ));
981
982 // echo reply
983 test_checksum_calc(EchoReply(
984 IcmpEchoHeader::from_bytes(bytes5to8)
985 ));
986
987 // router solicitation
988 test_checksum_calc(RouterSolicitation);
989
990 // router advertisement
991 test_checksum_calc(RouterAdvertisement(
992 RouterAdvertisementHeader::from_bytes(bytes5to8)
993 ));
994
995 // neighbor solicitation
996 test_checksum_calc(NeighborSolicitation);
997
998 // neighbor advertisement
999 test_checksum_calc(NeighborAdvertisement(
1000 NeighborAdvertisementHeader::from_bytes(bytes5to8)
1001 ));
1002
1003 // redirect
1004 test_checksum_calc(Redirect);
1005 }
1006 }
1007 }
1008
1009 proptest! {
1010 #[test]
1011 #[cfg(not(target_pointer_width = "32"))]
1012 fn to_header(
1013 ip_header in ipv6_any(),
1014 icmpv6_type in icmpv6_type_any(),
1015 // max length is u32::MAX - header_len (7)
1016 bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
1017 payload in proptest::collection::vec(any::<u8>(), 0..1024)
1018 ) {
1019 // size error case
1020 {
1021 // SAFETY: In case the error is not triggered
1022 // a segmentation fault will be triggered.
1023 let too_big_slice = unsafe {
1024 //NOTE: The pointer must be initialized with a non null value
1025 // otherwise a key constraint of slices is not fulfilled
1026 // which can lead to crashes in release mode.
1027 use core::ptr::NonNull;
1028 core::slice::from_raw_parts(
1029 NonNull::<u8>::dangling().as_ptr(),
1030 bad_len
1031 )
1032 };
1033 assert_eq!(
1034 icmpv6_type.to_header(ip_header.source, ip_header.destination, too_big_slice),
1035 Err(ValueTooBigError{
1036 actual: bad_len,
1037 max_allowed: (core::u32::MAX - 8) as usize,
1038 value_type: ValueType::Icmpv6PayloadLength,
1039 })
1040 );
1041 }
1042 // normal case
1043 assert_eq!(
1044 icmpv6_type.to_header(ip_header.source, ip_header.destination, &payload).unwrap(),
1045 Icmpv6Header {
1046 checksum: icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
1047 icmp_type: icmpv6_type,
1048 }
1049 );
1050 }
1051 }
1052
1053 proptest! {
1054 #[test]
1055 fn header_len(
1056 code_u8 in any::<u8>(),
1057 bytes5to8 in any::<[u8;4]>(),
1058 ) {
1059 let len_8_hdrs = [
1060 DestinationUnreachable(DestUnreachableCode::Prohibited),
1061 PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
1062 TimeExceeded(TimeExceededCode::FragmentReassemblyTimeExceeded),
1063 ParameterProblem(ParameterProblemHeader{
1064 code: ParameterProblemCode::UnrecognizedIpv6Option,
1065 pointer: u32::from_be_bytes(bytes5to8),
1066 }),
1067 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
1068 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
1069 RouterSolicitation,
1070 RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8)),
1071 NeighborSolicitation,
1072 NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)),
1073 Redirect,
1074 ];
1075
1076 for hdr in len_8_hdrs {
1077 assert_eq!(8, hdr.header_len());
1078 }
1079
1080 for t in 0..=u8::MAX {
1081 assert_eq!(
1082 8,
1083 Unknown{
1084 type_u8: t,
1085 code_u8,
1086 bytes5to8,
1087 }.header_len()
1088 );
1089 }
1090 }
1091 }
1092
1093 proptest! {
1094 #[test]
1095 fn fixed_payload_size(
1096 code_u8 in any::<u8>(),
1097 bytes5to8 in any::<[u8;4]>(),
1098 ) {
1099 let variable_payload_headers = [
1100 DestinationUnreachable(DestUnreachableCode::Prohibited),
1101 PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
1102 TimeExceeded(TimeExceededCode::HopLimitExceeded),
1103 ParameterProblem(ParameterProblemHeader{
1104 code: ParameterProblemCode::SrUpperLayerHeaderError,
1105 pointer: u32::from_be_bytes(bytes5to8),
1106 }),
1107 EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
1108 EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
1109 RouterSolicitation,
1110 RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8)),
1111 NeighborSolicitation,
1112 NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)),
1113 Redirect,
1114 ];
1115
1116 for hdr in variable_payload_headers {
1117 assert_eq!(None, hdr.fixed_payload_size());
1118 }
1119
1120 for t in 0..=u8::MAX {
1121 assert_eq!(
1122 None,
1123 Unknown{
1124 type_u8: t,
1125 code_u8,
1126 bytes5to8,
1127 }.fixed_payload_size()
1128 );
1129 }
1130 }
1131 }
1132
1133 #[test]
1134 fn payload_slice() {
1135 use crate::icmpv6::*;
1136
1137 assert_eq!(
1138 Icmpv6Type::RouterSolicitation
1139 .payload_slice(&[1, 1, 1, 2, 3, 4, 5, 6])
1140 .unwrap(),
1141 Icmpv6PayloadSlice::RouterSolicitation(
1142 RouterSolicitationPayloadSlice::from_slice(&[1, 1, 1, 2, 3, 4, 5, 6]).unwrap()
1143 )
1144 );
1145
1146 assert_eq!(
1147 Icmpv6Type::RouterAdvertisement(RouterAdvertisementHeader {
1148 cur_hop_limit: 0,
1149 managed_address_config: false,
1150 other_config: false,
1151 router_lifetime: 0,
1152 })
1153 .payload_slice(&[0, 0, 0, 1, 0, 0, 0, 2])
1154 .unwrap(),
1155 Icmpv6PayloadSlice::RouterAdvertisement(
1156 RouterAdvertisementPayloadSlice::from_slice(&[0, 0, 0, 1, 0, 0, 0, 2]).unwrap()
1157 )
1158 );
1159
1160 assert_eq!(
1161 Icmpv6Type::NeighborSolicitation
1162 .payload_from_slice(&[1; 16])
1163 .unwrap(),
1164 Some((
1165 Icmpv6Payload::NeighborSolicitation(NeighborSolicitationPayload {
1166 target_address: [1; 16].into()
1167 }),
1168 &[][..]
1169 ))
1170 );
1171
1172 assert_eq!(
1173 Icmpv6Type::NeighborSolicitation
1174 .payload_from_slice(&[1; 16 + 8])
1175 .unwrap(),
1176 Some((
1177 Icmpv6Payload::NeighborSolicitation(NeighborSolicitationPayload {
1178 target_address: [1; 16].into()
1179 }),
1180 &[1; 8][..]
1181 ))
1182 );
1183
1184 assert_eq!(
1185 Icmpv6Type::EchoRequest(IcmpEchoHeader { id: 1, seq: 2 })
1186 .payload_from_slice(&[1, 2, 3, 4])
1187 .unwrap(),
1188 None
1189 );
1190 }
1191
1192 #[test]
1193 fn debug() {
1194 assert_eq!(
1195 format!(
1196 "{:?}",
1197 Icmpv6Type::Unknown {
1198 type_u8: 0,
1199 code_u8: 1,
1200 bytes5to8: [2, 3, 4, 5]
1201 }
1202 ),
1203 "Unknown { type_u8: 0, code_u8: 1, bytes5to8: [2, 3, 4, 5] }"
1204 )
1205 }
1206
1207 proptest! {
1208 #[test]
1209 fn clone_eq(t in icmpv6_type_any()) {
1210 assert_eq!(t, t.clone());
1211 }
1212 }
1213}