pfctl 0.7.0

Library for interfacing with the Packet Filter (PF) firewall on macOS
Documentation
#[macro_use]
#[allow(dead_code)]
mod helper;

use crate::helper::pfcli;
use assert_matches::assert_matches;
use std::net::{Ipv4Addr, Ipv6Addr};

static ANCHOR_NAME: &str = "pfctl-rs.integration.testing.nat-rules";

fn nat_rule(dest: pfctl::Ip, nat_to: pfctl::Ip) -> pfctl::NatRule {
    pfctl::NatRuleBuilder::default()
        .action(pfctl::NatRuleAction::Nat {
            nat_to: nat_to.into(),
        })
        .to(pfctl::Endpoint::new(dest, 1234))
        .build()
        .unwrap()
}

fn nat_rule_ipv4() -> pfctl::NatRule {
    nat_rule(
        pfctl::Ip::from(Ipv4Addr::new(127, 0, 0, 1)),
        pfctl::Ip::from(Ipv4Addr::new(127, 0, 0, 2)),
    )
}

fn nat_rule_ipv6() -> pfctl::NatRule {
    nat_rule(
        pfctl::Ip::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
        pfctl::Ip::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2)),
    )
}

fn nonat_rule(dest: pfctl::Ip) -> pfctl::NatRule {
    pfctl::NatRuleBuilder::default()
        .action(pfctl::NatRuleAction::NoNat)
        .to(pfctl::Endpoint::new(dest, 1234))
        .build()
        .unwrap()
}

fn nonat_rule_ipv4() -> pfctl::NatRule {
    nonat_rule(pfctl::Ip::from(Ipv4Addr::new(127, 0, 0, 1)))
}

fn nonat_rule_ipv6() -> pfctl::NatRule {
    nonat_rule(pfctl::Ip::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))
}

fn before_each() {
    pfctl::PfCtl::new()
        .unwrap()
        .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Nat)
        .unwrap();
}

fn after_each() {
    pfcli::flush_rules(ANCHOR_NAME, pfcli::FlushOptions::Nat);
    pfctl::PfCtl::new()
        .unwrap()
        .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Nat)
        .unwrap();
}

test!(add_nat_rule_ipv4 {
    let mut pf = pfctl::PfCtl::new().unwrap();
    let rule = nat_rule_ipv4();
    assert_matches!(pf.add_nat_rule(ANCHOR_NAME, &rule), Ok(()));
    assert_eq!(
        pfcli::get_nat_rules(ANCHOR_NAME),
        &["nat inet from any to 127.0.0.1 port = 1234 -> 127.0.0.2"]
    );
});

test!(add_nat_rule_ipv6 {
    let mut pf = pfctl::PfCtl::new().unwrap();
    let rule = nat_rule_ipv6();
    assert_matches!(pf.add_nat_rule(ANCHOR_NAME, &rule), Ok(()));
    assert_eq!(
        pfcli::get_nat_rules(ANCHOR_NAME),
        &["nat inet6 from any to ::1 port = 1234 -> ::2"]
    );
});

test!(add_nonat_rule_ipv4 {
    let mut pf = pfctl::PfCtl::new().unwrap();
    let rule = nonat_rule_ipv4();
    assert_matches!(pf.add_nat_rule(ANCHOR_NAME, &rule), Ok(()));
    assert_eq!(
        pfcli::get_nat_rules(ANCHOR_NAME),
        &["no nat inet from any to 127.0.0.1 port = 1234"]
    );
});

test!(add_nonat_rule_ipv6 {
    let mut pf = pfctl::PfCtl::new().unwrap();
    let rule = nonat_rule_ipv6();
    assert_matches!(pf.add_nat_rule(ANCHOR_NAME, &rule), Ok(()));
    assert_eq!(
        pfcli::get_nat_rules(ANCHOR_NAME),
        &["no nat inet6 from any to ::1 port = 1234"]
    );
});