Pax be upon packets
System and network programming can be a magical mystery tour de force across several APIs and tools. Pax aspires to be your peaceful gesture at complexity, and seeks to facilitate prototype development.
Using Pax you describe a network packet processor in terms of what it does to packets sent and received through network logical ports, which are attached at runtime to network interfaces made available by your OS. The interfaces may be physical or virtual (e.g., a tap device).
A Pax processor can have any number of ports (numbered 0-3 in the drawing above) which serve as the main interface with the outside world. The processor can write to any of these ports, and processes data that arrives on some of the ports (according to its configuration).
Pax provides a library and runtime support that wrap underlying wrappers so you can quickly test prototypes of packet-processors written in high-level languages. Some example implementations are included in the repo.
Other than a C# (>=6.0) compiler and runtime, you need:
- libpcap (on UNIXy systems) or winpcap.dll (on Windows)
- SharpPcap and PacketDotNet
- Newtonsoft's JSON library (download one of the releases)
- On Ubuntu Linux (14.04) it's easiest to follow Mono's
instructions to install the latest version,
and
apt-get install
themono-complete
package. You might need to tweak Mono's config files (such as that in/etc/mono/config
) in order to direct Mono's loader to the right location of libpcap on your system (by adding a dllmap entry). - Optional Mininet for testing. We used Mininet 2.2.1 on Ubuntu 14.04 LTS.
Put the DLLs for Newtonsoft.Json, SharpPcap and PacketDotNet in Pax's lib/
directory.
Run the build.sh
script and everything should go smoothly.
This will produce a single file (Pax.exe), called an assembly in .NET jargon. This assembly serves two purposes:
- It is the tool that runs your packet processors using a configuration you provide.
- It is the library that your packet processors reference. This reference will be checked by the .NET compiler when compiling your code.
Packet processers using Pax can be written in any .NET language. They use Pax's API and define one or more functions that handle incoming packets. The examples included with Pax could help get you going. The workflow is as follows:
- Write your packet processors and use a .NET compiler to produce a DLL. Your DLL may contain multiple packet processors -- it is the configuration file that specifies which processor you wish you bind with which network interface.
- Write a configuration file (or scheme) for your packet processor. This specifies all parameters to your packet processor, including which specific network interfaces that are bound to logical ports. Configuration in Pax is JSON-encoded.
- Run Pax, indicating your configuration and DLL.
The configuration file "wires up" the network interfaces with packet processors in your assembly. Not all packet processors in your assembly need be connected, and different network interfaces may be connected to the same handler. The drawing example shows an assembly with four packet processors, only two of which is used. The blue processor handles packets coming on network port 0. The configuration file determines which processors handle traffic coming on which network interface.
Simply run Pax.exe CONFIGURATION_FILENAME ASSEMBLY_FILENAME
.
Probably you'd have to run this command with root/administrator-level access
because of the privileged access to hardware that's used while Pax is running.
For the example code I run:
sudo ./Bin/Pax.exe examples/wiring.json examples/Bin/Examples.dll
Pax then starts up and checks the configuration file and assembly, listing some of their contents. It connects the network interfaces with the handlers in the assembly, as specified in the configuration. Then Pax activates the handlers, and your code takes it from there.
The main handler function of our NAT example looks like this. The return value is the port over which to emit the (modified) packet. The actual network interface connected to that port is determined by the configuration file.
// Get the forwarding decision
ForwardingDecision forwardingDecision;
if (incomingNetworkInterface == Port_Outside)
forwardingDecision = OutsideToInside(packet);
else
forwardingDecision = InsideToOutside(packet, incomingNetworkInterface);
return forwardingDecision;
And the OutsideToInside
function is implemented as follows:
private ForwardingDecision OutsideToInside(TEncapsulation packet)
{
// Retrieve the mapping. If a mapping doesn't exist, then it means that we're not
// aware of a session to which the packet belongs: so drop the packet.
var key = new ConnectionKey(packet.GetSourceNode(), packet.GetDestinationNode());
NatConnection<TPacket,TNode> connection;
if (NAT_MapToInside.TryGetValue(key, out connection))
{
var destination = connection.InsideNode;
// Update any connection state, including resetting the inactivity timer
connection.ReceivedPacket(packet, packetFromInside: false);
// Rewrite the packet destination
packet.SetDestination(destination);
// Update checksums
packet.UpdateChecksums();
// Forward on the mapped network port
return new ForwardingDecision.SinglePortForward(destination.InterfaceNumber);
}
else
{
return Drop;
}
}
Apache 2.0
- Project NaaS and its funder (EPSRC).
- Colleagues at NetOS.
- Contributors to PacketDotNet, SharpPcap, Newtonsoft.JSON, libpcap and winpcap.
Try using Pax to implement prototypes of the following:
- Protocol conversion (e.g., IPv4 <-> IPv6)
- DPI
- Load balancer (see these descriptions from the HAProxy and NGINX sites for ideas)
- Datagram-based servers, e.g., DNS.
- Firewall
- Router
If you're interested in what Pax does, you might be interested in these other systems: