Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file.

### Breaking

- CLI upgrade required: the `InterfaceV2` onchain account format now includes `flex_algo_node_segments` (RFC-18). CLI versions prior to this release cannot deserialize device accounts written by the new program. Operators must upgrade the CLI before or alongside the program upgrade.

### Changes

- Activator
Expand All @@ -30,6 +32,10 @@ All notable changes to this project will be documented in this file.
- Extend `validate_program_account!` migration to remaining user and multicastgroup allowlist processors (`set_bgp_status`, `delete`, `closeaccount`, publisher/subscriber `add`/`remove`)
- Add `OutboundIcmp` target type (`= 2`) to the geolocation onchain program, enabling ICMP-based probing as an alternative to TWAMP for outbound geolocation targets
- Allow pending users with subs to be deleted
- Add `TopologyInfo` onchain account for IS-IS flex-algo link classification: auto-assigned TE admin-group bit (1–62), derived flex-algo number (128 + bit), and constraint type (`include-any`/`include-all`); capped at 62 topologies via `AdminGroupBits` resource extension
- Add `link_topologies: Vec<Pubkey>` (capped at 8) and `link_flags: u8` (bit 0 = unicast-drained) to the `Link` account
- Add `include_topologies` to the `Tenant` account for topology-filtered routing opt-in
- Enforce UNICAST-DEFAULT topology existence as a precondition for link activation
- Telemetry
- Device telemetry agent now posts `agent_version` and `agent_commit` in the `DeviceLatencySamplesHeader` when initializing new sample accounts, enabling version attribution of onchain telemetry data
- Add optional TLS support to state-ingest server via `--tls-cert-file` and `--tls-key-file` flags; when set, the server listens on both HTTP (`:8080`) and HTTPS (`:8443`) simultaneously
Expand Down
10 changes: 10 additions & 0 deletions activator/src/process/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ mod tests {
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status:
doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

let tunnel_cloned = tunnel.clone();
Expand Down Expand Up @@ -397,6 +399,8 @@ mod tests {
side_z_iface_name: "Ethernet1".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

let link_cloned = link.clone();
Expand Down Expand Up @@ -457,6 +461,8 @@ mod tests {
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status:
doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

let tunnel_clone = tunnel.clone();
Expand Down Expand Up @@ -544,6 +550,8 @@ mod tests {
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status:
doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

// SDK command fetches the link internally
Expand Down Expand Up @@ -623,6 +631,8 @@ mod tests {
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status:
doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

// SDK command fetches the link internally
Expand Down
4 changes: 4 additions & 0 deletions activator/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ mod tests {
side_z_iface_name: "Ethernet1".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

let mut existing_links: HashMap<Pubkey, Link> = HashMap::new();
Expand Down Expand Up @@ -804,6 +806,8 @@ mod tests {
side_z_iface_name: "Ethernet3".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::Pending,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

let new_link_cloned = new_link.clone();
Expand Down
2 changes: 2 additions & 0 deletions client/doublezero/src/command/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,7 @@ mod tests {
metro_routing: false,
route_liveness: false,
billing: TenantBillingConfig::default(),
include_topologies: vec![],
};

let mut tenants = HashMap::new();
Expand Down Expand Up @@ -1397,6 +1398,7 @@ mod tests {
metro_routing: false,
route_liveness: false,
billing: TenantBillingConfig::default(),
include_topologies: vec![],
};
tenants.insert(pk, tenant.clone());
(pk, tenant)
Expand Down
1 change: 1 addition & 0 deletions client/doublezero/src/dzd_latency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ mod tests {
ip_net: NetworkV4::new(ip, 32).unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
})
})
.collect();
Expand Down
2 changes: 1 addition & 1 deletion controlplane/controller/internal/controller/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type Interface struct {

// toInterface validates onchain data for a serviceability interface and converts it to a controller interface.
func toInterface(iface serviceability.Interface) (Interface, error) {
if iface == (serviceability.Interface{}) {
if iface.Name == "" {
return Interface{}, errors.New("serviceability interface cannot be nil")
}

Expand Down
72 changes: 40 additions & 32 deletions e2e/compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,34 +121,40 @@ var knownIncompatibilities = map[string]knownIncompat{
"write/device_drain": {ranges: before("0.8.1")},
"write/device_drain_2": {ranges: before("0.8.1")},

// device interface / link commands: --mtu requirement changed from 2048 to 9000.
// Versions before 0.12.0 didn't have these commands; versions 0.12.0–0.15.x send
// the old MTU value which the current program rejects.
"write/device_interface_create": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_create_2": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_create_3": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_create_4": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_set_unlinked": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_set_unlinked_2": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_set_unlinked_3": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_set_unlinked_4": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_create_wan": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_create_dzx": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_accept_dzx": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_update": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_set_health": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_set_health_dzx": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_get": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_wait_activated": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_wait_activated_dzx": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_drain": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_drain_dzx": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_delete": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/link_delete_dzx": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_delete": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_delete_2": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_delete_3": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_delete_4": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
// device interface create: versions 0.12.0–0.15.x sent MTU 2048; the current program
// requires 9000. Versions ≤0.11.0 and 0.16.0 can still create interfaces successfully
// (the create instruction does not require reading back the account).
"write/device_interface_create": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_create_2": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_create_3": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},
"write/device_interface_create_4": {ranges: []versionRange{{from: "0.12.0", before: "0.16.0"}}},

// RFC-18 mandatory upgrade boundary: all operations that read device accounts
// (set_unlinked, link create/update/delete) require a CLI that understands the new
// InterfaceV2 format (flex_algo_node_segments). All released versions prior to this
// release are incompatible. set_unlinked uses cascadeKnownFail so that downstream
// link phases are skipped rather than run-and-fail when this is a known incompatibility.
"write/device_interface_set_unlinked": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_set_unlinked_2": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_set_unlinked_3": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_set_unlinked_4": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_create_wan": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_create_dzx": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_accept_dzx": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_update": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_set_health": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_set_health_dzx": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_get": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_wait_activated": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_wait_activated_dzx": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_drain": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_drain_dzx": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_delete": {ranges: []versionRange{{before: "0.17.0"}}},
"write/link_delete_dzx": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_delete": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_delete_2": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_delete_3": {ranges: []versionRange{{before: "0.17.0"}}},
"write/device_interface_delete_4": {ranges: []versionRange{{before: "0.17.0"}}},
}

// =============================================================================
Expand Down Expand Up @@ -1170,14 +1176,16 @@ func runWriteWorkflows(
}},

// Transition all 4 interfaces to "unlinked" (required before link creation).
// cascadeKnownFail: when these are known-incompatible, downstream phases are
// skipped rather than allowed to run and fail independently.
{name: "activate_interfaces", parallel: true, steps: []writeStep{
{name: "device_interface_set_unlinked", cmd: cli + " device interface update " + deviceCode + " " + ifaceName +
{name: "device_interface_set_unlinked", cascadeKnownFail: true, cmd: cli + " device interface update " + deviceCode + " " + ifaceName +
" --status unlinked"},
{name: "device_interface_set_unlinked_2", cmd: cli + " device interface update " + deviceCode2 + " " + ifaceName +
{name: "device_interface_set_unlinked_2", cascadeKnownFail: true, cmd: cli + " device interface update " + deviceCode2 + " " + ifaceName +
" --status unlinked"},
{name: "device_interface_set_unlinked_3", cmd: cli + " device interface update " + deviceCode + " " + ifaceName2 +
{name: "device_interface_set_unlinked_3", cascadeKnownFail: true, cmd: cli + " device interface update " + deviceCode + " " + ifaceName2 +
" --status unlinked"},
{name: "device_interface_set_unlinked_4", cmd: cli + " device interface update " + deviceCode2 + " " + ifaceName2 +
{name: "device_interface_set_unlinked_4", cascadeKnownFail: true, cmd: cli + " device interface update " + deviceCode2 + " " + ifaceName2 +
" --status unlinked"},
}},

Expand Down
1 change: 1 addition & 0 deletions smartcontract/cli/src/accesspass/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ mod tests {
metro_routing: false,
route_liveness: false,
billing: TenantBillingConfig::default(),
include_topologies: vec![],
};

let mgroup_pubkey = Pubkey::new_unique();
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/device/interface/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ mod tests {
ip_net: "185.189.47.80/32".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: false,
flex_algo_node_segments: vec![],
}
.to_interface()],
max_users: 255,
Expand Down Expand Up @@ -321,6 +322,7 @@ mod tests {
ip_net: "10.0.0.1/24".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
}
.to_interface()],
max_users: 255,
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/device/interface/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ mod tests {
ip_net: "10.0.0.1/24".parse().unwrap(),
node_segment_idx: 12,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
}
.to_interface(),
CurrentInterfaceVersion {
Expand All @@ -126,6 +127,7 @@ mod tests {
ip_net: "10.0.1.1/24".parse().unwrap(),
node_segment_idx: 13,
user_tunnel_endpoint: false,
flex_algo_node_segments: vec![],
}
.to_interface(),
],
Expand Down
1 change: 1 addition & 0 deletions smartcontract/cli/src/device/interface/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ mod tests {
ip_net: "10.0.0.1/24".parse().unwrap(),
node_segment_idx: 42,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
}
.to_interface()],
max_users: 255,
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/device/interface/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ mod tests {
ip_net: "10.0.0.1/24".parse().unwrap(),
node_segment_idx: 12,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
}
.to_interface(),
CurrentInterfaceVersion {
Expand All @@ -182,6 +183,7 @@ mod tests {
ip_net: "10.0.1.1/24".parse().unwrap(),
node_segment_idx: 13,
user_tunnel_endpoint: false,
flex_algo_node_segments: vec![],
}
.to_interface(),
],
Expand Down
5 changes: 5 additions & 0 deletions smartcontract/cli/src/device/interface/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ mod tests {
ip_net: "10.0.0.1/24".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
}
.to_interface(),
CurrentInterfaceVersion {
Expand All @@ -259,6 +260,7 @@ mod tests {
ip_net: "10.0.1.1/24".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: false,
flex_algo_node_segments: vec![],
}
.to_interface(),
],
Expand Down Expand Up @@ -376,6 +378,7 @@ mod tests {
ip_net: "10.0.0.1/32".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: false,
flex_algo_node_segments: vec![],
}
.to_interface()],
max_users: 255,
Expand Down Expand Up @@ -424,6 +427,7 @@ mod tests {
ip_net: "185.189.47.80/32".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: false,
flex_algo_node_segments: vec![],
}
.to_interface()],
max_users: 255,
Expand Down Expand Up @@ -520,6 +524,7 @@ mod tests {
ip_net: "10.0.0.1/24".parse().unwrap(),
node_segment_idx: 0,
user_tunnel_endpoint: true,
flex_algo_node_segments: vec![],
}
.to_interface()],
max_users: 255,
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/link/accept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ mod tests {
side_z_iface_name: "Ethernet1/2".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::ReadyForService,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

client
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/link/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ mod tests {
side_z_iface_name: "eth1".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::ReadyForService,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

client
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/link/dzx_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ mod tests {
side_z_iface_name: "Ethernet1/2".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::ReadyForService,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

client
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/link/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ mod tests {
side_z_iface_name: "eth1".to_string(),
link_health: doublezero_serviceability::state::link::LinkHealth::ReadyForService,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
};

let contributor = Contributor {
Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/link/latency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ mod tests {
delay_override_ns: 0,
link_health: doublezero_serviceability::state::link::LinkHealth::ReadyForService,
desired_status: doublezero_serviceability::state::link::LinkDesiredStatus::Activated,
link_topologies: vec![],
link_flags: 0,
}
}

Expand Down
Loading
Loading