Skip to main content

manycastr/net/
mod.rs

1extern crate byteorder;
2use std::io::{Cursor, Write};
3
4use crate::custom_module::manycastr::{Address, address};
5pub(crate) use crate::net::icmp::ICMPPacket;
6pub(crate) use crate::net::tcp::TCPPacket;
7pub(crate) use crate::net::udp::{DNSAnswer, DNSRecord, TXTRecord, UDPPacket};
8use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
9
10mod icmp;
11pub(crate) mod packet;
12mod tcp;
13mod udp;
14
15/// A struct detailing an IPv4Packet <https://en.wikipedia.org/wiki/Internet_Protocol_version_4>
16#[derive(Debug)]
17pub struct IPv4Packet {
18    pub length: u16,            // 16-bit Total Length
19    pub ttl: u8,                // 8-bit Time To Live
20    pub src: u32,               // 32-bit Source IP Address
21    pub dst: u32,               // 32-bit Destination IP Address
22    pub payload: PacketPayload, // Payload
23    pub identifier: u16,        // 16-bit Identification
24}
25
26/// Convert list of u8 (i.e. received bytes) into an IPv4Packet
27impl From<&[u8]> for IPv4Packet {
28    fn from(data: &[u8]) -> Self {
29        let mut cursor = Cursor::new(data);
30        // Get header length, which is the 4 right bits in the first byte (hence & 0xF)
31        // header length is in number of 32 bits i.e. 4 bytes (hence *4)
32        let ihl: usize = ((cursor.read_u8().unwrap() & 0xF) * 4).into(); // Total Length
33        let identifier = cursor.read_u16::<NetworkEndian>().unwrap(); // Identification
34        cursor.set_position(8); // Time To Live
35        let ttl = cursor.read_u8().unwrap();
36        let packet_type = cursor.read_u8().unwrap(); // Protocol
37        cursor.set_position(12); // Address fields
38        let src = cursor.read_u32::<NetworkEndian>().unwrap(); // Source IP Address
39        let dst = cursor.read_u32::<NetworkEndian>().unwrap(); // Destination IP Address
40
41        // If the header length is longer than the data, the packet is incomplete
42        if ihl > data.len() {
43            return IPv4Packet {
44                length: ihl as u16,
45                ttl,
46                src,
47                dst,
48                payload: PacketPayload::Unimplemented,
49                identifier,
50            };
51        }
52
53        // The payload starts after the header (skipping any options)
54        let payload_bytes = &data[ihl..];
55
56        let payload = match packet_type {
57            1 => {
58                if payload_bytes.len() < 8 {
59                    PacketPayload::Unimplemented
60                } else {
61                    PacketPayload::Icmp {
62                        value: ICMPPacket::from(payload_bytes),
63                    }
64                }
65            }
66            17 => {
67                if payload_bytes.len() < 8 {
68                    PacketPayload::Unimplemented
69                } else {
70                    PacketPayload::Udp {
71                        value: UDPPacket::from(payload_bytes),
72                    }
73                }
74            }
75            6 => {
76                if payload_bytes.len() < 20 {
77                    PacketPayload::Unimplemented
78                } else {
79                    PacketPayload::Tcp {
80                        value: TCPPacket::from(payload_bytes),
81                    }
82                }
83            }
84            _ => PacketPayload::Unimplemented,
85        };
86
87        IPv4Packet {
88            length: ihl as u16,
89            ttl,
90            src,
91            dst,
92            payload,
93            identifier,
94        }
95    }
96}
97
98/// Convert IPv4Packet into a vector of bytes
99impl From<&IPv4Packet> for Vec<u8> {
100    fn from(packet: &IPv4Packet) -> Self {
101        let (payload_type, payload) = match &packet.payload {
102            PacketPayload::Icmp { value } => (1, value.into()),
103            PacketPayload::Udp { value } => (17, value.into()),
104            PacketPayload::Tcp { value } => (6, value.into()),
105            PacketPayload::Unimplemented => (0, vec![]),
106        };
107
108        const TOTAL_HEADER_LENGTH: usize = 20; // Base header length (no options)
109        let ihl = (TOTAL_HEADER_LENGTH / 4) as u8;
110
111        let mut wtr = Vec::with_capacity(TOTAL_HEADER_LENGTH);
112        wtr.write_u8((4 << 4) | ihl)
113            .expect("Unable to write to byte buffer for IPv4 packet"); // Version (4) and header length (5)
114        wtr.write_u8(0x00)
115            .expect("Unable to write to byte buffer for IPv4 packet"); // Type of Service
116        wtr.write_u16::<NetworkEndian>(packet.length)
117            .expect("Unable to write to byte buffer for IPv4 packet"); // Total Length
118        wtr.write_u16::<NetworkEndian>(packet.identifier)
119            .expect("Unable to write to byte buffer for IPv4 packet"); // Identification
120        wtr.write_u16::<NetworkEndian>(0x0000)
121            .expect("Unable to write to byte buffer for IPv4 packet"); // Flags (0) and Fragment Offset (0)
122        wtr.write_u8(packet.ttl)
123            .expect("Unable to write to byte buffer for IPv4 packet"); // Time To Live
124        wtr.write_u8(payload_type)
125            .expect("Unable to write to byte buffer for IPv4 packet"); // Protocol (ICMP)
126        wtr.write_u16::<NetworkEndian>(0x0000)
127            .expect("Unable to write to byte buffer for IPv4 packet"); // Header Checksum
128        wtr.write_u32::<NetworkEndian>(packet.src)
129            .expect("Unable to write to byte buffer for IPv4 packet"); // Source IP Address
130        wtr.write_u32::<NetworkEndian>(packet.dst)
131            .expect("Unable to write to byte buffer for IPv4 packet"); // Destination IP Address
132
133        // Calculate and write the checksum
134        let checksum = ICMPPacket::calc_checksum(&wtr);
135        let mut cursor = Cursor::new(wtr);
136        cursor.set_position(10); // Checksum position
137        cursor.write_u16::<NetworkEndian>(checksum).unwrap();
138
139        cursor.set_position(TOTAL_HEADER_LENGTH as u64); // Skip the IP header
140
141        // Add the payload
142        cursor
143            .write_all(&payload)
144            .expect("Unable to write to byte buffer for IPv4 packet"); // Payload
145
146        cursor.into_inner()
147    }
148}
149
150/// A struct detailing an IPv6Packet <https://en.wikipedia.org/wiki/IPv6>
151#[derive(Debug)]
152pub struct IPv6Packet {
153    // pub version: u8,                 // 4-bit Version
154    // pub traffic_class: u8,           // 8-bit Traffic Class
155    pub flow_label: u32,        // 20-bit Flow Label
156    pub payload_length: u16,    // 16-bit Payload Length
157    pub next_header: u8,        // 8-bit Next Header
158    pub hop_limit: u8,          // 8-bit Hop Limit
159    pub src: u128,              // 128-bit Source Address
160    pub dst: u128,              // 128-bit Destination Address
161    pub payload: PacketPayload, // Payload
162}
163
164/// Convert bytes into an IPv6Packet
165impl From<&[u8]> for IPv6Packet {
166    fn from(data: &[u8]) -> Self {
167        let mut cursor = Cursor::new(data);
168        let _version_traffic_flow: u32 = cursor.read_u32::<NetworkEndian>().unwrap();
169        let flow_label = _version_traffic_flow & 0x000FFFFF; // Lower 20 bits
170        let payload_length = cursor.read_u16::<NetworkEndian>().unwrap();
171        let next_header = cursor.read_u8().unwrap();
172        let hop_limit = cursor.read_u8().unwrap();
173
174        let src = cursor.read_u128::<NetworkEndian>().unwrap(); // Source Address
175        let dst = cursor.read_u128::<NetworkEndian>().unwrap(); // Destination Address
176        let payload = &cursor.into_inner()[40..]; // IPv6 header is 40 bytes
177
178        // Implement PacketPayload based on the next_header value
179        let payload = match next_header {
180            58 => {
181                // ICMPv6
182                PacketPayload::Icmp {
183                    value: ICMPPacket::from(payload),
184                }
185            }
186            17 => {
187                // UDP
188                if payload.len() < 8 {
189                    PacketPayload::Unimplemented
190                } else {
191                    PacketPayload::Udp {
192                        value: UDPPacket::from(payload),
193                    }
194                }
195            }
196            6 => {
197                // TCP
198                if payload.len() < 20 {
199                    PacketPayload::Unimplemented
200                } else {
201                    PacketPayload::Tcp {
202                        value: TCPPacket::from(payload),
203                    }
204                }
205            }
206            _ => PacketPayload::Unimplemented, // Extension headers
207        };
208
209        IPv6Packet {
210            // version: (version_traffic_class >> 12) as u8,
211            // traffic_class: ((version_traffic_class >> 4) & 0xFF) as u8,
212            flow_label,
213            payload_length,
214            next_header,
215            hop_limit,
216            src,
217            dst,
218            payload,
219        }
220    }
221}
222
223/// Convert an IPv6Packet into bytes
224impl From<&IPv6Packet> for Vec<u8> {
225    fn from(packet: &IPv6Packet) -> Self {
226        let mut wtr = vec![];
227        // Version (6), Traffic Class (0), Flow Label (from struct)
228        let vtf = 0x6000_0000u32 | (packet.flow_label & 0x000F_FFFF);
229        wtr.write_u32::<NetworkEndian>(vtf)
230            .expect("Unable to write to byte buffer for IPv6Packet");
231        wtr.write_u16::<NetworkEndian>(packet.payload_length)
232            .expect("Unable to write to byte buffer for IPv6Packet");
233        wtr.write_u8(packet.next_header)
234            .expect("Unable to write to byte buffer for IPv6Packet");
235        wtr.write_u8(packet.hop_limit)
236            .expect("Unable to write to byte buffer for IPv6Packet");
237        wtr.write_u128::<NetworkEndian>(packet.src)
238            .expect("Unable to write source address to byte buffer for IPv6Packet");
239        wtr.write_u128::<NetworkEndian>(packet.dst)
240            .expect("Unable to write destination address to byte buffer for IPv6Packet");
241
242        let payload = match &packet.payload {
243            PacketPayload::Icmp { value } => value.into(),
244            PacketPayload::Udp { value } => value.into(),
245            PacketPayload::Tcp { value } => value.into(),
246            PacketPayload::Unimplemented => vec![],
247        };
248
249        wtr.write_all(&payload)
250            .expect("Unable to write payload to byte buffer for IPv6Packet");
251
252        wtr
253    }
254}
255
256/// Definition of the IPV4Packet payload (either ICMPv4, UDP, TCP, or unimplemented)
257#[derive(Debug)]
258pub enum PacketPayload {
259    Icmp { value: ICMPPacket },
260    Udp { value: UDPPacket },
261    Tcp { value: TCPPacket },
262    Unimplemented,
263}
264
265/// Convert a packet payload to bytes
266impl From<PacketPayload> for Vec<u8> {
267    fn from(payload: PacketPayload) -> Self {
268        match payload {
269            PacketPayload::Icmp { value } => (&value).into(),
270            PacketPayload::Udp { value } => (&value).into(),
271            PacketPayload::Tcp { value } => (&value).into(),
272            PacketPayload::Unimplemented => vec![],
273        }
274    }
275}
276
277/// Wrap a transport-layer payload in an IPv4 or IPv6 header.
278///
279/// The IP protocol / IPv6 next-header is derived from the payload variant. `identifier` is
280/// written to the IPv4 Identification field, or to the low 16 bits of the IPv6 Flow Label.
281///
282/// # Returns
283/// The packet as a vector of bytes.
284///
285/// # Panics
286/// If `src` and `dst` are different IP versions (or unset).
287pub(crate) fn build_ip_packet(
288    src: &Address,
289    dst: &Address,
290    ttl: u8,
291    identifier: u16,
292    payload: PacketPayload,
293) -> Vec<u8> {
294    // Serialized length of the transport payload + the IP protocol number for that payload.
295    let (l4_len, next_header) = match &payload {
296        PacketPayload::Icmp { value } => (Vec::<u8>::from(value).len(), 58u8), // 1 (v4) handled below
297        PacketPayload::Udp { value } => (Vec::<u8>::from(value).len(), 17u8),
298        PacketPayload::Tcp { value } => (Vec::<u8>::from(value).len(), 6u8),
299        PacketPayload::Unimplemented => (0usize, 0u8),
300    };
301    let l4_len = l4_len as u16;
302
303    match (&src.value, &dst.value) {
304        (Some(address::Value::V6(_)), Some(address::Value::V6(_))) => (&IPv6Packet {
305            payload_length: l4_len,
306            flow_label: identifier as u32,
307            next_header,
308            hop_limit: ttl,
309            src: src.into(),
310            dst: dst.into(),
311            payload,
312        })
313            .into(),
314        (Some(address::Value::V4(_)), Some(address::Value::V4(_))) => (&IPv4Packet {
315            // IPv4 protocol byte is derived from the payload variant during serialization.
316            length: 20 + l4_len,
317            identifier,
318            ttl,
319            src: src.into(),
320            dst: dst.into(),
321            payload,
322        })
323            .into(),
324        (s, d) => panic!("IP version mismatch or unset address: src={s:?}, dst={d:?}"),
325    }
326}
327
328/// Struct defining the IPv4 pseudo-header for checksum calculation.
329#[derive(Debug)]
330pub struct PseudoHeaderV4 {
331    pub src: u32,
332    pub dst: u32,
333    pub protocol: u8,
334    pub length: u16,
335}
336
337/// Converting PsuedoHeader to bytes
338impl From<&PseudoHeaderV4> for Vec<u8> {
339    fn from(header: &PseudoHeaderV4) -> Self {
340        let mut wtr = vec![];
341        wtr.write_u32::<NetworkEndian>(header.src)
342            .expect("Unable to write to byte buffer for PseudoHeader");
343        wtr.write_u32::<NetworkEndian>(header.dst)
344            .expect("Unable to write to byte buffer for PseudoHeader");
345        wtr.write_u8(0) // 8 bits of zeroes
346            .expect("Unable to write to byte buffer for PseudoHeader");
347        wtr.write_u8(header.protocol)
348            .expect("Unable to write to byte buffer for PseudoHeader");
349        wtr.write_u16::<NetworkEndian>(header.length)
350            .expect("Unable to write to byte buffer for PseudoHeader");
351
352        wtr
353    }
354}
355
356/// Struct defining the IPv6 pseudo-header for checksum calculation.
357#[derive(Debug)]
358pub struct PseudoHeaderV6 {
359    pub src: u128,
360    pub dst: u128,
361    pub upper_layer_packet_length: u32,
362    pub next_header: u8,
363}
364
365/// Converting PsuedoHeaderv6 to bytes
366impl From<&PseudoHeaderV6> for Vec<u8> {
367    fn from(header: &PseudoHeaderV6) -> Self {
368        let mut wtr = vec![];
369        wtr.write_u128::<NetworkEndian>(header.src)
370            .expect("Unable to write to byte buffer for PseudoHeader");
371        wtr.write_u128::<NetworkEndian>(header.dst)
372            .expect("Unable to write to byte buffer for PseudoHeader");
373        wtr.write_u32::<NetworkEndian>(header.upper_layer_packet_length)
374            .expect("Unable to write to byte buffer for PseudoHeader");
375        wtr.write_u24::<NetworkEndian>(0) // zeroes
376            .expect("Unable to write to byte buffer for PseudoHeader");
377        wtr.write_u8(header.next_header)
378            .expect("Unable to write to byte buffer for PseudoHeader");
379
380        wtr
381    }
382}
383
384/// Struct defining a pseudo header that is used by both TCP and UDP to calculate their checksum
385#[derive(Debug)]
386pub enum PseudoHeader {
387    V4(PseudoHeaderV4),
388    V6(PseudoHeaderV6),
389}
390
391impl PseudoHeader {
392    pub fn new(src_addr: &Address, dst_addr: &Address, protocol: u8, packet_length: u32) -> Self {
393        match (&src_addr.value, &dst_addr.value) {
394            (Some(address::Value::V6(_)), Some(address::Value::V6(_))) => {
395                Self::V6(PseudoHeaderV6 {
396                    src: src_addr.into(),
397                    dst: dst_addr.into(),
398                    upper_layer_packet_length: packet_length,
399                    next_header: protocol,
400                })
401            }
402            (Some(address::Value::V4(_)), Some(address::Value::V4(_))) => {
403                Self::V4(PseudoHeaderV4 {
404                    src: src_addr.into(),
405                    dst: dst_addr.into(),
406                    protocol,
407                    length: packet_length as u16,
408                })
409            }
410            (s, d) => panic!(
411                "IP version mismatch or invalid address type: src={:?}, dst={:?}",
412                s, d
413            ),
414        }
415    }
416}
417
418/// Convert PseudoHeader to bytes
419impl From<&PseudoHeader> for Vec<u8> {
420    fn from(header: &PseudoHeader) -> Self {
421        match header {
422            PseudoHeader::V4(header) => header.into(),
423            PseudoHeader::V6(header) => header.into(),
424        }
425    }
426}
427
428/// Calculate the checksum for a UDP/TCP packet.
429///
430/// # Arguments
431///
432/// * `buffer` - the UDP/TCP packet as bytes (without the IP header)
433/// * `pseudo_header` - the pseudo header for this packet (IPv4 or IPv6)
434pub fn calculate_checksum(buffer: &[u8], pseudo_header: &PseudoHeader) -> u16 {
435    let mut sum = 0u32;
436    let mut packet: Vec<u8> = pseudo_header.into();
437    packet.extend_from_slice(buffer);
438
439    // Sum the packet buffer
440    let packet_len = packet.len();
441    for chunk in packet.as_chunks::<2>().0 {
442        let word = u16::from_be_bytes(*chunk);
443        sum += u32::from(word);
444    }
445
446    // If the packet length is odd, add the last byte as a half-word (padded with 0)
447    if !packet_len.is_multiple_of(2) {
448        sum += u32::from(packet[packet_len - 1]) << 8;
449    }
450
451    // Fold the sum to 16 bits by adding the carry
452    while (sum >> 16) != 0 {
453        sum = (sum & 0xffff) + (sum >> 16);
454    }
455
456    // Return the one's complement of the sum
457    !(sum as u16)
458}