igmp.rs 14 KB

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