libnftables1-sys 1.0.0

FFI bindings for libnftables1.
# AGENTS.md

This document provides essential information for AI agents working in the **libnftables1-sys** codebase.

## Project Overview

**libnftables1-sys** is a Rust FFI (Foreign Function Interface) crate that provides low-level bindings to the `libnftables` C library. This is a `-sys` crate, meaning it's focused on raw bindings generation rather than safe Rust abstractions.

- **Language**: Rust (2015 edition)
- **Type**: FFI/sys crate
- **License**: GPL-2.0
- **Purpose**: Expose libnftables C library functions to Rust code
- **Build System**: Cargo with custom build script using bindgen

### Key Facts
- Forked/updated from https://github.com/chifflier/libnftables-sys for libnftables1
- Part of the larger seqknock project (https://git.sr.ht/~upto/seqknock/)
- Minimal abstraction layer - mostly raw FFI bindings with one convenience struct

## Project Structure

```
libnftables1-sys/
├── Cargo.toml          # Package manifest
├── build.rs            # Build-time bindgen configuration
├── README.md           # User documentation
├── LICENSE             # GPL-2.0 license
├── .gitignore          # Git ignore rules
└── src/
    ├── lib.rs          # Main library with Nftables wrapper struct
    ├── bindings.rs     # Includes generated bindings (from OUT_DIR)
    └── wrapper.h       # C header wrapper for bindgen
```

### Generated Files
- `target/` - Build artifacts (gitignored)
- Bindings are generated at build time into `$OUT_DIR/bindings.rs` and included via `src/bindings.rs`

## Essential Commands

### Build
```bash
cargo build
```
**Note**: Requires `libnftables` development headers installed on system.

### Build (Release)
```bash
cargo build --release
```

### Test
```bash
cargo test
```
**WARNING**: Tests require **root privileges** (uid 0). The test `list_ruleset` explicitly checks `getuid() == 0`.

To run tests as root:
```bash
sudo -E cargo test
```

### Check (Fast compile check)
```bash
cargo check
```

### Generate Documentation
```bash
cargo doc --open
```

### Clean Build Artifacts
```bash
cargo clean
```

## System Dependencies

This crate requires the following system libraries to be installed:

### Required (always)
- **libnftables** - The main library we're binding to

### Required for static linking (musl targets)
When building for `musl` targets, these dependencies must also be present:
- **gmp** - GNU Multiple Precision Arithmetic Library
- **jansson** - JSON library
- **libmnl** - Minimalistic Netlink library
- **libnftnl** - Low-level netfilter netlink library
- **xtables** - iptables/nftables extension library

The build script automatically handles static vs dynamic linking based on target environment.

## Build System Details

### build.rs Overview
The `build.rs` script (src/build.rs:1-91):
- Uses `pkg-config` to locate system libraries
- Uses `bindgen` to generate Rust FFI bindings from C headers
- Supports environment variable overrides for library locations
- Handles both static (musl) and dynamic linking

### Environment Variables
You can override library locations with these environment variables:
- `NFTABLES_LIB_DIR` - Directory containing libnftables library
- `NFTABLES_INCLUDE_DIR` - Directory containing libnftables headers
- Similar variables for dependencies: `GMP_*`, `JANSSON_*`, `LIBMNL_*`, `LIBNFTNL_*`, `XTABLES_*`

### Bindgen Configuration (build.rs:66-84)
- **Allowlist functions**: Only binds functions starting with `nft_`
- **Allowlist types**: Only binds types starting with `nft_`
- **Allowlist vars**: Only binds constants starting with `NFT_` (includes NFT_CTX_OUTPUT_* flags)
- **Opaque types**: `nft_ctx_set_output`, `nft_ctx_set_error` (use FILE*)
- **Blocklist**: `__uint32_t` (redundant type)
- **Enum naming**: Uses simplified names (not prefixed)
- **Input header**: `src/wrapper.h`

### wrapper.h Trick
The `src/wrapper.h` file (src/wrapper.h:1-7) includes a clever workaround:
```c
#define _STDIO_H
#define FILE void
```
This prevents stdio.h inclusion and treats FILE as void, avoiding bindgen issues with FILE* types.

## Code Organization

### src/lib.rs (Main Library)
**Structure**:
1. **Module declaration** (line 1): Includes generated bindings module
2. **Imports** (lines 3-5): Standard FFI types and re-export all bindings
3. **Nftables struct** (lines 7-67): Safe-ish wrapper around raw `nft_ctx` pointer
4. **Tests** (lines 69-110): Two tests requiring root access

### Nftables Wrapper API
The `Nftables` struct provides a thin convenience layer:

```rust
impl Nftables {
    pub fn new() -> Nftables                           // Creates context with buffered output/error
    pub fn run_cmd(&mut self, cmd: *const c_char)     // Execute nftables command
        -> (i32, *const c_char, *const c_char)        // Returns (rc, output, error)
    pub fn set_debug(&mut self, flags: nft_debug_level)
    pub fn get_debug(&self) -> nft_debug_level
    pub fn set_output_handle(&mut self)                // Enable handle output (NFT_CTX_OUTPUT_HANDLE)
    pub fn set_numeric_time(&mut self)                 // Numeric time display (NFT_CTX_OUTPUT_NUMERIC_TIME)
    pub fn set_json(&mut self)                         // Enable JSON output (NFT_CTX_OUTPUT_JSON)
}
```

**Implementation notes**:
- Automatically calls `nft_ctx_buffer_output()` and `nft_ctx_buffer_error()` in constructor (lib.rs:20-21)
- Drop implementation properly frees context with `nft_ctx_free()` (lib.rs:63-66)
- Uses raw pointers - caller responsible for C string lifetime
- Flag manipulation via private `set_flags()` helper (lib.rs:48-51)
- All flag setters use named constants from generated bindings (NFT_CTX_OUTPUT_*)

### src/bindings.rs
Extremely minimal - just includes the generated bindings with proper lint allowances:
```rust
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
```

## Coding Conventions

### Style
- **Edition**: 2015 (older edition - be mindful of syntax differences)
- **Naming**: FFI types/functions use C conventions (snake_case, prefixed with `nft_`)
- **Lint overrides**: Non-idiomatic Rust naming allowed for generated bindings
- **Unsafe**: Liberal use appropriate for FFI crate; raw pointers expected

### Safety Patterns
- Null pointer checks before dereferencing (lib.rs:28-31)
- Assert context is non-null before operations (lib.rs:28)
- Proper Drop implementation to prevent leaks (lib.rs:62-67)
- Context pointer nulled after free (lib.rs:65)

### Clippy Exceptions
- `#[allow(clippy::not_unsafe_ptr_arg_deref)]` on `run_cmd()` - false positive, null checked before use (lib.rs:25-26)

## Testing

### Test Requirements
**CRITICAL**: Tests require root (uid 0) to interact with nftables.

### Available Tests

#### `list_ruleset` (lib.rs:91-104)
- **Requires root**: Checks `getuid() == 0` at start
- Creates Nftables context
- Runs "list ruleset" command
- Validates return code and output/error pointers

#### `set_debug` (lib.rs:106-120)
- Tests debug level get/set functionality
- Sets multiple debug flags: `NFT_DEBUG_SCANNER | NFT_DEBUG_EVALUATION | NFT_DEBUG_NETLINK`
- Validates combined flag value (0xd)
- Commented out root check - doesn't require actual nftables access

#### `set_output_flags` (lib.rs:122-145)
- Tests all flag setter methods without requiring root
- Verifies flags start at 0
- Tests `set_json()` sets `NFT_CTX_OUTPUT_JSON` flag
- Tests `set_output_handle()` sets `NFT_CTX_OUTPUT_HANDLE` flag
- Tests `set_numeric_time()` sets `NFT_CTX_OUTPUT_NUMERIC_TIME` flag
- Verifies flags are additive (bitwise OR)

#### `set_json_output` (lib.rs:147-171)
- **Requires root**: Checks `getuid() == 0` at start
- Tests JSON output functionality end-to-end
- Enables JSON flag via `set_json()`
- Runs "list ruleset" command
- Validates output is valid JSON (starts with '{')
- Converts C string to Rust string for validation

### Running Tests
```bash
# Will fail without root (list_ruleset and set_json_output require root)
cargo test

# Correct way - run all tests as root
sudo -E cargo test

# Run only non-root tests
cargo test set_debug set_output_flags
```

The `-E` flag preserves environment variables needed by Cargo.

**Test Summary:**
- **Require root**: `list_ruleset`, `set_json_output`
- **No root needed**: `set_debug`, `set_output_flags`

## Common Gotchas

### 1. Root Privileges Required
Some tests (`list_ruleset`, `set_json_output`) require root access. The error is explicit with `getuid() == 0` assertion. Other tests (`set_debug`, `set_output_flags`) work without root.

### 2. System Library Dependencies
Build will fail if `libnftables` development headers aren't installed:
```bash
# Debian/Ubuntu
apt-get install libnftables-dev

# Fedora/RHEL
dnf install nftables-devel
```

### 3. bindgen Version Pinning
Uses `bindgen = "0.70"`. Updating bindgen may change generated bindings significantly.

### 4. NFT_CTX_OUTPUT Constants
The output flag constants (NFT_CTX_OUTPUT_HANDLE, NFT_CTX_OUTPUT_JSON, etc.) are generated by bindgen via `.allowlist_var("NFT_.*")` in build.rs. These are defined in `/usr/include/nftables/libnftables.h` lines 58-75. Always use the named constants instead of hardcoded bit values.

### 5. Static Linking Complexity
MUSL targets require all transitive dependencies. If you get linking errors on musl:
- Verify all dependencies in `LIBNFTABLES_WITH_SUBDEPS` are installed
- Check `pkg-config --libs <library>` for each one

### 6. FILE* Type Handling
The `wrapper.h` hack works around bindgen's FILE* issues. Don't include stdio.h directly or modify this pattern without understanding the implications.

### 7. Rust 2015 Edition
This crate uses Rust 2015 edition. Be aware:
- `extern crate` declarations may be present (though not in lib.rs)
- Some modern syntax features unavailable
- Module system works differently

### 8. Raw Pointer Lifetimes
The `run_cmd()` method returns raw C string pointers. These are valid until:
- Next command execution (buffers may be reused)
- Context is dropped
Caller must convert to Rust strings immediately or risk use-after-free.

### 9. No Safe Abstractions
This is a `-sys` crate - intentionally low-level. Don't expect:
- String type conversions
- Safe error handling
- RAII beyond the Nftables struct itself
For safe abstractions, create a separate wrapper crate.

## Maintenance Notes

### When Updating Bindgen
1. Review generated bindings for breaking changes
2. Run tests to verify functionality
3. Check for new opaque/blocklist types needed

### When Adding Output Flags
If adding new flag setter methods (like `set_json()`):
1. Use the generated `NFT_CTX_OUTPUT_*` constants from bindings
2. Follow the pattern: call `self.set_flags(NFT_CTX_OUTPUT_*)` 
3. Never use hardcoded bit values like `1 << N`
4. Constants are generated via `.allowlist_var("NFT_.*")` in build.rs

### When Upgrading Libnftables
1. Test on target systems
2. Verify all dependencies still resolve
3. Check for new required dependencies
4. Update README if usage patterns change

## Related Resources

- **Original repo**: https://github.com/chifflier/libnftables-sys
- **Upstream project**: https://git.sr.ht/~upto/seqknock/
- **Libnftables docs**: https://netfilter.org/projects/nftables/
- **Rust FFI guide**: https://doc.rust-lang.org/nomicon/ffi.html
- **bindgen documentation**: https://rust-lang.github.io/rust-bindgen/

## Quick Reference

### Most Common Tasks

**Build after changing code**:
```bash
cargo build
```

**Regenerate bindings** (automatically happens on build):
```bash
cargo clean && cargo build
```

**Verify without system libraries** (won't work - system deps required):
Not possible - this crate fundamentally requires system libraries.

**Add new wrapper methods**:
1. Edit `src/lib.rs`
2. Add method to `impl Nftables`
3. Use raw bindings from `crate::bindings::*`
4. Follow existing patterns (null checks, unsafe blocks)
5. Add test if possible (requires root)

**Debug build issues**:
```bash
# Check pkg-config can find libnftables
pkg-config --libs --cflags libnftables

# Check bindgen separately
cargo build -vv  # Verbose output shows bindgen invocation
```