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;
  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 as usize {
  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.as_bytes());
  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. // Check if the address is 0.0.0.0 or multicast
  184. let addr = packet.group_addr();
  185. if !addr.is_unspecified() && !addr.is_multicast() {
  186. return Err(Error);
  187. }
  188. // construct a packet based on the Type field
  189. match packet.msg_type() {
  190. Message::MembershipQuery => {
  191. let max_resp_time = max_resp_code_to_duration(packet.max_resp_code());
  192. // See RFC 3376: 7.1. Query Version Distinctions
  193. let version = if packet.max_resp_code() == 0 {
  194. IgmpVersion::Version1
  195. } else {
  196. IgmpVersion::Version2
  197. };
  198. Ok(Repr::MembershipQuery {
  199. max_resp_time,
  200. group_addr: addr,
  201. version,
  202. })
  203. }
  204. Message::MembershipReportV2 => Ok(Repr::MembershipReport {
  205. group_addr: packet.group_addr(),
  206. version: IgmpVersion::Version2,
  207. }),
  208. Message::LeaveGroup => Ok(Repr::LeaveGroup {
  209. group_addr: packet.group_addr(),
  210. }),
  211. Message::MembershipReportV1 => {
  212. // for backwards compatibility with IGMPv1
  213. Ok(Repr::MembershipReport {
  214. group_addr: packet.group_addr(),
  215. version: IgmpVersion::Version1,
  216. })
  217. }
  218. _ => Err(Error),
  219. }
  220. }
  221. /// Return the length of a packet that will be emitted from this high-level representation.
  222. pub const fn buffer_len(&self) -> usize {
  223. // always 8 bytes
  224. field::GROUP_ADDRESS.end
  225. }
  226. /// Emit a high-level representation into an Internet Group Management Protocol v2 packet.
  227. pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
  228. where
  229. T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
  230. {
  231. match *self {
  232. Repr::MembershipQuery {
  233. max_resp_time,
  234. group_addr,
  235. version,
  236. } => {
  237. packet.set_msg_type(Message::MembershipQuery);
  238. match version {
  239. IgmpVersion::Version1 => packet.set_max_resp_code(0),
  240. IgmpVersion::Version2 => {
  241. packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time))
  242. }
  243. }
  244. packet.set_group_address(group_addr);
  245. }
  246. Repr::MembershipReport {
  247. group_addr,
  248. version,
  249. } => {
  250. match version {
  251. IgmpVersion::Version1 => packet.set_msg_type(Message::MembershipReportV1),
  252. IgmpVersion::Version2 => packet.set_msg_type(Message::MembershipReportV2),
  253. };
  254. packet.set_max_resp_code(0);
  255. packet.set_group_address(group_addr);
  256. }
  257. Repr::LeaveGroup { group_addr } => {
  258. packet.set_msg_type(Message::LeaveGroup);
  259. packet.set_group_address(group_addr);
  260. }
  261. }
  262. packet.fill_checksum()
  263. }
  264. }
  265. fn max_resp_code_to_duration(value: u8) -> Duration {
  266. let value: u64 = value.into();
  267. let decisecs = if value < 128 {
  268. value
  269. } else {
  270. let mant = value & 0xF;
  271. let exp = (value >> 4) & 0x7;
  272. (mant | 0x10) << (exp + 3)
  273. };
  274. Duration::from_millis(decisecs * 100)
  275. }
  276. const fn duration_to_max_resp_code(duration: Duration) -> u8 {
  277. let decisecs = duration.total_millis() / 100;
  278. if decisecs < 128 {
  279. decisecs as u8
  280. } else if decisecs < 31744 {
  281. let mut mant = decisecs >> 3;
  282. let mut exp = 0u8;
  283. while mant > 0x1F && exp < 0x8 {
  284. mant >>= 1;
  285. exp += 1;
  286. }
  287. 0x80 | (exp << 4) | (mant as u8 & 0xF)
  288. } else {
  289. 0xFF
  290. }
  291. }
  292. impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
  293. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  294. match Repr::parse(self) {
  295. Ok(repr) => write!(f, "{}", repr),
  296. Err(err) => write!(f, "IGMP ({})", err),
  297. }
  298. }
  299. }
  300. impl fmt::Display for Repr {
  301. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  302. match *self {
  303. Repr::MembershipQuery {
  304. max_resp_time,
  305. group_addr,
  306. version,
  307. } => write!(
  308. f,
  309. "IGMP membership query max_resp_time={} group_addr={} version={:?}",
  310. max_resp_time, group_addr, version
  311. ),
  312. Repr::MembershipReport {
  313. group_addr,
  314. version,
  315. } => write!(
  316. f,
  317. "IGMP membership report group_addr={} version={:?}",
  318. group_addr, version
  319. ),
  320. Repr::LeaveGroup { group_addr } => {
  321. write!(f, "IGMP leave group group_addr={})", group_addr)
  322. }
  323. }
  324. }
  325. }
  326. use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
  327. impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
  328. fn pretty_print(
  329. buffer: &dyn AsRef<[u8]>,
  330. f: &mut fmt::Formatter,
  331. indent: &mut PrettyIndent,
  332. ) -> fmt::Result {
  333. match Packet::new_checked(buffer) {
  334. Err(err) => writeln!(f, "{}({})", indent, err),
  335. Ok(packet) => writeln!(f, "{}{}", indent, packet),
  336. }
  337. }
  338. }
  339. #[cfg(test)]
  340. mod test {
  341. use super::*;
  342. static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96];
  343. static REPORT_PACKET_BYTES: [u8; 8] = [0x16, 0x00, 0x08, 0xda, 0xe1, 0x00, 0x00, 0x25];
  344. #[test]
  345. fn test_leave_group_deconstruct() {
  346. let packet = Packet::new_unchecked(&LEAVE_PACKET_BYTES[..]);
  347. assert_eq!(packet.msg_type(), Message::LeaveGroup);
  348. assert_eq!(packet.max_resp_code(), 0);
  349. assert_eq!(packet.checksum(), 0x269);
  350. assert_eq!(
  351. packet.group_addr(),
  352. Ipv4Address::from_bytes(&[224, 0, 6, 150])
  353. );
  354. assert!(packet.verify_checksum());
  355. }
  356. #[test]
  357. fn test_report_deconstruct() {
  358. let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
  359. assert_eq!(packet.msg_type(), Message::MembershipReportV2);
  360. assert_eq!(packet.max_resp_code(), 0);
  361. assert_eq!(packet.checksum(), 0x08da);
  362. assert_eq!(
  363. packet.group_addr(),
  364. Ipv4Address::from_bytes(&[225, 0, 0, 37])
  365. );
  366. assert!(packet.verify_checksum());
  367. }
  368. #[test]
  369. fn test_leave_construct() {
  370. let mut bytes = vec![0xa5; 8];
  371. let mut packet = Packet::new_unchecked(&mut bytes);
  372. packet.set_msg_type(Message::LeaveGroup);
  373. packet.set_max_resp_code(0);
  374. packet.set_group_address(Ipv4Address::from_bytes(&[224, 0, 6, 150]));
  375. packet.fill_checksum();
  376. assert_eq!(&*packet.into_inner(), &LEAVE_PACKET_BYTES[..]);
  377. }
  378. #[test]
  379. fn test_report_construct() {
  380. let mut bytes = vec![0xa5; 8];
  381. let mut packet = Packet::new_unchecked(&mut bytes);
  382. packet.set_msg_type(Message::MembershipReportV2);
  383. packet.set_max_resp_code(0);
  384. packet.set_group_address(Ipv4Address::from_bytes(&[225, 0, 0, 37]));
  385. packet.fill_checksum();
  386. assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]);
  387. }
  388. #[test]
  389. fn max_resp_time_to_duration_and_back() {
  390. for i in 0..256usize {
  391. let time1 = i as u8;
  392. let duration = max_resp_code_to_duration(time1);
  393. let time2 = duration_to_max_resp_code(duration);
  394. assert!(time1 == time2);
  395. }
  396. }
  397. #[test]
  398. fn duration_to_max_resp_time_max() {
  399. for duration in 31744..65536 {
  400. let time = duration_to_max_resp_code(Duration::from_millis(duration * 100));
  401. assert_eq!(time, 0xFF);
  402. }
  403. }
  404. }