1 // Packet implementation for the Multicast Listener Discovery 2 // protocol. See [RFC 3810] and [RFC 2710]. 3 // 4 // [RFC 3810]: https://tools.ietf.org/html/rfc3810 5 // [RFC 2710]: https://tools.ietf.org/html/rfc2710 6 7 use byteorder::{ByteOrder, NetworkEndian}; 8 9 use super::{Error, Result}; 10 use crate::wire::icmpv6::{field, Message, Packet}; 11 use crate::wire::Ipv6Address; 12 13 enum_with_unknown! { 14 /// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for 15 /// more details. 16 /// 17 /// [RFC 3810 § 5.2.12]: https://tools.ietf.org/html/rfc3010#section-5.2.12 18 pub enum RecordType(u8) { 19 /// Interface has a filter mode of INCLUDE for the specified multicast address. 20 ModeIsInclude = 0x01, 21 /// Interface has a filter mode of EXCLUDE for the specified multicast address. 22 ModeIsExclude = 0x02, 23 /// Interface has changed to a filter mode of INCLUDE for the specified 24 /// multicast address. 25 ChangeToInclude = 0x03, 26 /// Interface has changed to a filter mode of EXCLUDE for the specified 27 /// multicast address. 28 ChangeToExclude = 0x04, 29 /// Interface wishes to listen to the sources in the specified list. 30 AllowNewSources = 0x05, 31 /// Interface no longer wishes to listen to the sources in the specified list. 32 BlockOldSources = 0x06 33 } 34 } 35 36 /// Getters for the Multicast Listener Query message header. 37 /// See [RFC 3810 § 5.1]. 38 /// 39 /// [RFC 3810 § 5.1]: https://tools.ietf.org/html/rfc3010#section-5.1 40 impl<T: AsRef<[u8]>> Packet<T> { 41 /// Return the maximum response code field. 42 #[inline] max_resp_code(&self) -> u1643 pub fn max_resp_code(&self) -> u16 { 44 let data = self.buffer.as_ref(); 45 NetworkEndian::read_u16(&data[field::MAX_RESP_CODE]) 46 } 47 48 /// Return the address being queried. 49 #[inline] mcast_addr(&self) -> Ipv6Address50 pub fn mcast_addr(&self) -> Ipv6Address { 51 let data = self.buffer.as_ref(); 52 Ipv6Address::from_bytes(&data[field::QUERY_MCAST_ADDR]) 53 } 54 55 /// Return the Suppress Router-Side Processing flag. 56 #[inline] s_flag(&self) -> bool57 pub fn s_flag(&self) -> bool { 58 let data = self.buffer.as_ref(); 59 (data[field::SQRV] & 0x08) != 0 60 } 61 62 /// Return the Querier's Robustness Variable. 63 #[inline] qrv(&self) -> u864 pub fn qrv(&self) -> u8 { 65 let data = self.buffer.as_ref(); 66 data[field::SQRV] & 0x7 67 } 68 69 /// Return the Querier's Query Interval Code. 70 #[inline] qqic(&self) -> u871 pub fn qqic(&self) -> u8 { 72 let data = self.buffer.as_ref(); 73 data[field::QQIC] 74 } 75 76 /// Return number of sources. 77 #[inline] num_srcs(&self) -> u1678 pub fn num_srcs(&self) -> u16 { 79 let data = self.buffer.as_ref(); 80 NetworkEndian::read_u16(&data[field::QUERY_NUM_SRCS]) 81 } 82 } 83 84 /// Getters for the Multicast Listener Report message header. 85 /// See [RFC 3810 § 5.2]. 86 /// 87 /// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2 88 impl<T: AsRef<[u8]>> Packet<T> { 89 /// Return the number of Multicast Address Records. 90 #[inline] nr_mcast_addr_rcrds(&self) -> u1691 pub fn nr_mcast_addr_rcrds(&self) -> u16 { 92 let data = self.buffer.as_ref(); 93 NetworkEndian::read_u16(&data[field::NR_MCAST_RCRDS]) 94 } 95 } 96 97 /// Setters for the Multicast Listener Query message header. 98 /// See [RFC 3810 § 5.1]. 99 /// 100 /// [RFC 3810 § 5.1]: https://tools.ietf.org/html/rfc3010#section-5.1 101 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> { 102 /// Set the maximum response code field. 103 #[inline] set_max_resp_code(&mut self, code: u16)104 pub fn set_max_resp_code(&mut self, code: u16) { 105 let data = self.buffer.as_mut(); 106 NetworkEndian::write_u16(&mut data[field::MAX_RESP_CODE], code); 107 } 108 109 /// Set the address being queried. 110 #[inline] set_mcast_addr(&mut self, addr: Ipv6Address)111 pub fn set_mcast_addr(&mut self, addr: Ipv6Address) { 112 let data = self.buffer.as_mut(); 113 data[field::QUERY_MCAST_ADDR].copy_from_slice(addr.as_bytes()); 114 } 115 116 /// Set the Suppress Router-Side Processing flag. 117 #[inline] set_s_flag(&mut self)118 pub fn set_s_flag(&mut self) { 119 let data = self.buffer.as_mut(); 120 let current = data[field::SQRV]; 121 data[field::SQRV] = 0x8 | (current & 0x7); 122 } 123 124 /// Clear the Suppress Router-Side Processing flag. 125 #[inline] clear_s_flag(&mut self)126 pub fn clear_s_flag(&mut self) { 127 let data = self.buffer.as_mut(); 128 data[field::SQRV] &= 0x7; 129 } 130 131 /// Set the Querier's Robustness Variable. 132 #[inline] set_qrv(&mut self, value: u8)133 pub fn set_qrv(&mut self, value: u8) { 134 assert!(value < 8); 135 let data = self.buffer.as_mut(); 136 data[field::SQRV] = (data[field::SQRV] & 0x8) | value & 0x7; 137 } 138 139 /// Set the Querier's Query Interval Code. 140 #[inline] set_qqic(&mut self, value: u8)141 pub fn set_qqic(&mut self, value: u8) { 142 let data = self.buffer.as_mut(); 143 data[field::QQIC] = value; 144 } 145 146 /// Set number of sources. 147 #[inline] set_num_srcs(&mut self, value: u16)148 pub fn set_num_srcs(&mut self, value: u16) { 149 let data = self.buffer.as_mut(); 150 NetworkEndian::write_u16(&mut data[field::QUERY_NUM_SRCS], value); 151 } 152 } 153 154 /// Setters for the Multicast Listener Report message header. 155 /// See [RFC 3810 § 5.2]. 156 /// 157 /// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2 158 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> { 159 /// Set the number of Multicast Address Records. 160 #[inline] set_nr_mcast_addr_rcrds(&mut self, value: u16)161 pub fn set_nr_mcast_addr_rcrds(&mut self, value: u16) { 162 let data = self.buffer.as_mut(); 163 NetworkEndian::write_u16(&mut data[field::NR_MCAST_RCRDS], value) 164 } 165 } 166 167 /// A read/write wrapper around an MLDv2 Listener Report Message Address Record. 168 #[derive(Debug, PartialEq, Eq, Clone)] 169 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 170 pub struct AddressRecord<T: AsRef<[u8]>> { 171 buffer: T, 172 } 173 174 impl<T: AsRef<[u8]>> AddressRecord<T> { 175 /// Imbue a raw octet buffer with a Address Record structure. new_unchecked(buffer: T) -> Self176 pub const fn new_unchecked(buffer: T) -> Self { 177 Self { buffer } 178 } 179 180 /// Shorthand for a combination of [new_unchecked] and [check_len]. 181 /// 182 /// [new_unchecked]: #method.new_unchecked 183 /// [check_len]: #method.check_len new_checked(buffer: T) -> Result<Self>184 pub fn new_checked(buffer: T) -> Result<Self> { 185 let packet = Self::new_unchecked(buffer); 186 packet.check_len()?; 187 Ok(packet) 188 } 189 190 /// Ensure that no accessor method will panic if called. 191 /// Returns `Err(Error::Truncated)` if the buffer is too short. check_len(&self) -> Result<()>192 pub fn check_len(&self) -> Result<()> { 193 let len = self.buffer.as_ref().len(); 194 if len < field::RECORD_MCAST_ADDR.end { 195 Err(Error) 196 } else { 197 Ok(()) 198 } 199 } 200 201 /// Consume the packet, returning the underlying buffer. into_inner(self) -> T202 pub fn into_inner(self) -> T { 203 self.buffer 204 } 205 } 206 207 /// Getters for a MLDv2 Listener Report Message Address Record. 208 /// See [RFC 3810 § 5.2]. 209 /// 210 /// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2 211 impl<T: AsRef<[u8]>> AddressRecord<T> { 212 /// Return the record type for the given sources. 213 #[inline] record_type(&self) -> RecordType214 pub fn record_type(&self) -> RecordType { 215 let data = self.buffer.as_ref(); 216 RecordType::from(data[field::RECORD_TYPE]) 217 } 218 219 /// Return the length of the auxiliary data. 220 #[inline] aux_data_len(&self) -> u8221 pub fn aux_data_len(&self) -> u8 { 222 let data = self.buffer.as_ref(); 223 data[field::AUX_DATA_LEN] 224 } 225 226 /// Return the number of sources field. 227 #[inline] num_srcs(&self) -> u16228 pub fn num_srcs(&self) -> u16 { 229 let data = self.buffer.as_ref(); 230 NetworkEndian::read_u16(&data[field::RECORD_NUM_SRCS]) 231 } 232 233 /// Return the multicast address field. 234 #[inline] mcast_addr(&self) -> Ipv6Address235 pub fn mcast_addr(&self) -> Ipv6Address { 236 let data = self.buffer.as_ref(); 237 Ipv6Address::from_bytes(&data[field::RECORD_MCAST_ADDR]) 238 } 239 } 240 241 impl<'a, T: AsRef<[u8]> + ?Sized> AddressRecord<&'a T> { 242 /// Return a pointer to the address records. 243 #[inline] payload(&self) -> &'a [u8]244 pub fn payload(&self) -> &'a [u8] { 245 let data = self.buffer.as_ref(); 246 &data[field::RECORD_MCAST_ADDR.end..] 247 } 248 } 249 250 /// Setters for a MLDv2 Listener Report Message Address Record. 251 /// See [RFC 3810 § 5.2]. 252 /// 253 /// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2 254 impl<T: AsMut<[u8]> + AsRef<[u8]>> AddressRecord<T> { 255 /// Return the record type for the given sources. 256 #[inline] set_record_type(&mut self, rty: RecordType)257 pub fn set_record_type(&mut self, rty: RecordType) { 258 let data = self.buffer.as_mut(); 259 data[field::RECORD_TYPE] = rty.into(); 260 } 261 262 /// Return the length of the auxiliary data. 263 #[inline] set_aux_data_len(&mut self, len: u8)264 pub fn set_aux_data_len(&mut self, len: u8) { 265 let data = self.buffer.as_mut(); 266 data[field::AUX_DATA_LEN] = len; 267 } 268 269 /// Return the number of sources field. 270 #[inline] set_num_srcs(&mut self, num_srcs: u16)271 pub fn set_num_srcs(&mut self, num_srcs: u16) { 272 let data = self.buffer.as_mut(); 273 NetworkEndian::write_u16(&mut data[field::RECORD_NUM_SRCS], num_srcs); 274 } 275 276 /// Return the multicast address field. 277 /// 278 /// # Panics 279 /// This function panics if the given address is not a multicast address. 280 #[inline] set_mcast_addr(&mut self, addr: Ipv6Address)281 pub fn set_mcast_addr(&mut self, addr: Ipv6Address) { 282 assert!(addr.is_multicast()); 283 let data = self.buffer.as_mut(); 284 data[field::RECORD_MCAST_ADDR].copy_from_slice(addr.as_bytes()); 285 } 286 } 287 288 impl<T: AsRef<[u8]> + AsMut<[u8]>> AddressRecord<T> { 289 /// Return a pointer to the address records. 290 #[inline] payload_mut(&mut self) -> &mut [u8]291 pub fn payload_mut(&mut self) -> &mut [u8] { 292 let data = self.buffer.as_mut(); 293 &mut data[field::RECORD_MCAST_ADDR.end..] 294 } 295 } 296 297 /// A high-level representation of an MLDv2 packet header. 298 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 299 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 300 pub enum Repr<'a> { 301 Query { 302 max_resp_code: u16, 303 mcast_addr: Ipv6Address, 304 s_flag: bool, 305 qrv: u8, 306 qqic: u8, 307 num_srcs: u16, 308 data: &'a [u8], 309 }, 310 Report { 311 nr_mcast_addr_rcrds: u16, 312 data: &'a [u8], 313 }, 314 } 315 316 impl<'a> Repr<'a> { 317 /// Parse an MLDv2 packet and return a high-level representation. parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized,318 pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>> 319 where 320 T: AsRef<[u8]> + ?Sized, 321 { 322 match packet.msg_type() { 323 Message::MldQuery => Ok(Repr::Query { 324 max_resp_code: packet.max_resp_code(), 325 mcast_addr: packet.mcast_addr(), 326 s_flag: packet.s_flag(), 327 qrv: packet.qrv(), 328 qqic: packet.qqic(), 329 num_srcs: packet.num_srcs(), 330 data: packet.payload(), 331 }), 332 Message::MldReport => Ok(Repr::Report { 333 nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(), 334 data: packet.payload(), 335 }), 336 _ => Err(Error), 337 } 338 } 339 340 /// Return the length of a packet that will be emitted from this high-level representation. buffer_len(&self) -> usize341 pub const fn buffer_len(&self) -> usize { 342 match self { 343 Repr::Query { data, .. } => field::QUERY_NUM_SRCS.end + data.len(), 344 Repr::Report { data, .. } => field::NR_MCAST_RCRDS.end + data.len(), 345 } 346 } 347 348 /// Emit a high-level representation into an MLDv2 packet. emit<T>(&self, packet: &mut Packet<&mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,349 pub fn emit<T>(&self, packet: &mut Packet<&mut T>) 350 where 351 T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, 352 { 353 match self { 354 Repr::Query { 355 max_resp_code, 356 mcast_addr, 357 s_flag, 358 qrv, 359 qqic, 360 num_srcs, 361 data, 362 } => { 363 packet.set_msg_type(Message::MldQuery); 364 packet.set_msg_code(0); 365 packet.clear_reserved(); 366 packet.set_max_resp_code(*max_resp_code); 367 packet.set_mcast_addr(*mcast_addr); 368 if *s_flag { 369 packet.set_s_flag(); 370 } else { 371 packet.clear_s_flag(); 372 } 373 packet.set_qrv(*qrv); 374 packet.set_qqic(*qqic); 375 packet.set_num_srcs(*num_srcs); 376 packet.payload_mut().copy_from_slice(&data[..]); 377 } 378 Repr::Report { 379 nr_mcast_addr_rcrds, 380 data, 381 } => { 382 packet.set_msg_type(Message::MldReport); 383 packet.set_msg_code(0); 384 packet.clear_reserved(); 385 packet.set_nr_mcast_addr_rcrds(*nr_mcast_addr_rcrds); 386 packet.payload_mut().copy_from_slice(&data[..]); 387 } 388 } 389 } 390 } 391 392 #[cfg(test)] 393 mod test { 394 use super::*; 395 use crate::phy::ChecksumCapabilities; 396 use crate::wire::icmpv6::Message; 397 use crate::wire::Icmpv6Repr; 398 399 static QUERY_PACKET_BYTES: [u8; 44] = [ 400 0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 401 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02, 402 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 403 ]; 404 405 static QUERY_PACKET_PAYLOAD: [u8; 16] = [ 406 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 407 0x02, 408 ]; 409 410 static REPORT_PACKET_BYTES: [u8; 44] = [ 411 0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 413 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 414 ]; 415 416 static REPORT_PACKET_PAYLOAD: [u8; 36] = [ 417 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 418 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 419 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 420 ]; 421 create_repr<'a>(ty: Message) -> Icmpv6Repr<'a>422 fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> { 423 match ty { 424 Message::MldQuery => Icmpv6Repr::Mld(Repr::Query { 425 max_resp_code: 0x400, 426 mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, 427 s_flag: true, 428 qrv: 0x02, 429 qqic: 0x12, 430 num_srcs: 0x01, 431 data: &QUERY_PACKET_PAYLOAD, 432 }), 433 Message::MldReport => Icmpv6Repr::Mld(Repr::Report { 434 nr_mcast_addr_rcrds: 1, 435 data: &REPORT_PACKET_PAYLOAD, 436 }), 437 _ => { 438 panic!("Message type must be a MLDv2 message type"); 439 } 440 } 441 } 442 443 #[test] test_query_deconstruct()444 fn test_query_deconstruct() { 445 let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]); 446 assert_eq!(packet.msg_type(), Message::MldQuery); 447 assert_eq!(packet.msg_code(), 0); 448 assert_eq!(packet.checksum(), 0x7374); 449 assert_eq!(packet.max_resp_code(), 0x0400); 450 assert_eq!(packet.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES); 451 assert!(packet.s_flag()); 452 assert_eq!(packet.qrv(), 0x02); 453 assert_eq!(packet.qqic(), 0x12); 454 assert_eq!(packet.num_srcs(), 0x01); 455 assert_eq!( 456 Ipv6Address::from_bytes(packet.payload()), 457 Ipv6Address::LINK_LOCAL_ALL_ROUTERS 458 ); 459 } 460 461 #[test] test_query_construct()462 fn test_query_construct() { 463 let mut bytes = vec![0xff; 44]; 464 let mut packet = Packet::new_unchecked(&mut bytes[..]); 465 packet.set_msg_type(Message::MldQuery); 466 packet.set_msg_code(0); 467 packet.set_max_resp_code(0x0400); 468 packet.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES); 469 packet.set_s_flag(); 470 packet.set_qrv(0x02); 471 packet.set_qqic(0x12); 472 packet.set_num_srcs(0x01); 473 packet 474 .payload_mut() 475 .copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes()); 476 packet.clear_reserved(); 477 packet.fill_checksum( 478 &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), 479 &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), 480 ); 481 assert_eq!(&*packet.into_inner(), &QUERY_PACKET_BYTES[..]); 482 } 483 484 #[test] test_record_deconstruct()485 fn test_record_deconstruct() { 486 let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]); 487 assert_eq!(packet.msg_type(), Message::MldReport); 488 assert_eq!(packet.msg_code(), 0); 489 assert_eq!(packet.checksum(), 0x7385); 490 assert_eq!(packet.nr_mcast_addr_rcrds(), 0x01); 491 let addr_rcrd = AddressRecord::new_unchecked(packet.payload()); 492 assert_eq!(addr_rcrd.record_type(), RecordType::ModeIsInclude); 493 assert_eq!(addr_rcrd.aux_data_len(), 0x00); 494 assert_eq!(addr_rcrd.num_srcs(), 0x01); 495 assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES); 496 assert_eq!( 497 Ipv6Address::from_bytes(addr_rcrd.payload()), 498 Ipv6Address::LINK_LOCAL_ALL_ROUTERS 499 ); 500 } 501 502 #[test] test_record_construct()503 fn test_record_construct() { 504 let mut bytes = vec![0xff; 44]; 505 let mut packet = Packet::new_unchecked(&mut bytes[..]); 506 packet.set_msg_type(Message::MldReport); 507 packet.set_msg_code(0); 508 packet.clear_reserved(); 509 packet.set_nr_mcast_addr_rcrds(1); 510 { 511 let mut addr_rcrd = AddressRecord::new_unchecked(packet.payload_mut()); 512 addr_rcrd.set_record_type(RecordType::ModeIsInclude); 513 addr_rcrd.set_aux_data_len(0); 514 addr_rcrd.set_num_srcs(1); 515 addr_rcrd.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES); 516 addr_rcrd 517 .payload_mut() 518 .copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes()); 519 } 520 packet.fill_checksum( 521 &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), 522 &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), 523 ); 524 assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]); 525 } 526 527 #[test] test_query_repr_parse()528 fn test_query_repr_parse() { 529 let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]); 530 let repr = Icmpv6Repr::parse( 531 &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), 532 &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), 533 &packet, 534 &ChecksumCapabilities::default(), 535 ); 536 assert_eq!(repr, Ok(create_repr(Message::MldQuery))); 537 } 538 539 #[test] test_report_repr_parse()540 fn test_report_repr_parse() { 541 let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]); 542 let repr = Icmpv6Repr::parse( 543 &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), 544 &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), 545 &packet, 546 &ChecksumCapabilities::default(), 547 ); 548 assert_eq!(repr, Ok(create_repr(Message::MldReport))); 549 } 550 551 #[test] test_query_repr_emit()552 fn test_query_repr_emit() { 553 let mut bytes = [0x2a; 44]; 554 let mut packet = Packet::new_unchecked(&mut bytes[..]); 555 let repr = create_repr(Message::MldQuery); 556 repr.emit( 557 &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), 558 &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), 559 &mut packet, 560 &ChecksumCapabilities::default(), 561 ); 562 assert_eq!(&*packet.into_inner(), &QUERY_PACKET_BYTES[..]); 563 } 564 565 #[test] test_report_repr_emit()566 fn test_report_repr_emit() { 567 let mut bytes = [0x2a; 44]; 568 let mut packet = Packet::new_unchecked(&mut bytes[..]); 569 let repr = create_repr(Message::MldReport); 570 repr.emit( 571 &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), 572 &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), 573 &mut packet, 574 &ChecksumCapabilities::default(), 575 ); 576 assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]); 577 } 578 } 579