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