# 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` / `NftRule` authoring - `RenderRuleset` - `ValidateAndRenderRuleset` - `ValidateRuleset` - `ApplyRuleset` - `Snapshot` - `Restore` - Low-level managed wrapper: - `NftContext` for 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 - `libnftables` headers and shared library installed - `gcc` - .NET SDK 10+ - `swig` only 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 ```bash dotnet build ``` `dotnet build` compiles the native SWIG wrapper (`libLibNftablesBindings.so`) from the checked-in generated C wrapper code. ## Test ```bash 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 `libnftables` is unavailable - Privileged tests that only run when `LIBNFTABLES_RUN_PRIVILEGED_TESTS=1`, `uid == 0`, and `CAP_NET_ADMIN` is 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-13` label - `bash` - `curl` - `gcc` - `pkg-config` - system-installed `libnftables` development/runtime packages discoverable by `pkg-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_ADMIN` emit 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 ```csharp 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 ```csharp 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 ```csharp NftRenderedValidationResult preview = client.ValidateAndRenderRuleset(ruleset); if (preview.ValidationResult.IsValid) { client.ApplyRuleset(ruleset); } ``` ## Low-Level Example ```csharp 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 `libnftables` shared 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: ```bash ./eng/build-native.sh ``` - Regenerate SWIG bindings: ```bash ./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.