libpacket/icmpv6.rs
1// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! An ICMPv6 packet abstraction.
10
11use crate::ip::IpNextHeaderProtocols;
12use crate::{types::*, util, Packet, PrimitiveValues};
13use std::net::Ipv6Addr;
14
15/// Represents the "ICMPv6 type" header field.
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Icmpv6Type(pub u8);
18
19impl Icmpv6Type {
20 /// Create a new `Icmpv6Type` instance.
21 pub fn new(val: u8) -> Icmpv6Type {
22 Icmpv6Type(val)
23 }
24}
25
26impl PrimitiveValues for Icmpv6Type {
27 type T = (u8,);
28 fn to_primitive_values(&self) -> (u8,) {
29 (self.0,)
30 }
31}
32
33/// Represents the "ICMPv6 code" header field.
34#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct Icmpv6Code(pub u8);
36
37impl Icmpv6Code {
38 /// Create a new `Icmpv6Code` instance.
39 pub fn new(val: u8) -> Icmpv6Code {
40 Icmpv6Code(val)
41 }
42}
43
44impl PrimitiveValues for Icmpv6Code {
45 type T = (u8,);
46 fn to_primitive_values(&self) -> (u8,) {
47 (self.0,)
48 }
49}
50
51/// Represents a generic ICMPv6 packet [RFC 4443 § 2.1]
52///
53/// ```text
54/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55/// | Type | Code | Checksum |
56/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57/// | |
58/// + Message Body +
59/// | |
60/// ```
61///
62/// [RFC 4443 § 2.1]: https://tools.ietf.org/html/rfc4443#section-2.1
63#[derive(Debug, Packet)]
64pub struct Icmpv6 {
65 #[construct_with(u8)]
66 pub icmpv6_type: Icmpv6Type,
67 #[construct_with(u8)]
68 pub icmpv6_code: Icmpv6Code,
69 pub checksum: u16be,
70 #[payload]
71 pub payload: Vec<u8>,
72}
73
74/// Calculates a checksum of an ICMPv6 packet.
75pub fn checksum(packet: &Icmpv6Packet, source: &Ipv6Addr, destination: &Ipv6Addr) -> u16be {
76 util::ipv6_checksum(
77 packet.packet(),
78 1,
79 &[],
80 source,
81 destination,
82 IpNextHeaderProtocols::Icmpv6,
83 )
84}
85
86#[cfg(test)]
87mod checksum_tests {
88 use super::*;
89
90 #[test]
91 fn checksum_echo_request() {
92 // The equivalent of your typical ping -6 ::1%lo
93 let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
94 let mut data = vec![
95 0x80, // Icmpv6 Type
96 0x00, // Code
97 0xff, 0xff, // Checksum
98 0x00, 0x00, // Id
99 0x00, 0x01, // Sequence
100 // 56 bytes of "random" data
101 0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
102 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
103 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
104 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
105 ];
106 let mut pkg = MutableIcmpv6Packet::new(&mut data[..]).unwrap();
107 assert_eq!(checksum(&pkg.to_immutable(), lo, lo), 0x1d2e);
108
109 // Check
110 pkg.set_icmpv6_type(Icmpv6Type(0x81));
111 assert_eq!(checksum(&pkg.to_immutable(), lo, lo), 0x1c2e);
112 }
113}
114
115/// The enumeration of the recognized ICMPv6 types.
116#[allow(non_snake_case)]
117#[allow(non_upper_case_globals)]
118pub mod Icmpv6Types {
119 use crate::icmpv6::Icmpv6Type;
120 /// ICMPv6 type for "destination unreachable".
121 pub const DestinationUnreachable: Icmpv6Type = Icmpv6Type(1);
122 /// ICMPv6 type for "packet too big".
123 pub const PacketTooBig: Icmpv6Type = Icmpv6Type(2);
124 /// ICMPv6 type for "time exceeded".
125 pub const TimeExceeded: Icmpv6Type = Icmpv6Type(3);
126 /// ICMPv6 type for "parameter problem".
127 pub const ParameterProblem: Icmpv6Type = Icmpv6Type(4);
128 /// ICMPv6 type for "echo request".
129 pub const EchoRequest: Icmpv6Type = Icmpv6Type(128);
130 /// ICMPv6 type for "echo reply".
131 pub const EchoReply: Icmpv6Type = Icmpv6Type(129);
132 // Neighbor Discovery Protocol [RFC4861]
133 /// ICMPv6 type for "router solicitation".
134 pub const RouterSolicit: Icmpv6Type = Icmpv6Type(133);
135 /// ICMPv6 type for "router advertisement".
136 pub const RouterAdvert: Icmpv6Type = Icmpv6Type(134);
137 /// ICMPv6 type for "neighbor solicitation".
138 pub const NeighborSolicit: Icmpv6Type = Icmpv6Type(135);
139 /// ICMPv6 type for "neighbor advertisement".
140 pub const NeighborAdvert: Icmpv6Type = Icmpv6Type(136);
141 /// ICMPv6 type for "redirect".
142 pub const Redirect: Icmpv6Type = Icmpv6Type(137);
143}
144
145pub mod ndp {
146 //! Abstractions for the Neighbor Discovery Protocol [RFC 4861]
147 //!
148 //! [RFC 4861]: https://tools.ietf.org/html/rfc4861
149
150 use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
151 use crate::{types::*, Packet, PrimitiveValues};
152 use std::net::Ipv6Addr;
153
154 #[allow(non_snake_case)]
155 #[allow(non_upper_case_globals)]
156 pub mod Icmpv6Codes {
157 use crate::icmpv6::Icmpv6Code;
158 /// 0 is the only available ICMPv6 Code for the NDP.
159 pub const NoCode: Icmpv6Code = Icmpv6Code(0);
160 }
161
162 /// Represents a Neighbor Discovery Option Type.
163 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
164 pub struct NdpOptionType(pub u8);
165
166 impl NdpOptionType {
167 /// Create a new `NdpOptionType` instance.
168 pub fn new(value: u8) -> NdpOptionType {
169 NdpOptionType(value)
170 }
171 }
172
173 impl PrimitiveValues for NdpOptionType {
174 type T = (u8,);
175 fn to_primitive_values(&self) -> (u8,) {
176 (self.0,)
177 }
178 }
179
180 /// Neighbor Discovery Option Types [RFC 4861 § 4.6]
181 ///
182 /// [RFC 4861 § 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
183 #[allow(non_snake_case)]
184 #[allow(non_upper_case_globals)]
185 pub mod NdpOptionTypes {
186 use super::NdpOptionType;
187
188 /// Source Link-Layer Address Option [RFC 4861 § 4.6.1]
189 ///
190 /// ```text
191 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192 /// | Type | Length | Link-Layer Address ...
193 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194 /// ```
195 ///
196 /// [RFC 4861 § 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
197 pub const SourceLLAddr: NdpOptionType = NdpOptionType(1);
198
199 /// Target Link-Layer Address Option [RFC 4861 § 4.6.1]
200 ///
201 /// ```text
202 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
203 /// | Type | Length | Link-Layer Address ...
204 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
205 /// ```
206 ///
207 /// [RFC 4861 § 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
208 pub const TargetLLAddr: NdpOptionType = NdpOptionType(2);
209
210 /// Prefix Information Option [RFC 4861 § 4.6.2]
211 ///
212 /// ```text
213 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214 /// | Type | Length | Prefix Length |L|A| Reserved1 |
215 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
216 /// | Valid Lifetime |
217 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
218 /// | Preferred Lifetime |
219 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
220 /// | Reserved2 |
221 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
222 /// | |
223 /// + +
224 /// | |
225 /// + Prefix +
226 /// | |
227 /// + +
228 /// | |
229 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
230 /// ```
231 ///
232 /// [RFC 4861 § 4.6.2]: https://tools.ietf.org/html/rfc4861#section-4.6.2
233 pub const PrefixInformation: NdpOptionType = NdpOptionType(3);
234
235 /// Redirected Header Option [RFC 4861 § 4.6.3]
236 ///
237 /// ```text
238 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
239 /// | Type | Length | Reserved |
240 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
241 /// | Reserved |
242 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
243 /// | |
244 /// ~ IP header + data ~
245 /// | |
246 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247 /// ```
248 ///
249 /// [RFC 4861 § 4.6.3]: https://tools.ietf.org/html/rfc4861#section-4.6.3
250 pub const RedirectedHeader: NdpOptionType = NdpOptionType(4);
251
252 /// MTU Option [RFC 4861 § 4.6.4]
253 ///
254 /// ```text
255 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
256 /// | Type | Length | Reserved |
257 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
258 /// | MTU |
259 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
260 /// ```
261 ///
262 /// [RFC 4861 § 4.6.4]: https://tools.ietf.org/html/rfc4861#section-4.6.4
263 pub const MTU: NdpOptionType = NdpOptionType(5);
264 }
265
266 /// Neighbor Discovery Option [RFC 4861 § 4.6]
267 ///
268 /// ```text
269 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
270 /// | Type | Length | ... |
271 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
272 /// ~ ... ~
273 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
274 /// ```
275 ///
276 /// [RFC 4861 § 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
277 #[derive(Debug, Packet)]
278 pub struct NdpOption {
279 #[construct_with(u8)]
280 pub option_type: NdpOptionType,
281 #[construct_with(u8)]
282 pub length: u8,
283 #[length = "(length * 8).saturating_sub(2)"]
284 #[payload]
285 pub data: Vec<u8>,
286 }
287
288 /// Router Solicitation Message [RFC 4861 § 4.1]
289 ///
290 /// ```text
291 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
292 /// | Type | Code | Checksum |
293 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
294 /// | Reserved |
295 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
296 /// | Options ...
297 /// ```
298 ///
299 /// [RFC 4861 § 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
300 #[derive(Debug, Packet)]
301 pub struct RouterSolicit {
302 #[construct_with(u8)]
303 pub icmpv6_type: Icmpv6Type,
304 #[construct_with(u8)]
305 pub icmpv6_code: Icmpv6Code,
306 pub checksum: u16be,
307 pub reserved: u32be,
308 #[payload]
309 #[length = "0"]
310 pub payload: Vec<u8>,
311 pub options: Vec<NdpOption>,
312 }
313
314 /// The enumeration of recognized Router Advert flags.
315 #[allow(non_snake_case)]
316 #[allow(non_upper_case_globals)]
317 pub mod RouterAdvertFlags {
318 /// "Managed Address Configuration" flag. This is set when
319 /// addresses are available via DHCPv6.
320 pub const ManagedAddressConf: u8 = 0b10000000;
321 /// "Other Configuration" flag. This is set when other
322 /// configuration information is available via DHCPv6.
323 pub const OtherConf: u8 = 0b01000000;
324 }
325
326 /// Router Advertisement Message Format [RFC 4861 § 4.2]
327 ///
328 /// ```text
329 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330 /// | Type | Code | Checksum |
331 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332 /// | Cur Hop Limit |M|O| Reserved | Router Lifetime |
333 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334 /// | Reachable Time |
335 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
336 /// | Retrans Timer |
337 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
338 /// | Options ...
339 /// +-+-+-+-+-+-+-+-+-+-+-+-
340 /// ```
341 ///
342 /// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
343 #[derive(Debug, Packet)]
344 pub struct RouterAdvert {
345 #[construct_with(u8)]
346 pub icmpv6_type: Icmpv6Type,
347 #[construct_with(u8)]
348 pub icmpv6_code: Icmpv6Code,
349 pub checksum: u16be,
350 pub hop_limit: u8,
351 pub flags: u8,
352 pub lifetime: u16be,
353 pub reachable_time: u32be,
354 pub retrans_time: u32be,
355 #[payload]
356 #[length = "0"]
357 pub payload: Vec<u8>,
358 pub options: Vec<NdpOption>,
359 }
360
361 /// Neighbor Solicitation Message Format [RFC 4861 § 4.3]
362 ///
363 /// ```text
364 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 /// | Type | Code | Checksum |
366 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 /// | Reserved |
368 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 /// | |
370 /// + +
371 /// | |
372 /// + Target Address +
373 /// | |
374 /// + +
375 /// | |
376 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377 /// | Options ...
378 /// +-+-+-+-+-+-+-+-+-+-+-+-
379 /// ```
380 ///
381 /// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
382 #[derive(Debug, Packet)]
383 pub struct NeighborSolicit {
384 #[construct_with(u8)]
385 pub icmpv6_type: Icmpv6Type,
386 #[construct_with(u8)]
387 pub icmpv6_code: Icmpv6Code,
388 pub checksum: u16be,
389 pub reserved: u32be,
390 #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
391 pub target_addr: Ipv6Addr,
392 #[payload]
393 #[length = "0"]
394 pub payload: Vec<u8>,
395 pub options: Vec<NdpOption>,
396 }
397
398 /// Enumeration of recognized Neighbor Advert flags.
399 #[allow(non_snake_case)]
400 #[allow(non_upper_case_globals)]
401 pub mod NeighborAdvertFlags {
402 /// Indicates that the sender is a router.
403 pub const Router: u8 = 0b10000000;
404 /// Indicates that the advertisement was sent due to the receipt of a
405 /// Neighbor Solicitation message.
406 pub const Solicited: u8 = 0b01000000;
407 /// Indicates that the advertisement should override an existing cache
408 /// entry.
409 pub const Override: u8 = 0b00100000;
410 }
411
412 /// Neighbor Advertisement Message Format [RFC 4861 § 4.4]
413 ///
414 /// ```text
415 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416 /// | Type | Code | Checksum |
417 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
418 /// |R|S|O| Reserved |
419 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
420 /// | |
421 /// + +
422 /// | |
423 /// + Target Address +
424 /// | |
425 /// + +
426 /// | |
427 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428 /// | Options ...
429 /// +-+-+-+-+-+-+-+-+-+-+-+-
430 /// ```
431 ///
432 /// [RFC 4861 § 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
433 #[derive(Debug, Packet)]
434 pub struct NeighborAdvert {
435 #[construct_with(u8)]
436 pub icmpv6_type: Icmpv6Type,
437 #[construct_with(u8)]
438 pub icmpv6_code: Icmpv6Code,
439 pub checksum: u16be,
440 pub flags: u8,
441 pub reserved: u24be,
442 #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
443 pub target_addr: Ipv6Addr,
444 #[payload]
445 #[length = "0"]
446 pub payload: Vec<u8>,
447 pub options: Vec<NdpOption>,
448 }
449
450 /// Redirect Message Format [RFC 4861 § 4.5]
451 ///
452 /// ```text
453 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454 /// | Type | Code | Checksum |
455 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
456 /// | Reserved |
457 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
458 /// | |
459 /// + +
460 /// | |
461 /// + Target Address +
462 /// | |
463 /// + +
464 /// | |
465 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
466 /// | |
467 /// + +
468 /// | |
469 /// + Destination Address +
470 /// | |
471 /// + +
472 /// | |
473 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
474 /// | Options ...
475 /// +-+-+-+-+-+-+-+-+-+-+-+-
476 /// ```
477 ///
478 /// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
479 #[derive(Debug, Packet)]
480 pub struct Redirect {
481 #[construct_with(u8)]
482 pub icmpv6_type: Icmpv6Type,
483 #[construct_with(u8)]
484 pub icmpv6_code: Icmpv6Code,
485 pub checksum: u16be,
486 pub reserved: u32be,
487 #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
488 pub target_addr: Ipv6Addr,
489 #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
490 pub dest_addr: Ipv6Addr,
491 #[payload]
492 #[length = "0"]
493 pub payload: Vec<u8>,
494 pub options: Vec<NdpOption>,
495 }
496
497 #[cfg(test)]
498 mod ndp_tests {
499 use super::*;
500 use crate::icmpv6::{Icmpv6Code, Icmpv6Types};
501
502 #[test]
503 fn basic_option_parsing() {
504 let mut data = vec![
505 0x02, 0x01, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
506 // Extra bytes to confuse the parsing
507 0x00, 0x00, 0x00,
508 ];
509 let pkg = MutableNdpOptionPacket::new(&mut data[..]).unwrap();
510 assert_eq!(pkg.get_option_type(), NdpOptionTypes::TargetLLAddr);
511 assert_eq!(pkg.get_length(), 0x01);
512 assert_eq!(pkg.payload().len(), 6);
513 assert_eq!(pkg.payload(), &[0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
514 }
515
516 #[test]
517 fn basic_rs_parse() {
518 let mut data = vec![
519 0x85, // Type
520 0x00, // Code
521 0x00, 0x00, // Checksum
522 0x00, 0x00, 0x00, 0x00, // Reserved
523 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
524 0x00, 0x00,
525 ];
526
527 let pkg = MutableRouterSolicitPacket::new(&mut data[..]).unwrap();
528 assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::RouterSolicit);
529 assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
530 assert_eq!(pkg.get_checksum(), 0);
531 assert_eq!(pkg.get_reserved(), 0);
532 assert_eq!(pkg.get_options().len(), 2);
533
534 let option = &pkg.get_options()[0];
535 assert_eq!(option.option_type, NdpOptionTypes::TargetLLAddr);
536 assert_eq!(option.length, 0x01);
537 assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
538 assert_eq!(option.data.len(), 6);
539
540 let option = &pkg.get_options()[1];
541 assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
542 assert_eq!(option.length, 1);
543 assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
544 }
545
546 #[test]
547 fn basic_rs_create() {
548 let ref_packet = vec![
549 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
550 0x00, 0x00,
551 ];
552 let mut packet = [0u8; 16];
553 let options = vec![NdpOption {
554 option_type: NdpOptionTypes::SourceLLAddr,
555 length: 1,
556 data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
557 }];
558 {
559 let mut rs_packet = MutableRouterSolicitPacket::new(&mut packet[..]).unwrap();
560 rs_packet.set_icmpv6_type(Icmpv6Types::RouterSolicit);
561 rs_packet.set_icmpv6_code(Icmpv6Code(0));
562 rs_packet.set_options(&options[..]);
563 }
564 assert_eq!(&ref_packet[..], &packet[..]);
565 }
566
567 #[test]
568 fn basic_ra_parse() {
569 let mut data = vec![
570 0x86, // Type
571 0x00, // Code
572 0x00, 0x00, // Checksum
573 0xff, // Hop Limit
574 0x80, // Flags
575 0x09, 0x00, // Lifetime
576 0x12, 0x34, 0x56, 0x78, // Reachable
577 0x87, 0x65, 0x43, 0x21, // Retrans
578 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Source Link-Layer
579 0x05, 0x01, 0x00, 0x00, 0x57, 0x68, 0x61, 0x74, // MTU
580 ];
581 let pkg = MutableRouterAdvertPacket::new(&mut data[..]).unwrap();
582 assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::RouterAdvert);
583 assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
584 assert_eq!(pkg.get_checksum(), 0x00);
585 assert_eq!(pkg.get_hop_limit(), 0xff);
586 assert_eq!(pkg.get_flags(), RouterAdvertFlags::ManagedAddressConf);
587 assert_eq!(pkg.get_lifetime(), 0x900);
588 assert_eq!(pkg.get_reachable_time(), 0x12345678);
589 assert_eq!(pkg.get_retrans_time(), 0x87654321);
590 assert_eq!(pkg.get_options().len(), 2);
591
592 let option = &pkg.get_options()[0];
593 assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
594 assert_eq!(option.length, 1);
595 assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
596
597 let option = &pkg.get_options()[1];
598 assert_eq!(option.option_type, NdpOptionTypes::MTU);
599 assert_eq!(option.length, 1);
600 assert_eq!(option.data, &[0x00, 0x00, 0x57, 0x68, 0x61, 0x74]);
601 }
602
603 #[test]
604 fn basic_ra_create() {
605 let ref_packet = vec![
606 0x86, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
607 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608 ];
609 let mut packet = [0u8; 24];
610 let options = vec![NdpOption {
611 option_type: NdpOptionTypes::MTU,
612 length: 1,
613 data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
614 }];
615 {
616 let mut ra_packet = MutableRouterAdvertPacket::new(&mut packet[..]).unwrap();
617 ra_packet.set_icmpv6_type(Icmpv6Types::RouterAdvert);
618 ra_packet.set_icmpv6_code(Icmpv6Code(0));
619 ra_packet.set_hop_limit(0xff);
620 ra_packet.set_flags(RouterAdvertFlags::ManagedAddressConf);
621 ra_packet.set_options(&options[..]);
622 }
623 assert_eq!(&ref_packet[..], &packet[..]);
624 }
625
626 #[test]
627 fn basic_ns_parse() {
628 let mut data = vec![
629 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
630 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
631 ];
632 let pkg = MutableNeighborSolicitPacket::new(&mut data[..]).unwrap();
633 assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::NeighborSolicit);
634 assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
635 assert_eq!(pkg.get_checksum(), 0x00);
636 assert_eq!(pkg.get_reserved(), 0x00);
637 assert_eq!(
638 pkg.get_target_addr(),
639 Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
640 );
641 }
642
643 #[test]
644 fn basic_ns_create() {
645 let ref_packet = vec![
646 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
647 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
648 ];
649 let mut packet = [0u8; 24];
650 {
651 let mut ns_packet = MutableNeighborSolicitPacket::new(&mut packet[..]).unwrap();
652 ns_packet.set_icmpv6_type(Icmpv6Types::NeighborSolicit);
653 ns_packet.set_icmpv6_code(Icmpv6Code(0));
654 ns_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
655 }
656 assert_eq!(&ref_packet[..], &packet[..]);
657 }
658
659 #[test]
660 fn basic_na_parse() {
661 let mut data = vec![
662 0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
663 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
664 ];
665 let pkg = MutableNeighborAdvertPacket::new(&mut data[..]).unwrap();
666 assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::NeighborAdvert);
667 assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
668 assert_eq!(pkg.get_checksum(), 0x00);
669 assert_eq!(pkg.get_reserved(), 0x00);
670 assert_eq!(pkg.get_flags(), 0x80);
671 assert_eq!(
672 pkg.get_target_addr(),
673 Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
674 );
675 }
676
677 #[test]
678 fn basic_na_create() {
679 let ref_packet = vec![
680 0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
681 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
682 ];
683 let mut packet = [0u8; 24];
684 {
685 let mut na_packet = MutableNeighborAdvertPacket::new(&mut packet[..]).unwrap();
686 na_packet.set_icmpv6_type(Icmpv6Types::NeighborAdvert);
687 na_packet.set_icmpv6_code(Icmpv6Code(0));
688 na_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
689 na_packet.set_flags(NeighborAdvertFlags::Router);
690 }
691 assert_eq!(&ref_packet[..], &packet[..]);
692 }
693
694 #[test]
695 fn basic_redirect_parse() {
696 let mut data = vec![
697 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
698 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
699 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
700 ];
701 let pkg = MutableRedirectPacket::new(&mut data[..]).unwrap();
702 assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::Redirect);
703 assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
704 assert_eq!(pkg.get_checksum(), 0x00);
705 assert_eq!(pkg.get_reserved(), 0x00);
706 assert_eq!(
707 pkg.get_target_addr(),
708 Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
709 );
710 assert_eq!(pkg.get_dest_addr(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
711 }
712
713 #[test]
714 fn basic_redirect_create() {
715 let ref_packet = vec![
716 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
717 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
718 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
719 ];
720 let mut packet = [0u8; 40];
721 {
722 let mut rdr_packet = MutableRedirectPacket::new(&mut packet[..]).unwrap();
723 rdr_packet.set_icmpv6_type(Icmpv6Types::Redirect);
724 rdr_packet.set_icmpv6_code(Icmpv6Code(0));
725 rdr_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
726 rdr_packet.set_dest_addr(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
727 }
728 assert_eq!(&ref_packet[..], &packet[..]);
729 }
730 }
731}