diff --git a/CHANGELOG.md b/CHANGELOG.md index 6207b5ca6d..49dee76982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ All notable changes to this project will be documented in this file. - SDK - Go serviceability SDK adds `TopologyInfo` account type with `TopologyConstraint`, `IndexType` / `TopologyType` account-type constants, and `GetProgramData` dispatch case; extends `Link` with `LinkTopologies` and `LinkFlags`; extends `Tenant` with `IncludeTopologies` - CLI + - Add `tunnel_endpoint` field to `doublezero user list` output (table and JSON) showing the device-side GRE endpoint IP assigned to each user - Add `cyoa_ips` field to `doublezero device get` and `doublezero device list` output, showing the IP networks of interfaces with `user_tunnel_endpoint` enabled - Add `--tunnel_endpoint` flag to `user update` command so operators can set the tunnel endpoint IP of an existing user - Extend `doublezero resource verify` to check `MulticastPublisherBlock` against multicast publisher users' `dz_ip` allocations; legacy `dz_ip`s that fall outside the block's range are ignored so pre-existing users allocated before this extension existed do not produce false discrepancies diff --git a/e2e/fixtures/ibrl/doublezero_user_list_user_added.tmpl b/e2e/fixtures/ibrl/doublezero_user_list_user_added.tmpl index 54fbb2e680..69cf757f07 100644 --- a/e2e/fixtures/ibrl/doublezero_user_list_user_added.tmpl +++ b/e2e/fixtures/ibrl/doublezero_user_list_user_added.tmpl @@ -1,2 +1,2 @@ - account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | status | bgp_status | owner - IGNORED | | IBRL | | ny5-dz01 | New York | GREOverDIA | {{.ClientIP}} | {{.ClientIP}} | Prepaid: (MAX) | 500 | 169.254.0.0/31 | activated | unknown | {{.ClientPubkeyAddress}} + account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | tunnel_endpoint | status | bgp_status | owner + IGNORED | | IBRL | | ny5-dz01 | New York | GREOverDIA | {{.ClientIP}} | {{.ClientIP}} | Prepaid: (MAX) | 500 | 169.254.0.0/31 | {{.DeviceIP}} | activated | unknown | {{.ClientPubkeyAddress}} diff --git a/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_added.tmpl b/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_added.tmpl index 0a45b48299..1e279ba5fb 100644 --- a/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_added.tmpl +++ b/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_added.tmpl @@ -1,5 +1,5 @@ - account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | status | bgp_status | owner + account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | tunnel_endpoint | status | bgp_status | owner {{- range .IBRLUsers}} - IGNORED | | IBRL | | la2-dz01 | Los Angeles | GREOverDIA | {{.ClientIP}} | {{.ClientIP}} | Prepaid: (MAX) | {{.TunnelID}} | {{.TunnelNet}} | activated | unknown | {{$.ClientPubkeyAddress}} + IGNORED | | IBRL | | la2-dz01 | Los Angeles | GREOverDIA | {{.ClientIP}} | {{.ClientIP}} | Prepaid: (MAX) | {{.TunnelID}} | {{.TunnelNet}} | {{.TunnelEndpoint}} | activated | unknown | {{$.ClientPubkeyAddress}} {{- end}} - IGNORED | | IBRLWithAllocatedIP | | ny5-dz01 | New York | GREOverDIA | {{.ClientIP}} | {{.ExpectedAllocatedClientIP}} | Prepaid: (MAX) | {{.AllocatedUserTunnelID}} | {{.AllocatedUserTunnelNet}} | activated | unknown | {{.ClientPubkeyAddress}} + IGNORED | | IBRLWithAllocatedIP | | ny5-dz01 | New York | GREOverDIA | {{.ClientIP}} | {{.ExpectedAllocatedClientIP}} | Prepaid: (MAX) | {{.AllocatedUserTunnelID}} | {{.AllocatedUserTunnelNet}} | {{.DeviceIP}} | activated | unknown | {{.ClientPubkeyAddress}} diff --git a/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_removed.tmpl b/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_removed.tmpl index 6b33f45470..9d86e63600 100644 --- a/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_removed.tmpl +++ b/e2e/fixtures/ibrl_with_allocated_addr/doublezero_user_list_user_removed.tmpl @@ -1,4 +1,4 @@ - account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | status | bgp_status | owner + account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | tunnel_endpoint | status | bgp_status | owner {{- range .IBRLUsers}} - IGNORED | | IBRL | | la2-dz01 | Los Angeles | GREOverDIA | {{.ClientIP}} | {{if eq .ClientIP "3.4.5.6"}}0.0.0.0{{else}}{{.ClientIP}}{{end}} | Prepaid: (MAX) | {{.TunnelID}} | {{.TunnelNet}} | {{if eq .ClientIP "3.4.5.6"}}banned{{else}}activated{{end}} | unknown | {{$.ClientPubkeyAddress}} + IGNORED | | IBRL | | la2-dz01 | Los Angeles | GREOverDIA | {{.ClientIP}} | {{if eq .ClientIP "3.4.5.6"}}0.0.0.0{{else}}{{.ClientIP}}{{end}} | Prepaid: (MAX) | {{.TunnelID}} | {{.TunnelNet}} | {{.TunnelEndpoint}} | {{if eq .ClientIP "3.4.5.6"}}banned{{else}}activated{{end}} | unknown | {{$.ClientPubkeyAddress}} {{- end}} diff --git a/e2e/ibrl_test.go b/e2e/ibrl_test.go index 28d718b975..b41c83e86b 100644 --- a/e2e/ibrl_test.go +++ b/e2e/ibrl_test.go @@ -178,6 +178,7 @@ func checkIBRLPostConnect(t *testing.T, dn *TestDevnet, device *devnet.Device, c data: map[string]any{ "ClientIP": client.CYOANetworkIP, "ClientPubkeyAddress": client.Pubkey, + "DeviceIP": device.CYOANetworkIP, }, cmd: []string{"doublezero", "user", "list"}, }, diff --git a/e2e/ibrl_with_allocated_ip_test.go b/e2e/ibrl_with_allocated_ip_test.go index 3c5bfbcfa8..e5f27061c1 100644 --- a/e2e/ibrl_with_allocated_ip_test.go +++ b/e2e/ibrl_with_allocated_ip_test.go @@ -20,9 +20,10 @@ import ( // ibrlUserTunnelInfo holds tunnel assignment info for an IBRL user, parsed from user list output. type ibrlUserTunnelInfo struct { - ClientIP string - TunnelID string - TunnelNet string + ClientIP string + TunnelID string + TunnelNet string + TunnelEndpoint string } // parseIBRLTunnelInfo parses user list output and returns tunnel info for IBRL and IBRLWithAllocatedIP users. @@ -32,9 +33,10 @@ func parseIBRLTunnelInfo(output []byte) (ibrlUsers []ibrlUserTunnelInfo, allocUs switch row["user_type"] { case "IBRL": ibrlUsers = append(ibrlUsers, ibrlUserTunnelInfo{ - ClientIP: row["client_ip"], - TunnelID: row["tunnel_id"], - TunnelNet: row["tunnel_net"], + ClientIP: row["client_ip"], + TunnelID: row["tunnel_id"], + TunnelNet: row["tunnel_net"], + TunnelEndpoint: row["tunnel_endpoint"], }) case "IBRLWithAllocatedIP": allocUserTunnelID = row["tunnel_id"] diff --git a/smartcontract/cli/src/user/list.rs b/smartcontract/cli/src/user/list.rs index 97bd568740..202d5fefb3 100644 --- a/smartcontract/cli/src/user/list.rs +++ b/smartcontract/cli/src/user/list.rs @@ -112,6 +112,7 @@ pub struct UserDisplay { pub accesspass: String, pub tunnel_id: u16, pub tunnel_net: NetworkV4, + pub tunnel_endpoint: Ipv4Addr, pub status: UserStatus, pub bgp_status: BGPStatus, #[serde(serialize_with = "serializer::serialize_pubkey_as_string")] @@ -362,6 +363,7 @@ impl ListUserCliCommand { }, tunnel_id: user.tunnel_id, tunnel_net: user.tunnel_net, + tunnel_endpoint: user.tunnel_endpoint, status: user.status, bgp_status: user.bgp_status, owner: user.owner, @@ -782,7 +784,7 @@ mod tests { publishers: vec![], subscribers: vec![], validator_pubkey: Pubkey::default(), - tunnel_endpoint: std::net::Ipv4Addr::UNSPECIFIED, + tunnel_endpoint: [1, 2, 3, 4].into(), tunnel_flags: 0, bgp_status: Default::default(), last_bgp_up_at: 0, @@ -824,7 +826,7 @@ mod tests { publishers: vec![], subscribers: vec![mgroup1_pubkey], validator_pubkey: Pubkey::default(), - tunnel_endpoint: std::net::Ipv4Addr::UNSPECIFIED, + tunnel_endpoint: [5, 6, 7, 8].into(), tunnel_flags: 0, bgp_status: Default::default(), last_bgp_up_at: 0, @@ -894,7 +896,7 @@ mod tests { .execute(&client, &mut output); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); - assert_eq!(output_str, " account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | status | bgp_status | owner \n 11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 | Multicast | S:m_code | device1_code | location1_name | GREOverDIA | 1.2.3.4 | 2.3.4.5 | Prepaid: (expires epoch 10) | 500 | 1.2.3.5/32 | activated | unknown | 11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo \n"); + assert_eq!(output_str, " account | tenant | user_type | groups | device | location | cyoa_type | client_ip | dz_ip | accesspass | tunnel_id | tunnel_net | tunnel_endpoint | status | bgp_status | owner \n 11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 | Multicast | S:m_code | device1_code | location1_name | GREOverDIA | 1.2.3.4 | 2.3.4.5 | Prepaid: (expires epoch 10) | 500 | 1.2.3.5/32 | 5.6.7.8 | activated | unknown | 11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo \n"); let mut output = Vec::new(); let res = ListUserCliCommand { @@ -922,7 +924,7 @@ mod tests { assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); - assert_eq!(output_str, "[{\"account\":\"11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo\",\"tenant\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\",\"user_type\":\"Multicast\",\"device_pk\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\",\"multicast\":\"S:m_code\",\"publishers\":\"\",\"subscribers\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo8\",\"device_name\":\"device1_code\",\"location_code\":\"location1_code\",\"location_name\":\"location1_name\",\"cyoa_type\":\"GREOverDIA\",\"client_ip\":\"1.2.3.4\",\"dz_ip\":\"2.3.4.5\",\"accesspass\":\"Prepaid: (expires epoch 10)\",\"tunnel_id\":500,\"tunnel_net\":\"1.2.3.5/32\",\"status\":\"Activated\",\"bgp_status\":\"Unknown\",\"owner\":\"11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo\"}]\n"); + assert_eq!(output_str, "[{\"account\":\"11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo\",\"tenant\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\",\"user_type\":\"Multicast\",\"device_pk\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\",\"multicast\":\"S:m_code\",\"publishers\":\"\",\"subscribers\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo8\",\"device_name\":\"device1_code\",\"location_code\":\"location1_code\",\"location_name\":\"location1_name\",\"cyoa_type\":\"GREOverDIA\",\"client_ip\":\"1.2.3.4\",\"dz_ip\":\"2.3.4.5\",\"accesspass\":\"Prepaid: (expires epoch 10)\",\"tunnel_id\":500,\"tunnel_net\":\"1.2.3.5/32\",\"tunnel_endpoint\":\"5.6.7.8\",\"status\":\"Activated\",\"bgp_status\":\"Unknown\",\"owner\":\"11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo\"}]\n"); let mut output = Vec::new(); let res = ListUserCliCommand {