@@ -15,6 +15,11 @@ use std::{
15
15
os:: fd:: AsRawFd ,
16
16
} ;
17
17
18
+ /// magic bytes that are injected to end of icmp echo reply packets that
19
+ /// we craft and it get discarded later when parsing, it's purpose is to
20
+ /// detect automatic echo reply packets of kernel and ignore them
21
+ const ECHO_REPLY_MAGIC : [ u8 ; 3 ] = [ 0x24 , 0x74 , 0x33 ] ;
22
+
18
23
/// tracks if the icmp receiver thread is started or not, the first index
19
24
/// is for icmpv4 and the second is for icmpv6
20
25
static IS_RECEIVER_STARTED : [ Mutex < bool > ; 2 ] = [ Mutex :: new ( false ) , Mutex :: new ( false ) ] ;
@@ -109,8 +114,13 @@ impl SocketTrait for IcmpSocket {
109
114
}
110
115
111
116
fn send ( & self , buffer : & [ u8 ] ) -> io:: Result < usize > {
117
+ assert ! (
118
+ !self . is_blocking,
119
+ "IcmpSocket::send_to called on blocking socket"
120
+ ) ;
121
+
112
122
let dst_addr = self . connected_addr . unwrap ( ) ;
113
- let packet = craft_icmp_packet ( buffer, & self . local_addr ( ) ?, & dst_addr) ;
123
+ let packet = craft_icmp_packet ( buffer, & self . local_addr ( ) ?, & dst_addr, true ) ;
114
124
let dst_addr: SocketAddr = if dst_addr. is_ipv6 ( ) {
115
125
// in linux `send_to` on icmpv6 socket requires destination port to be zero
116
126
let mut addr_without_port = dst_addr;
@@ -130,7 +140,11 @@ impl SocketTrait for IcmpSocket {
130
140
}
131
141
132
142
fn send_to ( & self , buffer : & [ u8 ] , to : & SocketAddr ) -> io:: Result < usize > {
133
- let packet = craft_icmp_packet ( buffer, & self . local_addr ( ) ?, to) ;
143
+ assert ! (
144
+ self . is_blocking,
145
+ "IcmpSocket::send_to called on nonblocking socket"
146
+ ) ;
147
+ let packet = craft_icmp_packet ( buffer, & self . local_addr ( ) ?, to, false ) ;
134
148
let mut to_addr = * to;
135
149
// in linux `send_to` on icmpv6 socket requires destination port to be zero
136
150
to_addr. set_port ( 0 ) ;
@@ -194,30 +208,67 @@ impl SocketTrait for IcmpSocket {
194
208
}
195
209
}
196
210
197
- fn craft_icmp_packet ( payload : & [ u8 ] , source_addr : & SocketAddr , dst_addr : & SocketAddr ) -> Vec < u8 > {
198
- let echo_header = IcmpEchoHeader {
199
- id : dst_addr. port ( ) ,
200
- seq : source_addr. port ( ) ,
211
+ fn craft_icmp_packet (
212
+ payload : & [ u8 ] ,
213
+ source_addr : & SocketAddr ,
214
+ dst_addr : & SocketAddr ,
215
+ request : bool ,
216
+ ) -> Vec < u8 > {
217
+ // when we are sending echo reply we inject few magic bytes to the
218
+ // end of payload so when receiving reply packets we can determine
219
+ // if the echo reply packet is automatically sent from kernel
220
+ // (in case /proc/sys/net/ipv4/icmp_echo_ignore_all is not turned off)
221
+ // or we actually sent it
222
+ let payload = if !request {
223
+ let payload_with_magic_len = payload. len ( ) + ECHO_REPLY_MAGIC . len ( ) ;
224
+ let mut buffer = vec ! [ 0u8 ; payload_with_magic_len] ;
225
+ buffer[ ..payload. len ( ) ] . copy_from_slice ( payload) ;
226
+ buffer[ payload. len ( ) ..] . copy_from_slice ( & ECHO_REPLY_MAGIC ) ;
227
+ buffer
228
+ } else {
229
+ payload. to_vec ( )
230
+ } ;
231
+
232
+ // read comments on `receiver::parse_icmp_packet` on why the
233
+ // source and destination place changes based on echo reply or request
234
+ let echo_header = if request {
235
+ IcmpEchoHeader {
236
+ id : source_addr. port ( ) ,
237
+ seq : dst_addr. port ( ) ,
238
+ }
239
+ } else {
240
+ IcmpEchoHeader {
241
+ id : dst_addr. port ( ) ,
242
+ seq : source_addr. port ( ) ,
243
+ }
201
244
} ;
202
245
203
246
let icmp_header = if source_addr. is_ipv4 ( ) {
204
- let icmp_type = Icmpv4Type :: EchoRequest ( echo_header) ;
205
- Icmpv4Header :: with_checksum ( icmp_type, payload)
247
+ let icmp_type = if request {
248
+ Icmpv4Type :: EchoRequest ( echo_header)
249
+ } else {
250
+ Icmpv4Type :: EchoReply ( echo_header)
251
+ } ;
252
+ Icmpv4Header :: with_checksum ( icmp_type, & payload)
206
253
. to_bytes ( )
207
254
. to_vec ( )
208
255
} else {
209
- let icmp_type = Icmpv6Type :: EchoRequest ( echo_header) ;
256
+ let icmp_type = if request {
257
+ Icmpv6Type :: EchoRequest ( echo_header)
258
+ } else {
259
+ Icmpv6Type :: EchoReply ( echo_header)
260
+ } ;
210
261
let source_ip = as_socket_addr_v6 ( * source_addr) . ip ( ) . octets ( ) ;
211
262
let destination_ip = as_socket_addr_v6 ( * dst_addr) . ip ( ) . octets ( ) ;
212
- Icmpv6Header :: with_checksum ( icmp_type, source_ip, destination_ip, payload)
263
+ Icmpv6Header :: with_checksum ( icmp_type, source_ip, destination_ip, & payload)
213
264
. unwrap ( )
214
265
. to_bytes ( )
215
266
. to_vec ( )
216
267
} ;
217
268
218
269
let mut header_and_payload = Vec :: with_capacity ( icmp_header. len ( ) + payload. len ( ) ) ;
219
270
header_and_payload. extend_from_slice ( & icmp_header) ;
220
- header_and_payload. extend_from_slice ( payload) ;
271
+ header_and_payload. extend_from_slice ( & payload) ;
221
272
header_and_payload
222
273
}
223
274
0 commit comments