-
Notifications
You must be signed in to change notification settings - Fork 0
1. Overview
This page provides an overview of the supported filter expression dialects and how to use the BPF compiler and virtual machine interpreter.
JNetRuntime BPF compiler supports three major filter expression dialects, each with its own syntax and semantics but all compiling to standard BPF bytecode.
The traditional and most widely used packet filtering syntax, originated from the Berkeley Packet Filter.
// Example usage
BpfCompiler compiler = new PcapCompiler();
byte[] bytecode = compiler.compile("tcp port 80 and not broadcast");
Key characteristics:
- Simple, concise syntax
- Protocol-based filtering
- Direct access to packet fields
- Widely documented and understood
- Compatible with tcpdump and libpcap
More expressive and field-oriented syntax, familiar to Wireshark users.
// Example usage
BpfCompiler compiler = new WiresharkCompiler();
byte[] bytecode = compiler.compile("http.request.method == \"GET\" && ip.addr != 10.0.0.0/8");
Key characteristics:
- Protocol-field based syntax
- Rich comparison operators
- String and pattern matching
- Function support
- Extensive protocol support
Detailed Wireshark Filter Grammar
Hardware-acceleration oriented syntax with explicit protocol layering.
// Example usage
BpfCompiler compiler = new NtplCompiler();
byte[] bytecode = compiler.compile("Layer3Protocol == IPv4 AND TCP[DstPort] == 80");
Key characteristics:
- Explicit protocol layer handling
- Hardware-friendly constructs
- Rich set of protocol fields
- Support for complex protocol stacks
- Performance-oriented design
All dialect compilers implement the BpfCompiler interface:
public interface BpfCompiler {
byte[] compile(String expression) throws CompilerException;
// Optional: compile with specific options
byte[] compile(String expression, CompilerOptions options) throws CompilerException;
}
// Using the factory
BpfCompiler compiler = BpfCompiler.forDialect(FilterDialect.PCAP);
BpfCompiler compiler = BpfCompiler.forDialect(FilterDialect.WIRESHARK);
BpfCompiler compiler = BpfCompiler.forDialect(FilterDialect.NTPL);
// Or direct instantiation
BpfCompiler pcapCompiler = new PcapCompiler();
BpfCompiler wiresharkCompiler = new WiresharkCompiler();
BpfCompiler ntplCompiler = new NtplCompiler();
CompilerOptions options = CompilerOptions.builder()
.optimizationLevel(OptimizationLevel.AGGRESSIVE)
.debug(true)
.maxInstructions(1024)
.build();
byte[] bytecode = compiler.compile(expression, options);
The BPF VM is designed to be stateless, with all execution state maintained in a context object. This allows for concurrent execution of programs across multiple threads.
// Create VM instance (stateless)
BPFVirtualMachine vm = new BPFVirtualMachine();
// Create execution context (holds state)
BPFContext context = new BPFContext();
// Load program
byte[] bytecode = compiler.compile("tcp port 80");
BPFProgram program = BPFProgram.load(bytecode);
// Process multiple packets concurrently
void processPacket(byte[] packet) {
// Reset context for new execution
context.reset();
// Set packet data
context.setPacket(packet, packet.length);
// Execute program
boolean matches = program.execute(context);
}
// Example of concurrent execution
public class PacketProcessor {
private final BPFProgram program;
// ThreadLocal ensures each thread has its own context
private final ThreadLocal<BPFContext> contextHolder;
public PacketProcessor(String filter) {
byte[] bytecode = compiler.compile(filter);
this.program = BPFProgram.load(bytecode);
this.contextHolder = ThreadLocal.withInitial(BPFContext::new);
}
public boolean processPacket(byte[] packet) {
BPFContext context = contextHolder.get();
context.reset();
context.setPacket(packet, packet.length);
return program.execute(context);
}
}
-
Context Reuse
// Reuse context within the same thread BPFContext context = new BPFContext(); for (byte[] packet : packets) { context.reset(); context.setPacket(packet, packet.length); program.execute(context); }
-
Batch Processing
// Process packets in batches public void processBatch(List<byte[]> packets) { BPFContext context = contextHolder.get(); for (byte[] packet : packets) { context.reset(); context.setPacket(packet, packet.length); if (program.execute(context)) { // Handle matching packet } } }
-
Multiple Programs
// Run multiple programs on same packet public boolean matchesAny(byte[] packet, List<BPFProgram> programs) { BPFContext context = contextHolder.get(); for (BPFProgram program : programs) { context.reset(); context.setPacket(packet, packet.length); if (program.execute(context)) { return true; } } return false; }
try {
byte[] bytecode = compiler.compile(expression);
} catch (SyntaxException e) {
// Handle syntax errors
System.err.println("Syntax error at position " + e.getPosition());
} catch (CompilerException e) {
// Handle general compilation errors
} catch (BPFException e) {
// Handle VM execution errors
}