The CORD-FLOW Library

We strive towards all programmable, simple, easy to understand architecture, based on the assumption that networking, at least on dataplane level, should no longer be rocket science, but a simple flow of receiving a packet on one point (port or interface), then reading the headers (or payload) and, finally, taking a decision what to do with the packet (most likely changing something inside and then sending it over another point; or discarding it).

Another goal that we have in mind is that one day the same should apply to the control planes and the well-established routing protocol (OSPF, BGP, DNS and others).

We are building upon the foundation of virtualasation (like namespaces) and high-speed network processing (DPDK, XDP and even the Linux kernel itself, which is quite performant nowadays), also offering compability over different archictures and platforms - keeping the door for porting to new target from MCU to sophisticated network accelerators.

That way, if you need a router that inspects the traffic and acts as a proxy for specific traffic types, instead of browsing marketing materials what set of physical or virtual appliances to purchase to achieve a goal like this (then connecting them in a topology and configure them via vendor-specific shells or UI-softwares), we encourage you to write the exact logic you need by yourselves.

Architecture

The below diagrams are subject to frequent changes as the PacketCord.io is being delevoped, so we do not guarantee that they represent the actual architecture as it is in the latest commit.

CORD-FLOW is a modular component of the PacketCord network programming framework. It provides abstractions and components for high-performance packet flow programming in C (receive-match-action-transmit logic over packet headers and payload). It has been designed to be suitable for purposes across different platforms and architectures—from servers, through embedded systems, to specialised network hardware.

CORD-FLOW

Flow Overview

Based on the above statement how simple networking should be, we have designed the packet flow abstaction as universal as possible. Think of any networking appliance of a "box" with ports or interfaces - and call them flow points. Each flow point can receive and transmit packets. To certain more specific flow points, one can also attach a cBPF/eBPF filters.

Flow Overview

The RX() and TX() methods simply override calls like:

  • send() | recv()
  • sendmsg() | recvmsg()
  • sendmmsg() | recvmmsg()
  • read() | write ()
  • nread() | nwrite()
  • DPDK RX/TX bursts
  • Custom firmware or driver hooks and callbacks

Flow Point

Currently, we are exposing the following FlowPoint subtypes:

Flow Point

Kernel bypass has been famous for being the most optimal choice for high performance forwarding, but we also want to keep the Linux kernel exposed in different ways (because over time it has become quite flexible and performant). The network programmer is the one to chose the best strategy, or maybe combine the best of both worlds. Sometimes (for tunneling purposes, for example) it could be handy to interact even with the routing table of the IP stack. That is the reason we have also exposed the specific stack-inject flow point type.

The custom flow point could serve as a skeleton for porting a new software or hardware target. Some examples could be: a new Linux character device, Windows NPCAP, kernel hooks, FPGAs/DPUs/SmartNICs, embedded devices like Ethernet interfaced over SPI, custom radio or laser links over highly-specialised digital interfaces.

Event Hanlder

The CORD-FLOW library relies on the Linux API epoll() event notification mechanism and the DPDK RTE_ETH_FOREACH_DEV(port) loop to handle the input packets entering a flow point. In addition to this, there is also a skeleton for implementing a custom event handler. The below diagram depics how the code abstraction has been organised:

Event Handler

The epoll() logic behind the Linux API event handler is:

for (n = 0; n < n_fds; n++)
{
    if (events[n].data.fd == sock_fd)
    {
        ssize_t len = recvfrom(sock_fd);
        /* Process packet here ... */
    }
}