1
//! Support for asynchronous packet transmission.
2
//!
3
//! See [`Capture::sink`](super::Capture::sink).
4
use std::io;
5
use std::marker::Unpin;
6
use std::pin::Pin;
7
use std::task::{self, Poll};
8

            
9
use futures::{Sink, ready};
10

            
11
#[cfg(target_os = "linux")]
12
use {crate::capture::selectable::SelectableCapture, tokio::io::unix::AsyncFd};
13

            
14
#[cfg(not(target_os = "linux"))]
15
use tokio::task::coop;
16

            
17
use crate::{
18
    Error,
19
    capture::{Active, Capture},
20
};
21

            
22
impl Capture<Active> {
23
    /// Returns this capture as a [`futures::Sink`] for sending packets.
24
    ///
25
    /// ```no_run
26
    /// # use futures::SinkExt;
27
    /// # use pcap::{Active, Capture};
28
    /// # async fn doc(capture: Capture<Active>) -> Result<(), pcap::Error> {
29
    /// let mut sink = capture.sink()?;
30
    /// sink.send(vec![0u8; 64]).await?;
31
    /// # Ok(())
32
    /// # }
33
    /// ```
34
    ///
35
    /// # Errors
36
    ///
37
    /// If this capture is set to be blocking, an error will be returned. On Linux, where the sink
38
    /// waits for the interface, an error is also returned if the network device does not support
39
    /// `select()`.
40
2
    pub fn sink<C: AsRef<[u8]>>(self) -> Result<PacketSink<C>, Error> {
41
2
        if !self.is_nonblock() {
42
1
            return Err(Error::NonNonBlock);
43
1
        }
44
1
        PacketSink::new(self)
45
2
    }
46
}
47

            
48
/// Implement Sink for async use of pcap
49
///
50
/// The packet given to `start_send` is held until a later poll can send it, so it is sent without
51
/// being copied. Only one packet is held at a time. A packet that fails to send is dropped rather
52
/// than retried, as libpcap does not tell us how much of it made it onto the wire.
53
///
54
/// Closing the sink flushes it but does not close the capture, which happens when the
55
/// [`PacketSink`] is dropped. Once closed, the sink takes no more packets and reports
56
/// `Error::IoError` with a kind of `BrokenPipe` instead.
57
///
58
/// # Warning
59
///
60
/// Only on Linux does the capture report when the interface is ready for another packet.
61
/// Elsewhere the packet is sent from within the poll, and a full transmit queue comes back as an
62
/// error instead of pausing the sink until there is room.
63
pub struct PacketSink<C> {
64
    #[cfg(target_os = "linux")]
65
    inner: AsyncFd<SelectableCapture<Active>>,
66
    #[cfg(not(target_os = "linux"))]
67
    capture: Capture<Active>,
68
    #[cfg(not(target_os = "linux"))]
69
    sent_since_yield: u32,
70
    packet: Option<C>,
71
    closed: bool,
72
}
73

            
74
#[cfg(target_os = "linux")]
75
impl<C> PacketSink<C> {
76
5
    pub(crate) fn new(capture: Capture<Active>) -> Result<Self, Error> {
77
5
        let capture = SelectableCapture::new(capture)?;
78
        // SAFETY: The descriptor belongs to the capture that `AsyncFd` takes ownership of, and
79
        // stays open until that capture is dropped.
80
5
        let inner =
81
5
            unsafe { AsyncFd::register_with_interest(capture, tokio::io::Interest::WRITABLE) }
82
5
                .map_err(io::Error::from)?;
83
5
        Ok(PacketSink {
84
5
            inner,
85
5
            packet: None,
86
5
            closed: false,
87
5
        })
88
5
    }
89

            
90
    /// Returns a mutable reference to the inner [`Capture`].
91
    ///
92
    /// The caller must ensure the capture will not be set to be blocking.
93
1
    pub fn capture_mut(&mut self) -> &mut Capture<Active> {
94
1
        self.inner.get_mut().get_inner_mut()
95
1
    }
96

            
97
10
    fn poll_send(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>>
98
10
    where
99
10
        C: AsRef<[u8]>,
100
    {
101
10
        let Self { inner, packet, .. } = self;
102

            
103
        loop {
104
11
            let buf = match &*packet {
105
7
                Some(buf) => buf.as_ref(),
106
4
                None => return Poll::Ready(Ok(())),
107
            };
108

            
109
7
            let mut guard = ready!(inner.poll_write_ready_mut(cx))?;
110
            // A busy device goes through the io::Result, so that try_io knows to wait for the
111
            // next readiness event. A real error goes through the inner Result untouched.
112
4
            let result = guard.try_io(|inner| {
113
4
                match inner.get_mut().get_inner_mut().sendpacket_nonblock(buf) {
114
2
                    Ok(()) => Ok(Ok(())),
115
1
                    Err(e @ Error::IoError(io::ErrorKind::WouldBlock)) => {
116
1
                        Err(io::Error::new(io::ErrorKind::WouldBlock, e))
117
                    }
118
1
                    Err(e) => Ok(Err(e)),
119
                }
120
4
            });
121

            
122
4
            match result {
123
3
                Ok(result) => {
124
3
                    *packet = None;
125
3
                    return Poll::Ready(result?);
126
                }
127
1
                Err(_would_block) => continue,
128
            }
129
        }
130
10
    }
131
}
132

            
133
/// How many packets the sink sends before it yields to the executor.
134
///
135
/// This count and tokio's task budget are redundant rather than complementary: either alone is
136
/// enough to make the sink yield, and whichever runs out first is the one that does. The value
137
/// is the budget tokio gives a task, so a sink driven on tokio yields at much the same points
138
/// it otherwise would, but correctness does not depend on the two agreeing. Tokio keeps its own
139
/// number private and is free to change it; the smaller of the two then takes effect.
140
#[cfg(not(target_os = "linux"))]
141
const SENDS_BETWEEN_YIELDS: u32 = 128;
142

            
143
#[cfg(not(target_os = "linux"))]
144
impl<C> PacketSink<C> {
145
    pub(crate) fn new(capture: Capture<Active>) -> Result<Self, Error> {
146
        Ok(PacketSink {
147
            capture,
148
            sent_since_yield: 0,
149
            packet: None,
150
            closed: false,
151
        })
152
    }
153

            
154
    /// Returns a mutable reference to the inner [`Capture`].
155
    ///
156
    /// The caller must ensure the capture will not be set to be blocking.
157
    pub fn capture_mut(&mut self) -> &mut Capture<Active> {
158
        &mut self.capture
159
    }
160

            
161
    fn poll_send(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>>
162
    where
163
        C: AsRef<[u8]>,
164
    {
165
        let buf = match &self.packet {
166
            Some(packet) => packet.as_ref(),
167
            None => return Poll::Ready(Ok(())),
168
        };
169

            
170
        // Sending here never waits for the interface, so a sink that is kept fed would never
171
        // return Pending and the task it runs in would never let the executor poll anything
172
        // else. Two things stop that. The count applies to whoever is driving the sink, which off
173
        // Linux can be any executor, as it holds nothing of tokio's. Spending the task's budget
174
        // as well holds a task that also does tokio I/O to one budget between yields rather
175
        // than one for each source.
176
        if self.sent_since_yield == SENDS_BETWEEN_YIELDS {
177
            self.sent_since_yield = 0;
178
            cx.waker().wake_by_ref();
179
            return Poll::Pending;
180
        }
181
        let coop = ready!(coop::poll_proceed(cx));
182
        self.sent_since_yield += 1;
183

            
184
        let result = self.capture.sendpacket(buf);
185
        coop.made_progress();
186
        self.packet = None;
187
        Poll::Ready(result)
188
    }
189
}
190

            
191
impl<C> Unpin for PacketSink<C> {}
192

            
193
impl<C: AsRef<[u8]>> Sink<C> for PacketSink<C> {
194
    type Error = Error;
195

            
196
3
    fn poll_ready(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
197
3
        let sink = Pin::into_inner(self);
198
3
        if sink.closed {
199
1
            return Poll::Ready(Err(Error::IoError(io::ErrorKind::BrokenPipe)));
200
2
        }
201
2
        sink.poll_send(cx)
202
3
    }
203

            
204
4
    fn start_send(self: Pin<&mut Self>, item: C) -> Result<(), Error> {
205
4
        let sink = Pin::into_inner(self);
206
4
        if sink.closed {
207
            return Err(Error::IoError(io::ErrorKind::BrokenPipe));
208
4
        }
209
4
        if sink.packet.is_some() {
210
            // poll_ready did not report the sink ready, so the packet it was given last is
211
            // still waiting. Keep it: dropping it here would lose a packet the caller has
212
            // already been told the sink took.
213
1
            return Err(Error::IoError(io::ErrorKind::WouldBlock));
214
3
        }
215
3
        sink.packet = Some(item);
216
3
        Ok(())
217
4
    }
218

            
219
5
    fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
220
5
        Pin::into_inner(self).poll_send(cx)
221
5
    }
222

            
223
2
    fn poll_close(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
224
2
        let sink = Pin::into_inner(self);
225
2
        ready!(sink.poll_send(cx))?;
226
2
        sink.closed = true;
227
2
        Poll::Ready(Ok(()))
228
2
    }
229
}
230

            
231
#[cfg(test)]
232
mod tests {
233
    use futures::SinkExt;
234

            
235
    use crate::{
236
        capture::testmod::test_capture,
237
        raw::{
238
            mock_ffi::*,
239
            testmod::{RAWMTX, as_pcap_t, geterr_expect},
240
        },
241
    };
242

            
243
    use super::*;
244

            
245
    #[test]
246
    fn test_sink_error() {
247
        let _m = RAWMTX.lock();
248

            
249
        let mut dummy: isize = 777;
250
        let pcap = as_pcap_t(&mut dummy);
251

            
252
        let test_capture = test_capture::<Active>(pcap);
253
        let capture = test_capture.capture;
254
        assert!(!capture.is_nonblock());
255

            
256
        let result = capture.sink::<Vec<u8>>();
257
        assert!(result.is_err());
258
    }
259

            
260
    #[cfg(target_os = "linux")]
261
    mod linux {
262
        use std::os::unix::io::RawFd;
263

            
264
        use crate::{capture::selectable::testmod::FdPair, raw};
265

            
266
        use super::*;
267

            
268
        // The caller holds on to the TestCapture, which owns the pcap_close expectation that fires
269
        // when the sink is dropped.
270
        fn test_sink(
271
            pcap: *mut raw::pcap_t,
272
            capture: Capture<Active>,
273
            fd: RawFd,
274
        ) -> PacketSink<Vec<u8>> {
275
            let ctx = raw::pcap_get_selectable_fd_context();
276
            ctx.expect()
277
                .withf_st(move |arg1| *arg1 == pcap)
278
                .return_once(move |_| fd);
279

            
280
            PacketSink::new(capture).unwrap()
281
        }
282

            
283
        #[tokio::test]
284
        async fn test_sink_ok() {
285
            let _m = RAWMTX.lock();
286

            
287
            let mut dummy: isize = 777;
288
            let pcap = as_pcap_t(&mut dummy);
289
            let fds = FdPair::new();
290
            let fd = fds.0[0];
291

            
292
            let test_capture = test_capture::<Active>(pcap);
293

            
294
            let ctx = raw::pcap_setnonblock_context();
295
            ctx.expect()
296
                .withf_st(move |arg1, arg2, _| (*arg1 == pcap) && (*arg2 == 1))
297
                .return_once(|_, _, _| 0);
298

            
299
            let capture = test_capture.capture.setnonblock().unwrap();
300

            
301
            let ctx = raw::pcap_get_selectable_fd_context();
302
            ctx.expect()
303
                .withf_st(move |arg1| *arg1 == pcap)
304
                .return_once(move |_| fd);
305

            
306
            let mut sink = capture.sink::<Vec<u8>>().unwrap();
307
            assert!(sink.capture_mut().is_nonblock());
308

            
309
            // Closing flushes the sink. The capture stays open until the sink is dropped.
310
            sink.close().await.unwrap();
311
        }
312

            
313
        #[tokio::test]
314
        async fn test_sink_sends() {
315
            let _m = RAWMTX.lock();
316

            
317
            let mut dummy: isize = 777;
318
            let pcap = as_pcap_t(&mut dummy);
319
            let fds = FdPair::new();
320

            
321
            let test_capture = test_capture::<Active>(pcap);
322
            let mut sink = test_sink(pcap, test_capture.capture, fds.0[0]);
323

            
324
            let ctx = pcap_sendpacket_context();
325
            ctx.expect()
326
                .withf_st(move |arg1, _, arg3| (*arg1 == pcap) && (*arg3 == 4))
327
                .return_once(|_, _, _| 0);
328

            
329
            sink.send(vec![1, 2, 3, 4]).await.unwrap();
330
            assert!(sink.packet.is_none());
331

            
332
            let ctx = pcap_sendpacket_context();
333
            ctx.checkpoint();
334
            ctx.expect()
335
                .withf_st(move |arg1, _, _| *arg1 == pcap)
336
                .return_once(|_, _, _| {
337
                    errno::set_errno(errno::Errno(libc::EINVAL));
338
                    -1
339
                });
340

            
341
            let _err = geterr_expect(pcap);
342

            
343
            let result = sink.send(vec![1, 2, 3, 4]).await;
344
            assert!(matches!(result, Err(Error::PcapError(_))));
345

            
346
            // The failed packet is dropped, so the sink can be used again.
347
            assert!(sink.packet.is_none());
348
        }
349

            
350
        #[tokio::test]
351
        async fn test_sink_backpressure() {
352
            let _m = RAWMTX.lock();
353

            
354
            let mut dummy: isize = 777;
355
            let pcap = as_pcap_t(&mut dummy);
356
            let fds = FdPair::new();
357

            
358
            let test_capture = test_capture::<Active>(pcap);
359
            let mut sink = test_sink(pcap, test_capture.capture, fds.0[0]);
360

            
361
            let ctx = pcap_sendpacket_context();
362
            ctx.expect()
363
                .withf_st(move |arg1, _, _| *arg1 == pcap)
364
                .returning(|_, _, _| {
365
                    errno::set_errno(errno::Errno(libc::EAGAIN));
366
                    -1
367
                });
368

            
369
            // Wait until the socketpair is reported writable, so the poll below gets past the
370
            // readiness check and asks libpcap to send. The guard is dropped without clearing,
371
            // which leaves the readiness in place.
372
            drop(sink.inner.writable().await.unwrap());
373

            
374
            sink.packet = Some(vec![1, 2, 3]);
375

            
376
            // A busy device is not an error. The sink waits and the packet stays queued.
377
            let poll = futures::future::poll_fn(|cx| Poll::Ready(sink.poll_send(cx))).await;
378
            assert!(poll.is_pending());
379
            assert_eq!(sink.packet, Some(vec![1, 2, 3]));
380
        }
381

            
382
        #[tokio::test]
383
        async fn test_sink_closed() {
384
            let _m = RAWMTX.lock();
385

            
386
            let mut dummy: isize = 777;
387
            let pcap = as_pcap_t(&mut dummy);
388
            let fds = FdPair::new();
389

            
390
            let test_capture = test_capture::<Active>(pcap);
391
            let mut sink = test_sink(pcap, test_capture.capture, fds.0[0]);
392

            
393
            sink.close().await.unwrap();
394

            
395
            // There is no pcap_sendpacket expectation, so a send here would fail the test.
396
            assert_eq!(
397
                sink.send(vec![1, 2, 3, 4]).await,
398
                Err(Error::IoError(io::ErrorKind::BrokenPipe))
399
            );
400
            assert!(sink.packet.is_none());
401
        }
402

            
403
        #[tokio::test]
404
        async fn test_sink_start_send_twice() {
405
            let _m = RAWMTX.lock();
406

            
407
            let mut dummy: isize = 777;
408
            let pcap = as_pcap_t(&mut dummy);
409
            let fds = FdPair::new();
410

            
411
            let test_capture = test_capture::<Active>(pcap);
412
            let mut sink = test_sink(pcap, test_capture.capture, fds.0[0]);
413

            
414
            Pin::new(&mut sink).start_send(vec![1, 2, 3, 4]).unwrap();
415

            
416
            // Nothing has sent the first packet yet, so the second one is refused rather than
417
            // put in its place.
418
            assert_eq!(
419
                Pin::new(&mut sink).start_send(vec![5, 6, 7]),
420
                Err(Error::IoError(io::ErrorKind::WouldBlock))
421
            );
422
            assert_eq!(sink.packet, Some(vec![1, 2, 3, 4]));
423

            
424
            let ctx = pcap_sendpacket_context();
425
            ctx.expect()
426
                .withf_st(move |arg1, _, arg3| (*arg1 == pcap) && (*arg3 == 4))
427
                .return_once(|_, _, _| 0);
428

            
429
            sink.flush().await.unwrap();
430
            assert!(sink.packet.is_none());
431
        }
432
    }
433

            
434
    #[cfg(not(target_os = "linux"))]
435
    #[tokio::test]
436
    async fn test_sink_ok() {
437
        let _m = RAWMTX.lock();
438

            
439
        let mut dummy: isize = 777;
440
        let pcap = as_pcap_t(&mut dummy);
441

            
442
        let test_capture = test_capture::<Active>(pcap);
443

            
444
        let ctx = pcap_setnonblock_context();
445
        ctx.expect()
446
            .withf_st(move |arg1, arg2, _| (*arg1 == pcap) && (*arg2 == 1))
447
            .return_once(|_, _, _| 0);
448

            
449
        let capture = test_capture.capture.setnonblock().unwrap();
450

            
451
        let mut sink = capture.sink::<Vec<u8>>().unwrap();
452
        assert!(sink.capture_mut().is_nonblock());
453

            
454
        // Closing flushes the sink. The capture stays open until the sink is dropped.
455
        sink.close().await.unwrap();
456
    }
457

            
458
    #[cfg(not(target_os = "linux"))]
459
    #[tokio::test]
460
    async fn test_sink_sends() {
461
        let _m = RAWMTX.lock();
462

            
463
        let mut dummy: isize = 777;
464
        let pcap = as_pcap_t(&mut dummy);
465

            
466
        let test_capture = test_capture::<Active>(pcap);
467
        let mut sink = PacketSink::new(test_capture.capture).unwrap();
468

            
469
        let ctx = pcap_sendpacket_context();
470
        ctx.expect()
471
            .withf_st(move |arg1, _, arg3| (*arg1 == pcap) && (*arg3 == 4))
472
            .return_once(|_, _, _| 0);
473

            
474
        sink.send(vec![1, 2, 3, 4]).await.unwrap();
475
        assert!(sink.packet.is_none());
476

            
477
        let ctx = pcap_sendpacket_context();
478
        ctx.checkpoint();
479
        ctx.expect()
480
            .withf_st(move |arg1, _, _| *arg1 == pcap)
481
            .return_once(|_, _, _| -1);
482

            
483
        let _err = geterr_expect(pcap);
484

            
485
        let result = sink.send(vec![1, 2, 3, 4]).await;
486
        assert!(matches!(result, Err(Error::PcapError(_))));
487
        assert!(sink.packet.is_none());
488
    }
489

            
490
    #[cfg(not(target_os = "linux"))]
491
    #[tokio::test]
492
    async fn test_sink_closed() {
493
        let _m = RAWMTX.lock();
494

            
495
        let mut dummy: isize = 777;
496
        let pcap = as_pcap_t(&mut dummy);
497

            
498
        let test_capture = test_capture::<Active>(pcap);
499
        let mut sink = PacketSink::new(test_capture.capture).unwrap();
500

            
501
        sink.close().await.unwrap();
502

            
503
        // There is no pcap_sendpacket expectation, so a send here would fail the test.
504
        assert_eq!(
505
            sink.send(vec![1, 2, 3, 4]).await,
506
            Err(Error::IoError(io::ErrorKind::BrokenPipe))
507
        );
508
        assert!(sink.packet.is_none());
509
    }
510

            
511
    #[cfg(not(target_os = "linux"))]
512
    #[tokio::test]
513
    async fn test_sink_start_send_twice() {
514
        let _m = RAWMTX.lock();
515

            
516
        let mut dummy: isize = 777;
517
        let pcap = as_pcap_t(&mut dummy);
518

            
519
        let test_capture = test_capture::<Active>(pcap);
520
        let mut sink = PacketSink::new(test_capture.capture).unwrap();
521

            
522
        Pin::new(&mut sink).start_send(vec![1, 2, 3, 4]).unwrap();
523

            
524
        // Nothing has sent the first packet yet, so the second one is refused rather than put
525
        // in its place.
526
        assert_eq!(
527
            Pin::new(&mut sink).start_send(vec![5, 6, 7]),
528
            Err(Error::IoError(io::ErrorKind::WouldBlock))
529
        );
530
        assert_eq!(sink.packet, Some(vec![1, 2, 3, 4]));
531

            
532
        let ctx = pcap_sendpacket_context();
533
        ctx.expect()
534
            .withf_st(move |arg1, _, arg3| (*arg1 == pcap) && (*arg3 == 4))
535
            .return_once(|_, _, _| 0);
536

            
537
        sink.flush().await.unwrap();
538
        assert!(sink.packet.is_none());
539
    }
540

            
541
    // Deliberately not a tokio test. The sink is a futures::Sink and off Linux it holds nothing
542
    // of tokio's, so it has to give up its turn whatever executor is driving it.
543
    #[cfg(not(target_os = "linux"))]
544
    #[test]
545
    fn test_sink_yields() {
546
        let _m = RAWMTX.lock();
547

            
548
        let mut dummy: isize = 777;
549
        let pcap = as_pcap_t(&mut dummy);
550

            
551
        let test_capture = test_capture::<Active>(pcap);
552
        let mut sink = PacketSink::new(test_capture.capture).unwrap();
553

            
554
        let ctx = pcap_sendpacket_context();
555
        ctx.expect()
556
            .withf_st(move |arg1, _, _| *arg1 == pcap)
557
            .returning(|_, _, _| 0);
558

            
559
        let waker = futures::task::noop_waker();
560
        let mut cx = task::Context::from_waker(&waker);
561

            
562
        // Keep the sink fed and poll it until it asks to be polled again later. One that never
563
        // does keeps the thread to itself for as long as there are packets to send.
564
        let mut sends = 0;
565
        loop {
566
            sink.packet = Some(vec![1, 2, 3, 4]);
567
            if sink.poll_send(&mut cx).is_pending() {
568
                break;
569
            }
570
            sends += 1;
571
            assert!(sends < 10_000, "the sink never gave up its turn");
572
        }
573
        assert_eq!(sends, SENDS_BETWEEN_YIELDS);
574

            
575
        // The packet the sink yielded on is still there for the poll after it.
576
        assert_eq!(sink.packet, Some(vec![1, 2, 3, 4]));
577
    }
578

            
579
    // Under tokio the sink spends the task's budget too, so a task that has already spent some
580
    // of it elsewhere gets its turn back sooner than the count alone would give it.
581
    #[cfg(not(target_os = "linux"))]
582
    #[tokio::test]
583
    async fn test_sink_yields_on_task_budget() {
584
        let _m = RAWMTX.lock();
585

            
586
        let mut dummy: isize = 777;
587
        let pcap = as_pcap_t(&mut dummy);
588

            
589
        let test_capture = test_capture::<Active>(pcap);
590
        let mut sink = PacketSink::new(test_capture.capture).unwrap();
591

            
592
        let ctx = pcap_sendpacket_context();
593
        ctx.expect()
594
            .withf_st(move |arg1, _, _| *arg1 == pcap)
595
            .returning(|_, _, _| 0);
596

            
597
        // Spend half the budget on something that is not the sink.
598
        for _ in 0..SENDS_BETWEEN_YIELDS / 2 {
599
            coop::consume_budget().await;
600
        }
601

            
602
        let mut sends = 0;
603
        loop {
604
            sink.packet = Some(vec![1, 2, 3, 4]);
605
            if futures::future::poll_fn(|cx| Poll::Ready(sink.poll_send(cx)))
606
                .await
607
                .is_pending()
608
            {
609
                break;
610
            }
611
            sends += 1;
612
            assert!(sends < 10_000, "the sink never gave up its turn");
613
        }
614
        assert!(
615
            sends < SENDS_BETWEEN_YIELDS,
616
            "the sink sent {sends} before yielding, so it kept a budget of its own"
617
        );
618
    }
619
}