nvpn 4.1.8

CLI and daemon for Nostr VPN private mesh networks
Documentation
#[test]
fn macos_default_routes_from_netstat_finds_underlay_and_utun_routes() {
    let routes = macos_default_routes_from_netstat(
        "Routing tables\n\
Internet:\n\
Destination        Gateway            Flags               Netif Expire\n\
default            192.168.64.1       UGScg                 en0\n\
default            link#13            UCSIg               utun5\n\
default            link#26            UCSIg           bridge100      !\n",
    );

    assert_eq!(
        routes,
        vec![
            MacosRouteSpec {
                gateway: Some("192.168.64.1".to_string()),
                interface: "en0".to_string(),
            },
            MacosRouteSpec {
                gateway: None,
                interface: "utun5".to_string(),
            },
            MacosRouteSpec {
                gateway: None,
                interface: "bridge100".to_string(),
            },
        ]
    );

    assert_eq!(
        macos_underlay_default_route_from_routes(&routes),
        Some(MacosRouteSpec {
            gateway: Some("192.168.64.1".to_string()),
            interface: "en0".to_string(),
        })
    );
}

#[test]
fn macos_underlay_selection_prefers_the_snapshot_interface_when_two_are_active() {
    let candidates = vec![
        MacosRouteSpec {
            gateway: Some("192.168.178.1".to_string()),
            interface: "en0".to_string(),
        },
        MacosRouteSpec {
            gateway: Some("10.168.32.48".to_string()),
            interface: "en1".to_string(),
        },
    ];

    assert_eq!(
        macos_underlay_route_from_candidates(&candidates, Some("en1")),
        Some(candidates[1].clone()),
        "the daemon snapshot, not interface enumeration order, owns the selected underlay"
    );
    assert_eq!(
        macos_underlay_route_from_candidates(&candidates, Some("en0")),
        Some(candidates[0].clone())
    );
}

#[test]
fn macos_selected_default_route_uses_kernel_global_route_during_handoff() {
    let route_get = "\
   route to: default
destination: default
       mask: default
    gateway: 10.168.32.48
  interface: en1
      flags: <UP,GATEWAY,DONE,STATIC,PRCLONING,GLOBAL>
";

    assert_eq!(
        macos_selected_default_route_from_route_get(route_get),
        Some(MacosRouteSpec {
            gateway: Some("10.168.32.48".to_string()),
            interface: "en1".to_string(),
        }),
        "the kernel-selected global default must win even while netstat still lists stale en0 first"
    );
}

#[test]
fn macos_gateway_route_args_scope_host_routes_to_the_recorded_interface() {
    assert_eq!(
        macos_gateway_route_args("add", "65.109.48.91/32", "192.168.64.1", Some("en0"),),
        vec![
            "-n".to_string(),
            "add".to_string(),
            "-host".to_string(),
            "65.109.48.91".to_string(),
            "192.168.64.1".to_string(),
            "-ifscope".to_string(),
            "en0".to_string(),
        ]
    );
    assert_eq!(
        macos_gateway_route_args("change", "0.0.0.0/0", "192.168.64.1", None),
        vec![
            "-n".to_string(),
            "change".to_string(),
            "default".to_string(),
            "192.168.64.1".to_string(),
        ]
    );
}

#[test]
fn macos_ifconfig_has_ipv4_matches_exact_interface_address() {
    let output = "utun5: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380\n\
\tinet 10.44.10.23 --> 10.44.10.23 netmask 0xffffffff\n\
\tinet6 fe80::1%utun5 prefixlen 64 scopeid 0x8\n";

    assert!(macos_ifconfig_has_ipv4(
        output,
        Ipv4Addr::new(10, 44, 10, 23)
    ));
    assert!(!macos_ifconfig_has_ipv4(
        output,
        Ipv4Addr::new(10, 44, 10, 24)
    ));
}

#[test]
fn split_default_cleanup_only_claims_the_route_owner() {
    let routes = "\
Routing tables\n\
Internet:\n\
Destination        Gateway            Flags               Netif Expire\n\
0/1                utun6              USc                 utun6       1161\n\
1.0.0.1            192.168.64.9       UGHS                  en9\n\
128.0/1            utun7              USc                 utun7\n\
default            192.168.64.1       UGScg                 en0\n";
    assert_eq!(
        macos_split_default_route_owners_from_netstat(routes, "0.0.0.0/1"),
        vec!["utun6".to_string()]
    );
    assert_eq!(
        macos_split_default_route_owners_from_netstat(routes, "128.0.0.0/1"),
        vec!["utun7".to_string()]
    );
    assert!(macos_split_default_route_owners_from_netstat(routes, "0.0.0.0/0").is_empty());
}

#[test]
fn managed_route_ownership_requires_exact_gateway_and_interface() {
    let routes = "\
Destination        Gateway            Flags               Netif Expire\n\
65.109.48.91       192.168.64.1       UGHS                  en0       1161\n\
65.109.48.92       10.0.0.1           UGHS                  en1\n\
65.109.48.93       192.168.64.1       UGHS                  en1\n\
0/1                utun6              USc                 utun6\n";
    assert!(macos_managed_route_present(
        routes,
        "65.109.48.91/32",
        Some("192.168.64.1"),
        Some("en0"),
    ));
    assert!(!macos_managed_route_present(
        routes,
        "65.109.48.91/32",
        Some("10.0.0.1"),
        Some("en0"),
    ));
    assert!(!macos_managed_route_present(
        routes,
        "65.109.48.91/32",
        Some("192.168.64.1"),
        Some("en1"),
    ));
    assert!(!macos_managed_route_present(
        routes,
        "65.109.48.93/32",
        Some("192.168.64.1"),
        Some("en2"),
    ));
    assert!(macos_managed_route_present(
        routes,
        "0.0.0.0/1",
        None,
        Some("utun6"),
    ));
    assert!(!macos_managed_route_present(
        routes,
        "0.0.0.0/1",
        None,
        Some("utun7"),
    ));
    assert!(
        !macos_managed_route_present(routes, "65.109.48.91/32", None, None),
        "cleanup without an owner must fail closed"
    );
}

#[test]
fn direct_transition_repairs_only_a_missing_physical_default() {
    let tunnel_only = vec![MacosRouteSpec {
        gateway: None,
        interface: "utun5".to_string(),
    }];
    assert!(macos_underlay_default_route_needs_restore(&tunnel_only));

    let physical_and_tunnel = vec![
        MacosRouteSpec {
            gateway: Some("192.168.64.1".to_string()),
            interface: "en0".to_string(),
        },
        MacosRouteSpec {
            gateway: None,
            interface: "utun5".to_string(),
        },
    ];
    assert!(!macos_underlay_default_route_needs_restore(
        &physical_and_tunnel
    ));
}

#[test]
fn underlay_restore_adds_without_changing_a_foreign_default() {
    assert_eq!(
        macos_add_underlay_default_route_args("192.168.64.1"),
        vec!["-n", "add", "default", "192.168.64.1"]
    );
}