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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
//! # Submodule: I/O functions
//!
//! This submodule defines a set of functions that interact with the NDIS filter driver.
//! These functions include clearing the packet queue associated with a network adapter,
//! retrieving the number of packets currently queued in the packet queue, reading single
//! or multiple network packets from the NDIS filter driver, and sending single or multiple
//! network packets to the NDIS filter driver to be passed down or up the network stack.
//! This submodule also provides a function to set a Win32 event to be signaled by the
//! NDIS filter when packets are available for read on a network adapter.
//!

use std::mem::size_of;

use windows::{
    core::Result,
    Win32::Foundation::{GetLastError, HANDLE},
    Win32::System::IO::DeviceIoControl,
};

use super::Ndisapi;
use crate::driver::*;

impl Ndisapi {
    /// This function clears the packet queue associated with the specified network adapter
    /// handle in the NDIS filter driver.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle: HANDLE`: The handle of the network adapter whose packet queue
    ///   should be flushed.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn flush_adapter_packet_queue(&self, adapter_handle: HANDLE) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_FLUSH_ADAPTER_QUEUE,
                Some(&adapter_handle as *const HANDLE as *const std::ffi::c_void),
                size_of::<HANDLE>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function retrieves the number of packets currently queued in the packet queue associated with the
    /// specified network adapter handle in the NDIS filter driver.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle: HANDLE`: The handle of the network adapter whose packet queue size should be queried.
    ///
    /// # Returns
    ///
    /// * `Result<u32>`: If successful, returns `Ok(queue_size)` where `queue_size` is the number of packets in the
    ///   adapter's packet queue. Otherwise, returns an error.
    pub fn get_adapter_packet_queue_size(&self, adapter_handle: HANDLE) -> Result<u32> {
        let mut queue_size = 0u32;

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_ADAPTER_QUEUE_SIZE,
                Some(&adapter_handle as *const HANDLE as *const std::ffi::c_void),
                size_of::<HANDLE>() as u32,
                Some(&mut queue_size as *mut u32 as *mut std::ffi::c_void),
                size_of::<u32>() as u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(queue_size)
        }
    }

    /// This function retrieves a single network packet from the NDIS filter driver associated with
    /// the specified `EthRequest`.
    ///
    /// # Arguments
    ///
    /// * `packet: &mut EthRequest`: A mutable reference to the `EthRequest` structure that will be filled with
    ///   the retrieved packet data.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn read_packet(&self, packet: &mut EthRequest) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_READ_PACKET,
                Some(packet as *const EthRequest as *const std::ffi::c_void),
                size_of::<EthRequest>() as u32,
                None,
                0u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function retrieves a block of network packets from the NDIS filter driver associated with
    /// the specified `EthMRequest<N>`.
    ///
    /// # Arguments
    ///
    /// * `packets: &mut EthMRequest<N>`: A mutable reference to the `EthMRequest<N>` structure that will be filled with
    ///   the retrieved packet data.
    ///
    /// # Returns
    ///
    /// * `Result<usize>`: If successful, returns `Ok(packet_count)` where `packet_count` is the number of packets read
    ///   into `EthMRequest<N>`. Otherwise, returns an error.
    pub fn read_packets<const N: usize>(&self, packets: &mut EthMRequest<N>) -> Result<usize> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_READ_PACKETS,
                Some(packets as *const EthMRequest<N> as *const std::ffi::c_void),
                size_of::<EthMRequest<N>>() as u32,
                Some(packets as *mut EthMRequest<N> as *mut std::ffi::c_void),
                size_of::<EthMRequest<N>>() as u32,
                None,
                None,
            )
        };

        if result.as_bool() {
            Ok(packets.get_packet_success() as usize)
        } else {
            Err(unsafe { GetLastError() }.into())
        }
    }

    /// This function sends a single network packet to the NDIS filter driver associated with
    /// the specified `EthRequest`, which will then be passed down the network stack.
    ///
    /// # Arguments
    ///
    /// * `packet: &EthRequest`: A reference to the `EthRequest` structure containing the packet data to be sent.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn send_packet_to_adapter(&self, packet: &EthRequest) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SEND_PACKET_TO_ADAPTER,
                Some(packet as *const EthRequest as *const std::ffi::c_void),
                size_of::<EthRequest>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function sends a single network packet to the NDIS filter driver associated with
    /// the specified `EthRequest`, which will then be passed down the network stack to the Microsoft TCP/IP stack.
    ///
    /// # Arguments
    ///
    /// * `packet: &EthRequest`: A reference to the `EthRequest` structure containing the packet data to be sent.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn send_packet_to_mstcp(&self, packet: &EthRequest) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SEND_PACKET_TO_MSTCP,
                Some(packet as *const EthRequest as *const std::ffi::c_void),
                size_of::<EthRequest>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function sends a block of network packets to the NDIS filter driver associated with
    /// the specified `EthMRequest<N>`, which will then be passed down the network stack.
    ///
    /// # Arguments
    ///
    /// * `packets: &EthMRequest<N>`: A reference to the `EthMRequest<N>` structure containing the packet data to be sent.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn send_packets_to_adapter<const N: usize>(&self, packets: &EthMRequest<N>) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SEND_PACKETS_TO_ADAPTER,
                Some(packets as *const EthMRequest<N> as *const std::ffi::c_void),
                size_of::<EthMRequest<N>>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function sends a block of network packets to the NDIS filter driver associated with
    /// the specified `EthMRequest<N>`, which will then be passed up the network stack to the Microsoft TCP/IP stack.
    ///
    /// # Arguments
    ///
    /// * `packets: &EthMRequest<N>`: A reference to the `EthMRequest<N>` structure containing the packet data to be sent.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn send_packets_to_mstcp<const N: usize>(&self, packets: &EthMRequest<N>) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SEND_PACKETS_TO_MSTCP,
                Some(packets as *const EthMRequest<N> as *const std::ffi::c_void),
                size_of::<EthMRequest<N>>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function sets a Win32 event to be signaled by the NDIS filter when it has queued packets available for read
    /// on the specified network adapter.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle: HANDLE`: The handle of the network adapter to associate the event with.
    /// * `event_handle: HANDLE`: The handle of the Win32 event to be signaled when queued packets are available.
    ///
    /// # Returns
    ///
    /// * `Result<()>`: If successful, returns `Ok(())`. Otherwise, returns an error.
    pub fn set_packet_event(&self, adapter_handle: HANDLE, event_handle: HANDLE) -> Result<()> {
        let adapter_event = AdapterEvent {
            adapter_handle,
            event_handle,
        };

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SET_EVENT,
                Some(&adapter_event as *const AdapterEvent as *const std::ffi::c_void),
                size_of::<AdapterEvent>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }
}