FMADIO Systems have a simple internal ring bus architecture. This Ring bus can be used for sending packet data within the system to containers all without dropping any packets.
In the diagram below its a single Shared Memory lockless Ring.
Producer will write packets into the shared memory PUT slot + updated the PUT pointer
Consumer will read packets from the shared memory GET slot + update the GET pointer
Flow control ensures the PUT and GET pointers do not overflow or underflow
Lock free design. There are not mutexes or locks
Its a simple system, by default the FMADIO Ring is only 1024 × 9K jumbo packets deep. This keeps the data hot in the L1, L2, L3 caches while still allow ebbs and flows of the processing.
The location of the rings are in
/opt/fmadio/queue/lxc_*
This allows an easy way to pass the the entire FMADIO Ring to an LXC container. For example the FShark2 container has the following in the lxc config
#set lxc ring mount
lxc.mount.entry = /opt/fmadio/queue/lxc_fshark2 opt/fmadio/queue/lxc_fshark2 none bind,create=file 0 0
lxc.prlimit.nofile = 65535
lxc.prlimit.memlock = unlimited
Monitoring Ring Status
fmadiocli
FMADIO CLI can be used to monitor the status of the ring using the command
fmadiocli "show ring status"
Example output below:
fmadio@fmadio200v4-636:~$ fmadiocli "show ring status"
fmad fmadlua Oct 18 2024 (/opt/fmadio/bin/fmadiolua --nocal /opt/fmadio/bin/fmadiocli show ring status )
Disable cycle calibration
[Tue Dec 17 19:09:17 2024] CmdLine [show ring status]
[Tue Dec 17 19:09:17 2024] Cmd [show ring status]
[Tue Dec 17 19:09:17 2024] Name : Path : Status : Pkt Put : Pkt Get : Pkt Queued : Desc
[Tue Dec 17 19:09:17 2024] -----------------------------------------+--------------------------------------------------------------+------------+------------------+------------------+------------+------------------------------------
[Tue Dec 17 19:09:17 2024] lxc_fshark2 : /opt/fmadio/queue/lxc_fshark2 : online : 3,137,624 : 3,137,624 : 0 :
[Tue Dec 17 19:09:17 2024] -----------------------------------------+--------------------------------------------------------------+------------+------------------+------------------+------------+------------------------------------
done 0.036744Sec 0.000612Min
fmadio@fmadio200v4-636:~$
Producer : stream_cat
The primary packet producer is the stream_cat utility. This will fetch data from the local storage, filter it and write to the specified ring
Offline Capture
Below is the command to write a PCAP without filtering into the ring
sudo stream_cat -v --ring /opt/fmadio/queue/lxc_fshark2 <capture name>
Example output looks like below
fmadio@fmadio200v4-636:~$ sudo stream_cat -v --ring /opt/fmadio/queue/lxc_fshark2 wan0_20241217_1319
Create FMAD Ring: 0 [/opt/fmadio/queue/lxc_fshark2]
RING[/opt/fmadio/queue/lxc_fshark2 ] 00 : CPU: 0 FilterBPF:[(null)] FilterFrame:[(null)]
StartChunk: 6338017
RING[/opt/fmadio/queue/lxc_fshark2 ] Size : 12595200 16777216
RING[/opt/fmadio/queue/lxc_fshark2 ] Version: 100 100
RING[/opt/fmadio/queue/lxc_fshark2 ] Put:3ff 3ff 0x7f5af8334000
RING[/opt/fmadio/queue/lxc_fshark2 ] Get:3ff 3ff 0x7f5af8335000
RING[/opt/fmadio/queue/lxc_fshark2 ] thread:0
RING[/opt/fmadio/queue/lxc_fshark2 ] worker thread start
{"tstr":"20241217_190746", "timestamp":1734433666,"PktCnt": 0, "PktByte": 0, "ChunkID":6338017,"PCAPTS":"00:00:00.000.000.000","PendingB":49860575232, "Read_bps":0,"Read_pps":0, "Write_bps":0,"Write_pps":0, "FwdPct":0.000 }
{"tstr":"20241217_190747", "timestamp":1734433667,"PktCnt": 504489, "PktByte": 365485290, "ChunkID":6339449,"PCAPTS":"20:00:03.678.433.179","PendingB":49485185024, "Read_bps":2918460195,"Read_pps":503553, "Write_bps":5829923357,"Write_pps":1007107, "FwdPct":2.000 }
{"tstr":"20241217_190748", "timestamp":1734433668,"PktCnt": 1000274, "PktByte": 797697754, "ChunkID":6341128,"PCAPTS":"20:00:07.418.689.821","PendingB":49045045248, "Read_bps":3456240418,"Read_pps":495576, "Write_bps":6902900114,"Write_pps":991152, "FwdPct":2.000 }
{"tstr":"20241217_190749", "timestamp":1734433669,"PktCnt": 1513100, "PktByte": 1154918776, "ChunkID":6342522,"PCAPTS":"20:00:11.297.875.278","PendingB":48679616512, "Read_bps":2855335713,"Read_pps":512389, "Write_bps":5703834082,"Write_pps":1024779, "FwdPct":2.000 }
{"tstr":"20241217_190750", "timestamp":1734433670,"PktCnt": 2030128, "PktByte": 1479828590, "ChunkID":6343793,"PCAPTS":"20:00:15.832.633.068","PendingB":48346431488, "Read_bps":2593419322,"Read_pps":515863, "Write_bps":5180966837,"Write_pps":1031725, "FwdPct":2.000
.
.
.
Offline Filtered Capture
By adding --ring-filter-bpf <ring> BPF
to the command line a BPF filter can be applied to the traffic going into the FMADIO Ring, in this example filtering for vlan 2
sudo stream_cat -v --ring /opt/fmadio/queue/lxc_fshark2 --ring-filter-bpf /opt/fmadio/queue/lxc_fshark2 "vlan 2" <capture name>
Realtime Capture
Forwarding the captured data in realtime is achieved using the --follow
command line option. Example below
sudo stream_cat -v --follow --ring /opt/fmadio/queue/lxc_fshark2
Realtime BPF Filtered Capture
Sending BPF filtered capture data in realtime to the FMADIO Ring, Example below
sudo stream_cat -v --follow --ring /opt/fmadio/queue/lxc_fshark2 --ring-filter-bpf /opt/fmadio/queue/lxc_fshark2 "vlan 2"
Realtime BPF Filtered Port Filtered Capture
Finally filtering on the physical capture port and a BPF filter sending data into the FMADIO Ring. This uses the --ring-filter-frame <ring> <frame filter>
functionality. Example below
sudo stream_cat -v --follow --ring /opt/fmadio/queue/lxc_fshark2 --ring-filter-bpf /opt/fmadio/queue/lxc_fshark2 "vlan 2" --ring-filter-bpf /opt/fmadio/queue/lxc_fshark2 "capture.port=1"
Producer : pcap2fmadio
In addition to stream_cat on the host system there is a utility pcap2fmadio
. This does a similar function as stream_cat however it operates using PCAP format on STDIN.
Unfiltered PCAP
For example to send a pcap named test.pcap
into the lxc_fshark2
FMADIO Ring, shown below
cat test.pcap | pcap2fmadio -i /opt/fmadio/queue/lxc_fshark2
Example output:
fmadio@fmadio200v4-636:~$ cat test.pcap | pcap2fmadio -i /opt/fmadio/queue/lxc_fshark2
Nano PCAP
RING[/opt/fmadio/queue/lxc_fshark2 ] Size : 12595200 16777216
RING[/opt/fmadio/queue/lxc_fshark2 ] Version: 100 100
RING[/opt/fmadio/queue/lxc_fshark2 ] Put:428935 135 0x7ffac3f9a000
RING[/opt/fmadio/queue/lxc_fshark2 ] Get:428536 136 0x7ffac3f9b000
Filtered PCAP
A more complicated example can use zcat and tcpdump to filter the data before it goes into the ring buffer. In the example below it is
decompressing in gzip format
filtering for non VLAN tagged packets using tcpdump
writing the filtered packets into lxc_fshark2 ring
zcat test.pcap.gz | tcpdump -r - -w - "not vlan" | pcap2fmadio -i /opt/fmadio/queue/lxc_fshark2
Example output:
fmadio@fmadio200v4-636:~$ zcat test.pcap.gz | tcpdump -r - -w - "not vlan" | pcap2fmadio -i /opt/fmadio/queue/lxc_fshark2
reading from file -, link-type EN10MB (Ethernet), snapshot length 16384
Nano PCAP
RING[/opt/fmadio/queue/lxc_fshark2 ] Size : 12595200 16777216
RING[/opt/fmadio/queue/lxc_fshark2 ] Version: 100 100
RING[/opt/fmadio/queue/lxc_fshark2 ] Put:488501 101 0x7efdd0c66000
RING[/opt/fmadio/queue/lxc_fshark2 ] Get:488501 101 0x7efdd0c67000
Consumer : fmadio2pcap
On the otherside the utility fmadio2pcap
will take the FMADIO Ring and convert that into standard PCAP format. While this is a suboptimal approach almost all network analysis tools have support for PCAPs. This enables a fast way to get up and running with almost any network analysis tool.
tcpdump output
Example piping the output into tcpdump
fmadio2pcap -i /opt/fmadio/queue/lxc_fshark2 | tcpdump -r - -nn
Example output below:
Library
To enable processing there is a simple API, its part of our public code repo located below:
https://github.com/fmadio/platform/blob/main/include/fmadio_packet.h
Full BSD license opensource library. Its a simple shared memory ring buffer making it simple for direct integration. Basic full source code example is shown below with the fmadio2pcap reference code.
fmadio2pcap example
The above fmadio2pcap example utility is located
https://github.com/fmadio/platform/tree/main/fmadio2pcap
The example fmadio2pcap utility shows how simple it is to get packets off the FMADIO Ring. Core code snippet below
while (!s_Exit)
{
u64 TS;
PCAPPacket_t* Pkt = (PCAPPacket_t*)PktBuffer;
u32 PktFlag = 0;
// fetch packet from ring without blocking
int ret = FMADPacket_RecvV1(s_RING, false, &TS, &Pkt->LengthWire, &Pkt->LengthCapture, NULL, &PktFlag, Pkt + 1);
// if it has valid data
if (ret > 0)
{
// count flaged FCS packets
if (PktFlag & FMADRING_FLAG_FCSERR)
{
TotalPktFCS++;
}
// santize it
assert(Pkt->LengthCapture > 0);
assert(Pkt->LengthCapture < 16*1024);
// convert 64b epoch into sec/subsec for pcap
Pkt->Sec = TS / (u64)1e9;
Pkt->NSec = TS % (u64)1e9;
// write PCAP header and payload
fwrite(PktBuffer, 1, sizeof(PCAPPacket_t) + Pkt->LengthCapture, FPCAP);
// general stats
TotalPkt += 1;
TotalByte += ret;
}
// end of stream
if (ret < 0) break;
}
fflush(stdout);