-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathicmp.rs
112 lines (91 loc) · 2.57 KB
/
icmp.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::io::Write;
use thiserror::Error;
pub const HEADER_SIZE: usize = 8;
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid size")]
InvalidSize,
#[error("invalid packet")]
InvalidPacket,
}
pub struct IcmpV4;
pub struct IcmpV6;
pub trait Proto {
const ECHO_REQUEST_TYPE: u8;
const ECHO_REQUEST_CODE: u8;
const ECHO_REPLY_TYPE: u8;
const ECHO_REPLY_CODE: u8;
}
impl Proto for IcmpV4 {
const ECHO_REQUEST_TYPE: u8 = 8;
const ECHO_REQUEST_CODE: u8 = 0;
const ECHO_REPLY_TYPE: u8 = 0;
const ECHO_REPLY_CODE: u8 = 0;
}
impl Proto for IcmpV6 {
const ECHO_REQUEST_TYPE: u8 = 128;
const ECHO_REQUEST_CODE: u8 = 0;
const ECHO_REPLY_TYPE: u8 = 129;
const ECHO_REPLY_CODE: u8 = 0;
}
pub struct EchoRequest<'a> {
pub ident: u16,
pub seq_cnt: u16,
pub payload: &'a [u8],
}
impl<'a> EchoRequest<'a> {
pub fn encode<P: Proto>(&self, buffer: &mut [u8]) -> Result<(), Error> {
buffer[0] = P::ECHO_REQUEST_TYPE;
buffer[1] = P::ECHO_REQUEST_CODE;
buffer[4] = (self.ident >> 8) as u8;
buffer[5] = self.ident as u8;
buffer[6] = (self.seq_cnt >> 8) as u8;
buffer[7] = self.seq_cnt as u8;
if let Err(_) = (&mut buffer[8..]).write(self.payload) {
return Err(Error::InvalidSize);
}
write_checksum(buffer);
Ok(())
}
}
pub struct EchoReply<'a> {
pub ident: u16,
pub seq_cnt: u16,
pub payload: &'a [u8],
}
impl<'a> EchoReply<'a> {
pub fn decode<P: Proto>(buffer: &'a [u8]) -> Result<Self, Error> {
if buffer.as_ref().len() < HEADER_SIZE {
return Err(Error::InvalidSize);
}
let type_ = buffer[0];
let code = buffer[1];
if type_ != P::ECHO_REPLY_TYPE || code != P::ECHO_REPLY_CODE {
return Err(Error::InvalidPacket);
}
let ident = (u16::from(buffer[4]) << 8) + u16::from(buffer[5]);
let seq_cnt = (u16::from(buffer[6]) << 8) + u16::from(buffer[7]);
let payload = &buffer[HEADER_SIZE..];
Ok(EchoReply {
ident,
seq_cnt,
payload,
})
}
}
fn write_checksum(buffer: &mut [u8]) {
let mut sum = 0u32;
for word in buffer.chunks(2) {
let mut part = u16::from(word[0]) << 8;
if word.len() > 1 {
part += u16::from(word[1]);
}
sum = sum.wrapping_add(u32::from(part));
}
while (sum >> 16) > 0 {
sum = (sum & 0xffff) + (sum >> 16);
}
let sum = !sum as u16;
buffer[2] = (sum >> 8) as u8;
buffer[3] = (sum & 0xff) as u8;
}