libnftables-dotnet
libnftables-dotnet is a typed-first .NET wrapper over system-installed libnftables, with a high-level object model for common workflows and low-level SWIG-generated bindings for advanced control.
Current Scope
This library is intentionally narrow.
- High-level managed API:
- typed
NftRuleset/NftTable/NftSet/NftMap/NftChain/NftRuleauthoring RenderRulesetValidateAndRenderRulesetValidateRulesetApplyRulesetSnapshotRestore
- typed
- Low-level managed wrapper:
NftContextfor direct control over flags, buffering, include paths, variables, and command execution
Non-goals for the current release:
- Full nft expression parity and snapshot parsing back into object models
- Event monitoring or subscriptions
- Cross-platform support beyond Linux x64
Runtime Support
Native operations currently support:
- Linux only
- x64 only
- System-installed
libnftables
The package includes the generated Linux x64 native wrapper, but it still depends on the host system providing libnftables.
Requirements
- Linux
libnftablesheaders and shared library installedgcc- .NET SDK 10+
swigonly if you regenerate bindings
On Debian/Ubuntu-like systems, the runtime dependency is typically installed from the system package repository. Exact package names can vary by distro.
Build
dotnet build
dotnet build compiles the native SWIG wrapper (libLibNftablesBindings.so) from the checked-in generated C wrapper code.
Test
dotnet test LibNftables.slnx
The test suite contains:
- Managed/unit tests that do not require a native runtime
- Native integration tests that self-gate when
libnftablesis unavailable - Privileged tests that only run when
LIBNFTABLES_RUN_PRIVILEGED_TESTS=1,uid == 0, andCAP_NET_ADMINis available
Gitea Smoke CI
The repository includes a Gitea Actions smoke workflow at .gitea/workflows/smoke.yml.
- Trigger: push and pull request
- Runner label:
debian-13 - Job model: root-aware smoke verification
- Workflow actions:
- use a preinstalled .NET 10 SDK when the runner already has one
- otherwise bootstrap a local .NET 10 SDK inside the job
- restore
- build
- always run the non-privileged smoke test lane
- run the privileged test lane when the runner process is
uid 0
Important runner prerequisites:
- Debian 13 host with the
debian-13label bashcurlgccpkg-config- system-installed
libnftablesdevelopment/runtime packages discoverable bypkg-config
The job does not attempt package-manager installs or privilege escalation. It is intended to catch restore/build/test regressions with opportunistic privileged smoke coverage on root runners.
The workflow auto-detects id -u at runtime:
- non-root runners stay on the smoke-only path
- root runners enable
LIBNFTABLES_RUN_PRIVILEGED_TESTS=1 - root runners without effective
CAP_NET_ADMINemit a warning and skip the privileged lane
For local runs, dotnet test keeps privileged tests disabled by default. To opt in intentionally, set LIBNFTABLES_RUN_PRIVILEGED_TESTS=1 in an environment where the process is running as root with CAP_NET_ADMIN.
High-Level Example
using LibNftables;
INftablesClient client = new NftablesClient();
var ruleset = new NftRuleset();
var table = new NftTable
{
Family = NftFamily.Inet,
Name = "filter",
};
var blocked = new NftSet
{
Name = "blocked_ipv4",
Type = NftSetType.Ipv4Address,
};
blocked.Elements.Add(NftValue.Address(System.Net.IPAddress.Parse("10.0.0.1")));
blocked.Elements.Add(NftValue.Address(System.Net.IPAddress.Parse("10.0.0.2")));
table.Sets.Add(blocked);
var chain = new NftChain
{
Name = "input",
Type = NftChainType.Filter,
Hook = NftHook.Input,
Priority = 0,
Policy = NftChainPolicy.Drop,
};
chain.Rules.Add(new NftRule
{
InputInterface = NftValue.Interface("eth0"),
SourceAddressSetName = "blocked_ipv4",
TransportProtocol = NftTransportProtocol.Tcp,
DestinationPort = NftValue.Port(22),
Verdict = NftVerdict.Accept,
});
table.Chains.Add(chain);
ruleset.Tables.Add(table);
string preview = client.RenderRuleset(ruleset);
var validation = client.ValidateRuleset(ruleset);
if (validation.IsValid)
{
client.ApplyRuleset(ruleset);
}
Raw command text remains available through NftApplyRequest as a fallback for nft syntax not yet modeled by the typed API.
Supported Typed Subset
The typed API currently supports this subset directly:
- Sets with typed values for IPv4/IPv6 addresses and CIDRs, ports/services, interface names, marks, and raw literals.
- Maps declared and populated inline, authored through dictionary-style helpers on
NftMap. - Chains with typed base-chain metadata: type, hook, priority, and policy.
- Common firewall rules:
- source/destination address matches
- source/destination port matches
- input/output interface matches
- set membership matches
- verdicts: accept, drop, reject, jump
Use NftApplyRequest as the fallback when you need:
- nft features outside the current typed subset
- live map mutation commands
- custom map key/value type expressions with non-raw typed values
- snapshot parsing back into typed object models
- full nft expression parity
Map Authoring Example
var map = new NftMap
{
Name = "service_policy",
KeyType = NftMapType.InetService,
ValueType = NftMapType.Verdict,
};
map.Add(NftValue.Port(80), NftValue.Verdict(NftVerdict.Accept));
map.Add(NftValue.Port(443), NftValue.Verdict(NftVerdict.Drop));
Entries are rendered in insertion order. Duplicate keys are rejected by NftMap.Add. Use NftMap.Set to replace an existing key without changing its order.
Validate-And-Render Example
NftRenderedValidationResult preview = client.ValidateAndRenderRuleset(ruleset);
if (preview.ValidationResult.IsValid)
{
client.ApplyRuleset(ruleset);
}
Low-Level Example
using LibNftables;
using var context = new NftContext();
context.DryRun = true;
context.BufferOutput();
context.BufferError();
context.RunCommand("add table inet demo");
string? output = context.GetOutputBuffer();
string? error = context.GetErrorBuffer();
Troubleshooting
NftNativeLoadException
This usually means one of these is missing or incompatible:
- the bundled wrapper
libLibNftablesBindings.so - the host
libnftablesshared library - Linux x64 runtime compatibility
NftUnsupportedException
This is expected on:
- non-Linux hosts
- non-x64 processes
NftPermissionException
Some operations require elevated privileges or CAP_NET_ADMIN, especially when interacting with the live ruleset.
Validation failures
ValidateRuleset and ValidateAndRenderRuleset return IsValid = false for invalid nft syntax after rendering the typed model. ApplyRuleset and Restore throw when the typed/request shape is invalid or native parsing fails.
Bindings and Regeneration
- Native wrapper build only:
./eng/build-native.sh
- Regenerate SWIG bindings:
./eng/regen-bindings.sh
Low-level generated binding reference:
docs/low-level-bindings-reference.md
Generated SWIG files under src/LibNftables.Bindings/Generated/ are generated artifacts and should not be edited by hand.