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
//! WinPcap/npcap sendqueue support module.
//!
//! Sending individual packets through WinPcap/npcap can be stunningly slow, since a user-to-kernel
//! transition is required for each packet transfer. To alleviate this there's support for
//! queueing up a batch of packets in userland, requiring only a single transition to kernel to
//! transmit them all.
use std::convert::TryInto;
use std::io::IoSlice;
use std::ptr::NonNull;
use crate::raw;
use crate::Error;
use crate::{Active, Capture};
pub struct SendQueue(NonNull<raw::pcap_send_queue>);
pub enum SendSync {
Off = 0,
On = 1,
}
#[inline]
fn make_pkthdr(ts: Option<std::time::Duration>, len: u32) -> raw::pcap_pkthdr {
raw::pcap_pkthdr {
ts: if let Some(ts) = ts {
libc::timeval {
// tv_sec is currently i32 in libc when building for Windows
tv_sec: ts.as_secs() as i32,
tv_usec: ts.subsec_micros() as i32,
}
} else {
libc::timeval {
tv_sec: 0,
tv_usec: 0,
}
},
caplen: len,
len,
}
}
impl SendQueue {
/// Create a new `SendQueue` object with a maximum capacity of `memsize`.
///
/// The buffer size `memsize` must be able to contain both packet headers and actual packet
/// contents.
pub fn new(memsize: u32) -> Result<Self, Error> {
let squeue = unsafe { raw::pcap_sendqueue_alloc(memsize) };
let squeue = NonNull::new(squeue).ok_or(Error::InsufficientMemory)?;
Ok(Self(squeue))
}
pub fn maxlen(&self) -> u32 {
unsafe { self.0.as_ref().maxlen }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> u32 {
unsafe { self.0.as_ref().len }
}
/// Add a packet to the queue.
///
/// The `ts` argument only needs to be a `Some()` value if the transmission mode will be
/// synchronous when calling [`SendQueue::transmit()`].
pub fn queue(&mut self, ts: Option<std::time::Duration>, buf: &[u8]) -> Result<(), Error> {
let len = buf.len().try_into().ok().ok_or(Error::BufferOverflow)?;
let pkthdr = make_pkthdr(ts, len);
let ph = &pkthdr as *const _;
let res = unsafe { raw::pcap_sendqueue_queue(self.0.as_ptr(), ph, buf.as_ptr()) };
if res == -1 {
return Err(Error::InsufficientMemory);
}
Ok(())
}
/// Add a (potentially) scattered packet to the queue.
///
/// ```
/// use std::io::IoSlice;
/// use pcap::sendqueue::SendQueue;
/// let dstmac: [u8; 6] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
/// let srcmac: [u8; 6] = [0, 0, 0, 0, 0, 0];
/// let ethtype: [u8; 2] = [0x12, 0x34];
/// let payload: [u8; 5] = [0x00, 0x01, 0x02, 0x03, 0x04];
/// let iov = [
/// IoSlice::new(&dstmac),
/// IoSlice::new(&srcmac),
/// IoSlice::new(ðtype),
/// IoSlice::new(&payload),
/// ];
/// let mut sq = SendQueue::new(1024*1024).unwrap();
/// sq.queue_sg(None, &iov).unwrap();
/// ```
pub fn queue_sg(
&mut self,
ts: Option<std::time::Duration>,
iov: &[IoSlice<'_>],
) -> Result<(), Error> {
// Calculate the total packet size from the scatter/gather list.
let pktsize: usize = iov.iter().map(|b| b.len()).sum();
// Make sure there's enough room for packet header and (assembled) packet.
// Note: It is assumed that len cannot exceed maxlen. This invariant must be upheld by
// all methods implemented by SendQueue.
let remain = (self.maxlen() - self.len()) as usize;
let need = std::mem::size_of::<raw::pcap_pkthdr>() + pktsize;
if remain < need {
return Err(Error::BufferOverflow);
}
// SAFETY:
// At this point it is know that the internal sendqueue buffer will fit the packet data,
// and as such any further buffer length validations are not needed.
let pktlen = pktsize.try_into().ok().ok_or(Error::BufferOverflow)?;
// Generate a raw packet header and get a pointer to it.
let pkthdr = make_pkthdr(ts, pktlen);
let rawhdr = &pkthdr as *const _ as *const u8;
// Get a raw pointer to the current write location in sendqueue's internal buffer.
let rawsq = unsafe { self.0.as_mut() };
let sqbuf = rawsq.buffer as *mut u8;
let bufoffs = rawsq.len.try_into().ok().ok_or(Error::BufferOverflow)?;
let mut wbuf = unsafe { sqbuf.offset(bufoffs) };
// Copy packet header into the sendqueue's buffer
let mut lastlen = std::mem::size_of::<raw::pcap_pkthdr>();
unsafe {
std::ptr::copy_nonoverlapping(rawhdr, wbuf, lastlen);
}
// Iterate over scatter/gather list and copy each entry into the sendqueue's raw buffer
for b in iov {
// Get a write pointer at the next position
let len = lastlen.try_into().ok().ok_or(Error::BufferOverflow)?;
wbuf = unsafe { wbuf.offset(len) };
unsafe {
std::ptr::copy_nonoverlapping(b.as_ptr(), wbuf, b.len());
}
lastlen = b.len();
}
// 'len' is used as write cursor
rawsq.len += need as u32;
Ok(())
}
/// Transmit the contents of the queue.
///
/// If entire queue was transmitted successfully the queue will be automatically reset.
///
/// If `sync` is set to `SendSync::On` the difference between packet header timestamps
/// will be used as a delay between sending each packet. If `SendSync::Off` is used the packets
/// will be transmitted with no delay between packets.
pub fn transmit(&mut self, dev: &mut Capture<Active>, sync: SendSync) -> Result<(), Error> {
let res = unsafe {
raw::pcap_sendqueue_transmit(dev.handle.as_ptr(), self.0.as_ptr(), sync as i32)
};
if res < self.len() {
return unsafe { Err(Error::new(raw::pcap_geterr(dev.handle.as_ptr()))) };
} else {
self.reset();
}
Ok(())
}
pub fn reset(&mut self) {
unsafe { self.0.as_mut() }.len = 0;
}
}
impl Drop for SendQueue {
fn drop(&mut self) {
unsafe {
raw::pcap_sendqueue_destroy(self.0.as_ptr());
}
}
}