igmp.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. use byteorder::{ByteOrder, NetworkEndian};
  2. use core::fmt;
  3. use super::{Error, Result};
  4. use crate::time::Duration;
  5. use crate::wire::ip::checksum;
  6. use crate::wire::{Ipv4Address, Ipv4AddressExt};
  7. enum_with_unknown! {
  8. /// Internet Group Management Protocol v1/v2 message version/type.
  9. pub enum Message(u8) {
  10. /// Membership Query
  11. MembershipQuery = 0x11,
  12. /// Version 2 Membership Report
  13. MembershipReportV2 = 0x16,
  14. /// Leave Group
  15. LeaveGroup = 0x17,
  16. /// Version 1 Membership Report
  17. MembershipReportV1 = 0x12
  18. }
  19. }
  20. /// A read/write wrapper around an Internet Group Management Protocol v1/v2 packet buffer.
  21. #[derive(Debug)]
  22. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  23. pub struct Packet<T: AsRef<[u8]>> {
  24. buffer: T,
  25. }
  26. mod field {
  27. use crate::wire::field::*;
  28. pub const TYPE: usize = 0;
  29. pub const MAX_RESP_CODE: usize = 1;
  30. pub const CHECKSUM: Field = 2..4;
  31. pub const GROUP_ADDRESS: Field = 4..8;
  32. }
  33. impl fmt::Display for Message {
  34. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  35. match *self {
  36. Message::MembershipQuery => write!(f, "membership query"),
  37. Message::MembershipReportV2 => write!(f, "version 2 membership report"),
  38. Message::LeaveGroup => write!(f, "leave group"),
  39. Message::MembershipReportV1 => write!(f, "version 1 membership report"),
  40. Message::Unknown(id) => write!(f, "{id}"),
  41. }
  42. }
  43. }
  44. /// Internet Group Management Protocol v1/v2 defined in [RFC 2236].
  45. ///
  46. /// [RFC 2236]: https://tools.ietf.org/html/rfc2236
  47. impl<T: AsRef<[u8]>> Packet<T> {
  48. /// Imbue a raw octet buffer with IGMPv2 packet structure.
  49. pub const fn new_unchecked(buffer: T) -> Packet<T> {
  50. Packet { buffer }
  51. }
  52. /// Shorthand for a combination of [new_unchecked] and [check_len].
  53. ///
  54. /// [new_unchecked]: #method.new_unchecked
  55. /// [check_len]: #method.check_len
  56. pub fn new_checked(buffer: T) -> Result<Packet<T>> {
  57. let packet = Self::new_unchecked(buffer);
  58. packet.check_len()?;
  59. Ok(packet)
  60. }
  61. /// Ensure that no accessor method will panic if called.
  62. /// Returns `Err(Error)` if the buffer is too short.
  63. pub fn check_len(&self) -> Result<()> {
  64. let len = self.buffer.as_ref().len();
  65. if len < field::GROUP_ADDRESS.end {
  66. Err(Error)
  67. } else {
  68. Ok(())
  69. }
  70. }
  71. /// Consume the packet, returning the underlying buffer.
  72. pub fn into_inner(self) -> T {
  73. self.buffer
  74. }
  75. /// Return the message type field.
  76. #[inline]
  77. pub fn msg_type(&self) -> Message {
  78. let data = self.buffer.as_ref();
  79. Message::from(data[field::TYPE])
  80. }
  81. /// Return the maximum response time, using the encoding specified in
  82. /// [RFC 3376]: 4.1.1. Max Resp Code.
  83. ///
  84. /// [RFC 3376]: https://tools.ietf.org/html/rfc3376
  85. #[inline]
  86. pub fn max_resp_code(&self) -> u8 {
  87. let data = self.buffer.as_ref();
  88. data[field::MAX_RESP_CODE]
  89. }
  90. /// Return the checksum field.
  91. #[inline]
  92. pub fn checksum(&self) -> u16 {
  93. let data = self.buffer.as_ref();
  94. NetworkEndian::read_u16(&data[field::CHECKSUM])
  95. }
  96. /// Return the source address field.
  97. #[inline]
  98. pub fn group_addr(&self) -> Ipv4Address {
  99. let data = self.buffer.as_ref();
  100. Ipv4Address::from_bytes(&data[field::GROUP_ADDRESS])
  101. }
  102. /// Validate the header checksum.
  103. ///
  104. /// # Fuzzing
  105. /// This function always returns `true` when fuzzing.
  106. pub fn verify_checksum(&self) -> bool {
  107. if cfg!(fuzzing) {
  108. return true;
  109. }
  110. let data = self.buffer.as_ref();
  111. checksum::data(data) == !0
  112. }
  113. }
  114. impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
  115. /// Set the message type field.
  116. #[inline]
  117. pub fn set_msg_type(&mut self, value: Message) {
  118. let data = self.buffer.as_mut();
  119. data[field::TYPE] = value.into()
  120. }
  121. /// Set the maximum response time, using the encoding specified in
  122. /// [RFC 3376]: 4.1.1. Max Resp Code.
  123. #[inline]
  124. pub fn set_max_resp_code(&mut self, value: u8) {
  125. let data = self.buffer.as_mut();
  126. data[field::MAX_RESP_CODE] = value;
  127. }
  128. /// Set the checksum field.
  129. #[inline]
  130. pub fn set_checksum(&mut self, value: u16) {
  131. let data = self.buffer.as_mut();
  132. NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
  133. }
  134. /// Set the group address field
  135. #[inline]
  136. pub fn set_group_address(&mut self, addr: Ipv4Address) {
  137. let data = self.buffer.as_mut();
  138. data[field::GROUP_ADDRESS].copy_from_slice(&addr.octets());
  139. }
  140. /// Compute and fill in the header checksum.
  141. pub fn fill_checksum(&mut self) {
  142. self.set_checksum(0);
  143. let checksum = {
  144. let data = self.buffer.as_ref();
  145. !checksum::data(data)
  146. };
  147. self.set_checksum(checksum)
  148. }
  149. }
  150. /// A high-level representation of an Internet Group Management Protocol v1/v2 header.
  151. #[derive(Debug, PartialEq, Eq, Clone)]
  152. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  153. pub enum Repr {
  154. MembershipQuery {
  155. max_resp_time: Duration,
  156. group_addr: Ipv4Address,
  157. version: IgmpVersion,
  158. },
  159. MembershipReport {
  160. group_addr: Ipv4Address,
  161. version: IgmpVersion,
  162. },
  163. LeaveGroup {
  164. group_addr: Ipv4Address,
  165. },
  166. }
  167. /// Type of IGMP membership report version
  168. #[derive(Debug, PartialEq, Eq, Clone, Copy)]
  169. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  170. pub enum IgmpVersion {
  171. /// IGMPv1
  172. Version1,
  173. /// IGMPv2
  174. Version2,
  175. }
  176. impl Repr {
  177. /// Parse an Internet Group Management Protocol v1/v2 packet and return
  178. /// a high-level representation.
  179. pub fn parse<T>(packet: &Packet<&T>) -> Result<Repr>
  180. where
  181. T: AsRef<[u8]> + ?Sized,
  182. {
  183. packet.check_len()?;
  184. // Check if the address is 0.0.0.0 or multicast
  185. let addr = packet.group_addr();
  186. if !addr.is_unspecified() && !addr.is_multicast() {
  187. return Err(Error);
  188. }
  189. // construct a packet based on the Type field
  190. match packet.msg_type() {
  191. Message::MembershipQuery => {
  192. let max_resp_time = max_resp_code_to_duration(packet.max_resp_code());
  193. // See RFC 3376: 7.1. Query Version Distinctions
  194. let version = if packet.max_resp_code() == 0 {
  195. IgmpVersion::Version1
  196. } else {
  197. IgmpVersion::Version2
  198. };
  199. Ok(Repr::MembershipQuery {
  200. max_resp_time,
  201. group_addr: addr,
  202. version,
  203. })
  204. }
  205. Message::MembershipReportV2 => Ok(Repr::MembershipReport {
  206. group_addr: packet.group_addr(),
  207. version: IgmpVersion::Version2,
  208. }),
  209. Message::LeaveGroup => Ok(Repr::LeaveGroup {
  210. group_addr: packet.group_addr(),
  211. }),
  212. Message::MembershipReportV1 => {
  213. // for backwards compatibility with IGMPv1
  214. Ok(Repr::MembershipReport {
  215. group_addr: packet.group_addr(),
  216. version: IgmpVersion::Version1,
  217. })
  218. }
  219. _ => Err(Error),
  220. }
  221. }
  222. /// Return the length of a packet that will be emitted from this high-level representation.
  223. pub const fn buffer_len(&self) -> usize {
  224. // always 8 bytes
  225. field::GROUP_ADDRESS.end
  226. }
  227. /// Emit a high-level representation into an Internet Group Management Protocol v2 packet.
  228. pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
  229. where
  230. T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
  231. {
  232. match *self {
  233. Repr::MembershipQuery {
  234. max_resp_time,
  235. group_addr,
  236. version,
  237. } => {
  238. packet.set_msg_type(Message::MembershipQuery);
  239. match version {
  240. IgmpVersion::Version1 => packet.set_max_resp_code(0),
  241. IgmpVersion::Version2 => {
  242. packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time))
  243. }
  244. }
  245. packet.set_group_address(group_addr);
  246. }
  247. Repr::MembershipReport {
  248. group_addr,
  249. version,
  250. } => {
  251. match version {
  252. IgmpVersion::Version1 => packet.set_msg_type(Message::MembershipReportV1),
  253. IgmpVersion::Version2 => packet.set_msg_type(Message::MembershipReportV2),
  254. };
  255. packet.set_max_resp_code(0);
  256. packet.set_group_address(group_addr);
  257. }
  258. Repr::LeaveGroup { group_addr } => {
  259. packet.set_msg_type(Message::LeaveGroup);
  260. packet.set_group_address(group_addr);
  261. }
  262. }
  263. packet.fill_checksum()
  264. }
  265. }
  266. fn max_resp_code_to_duration(value: u8) -> Duration {
  267. let value: u64 = value.into();
  268. let decisecs = if value < 128 {
  269. value
  270. } else {
  271. let mant = value & 0xF;
  272. let exp = (value >> 4) & 0x7;
  273. (mant | 0x10) << (exp + 3)
  274. };
  275. Duration::from_millis(decisecs * 100)
  276. }
  277. const fn duration_to_max_resp_code(duration: Duration) -> u8 {
  278. let decisecs = duration.total_millis() / 100;
  279. if decisecs < 128 {
  280. decisecs as u8
  281. } else if decisecs < 31744 {
  282. let mut mant = decisecs >> 3;
  283. let mut exp = 0u8;
  284. while mant > 0x1F && exp < 0x8 {
  285. mant >>= 1;
  286. exp += 1;
  287. }
  288. 0x80 | (exp << 4) | (mant as u8 & 0xF)
  289. } else {
  290. 0xFF
  291. }
  292. }
  293. impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
  294. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  295. match Repr::parse(self) {
  296. Ok(repr) => write!(f, "{repr}"),
  297. Err(err) => write!(f, "IGMP ({err})"),
  298. }
  299. }
  300. }
  301. impl fmt::Display for Repr {
  302. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  303. match *self {
  304. Repr::MembershipQuery {
  305. max_resp_time,
  306. group_addr,
  307. version,
  308. } => write!(
  309. f,
  310. "IGMP membership query max_resp_time={max_resp_time} group_addr={group_addr} version={version:?}"
  311. ),
  312. Repr::MembershipReport {
  313. group_addr,
  314. version,
  315. } => write!(
  316. f,
  317. "IGMP membership report group_addr={group_addr} version={version:?}"
  318. ),
  319. Repr::LeaveGroup { group_addr } => {
  320. write!(f, "IGMP leave group group_addr={group_addr})")
  321. }
  322. }
  323. }
  324. }
  325. use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
  326. impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
  327. fn pretty_print(
  328. buffer: &dyn AsRef<[u8]>,
  329. f: &mut fmt::Formatter,
  330. indent: &mut PrettyIndent,
  331. ) -> fmt::Result {
  332. match Packet::new_checked(buffer) {
  333. Err(err) => writeln!(f, "{indent}({err})"),
  334. Ok(packet) => writeln!(f, "{indent}{packet}"),
  335. }
  336. }
  337. }
  338. #[cfg(test)]
  339. mod test {
  340. use super::*;
  341. static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96];
  342. static REPORT_PACKET_BYTES: [u8; 8] = [0x16, 0x00, 0x08, 0xda, 0xe1, 0x00, 0x00, 0x25];
  343. #[test]
  344. fn test_leave_group_deconstruct() {
  345. let packet = Packet::new_unchecked(&LEAVE_PACKET_BYTES[..]);
  346. assert_eq!(packet.msg_type(), Message::LeaveGroup);
  347. assert_eq!(packet.max_resp_code(), 0);
  348. assert_eq!(packet.checksum(), 0x269);
  349. assert_eq!(
  350. packet.group_addr(),
  351. Ipv4Address::from_bytes(&[224, 0, 6, 150])
  352. );
  353. assert!(packet.verify_checksum());
  354. }
  355. #[test]
  356. fn test_report_deconstruct() {
  357. let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
  358. assert_eq!(packet.msg_type(), Message::MembershipReportV2);
  359. assert_eq!(packet.max_resp_code(), 0);
  360. assert_eq!(packet.checksum(), 0x08da);
  361. assert_eq!(
  362. packet.group_addr(),
  363. Ipv4Address::from_bytes(&[225, 0, 0, 37])
  364. );
  365. assert!(packet.verify_checksum());
  366. }
  367. #[test]
  368. fn test_leave_construct() {
  369. let mut bytes = vec![0xa5; 8];
  370. let mut packet = Packet::new_unchecked(&mut bytes);
  371. packet.set_msg_type(Message::LeaveGroup);
  372. packet.set_max_resp_code(0);
  373. packet.set_group_address(Ipv4Address::from_bytes(&[224, 0, 6, 150]));
  374. packet.fill_checksum();
  375. assert_eq!(&*packet.into_inner(), &LEAVE_PACKET_BYTES[..]);
  376. }
  377. #[test]
  378. fn test_report_construct() {
  379. let mut bytes = vec![0xa5; 8];
  380. let mut packet = Packet::new_unchecked(&mut bytes);
  381. packet.set_msg_type(Message::MembershipReportV2);
  382. packet.set_max_resp_code(0);
  383. packet.set_group_address(Ipv4Address::from_bytes(&[225, 0, 0, 37]));
  384. packet.fill_checksum();
  385. assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]);
  386. }
  387. #[test]
  388. fn max_resp_time_to_duration_and_back() {
  389. for i in 0..256usize {
  390. let time1 = i as u8;
  391. let duration = max_resp_code_to_duration(time1);
  392. let time2 = duration_to_max_resp_code(duration);
  393. assert!(time1 == time2);
  394. }
  395. }
  396. #[test]
  397. fn duration_to_max_resp_time_max() {
  398. for duration in 31744..65536 {
  399. let time = duration_to_max_resp_code(Duration::from_millis(duration * 100));
  400. assert_eq!(time, 0xFF);
  401. }
  402. }
  403. }