261 lines
7.2 KiB
Markdown
261 lines
7.2 KiB
Markdown
# 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.
|