softflowd Network Monitor

repository·master·Indexed 42 days ago

https://github.com/irino/softflowd

A flow-based network monitor that tracks network flows by listening promiscuously on an interface and exporting them via NetFlow (v1, v5, v9) and IPFIX. The project includes a modern C++ rewrite (softflowd_cpp) with memory-safety practices and a Rust implementation (rsoftflowd) supporting NetFlow v1, 5, 9, and 10 (IPFIX), as well as IPv6 tracking. It features a companion control tool, softflowctl, for managing the daemon.

Tokens
16K
Snippets
51
Records
68
Agent score
51%

What's inside softflowd

  1. What is rsoftflowd?

    master

    rsoftflowd is a flow-based network monitor written in Rust. It listens promiscuously on a network interface and semi-statefully tracks network flows.

    Key capabilities include:

    • Flow Export: Supports NetFlow versions 1, 5, 9, and 10 (IPFIX).
    • IPv6 Support: Fully capable of tracking IPv6 flows and exporting them to IPv6 hosts.
    • Compatibility: Works with any NetFlow-compatible collector.
  2. Project Directory Structure

    master

    The project is organized into headers, source files, and tests:

    • include/softflow/: Core logic including FlowTable, PacketParser, PcapHandle, and protocol implementations (netflow1.hpp, netflow5.hpp, netflow9.hpp, ipfix.hpp, psamp.hpp).
    • src/: Implementation files for the daemon (softflowd.cpp), the control client (softflowctl.cpp), and protocol exporters.
    • tests/: Unit tests for flow tables, packet parsing, and specific protocols (NetFlow, IPFIX, PSAMP).
  3. Understand the softflowd-cpp directory layout

    master

    The project is organized into include/softflow/ for public headers and src/ for implementations. Building the project produces two primary executables: softflowd_cpp (the daemon) and softflowctl_cpp (the control client).

    Core Headers (include/softflow/)

    • softflowd.hpp: Contains all core public types including Flow, FlowKey, FlowTable<Backend>, FlowTableRuntime, PacketParser, PcapHandle, ByteWriter, ExportDestination, and TransportKind.
    • daemon.hpp: Provides PidFile, daemonize(), and SignalPipe.
    • softflowctl.hpp: Defines the control protocol and ControlClient.
    • netflow1.hpp, netflow5.hpp, netflow9.hpp: NetFlow protocol implementations.
    • ipfix.hpp: IPFIX implementation, including support for MPLS, RFC 5103 biflow, and absolute time formats.
    • psamp.hpp: PSAMP implementation and PsampReceiver (used for -R receive-mode).

    Source Files (src/)

    • softflowd.cpp: Contains the main() function, core implementations, the live-capture event loop, the control-socket server, and the PSAMP receive socket.
    • daemon.cpp: Daemonization logic.
    • softflowctl.cpp: Control protocol implementation and the softflowctl_cpp main entry point.
    • Protocol-specific implementations: netflow1.cpp, netflow5.cpp, netflow9.cpp, ipfix.cpp, and psamp.cpp.
  4. How to choose a FlowTable backend (Hash vs Tree)

    master

    The FlowTable manages the collection of flows. You can choose between two backends depending on your performance and ordering requirements:

    1. Hash Backend (FlowIndexBackend::Hash): Uses std::unordered_map. Provides average $O(1)$ complexity. Best for high-performance flow tracking.
    2. Tree Backend (FlowIndexBackend::Tree): Uses std::map (typically a Red-Black tree). Closest to the original softflowd implementation. Provides $O(\log n)$ complexity.

    Compile-time selection

    Use templates to select the backend with zero runtime overhead:

    softflow::FlowTable<softflow::FlowIndexBackend::Tree> table(65536);

    Runtime selection

    Use FlowTableRuntime to switch backends at runtime (e.g., via command-line arguments):

    softflow::FlowTableRuntime table(softflow::FlowIndexBackend::Tree, 65536);

    In the softflowd_cpp executable, you can switch the backend using the --backend flag:

    softflowd_cpp --backend=hash ...
    softflowd_cpp --backend=tree ...
  5. Verify compatibility between softflowd (C) and rsoftflowd (Rust)

    master

    To ensure that the Rust implementation (rsoftflowd, rsoftflowctl) is fully compatible with the original C implementation (softflowd, softflowctl), verification should focus on three axes:

    1. Behavioral Compatibility: CLI options, arguments, and environment variables must behave identically.
    2. Control Socket (IPC) Protocol Compatibility: Commands sent via UNIX domain sockets (e.g., statistics, expire all, shutdown) must elicit the exact same responses from both daemons. rsoftflowctl should be able to control softflowd and vice versa.
    3. Format Compatibility: Given the same pcap input, both daemons must produce NetFlow (v1, v5, v9) and IPFIX packets with identical structures, field values, sequence numbers, and timestamp calculations (within specification tolerances).
  6. Configure flow-tracking backends (Hash vs Tree)

    master

    The FlowTable class allows you to choose between two data structures for tracking flows. This can be done at compile-time for zero overhead or at run-time via the CLI.

    Compile-time selection

    Use the FlowIndexBackend template parameter with FlowTable.

    Run-time selection

    Use the --backend flag with the softflowd_cpp executable.

    • hash: Uses std::unordered_map (average O(1) lookup).
    • tree: Uses std::map (red-black tree, similar to the original sys-tree.h).
    // Compile-time selection
    softflow::FlowTable<softflow::FlowIndexBackend::Tree> table(65536);
    
    // Run-time selection via wrapper
    softflow::FlowTableRuntime table(softflow::FlowIndexBackend::Tree, 65536);
  7. Perform cross-control socket testing

    master

    Verify that the control plane (the communication between the CLI tool and the daemon via UNIX domain sockets) is compatible by cross-testing the C and Rust components.

    Test Matrix:

    • softflowctl (C) $\rightarrow$ softflowd (C) [Reference]
    • rsoftflowctl (Rust) $\rightarrow$ softflowd (C) [Cross-test]
    • softflowctl (C) $\rightarrow$ rsoftflowd (Rust) [Cross-test]
    • rsoftflowctl (Rust) $\rightarrow$ rsoftflowd (Rust) [Reference]

    Key commands to verify:

    • statistics: Ensure text output format is identical.
    • expire all: Ensure cached flows are immediately exported to the collector.
    • shutdown: Ensure the daemon performs a clean shutdown.
  8. Build rsoftflowd and rsoftflowctl

    master

    To build the rsoftflowd monitor and its companion control tool rsoftflowctl, use cargo to perform a release build. The resulting binaries will be located in the target/release/ directory.

    cargo build --release
  9. Perform differential testing for packet output verification

    master

    Use differential testing to compare the NetFlow/IPFIX packets generated by both versions. This involves feeding the same pcap file to both daemons and capturing their UDP output.

    Steps:

    1. Prepare a pcap file with diverse traffic (TCP, UDP, ICMP, IPv4, IPv6, etc.).
    2. Run both daemons in offline mode using the -r option to read the pcap, and direct them to different local ports.
    3. Capture the output using tcpdump.
    4. Compare the resulting pcap files using tshark or a script to ensure fields like IP addresses, ports, packet counts, and byte counts match.

    Note: You may need to mask dynamic fields like system boot time, real-time timestamps, or randomized sequence numbers during comparison.

    # softflowd (C) execution and capture
    softflowd -r test.pcap -n 127.0.0.1:9991 -d &
    tcpdump -i lo -w output_c.pcap udp port 9991 &
    
    # rsoftflowd (Rust) execution and capture
    rsoftflowd -r test.pcap -n 127.0.0.1:9992 -d &
    tcpdump -i lo -w output_rust.pcap udp port 9992 &
  10. Build softflowd+

    master

    To build softflowd+, use CMake to configure the project and then build it using the specified build directory. This project is a modern C++ rewrite of the original softflowd designed with memory-safety practices.

    cmake -S . -B build
    cmake --build build -j
  11. Run softflowd_cpp with standard options

    master

    The softflowd_cpp executable implements the exact same single-letter option grammar as the original softflowd(8). This ensures compatibility with existing deployment scripts and init files.

    Common usage patterns include:

    • Live capture: Use -i to specify an interface and -n to specify export destinations.
    • Pcap processing: Use -r to read from a file (prints statistics on exit).
    • Daemonization: By default, it runs as a daemon. Use -d to run in the foreground.
    • Control: Specify pidfiles with -p and control sockets with -c to allow management via softflowctl_cpp.
    # Live capture on eth0, exporting to a specific host:port
    softflowd_cpp -i eth0 -n 10.1.0.2:4432 -m 65536 -t udp=1m30s
    
    # Load-balance across multiple destinations
    softflowd_cpp -i eth0 -l -n 10.1.0.2:4432,10.1.0.3:4432
    
    # Read from a pcap file
    softflowd_cpp -r path/to/capture.pcap
    
    # Run in foreground with a custom control socket
    softflowd_cpp -i eth0 -d -c /tmp/sf.ctl
  12. Build softflowd-cpp from source

    master

    To build the softflowd-cpp project, you need libpcap-dev installed on your system. The build process uses CMake. By default, CMAKE_BUILD_TYPE=Debug is used, which automatically enables AddressSanitizer and UndefinedBehaviorSanitizer for memory safety testing.

    cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
    cmake --build build -j
    ctest --test-dir build --output-on-failure