Table of Contents
General description
Soft-Router is the SSG routing function that builds and maintains its own routing table. It is implemented on top of standard Linux mechanisms (kernel route table, network namespace) and a third-party routing daemon (BIRD, FRR, etc.) that handles dynamic routing protocols, while SSG uses the table built by the daemon to route subscriber traffic. In addition to basic routing, Soft-Router supports VRF Lite — isolation of routing tables for different subscriber groups or services within a single device.
SSG itself does not build the routing table. It delegates this work to proven specialized tools; the internal router architecture example uses the BIRD routing daemon. The routing daemon processes the required routing protocols (BGP, OSPF, etc.), uses them to build a common routing table and loads it into the operating system kernel. SSG routes packets according to this table. In other words, building the routing table and processing traffic according to it are separated.
SSG reads the routing table only through the standard Linux interface, so it is compatible with any routing daemon.
Internal architecture
Since BIRD builds the routing table in the OS kernel, the BIRD routing daemon runs in a separate net namespace (router netns in the internal router architecture diagram) so that the Linux server itself does not apply these rules. Routing protocol packets arrive at the SSG in/out devices as part of the general traffic. For each in/out device involved in routing, a veth pair of "shadow" interfaces is created: a TAP interface on the SSG side (for example, bng-w) and a peer interface (for example, rib-w) — a regular system interface in the routing daemon netns.
System requirements are described in Hardware requirements and performance.
Data from the kernel route table is read (rtnetlink) into the router RIB (Routing Information Base). RIB is a prefix tree that is convenient to modify on kernel route table change events (deleting/adding entries). RIB is handled by a separate router thread, because RIB does not support multithreaded access.
Synchronization is performed in at least two passes: it is considered successful if two consecutive reads of the Linux route table return the same number of entries. fastDPI keeps reading the route table until this condition is met; however, if four consecutive attempts fail, fastDPI builds its RIB/FIB from the last attempt and schedules a new synchronization after a short time (2 seconds).
fastDPI worker threads use the FIB (Forwarding Information Base). This structure is designed for multithreaded lookup (LPM — longest prefix match), but not for modification (deleting/adding entries). A FIB can only be built from scratch from the RIB and then used for LPM. Therefore, SSG has two FIBs — the current one (used for routing by the worker threads right now) and the "future" one. Every router_fib_refresh seconds, SSG checks whether the RIB has changed since the current FIB was built. If it has, SSG builds a new FIB (in the router thread) in place of the "future" one and then immediately switches the current FIB to the new one. This way, the worker threads see all changes made to the routing table.
Supported routing daemons
The routing daemon (BIRD, FRR, etc.) and SSG settings must be consistent: the routing daemon must create a kernel route table with the number set by the router_kernel_table parameter.
Supported routing daemons:
- BIRD. BIRD version 2 and higher is supported. Version 1 is not supported.
Configuration example
Official documentation - FRRouting
Official documentation - QUAGGA
Official documentation - Juniper CRPD
Official documentation
LAG support
The router supports link aggregation (LAG).
With aggregated links, packets that must be diverted to the TAP interface can arrive at any device in the LAG. To avoid creating essentially identical TAP interfaces for every device in the LAG, the router takes into account which devices belong to the LAG and diverts traffic of the specified subnets to the TAP (to the BIRD daemon) from all of them.
Each LAG is defined by a separate section in fastdpi.conf that lists all devices included in the LAG. We recommend using active LAG (with LACP); use "transparent" LAG without LACP only for SSG redundancy where acceptable (for example, for filtering). Example of an active LAG towards inet:
in_dev=01-00.0:02-00.0
out_dev=01-00.1:02-00.1
lag {
name=inet
lacp=2
short_timeout=on
device=01-00.1
device=02-00.1
}
router_device {
device=01-00.1
tap=bng-w
peer=rib-w
subnet=10.0.10.0/26
}
Where:
in_dev,out_dev— input and output devices combined into the LAG.lag— description of the LAG towards inet:name— optional LAG name, used only for log output;lacp— LACP processing mode:0— LACP processing is disabled: SSG does not hold the LAG and passes traffic "transparently" (default);1— passive mode: SSG does not send periodic LACPDUs but responds to incoming ones;2— active mode: SSG sends periodic LACPDUs. Recommended value, used in the example;
short_timeout— LACP timeout:on— short,off— long;device— a device included in the LAG. Each device is set by a separatedeviceparameter.
router_device— router interface description:device— device name fromout_dev. The veth pair of TAP interfaces is created only for this device, not for every device in the LAG;tap— TAP interface name for the device. Default —dpi+ device name;peer— name of the peer TAP interface in the netns. Default —rib+ device name;subnet— a subnet diverted from the general traffic to the TAP interface. If there are several subnets, each is set by a separatesubnetparameter.
If the router works both towards inet and towards subs (for example, BGP on the inet side and OSPF towards subs), a separate router_device section is set for the LAG of each direction. Active LAG configuration is described in detail in Configuring on-stick and LAG/LACP.
lag sections in fastdpi.conf are cold parameters: changing the LAG description requires a fastDPI restart.
ARP management
NeighborDB is a nexthop database maintained by fastDPI itself: it sends ARP requests to resolve nexthop MAC addresses and updates the ARP cache for these nexthops. The following options control updates:
router_neighbor_lifetime— lifetime of an ARP cache entry, in seconds.
After this time, the entry must be refreshed by sending an ARP request (ICMPv6 NDP for IPv6).
Default: 300 seconds.router_neighbor_refresh_timeout— time to wait for a response to an ARP request, in seconds.
Default: 3 seconds.router_neighbor_refresh_attempt— number of failed refresh attempts after which an ARP cache entry is marked Unreachable; in other words, how many consecutive ARP Requests must go unanswered for the entry to become unreachable.
Default: 10 attempts.router_neighbor_unreachable_refresh— after an entry becomes Unreachable, fastDPI periodically tries to revive it by sending ARP Requests. This option sets that period in seconds.
Default: 30 seconds.
A nexthop entry consists of:
- nexthop IP;
- Mandatory — name of the port used to reach this nexthop (for LAG — one of the ports in the LAG);
- Optional — VLAN
- Optional — nexthop MAC address. Entries with an explicitly set nexthop MAC address are static ARP cache records — ARP requests are never sent for them.
NeighborDB (IPv4 and IPv6) is stored in SDR in the router.mdb file. SDR (System Data Repository) is a storage similar to UDR, but it holds system settings. The SDR directory is set by the sdr_path parameter, default — /var/db/dpisdr.
NeighborDB is managed (entries added/modified/deleted) with CLI commands:
router neighbor dump— outputs the full list of all loaded entries and their internal staterouter neighbor dump db— outputs a dump of therouter.mdbdatabase.router neighbor add— adds a new entry (new nexthop) to NeighborDB.router neighbor update— modifies an existing NeighborDB entryrouter neighbor delete— deletes a NeighborDB entryrouter neighbor refresh— forces a refresh (sends an ARP Request) of the MAC address of a specific nexthop IP, of all nexthops in the specified VRF, or of the entire NeighborDBrouter neighbor purge— deletes all NeighborDB entries (full NeighborDB cleanup).
fdpi_cli <command> ?
Example output of the router neighbor add command:
[root@dpi ~]# fdpi_cli router neighbor add 192.168.70.2 dev=00:17.0 vlan=111 IP=192.168.70.2 MAC=<n/a> device='00-17.0' Placement in neighbor cache: index=3 gen=95 State: [0]Initial, remaining refresh probe count=0 Next refresh: 2026/11/11 13:08:58, -00:00:00.106 (10026508443332220 ticks)
Example output of the router neighbor dump command:
[root@dpi ~]# fdpi_cli router neighbor dump Rec#0 IP=192.168.70.2 MAC=<n/a> device='00-17.0' Placement in neighbor cache: index=3 gen=95 State: [1]Refreshing, remaining refresh probe count=6 Next refresh: 2026/11/11 13:09:11, +00:00:00.699 (10026532995292063 ticks) ---------- TOTAL: 1 recs
ARP cache priority
With NeighborDB, fastDPI has two independent sources of ARP cache entries:
- [previous] the OS network stack in the netns. fastDPI intercepts the interaction with neighbors and fills its ARP cache for nexthops accordingly. ARP requests are sent by Soft-Router or any other service that requests neighbor data.
- [new] NeighborDB: fastDPI itself keeps the entries up to date by sending ARP Requests in time. NeighborDB entries have higher priority than entries obtained from the OS network stack.
Address announcement specifics
Subscriber announcements and NAT pool
Subscriber address announcement is enabled by a parameter in fastdpi.conf:
router_subs_announce=6
The parameter is a bit mask. Values:
0x00001— announce the subscriber address towards subs0x00002— announce the subscriber address towards inet (if NAT is not enabled for the subscriber)0x00004— announce NAT subnets towards inet0x00008— announce subscriber gateways (the direction is set by flags0x00001,0x00002)0x10000— withdraw the announcement of an L3 subscriber on acct idle (acct session closed by idle timeout)
Default: 0 — announce nothing
Flags are added together. Possible router_subs_announce values:
| Value (hex) | Value (dec) | Flags | What is announced | Note |
|---|---|---|---|---|
0x00000 | 0 | — | nothing is announced | |
0x00001 | 1 | 0x00001 | subscriber address towards subs | |
0x00002 | 2 | 0x00002 | subscriber address towards inet (if NAT is not enabled for the subscriber) | |
0x00003 | 3 | 0x00001 + 0x00002 | subscriber address towards subs and inet | |
0x00004 | 4 | 0x00004 | NAT subnets towards inet | |
0x00005 | 5 | 0x00001 + 0x00004 | subscriber address towards subs + NAT subnets towards inet | |
0x00006 | 6 | 0x00002 + 0x00004 | subscriber address towards inet + NAT subnets towards inet | |
0x00007 | 7 | 0x00001 + 0x00002 + 0x00004 | subscriber address towards subs and inet + NAT subnets towards inet | |
0x00008 | 8 | 0x00008 | subscriber gateways (the direction is set by flags 0x00001, 0x00002) | not used |
0x00009 | 9 | 0x00001 + 0x00008 | subscriber address and subscriber gateways towards subs | |
0x0000a | 10 | 0x00002 + 0x00008 | subscriber address and subscriber gateways towards inet | |
0x0000b | 11 | 0x00001 + 0x00002 + 0x00008 | subscriber address and subscriber gateways towards subs and inet | |
0x0000c | 12 | 0x00004 + 0x00008 | NAT subnets towards inet + subscriber gateways (the direction is set by flags 0x00001, 0x00002) | not used |
0x0000d | 13 | 0x00001 + 0x00004 + 0x00008 | subscriber address towards subs + NAT subnets towards inet + subscriber gateways towards subs | |
0x0000e | 14 | 0x00002 + 0x00004 + 0x00008 | subscriber address towards inet + NAT subnets towards inet + subscriber gateways towards inet | |
0x0000f | 15 | 0x00001 + 0x00002 + 0x00004 + 0x00008 | subscriber address towards subs and inet + NAT subnets towards inet + subscriber gateways towards subs and inet |
To also withdraw the announcement of an L3 subscriber when its acct session is closed by idle timeout, add the 0x10000 flag (65536) to a value from the table. For example, router_subs_announce=0x10006 (65542) — announce the subscriber address and NAT subnets towards inet and withdraw the subscriber announcement on idle timeout. The values 0x10000, 0x10008 and 0x1000c are not used.
NAT public address subnets are announced only towards inet when SSG starts and when NAT profiles are added/removed/modified.
Subscriber addresses can be announced both towards inet and towards subs. However, if the subscriber IP address is in the private address range and service 11 is assigned to it, the subscriber address is not announced towards inet, so define private address ranges carefully.
The announcement is made to the BIRD routing table for all TAP devices of the allowed direction; BIRD then picks up the changes and announces them via the relevant protocols according to its configuration.
If nat_exclude_private=1 is set in fastdpi.conf (do not apply NAT to "private IP → private IP" packets), SSG will announce the private address of a subscriber with NAT enabled towards inet, because the upstream router must know the route to the subscriber's private IP address.
Announcement of L3 subscriber addresses
Algorithm for announcing IP addresses of L3 subscribers:
- by default, the address is announced on successful authorization (accept) and is NOT announced on failed authorization (reject)
- the
fastdpi.confparameterauth_announce_rejectglobally allows announcements for reject. Possible values:1— announce unauthorized (Reject) subscribers0— do not announce unauthorized (Reject) subscribers (default)
- RADIUS attribute
VasExperts-Route-Announce: value0— do not announce the subscriber address, value1— announce it. This attribute takes precedence overauth_announce_reject.
So, the following algorithm decides whether to announce the subscriber IP address:
- if the
VasExperts-Route-Announceattribute is explicitly set in the RADIUS response (Accept or Reject), the address is announced forVasExperts-Route-Announce=1and is NOT announced forVasExperts-Route-Announce=0 - if the RADIUS response contains no
VasExperts-Route-Announceattribute:- For Access-Accept (successful authorization), the subscriber IP address is announced
- For Access-Reject (failed authorization), the IP address is announced only if the
fastdpi.confparameterauth_announce_reject=1
fastdpi.conf parameter router_subs_announce.
This algorithm also applies to ARP authorization and GTP authorization, that is, to all authorization types where the subscriber IP address is known when authorization is called and cannot change.
For other L2 authorization types (DHCP, PPP — all types where the IP address is explicitly assigned), the VasExperts-Route-Announce attribute is ignored and the address is announced once it is assigned to the subscriber.
Multi-path routing (ECMP) support
SSG supports Equal-cost multi-path routing (ECMP).
SSG balances traffic (round-robin) at the flow level across all routes of a multi-path. Flow-level balancing means that a flow is bound to one of the multi-path routes, and the selected route does not change until the flow ends (unless the multi-path set changes due to external events from the routing daemon).
When sending traffic over the selected route, SSG uses the tx device associated with that route.
NUMA-aware route selection
On dual-CPU servers, SSG can take the system NUMA topology into account when selecting a route from a multi-path. This requires two conditions:
- the
numa=onparameter is set infastdpi.conf; - the system has more than one NUMA node (the number of NUMA nodes is determined by DPDK during initialization).
In this case, priority is given to the route via a tx device that belongs to the same NUMA node as the rx port that received the packet. If the multi-path has no such route, the route is selected without regard to NUMA.
Configuration
No configuration is required in SSG to enable multi-path. ECMP support is enabled in the routing daemon configuration. For example, in BIRD, ECMP is enabled with merge paths yes in the kernel protocol, see the BIRD documentation.
Was this information helpful?