How Sniffnet Identifies Services Based on Well‑Known Ports: A Deep Dive into the PHF Map Implementation

Sniffnet identifies network services by querying a compile‑time perfect hash map that maps port‑protocol pairs to service names, scoring well‑known ports (below 1024) higher than ephemeral ports to determine the most likely service.

Sniffnet is an open‑source network monitoring application that analyzes live traffic to display connections and their associated services. When processing packets, the application must resolve which service (HTTP, SSH, HTTPS, etc.) is running on a given port. According to the Sniffnet source code, this identification relies on a static lookup table generated at compile time and a weighted scoring algorithm that prioritizes well‑known ports.

Build‑Time Generation of the Service Map

Sniffnet does not perform dynamic DNS queries or heuristic payload inspection to guess services. Instead, it embeds a perfect hash map directly into the binary.

The build process in build.rs reads the services.txt file located at the project root. This file contains entries in the format:

<service_name>\t<port>/<protocol>

Each line is validated and transformed using phf_codegen to generate a static map stored in OUT_DIR/services.rs. This map links ServiceQuery keys to Service values:

// build.rs – lines 33‑60
static SERVICES: phf::Map<ServiceQuery, Service> = …;

This approach ensures zero‑cost lookups at runtime because the map is baked into the compiled artifact.

Runtime Service Lookup Logic

When Sniffnet captures a packet, the get_service function in src/networking/manage_packets.rs receives an AddressPortPair containing the source and destination IP addresses, ports, and the transport protocol. The function performs lookups for both the source and destination ports:

// manage_packets.rs – lines 36‑42
let service1 = SERVICES.get(&ServiceQuery(port1, key.protocol)).unwrap_or(&unknown);
let service2 = SERVICES.get(&ServiceQuery(port2, key.protocol)).unwrap_or(&unknown);

If neither port exists in the map, the function returns Service::Unknown. For ICMP or ARP traffic, it returns Service::NotApplicable.

The ServiceQuery Key Structure

The lookup key is defined in src/networking/types/service_query.rs as a simple tuple struct wrapping the port number and protocol:

ServiceQuery(u16, Protocol)

This lightweight structure enables efficient hashing and comparison against the static SERVICES map generated during the build phase.

Scoring Well‑Known vs. Ephemeral Ports

When both the source and destination ports match known services, Sniffnet must decide which name to report. It calculates a service score using a closure defined at lines 28‑33 of manage_packets.rs:

// manage_packets.rs – lines 28‑33
let service_is_some = u8::from(matches!(service, Service::Name(_)));
let port_is_well_known = if port < 1024 { 3 } else { 1 };
let bonus_direction = u8::from(bonus_direction);
service_is_some * (port_is_well_known + bonus_direction)

The algorithm applies the following weights:

  • Well‑known ports (port < 1024) receive a weight of 3.
  • Ephemeral or registered ports (1024 and above) receive a weight of 1.
  • An additional +1 bonus is added when the remote port (or destination port in multicast scenarios) is more likely to identify the service based on traffic direction.

The service with the higher score wins. If both ports have equal scores, the logic favors the port that aligns with the traffic direction heuristic.

The Service Enum and Type System

The result of the identification process is stored in the Service enum defined in src/networking/types/service.rs:

// src/networking/types/service.rs
pub enum Service {
    Name(&'static str),   // known service, e.g. "https"
    Unknown,              // not found in the map
    NotApplicable,        // ICMP/ARP traffic
}

Using a &'static str for known services avoids runtime allocations and keeps the binary size efficient. The Unknown variant handles ports not listed in services.txt, while NotApplicable covers non‑transport protocols.

Practical Implementation Example

The following example demonstrates how Sniffnet identifies HTTPS traffic on port 443:

use sniffet::networking::{
    types::{AddressPortPair, Service, TrafficDirection, Protocol},
    manage_packets::get_service,
};
use std::net::{IpAddr, Ipv4Addr};
use pcap::Address;

// Simulate an outgoing TCP connection to port 443
let key = AddressPortPair::new(
    IpAddr::V4(Ipv4Addr::UNSPECIFIED), // source IP
    Some(54321),                       // ephemeral source port
    IpAddr::V4(Ipv4Addr::UNSPECIFIED), // destination IP
    Some(443),                         // well‑known HTTPS port
    Protocol::TCP,
);

let direction = TrafficDirection::Outgoing;
let interfaces: &[Address] = &[];

let service = get_service(&key, direction, interfaces);
println!("Identified service: {}", service); // Output: "https"

The weighting algorithm ensures that services on well‑known ports are preferred over high‑numbered ephemeral ports:

// Source port 22 (SSH, well‑known) vs destination port 8080 (HTTP alternative, not well‑known)
let key = AddressPortPair::new(
    IpAddr::V4(Ipv4Addr::UNSPECIFIED), Some(22),   // source: SSH
    IpAddr::V4(Ipv4Addr::UNSPECIFIED), Some(8080), // destination: http‑alt
    Protocol::TCP,
);

let service = get_service(&key, TrafficDirection::Outgoing, &[]);
assert_eq!(service, Service::Name("ssh")); // Port 22 scores higher (3 vs 1)

Summary

  • Sniffnet uses a compile‑time perfect hash map generated by build.rs from the services.txt file to avoid runtime overhead.
  • The get_service function in src/networking/manage_packets.rs performs parallel lookups on both source and destination ports using the ServiceQuery key.
  • A scoring algorithm weights well‑known ports (those below 1024) three times higher than ephemeral ports to disambiguate which side of a connection defines the service.
  • The Service enum efficiently represents known services, unknown ports, and non‑applicable protocols without heap allocations.

Frequently Asked Questions

How does Sniffnet handle ports that are not listed in the services.txt file?

When a port is not found in the compile‑time SERVICES map, the get_service function returns Service::Unknown. This variant indicates that the traffic is using a port not registered in the Internet Assigned Numbers Authority (IANA) list or the curated services.txt file.

Why does Sniffnet use a perfect hash map instead of a standard HashMap?

Sniffnet uses phf_codegen to generate a perfect hash function at build time because it guarantees constant‑time lookups with no collisions and zero runtime initialization cost. Unlike std::collections::HashMap, the PHF map requires no dynamic memory allocation and has minimal binary size overhead, which is critical for a real‑time packet analysis tool.

What is the significance of the port number 1024 in Sniffnet's scoring algorithm?

Ports below 1024 are reserved as well‑known ports by IANA and typically require root privileges to bind on Unix systems. Sniffnet exploits this convention by assigning these ports a weight of 3 versus 1 for higher ports, making it statistically more likely that the correct service is identified when both ends of a connection use known services.

Can Sniffnet identify services running on non‑standard ports?

No. Sniffnet relies strictly on the static port‑to‑service mapping defined in services.txt. If a service like HTTP runs on port 8080 instead of 80, it will only be identified as "http" if that specific mapping exists in the source file. The application does not perform deep packet inspection or behavioral analysis to detect services on arbitrary ports.

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 →