icmpv4.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. use core::fmt;
  2. use byteorder::{ByteOrder, NetworkEndian};
  3. use Error;
  4. use super::ip::rfc1071_checksum;
  5. enum_with_unknown! {
  6. /// Internet protocol control message type.
  7. pub doc enum Type(u8) {
  8. /// Echo reply
  9. EchoReply = 0,
  10. /// Destination unreachable
  11. DstUnreachable = 1,
  12. /// Message redirect
  13. Redirect = 5,
  14. /// Echo request
  15. EchoRequest = 8,
  16. /// Router advertisement
  17. RouterAdvert = 9,
  18. /// Router solicitation
  19. RouterSolicit = 10,
  20. /// Time exceeded
  21. TimeExceeded = 11,
  22. /// Parameter problem
  23. ParamProblem = 12,
  24. /// Timestamp
  25. Timestamp = 13,
  26. /// Timestamp reply
  27. TimestampReply = 14
  28. }
  29. }
  30. impl fmt::Display for Type {
  31. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  32. match self {
  33. &Type::EchoReply => write!(f, "echo reply"),
  34. &Type::DstUnreachable => write!(f, "destination unreachable"),
  35. &Type::Redirect => write!(f, "message redirect"),
  36. &Type::EchoRequest => write!(f, "echo request"),
  37. &Type::RouterAdvert => write!(f, "router advertisement"),
  38. &Type::RouterSolicit => write!(f, "router solicitation"),
  39. &Type::TimeExceeded => write!(f, "time exceeded"),
  40. &Type::ParamProblem => write!(f, "parameter problem"),
  41. &Type::Timestamp => write!(f, "timestamp"),
  42. &Type::TimestampReply => write!(f, "timestamp reply"),
  43. &Type::Unknown(id) => write!(f, "{}", id)
  44. }
  45. }
  46. }
  47. enum_with_unknown! {
  48. /// Internet protocol control message subtype for type "Destination Unreachable".
  49. pub doc enum DstUnreachable(u8) {
  50. /// Destination network unreachable
  51. NetUnreachable = 0,
  52. /// Destination host unreachable
  53. HostUnreachable = 1,
  54. /// Destination protocol unreachable
  55. ProtoUnreachable = 2,
  56. /// Destination port unreachable
  57. PortUnreachable = 3,
  58. /// Fragmentation required, and DF flag set
  59. FragRequired = 4,
  60. /// Source route failed
  61. SrcRouteFailed = 5,
  62. /// Destination network unknown
  63. DstNetUnknown = 6,
  64. /// Destination host unknown
  65. DstHostUnknown = 7,
  66. /// Source host isolated
  67. SrcHostIsolated = 8,
  68. /// Network administratively prohibited
  69. NetProhibited = 9,
  70. /// Host administratively prohibited
  71. HostProhibited = 10,
  72. /// Network unreachable for ToS
  73. NetUnreachToS = 11,
  74. /// Host unreachable for ToS
  75. HostUnreachToS = 12,
  76. /// Communication administratively prohibited
  77. CommProhibited = 13,
  78. /// Host precedence violation
  79. HostPrecedViol = 14,
  80. /// Precedence cutoff in effect
  81. PrecedCutoff = 15
  82. }
  83. }
  84. enum_with_unknown! {
  85. /// Internet protocol control message subtype for type "Redirect Message".
  86. pub doc enum Redirect(u8) {
  87. /// Redirect Datagram for the Network
  88. Net = 0,
  89. /// Redirect Datagram for the Host
  90. Host = 1,
  91. /// Redirect Datagram for the ToS & network
  92. NetToS = 2,
  93. /// Redirect Datagram for the ToS & host
  94. HostToS = 3
  95. }
  96. }
  97. enum_with_unknown! {
  98. /// Internet protocol control message subtype for type "Time Exceeded".
  99. pub doc enum TimeExceeded(u8) {
  100. /// TTL expired in transit
  101. TtlExpired = 0,
  102. /// Fragment reassembly time exceeded
  103. FragExpired = 1
  104. }
  105. }
  106. enum_with_unknown! {
  107. /// Internet protocol control message subtype for type "Parameter Problem".
  108. pub doc enum ParamProblem(u8) {
  109. /// Pointer indicates the error
  110. AtPointer = 0,
  111. /// Missing a required option
  112. MissingOption = 1,
  113. /// Bad length
  114. BadLength = 2
  115. }
  116. }
  117. /// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer.
  118. #[derive(Debug)]
  119. pub struct Packet<T: AsRef<[u8]>> {
  120. buffer: T
  121. }
  122. mod field {
  123. #![allow(non_snake_case)]
  124. use wire::field::*;
  125. pub const TYPE: usize = 0;
  126. pub const CODE: usize = 1;
  127. pub const CHECKSUM: Field = 2..4;
  128. pub const ECHO_IDENT: Field = 4..6;
  129. pub const ECHO_SEQNO: Field = 6..8;
  130. }
  131. impl<T: AsRef<[u8]>> Packet<T> {
  132. /// Wrap a buffer with an ICMPv4 packet. Returns an error if the buffer
  133. /// is too small to contain one.
  134. pub fn new(buffer: T) -> Result<Packet<T>, Error> {
  135. let len = buffer.as_ref().len();
  136. if len < field::CHECKSUM.end {
  137. Err(Error::Truncated)
  138. } else {
  139. let packet = Packet { buffer: buffer };
  140. if len < packet.header_len() {
  141. Err(Error::Truncated)
  142. } else {
  143. Ok(packet)
  144. }
  145. }
  146. }
  147. /// Consumes the packet, returning the underlying buffer.
  148. pub fn into_inner(self) -> T {
  149. self.buffer
  150. }
  151. /// Return the message type field.
  152. #[inline(always)]
  153. pub fn msg_type(&self) -> Type {
  154. let data = self.buffer.as_ref();
  155. Type::from(data[field::TYPE])
  156. }
  157. /// Return the message code field.
  158. #[inline(always)]
  159. pub fn msg_code(&self) -> u8 {
  160. let data = self.buffer.as_ref();
  161. data[field::CODE]
  162. }
  163. /// Return the checksum field.
  164. #[inline(always)]
  165. pub fn checksum(&self) -> u16 {
  166. let data = self.buffer.as_ref();
  167. NetworkEndian::read_u16(&data[field::CHECKSUM])
  168. }
  169. /// Return the identifier field (for echo request and reply packets).
  170. ///
  171. /// # Panics
  172. /// This function may panic if this packet is not an echo request or reply packet.
  173. #[inline(always)]
  174. pub fn echo_ident(&self) -> u16 {
  175. let data = self.buffer.as_ref();
  176. NetworkEndian::read_u16(&data[field::ECHO_IDENT])
  177. }
  178. /// Return the sequence number field (for echo request and reply packets).
  179. ///
  180. /// # Panics
  181. /// This function may panic if this packet is not an echo request or reply packet.
  182. #[inline(always)]
  183. pub fn echo_seq_no(&self) -> u16 {
  184. let data = self.buffer.as_ref();
  185. NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
  186. }
  187. /// Return the header length.
  188. /// The result depends on the value of the message type field.
  189. pub fn header_len(&self) -> usize {
  190. match self.msg_type() {
  191. Type::EchoRequest => field::ECHO_SEQNO.end,
  192. Type::EchoReply => field::ECHO_SEQNO.end,
  193. _ => field::CHECKSUM.end // make a conservative assumption
  194. }
  195. }
  196. /// Return a pointer to the type-specific data.
  197. #[inline(always)]
  198. pub fn data(&self) -> &[u8] {
  199. let data = self.buffer.as_ref();
  200. &data[self.header_len()..]
  201. }
  202. /// Validate the header checksum.
  203. pub fn verify_checksum(&self) -> bool {
  204. let checksum = {
  205. let data = self.buffer.as_ref();
  206. rfc1071_checksum(field::CHECKSUM.start, &data[..self.header_len()])
  207. };
  208. self.checksum() == checksum
  209. }
  210. }
  211. impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
  212. /// Set the message type field.
  213. #[inline(always)]
  214. pub fn set_msg_type(&mut self, value: Type) {
  215. let mut data = self.buffer.as_mut();
  216. data[field::TYPE] = value.into()
  217. }
  218. /// Set the message code field.
  219. #[inline(always)]
  220. pub fn set_msg_code(&mut self, value: u8) {
  221. let mut data = self.buffer.as_mut();
  222. data[field::CODE] = value
  223. }
  224. /// Set the checksum field.
  225. #[inline(always)]
  226. pub fn set_checksum(&mut self, value: u16) {
  227. let mut data = self.buffer.as_mut();
  228. NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
  229. }
  230. /// Set the identifier field (for echo request and reply packets).
  231. ///
  232. /// # Panics
  233. /// This function may panic if this packet is not an echo request or reply packet.
  234. #[inline(always)]
  235. pub fn set_echo_ident(&mut self, value: u16) {
  236. let mut data = self.buffer.as_mut();
  237. NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value)
  238. }
  239. /// Set the sequence number field (for echo request and reply packets).
  240. ///
  241. /// # Panics
  242. /// This function may panic if this packet is not an echo request or reply packet.
  243. #[inline(always)]
  244. pub fn set_echo_seq_no(&mut self, value: u16) {
  245. let mut data = self.buffer.as_mut();
  246. NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
  247. }
  248. /// Return a mutable pointer to the type-specific data.
  249. #[inline(always)]
  250. pub fn data_mut(&mut self) -> &mut [u8] {
  251. let range = self.header_len()..;
  252. let mut data = self.buffer.as_mut();
  253. &mut data[range]
  254. }
  255. /// Compute and fill in the header checksum.
  256. pub fn fill_checksum(&mut self) {
  257. let checksum = {
  258. let data = self.buffer.as_ref();
  259. rfc1071_checksum(field::CHECKSUM.start, &data[..self.header_len()])
  260. };
  261. self.set_checksum(checksum)
  262. }
  263. }
  264. /// A high-level representation of an Internet Control Message Protocol version 4 packet header.
  265. #[derive(Debug, PartialEq, Eq, Clone, Copy)]
  266. pub enum Repr<'a> {
  267. EchoRequest {
  268. ident: u16,
  269. seq_no: u16,
  270. data: &'a [u8]
  271. },
  272. EchoReply {
  273. ident: u16,
  274. seq_no: u16,
  275. data: &'a [u8]
  276. },
  277. #[doc(hidden)]
  278. __Nonexhaustive
  279. }
  280. impl<'a> Repr<'a> {
  281. /// Parse an Internet Control Message Protocol version 4 packet and return
  282. /// a high-level representation, or return `Err(())` if the packet is not recognized
  283. /// or is malformed.
  284. pub fn parse<T: AsRef<[u8]>>(packet: &'a Packet<T>) -> Result<Repr<'a>, Error> {
  285. match (packet.msg_type(), packet.msg_code()) {
  286. (Type::EchoRequest, 0) => {
  287. Ok(Repr::EchoRequest {
  288. ident: packet.echo_ident(),
  289. seq_no: packet.echo_seq_no(),
  290. data: packet.data()
  291. })
  292. },
  293. (Type::EchoReply, 0) => {
  294. Ok(Repr::EchoReply {
  295. ident: packet.echo_ident(),
  296. seq_no: packet.echo_seq_no(),
  297. data: packet.data()
  298. })
  299. },
  300. _ => Err(Error::Unrecognized)
  301. }
  302. }
  303. /// Emit a high-level representation into an Internet Protocol version 4 packet.
  304. pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
  305. packet.set_msg_code(0);
  306. match self {
  307. &Repr::EchoRequest { ident, seq_no, data } => {
  308. packet.set_msg_type(Type::EchoRequest);
  309. packet.set_echo_ident(ident);
  310. packet.set_echo_seq_no(seq_no);
  311. packet.data_mut().copy_from_slice(data)
  312. },
  313. &Repr::EchoReply { ident, seq_no, data } => {
  314. packet.set_msg_type(Type::EchoReply);
  315. packet.set_echo_ident(ident);
  316. packet.set_echo_seq_no(seq_no);
  317. packet.data_mut().copy_from_slice(data)
  318. },
  319. &Repr::__Nonexhaustive => unreachable!()
  320. }
  321. packet.fill_checksum()
  322. }
  323. }
  324. impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
  325. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  326. match Repr::parse(self) {
  327. Ok(repr) => write!(f, "{}", repr),
  328. _ => {
  329. write!(f, "ICMPv4 type={} code={}",
  330. self.msg_type(), self.msg_code())
  331. }
  332. }
  333. }
  334. }
  335. impl<'a> fmt::Display for Repr<'a> {
  336. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  337. match self {
  338. &Repr::EchoRequest { ident, seq_no, .. } =>
  339. write!(f, "ICMPv4 Echo Request id={} seq={}", ident, seq_no),
  340. &Repr::EchoReply { ident, seq_no, .. } =>
  341. write!(f, "ICMPv4 Echo Reply id={} seq={}", ident, seq_no),
  342. &Repr::__Nonexhaustive => unreachable!()
  343. }
  344. }
  345. }
  346. use super::pretty_print::{PrettyPrint, PrettyIndent};
  347. impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
  348. fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
  349. indent: &mut PrettyIndent) -> fmt::Result {
  350. match Packet::new(buffer) {
  351. Err(err) => write!(f, "{}({})\n", indent, err),
  352. Ok(frame) => write!(f, "{}{}\n", indent, frame)
  353. }
  354. }
  355. }
  356. #[cfg(test)]
  357. mod test {
  358. use super::*;
  359. static ECHO_PACKET_BYTES: [u8; 12] =
  360. [0x08, 0x00, 0x39, 0xfe,
  361. 0x12, 0x34, 0xab, 0xcd,
  362. 0xaa, 0x00, 0x00, 0xff];
  363. static ECHO_DATA_BYTES: [u8; 4] =
  364. [0xaa, 0x00, 0x00, 0xff];
  365. #[test]
  366. fn test_echo_deconstruct() {
  367. let packet = Packet::new(&ECHO_PACKET_BYTES[..]).unwrap();
  368. assert_eq!(packet.msg_type(), Type::EchoRequest);
  369. assert_eq!(packet.msg_code(), 0);
  370. assert_eq!(packet.checksum(), 0x39fe);
  371. assert_eq!(packet.echo_ident(), 0x1234);
  372. assert_eq!(packet.echo_seq_no(), 0xabcd);
  373. assert_eq!(packet.verify_checksum(), true);
  374. assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
  375. }
  376. #[test]
  377. fn test_echo_construct() {
  378. let mut bytes = vec![0; 12];
  379. let mut packet = Packet::new(&mut bytes).unwrap();
  380. packet.set_msg_type(Type::EchoRequest);
  381. packet.set_msg_code(0);
  382. packet.set_echo_ident(0x1234);
  383. packet.set_echo_seq_no(0xabcd);
  384. packet.fill_checksum();
  385. packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]);
  386. assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
  387. }
  388. fn echo_packet_repr() -> Repr<'static> {
  389. Repr::EchoRequest {
  390. ident: 0x1234,
  391. seq_no: 0xabcd,
  392. data: &ECHO_DATA_BYTES
  393. }
  394. }
  395. #[test]
  396. fn test_echo_parse() {
  397. let packet = Packet::new(&ECHO_PACKET_BYTES[..]).unwrap();
  398. let repr = Repr::parse(&packet).unwrap();
  399. assert_eq!(repr, echo_packet_repr());
  400. }
  401. #[test]
  402. fn test_echo_emit() {
  403. let mut bytes = vec![0; 12];
  404. let mut packet = Packet::new(&mut bytes).unwrap();
  405. echo_packet_repr().emit(&mut packet);
  406. assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
  407. }
  408. }