#serde-json #nftables #json #serde #json-object #linux

nftables-json

Serde JSON model for interacting with the nftables nft executable

3 releases (breaking)

0.3.0 Dec 26, 2022
0.2.0 Dec 25, 2022
0.1.0 Dec 12, 2022

#772 in Unix APIs

Download history 23/week @ 2023-12-11 26/week @ 2023-12-18 8/week @ 2023-12-25 39/week @ 2024-01-01 10/week @ 2024-01-22 43/week @ 2024-01-29 19/week @ 2024-02-05 33/week @ 2024-02-12 44/week @ 2024-02-19 31/week @ 2024-02-26 1/week @ 2024-03-04 31/week @ 2024-03-11 22/week @ 2024-03-18

86 downloads per month

MIT/Apache

65KB
1.5K SLoC

nftables-json

Serde JSON model for interacting with the nftables nft executable

Provides Rust types that map directly to the nftables JSON object model, allowing serialization and deserialization of input and output from the nft --json command using Serde and serde_json.

Contributing

This library is made available under the terms of either the Apache License, Version 2.0 or the MIT License, at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this library by you shall be dual licensed as above, without any additional terms or conditions.

Copyright (c) nftables-json Developers

SPDX-License-Identifier: MIT OR Apache-2.0

Note that the tests for this library are made available under a different license. See the README in the tests/ directory for more information.


lib.rs:

Serde JSON model for interacting with the nftables nft executable

Provides Rust types that map directly to the nftables JSON object model, allowing serialization and deserialization of input and output from the nft --json command using Serde and serde_json.

Example

Creating commands that can be piped into nft --json --file -

use nftables_json::{command::*, expression::*, statement::*};

let mut commands = Commands::default();
commands.extend([
    // flush rulesets for all families
    Command::Flush(Flush::Ruleset(None)),
    // create a new table called "default"
    Command::Add(Add::Table(AddTable {
        family: "inet".into(),
        name: "default".into(),
        ..AddTable::default()
    })),
    // attach a chain to the "default" table called "input"
    Command::Add(Add::Chain(AddChain {
        family: "inet".into(),
        table: "default".into(),
        name: "input".into(),
        r#type: Some("filter".into()),
        hook: Some("input".into()),
        prio: Some(0),
        policy: Some("accept".into()),
        ..AddChain::default()
    })),
    Command::Add(Add::Rule(AddRule {
        // attach a rule to the "input" chain in the "default" table that drops udp sport 53
        family: "inet".into(),
        table: "default".into(),
        chain: "input".into(),
        expr: Some(vec![
            Statement::Match(Match {
                left: Expression::Payload {
                    payload: Payload::Named { protocol: "udp".into(), field: "sport".into() },
                }
                .into(),
                right: Expression::Immediate(Immediate::Number(53)).into(),
                op: "==".into(),
            }),
            Statement::Drop(()),
        ]),
        ..AddRule::default()
    })),
]);

// not shown: how to invoke `nft` from Rust and pipe json to stdin
println!("{}", commands.to_string().unwrap());

/*
{"nftables":[
  {"flush":{"ruleset":null}},
  {"add":{"table":{"family":"inet","name":"default"}}},
  {"add":{"chain":{"family":"inet","table":"default","name":"input","policy":"accept","type":"filter","hook":"input","prio":0}}},
  {"add":{"rule":{"family":"inet","table":"default","chain":"input","expr":[
    {"match":{"left":{"payload":{"protocol":"udp","field":"sport"}},"right":53,"op":"=="}},
    {"drop":null}
  ]}}}
]}
*/

Example

Parsing a ruleset produced by printf '{"nftables":[{"list":{"ruleset":null}}]}' | nft --json --file -

use nftables_json::{object::*, expression::*, statement::*};

// not shown: how to invoke `nft` from Rust and collect stdout
let output_str = r#"
{"nftables":[
  {"metainfo":{"version":"0.9.8","release_name":"E.D.S.","json_schema_version":1}},
  {"table":{"family":"inet","name":"default","handle":3}},
  {"chain":{"family":"inet","table":"default","name":"input","handle":1,"type":"filter","hook":"input","prio":0,"policy":"accept"}},
  {"rule":{"family":"inet","table":"default","chain":"input","handle":3,"expr":[
    {"match":{"op":"==","left":{"payload":{"protocol":"udp","field":"sport"}},"right":53}},
    {"drop":null}
  ]}},
  {"chain":{"family":"inet","table":"default","name":"output","handle":2,"type":"filter","hook":"output","prio":0,"policy":"accept"}}
]}
"#;

let objects = Objects::from_str(output_str).unwrap();

for object in objects.iter() {
    match object {
        Object::Metainfo(Metainfo { json_schema_version, .. }) => {
            eprintln!("[metainfo] schema version {json_schema_version}");
        }
        Object::Table(Table { family, name, .. }) => {
            eprintln!("[table] {family} {name}");
        }
        Object::Chain(Chain { family, table, name, hook: Some(hook), .. }) => {
            eprintln!("[chain] {family} {table} {name} hook {hook}");
        }
        Object::Rule(Rule { family, table, chain, .. }) => {
            eprintln!("[rule] {family} {table} {chain}");
        }
        _ => {}
    }
}

/*
schema version 1
table family inet name default
chain family inet table default name input hook input
rule family inet table default chain input
chain family inet table default name output hook output
*/

Dependencies

~1.2–2MB
~43K SLoC