Package com.slytechs.sdk.jnetpcap.api
This package provides NetPcap, a high-level packet capture interface
that wraps the low-level Pcap bindings and
integrates with protocol dissection. Packets delivered through
dispatch(), loop(), next(), and nextEx() are
fully dissected with protocol headers accessible via the zero-allocation
hasHeader() pattern.
API Layers
jNetPcap provides two API layers:
| Class | Module | Description |
|---|---|---|
Pcap | jnetpcap-bindings | Low-level libpcap bindings — direct 1:1 native access |
NetPcap | jnetpcap-api | High-level API with integrated protocol dissection |
Configuration: NetPcap vs PacketSettings
There are two distinct configuration concerns:
| Configuration | Purpose | Methods |
|---|---|---|
NetPcap setters | Capture properties (pcap API) | setSnaplen(), setTimeout(), setPromisc(),
setFilter(), setBufferSize(), etc. |
PacketSettings |
Packet structure & memory | dissect(), zeroCopy(), descriptorType(),
etc. |
NetPcap Configuration
NetPcap setters configure traditional pcap capture properties — how packets are captured from the network or read from files:
setSnaplen(int)— Maximum bytes to capture per packetsetTimeout(Duration)— Read timeoutsetPromisc(boolean)— Promiscuous modesetFilter(String)— BPF filter expressionsetBufferSize(int)— Kernel buffer sizesetImmediateMode(boolean)— Disable bufferingsetDirection(PcapDirection)— Capture directionsetTstampType(PcapTstampType)— Timestamp sourcesetTstampPrecision(PcapTStampPrecision)— Timestamp precision
PacketSettings Configuration
PacketSettings configures how packets
are structured and managed in memory:
dissect()— Enable eager protocol dissection (TYPE2 descriptor)onDemand()— Enable on-demand dissectionzeroCopy()— Use scoped memory (valid only in callback)descriptorType(DescriptorTypeInfo)— Choose descriptor format
Quick Start
Simple Live Capture
try (NetPcap pcap = NetPcap.openLive("eth0", 65535, true, Duration.ofSeconds(1))) {
Ip4 ip = new Ip4();
pcap.loop(100, packet -> {
if (packet.hasHeader(ip))
System.out.printf("%s -> %s%n", ip.src(), ip.dst());
});
}
Offline File Reading with PacketSettings
PacketSettings settings = new PacketSettings().dissect();
try (NetPcap pcap = NetPcap.openOffline("capture.pcap", settings)) {
pcap.setFilter("tcp port 80");
Packet packet;
while ((packet = pcap.next()) != null) {
// Process dissected packet...
}
}
Two-Stage Capture Configuration
try (NetPcap pcap = NetPcap.create("eth0")) {
pcap.setSnaplen(128)
.setPromisc(true)
.setTimeout(Duration.ofMillis(100))
.setImmediateMode(true)
.activate();
pcap.dispatch(1000, packet -> {
// Process packet...
});
}
Full Configuration Example
PacketSettings settings = new PacketSettings().dissect();
try (NetPcap pcap = NetPcap.create("eth0", settings)) {
pcap.setSnaplen(65535)
.setTimeout(Duration.ofSeconds(1))
.setPromisc(true)
.setBufferSize(16 * 1024 * 1024)
.setTstampPrecision(PcapTStampPrecision.NANO)
.activate();
pcap.setFilter("tcp port 80 or tcp port 443");
Ethernet eth = new Ethernet();
Ip4 ip4 = new Ip4();
Tcp tcp = new Tcp();
pcap.dispatch(1000, packet -> {
if (packet.hasHeader(eth) && packet.hasHeader(ip4) && packet.hasHeader(tcp))
System.out.printf("%s:%d -> %s:%d%n",
ip4.src(), tcp.srcPort(),
ip4.dst(), tcp.dstPort());
});
}
Core Components
| Class | Description |
|---|---|
NetPcap | High-level capture with protocol dissection |
PacketSettings |
Packet structure and memory configuration |
PacketHandler | Packet callback interfaces |
Pcap |
Low-level libpcap bindings |
PcapIf |
Network interface descriptor |
BpFilter |
Compiled BPF filter |
Memory Model
jNetPcap uses a layered memory model optimized for performance:
┌─────────────────────────────────────────────────────────────────┐ │ Native Capture Buffer │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Packet 1 │ Packet 2 │ Packet 3 │ ... │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ▲ │ │ │ ScopedMemory (zero-copy binding) │ │ │ │ │ ┌────┴────────────────┐ │ │ │ Packet │ │ │ │ ├─ ScopedMemory │ Data bound to native buffer │ │ │ ├─ Descriptor │ Protocol dissection results │ │ │ └─ Header bindings │ Zero-allocation header access │ │ └─────────────────────┘ │ │ │ │ Scope: Valid within dispatch/loop callback only │ └─────────────────────────────────────────────────────────────────┘
Memory Types
| Type | Allocation | Lifetime | Use Case |
|---|---|---|---|
| ScopedMemory | None (bind only) | Callback scope | Zero-copy capture |
| FixedMemory | Pool or Arena | Until recycled | Persistent packets |
| Hybrid | Mixed | Mixed | Zero-copy data + fixed descriptor |
Protocol Dissection
Dissection Modes
| Mode | Method | Description |
|---|---|---|
| Eager | dissect() |
Full dissection before callback, TYPE2 descriptor |
| On-demand | onDemand() |
Dissect lazily on first hasHeader() |
| None | (default) | No dissection, PCAP descriptor only |
Zero-Allocation Header Access
The hasHeader(Header) pattern provides zero-allocation protocol
access by reusing pre-allocated header instances:
// Allocate once outside the loop — reused for every packet, zero GC pressure
Ethernet eth = new Ethernet();
Ip4 ip4 = new Ip4();
Ip6 ip6 = new Ip6();
Tcp tcp = new Tcp();
Udp udp = new Udp();
pcap.loop(-1, packet -> {
if (packet.hasHeader(eth)) {
byte[] dstMac = eth.dst();
int etherType = eth.type();
}
if (packet.hasHeader(ip4)) {
String srcIp = ip4.src();
int ttl = ip4.ttl();
} else if (packet.hasHeader(ip6)) {
String srcIp = ip6.src();
int hopLimit = ip6.hopLimit();
}
if (packet.hasHeader(tcp)) {
int srcPort = tcp.srcPort();
int dstPort = tcp.dstPort();
}
});
Tunneled Protocol Access
For tunneled packets (GRE, VXLAN, etc.), use the depth parameter:
Ip4 outerIp = new Ip4();
Ip4 innerIp = new Ip4();
if (packet.hasHeader(outerIp, 0)) // Outer IP (depth 0)
System.out.println("Outer: " + outerIp.src());
if (packet.hasHeader(innerIp, 1)) // Inner IP (depth 1)
System.out.println("Inner: " + innerIp.src());
Packet Persistence
Packets in callbacks are bound to native buffers and valid only within callback scope. To keep packets beyond the callback, use the persistence API:
| Method | Returns | Memory | Use Case |
|---|---|---|---|
persist() | Same or copy | Fixed | Keep beyond callback scope |
persistTo(pool) | Pooled copy | Pool memory | High-volume zero-GC capture |
copy() | New copy | Auto-managed | Independent lifecycle |
duplicate() | New object | Shared (+ref) | Parallel processing |
recycle() | void | Returns to pool | Release pooled packet when done |
Queue<Packet> queue = new ConcurrentLinkedQueue<>();
pcap.loop(-1, packet -> {
if (isInteresting(packet))
queue.add(packet.persist());
});
// Consumer thread
while (running) {
Packet p = queue.poll();
if (p != null) {
process(p);
p.recycle(); // Return to pool (no-op if non-pooled)
}
}
Berkeley Packet Filter (BPF)
try (NetPcap pcap = NetPcap.openLive("eth0")) {
pcap.setFilter("tcp port 80 or tcp port 443");
pcap.loop(-1, handler);
}
Common Filter Expressions
"tcp" All TCP packets "udp port 53" DNS traffic "host 192.168.1.1" Traffic to/from host "net 10.0.0.0/8" Traffic to/from network "tcp port 80 or tcp port 443" HTTP and HTTPS "icmp" ICMP packets "vlan 100" VLAN tagged traffic "tcp[tcpflags] & tcp-syn != 0" TCP SYN packets only
Packet Descriptors
| Type | Size | Use Case |
|---|---|---|
PCAP_PADDED | 24 bytes | Kernel format (x64 padded) |
PCAP_PACKED | 16 bytes | File format (packed) |
TYPE2 | 96 bytes | Full protocol dissection results |
Thread Safety
NetPcap is strictly single-threaded. All capture operations must occur on the same thread. For multi-threaded processing:
- Capture on a dedicated thread
- Use
persist()for packets leaving the capture thread - Each worker thread needs its own header instances
BlockingQueue<Packet> workQueue = new LinkedBlockingQueue<>(10000);
// Capture thread
Thread captureThread = new Thread(() -> {
try (NetPcap pcap = NetPcap.openLive("eth0")) {
pcap.loop(-1, packet -> {
if (filter(packet))
workQueue.put(packet.persist());
});
}
});
// Worker threads — each needs its own header instances
ExecutorService workers = Executors.newFixedThreadPool(4);
for (int i = 0; i < 4; i++) {
workers.submit(() -> {
Tcp tcp = new Tcp(); // Thread-local, not shared
while (running) {
Packet p = workQueue.take();
if (p.hasHeader(tcp))
process(tcp);
p.recycle();
}
});
}
Capture Methods
| Method | Respects Timeout | Description |
|---|---|---|
loop(count, handler) | No | Process until count reached or breakloop() called |
dispatch(count, handler) | Yes | Process available packets up to count |
next() | Yes | Return next packet or null on timeout |
nextEx() | Yes | Return next packet with explicit status code |
Platform Support
| Platform | Native Library | Notes |
|---|---|---|
| Linux | libpcap 1.0+ | Best performance |
| Windows | Npcap 1.0+ | Administrator rights required for live capture |
| macOS | libpcap (system) | BPF device access required |
Related Packages
com.slytechs.sdk.jnetpcap— Low-level Pcap bindingscom.slytechs.sdk.protocol.core— Packet, Header, PacketSettingscom.slytechs.sdk.protocol.tcpip— Ethernet, IPv4, IPv6, TCP, UDPcom.slytechs.sdk.protocol.web— HTTP, TLS, QUICcom.slytechs.sdk.common.memory.pool— Pooling and persistence
- Since:
- 3.0
- Author:
- Mark Bednarczyk [mark@slytechs.com], Sly Technologies Inc.
- See Also:
-
ClassDescriptionBase class for NetPcap providing delegation to low-level Pcap bindings.High-level packet capture and protocol dissection API.A marker interface for different types of packet handlers used in packet capture and processing.Provides high-level packet handling using the Packet object model.Provides a Java Consumer-style interface for packet handling.