1 use super::super::*; 2 3 mod icmpv4_regression { 4 use super::*; 5 6 #[test] icmp4_echo_marshall_unmarshall()7 fn icmp4_echo_marshall_unmarshall() { 8 let icmp4 = Icmpv4Header { 9 icmp_type: Icmpv4Type::EchoRequest(IcmpEchoHeader { seq: 1, id: 2 }), 10 checksum: 0, 11 }; 12 // serialize 13 let mut buffer: Vec<u8> = Vec::with_capacity(256); 14 icmp4.write(&mut buffer).unwrap(); 15 let (new_icmp4, rest) = Icmpv4Header::from_slice(&buffer).unwrap(); 16 assert_eq!(icmp4, new_icmp4); 17 assert_eq!(rest.len(), 0); 18 } 19 20 #[test] ip4_echo_marshall_unmarshall()21 fn ip4_echo_marshall_unmarshall() { 22 let builder = PacketBuilder::ipv4( 23 [192, 168, 1, 1], //source ip 24 [192, 168, 1, 2], //destination ip 25 20, 26 ) //time to life 27 .icmpv4_echo_request(1, 2); 28 let payload = [0xde, 0xad, 0xbe, 0xef]; 29 //get some memory to store the result 30 let mut result = Vec::<u8>::with_capacity(builder.size(payload.len())); 31 32 //serialize 33 builder.write(&mut result, &payload).unwrap(); 34 35 let new_ip = PacketHeaders::from_ip_slice(&result).unwrap(); 36 if let Some(TransportHeader::Icmpv4(hdr)) = new_ip.transport { 37 if let Icmpv4Type::EchoRequest(echo) = hdr.icmp_type { 38 assert_eq!(echo.id, 1); 39 assert_eq!(echo.seq, 2); 40 } else { 41 panic!("Not an EchoRequest!?"); 42 } 43 } else { 44 panic!("No transport header found!?") 45 } 46 } 47 const ICMP4_ECHO_REQUEST_BYTES: [u8; 98] = [ 48 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 49 0x00, 0x00, 0x54, 0x13, 0x6f, 0x40, 0x00, 0x40, 0x01, 0x29, 0x38, 0x7f, 0x00, 0x00, 0x01, 50 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc9, 0x99, 0x00, 0x03, 0x00, 0x01, 0x79, 0xc5, 0xd9, 51 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x68, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 52 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 53 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 54 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 55 ]; 56 57 const ICMP4_ECHO_REPLY_BYTES: [u8; 98] = [ 58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 59 0x00, 0x00, 0x54, 0x13, 0x70, 0x00, 0x00, 0x40, 0x01, 0x69, 0x37, 0x7f, 0x00, 0x00, 0x01, 60 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0xd1, 0x99, 0x00, 0x03, 0x00, 0x01, 0x79, 0xc5, 0xd9, 61 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x68, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 62 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 63 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 64 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 65 ]; 66 67 // real echo request/reply captured from tcpdump 68 // ping 127.0.0.1 to 127.0.0.1 69 #[test] pcap_echo_session()70 fn pcap_echo_session() { 71 let request = PacketHeaders::from_ethernet_slice(&ICMP4_ECHO_REQUEST_BYTES).unwrap(); 72 let request_icmp4 = request.transport.unwrap().icmpv4().unwrap(); 73 match request_icmp4.icmp_type { 74 Icmpv4Type::EchoRequest(echo) => { 75 assert_eq!(echo.seq, 1); 76 assert_eq!(echo.id, 3); // arbitrarily assigned by OS 77 } 78 _ => panic!(r#"Request didn't parse as ICMP4!?"#), 79 } 80 81 let reply = PacketHeaders::from_ethernet_slice(&ICMP4_ECHO_REPLY_BYTES).unwrap(); 82 let reply_icmp4 = reply.transport.unwrap().icmpv4().unwrap(); 83 match reply_icmp4.icmp_type { 84 Icmpv4Type::EchoReply(echo) => { 85 assert_eq!(echo.seq, 1); 86 assert_eq!(echo.id, 3); // arbitrarily assigned by OS 87 } 88 _ => panic!(r#"Request didn't parse as ICMP4!?"#), 89 } 90 let request_iph = request.net.unwrap(); 91 let reply_iph = reply.net.unwrap(); 92 if let NetHeaders::Ipv4(request_ip, _) = request_iph { 93 if let NetHeaders::Ipv4(reply_ip, _) = reply_iph { 94 assert_eq!(reply_ip.source, request_ip.destination); 95 assert_eq!(reply_ip.destination, request_ip.source); 96 } else { 97 panic!("reply ip not v4!?"); 98 } 99 } else { 100 panic!("request ip not v4!?"); 101 } 102 } 103 104 #[test] echo_request_slice()105 fn echo_request_slice() { 106 let echo = SlicedPacket::from_ethernet(&ICMP4_ECHO_REQUEST_BYTES).unwrap(); 107 use TransportSlice::*; 108 let icmp4 = match echo.transport.unwrap() { 109 Icmpv4(icmp4) => icmp4, 110 Icmpv6(_) | Udp(_) | Tcp(_) => panic!("Misparsed header!"), 111 }; 112 assert!(matches!(icmp4.icmp_type(), Icmpv4Type::EchoRequest(_))); 113 } 114 115 #[test] verify_icmp4_checksum()116 fn verify_icmp4_checksum() { 117 for (pkt, checksum) in [ 118 (ICMP4_ECHO_REQUEST_BYTES, 0xc999), 119 (ICMP4_ECHO_REPLY_BYTES, 0xd199), 120 ] { 121 // make sure we can unmarshall the correct checksum 122 let request = PacketHeaders::from_ethernet_slice(&pkt).unwrap(); 123 let mut icmp4 = request.transport.unwrap().icmpv4().unwrap(); 124 let valid_checksum = icmp4.checksum; 125 assert_ne!(valid_checksum, 0); 126 assert_eq!(valid_checksum, checksum); 127 // reset it and recalculate 128 icmp4.checksum = 0; 129 assert_eq!( 130 icmp4.icmp_type.calc_checksum(request.payload.slice()), 131 valid_checksum 132 ); 133 } 134 } 135 136 // TTL unreachable from 'traceroute google.com' 137 const ICMP4_TTL_EXCEEDED_BYTES: [u8; 94] = [ 138 0x98, 0x8d, 0x46, 0xc5, 0x03, 0x82, 0x60, 0xa4, 0xb7, 0x25, 0x4b, 0x84, 0x08, 0x00, 0x45, 139 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x5c, 0x87, 0xd4, 0x9c, 0xc9, 0x72, 140 0xc0, 0xa8, 0x01, 0x6e, 0x0b, 0x00, 0x24, 0x29, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 141 0x3c, 0xe3, 0xaf, 0x00, 0x00, 0x01, 0x11, 0x14, 0x84, 0xc0, 0xa8, 0x01, 0x6e, 0xd8, 0xef, 142 0x26, 0x78, 0xc2, 0x8e, 0x82, 0x9f, 0x00, 0x28, 0x03, 0xed, 0x40, 0x41, 0x42, 0x43, 0x44, 143 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 144 0x54, 0x55, 0x56, 0x57, 145 ]; 146 #[test] parse_icmp4_ttl_exceeded()147 fn parse_icmp4_ttl_exceeded() { 148 let ttl_exceeded = PacketHeaders::from_ethernet_slice(&ICMP4_TTL_EXCEEDED_BYTES).unwrap(); 149 let ip_header = match ttl_exceeded.net.unwrap() { 150 NetHeaders::Ipv4(ip4, _) => ip4, 151 _ => panic!("Didn't parse inner v4 IP header!?"), 152 }; 153 assert_eq!(ip_header.source, [212, 156, 201, 114]); 154 let icmp4 = ttl_exceeded.transport.unwrap().icmpv4().unwrap(); 155 let icmp_bytes = icmp4.to_bytes(); 156 assert_eq!(8, icmp_bytes.len()); 157 assert_eq!(icmp_bytes[0], icmpv4::TYPE_TIME_EXCEEDED); 158 assert_eq!(icmp_bytes[1], 0); // code 159 assert_eq!(&icmp_bytes[4..], &[0; 4]); // TTL exceeded doesn't use this field 160 // now unpack the bounced packet in the payload 161 162 let embedded_pkt = LaxPacketHeaders::from_ip(ttl_exceeded.payload.slice()).unwrap(); 163 let ip_header = match embedded_pkt.net.unwrap() { 164 NetHeaders::Ipv4(ip4, _) => ip4, 165 _ => panic!("Didn't parse inner v4 IP header!?"), 166 }; 167 use std::net::Ipv4Addr; 168 assert_eq!( 169 Ipv4Addr::from(ip_header.source), 170 "192.168.1.110".parse::<Ipv4Addr>().unwrap() 171 ); 172 assert_eq!( 173 Ipv4Addr::from(ip_header.destination), 174 "216.239.38.120".parse::<Ipv4Addr>().unwrap() 175 ); 176 let udp_header = embedded_pkt.transport.unwrap().udp().unwrap(); 177 assert_eq!(udp_header.source_port, 49806); // numbers read from wireshark 178 assert_eq!(udp_header.destination_port, 33439); 179 } 180 181 const ICMP4_PORT_UNREACHABLE_BYTES: [u8; 70] = [ 182 0x98, 0x8d, 0x46, 0xc5, 0x03, 0x82, 0x60, 0xa4, 0xb7, 0x25, 0x4b, 0x84, 0x08, 0x00, 0x45, 183 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x39, 0x01, 0xc0, 0x47, 0xd8, 0xef, 0x26, 0x78, 184 0xc0, 0xa8, 0x01, 0x6e, 0x03, 0x03, 0xb3, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x45, 0x80, 0x00, 185 0x3c, 0xe3, 0xd2, 0x00, 0x00, 0x01, 0x11, 0x13, 0xe1, 0xc0, 0xa8, 0x01, 0x6e, 0xd8, 0xef, 186 0x26, 0x78, 0xb3, 0x4e, 0x82, 0xb2, 0x00, 0x28, 0x13, 0x1a, 187 ]; 188 #[test] icmp4_dst_unreachable()189 fn icmp4_dst_unreachable() { 190 let offset = 14 + 20 + 1; // ethernet + iphdr + icmp_type 191 // test all of the unreachable codes to make sure the maps are right 192 for code_u8 in 0..icmpv4::CODE_DST_UNREACH_PRECEDENCE_CUTOFF { 193 let mut pkt = ICMP4_PORT_UNREACHABLE_BYTES.clone(); 194 pkt[offset] = code_u8; // over write the code 195 let parsed = PacketHeaders::from_ethernet_slice(&pkt).unwrap(); 196 let icmp4 = parsed.transport.unwrap().icmpv4().unwrap(); 197 if let Icmpv4Type::DestinationUnreachable(icmp_code) = icmp4.icmp_type { 198 assert_eq!(code_u8, icmp_code.code_u8()); 199 } else { 200 panic!("Not destination unreachable!?"); 201 } 202 } 203 } 204 } 205