How to Pause and Resume Packet Monitoring Using Sniffnet's Freeze Feature

Sniffnet's freeze feature temporarily suspends the live packet-parsing thread via a tokio::sync::broadcast channel, allowing you to pause and resume monitoring without terminating the underlying capture session.

Sniffnet, the open-source network traffic analyzer maintained by GyulyVGC/sniffnet, implements a sophisticated freeze mechanism that decouples the GUI from background packet processing. This feature enables users to halt packet parsing on demand through a toolbar toggle button while keeping the capture session alive. The implementation relies on asynchronous message passing between the main application state and the parsing thread, ensuring smooth state transitions without data loss.

Architecture of the Freeze Mechanism

Sniffnet coordinates the pause and resume functionality through four key components that communicate via broadcast channels. When you click the freeze button, the system toggles a boolean flag and signals the background thread to block until further notice.

The flow works as follows:

  1. The user clicks the Pause/Resume button in the header toolbar, dispatching Message::Freeze to the event loop
  2. The Sniffer state machine flips its frozen boolean and broadcasts a signal through freeze_tx
  3. The packet-parsing thread detects the signal via freeze_rx.try_recv() and enters a blocking state using freeze_rx.blocking_recv()
  4. A subsequent click sends a second broadcast, unblocking the receiver and resuming normal packet processing

Implementation Details

UI Button and Message Dispatch (src/gui/components/header.rs)

The freeze control resides in the header toolbar, implemented in src/gui/components/header.rs (lines 173-204). The get_button_freeze function constructs a button that dynamically switches between pause and resume icons based on the current frozen state:

pub fn get_button_freeze<'a>(
    language: Language,
    frozen: bool,
    thumbnail: bool,
) -> Tooltip<'a, Message, StyleType> {
    let icon = if frozen { Icon::Resume } else { Icon::Pause };
    let tooltip = if frozen {
        resume_translation(language)   // “Resume monitoring”
    } else {
        pause_translation(language)    // “Pause monitoring”
    };

    Tooltip::new(
        button(icon.to_text())
            .height(button_size)
            .width(button_size)
            .on_press(Message::Freeze),   // <‑‑ dispatch Freeze message
        Text::new(tooltip),
        Position::FollowCursor,
    )
}

This component triggers the Message::Freeze variant defined in src/gui/types/message.rs (lines 155-162), which carries no payload but serves as the toggle command:

enum Message {
    // … other UI messages …
    Freeze,            // toggles pause/resume
}

State Management and Channel Setup (src/gui/sniffer.rs)

The core state logic lives in src/gui/sniffer.rs. The Sniffer struct maintains a frozen boolean and an optional broadcast sender freeze_tx: Option<broadcast::Sender<()>>.

When initializing a capture session (around lines 989-991), Sniffnet creates the broadcast channel with a capacity of 1,048,575 messages and prepares two receivers for the parsing thread:

let (freeze_tx, freeze_rx) = tokio::sync::broadcast::channel(1_048_575);
let freeze_rx2 = freeze_tx.subscribe();   // second receiver for the parser
self.freeze_tx = Some(freeze_tx);
...
parse_packets(..., (freeze_rx, freeze_rx2));

The freeze() method (lines 59-64) handles the toggle logic by flipping the state flag and broadcasting a unit value:

fn freeze(&mut self) {
    self.frozen = !self.frozen;      // flip the UI flag
    if let Some(tx) = &self.freeze_tx {
        let _ = tx.send(());        // broadcast pause or resume request
    }
}

Parser Thread Coordination (src/networking/parse_packets.rs)

The background thread executing parse_packets in src/networking/parse_packets.rs (lines 81-88) implements non-blocking checks for freeze signals. When a signal arrives, the thread blocks on the receiver until the next broadcast arrives:

loop {
    // Pause detection
    if freeze_rx.try_recv().is_ok() {
        // Block until a resume signal arrives
        let _ = freeze_rx.blocking_recv();
        // Reset timing for live captures
        first_packet_ticks = Some(Instant::now());
    }

    // … normal packet processing …
}

This design ensures that packets continue to be captured at the OS level (by the underlying pcap library) but are not processed or displayed until the thread resumes. The blocking_recv() call efficiently parks the thread without consuming CPU cycles during the pause state.

Summary

  • Sniffnet's freeze feature uses a tokio::sync::broadcast channel to coordinate between the GUI and the packet-parsing thread
  • The UI button in src/gui/components/header.rs dispatches Message::Freeze to toggle states between pause and resume icons
  • State management in src/gui/sniffer.rs flips a boolean flag and broadcasts signals via freeze_tx.send(())
  • The parsing thread in src/networking/parse_packets.rs detects freeze signals with try_recv() and blocks with blocking_recv() until resumed
  • This architecture maintains the capture session while halting processing, allowing inspection of current traffic data without terminating the underlying capture

Frequently Asked Questions

Does freezing stop the network capture entirely?

No, freezing only pauses the packet-parsing thread. The underlying pcap capture session continues running in the background, but incoming packets are not processed or displayed in the GUI until you resume. This distinction ensures you don't lose network events or drop the capture handle during the pause.

What happens to packets received while Sniffnet is frozen?

Packets captured during the freeze state remain in the OS buffer or pcap buffer but are not parsed by the application. When you resume, the parser continues from where it left off, processing new packets as they arrive. The first_packet_ticks reset in the resume logic ensures timing calculations remain accurate for live captures.

Why does Sniffnet use a broadcast channel instead of a simple boolean flag?

The tokio::sync::broadcast channel provides thread-safe communication without requiring shared mutable state between the GUI and the background thread. The blocking receive mechanism (blocking_recv()) allows the parsing thread to sleep efficiently without polling, reducing CPU usage to zero while frozen. A simple boolean would require the thread to poll continuously or use additional synchronization primitives.

Can the freeze feature be triggered programmatically?

Yes, since Message::Freeze is a standard variant of the Message enum, any code that can dispatch messages to the Sniffnet update loop can trigger the freeze. This includes custom UI components, keyboard shortcuts, or automated scripts that interface with the Sniffer's message handling system defined in src/gui/types/message.rs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →