diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 542c06fb80..791bf11303 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -405,8 +405,8 @@ }, "@@aspect_rules_js+//npm:extensions.bzl%pnpm": { "general": { - "bzlTransitiveDigest": "cYE3QE61mjLIM5IdiE+TCpLJPIIsMr0Plg/2aOdcHOA=", - "usagesDigest": "8ggfSW1eOJDzHp5oo9r33sAB0sGvuQg4MjIOnRBgj2E=", + "bzlTransitiveDigest": "oYNqMRBQZT4eUO64w7LUy20WAgJZy2VJVMHy+qXr6Og=", + "usagesDigest": "EFKKU7eUgTryCbo1pjmIBAfRNXSqYCgsTqt5M8RIwhE=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -415,11 +415,11 @@ "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", "attributes": { "package": "pnpm", - "version": "8.6.7", + "version": "8.15.9", "root_package": "", "link_workspace": "", "link_packages": {}, - "integrity": "sha512-vRIWpD/L4phf9Bk2o/O2TDR8fFoJnpYrp2TKqTIZF/qZ2/rgL3qKXzHofHgbXsinwMoSEigz28sqk3pQ+yMEQQ==", + "integrity": "sha512-SZQ0ydj90aJ5Tr9FUrOyXApjOrzuW7Fee13pDzL0e1E6ypjNXP0AHDHw20VLw4BO3M1XhQHkyik6aBYWa72fgQ==", "url": "", "commit": "", "patch_args": [ @@ -435,14 +435,14 @@ "extra_build_content": "load(\"@aspect_rules_js//js:defs.bzl\", \"js_binary\")\njs_binary(name = \"pnpm\", data = glob([\"package/**\"]), entry_point = \"package/dist/pnpm.cjs\", visibility = [\"//visibility:public\"])", "generate_bzl_library_targets": false, "extract_full_archive": true, - "system_tar": "auto" + "exclude_package_contents": [] } }, "pnpm__links": { "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", "attributes": { "package": "pnpm", - "version": "8.6.7", + "version": "8.15.9", "dev": false, "root_package": "", "link_packages": {}, @@ -464,6 +464,11 @@ } }, "recordedRepoMappingEntries": [ + [ + "aspect_bazel_lib+", + "bazel_lib", + "bazel_lib+" + ], [ "aspect_bazel_lib+", "bazel_skylib", @@ -474,16 +479,36 @@ "bazel_tools", "bazel_tools" ], + [ + "aspect_bazel_lib+", + "tar.bzl", + "tar.bzl+" + ], [ "aspect_rules_js+", "aspect_bazel_lib", "aspect_bazel_lib+" ], + [ + "aspect_rules_js+", + "aspect_rules_js", + "aspect_rules_js+" + ], + [ + "aspect_rules_js+", + "aspect_tools_telemetry_report", + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" + ], [ "aspect_rules_js+", "bazel_features", "bazel_features+" ], + [ + "aspect_rules_js+", + "bazel_lib", + "bazel_lib+" + ], [ "aspect_rules_js+", "bazel_skylib", @@ -503,6 +528,31 @@ "bazel_features+", "bazel_features_version", "bazel_features++version_extension+bazel_features_version" + ], + [ + "bazel_lib+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "tar.bzl+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "tar.bzl+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "tar.bzl+", + "tar.bzl", + "tar.bzl+" ] ] } @@ -539,6 +589,156 @@ ] } }, + "@@pybind11_bazel+//:internal_configure.bzl%internal_configure_extension": { + "general": { + "bzlTransitiveDigest": "yA4GkX1zdUTOXU4qKsh/YkjZKHAWkG8Fd7adNeGwiRc=", + "usagesDigest": "D1r3lfzMuUBFxgG8V6o0bQTLMk3GkaGOaPzw53wrwyw=", + "recordedFileInputs": { + "@@pybind11_bazel+//MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "pybind11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@pybind11_bazel+//:pybind11-BUILD.bazel", + "strip_prefix": "pybind11-2.12.0", + "urls": [ + "https://github.com/pybind/pybind11/archive/v2.12.0.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "pybind11_bazel+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_apple+//apple:apple.bzl%provisioning_profile_repository_extension": { + "general": { + "bzlTransitiveDigest": "pdRt+Wm+XQmS427nAIg9z7qfm56CnZChO+HJ4zu3Xa8=", + "usagesDigest": "vsJl8Rw5NL+5Ag2wdUDoTeRF/5klkXO8545Iy7U1Q08=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_provisioning_profiles": { + "repoRuleId": "@@rules_apple+//apple/internal:local_provisioning_profiles.bzl%provisioning_profile_repository", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "bazel_tools", + "rules_cc", + "rules_cc+" + ], + [ + "rules_apple+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_apple+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_apple+", + "build_bazel_apple_support", + "apple_support+" + ], + [ + "rules_apple+", + "build_bazel_rules_swift", + "rules_swift+" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_swift+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_swift+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_swift+", + "build_bazel_apple_support", + "apple_support+" + ], + [ + "rules_swift+", + "build_bazel_rules_swift", + "rules_swift+" + ], + [ + "rules_swift+", + "build_bazel_rules_swift_local_config", + "rules_swift++non_module_deps+build_bazel_rules_swift_local_config" + ] + ] + } + }, + "@@rules_apple+//apple:extensions.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "rdeowIB16n42a/MEy5gV/a/kpjt45s1nmLJCD/kXsPU=", + "usagesDigest": "M3VqFpeTCo4qmrNKGZw0dxBHvTYDrfV3cscGzlSAhQ4=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "xctestrunner": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/google/xctestrunner/archive/b7698df3d435b6491b4b4c0f9fc7a63fbed5e3a6.tar.gz" + ], + "strip_prefix": "xctestrunner-b7698df3d435b6491b4b4c0f9fc7a63fbed5e3a6", + "sha256": "ae3a063c985a8633cb7eb566db21656f8db8eb9a0edb8c182312c7f0db53730d" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_apple+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { "bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=", @@ -1243,6 +1443,317 @@ ] } }, + "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { + "general": { + "bzlTransitiveDigest": "dTMqlNf5XNNms3D6jnwfyN3A189YbUbRc2OuxgtL/IM=", + "usagesDigest": "h9wqdPgSnpDxdRNP6kO+DMoTuAWtxtek2SnJOOL321c=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "cargo_bazel_bootstrap": { + "repoRuleId": "@@rules_rust+//cargo/private:cargo_bootstrap.bzl%cargo_bootstrap_repository", + "attributes": { + "srcs": [ + "@@rules_rust+//crate_universe:src/api.rs", + "@@rules_rust+//crate_universe:src/api/lockfile.rs", + "@@rules_rust+//crate_universe:src/cli.rs", + "@@rules_rust+//crate_universe:src/cli/generate.rs", + "@@rules_rust+//crate_universe:src/cli/query.rs", + "@@rules_rust+//crate_universe:src/cli/render.rs", + "@@rules_rust+//crate_universe:src/cli/splice.rs", + "@@rules_rust+//crate_universe:src/cli/vendor.rs", + "@@rules_rust+//crate_universe:src/config.rs", + "@@rules_rust+//crate_universe:src/context.rs", + "@@rules_rust+//crate_universe:src/context/crate_context.rs", + "@@rules_rust+//crate_universe:src/context/platforms.rs", + "@@rules_rust+//crate_universe:src/lib.rs", + "@@rules_rust+//crate_universe:src/lockfile.rs", + "@@rules_rust+//crate_universe:src/main.rs", + "@@rules_rust+//crate_universe:src/metadata.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_bin.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_resolver.rs", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.bat", + "@@rules_rust+//crate_universe:src/metadata/cargo_tree_rustc_wrapper.sh", + "@@rules_rust+//crate_universe:src/metadata/dependency.rs", + "@@rules_rust+//crate_universe:src/metadata/metadata_annotation.rs", + "@@rules_rust+//crate_universe:src/rendering.rs", + "@@rules_rust+//crate_universe:src/rendering/template_engine.rs", + "@@rules_rust+//crate_universe:src/rendering/templates/module_bzl.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/header.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/aliases_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/deps_map.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_git.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/partials/module/repo_http.j2", + "@@rules_rust+//crate_universe:src/rendering/templates/vendor_module.j2", + "@@rules_rust+//crate_universe:src/rendering/verbatim/alias_rules.bzl", + "@@rules_rust+//crate_universe:src/select.rs", + "@@rules_rust+//crate_universe:src/splicing.rs", + "@@rules_rust+//crate_universe:src/splicing/cargo_config.rs", + "@@rules_rust+//crate_universe:src/splicing/crate_index_lookup.rs", + "@@rules_rust+//crate_universe:src/splicing/splicer.rs", + "@@rules_rust+//crate_universe:src/test.rs", + "@@rules_rust+//crate_universe:src/utils.rs", + "@@rules_rust+//crate_universe:src/utils/starlark.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/glob.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/label.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_dict.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_list.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_scalar.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/select_set.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/serialize.rs", + "@@rules_rust+//crate_universe:src/utils/starlark/target_compatible_with.rs", + "@@rules_rust+//crate_universe:src/utils/symlink.rs", + "@@rules_rust+//crate_universe:src/utils/target_triple.rs" + ], + "binary": "cargo-bazel", + "cargo_lockfile": "@@rules_rust+//crate_universe:Cargo.lock", + "cargo_toml": "@@rules_rust+//crate_universe:Cargo.toml", + "version": "1.86.0", + "timeout": 900, + "rust_toolchain_cargo_template": "@rust_host_tools//:bin/{tool}", + "rust_toolchain_rustc_template": "@rust_host_tools//:bin/{tool}", + "compressed_windows_toolchain_names": false + } + } + }, + "moduleExtensionMetadata": { + "explicitRootModuleDirectDeps": [ + "cargo_bazel_bootstrap" + ], + "explicitRootModuleDirectDevDeps": [], + "useAllRepos": "NO", + "reproducible": false + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_rust+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_rust+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_rust+", + "cui", + "rules_rust++cu+cui" + ], + [ + "rules_rust+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_rust+", + "rules_rust", + "rules_rust+" + ], + [ + "rules_rust+", + "rules_rust_ctve", + "rules_rust++i2+rules_rust_ctve" + ] + ] + } + }, + "@@rules_swift+//swift:extensions.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "X3B53n1AmUggqagsfW3TwgDmiWEQnqWY1izDXQ3mZtY=", + "usagesDigest": "mhACFnrdMv9Wi0Mt67bxocJqviRkDSV+Ee5Mqdj5akA=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_apple_swift_protobuf": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-protobuf/archive/1.20.2.tar.gz" + ], + "sha256": "3fb50bd4d293337f202d917b6ada22f9548a0a0aed9d9a4d791e6fbd8a246ebb", + "strip_prefix": "swift-protobuf-1.20.2/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_protobuf/BUILD.overlay" + } + }, + "com_github_grpc_grpc_swift": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/grpc/grpc-swift/archive/1.16.0.tar.gz" + ], + "sha256": "58b60431d0064969f9679411264b82e40a217ae6bd34e17096d92cc4e47556a5", + "strip_prefix": "grpc-swift-1.16.0/", + "build_file": "@@rules_swift+//third_party:com_github_grpc_grpc_swift/BUILD.overlay" + } + }, + "com_github_apple_swift_docc_symbolkit": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-docc-symbolkit/archive/refs/tags/swift-5.10-RELEASE.tar.gz" + ], + "sha256": "de1d4b6940468ddb53b89df7aa1a81323b9712775b0e33e8254fa0f6f7469a97", + "strip_prefix": "swift-docc-symbolkit-swift-5.10-RELEASE", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_docc_symbolkit/BUILD.overlay" + } + }, + "com_github_apple_swift_nio": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio/archive/2.42.0.tar.gz" + ], + "sha256": "e3304bc3fb53aea74a3e54bd005ede11f6dc357117d9b1db642d03aea87194a0", + "strip_prefix": "swift-nio-2.42.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_http2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-http2/archive/1.26.0.tar.gz" + ], + "sha256": "f0edfc9d6a7be1d587e5b403f2d04264bdfae59aac1d74f7d974a9022c6d2b25", + "strip_prefix": "swift-nio-http2-1.26.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_http2/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_transport_services": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-transport-services/archive/1.15.0.tar.gz" + ], + "sha256": "f3498dafa633751a52b9b7f741f7ac30c42bcbeb3b9edca6d447e0da8e693262", + "strip_prefix": "swift-nio-transport-services-1.15.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_transport_services/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_extras": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-extras/archive/1.4.0.tar.gz" + ], + "sha256": "4684b52951d9d9937bb3e8ccd6b5daedd777021ef2519ea2f18c4c922843b52b", + "strip_prefix": "swift-nio-extras-1.4.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_extras/BUILD.overlay" + } + }, + "com_github_apple_swift_log": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-log/archive/1.4.4.tar.gz" + ], + "sha256": "48fe66426c784c0c20031f15dc17faf9f4c9037c192bfac2f643f65cb2321ba0", + "strip_prefix": "swift-log-1.4.4/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_log/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_ssl": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-ssl/archive/2.23.0.tar.gz" + ], + "sha256": "4787c63f61dd04d99e498adc3d1a628193387e41efddf8de19b8db04544d016d", + "strip_prefix": "swift-nio-ssl-2.23.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_ssl/BUILD.overlay" + } + }, + "com_github_apple_swift_collections": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-collections/archive/1.0.4.tar.gz" + ], + "sha256": "d9e4c8a91c60fb9c92a04caccbb10ded42f4cb47b26a212bc6b39cc390a4b096", + "strip_prefix": "swift-collections-1.0.4/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_collections/BUILD.overlay" + } + }, + "com_github_apple_swift_atomics": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-atomics/archive/1.1.0.tar.gz" + ], + "sha256": "1bee7f469f7e8dc49f11cfa4da07182fbc79eab000ec2c17bfdce468c5d276fb", + "strip_prefix": "swift-atomics-1.1.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_atomics/BUILD.overlay" + } + }, + "build_bazel_rules_swift_index_import": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@rules_swift+//third_party:build_bazel_rules_swift_index_import/BUILD.overlay", + "canonical_id": "index-import-5.8", + "urls": [ + "https://github.com/MobileNativeFoundation/index-import/releases/download/5.8.0.1/index-import.tar.gz" + ], + "sha256": "28c1ffa39d99e74ed70623899b207b41f79214c498c603915aef55972a851a15" + } + }, + "build_bazel_rules_swift_local_config": { + "repoRuleId": "@@rules_swift+//swift/internal:swift_autoconfiguration.bzl%swift_autoconfiguration", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_swift+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_swift+", + "build_bazel_rules_swift", + "rules_swift+" + ] + ] + } + }, "@@yq.bzl+//yq:extensions.bzl%yq": { "general": { "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=", diff --git a/acceptance/common/scion.py b/acceptance/common/scion.py index c330fb20c3..587d4ce284 100644 --- a/acceptance/common/scion.py +++ b/acceptance/common/scion.py @@ -68,6 +68,19 @@ def update_json(change_dict: Dict[str, Any], files: LocalPath): with open(file, "w") as f: json.dump(t, f, indent=2) +def write_file(contents: str, files: LocalPath): + """ Writes text into file(s). + + Args: + contents: text to write into the file(s). + files: names of file or files to update. + + Raises: + IOError / FileNotFoundError: File path is not valid + """ + for file in files: + with open(file, "w") as f: + f.write(contents) def load_from_json(key: str, files: LocalPath) -> Any: """ Reads the value associated with the given key from the given json files. diff --git a/acceptance/transit_traffic/BUILD.bazel b/acceptance/transit_traffic/BUILD.bazel new file mode 100644 index 0000000000..cf17ffb0df --- /dev/null +++ b/acceptance/transit_traffic/BUILD.bazel @@ -0,0 +1,33 @@ +load("//acceptance/common:topogen.bzl", "topogen_test") + +py_library( + name = "transit_traffic_base", + srcs = ["transit_traffic_base.py"], +) + +topogen_test( + name = "test_isd_1", + src = "test_isd_1.py", + topo = "//topology:testdata/big.topo", + deps = [ + ":transit_traffic_base", + ], +) + +topogen_test( + name = "test_isd_3", + src = "test_isd_3.py", + topo = "//topology:testdata/big.topo", + deps = [ + ":transit_traffic_base", + ], +) + +topogen_test( + name = "test_incomplete_config", + src = "test_incomplete_config.py", + topo = "//topology:testdata/big.topo", + deps = [ + ":transit_traffic_base", + ], +) diff --git a/acceptance/transit_traffic/test_incomplete_config.py b/acceptance/transit_traffic/test_incomplete_config.py new file mode 100644 index 0000000000..aaae354d7c --- /dev/null +++ b/acceptance/transit_traffic/test_incomplete_config.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +# Copyright 2026 SCION Association +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acceptance.transit_traffic import transit_traffic_base + +class Test(transit_traffic_base.Test): + """ + This test disallows transit traffic at only one of two core ASes in an ISD. + It demonstrates that an incomplete configuration (only AS 120 blocks transit, + while AS 110 does not) creates partial isolation: ISDs that both connect + through the blocking AS lose connectivity to each other, but can still reach + ISDs reachable through the non-blocking core AS. + + The graph picture can be found here: topology/testdata/big.topo.png + + Transit traffic is blocked only at AS 120. + """ + + def setup_prepare(self): + super().setup_prepare("1", ["120"]) + + def _run(self): + # Since only AS 120 is blocking transit traffic, there's no propagation + # from ISDs 2 and 6 towards ISDs 3, 4 and 5. However, beacons from + # ISDs 3, 4 and 5 are not blocked by AS 120 since they are propagating + # to AS 110, which does not count as transit traffic. So there're + # no paths only in one direction. + self._assert_no_path("310", "210") + self._assert_path("210", "310") + self._assert_no_path("411", "211") + self._assert_path("211", "411") + self._assert_no_path("510", "210") + self._assert_path("210", "510") + + # ISDs 3 and 4 cannot reach ISD 5: the only path goes through 120. + self._assert_no_path_in_both_directions("310", "510") + self._assert_no_path_in_both_directions("410", "510") + self._assert_no_path_in_both_directions("311", "510") + self._assert_no_path_in_both_directions("411", "510") + + # Traffic originating or ending in AS 120 is allowed. + self._assert_bidirectional_path("120", "310") + self._assert_bidirectional_path("120", "510") + self._assert_bidirectional_path("120", "110") + + # Traffic outside of ISD 1 is not affected. + self._assert_bidirectional_path("310", "410") + self._assert_bidirectional_path("311", "411") + self._assert_bidirectional_path("610", "210") + self._assert_bidirectional_path("410", "411") + self._assert_bidirectional_path("310", "311") + self._assert_bidirectional_path("110", "111") + self._assert_bidirectional_path("122", "123") + self._assert_bidirectional_path("610", "611") + self._assert_bidirectional_path("620", "621") + +if __name__ == "__main__": + transit_traffic_base.main(Test) diff --git a/acceptance/transit_traffic/test_isd_1.py b/acceptance/transit_traffic/test_isd_1.py new file mode 100644 index 0000000000..530651b849 --- /dev/null +++ b/acceptance/transit_traffic/test_isd_1.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +# Copyright 2026 SCION Association +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acceptance.transit_traffic import transit_traffic_base + +class Test(transit_traffic_base.Test): + """ + This test disallows transit traffic at all core ASes in ISD 1, + the central hub ISD. It differs from ISD 3 test since there are + no peering links between ISDs 1,2,4,5 and ISDs 2,6. + + The graph picture can be found here: topology/testdata/big.topo.png + + With transit blocked at both 110 and 120, ISD 1 becomes a wall: + each neighboring ISD can reach ISD 1 via 2-ISD origination beacons, + but no beacon transits through ISD 1 to connect two other ISDs. + Only ISDs with direct core links bypass ISD 1: + ISD 3 <-> ISD 4 (310-410) and ISD 2 <-> ISD 6 (210-610). + """ + + def setup_prepare(self): + super().setup_prepare("1", ["110", "120"]) + + def _run(self): + # Traffic originating or ending in ISD 1 is allowed. + self._assert_bidirectional_path("210", "110") + self._assert_bidirectional_path("310", "120") + self._assert_bidirectional_path("410", "110") + self._assert_bidirectional_path("510", "120") + self._assert_bidirectional_path("610", "110") + self._assert_bidirectional_path("211", "111") + self._assert_bidirectional_path("311", "121") + self._assert_bidirectional_path("611", "111") + self._assert_bidirectional_path("110", "111") + self._assert_bidirectional_path("120", "122") + + # Traffic outside of ISD 1 is not affected. + self._assert_bidirectional_path("310", "410") + self._assert_bidirectional_path("311", "411") + self._assert_bidirectional_path("210", "610") + self._assert_bidirectional_path("211", "611") + self._assert_bidirectional_path("310", "311") + self._assert_bidirectional_path("410", "411") + self._assert_bidirectional_path("610", "611") + self._assert_bidirectional_path("620", "621") + + # Transit traffic via ISD 1 is not allowed. + self._assert_no_path_in_both_directions("210", "310") + self._assert_no_path_in_both_directions("210", "510") + self._assert_no_path_in_both_directions("310", "510") + self._assert_no_path_in_both_directions("310", "610") + self._assert_no_path_in_both_directions("410", "510") + self._assert_no_path_in_both_directions("410", "610") + self._assert_no_path_in_both_directions("510", "610") + self._assert_no_path_in_both_directions("211", "311") + self._assert_no_path_in_both_directions("411", "611") + +if __name__ == "__main__": + transit_traffic_base.main(Test) diff --git a/acceptance/transit_traffic/test_isd_3.py b/acceptance/transit_traffic/test_isd_3.py new file mode 100644 index 0000000000..93a05877ae --- /dev/null +++ b/acceptance/transit_traffic/test_isd_3.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +# Copyright 2026 SCION Association +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acceptance.transit_traffic import transit_traffic_base + +class Test(transit_traffic_base.Test): + """ + This test disallows transit traffic in all ASes of ISD 3 and checks the + resulting paths. It differs from ISD 1 test since there are + peering links between ISD 4 and ISD 1. + + The graph picture can be found here: topology/testdata/big.topo.png + + Transit traffic is blocked at all ASes in ISD 3 (310 and 311). + """ + + def setup_prepare(self): + super().setup_prepare("3", ["310", "311"]) + + def _run(self): + # Traffic originating or ending in ISD 3 is allowed. + self._assert_bidirectional_path("310", "410") + self._assert_bidirectional_path("311", "410") + self._assert_bidirectional_path("310", "411") + self._assert_bidirectional_path("311", "411") + self._assert_bidirectional_path("310", "210") + self._assert_bidirectional_path("311", "123") + self._assert_bidirectional_path("310", "510") + self._assert_bidirectional_path("311", "111") + self._assert_bidirectional_path("310", "610") + self._assert_bidirectional_path("311", "611") + + # Transit traffic via ISD 3 is not allowed, + # therefore beacons are not making it through ISD 3. + # Because of it, peering links info is not propagated. + # While the graph has peering links for all cases below, + # there's no path in such config. + self._assert_no_path_in_both_directions("410", "210") + self._assert_no_path_in_both_directions("411", "211") + self._assert_no_path_in_both_directions("411", "510") + self._assert_no_path_in_both_directions("410", "123") + self._assert_no_path_in_both_directions("411", "123") + self._assert_no_path_in_both_directions("410", "111") + self._assert_no_path_in_both_directions("410", "610") + self._assert_no_path_in_both_directions("411", "611") + + # Traffic outside of ISD 3 is not affected. + self._assert_bidirectional_path("123", "510") + self._assert_bidirectional_path("123", "211") + self._assert_bidirectional_path("122", "111") + self._assert_bidirectional_path("120", "210") + self._assert_bidirectional_path("510", "211") + self._assert_bidirectional_path("111", "211") + self._assert_bidirectional_path("410", "411") + self._assert_bidirectional_path("610", "210") + self._assert_bidirectional_path("610", "110") + self._assert_bidirectional_path("610", "510") + self._assert_bidirectional_path("611", "211") + self._assert_bidirectional_path("610", "611") + self._assert_bidirectional_path("620", "621") + +if __name__ == "__main__": + transit_traffic_base.main(Test) diff --git a/acceptance/transit_traffic/transit_traffic_base.py b/acceptance/transit_traffic/transit_traffic_base.py new file mode 100644 index 0000000000..2d8910f8dd --- /dev/null +++ b/acceptance/transit_traffic/transit_traffic_base.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +# Copyright 2026 SCION Association +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import time + +from acceptance.common import base, scion +from tools.topology.scion_addr import ISD_AS + +class Test(base.TestTopogen): + _ases = { + "110": "1-ff00:0:110", + "111": "1-ff00:0:111", + "120": "1-ff00:0:120", + "121": "1-ff00:0:121", + "122": "1-ff00:0:122", + "123": "1-ff00:0:123", + "210": "2-ff00:0:210", + "211": "2-ff00:0:211", + "310": "3-ff00:0:310", + "311": "3-ff00:0:311", + "410": "4-ff00:0:410", + "411": "4-ff00:0:411", + "510": "5-ff00:0:510", + "610": "6-ff00:0:610", + "611": "6-ff00:0:611", + "612": "6-ff00:0:612", + "620": "6-ff00:0:620", + "621": "6-ff00:0:621", + "622": "6-ff00:0:622", + } + + def setup_prepare(self, no_transit_isd_number, no_transit_as_numbers): + super().setup_prepare() + + for as_number in no_transit_as_numbers: + as_number_string = "ff00_0_%s" % as_number + as_relative_dir_path = "AS%s" % as_number_string + as_absolute_dir_path = self.artifacts / "gen" / as_relative_dir_path + + cs_toml_path = ( + as_absolute_dir_path + / ("cs%s-%s-1.toml" % (no_transit_isd_number, as_number_string)) + ) + policy_path = as_absolute_dir_path / "policy.yaml" + + scion.update_toml( + {"beaconing.policies.propagation": "/etc/scion/policy.yaml"}, + [cs_toml_path]) + + scion.write_file("""Filter: + AllowTransitTraffic: False""", [policy_path]) + + def setup_start(self): + super().setup_start() + # since some paths are not available, self.await_connectivity() doesn't work well + time.sleep(15) + + def _showpaths(self, source_as: str, destination_as: str): + print(self.execute_tester(ISD_AS(self._ases[source_as]), + "scion", "sp", self._ases[destination_as], "--timeout", "2s")) + + def _assert_path(self, source_as: str, destination_as: str): + try: + self._showpaths(source_as, destination_as) + except Exception as e: + raise AssertionError(f"No path found: {source_as} -> {destination_as}") from e + + def _assert_bidirectional_path(self, source_as: str, destination_as: str): + self._assert_path(source_as, destination_as) + self._assert_path(destination_as, source_as) + + def _assert_no_path(self, source_as: str, destination_as: str): + try: + self._showpaths(source_as, destination_as) + except Exception as e: + print(e) + else: + raise AssertionError(f"Unexpected path: {source_as} -> {destination_as}") + + def _assert_no_path_in_both_directions(self, source_as: str, destination_as: str): + self._assert_no_path(source_as, destination_as) + self._assert_no_path(destination_as, source_as) + +def main(test_class): + base.main(test_class) diff --git a/control/beacon/policy.go b/control/beacon/policy.go index 549f3a72e8..19136ed989 100644 --- a/control/beacon/policy.go +++ b/control/beacon/policy.go @@ -429,6 +429,8 @@ type Filter struct { IsdBlackList []addr.ISD `yaml:"IsdBlackList"` // AllowIsdLoop indicates whether ISD loops should not be filtered. AllowIsdLoop *bool `yaml:"AllowIsdLoop"` + // AllowTransitTraffic indicates whether transit traffic via this ISD is allowed. + AllowTransitTraffic *bool `yaml:"AllowTransitTraffic"` } // InitDefaults initializes the default values for unset fields. @@ -439,6 +441,9 @@ func (f *Filter) InitDefaults() { if f.AllowIsdLoop == nil { f.AllowIsdLoop = ptr.To(true) } + if f.AllowTransitTraffic == nil { + f.AllowTransitTraffic = ptr.To(true) + } } // Apply returns an error if the beacon is filtered. @@ -523,3 +528,25 @@ func filterIsdLoop(hops []addr.IA) addr.ISD { } return 0 } + +// FilterTransitTraffic returns an error if transit traffic is forbidden and propagating +// the beacon could cause transit traffic. +func FilterTransitTraffic(beacon Beacon, next addr.IA, allowTransitTraffic bool) error { + if allowTransitTraffic { + return nil + } + if len(beacon.Segment.ASEntries) == 0 { + return nil + } + isds := make(map[addr.ISD]struct{}) + for _, asEntry := range beacon.Segment.ASEntries { + isds[asEntry.Local.ISD()] = struct{}{} + } + curISD := beacon.Segment.ASEntries[len(beacon.Segment.ASEntries)-1].Next.ISD() + isds[curISD] = struct{}{} + isds[next.ISD()] = struct{}{} + if (curISD != next.ISD()) && (len(isds) > 2) { + return serrors.New("Transit traffic", "isd", next.ISD()) + } + return nil +} diff --git a/control/beacon/policy_test.go b/control/beacon/policy_test.go index 63d7b96f78..74aa698a70 100644 --- a/control/beacon/policy_test.go +++ b/control/beacon/policy_test.go @@ -72,6 +72,7 @@ func TestLoadPolicyFromYaml(t *testing.T) { assert.Equal(t, []addr.AS{ia110.AS(), ia111.AS()}, p.Filter.AsBlackList) assert.Equal(t, []addr.ISD{1, 2, 3}, p.Filter.IsdBlackList) assert.True(t, *p.Filter.AllowIsdLoop) + assert.True(t, *p.Filter.AllowTransitTraffic) }) } } diff --git a/control/beacon/store.go b/control/beacon/store.go index ac7c06edc3..12ec8561b5 100644 --- a/control/beacon/store.go +++ b/control/beacon/store.go @@ -210,18 +210,26 @@ func (s *CoreStore) BeaconsToPropagate(ctx context.Context) ([]Beacon, error) { // SegmentsToRegister returns a GroupedBeacons to register at the time of the call. // The selection is based on the configured policy for the requested segment type. +// For TypeDown or TypeUp, returns an empty map (core ASes don't have up/down beacons, +// but the caller may add one-hop segments for core-to-core peering). func (s *CoreStore) SegmentsToRegister( ctx context.Context, segType seg.Type, ) (GroupedBeacons, error) { - if segType != seg.TypeCore { + switch segType { + case seg.TypeCore: + beacons, err := s.getBeacons(ctx, &s.policies.CoreReg) + if err != nil { + return nil, err + } + return groupBeacons(beacons, &s.policies.CoreReg), nil + case seg.TypeUp: + // Core ASes don't have up beacons from the beacon store, but the + // caller adds one-hop segments for core AS peering. Return empty. + return make(GroupedBeacons), nil + default: return nil, serrors.New("Unsupported segment type", "type", segType) } - beacons, err := s.getBeacons(ctx, &s.policies.CoreReg) - if err != nil { - return nil, err - } - return groupBeacons(beacons, &s.policies.CoreReg), nil } // getBeacons fetches the candidate beacons from the database and serves the diff --git a/control/beacon/testdata/policy.yml b/control/beacon/testdata/policy.yml index 6a0c5f61d8..94a3a9a6c0 100644 --- a/control/beacon/testdata/policy.yml +++ b/control/beacon/testdata/policy.yml @@ -7,4 +7,5 @@ Filter: AsBlackList: ["ff00:0:110", "ff00:0:111"] IsdBlackList: [1, 2, 3] AllowIsdLoop: true + AllowTransitTraffic: true diff --git a/control/beacon/testdata/typedPolicy.yml b/control/beacon/testdata/typedPolicy.yml index 6a13be0478..65edaed3c0 100644 --- a/control/beacon/testdata/typedPolicy.yml +++ b/control/beacon/testdata/typedPolicy.yml @@ -7,4 +7,5 @@ Filter: AsBlackList: ["ff00:0:110", "ff00:0:111"] IsdBlackList: [1, 2, 3] AllowIsdLoop: true + AllowTransitTraffic: true Type: Propagation diff --git a/control/beaconing/extender.go b/control/beaconing/extender.go index a8e8d68e16..22658b4e1d 100644 --- a/control/beaconing/extender.go +++ b/control/beaconing/extender.go @@ -110,8 +110,8 @@ func (s *DefaultExtender) Extend( if ingress != 0 && firstHop { return serrors.New("ingress must be zero in first hop", "ingress_interface", ingress) } - if ingress == 0 && egress == 0 { - return serrors.New("ingress and egress must not be both 0") + if ingress == 0 && egress == 0 && len(peers) == 0 { + return serrors.New("ingress and egress must not be both 0 without peers") } ts := pseg.Info.Timestamp diff --git a/control/beaconing/propagator.go b/control/beaconing/propagator.go index faed1623ac..e63a1f7a91 100644 --- a/control/beaconing/propagator.go +++ b/control/beaconing/propagator.go @@ -61,6 +61,7 @@ type Propagator struct { AllInterfaces *ifstate.Interfaces PropagationInterfaces func() []*ifstate.Interface AllowIsdLoop bool + AllowTransitTraffic bool Propagated metrics.Counter InternalErrors metrics.Counter @@ -194,6 +195,10 @@ func (p *Propagator) shouldIgnore(bseg beacon.Beacon, intf *ifstate.Interface) b if err := beacon.FilterLoop(bseg, intf.TopoInfo().IA, p.AllowIsdLoop); err != nil { return true } + if err := beacon.FilterTransitTraffic(bseg, intf.TopoInfo().IA, + p.AllowTransitTraffic); err != nil { + return true + } return false } diff --git a/control/beaconing/propagator_test.go b/control/beaconing/propagator_test.go index a6048ac191..2f434bd5e0 100644 --- a/control/beaconing/propagator_test.go +++ b/control/beaconing/propagator_test.go @@ -1,5 +1,5 @@ // Copyright 2019 Anapaya Systems -// Copyright 2025 SCION Association +// Copyright 2026 SCION Association // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import ( "crypto/elliptic" "crypto/rand" "net" + "strings" "testing" "time" @@ -42,6 +43,13 @@ import ( "github.com/scionproto/scion/private/trust" ) +const ( + IA_1_ff00_0_110 = "testdata/big/ASff00_0_110.json" + IA_1_ff00_0_120 = "testdata/big/ASff00_0_120.json" + IA_1_ff00_0_121 = "testdata/big/ASff00_0_121.json" + IA_3_ff00_0_310 = "testdata/big/ASff00_0_310.json" +) + func TestPropagatorRunNonCore(t *testing.T) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) require.NoError(t, err) @@ -59,9 +67,6 @@ func TestPropagatorRunNonCore(t *testing.T) { intfs := ifstate.NewInterfaces(interfaceInfos(topo), ifstate.Config{}) provider := mock_beaconing.NewMockBeaconProvider(mctrl) senderFactory := mock_beaconing.NewMockSenderFactory(mctrl) - filter := func(intf *ifstate.Interface) bool { - return intf.TopoInfo().LinkType == topology.Child - } p := beaconing.Propagator{ Extender: &beaconing.DefaultExtender{ IA: topo.IA(), @@ -80,10 +85,11 @@ func TestPropagatorRunNonCore(t *testing.T) { Signer: testSigner(t, priv, topo.IA()), AllInterfaces: intfs, PropagationInterfaces: func() []*ifstate.Interface { - return intfs.Filtered(filter) + return intfs.Filtered(childLinkTypeFilter) }, - Tick: beaconing.NewTick(time.Hour), - Provider: provider, + Tick: beaconing.NewTick(time.Hour), + Provider: provider, + AllowTransitTraffic: true, } g := graph.NewDefaultGraph(mctrl) provider.EXPECT().BeaconsToPropagate(gomock.Any()).Times(1).DoAndReturn( @@ -135,9 +141,6 @@ func TestPropagatorRunCore(t *testing.T) { intfs := ifstate.NewInterfaces(interfaceInfos(topo), ifstate.Config{}) provider := mock_beaconing.NewMockBeaconProvider(mctrl) senderFactory := mock_beaconing.NewMockSenderFactory(mctrl) - filter := func(intf *ifstate.Interface) bool { - return intf.TopoInfo().LinkType == topology.Core - } p := beaconing.Propagator{ Extender: &beaconing.DefaultExtender{ IA: topo.IA(), @@ -156,7 +159,7 @@ func TestPropagatorRunCore(t *testing.T) { Signer: testSigner(t, priv, topo.IA()), AllInterfaces: intfs, PropagationInterfaces: func() []*ifstate.Interface { - return intfs.Filtered(filter) + return intfs.Filtered(coreLinkTypeFilter) }, Tick: beaconing.NewTick(time.Hour), Provider: provider, @@ -224,9 +227,6 @@ func TestPropagatorFastRecovery(t *testing.T) { provider := mock_beaconing.NewMockBeaconProvider(mctrl) senderFactory := mock_beaconing.NewMockSenderFactory(mctrl) sender := mock_beaconing.NewMockSender(mctrl) - filter := func(intf *ifstate.Interface) bool { - return intf.TopoInfo().LinkType == topology.Core - } p := beaconing.Propagator{ Extender: &beaconing.DefaultExtender{ @@ -246,10 +246,11 @@ func TestPropagatorFastRecovery(t *testing.T) { Signer: testSigner(t, priv, topo.IA()), AllInterfaces: intfs, PropagationInterfaces: func() []*ifstate.Interface { - return intfs.Filtered(filter) + return intfs.Filtered(coreLinkTypeFilter) }, - Tick: beaconing.NewTick(2 * time.Second), - Provider: provider, + Tick: beaconing.NewTick(2 * time.Second), + Provider: provider, + AllowTransitTraffic: true, } g := graph.NewDefaultGraph(mctrl) @@ -285,6 +286,241 @@ func TestPropagatorFastRecovery(t *testing.T) { p.Run(context.Background()) } +func TestPropagatorTransitTraffic(t *testing.T) { + // The graph without peering links looks as follows: + // 411 123 + // | | + // 410 121 122 111 211 + // \ \ | / / + // 310---120---110---210 + // / | + // 311 510 + + var tests = []struct { + name string + topoFile string + filter func(*ifstate.Interface) bool + beacons [][]uint16 + ifIDs []uint16 + filteredIfIDs []uint16 + allowTransitTraffic bool + }{ + { + name: strings.Join([]string{"Core beaconing", + "transit traffic allowed", + "propagation expected from 1-ff00:0:110 to 2-ff00:0:210"}, ","), + topoFile: IA_1_ff00_0_110, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_410_X_310_X, graph.If_310_X_120_X, graph.If_120_X_110_X}, + {graph.If_510_X_120_X, graph.If_120_X_110_X}, + }, + ifIDs: []uint16{graph.If_110_X_210_X}, + filteredIfIDs: []uint16{}, + allowTransitTraffic: true, + }, + { + name: strings.Join([]string{"Core beaconing", + "transit traffic allowed", + "propagation expected from 1-ff00:0:120 to 3-ff00:0:310 and 5-ff00:0:510"}, ","), + topoFile: IA_1_ff00_0_120, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_210_X_110_X, graph.If_110_X_120_X}, + }, + ifIDs: []uint16{graph.If_120_X_310_X, graph.If_120_X_510_X}, + filteredIfIDs: []uint16{}, + allowTransitTraffic: true, + }, + { + name: strings.Join([]string{"Core beaconing", + "transit traffic not allowed", + "propagation not expected from 1-ff00:0:110"}, ","), + topoFile: IA_1_ff00_0_110, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_410_X_310_X, graph.If_310_X_120_X, graph.If_120_X_110_X}, + {graph.If_510_X_120_X, graph.If_120_X_110_X}, + }, + ifIDs: []uint16{}, + filteredIfIDs: []uint16{graph.If_110_X_210_X}, + allowTransitTraffic: false, + }, + { + name: strings.Join([]string{"Core beaconing", + "transit traffic not allowed", + "propagation not expected from 3-ff00:0:310"}, ","), + topoFile: IA_3_ff00_0_310, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_410_X_310_X}, + }, + ifIDs: []uint16{}, + filteredIfIDs: []uint16{graph.If_310_X_120_X}, + allowTransitTraffic: false, + }, + { + name: strings.Join([]string{"Core beaconing", + "transit traffic not allowed", + "propagation expected from 1-ff00:0:120 to 1-ff00:0:110", + "propagation not expected from 1-ff00:0:120 to 5-ff00:0:510"}, ","), + topoFile: IA_1_ff00_0_120, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_410_X_310_X, graph.If_310_X_120_X}, + }, + ifIDs: []uint16{graph.If_120_X_110_X}, + filteredIfIDs: []uint16{graph.If_120_X_510_X}, + allowTransitTraffic: false, + }, + { + name: strings.Join([]string{"Core beaconing", + "transit traffic not allowed", + "propagation expected from 1-ff00:0:110 to 2-ff00:0:210"}, ","), + topoFile: IA_1_ff00_0_110, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_120_X_110_X}, + }, + ifIDs: []uint16{graph.If_110_X_210_X}, + filteredIfIDs: []uint16{}, + allowTransitTraffic: false, + }, + { + name: strings.Join([]string{"Core beaconing", + "transit traffic not allowed", + "propagation expected from 1-ff00:0:120 to 3-ff00:0:310 and 5-ff00:0:510"}, ","), + topoFile: IA_1_ff00_0_120, + filter: coreLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_110_X_120_X}, + }, + ifIDs: []uint16{graph.If_120_X_310_X, graph.If_120_X_510_X}, + filteredIfIDs: []uint16{}, + allowTransitTraffic: false, + }, + { + name: strings.Join([]string{"Intra-ISD beaconing", + "transit traffic allowed", + "propagation expected from 1-ff00:0:121 to 1-ff00:0:123"}, ","), + topoFile: IA_1_ff00_0_121, + filter: childLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_120_X_121_X}, + }, + ifIDs: []uint16{graph.If_121_X_123_X}, + filteredIfIDs: []uint16{}, + allowTransitTraffic: true, + }, + { + name: strings.Join([]string{"Intra-ISD beaconing", + "transit traffic not allowed", + "propagation expected from 1-ff00:0:121 to 1-ff00:0:123"}, ","), + topoFile: IA_1_ff00_0_121, + filter: childLinkTypeFilter, + beacons: [][]uint16{ + {graph.If_120_X_121_X}, + }, + ifIDs: []uint16{graph.If_121_X_123_X}, + filteredIfIDs: []uint16{}, + allowTransitTraffic: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + runTransitTrafficTest(t, test.topoFile, test.filter, test.beacons, + test.ifIDs, test.filteredIfIDs, test.allowTransitTraffic) + }) + } +} + +func runTransitTrafficTest(t *testing.T, topoFile string, filter func(*ifstate.Interface) bool, + beacons [][]uint16, ifIDs, filteredIfIDs []uint16, allowTransitTraffic bool) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + pub := priv.Public() + + mctrl := gomock.NewController(t) + topo, err := topology.FromJSONFile(topoFile) + require.NoError(t, err) + intfs := ifstate.NewInterfaces(interfaceInfos(topo), ifstate.Config{}) + provider := mock_beaconing.NewMockBeaconProvider(mctrl) + senderFactory := mock_beaconing.NewMockSenderFactory(mctrl) + p := beaconing.Propagator{ + Extender: &beaconing.DefaultExtender{ + IA: topo.IA(), + MTU: topo.MTU(), + SignerGen: testSignerGen{Signers: []trust.Signer{testSigner(t, priv, topo.IA())}}, + Intfs: intfs, + MAC: macFactory, + MaxExpTime: func() uint8 { return beacon.DefaultMaxExpTime }, + StaticInfo: func() *beaconing.StaticInfoCfg { return nil }, + DiscoveryInformation: func() *discovery.Extension { return nil }, + }, + SenderFactory: senderFactory, + IA: topo.IA(), + Signer: testSigner(t, priv, topo.IA()), + AllInterfaces: intfs, + PropagationInterfaces: func() []*ifstate.Interface { + return intfs.Filtered(filter) + }, + Tick: beaconing.NewTick(time.Hour), + Provider: provider, + AllowTransitTraffic: allowTransitTraffic, + } + g := graph.NewFromDescription(mctrl, graph.BigGraphDescription) + + // Provider still provides all beacons as it doesn't know anything about their destination + provider.EXPECT().BeaconsToPropagate(gomock.Any()).Times(1).DoAndReturn( + func(_ any) ([]beacon.Beacon, error) { + res := make([]beacon.Beacon, 0, len(beacons)) + for _, desc := range beacons { + res = append(res, testBeacon(g, desc)) + } + return res, nil + }, + ) + + if len(ifIDs) == 0 { + senderFactory.EXPECT().NewSender(gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any()).Times(0) + } else { + for _, ifID := range ifIDs { + senderFactory.EXPECT().NewSender(gomock.Any(), gomock.Any(), ifID, + gomock.Any()).Times(1).DoAndReturn( + func(_ context.Context, _ addr.IA, egIfID uint16, + nextHop *net.UDPAddr, + ) (beaconing.Sender, error) { + sender := mock_beaconing.NewMockSender(mctrl) + sender.EXPECT().Send(gomock.Any(), gomock.Any()).Times(len(beacons)).DoAndReturn( + func(ctx context.Context, b *seg.PathSegment) error { + validateSend(ctx, t, b, egIfID, nextHop, pub, topo) + return nil + }, + ) + sender.EXPECT().Close().Times(1) + return sender, nil + }, + ) + } + for _, filteredIfID := range filteredIfIDs { + senderFactory.EXPECT().NewSender(gomock.Any(), gomock.Any(), + filteredIfID, gomock.Any()).Times(0) + } + } + + p.Run(context.Background()) +} + +func childLinkTypeFilter(intf *ifstate.Interface) bool { + return intf.TopoInfo().LinkType == topology.Child +} + +func coreLinkTypeFilter(intf *ifstate.Interface) bool { + return intf.TopoInfo().LinkType == topology.Core +} + func validateSend( ctx context.Context, t *testing.T, diff --git a/control/beaconing/testdata/big/ASff00_0_110.json b/control/beaconing/testdata/big/ASff00_0_110.json index ff575d47a9..3c619efec6 100644 --- a/control/beaconing/testdata/big/ASff00_0_110.json +++ b/control/beaconing/testdata/big/ASff00_0_110.json @@ -8,17 +8,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_110-1": { - "addr": "127.0.0.52:31000" + "addr": "127.0.0.68:31000" } }, "discovery_service": { "cs1-ff00_0_110-1": { - "addr": "127.0.0.52:31000" + "addr": "127.0.0.68:31000" } }, "border_routers": { "br1-ff00_0_110-1": { - "internal_addr": "127.0.0.49:31002", + "internal_addr": "127.0.0.65:31002", "interfaces": { "1112": { "underlay": { @@ -32,7 +32,7 @@ } }, "br1-ff00_0_110-2": { - "internal_addr": "127.0.0.50:31004", + "internal_addr": "127.0.0.66:31004", "interfaces": { "1121": { "underlay": { @@ -46,7 +46,7 @@ } }, "br1-ff00_0_110-3": { - "internal_addr": "127.0.0.51:31006", + "internal_addr": "127.0.0.67:31006", "interfaces": { "1120": { "underlay": { diff --git a/control/beaconing/testdata/big/ASff00_0_111.json b/control/beaconing/testdata/big/ASff00_0_111.json index 7ff9bc7dd9..08bf05be31 100644 --- a/control/beaconing/testdata/big/ASff00_0_111.json +++ b/control/beaconing/testdata/big/ASff00_0_111.json @@ -6,17 +6,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_111-1": { - "addr": "127.0.0.60:31020" + "addr": "127.0.0.76:31022" } }, "discovery_service": { "cs1-ff00_0_111-1": { - "addr": "127.0.0.60:31020" + "addr": "127.0.0.76:31022" } }, "border_routers": { "br1-ff00_0_111-1": { - "internal_addr": "127.0.0.57:31022", + "internal_addr": "127.0.0.73:31024", "interfaces": { "2011": { "underlay": { @@ -30,7 +30,7 @@ } }, "br1-ff00_0_111-2": { - "internal_addr": "127.0.0.58:31024", + "internal_addr": "127.0.0.74:31026", "interfaces": { "2021": { "underlay": { @@ -45,7 +45,7 @@ } }, "br1-ff00_0_111-3": { - "internal_addr": "127.0.0.59:31026", + "internal_addr": "127.0.0.75:31028", "interfaces": { "2035": { "underlay": { diff --git a/control/beaconing/testdata/big/ASff00_0_120.json b/control/beaconing/testdata/big/ASff00_0_120.json index 653a520f60..9cf080a5f9 100644 --- a/control/beaconing/testdata/big/ASff00_0_120.json +++ b/control/beaconing/testdata/big/ASff00_0_120.json @@ -8,17 +8,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_120-1": { - "addr": "127.0.0.70:31008" + "addr": "127.0.0.87:31008" } }, "discovery_service": { "cs1-ff00_0_120-1": { - "addr": "127.0.0.70:31008" + "addr": "127.0.0.87:31008" } }, "border_routers": { "br1-ff00_0_120-1": { - "internal_addr": "127.0.0.65:31010", + "internal_addr": "127.0.0.81:31010", "interfaces": { "1211": { "underlay": { @@ -32,7 +32,7 @@ } }, "br1-ff00_0_120-2": { - "internal_addr": "127.0.0.66:31012", + "internal_addr": "127.0.0.82:31012", "interfaces": { "1215": { "underlay": { @@ -46,7 +46,7 @@ } }, "br1-ff00_0_120-3": { - "internal_addr": "127.0.0.67:31014", + "internal_addr": "127.0.0.83:31014", "interfaces": { "1218": { "underlay": { @@ -60,7 +60,7 @@ } }, "br1-ff00_0_120-4": { - "internal_addr": "127.0.0.68:31016", + "internal_addr": "127.0.0.84:31016", "interfaces": { "1236": { "underlay": { @@ -74,13 +74,28 @@ } }, "br1-ff00_0_120-5": { - "internal_addr": "127.0.0.69:31018", + "internal_addr": "127.0.0.85:31018", "interfaces": { - "1237": { + "1240": { "underlay": { "local": "127.0.0.20:50000", "remote": "127.0.0.21:50000" }, + "isd_as": "4-ff00:0:410", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4012 + } + } + }, + "br1-ff00_0_120-6": { + "internal_addr": "127.0.0.86:31020", + "interfaces": { + "1237": { + "underlay": { + "local": "127.0.0.22:50000", + "remote": "127.0.0.23:50000" + }, "isd_as": "5-ff00:0:510", "link_to": "core", "mtu": 1472 diff --git a/control/beaconing/testdata/big/ASff00_0_121.json b/control/beaconing/testdata/big/ASff00_0_121.json index c896d2d25e..cc85e8b790 100644 --- a/control/beaconing/testdata/big/ASff00_0_121.json +++ b/control/beaconing/testdata/big/ASff00_0_121.json @@ -6,17 +6,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_121-1": { - "addr": "127.0.0.84:31028" + "addr": "127.0.0.100:31030" } }, "discovery_service": { "cs1-ff00_0_121-1": { - "addr": "127.0.0.84:31028" + "addr": "127.0.0.100:31030" } }, "border_routers": { "br1-ff00_0_121-1": { - "internal_addr": "127.0.0.81:31030", + "internal_addr": "127.0.0.97:31032", "interfaces": { "1512": { "underlay": { @@ -30,12 +30,12 @@ } }, "br1-ff00_0_121-2": { - "internal_addr": "127.0.0.82:31032", + "internal_addr": "127.0.0.98:31034", "interfaces": { "1538": { "underlay": { - "local": "127.0.0.22:50000", - "remote": "127.0.0.23:50000" + "local": "127.0.0.24:50000", + "remote": "127.0.0.25:50000" }, "isd_as": "1-ff00:0:123", "link_to": "child", @@ -44,12 +44,12 @@ } }, "br1-ff00_0_121-3": { - "internal_addr": "127.0.0.83:31034", + "internal_addr": "127.0.0.99:31036", "interfaces": { "1518": { "underlay": { - "local": "127.0.0.24:50000", - "remote": "127.0.0.25:50000" + "local": "127.0.0.26:50000", + "remote": "127.0.0.27:50000" }, "isd_as": "1-ff00:0:122", "link_to": "peer", diff --git a/control/beaconing/testdata/big/ASff00_0_122.json b/control/beaconing/testdata/big/ASff00_0_122.json index a845293b6e..f01a874eac 100644 --- a/control/beaconing/testdata/big/ASff00_0_122.json +++ b/control/beaconing/testdata/big/ASff00_0_122.json @@ -6,17 +6,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_122-1": { - "addr": "127.0.0.101:31036" + "addr": "127.0.0.117:31038" } }, "discovery_service": { "cs1-ff00_0_122-1": { - "addr": "127.0.0.101:31036" + "addr": "127.0.0.117:31038" } }, "border_routers": { "br1-ff00_0_122-1": { - "internal_addr": "127.0.0.97:31038", + "internal_addr": "127.0.0.113:31040", "interfaces": { "1812": { "underlay": { @@ -30,12 +30,12 @@ } }, "br1-ff00_0_122-2": { - "internal_addr": "127.0.0.98:31040", + "internal_addr": "127.0.0.114:31042", "interfaces": { "1815": { "underlay": { - "local": "127.0.0.25:50000", - "remote": "127.0.0.24:50000" + "local": "127.0.0.27:50000", + "remote": "127.0.0.26:50000" }, "isd_as": "1-ff00:0:121", "link_to": "peer", @@ -45,12 +45,12 @@ } }, "br1-ff00_0_122-3": { - "internal_addr": "127.0.0.99:31042", + "internal_addr": "127.0.0.115:31044", "interfaces": { "1836": { "underlay": { - "local": "127.0.0.26:50000", - "remote": "127.0.0.27:50000" + "local": "127.0.0.28:50000", + "remote": "127.0.0.29:50000" }, "isd_as": "3-ff00:0:310", "link_to": "peer", @@ -60,12 +60,12 @@ } }, "br1-ff00_0_122-4": { - "internal_addr": "127.0.0.100:31044", + "internal_addr": "127.0.0.116:31046", "interfaces": { "1839": { "underlay": { - "local": "127.0.0.28:50000", - "remote": "127.0.0.29:50000" + "local": "127.0.0.30:50000", + "remote": "127.0.0.31:50000" }, "isd_as": "3-ff00:0:311", "link_to": "peer", diff --git a/control/beaconing/testdata/big/ASff00_0_123.json b/control/beaconing/testdata/big/ASff00_0_123.json index 8b1402f94a..771cd0ff74 100644 --- a/control/beaconing/testdata/big/ASff00_0_123.json +++ b/control/beaconing/testdata/big/ASff00_0_123.json @@ -6,22 +6,22 @@ "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_123-1": { - "addr": "127.0.0.92:31046" + "addr": "127.0.0.108:31048" } }, "discovery_service": { "cs1-ff00_0_123-1": { - "addr": "127.0.0.92:31046" + "addr": "127.0.0.108:31048" } }, "border_routers": { "br1-ff00_0_123-1": { - "internal_addr": "127.0.0.89:31048", + "internal_addr": "127.0.0.105:31050", "interfaces": { "3815": { "underlay": { - "local": "127.0.0.23:50000", - "remote": "127.0.0.22:50000" + "local": "127.0.0.25:50000", + "remote": "127.0.0.24:50000" }, "isd_as": "1-ff00:0:121", "link_to": "parent", @@ -30,12 +30,12 @@ } }, "br1-ff00_0_123-2": { - "internal_addr": "127.0.0.90:31050", + "internal_addr": "127.0.0.106:31052", "interfaces": { "3840": { "underlay": { - "local": "127.0.0.30:50000", - "remote": "127.0.0.31:50000" + "local": "127.0.0.32:50000", + "remote": "127.0.0.33:50000" }, "isd_as": "4-ff00:0:410", "link_to": "peer", @@ -45,12 +45,12 @@ } }, "br1-ff00_0_123-3": { - "internal_addr": "127.0.0.91:31052", + "internal_addr": "127.0.0.107:31054", "interfaces": { "3841": { "underlay": { - "local": "127.0.0.32:50000", - "remote": "127.0.0.33:50000" + "local": "127.0.0.34:50000", + "remote": "127.0.0.35:50000" }, "isd_as": "4-ff00:0:411", "link_to": "peer", diff --git a/control/beaconing/testdata/big/ASff00_0_210.json b/control/beaconing/testdata/big/ASff00_0_210.json index a0db1a201d..40da5c20b0 100644 --- a/control/beaconing/testdata/big/ASff00_0_210.json +++ b/control/beaconing/testdata/big/ASff00_0_210.json @@ -8,17 +8,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs2-ff00_0_210-1": { - "addr": "127.0.0.116:31054" + "addr": "127.0.0.133:31056" } }, "discovery_service": { "cs2-ff00_0_210-1": { - "addr": "127.0.0.116:31054" + "addr": "127.0.0.133:31056" } }, "border_routers": { "br2-ff00_0_210-1": { - "internal_addr": "127.0.0.113:31056", + "internal_addr": "127.0.0.129:31058", "interfaces": { "2111": { "underlay": { @@ -32,7 +32,7 @@ } }, "br2-ff00_0_210-2": { - "internal_addr": "127.0.0.114:31058", + "internal_addr": "127.0.0.130:31060", "interfaces": { "2120": { "underlay": { @@ -47,18 +47,32 @@ } }, "br2-ff00_0_210-3": { - "internal_addr": "127.0.0.115:31060", + "internal_addr": "127.0.0.131:31062", "interfaces": { "2135": { "underlay": { - "local": "127.0.0.34:50000", - "remote": "127.0.0.35:50000" + "local": "127.0.0.36:50000", + "remote": "127.0.0.37:50000" }, "isd_as": "2-ff00:0:211", "link_to": "child", "mtu": 1472 } } + }, + "br2-ff00_0_210-4": { + "internal_addr": "127.0.0.132:31064", + "interfaces": { + "2142": { + "underlay": { + "local": "127.0.0.38:50000", + "remote": "127.0.0.39:50000" + }, + "isd_as": "6-ff00:0:610", + "link_to": "core", + "mtu": 1472 + } + } } } } diff --git a/control/beaconing/testdata/big/ASff00_0_211.json b/control/beaconing/testdata/big/ASff00_0_211.json index 29a96874d7..bbc4832c55 100644 --- a/control/beaconing/testdata/big/ASff00_0_211.json +++ b/control/beaconing/testdata/big/ASff00_0_211.json @@ -6,17 +6,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs2-ff00_0_211-1": { - "addr": "127.0.0.123:31062" + "addr": "127.0.0.147:31066" } }, "discovery_service": { "cs2-ff00_0_211-1": { - "addr": "127.0.0.123:31062" + "addr": "127.0.0.147:31066" } }, "border_routers": { "br2-ff00_0_211-1": { - "internal_addr": "127.0.0.121:31064", + "internal_addr": "127.0.0.145:31068", "interfaces": { "3520": { "underlay": { @@ -31,12 +31,12 @@ } }, "br2-ff00_0_211-2": { - "internal_addr": "127.0.0.122:31066", + "internal_addr": "127.0.0.146:31070", "interfaces": { "3521": { "underlay": { - "local": "127.0.0.35:50000", - "remote": "127.0.0.34:50000" + "local": "127.0.0.37:50000", + "remote": "127.0.0.36:50000" }, "isd_as": "2-ff00:0:210", "link_to": "parent", diff --git a/control/beaconing/testdata/big/ASff00_0_310.json b/control/beaconing/testdata/big/ASff00_0_310.json index 48d1c2fb09..9cf26d17f6 100644 --- a/control/beaconing/testdata/big/ASff00_0_310.json +++ b/control/beaconing/testdata/big/ASff00_0_310.json @@ -8,17 +8,17 @@ "dispatched_ports": "31000-32767", "control_service": { "cs3-ff00_0_310-1": { - "addr": "127.0.0.133:31068" + "addr": "127.0.0.165:31072" } }, "discovery_service": { "cs3-ff00_0_310-1": { - "addr": "127.0.0.133:31068" + "addr": "127.0.0.165:31072" } }, "border_routers": { "br3-ff00_0_310-1": { - "internal_addr": "127.0.0.129:31070", + "internal_addr": "127.0.0.161:31074", "interfaces": { "3612": { "underlay": { @@ -32,12 +32,12 @@ } }, "br3-ff00_0_310-2": { - "internal_addr": "127.0.0.130:31072", + "internal_addr": "127.0.0.162:31076", "interfaces": { "3618": { "underlay": { - "local": "127.0.0.27:50000", - "remote": "127.0.0.26:50000" + "local": "127.0.0.29:50000", + "remote": "127.0.0.28:50000" }, "isd_as": "1-ff00:0:122", "link_to": "peer", @@ -47,12 +47,12 @@ } }, "br3-ff00_0_310-3": { - "internal_addr": "127.0.0.131:31074", + "internal_addr": "127.0.0.163:31078", "interfaces": { "3640": { "underlay": { - "local": "127.0.0.36:50000", - "remote": "127.0.0.37:50000" + "local": "127.0.0.40:50000", + "remote": "127.0.0.41:50000" }, "isd_as": "4-ff00:0:410", "link_to": "core", @@ -61,12 +61,12 @@ } }, "br3-ff00_0_310-4": { - "internal_addr": "127.0.0.132:31076", + "internal_addr": "127.0.0.164:31080", "interfaces": { "3639": { "underlay": { - "local": "127.0.0.38:50000", - "remote": "127.0.0.39:50000" + "local": "127.0.0.42:50000", + "remote": "127.0.0.43:50000" }, "isd_as": "3-ff00:0:311", "link_to": "child", diff --git a/control/beaconing/testdata/big/ASff00_0_311.json b/control/beaconing/testdata/big/ASff00_0_311.json index 164dd3dc7f..73e7910ee5 100644 --- a/control/beaconing/testdata/big/ASff00_0_311.json +++ b/control/beaconing/testdata/big/ASff00_0_311.json @@ -6,22 +6,22 @@ "dispatched_ports": "31000-32767", "control_service": { "cs3-ff00_0_311-1": { - "addr": "127.0.0.147:31078" + "addr": "127.0.0.155:31082" } }, "discovery_service": { "cs3-ff00_0_311-1": { - "addr": "127.0.0.147:31078" + "addr": "127.0.0.155:31082" } }, "border_routers": { "br3-ff00_0_311-1": { - "internal_addr": "127.0.0.145:31080", + "internal_addr": "127.0.0.153:31084", "interfaces": { "3918": { "underlay": { - "local": "127.0.0.29:50000", - "remote": "127.0.0.28:50000" + "local": "127.0.0.31:50000", + "remote": "127.0.0.30:50000" }, "isd_as": "1-ff00:0:122", "link_to": "peer", @@ -31,12 +31,12 @@ } }, "br3-ff00_0_311-2": { - "internal_addr": "127.0.0.146:31082", + "internal_addr": "127.0.0.154:31086", "interfaces": { "3936": { "underlay": { - "local": "127.0.0.39:50000", - "remote": "127.0.0.38:50000" + "local": "127.0.0.43:50000", + "remote": "127.0.0.42:50000" }, "isd_as": "3-ff00:0:310", "link_to": "parent", diff --git a/control/beaconing/testdata/big/ASff00_0_410.json b/control/beaconing/testdata/big/ASff00_0_410.json index 49d7f3195d..fad6c65b5f 100644 --- a/control/beaconing/testdata/big/ASff00_0_410.json +++ b/control/beaconing/testdata/big/ASff00_0_410.json @@ -8,22 +8,37 @@ "dispatched_ports": "31000-32767", "control_service": { "cs4-ff00_0_410-1": { - "addr": "127.0.0.156:31084" + "addr": "127.0.0.181:31088" } }, "discovery_service": { "cs4-ff00_0_410-1": { - "addr": "127.0.0.156:31084" + "addr": "127.0.0.181:31088" } }, "border_routers": { "br4-ff00_0_410-1": { - "internal_addr": "127.0.0.153:31086", + "internal_addr": "127.0.0.177:31090", + "interfaces": { + "4012": { + "underlay": { + "local": "127.0.0.21:50000", + "remote": "127.0.0.20:50000" + }, + "isd_as": "1-ff00:0:120", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 1240 + } + } + }, + "br4-ff00_0_410-2": { + "internal_addr": "127.0.0.178:31092", "interfaces": { "4038": { "underlay": { - "local": "127.0.0.31:50000", - "remote": "127.0.0.30:50000" + "local": "127.0.0.33:50000", + "remote": "127.0.0.32:50000" }, "isd_as": "1-ff00:0:123", "link_to": "peer", @@ -32,13 +47,13 @@ } } }, - "br4-ff00_0_410-2": { - "internal_addr": "127.0.0.154:31088", + "br4-ff00_0_410-3": { + "internal_addr": "127.0.0.179:31094", "interfaces": { "4036": { "underlay": { - "local": "127.0.0.37:50000", - "remote": "127.0.0.36:50000" + "local": "127.0.0.41:50000", + "remote": "127.0.0.40:50000" }, "isd_as": "3-ff00:0:310", "link_to": "core", @@ -46,13 +61,13 @@ } } }, - "br4-ff00_0_410-3": { - "internal_addr": "127.0.0.155:31090", + "br4-ff00_0_410-4": { + "internal_addr": "127.0.0.180:31096", "interfaces": { "4041": { "underlay": { - "local": "127.0.0.40:50000", - "remote": "127.0.0.41:50000" + "local": "127.0.0.44:50000", + "remote": "127.0.0.45:50000" }, "isd_as": "4-ff00:0:411", "link_to": "child", diff --git a/control/beaconing/testdata/big/ASff00_0_411.json b/control/beaconing/testdata/big/ASff00_0_411.json index 79a83bf1c7..7881104d8a 100644 --- a/control/beaconing/testdata/big/ASff00_0_411.json +++ b/control/beaconing/testdata/big/ASff00_0_411.json @@ -6,22 +6,22 @@ "dispatched_ports": "31000-32767", "control_service": { "cs4-ff00_0_411-1": { - "addr": "127.0.0.163:31092" + "addr": "127.0.0.195:31098" } }, "discovery_service": { "cs4-ff00_0_411-1": { - "addr": "127.0.0.163:31092" + "addr": "127.0.0.195:31098" } }, "border_routers": { "br4-ff00_0_411-1": { - "internal_addr": "127.0.0.161:31094", + "internal_addr": "127.0.0.193:31100", "interfaces": { "4138": { "underlay": { - "local": "127.0.0.33:50000", - "remote": "127.0.0.32:50000" + "local": "127.0.0.35:50000", + "remote": "127.0.0.34:50000" }, "isd_as": "1-ff00:0:123", "link_to": "peer", @@ -31,12 +31,12 @@ } }, "br4-ff00_0_411-2": { - "internal_addr": "127.0.0.162:31096", + "internal_addr": "127.0.0.194:31102", "interfaces": { "4140": { "underlay": { - "local": "127.0.0.41:50000", - "remote": "127.0.0.40:50000" + "local": "127.0.0.45:50000", + "remote": "127.0.0.44:50000" }, "isd_as": "4-ff00:0:410", "link_to": "parent", diff --git a/control/beaconing/testdata/big/ASff00_0_510.json b/control/beaconing/testdata/big/ASff00_0_510.json index 723425c74e..e5edf8a92e 100644 --- a/control/beaconing/testdata/big/ASff00_0_510.json +++ b/control/beaconing/testdata/big/ASff00_0_510.json @@ -8,22 +8,22 @@ "dispatched_ports": "31000-32767", "control_service": { "cs5-ff00_0_510-1": { - "addr": "127.0.0.170:31098" + "addr": "127.0.0.202:31104" } }, "discovery_service": { "cs5-ff00_0_510-1": { - "addr": "127.0.0.170:31098" + "addr": "127.0.0.202:31104" } }, "border_routers": { "br5-ff00_0_510-1": { - "internal_addr": "127.0.0.169:31100", + "internal_addr": "127.0.0.201:31106", "interfaces": { "3712": { "underlay": { - "local": "127.0.0.21:50000", - "remote": "127.0.0.20:50000" + "local": "127.0.0.23:50000", + "remote": "127.0.0.22:50000" }, "isd_as": "1-ff00:0:120", "link_to": "core", diff --git a/control/beaconing/testdata/big/ASff00_0_610.json b/control/beaconing/testdata/big/ASff00_0_610.json new file mode 100644 index 0000000000..f29635a8b6 --- /dev/null +++ b/control/beaconing/testdata/big/ASff00_0_610.json @@ -0,0 +1,77 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "6-ff00:0:610", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs6-ff00_0_610-1": { + "addr": "127.0.0.213:31108" + } + }, + "discovery_service": { + "cs6-ff00_0_610-1": { + "addr": "127.0.0.213:31108" + } + }, + "border_routers": { + "br6-ff00_0_610-1": { + "internal_addr": "127.0.0.209:31110", + "interfaces": { + "4221": { + "underlay": { + "local": "127.0.0.39:50000", + "remote": "127.0.0.38:50000" + }, + "isd_as": "2-ff00:0:210", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br6-ff00_0_610-2": { + "internal_addr": "127.0.0.210:31112", + "interfaces": { + "4245": { + "underlay": { + "local": "127.0.0.46:50000", + "remote": "127.0.0.47:50000" + }, + "isd_as": "6-ff00:0:620", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br6-ff00_0_610-3": { + "internal_addr": "127.0.0.211:31114", + "interfaces": { + "4243": { + "underlay": { + "local": "127.0.0.48:50000", + "remote": "127.0.0.49:50000" + }, + "isd_as": "6-ff00:0:611", + "link_to": "child", + "mtu": 1472 + } + } + }, + "br6-ff00_0_610-4": { + "internal_addr": "127.0.0.212:31116", + "interfaces": { + "4244": { + "underlay": { + "local": "127.0.0.50:50000", + "remote": "127.0.0.51:50000" + }, + "isd_as": "6-ff00:0:612", + "link_to": "child", + "mtu": 1472 + } + } + } + } +} diff --git a/control/beaconing/testdata/big/ASff00_0_611.json b/control/beaconing/testdata/big/ASff00_0_611.json new file mode 100644 index 0000000000..de1c8e51b8 --- /dev/null +++ b/control/beaconing/testdata/big/ASff00_0_611.json @@ -0,0 +1,78 @@ +{ + "attributes": [], + "isd_as": "6-ff00:0:611", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs6-ff00_0_611-1": { + "addr": "127.0.0.229:31128" + } + }, + "discovery_service": { + "cs6-ff00_0_611-1": { + "addr": "127.0.0.229:31128" + } + }, + "border_routers": { + "br6-ff00_0_611-1": { + "internal_addr": "127.0.0.225:31130", + "interfaces": { + "4342": { + "underlay": { + "local": "127.0.0.49:50000", + "remote": "127.0.0.48:50000" + }, + "isd_as": "6-ff00:0:610", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br6-ff00_0_611-2": { + "internal_addr": "127.0.0.226:31132", + "interfaces": { + "4345": { + "underlay": { + "local": "127.0.0.52:50000", + "remote": "127.0.0.53:50000" + }, + "isd_as": "6-ff00:0:620", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4543 + } + } + }, + "br6-ff00_0_611-3": { + "internal_addr": "127.0.0.227:31134", + "interfaces": { + "4346": { + "underlay": { + "local": "127.0.0.54:50000", + "remote": "127.0.0.55:50000" + }, + "isd_as": "6-ff00:0:621", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4643 + } + } + }, + "br6-ff00_0_611-4": { + "internal_addr": "127.0.0.228:31136", + "interfaces": { + "4344": { + "underlay": { + "local": "127.0.0.56:50000", + "remote": "127.0.0.57:50000" + }, + "isd_as": "6-ff00:0:612", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4443 + } + } + } + } +} diff --git a/control/beaconing/testdata/big/ASff00_0_612.json b/control/beaconing/testdata/big/ASff00_0_612.json new file mode 100644 index 0000000000..11da1faf99 --- /dev/null +++ b/control/beaconing/testdata/big/ASff00_0_612.json @@ -0,0 +1,48 @@ +{ + "attributes": [], + "isd_as": "6-ff00:0:612", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs6-ff00_0_612-1": { + "addr": "127.0.0.243:31138" + } + }, + "discovery_service": { + "cs6-ff00_0_612-1": { + "addr": "127.0.0.243:31138" + } + }, + "border_routers": { + "br6-ff00_0_612-1": { + "internal_addr": "127.0.0.241:31140", + "interfaces": { + "4442": { + "underlay": { + "local": "127.0.0.51:50000", + "remote": "127.0.0.50:50000" + }, + "isd_as": "6-ff00:0:610", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br6-ff00_0_612-2": { + "internal_addr": "127.0.0.242:31142", + "interfaces": { + "4443": { + "underlay": { + "local": "127.0.0.57:50000", + "remote": "127.0.0.56:50000" + }, + "isd_as": "6-ff00:0:611", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4344 + } + } + } + } +} diff --git a/control/beaconing/testdata/big/ASff00_0_620.json b/control/beaconing/testdata/big/ASff00_0_620.json new file mode 100644 index 0000000000..f0231cc466 --- /dev/null +++ b/control/beaconing/testdata/big/ASff00_0_620.json @@ -0,0 +1,78 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "6-ff00:0:620", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs6-ff00_0_620-1": { + "addr": "127.0.1.5:31118" + } + }, + "discovery_service": { + "cs6-ff00_0_620-1": { + "addr": "127.0.1.5:31118" + } + }, + "border_routers": { + "br6-ff00_0_620-1": { + "internal_addr": "127.0.1.1:31120", + "interfaces": { + "4542": { + "underlay": { + "local": "127.0.0.47:50000", + "remote": "127.0.0.46:50000" + }, + "isd_as": "6-ff00:0:610", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br6-ff00_0_620-2": { + "internal_addr": "127.0.1.2:31122", + "interfaces": { + "4546": { + "underlay": { + "local": "127.0.0.58:50000", + "remote": "127.0.0.59:50000" + }, + "isd_as": "6-ff00:0:621", + "link_to": "child", + "mtu": 1472 + } + } + }, + "br6-ff00_0_620-3": { + "internal_addr": "127.0.1.3:31124", + "interfaces": { + "4547": { + "underlay": { + "local": "127.0.0.60:50000", + "remote": "127.0.0.61:50000" + }, + "isd_as": "6-ff00:0:622", + "link_to": "child", + "mtu": 1472 + } + } + }, + "br6-ff00_0_620-4": { + "internal_addr": "127.0.1.4:31126", + "interfaces": { + "4543": { + "underlay": { + "local": "127.0.0.53:50000", + "remote": "127.0.0.52:50000" + }, + "isd_as": "6-ff00:0:611", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4345 + } + } + } + } +} diff --git a/control/beaconing/testdata/big/ASff00_0_621.json b/control/beaconing/testdata/big/ASff00_0_621.json new file mode 100644 index 0000000000..e0c379de3a --- /dev/null +++ b/control/beaconing/testdata/big/ASff00_0_621.json @@ -0,0 +1,48 @@ +{ + "attributes": [], + "isd_as": "6-ff00:0:621", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs6-ff00_0_621-1": { + "addr": "127.0.0.251:31144" + } + }, + "discovery_service": { + "cs6-ff00_0_621-1": { + "addr": "127.0.0.251:31144" + } + }, + "border_routers": { + "br6-ff00_0_621-1": { + "internal_addr": "127.0.0.249:31146", + "interfaces": { + "4645": { + "underlay": { + "local": "127.0.0.59:50000", + "remote": "127.0.0.58:50000" + }, + "isd_as": "6-ff00:0:620", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br6-ff00_0_621-2": { + "internal_addr": "127.0.0.250:31148", + "interfaces": { + "4643": { + "underlay": { + "local": "127.0.0.55:50000", + "remote": "127.0.0.54:50000" + }, + "isd_as": "6-ff00:0:611", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 4346 + } + } + } + } +} diff --git a/control/beaconing/testdata/big/ASff00_0_622.json b/control/beaconing/testdata/big/ASff00_0_622.json new file mode 100644 index 0000000000..f4441d25dc --- /dev/null +++ b/control/beaconing/testdata/big/ASff00_0_622.json @@ -0,0 +1,33 @@ +{ + "attributes": [], + "isd_as": "6-ff00:0:622", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs6-ff00_0_622-1": { + "addr": "127.0.1.18:31150" + } + }, + "discovery_service": { + "cs6-ff00_0_622-1": { + "addr": "127.0.1.18:31150" + } + }, + "border_routers": { + "br6-ff00_0_622-1": { + "internal_addr": "127.0.1.17:31152", + "interfaces": { + "4745": { + "underlay": { + "local": "127.0.0.61:50000", + "remote": "127.0.0.60:50000" + }, + "isd_as": "6-ff00:0:620", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/control/beaconing/writer.go b/control/beaconing/writer.go index 1857f98f96..e6ff38fd87 100644 --- a/control/beaconing/writer.go +++ b/control/beaconing/writer.go @@ -17,7 +17,9 @@ package beaconing import ( "context" + "crypto/rand" "errors" + "math/big" "net" "strconv" "sync" @@ -103,6 +105,9 @@ type WriteScheduler struct { // Write is used to write the segments once the scheduling determines it is // time to write. Writer Writer + // Core indicates whether this AS is a core AS. When true and peering + // interfaces exist, one-hop segments with peer entries are created. + Core bool // Tick is mutable. It's used to determine when to call write. Tick Tick @@ -135,6 +140,24 @@ func (r *WriteScheduler) run(ctx context.Context) error { return err } peers := sortedIntfs(r.Intfs, topology.Peer) + // Create one-hop segments for core ASes with peering links. + // These segments have one AS entry with ConsIngress=0, ConsEgress=0, plus + // peer entries for each peering interface. + if (r.Type == seg.TypeDown || r.Type == seg.TypeUp) && r.Core && len(peers) != 0 { + segID, err := rand.Int(rand.Reader, big.NewInt(1<<16)) + if err != nil { + return err + } + b, err := seg.CreateSegment(time.Now(), uint16(segID.Uint64())) + if err != nil { + return err + } + segments[beacon.DefaultGroup] = append(segments[beacon.DefaultGroup], beacon.Beacon{ + Segment: b, + InIfID: 0, + }) + } + stats, err := r.Writer.Write(ctx, segments, peers) if err != nil { return err @@ -226,7 +249,15 @@ func (r *LocalWriter) RegisterSegments( logBeacons := make(map[string]beacon.Beacon) var toRegister []*seg.Meta for _, b := range beacons { - toRegister = append(toRegister, &seg.Meta{Type: r.Type, Segment: b.Segment}) + segType := r.Type + // One-hop segments (for core AS peering) are always stored as Down + // segments. The combinator uses them on the destination side of peering + // shortcuts, where down segments provide the edge to the destination. + // Source-side peering edges come from core segment processing instead. + if isOneHopSegment(b.Segment) { + segType = seg.TypeDown + } + toRegister = append(toRegister, &seg.Meta{Type: segType, Segment: b.Segment}) logBeacons[b.Segment.GetLoggingID()] = b } stats, err := r.Store.StoreSegs(ctx, toRegister) @@ -462,7 +493,7 @@ func (w *GroupWriter) processSegments( processedGroup := make([]beacon.Beacon, 0, len(beacons)) for i, b := range beacons { // If the beacon does not have a valid interface ID, skip it. - if w.Intfs != nil && w.Intfs.Get(b.InIfID) == nil { + if w.Intfs != nil && w.Intfs.Get(b.InIfID) == nil && b.InIfID != 0 { continue } // Try to terminate the segment if an extender is configured. @@ -556,3 +587,14 @@ func (w *GroupWriter) Write( } return writeStats, nil } + +// isOneHopSegment returns true if the segment is a one-hop segment, i.e., a segment +// with a single AS entry where both ingress and egress interfaces are 0. These segments +// are used to represent core ASes with peering links for path combination purposes. +func isOneHopSegment(s *seg.PathSegment) bool { + if len(s.ASEntries) != 1 { + return false + } + hf := s.ASEntries[0].HopEntry.HopField + return hf.ConsIngress == 0 && hf.ConsEgress == 0 +} diff --git a/control/beaconing/writer_test.go b/control/beaconing/writer_test.go index d9d5a56883..858c76594d 100644 --- a/control/beaconing/writer_test.go +++ b/control/beaconing/writer_test.go @@ -255,7 +255,7 @@ func TestRegistrarRun(t *testing.T) { g := graph.NewDefaultGraph(mctrl) segProvider.EXPECT().SegmentsToRegister(gomock.Any(), test.segType).DoAndReturn( func(_, _ any) (beacon.GroupedBeacons, error) { - res := make([]beacon.Beacon, len(test.beacons)) + res := make([]beacon.Beacon, 0, len(test.beacons)) for _, desc := range test.beacons { res = append(res, testBeacon(g, desc)) } diff --git a/control/cmd/control/main.go b/control/cmd/control/main.go index 13054185db..a6ca237c0e 100644 --- a/control/cmd/control/main.go +++ b/control/cmd/control/main.go @@ -281,7 +281,7 @@ func realMain(ctx context.Context) error { if err != nil { return serrors.Wrap("loading policies", err) } - beaconStore, isdLoopAllowed, err := createBeaconStore( + beaconStore, isdLoopAllowed, transitTrafficAllowed, err := createBeaconStore( policies, beaconDB, trust.FetchingProvider{ @@ -426,6 +426,7 @@ func realMain(ctx context.Context) error { Inspector: inspector, PathDB: pathDB, }, + PathDB: pathDB, }, RevCache: revCache, Requests: libmetrics.NewPromCounter(metrics.SegmentLookupRequestsTotal), @@ -994,6 +995,7 @@ func realMain(ctx context.Context) error { DRKeyEpochInterval: epochDuration, HiddenPathRegistrationCfg: hpWriterCfg, AllowIsdLoop: isdLoopAllowed, + AllowTransitTraffic: transitTrafficAllowed, EPIC: globalCfg.BS.EPIC, } @@ -1122,7 +1124,7 @@ func createBeaconStore( policies loadedPolicies, db storage.BeaconDB, provider beacon.ChainProvider, -) (cs.Store, bool, error) { +) (cs.Store, bool, bool, error) { switch { case policies.CorePolicies != nil: policies := policies.CorePolicies @@ -1130,16 +1132,22 @@ func createBeaconStore( store, err := beacon.NewCoreBeaconStore(*policies, db, beacon.WithSelectionAlgorithm(selectionAlgo), ) - return store, *policies.Prop.Filter.AllowIsdLoop, err + return store, + *policies.Prop.Filter.AllowIsdLoop, + *policies.Prop.Filter.AllowTransitTraffic, + err case policies.NonCorePolicies != nil: policies := policies.NonCorePolicies selectionAlgo := beacon.NewChainsAvailableAlgo(provider, beacon.DefaultSelectionAlgorithm()) store, err := beacon.NewBeaconStore(*policies, db, beacon.WithSelectionAlgorithm(selectionAlgo), ) - return store, *policies.Prop.Filter.AllowIsdLoop, err + return store, + *policies.Prop.Filter.AllowIsdLoop, + *policies.Prop.Filter.AllowTransitTraffic, + err default: - return nil, false, serrors.New("no policies loaded") + return nil, false, false, serrors.New("no policies loaded") } } diff --git a/control/segreq/authoritative.go b/control/segreq/authoritative.go index 3e1a91dc70..905fa35cae 100644 --- a/control/segreq/authoritative.go +++ b/control/segreq/authoritative.go @@ -37,6 +37,14 @@ type AuthoritativeLookup struct { func (a AuthoritativeLookup) LookupSegments( ctx context.Context, src, dst addr.IA, ) (segfetcher.Segments, error) { + + // Special case: src == dst requests one-hop segments for core AS peering. + // One-hop segments are stored as Down type and provide peer entries that + // the combinator uses on the destination side of peering shortcuts. + if src == dst && src == a.LocalIA { + return getOneHopSegments(ctx, a.PathDB, a.LocalIA) + } + segType, err := a.classify(ctx, src, dst) if err != nil { return nil, err @@ -60,6 +68,9 @@ func (a AuthoritativeLookup) classify(ctx context.Context, case src != a.LocalIA: return 0, serrors.JoinNoStack(segfetcher.ErrInvalidRequest, nil, "src", src, "dst", dst, "reason", "src must be local AS") + case src == dst: + // need to justify why + return seg.TypeDown, nil case dst.ISD() == 0: return 0, serrors.JoinNoStack(segfetcher.ErrInvalidRequest, nil, @@ -112,3 +123,20 @@ func getDownSegments(ctx context.Context, pathDB pathdb.DB, } return res.SegMetas(), nil } + +// getOneHopSegments loads one-hop segments (segments that start and end at the same AS) +// for core AS peering support. One-hop segments are Down segments that carry peer entries +// for the combinator to build peering shortcuts. +func getOneHopSegments(ctx context.Context, pathDB pathdb.DB, + localIA addr.IA, +) (segfetcher.Segments, error) { + res, err := pathDB.Get(ctx, &query.Params{ + StartsAt: []addr.IA{localIA}, + EndsAt: []addr.IA{localIA}, + SegTypes: []seg.Type{seg.TypeDown}, + }) + if err != nil { + return segfetcher.Segments{}, err + } + return res.SegMetas(), nil +} diff --git a/control/segreq/fetcher.go b/control/segreq/fetcher.go index 58292b3948..0eacf5415a 100644 --- a/control/segreq/fetcher.go +++ b/control/segreq/fetcher.go @@ -169,7 +169,6 @@ func (p *dstProvider) Dst(ctx context.Context, req segfetcher.Request) (net.Addr // The request is directed to the AS at the start of the requested segment: dst := req.Src - var path snet.Path switch req.SegType { case seg.TypeCore: // fast/simple path for core segment requests (only up segment required). @@ -184,6 +183,10 @@ func (p *dstProvider) Dst(ctx context.Context, req segfetcher.Request) (net.Addr } return up, nil case seg.TypeDown: + // Special case: src == dst is a one-hop segment request (core AS peering). + if req.Src == req.Dst { + return p.oneHopPath(ctx, dst) + } // Select a random path (just like we choose a random segment above) // Avoids potentially being stuck with a broken but not revoked path; // allows clients to retry with possibly different path in case of failure. @@ -194,15 +197,11 @@ func (p *dstProvider) Dst(ctx context.Context, req segfetcher.Request) (net.Addr if len(paths) == 0 { return nil, segfetcher.ErrNotReachable } - path = paths[rand.IntN(len(paths))] - addr := addrutil.ExtractDestinationServiceAddress(addr.SvcCS, path) - return addr, nil + path := paths[rand.IntN(len(paths))] + return addrutil.ExtractDestinationServiceAddress(addr.SvcCS, path), nil default: - panic( - "unsupported segment type for request forwarding: " + - "up segment should have been resolved locally: " + - req.SegType.String(), - ) + return nil, serrors.JoinNoStack(segfetcher.ErrNotReachable, nil, + "reason", "unsupported segment type", "type", req.SegType.String()) } } @@ -214,3 +213,40 @@ func (p *dstProvider) upPath(ctx context.Context, dst addr.IA) (net.Addr, error) SegTypes: []seg.Type{seg.TypeUp}, }) } + +// corePath finds a path to the destination using core segments only. +// Used for one-hop segment requests to remote core ASes to avoid recursion. +func (p *dstProvider) corePath(ctx context.Context, dst addr.IA) (net.Addr, error) { + return p.segSelector.SelectSeg(ctx, &query.Params{ + StartsAt: []addr.IA{dst}, + EndsAt: []addr.IA{p.localIA}, + SegTypes: []seg.Type{seg.TypeCore}, + }) +} + +// oneHopPath finds a path to a core AS for fetching its one-hop segment. +// Tries simple paths first (core, up) to avoid recursion, then falls back +// to the router with SkipOneHopKey to prevent infinite recursion. +func (p *dstProvider) oneHopPath(ctx context.Context, dst addr.IA) (net.Addr, error) { + // Try simple paths first (avoids recursion) + if addr, err := p.corePath(ctx, dst); err == nil { + return addr, nil + } + if addr, err := p.upPath(ctx, dst); err == nil { + return addr, nil + } + + // Fall back to router with recursion prevention + ctx = context.WithValue(ctx, segfetcher.SkipOneHopKey, true) + paths, err := p.router.AllRoutes(ctx, dst) + if err != nil { + return nil, serrors.JoinNoStack(segfetcher.ErrNotReachable, err, + "reason", "no path to core for one-hop request") + } + if len(paths) == 0 { + return nil, serrors.JoinNoStack(segfetcher.ErrNotReachable, nil, + "reason", "no path to core for one-hop request") + } + path := paths[rand.IntN(len(paths))] + return addrutil.ExtractDestinationServiceAddress(addr.SvcCS, path), nil +} diff --git a/control/segreq/forwarder.go b/control/segreq/forwarder.go index 89c906ea4d..f70cba61d8 100644 --- a/control/segreq/forwarder.go +++ b/control/segreq/forwarder.go @@ -20,17 +20,19 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/private/serrors" seg "github.com/scionproto/scion/pkg/segment" + "github.com/scionproto/scion/private/pathdb" "github.com/scionproto/scion/private/segment/segfetcher" ) -// ForwardingLookup handles path segment lookup requests in a non-core AS. If -// segments are missing, the request is forwarded to the respective core ASes. -// It should only be used in a non-core AS. +// ForwardingLookup handles path segment lookup requests by forwarding to the +// responsible core ASes if segments are missing. It is used for internal (intra-AS) +// requests at all ASes, and for external requests at non-core ASes. type ForwardingLookup struct { LocalIA addr.IA CoreChecker CoreChecker Fetcher *segfetcher.Fetcher Expander WildcardExpander + PathDB pathdb.DB } // LookupSegments looks up the segments for the given request @@ -41,6 +43,12 @@ type ForwardingLookup struct { func (f ForwardingLookup) LookupSegments(ctx context.Context, src, dst addr.IA) (segfetcher.Segments, error) { + // Special case: src == dst == localIA is a request for local one-hop segments. + // One-hop segments are Down segments used for core AS peering. + if src == dst && src == f.LocalIA && f.PathDB != nil { + return getOneHopSegments(ctx, f.PathDB, f.LocalIA) + } + segType, err := f.classify(ctx, src, dst) if err != nil { return nil, err @@ -68,10 +76,22 @@ func (f ForwardingLookup) classify(ctx context.Context, "src", src, "dst", dst, "reason", "zero ISD src or dst") } + // Special case: src == dst is a request for one-hop segments (core AS peering). + // Treat as Down segment to forward to that AS's control service. + if src == dst { + return seg.TypeDown, nil + } if dst == f.LocalIA { - // this could be an otherwise valid request, but probably the requester switched Src and Dst - return 0, serrors.JoinNoStack(segfetcher.ErrInvalidRequest, nil, - "src", src, "dst", dst, "reason", "dst is local AS, confusion?") + isCore, err := f.CoreChecker.IsCore(ctx, dst) + if err != nil { + return 0, err + } + if !isCore { + // this could be an otherwise valid request, + // but probably the requester switched Src and Dst + return 0, serrors.JoinNoStack(segfetcher.ErrInvalidRequest, nil, + "src", src, "dst", dst, "reason", "dst is local AS, confusion?") + } } srcCore, err := f.CoreChecker.IsCore(ctx, src) diff --git a/control/tasks.go b/control/tasks.go index 2b87cd13e4..511e1fa243 100644 --- a/control/tasks.go +++ b/control/tasks.go @@ -36,6 +36,7 @@ import ( "github.com/scionproto/scion/private/pathdb" "github.com/scionproto/scion/private/periodic" "github.com/scionproto/scion/private/revcache" + "github.com/scionproto/scion/private/topology" "github.com/scionproto/scion/private/trust" ) @@ -78,7 +79,8 @@ type TasksConfig struct { // registration is used instead. HiddenPathRegistrationCfg *HiddenPathRegistrationCfg - AllowIsdLoop bool + AllowIsdLoop bool + AllowTransitTraffic bool EPIC bool @@ -188,6 +190,7 @@ func (t *TasksConfig) Propagator() *periodic.Runner { AllInterfaces: t.AllInterfaces, PropagationInterfaces: t.PropagationInterfaces, AllowIsdLoop: t.AllowIsdLoop, + AllowTransitTraffic: t.AllowTransitTraffic, Tick: beaconing.NewTick(t.PropagationInterval), } if t.Metrics != nil { @@ -201,7 +204,16 @@ func (t *TasksConfig) Propagator() *periodic.Runner { // SegmentWriters starts periodic segment registration tasks. func (t *TasksConfig) SegmentWriters() []*periodic.Runner { if t.Core { - return []*periodic.Runner{t.segmentWriter(beacon.RegPolicyTypeCore)} + // Core ASes register core segments. If the core AS has peering links, + // also start an Up-policy writer that creates one-hop segments for + // peering. The Up policy is used because it routes to the local + // registration plugin; the segments themselves are stored as Down type + // by the LocalWriter. + writers := []*periodic.Runner{t.segmentWriter(beacon.RegPolicyTypeCore)} + if hasPeeringInterfaces(t.AllInterfaces) { + writers = append(writers, t.segmentWriter(beacon.RegPolicyTypeUp)) + } + return writers } return []*periodic.Runner{ t.segmentWriter(beacon.RegPolicyTypeDown), @@ -217,6 +229,7 @@ func (t *TasksConfig) segmentWriter( panic("segment registrars not initialized, call InitPlugins first") } r := &beaconing.WriteScheduler{ + Core: t.Core, Provider: t.BeaconStore, Intfs: t.AllInterfaces, Type: policyType.SegmentType(), @@ -398,3 +411,14 @@ type Store interface { // MaxExpTime returns the segment maximum expiration time for the given policy. MaxExpTime(policyType beacon.PolicyType) uint8 } + +// hasPeeringInterfaces returns true if the interface set contains at least one +// peering interface. +func hasPeeringInterfaces(intfs *ifstate.Interfaces) bool { + for _, intf := range intfs.All() { + if intf.TopoInfo().LinkType == topology.Peer { + return true + } + } + return false +} diff --git a/pkg/experimental/hiddenpath/beaconwriter.go b/pkg/experimental/hiddenpath/beaconwriter.go index f60d68705d..a51e9d4289 100644 --- a/pkg/experimental/hiddenpath/beaconwriter.go +++ b/pkg/experimental/hiddenpath/beaconwriter.go @@ -86,7 +86,8 @@ func (w *BeaconWriter) Write( var wg sync.WaitGroup for _, b := range beacons[beacon.DefaultGroup] { - if w.Intfs.Get(b.InIfID) == nil { + // TODO: less hacky? + if w.Intfs.Get(b.InIfID) == nil && b.InIfID != 0 { logger.Error("Received beacon for non-existing interface", "interface", b.InIfID) metrics.CounterInc(w.InternalErrors) continue diff --git a/pkg/private/xtest/graph/big_gen.go b/pkg/private/xtest/graph/big_gen.go index cedfea4f0c..6b8b50a019 100644 --- a/pkg/private/xtest/graph/big_gen.go +++ b/pkg/private/xtest/graph/big_gen.go @@ -18,6 +18,12 @@ var BigGraphDescription = &Description{ "4-ff00:0:410", // X = 40 "4-ff00:0:411", // X = 41 "5-ff00:0:510", // X = 37 + "6-ff00:0:610", // X = 42 + "6-ff00:0:611", // X = 43 + "6-ff00:0:612", // X = 44 + "6-ff00:0:620", // X = 45 + "6-ff00:0:621", // X = 46 + "6-ff00:0:622", // X = 47 }, Edges: []EdgeDesc{ {Xia: "1-ff00:0:110", XifID: If_110_X_120_X, Yia: "1-ff00:0:120", YifID: If_120_X_110_X, Peer: false}, @@ -28,6 +34,7 @@ var BigGraphDescription = &Description{ {Xia: "1-ff00:0:120", XifID: If_120_X_121_X, Yia: "1-ff00:0:121", YifID: If_121_X_120_X, Peer: false}, {Xia: "1-ff00:0:120", XifID: If_120_X_122_X, Yia: "1-ff00:0:122", YifID: If_122_X_120_X, Peer: false}, {Xia: "1-ff00:0:120", XifID: If_120_X_310_X, Yia: "3-ff00:0:310", YifID: If_310_X_120_X, Peer: false}, + {Xia: "1-ff00:0:120", XifID: If_120_X_410_X, Yia: "4-ff00:0:410", YifID: If_410_X_120_X, Peer: true}, {Xia: "1-ff00:0:120", XifID: If_120_X_510_X, Yia: "5-ff00:0:510", YifID: If_510_X_120_X, Peer: false}, {Xia: "1-ff00:0:121", XifID: If_121_X_123_X, Yia: "1-ff00:0:123", YifID: If_123_X_121_X, Peer: false}, {Xia: "1-ff00:0:121", XifID: If_121_X_122_X, Yia: "1-ff00:0:122", YifID: If_122_X_121_X, Peer: true}, @@ -36,8 +43,17 @@ var BigGraphDescription = &Description{ {Xia: "1-ff00:0:123", XifID: If_123_X_410_X, Yia: "4-ff00:0:410", YifID: If_410_X_123_X, Peer: true}, {Xia: "1-ff00:0:123", XifID: If_123_X_411_X, Yia: "4-ff00:0:411", YifID: If_411_X_123_X, Peer: true}, {Xia: "2-ff00:0:210", XifID: If_210_X_211_X, Yia: "2-ff00:0:211", YifID: If_211_X_210_X, Peer: false}, + {Xia: "2-ff00:0:210", XifID: If_210_X_610_X, Yia: "6-ff00:0:610", YifID: If_610_X_210_X, Peer: false}, {Xia: "3-ff00:0:310", XifID: If_310_X_410_X, Yia: "4-ff00:0:410", YifID: If_410_X_310_X, Peer: false}, {Xia: "3-ff00:0:310", XifID: If_310_X_311_X, Yia: "3-ff00:0:311", YifID: If_311_X_310_X, Peer: false}, {Xia: "4-ff00:0:410", XifID: If_410_X_411_X, Yia: "4-ff00:0:411", YifID: If_411_X_410_X, Peer: false}, + {Xia: "6-ff00:0:610", XifID: If_610_X_620_X, Yia: "6-ff00:0:620", YifID: If_620_X_610_X, Peer: false}, + {Xia: "6-ff00:0:610", XifID: If_610_X_611_X, Yia: "6-ff00:0:611", YifID: If_611_X_610_X, Peer: false}, + {Xia: "6-ff00:0:610", XifID: If_610_X_612_X, Yia: "6-ff00:0:612", YifID: If_612_X_610_X, Peer: false}, + {Xia: "6-ff00:0:620", XifID: If_620_X_621_X, Yia: "6-ff00:0:621", YifID: If_621_X_620_X, Peer: false}, + {Xia: "6-ff00:0:620", XifID: If_620_X_622_X, Yia: "6-ff00:0:622", YifID: If_622_X_620_X, Peer: false}, + {Xia: "6-ff00:0:611", XifID: If_611_X_620_X, Yia: "6-ff00:0:620", YifID: If_620_X_611_X, Peer: true}, + {Xia: "6-ff00:0:611", XifID: If_611_X_621_X, Yia: "6-ff00:0:621", YifID: If_621_X_611_X, Peer: true}, + {Xia: "6-ff00:0:611", XifID: If_611_X_612_X, Yia: "6-ff00:0:612", YifID: If_612_X_611_X, Peer: true}, }, } diff --git a/pkg/private/xtest/graph/graph.go b/pkg/private/xtest/graph/graph.go index bce25476a1..9410aa17cd 100644 --- a/pkg/private/xtest/graph/graph.go +++ b/pkg/private/xtest/graph/graph.go @@ -241,6 +241,64 @@ func (g *Graph) BeaconWithStaticInfo(ifIDs []uint16) *seg.PathSegment { return g.beacon(ifIDs, true) } +func (g *Graph) PeeringBeacon(ia addr.IA) *seg.PathSegment { + as, ok := g.ases[ia] + if !ok { + panic(fmt.Sprintf("AS %s not in graph", ia)) + } + + segment, err := seg.CreateSegment(time.Now(), uint16(rand.Int())) + if err != nil { + panic(err) + } + + mac := [path.MacLen]byte{byte(0)} + asEntry := seg.ASEntry{ + Local: ia, + Next: 0, + MTU: 2000, + HopEntry: seg.HopEntry{ + HopField: seg.HopField{ + ExpTime: 63, + ConsIngress: 0, + ConsEgress: 0, + MAC: mac, + }, + IngressMTU: 1280, + }, + } + + // use int to avoid implementing sort.Interface + var ifIDs []int + for peeringLocalIF := range as.IfIDs { + ifIDs = append(ifIDs, int(peeringLocalIF)) + } + sort.Ints(ifIDs) + + for _, intIfID := range ifIDs { + peeringLocalIF := uint16(intIfID) + if g.isPeer[peeringLocalIF] { + peeringRemoteIF := g.links[peeringLocalIF] + asEntry.PeerEntries = append(asEntry.PeerEntries, seg.PeerEntry{ + Peer: g.parents[peeringRemoteIF], + PeerInterface: peeringRemoteIF, + PeerMTU: 1280, + HopField: seg.HopField{ + ExpTime: 63, + ConsIngress: peeringLocalIF, + ConsEgress: 0, + MAC: mac, + }, + }) + } + } + // FIXME: add static info? + if err := segment.AddASEntry(context.Background(), asEntry, g.signers[ia]); err != nil { + panic(serrors.Wrap("adding AS entry", err)) + } + return segment +} + // beacon constructs path segments across a series of egress ifIDs. The parent // AS of the first IfID is the origin of the beacon, and the beacon propagates // down to the parent AS of the remote counterpart of the last IfID. The @@ -582,6 +640,10 @@ func NewDefaultGraph(ctrl *gomock.Controller) *Graph { return NewFromDescription(ctrl, DefaultGraphDescription) } +func NewGraph(ctrl *gomock.Controller, desc *Description) *Graph { + return NewFromDescription(ctrl, desc) +} + // generateStaticInfo is used during mock beaconing. It takes any interface of the AS // that is doing the beaconing as well as the egress interface for that beacon. // It then uses that interface to generate characteristic StaticInfo for said interface, diff --git a/pkg/private/xtest/graph/ifaceids.go b/pkg/private/xtest/graph/ifaceids.go index 471032c8ca..6c8c327e67 100644 --- a/pkg/private/xtest/graph/ifaceids.go +++ b/pkg/private/xtest/graph/ifaceids.go @@ -49,5 +49,11 @@ var ( "410_X": 40, "411_X": 41, "510_X": 37, + "610_X": 42, + "611_X": 43, + "612_X": 44, + "620_X": 45, + "621_X": 46, + "622_X": 47, } ) diff --git a/pkg/private/xtest/graph/links_gen.go b/pkg/private/xtest/graph/links_gen.go index b4455a7209..72b26e0c96 100644 --- a/pkg/private/xtest/graph/links_gen.go +++ b/pkg/private/xtest/graph/links_gen.go @@ -35,6 +35,12 @@ const ( If_110_X_410_X = uint16(1140) If_110_X_411_X = uint16(1141) If_110_X_510_X = uint16(1137) + If_110_X_610_X = uint16(1142) + If_110_X_611_X = uint16(1143) + If_110_X_612_X = uint16(1144) + If_110_X_620_X = uint16(1145) + If_110_X_621_X = uint16(1146) + If_110_X_622_X = uint16(1147) If_111_A_110_X = uint16(1411) If_111_A_111_B = uint16(1427) If_111_A_111_C = uint16(1428) @@ -66,6 +72,12 @@ const ( If_111_A_410_X = uint16(1440) If_111_A_411_X = uint16(1441) If_111_A_510_X = uint16(1437) + If_111_A_610_X = uint16(1442) + If_111_A_611_X = uint16(1443) + If_111_A_612_X = uint16(1444) + If_111_A_620_X = uint16(1445) + If_111_A_621_X = uint16(1446) + If_111_A_622_X = uint16(1447) If_111_B_110_X = uint16(2711) If_111_B_111_A = uint16(2714) If_111_B_111_C = uint16(2728) @@ -97,6 +109,12 @@ const ( If_111_B_410_X = uint16(2740) If_111_B_411_X = uint16(2741) If_111_B_510_X = uint16(2737) + If_111_B_610_X = uint16(2742) + If_111_B_611_X = uint16(2743) + If_111_B_612_X = uint16(2744) + If_111_B_620_X = uint16(2745) + If_111_B_621_X = uint16(2746) + If_111_B_622_X = uint16(2747) If_111_C_110_X = uint16(2811) If_111_C_111_A = uint16(2814) If_111_C_111_B = uint16(2827) @@ -128,6 +146,12 @@ const ( If_111_C_410_X = uint16(2840) If_111_C_411_X = uint16(2841) If_111_C_510_X = uint16(2837) + If_111_C_610_X = uint16(2842) + If_111_C_611_X = uint16(2843) + If_111_C_612_X = uint16(2844) + If_111_C_620_X = uint16(2845) + If_111_C_621_X = uint16(2846) + If_111_C_622_X = uint16(2847) If_111_X_110_X = uint16(2011) If_111_X_111_A = uint16(2014) If_111_X_111_B = uint16(2027) @@ -159,6 +183,12 @@ const ( If_111_X_410_X = uint16(2040) If_111_X_411_X = uint16(2041) If_111_X_510_X = uint16(2037) + If_111_X_610_X = uint16(2042) + If_111_X_611_X = uint16(2043) + If_111_X_612_X = uint16(2044) + If_111_X_620_X = uint16(2045) + If_111_X_621_X = uint16(2046) + If_111_X_622_X = uint16(2047) If_112_X_110_X = uint16(1711) If_112_X_111_A = uint16(1714) If_112_X_111_B = uint16(1727) @@ -190,6 +220,12 @@ const ( If_112_X_410_X = uint16(1740) If_112_X_411_X = uint16(1741) If_112_X_510_X = uint16(1737) + If_112_X_610_X = uint16(1742) + If_112_X_611_X = uint16(1743) + If_112_X_612_X = uint16(1744) + If_112_X_620_X = uint16(1745) + If_112_X_621_X = uint16(1746) + If_112_X_622_X = uint16(1747) If_120_A_110_X = uint16(2911) If_120_A_111_A = uint16(2914) If_120_A_111_B = uint16(2927) @@ -221,6 +257,12 @@ const ( If_120_A_410_X = uint16(2940) If_120_A_411_X = uint16(2941) If_120_A_510_X = uint16(2937) + If_120_A_610_X = uint16(2942) + If_120_A_611_X = uint16(2943) + If_120_A_612_X = uint16(2944) + If_120_A_620_X = uint16(2945) + If_120_A_621_X = uint16(2946) + If_120_A_622_X = uint16(2947) If_120_B_110_X = uint16(3011) If_120_B_111_A = uint16(3014) If_120_B_111_B = uint16(3027) @@ -252,6 +294,12 @@ const ( If_120_B_410_X = uint16(3040) If_120_B_411_X = uint16(3041) If_120_B_510_X = uint16(3037) + If_120_B_610_X = uint16(3042) + If_120_B_611_X = uint16(3043) + If_120_B_612_X = uint16(3044) + If_120_B_620_X = uint16(3045) + If_120_B_621_X = uint16(3046) + If_120_B_622_X = uint16(3047) If_120_B1_110_X = uint16(3111) If_120_B1_111_A = uint16(3114) If_120_B1_111_B = uint16(3127) @@ -283,6 +331,12 @@ const ( If_120_B1_410_X = uint16(3140) If_120_B1_411_X = uint16(3141) If_120_B1_510_X = uint16(3137) + If_120_B1_610_X = uint16(3142) + If_120_B1_611_X = uint16(3143) + If_120_B1_612_X = uint16(3144) + If_120_B1_620_X = uint16(3145) + If_120_B1_621_X = uint16(3146) + If_120_B1_622_X = uint16(3147) If_120_X_110_X = uint16(1211) If_120_X_111_A = uint16(1214) If_120_X_111_B = uint16(1227) @@ -314,6 +368,12 @@ const ( If_120_X_410_X = uint16(1240) If_120_X_411_X = uint16(1241) If_120_X_510_X = uint16(1237) + If_120_X_610_X = uint16(1242) + If_120_X_611_X = uint16(1243) + If_120_X_612_X = uint16(1244) + If_120_X_620_X = uint16(1245) + If_120_X_621_X = uint16(1246) + If_120_X_622_X = uint16(1247) If_121_X_110_X = uint16(1511) If_121_X_111_A = uint16(1514) If_121_X_111_B = uint16(1527) @@ -345,6 +405,12 @@ const ( If_121_X_410_X = uint16(1540) If_121_X_411_X = uint16(1541) If_121_X_510_X = uint16(1537) + If_121_X_610_X = uint16(1542) + If_121_X_611_X = uint16(1543) + If_121_X_612_X = uint16(1544) + If_121_X_620_X = uint16(1545) + If_121_X_621_X = uint16(1546) + If_121_X_622_X = uint16(1547) If_122_X_110_X = uint16(1811) If_122_X_111_A = uint16(1814) If_122_X_111_B = uint16(1827) @@ -376,6 +442,12 @@ const ( If_122_X_410_X = uint16(1840) If_122_X_411_X = uint16(1841) If_122_X_510_X = uint16(1837) + If_122_X_610_X = uint16(1842) + If_122_X_611_X = uint16(1843) + If_122_X_612_X = uint16(1844) + If_122_X_620_X = uint16(1845) + If_122_X_621_X = uint16(1846) + If_122_X_622_X = uint16(1847) If_123_X_110_X = uint16(3811) If_123_X_111_A = uint16(3814) If_123_X_111_B = uint16(3827) @@ -407,6 +479,12 @@ const ( If_123_X_410_X = uint16(3840) If_123_X_411_X = uint16(3841) If_123_X_510_X = uint16(3837) + If_123_X_610_X = uint16(3842) + If_123_X_611_X = uint16(3843) + If_123_X_612_X = uint16(3844) + If_123_X_620_X = uint16(3845) + If_123_X_621_X = uint16(3846) + If_123_X_622_X = uint16(3847) If_130_A_110_X = uint16(1311) If_130_A_111_A = uint16(1314) If_130_A_111_B = uint16(1327) @@ -438,6 +516,12 @@ const ( If_130_A_410_X = uint16(1340) If_130_A_411_X = uint16(1341) If_130_A_510_X = uint16(1337) + If_130_A_610_X = uint16(1342) + If_130_A_611_X = uint16(1343) + If_130_A_612_X = uint16(1344) + If_130_A_620_X = uint16(1345) + If_130_A_621_X = uint16(1346) + If_130_A_622_X = uint16(1347) If_130_B_110_X = uint16(3211) If_130_B_111_A = uint16(3214) If_130_B_111_B = uint16(3227) @@ -469,6 +553,12 @@ const ( If_130_B_410_X = uint16(3240) If_130_B_411_X = uint16(3241) If_130_B_510_X = uint16(3237) + If_130_B_610_X = uint16(3242) + If_130_B_611_X = uint16(3243) + If_130_B_612_X = uint16(3244) + If_130_B_620_X = uint16(3245) + If_130_B_621_X = uint16(3246) + If_130_B_622_X = uint16(3247) If_131_X_110_X = uint16(1611) If_131_X_111_A = uint16(1614) If_131_X_111_B = uint16(1627) @@ -500,6 +590,12 @@ const ( If_131_X_410_X = uint16(1640) If_131_X_411_X = uint16(1641) If_131_X_510_X = uint16(1637) + If_131_X_610_X = uint16(1642) + If_131_X_611_X = uint16(1643) + If_131_X_612_X = uint16(1644) + If_131_X_620_X = uint16(1645) + If_131_X_621_X = uint16(1646) + If_131_X_622_X = uint16(1647) If_132_X_110_X = uint16(1911) If_132_X_111_A = uint16(1914) If_132_X_111_B = uint16(1927) @@ -531,6 +627,12 @@ const ( If_132_X_410_X = uint16(1940) If_132_X_411_X = uint16(1941) If_132_X_510_X = uint16(1937) + If_132_X_610_X = uint16(1942) + If_132_X_611_X = uint16(1943) + If_132_X_612_X = uint16(1944) + If_132_X_620_X = uint16(1945) + If_132_X_621_X = uint16(1946) + If_132_X_622_X = uint16(1947) If_133_X_110_X = uint16(1011) If_133_X_111_A = uint16(1014) If_133_X_111_B = uint16(1027) @@ -562,6 +664,12 @@ const ( If_133_X_410_X = uint16(1040) If_133_X_411_X = uint16(1041) If_133_X_510_X = uint16(1037) + If_133_X_610_X = uint16(1042) + If_133_X_611_X = uint16(1043) + If_133_X_612_X = uint16(1044) + If_133_X_620_X = uint16(1045) + If_133_X_621_X = uint16(1046) + If_133_X_622_X = uint16(1047) If_210_X_110_X = uint16(2111) If_210_X_111_A = uint16(2114) If_210_X_111_B = uint16(2127) @@ -593,6 +701,12 @@ const ( If_210_X_410_X = uint16(2140) If_210_X_411_X = uint16(2141) If_210_X_510_X = uint16(2137) + If_210_X_610_X = uint16(2142) + If_210_X_611_X = uint16(2143) + If_210_X_612_X = uint16(2144) + If_210_X_620_X = uint16(2145) + If_210_X_621_X = uint16(2146) + If_210_X_622_X = uint16(2147) If_210_X1_110_X = uint16(3311) If_210_X1_111_A = uint16(3314) If_210_X1_111_B = uint16(3327) @@ -624,6 +738,12 @@ const ( If_210_X1_410_X = uint16(3340) If_210_X1_411_X = uint16(3341) If_210_X1_510_X = uint16(3337) + If_210_X1_610_X = uint16(3342) + If_210_X1_611_X = uint16(3343) + If_210_X1_612_X = uint16(3344) + If_210_X1_620_X = uint16(3345) + If_210_X1_621_X = uint16(3346) + If_210_X1_622_X = uint16(3347) If_211_A_110_X = uint16(2311) If_211_A_111_A = uint16(2314) If_211_A_111_B = uint16(2327) @@ -655,6 +775,12 @@ const ( If_211_A_410_X = uint16(2340) If_211_A_411_X = uint16(2341) If_211_A_510_X = uint16(2337) + If_211_A_610_X = uint16(2342) + If_211_A_611_X = uint16(2343) + If_211_A_612_X = uint16(2344) + If_211_A_620_X = uint16(2345) + If_211_A_621_X = uint16(2346) + If_211_A_622_X = uint16(2347) If_211_A1_110_X = uint16(3411) If_211_A1_111_A = uint16(3414) If_211_A1_111_B = uint16(3427) @@ -686,6 +812,12 @@ const ( If_211_A1_410_X = uint16(3440) If_211_A1_411_X = uint16(3441) If_211_A1_510_X = uint16(3437) + If_211_A1_610_X = uint16(3442) + If_211_A1_611_X = uint16(3443) + If_211_A1_612_X = uint16(3444) + If_211_A1_620_X = uint16(3445) + If_211_A1_621_X = uint16(3446) + If_211_A1_622_X = uint16(3447) If_211_X_110_X = uint16(3511) If_211_X_111_A = uint16(3514) If_211_X_111_B = uint16(3527) @@ -717,6 +849,12 @@ const ( If_211_X_410_X = uint16(3540) If_211_X_411_X = uint16(3541) If_211_X_510_X = uint16(3537) + If_211_X_610_X = uint16(3542) + If_211_X_611_X = uint16(3543) + If_211_X_612_X = uint16(3544) + If_211_X_620_X = uint16(3545) + If_211_X_621_X = uint16(3546) + If_211_X_622_X = uint16(3547) If_212_X_110_X = uint16(2511) If_212_X_111_A = uint16(2514) If_212_X_111_B = uint16(2527) @@ -748,6 +886,12 @@ const ( If_212_X_410_X = uint16(2540) If_212_X_411_X = uint16(2541) If_212_X_510_X = uint16(2537) + If_212_X_610_X = uint16(2542) + If_212_X_611_X = uint16(2543) + If_212_X_612_X = uint16(2544) + If_212_X_620_X = uint16(2545) + If_212_X_621_X = uint16(2546) + If_212_X_622_X = uint16(2547) If_220_X_110_X = uint16(2211) If_220_X_111_A = uint16(2214) If_220_X_111_B = uint16(2227) @@ -779,6 +923,12 @@ const ( If_220_X_410_X = uint16(2240) If_220_X_411_X = uint16(2241) If_220_X_510_X = uint16(2237) + If_220_X_610_X = uint16(2242) + If_220_X_611_X = uint16(2243) + If_220_X_612_X = uint16(2244) + If_220_X_620_X = uint16(2245) + If_220_X_621_X = uint16(2246) + If_220_X_622_X = uint16(2247) If_221_X_110_X = uint16(2411) If_221_X_111_A = uint16(2414) If_221_X_111_B = uint16(2427) @@ -810,6 +960,12 @@ const ( If_221_X_410_X = uint16(2440) If_221_X_411_X = uint16(2441) If_221_X_510_X = uint16(2437) + If_221_X_610_X = uint16(2442) + If_221_X_611_X = uint16(2443) + If_221_X_612_X = uint16(2444) + If_221_X_620_X = uint16(2445) + If_221_X_621_X = uint16(2446) + If_221_X_622_X = uint16(2447) If_222_X_110_X = uint16(2611) If_222_X_111_A = uint16(2614) If_222_X_111_B = uint16(2627) @@ -841,6 +997,12 @@ const ( If_222_X_410_X = uint16(2640) If_222_X_411_X = uint16(2641) If_222_X_510_X = uint16(2637) + If_222_X_610_X = uint16(2642) + If_222_X_611_X = uint16(2643) + If_222_X_612_X = uint16(2644) + If_222_X_620_X = uint16(2645) + If_222_X_621_X = uint16(2646) + If_222_X_622_X = uint16(2647) If_310_X_110_X = uint16(3611) If_310_X_111_A = uint16(3614) If_310_X_111_B = uint16(3627) @@ -872,6 +1034,12 @@ const ( If_310_X_410_X = uint16(3640) If_310_X_411_X = uint16(3641) If_310_X_510_X = uint16(3637) + If_310_X_610_X = uint16(3642) + If_310_X_611_X = uint16(3643) + If_310_X_612_X = uint16(3644) + If_310_X_620_X = uint16(3645) + If_310_X_621_X = uint16(3646) + If_310_X_622_X = uint16(3647) If_311_X_110_X = uint16(3911) If_311_X_111_A = uint16(3914) If_311_X_111_B = uint16(3927) @@ -903,6 +1071,12 @@ const ( If_311_X_410_X = uint16(3940) If_311_X_411_X = uint16(3941) If_311_X_510_X = uint16(3937) + If_311_X_610_X = uint16(3942) + If_311_X_611_X = uint16(3943) + If_311_X_612_X = uint16(3944) + If_311_X_620_X = uint16(3945) + If_311_X_621_X = uint16(3946) + If_311_X_622_X = uint16(3947) If_410_X_110_X = uint16(4011) If_410_X_111_A = uint16(4014) If_410_X_111_B = uint16(4027) @@ -934,6 +1108,12 @@ const ( If_410_X_311_X = uint16(4039) If_410_X_411_X = uint16(4041) If_410_X_510_X = uint16(4037) + If_410_X_610_X = uint16(4042) + If_410_X_611_X = uint16(4043) + If_410_X_612_X = uint16(4044) + If_410_X_620_X = uint16(4045) + If_410_X_621_X = uint16(4046) + If_410_X_622_X = uint16(4047) If_411_X_110_X = uint16(4111) If_411_X_111_A = uint16(4114) If_411_X_111_B = uint16(4127) @@ -965,6 +1145,12 @@ const ( If_411_X_311_X = uint16(4139) If_411_X_410_X = uint16(4140) If_411_X_510_X = uint16(4137) + If_411_X_610_X = uint16(4142) + If_411_X_611_X = uint16(4143) + If_411_X_612_X = uint16(4144) + If_411_X_620_X = uint16(4145) + If_411_X_621_X = uint16(4146) + If_411_X_622_X = uint16(4147) If_510_X_110_X = uint16(3711) If_510_X_111_A = uint16(3714) If_510_X_111_B = uint16(3727) @@ -996,4 +1182,232 @@ const ( If_510_X_311_X = uint16(3739) If_510_X_410_X = uint16(3740) If_510_X_411_X = uint16(3741) + If_510_X_610_X = uint16(3742) + If_510_X_611_X = uint16(3743) + If_510_X_612_X = uint16(3744) + If_510_X_620_X = uint16(3745) + If_510_X_621_X = uint16(3746) + If_510_X_622_X = uint16(3747) + If_610_X_110_X = uint16(4211) + If_610_X_111_A = uint16(4214) + If_610_X_111_B = uint16(4227) + If_610_X_111_C = uint16(4228) + If_610_X_111_X = uint16(4220) + If_610_X_112_X = uint16(4217) + If_610_X_120_A = uint16(4229) + If_610_X_120_B = uint16(4230) + If_610_X_120_B1 = uint16(4231) + If_610_X_120_X = uint16(4212) + If_610_X_121_X = uint16(4215) + If_610_X_122_X = uint16(4218) + If_610_X_123_X = uint16(4238) + If_610_X_130_A = uint16(4213) + If_610_X_130_B = uint16(4232) + If_610_X_131_X = uint16(4216) + If_610_X_132_X = uint16(4219) + If_610_X_133_X = uint16(4210) + If_610_X_210_X = uint16(4221) + If_610_X_210_X1 = uint16(4233) + If_610_X_211_A = uint16(4223) + If_610_X_211_A1 = uint16(4234) + If_610_X_211_X = uint16(4235) + If_610_X_212_X = uint16(4225) + If_610_X_220_X = uint16(4222) + If_610_X_221_X = uint16(4224) + If_610_X_222_X = uint16(4226) + If_610_X_310_X = uint16(4236) + If_610_X_311_X = uint16(4239) + If_610_X_410_X = uint16(4240) + If_610_X_411_X = uint16(4241) + If_610_X_510_X = uint16(4237) + If_610_X_611_X = uint16(4243) + If_610_X_612_X = uint16(4244) + If_610_X_620_X = uint16(4245) + If_610_X_621_X = uint16(4246) + If_610_X_622_X = uint16(4247) + If_611_X_110_X = uint16(4311) + If_611_X_111_A = uint16(4314) + If_611_X_111_B = uint16(4327) + If_611_X_111_C = uint16(4328) + If_611_X_111_X = uint16(4320) + If_611_X_112_X = uint16(4317) + If_611_X_120_A = uint16(4329) + If_611_X_120_B = uint16(4330) + If_611_X_120_B1 = uint16(4331) + If_611_X_120_X = uint16(4312) + If_611_X_121_X = uint16(4315) + If_611_X_122_X = uint16(4318) + If_611_X_123_X = uint16(4338) + If_611_X_130_A = uint16(4313) + If_611_X_130_B = uint16(4332) + If_611_X_131_X = uint16(4316) + If_611_X_132_X = uint16(4319) + If_611_X_133_X = uint16(4310) + If_611_X_210_X = uint16(4321) + If_611_X_210_X1 = uint16(4333) + If_611_X_211_A = uint16(4323) + If_611_X_211_A1 = uint16(4334) + If_611_X_211_X = uint16(4335) + If_611_X_212_X = uint16(4325) + If_611_X_220_X = uint16(4322) + If_611_X_221_X = uint16(4324) + If_611_X_222_X = uint16(4326) + If_611_X_310_X = uint16(4336) + If_611_X_311_X = uint16(4339) + If_611_X_410_X = uint16(4340) + If_611_X_411_X = uint16(4341) + If_611_X_510_X = uint16(4337) + If_611_X_610_X = uint16(4342) + If_611_X_612_X = uint16(4344) + If_611_X_620_X = uint16(4345) + If_611_X_621_X = uint16(4346) + If_611_X_622_X = uint16(4347) + If_612_X_110_X = uint16(4411) + If_612_X_111_A = uint16(4414) + If_612_X_111_B = uint16(4427) + If_612_X_111_C = uint16(4428) + If_612_X_111_X = uint16(4420) + If_612_X_112_X = uint16(4417) + If_612_X_120_A = uint16(4429) + If_612_X_120_B = uint16(4430) + If_612_X_120_B1 = uint16(4431) + If_612_X_120_X = uint16(4412) + If_612_X_121_X = uint16(4415) + If_612_X_122_X = uint16(4418) + If_612_X_123_X = uint16(4438) + If_612_X_130_A = uint16(4413) + If_612_X_130_B = uint16(4432) + If_612_X_131_X = uint16(4416) + If_612_X_132_X = uint16(4419) + If_612_X_133_X = uint16(4410) + If_612_X_210_X = uint16(4421) + If_612_X_210_X1 = uint16(4433) + If_612_X_211_A = uint16(4423) + If_612_X_211_A1 = uint16(4434) + If_612_X_211_X = uint16(4435) + If_612_X_212_X = uint16(4425) + If_612_X_220_X = uint16(4422) + If_612_X_221_X = uint16(4424) + If_612_X_222_X = uint16(4426) + If_612_X_310_X = uint16(4436) + If_612_X_311_X = uint16(4439) + If_612_X_410_X = uint16(4440) + If_612_X_411_X = uint16(4441) + If_612_X_510_X = uint16(4437) + If_612_X_610_X = uint16(4442) + If_612_X_611_X = uint16(4443) + If_612_X_620_X = uint16(4445) + If_612_X_621_X = uint16(4446) + If_612_X_622_X = uint16(4447) + If_620_X_110_X = uint16(4511) + If_620_X_111_A = uint16(4514) + If_620_X_111_B = uint16(4527) + If_620_X_111_C = uint16(4528) + If_620_X_111_X = uint16(4520) + If_620_X_112_X = uint16(4517) + If_620_X_120_A = uint16(4529) + If_620_X_120_B = uint16(4530) + If_620_X_120_B1 = uint16(4531) + If_620_X_120_X = uint16(4512) + If_620_X_121_X = uint16(4515) + If_620_X_122_X = uint16(4518) + If_620_X_123_X = uint16(4538) + If_620_X_130_A = uint16(4513) + If_620_X_130_B = uint16(4532) + If_620_X_131_X = uint16(4516) + If_620_X_132_X = uint16(4519) + If_620_X_133_X = uint16(4510) + If_620_X_210_X = uint16(4521) + If_620_X_210_X1 = uint16(4533) + If_620_X_211_A = uint16(4523) + If_620_X_211_A1 = uint16(4534) + If_620_X_211_X = uint16(4535) + If_620_X_212_X = uint16(4525) + If_620_X_220_X = uint16(4522) + If_620_X_221_X = uint16(4524) + If_620_X_222_X = uint16(4526) + If_620_X_310_X = uint16(4536) + If_620_X_311_X = uint16(4539) + If_620_X_410_X = uint16(4540) + If_620_X_411_X = uint16(4541) + If_620_X_510_X = uint16(4537) + If_620_X_610_X = uint16(4542) + If_620_X_611_X = uint16(4543) + If_620_X_612_X = uint16(4544) + If_620_X_621_X = uint16(4546) + If_620_X_622_X = uint16(4547) + If_621_X_110_X = uint16(4611) + If_621_X_111_A = uint16(4614) + If_621_X_111_B = uint16(4627) + If_621_X_111_C = uint16(4628) + If_621_X_111_X = uint16(4620) + If_621_X_112_X = uint16(4617) + If_621_X_120_A = uint16(4629) + If_621_X_120_B = uint16(4630) + If_621_X_120_B1 = uint16(4631) + If_621_X_120_X = uint16(4612) + If_621_X_121_X = uint16(4615) + If_621_X_122_X = uint16(4618) + If_621_X_123_X = uint16(4638) + If_621_X_130_A = uint16(4613) + If_621_X_130_B = uint16(4632) + If_621_X_131_X = uint16(4616) + If_621_X_132_X = uint16(4619) + If_621_X_133_X = uint16(4610) + If_621_X_210_X = uint16(4621) + If_621_X_210_X1 = uint16(4633) + If_621_X_211_A = uint16(4623) + If_621_X_211_A1 = uint16(4634) + If_621_X_211_X = uint16(4635) + If_621_X_212_X = uint16(4625) + If_621_X_220_X = uint16(4622) + If_621_X_221_X = uint16(4624) + If_621_X_222_X = uint16(4626) + If_621_X_310_X = uint16(4636) + If_621_X_311_X = uint16(4639) + If_621_X_410_X = uint16(4640) + If_621_X_411_X = uint16(4641) + If_621_X_510_X = uint16(4637) + If_621_X_610_X = uint16(4642) + If_621_X_611_X = uint16(4643) + If_621_X_612_X = uint16(4644) + If_621_X_620_X = uint16(4645) + If_621_X_622_X = uint16(4647) + If_622_X_110_X = uint16(4711) + If_622_X_111_A = uint16(4714) + If_622_X_111_B = uint16(4727) + If_622_X_111_C = uint16(4728) + If_622_X_111_X = uint16(4720) + If_622_X_112_X = uint16(4717) + If_622_X_120_A = uint16(4729) + If_622_X_120_B = uint16(4730) + If_622_X_120_B1 = uint16(4731) + If_622_X_120_X = uint16(4712) + If_622_X_121_X = uint16(4715) + If_622_X_122_X = uint16(4718) + If_622_X_123_X = uint16(4738) + If_622_X_130_A = uint16(4713) + If_622_X_130_B = uint16(4732) + If_622_X_131_X = uint16(4716) + If_622_X_132_X = uint16(4719) + If_622_X_133_X = uint16(4710) + If_622_X_210_X = uint16(4721) + If_622_X_210_X1 = uint16(4733) + If_622_X_211_A = uint16(4723) + If_622_X_211_A1 = uint16(4734) + If_622_X_211_X = uint16(4735) + If_622_X_212_X = uint16(4725) + If_622_X_220_X = uint16(4722) + If_622_X_221_X = uint16(4724) + If_622_X_222_X = uint16(4726) + If_622_X_310_X = uint16(4736) + If_622_X_311_X = uint16(4739) + If_622_X_410_X = uint16(4740) + If_622_X_411_X = uint16(4741) + If_622_X_510_X = uint16(4737) + If_622_X_610_X = uint16(4742) + If_622_X_611_X = uint16(4743) + If_622_X_612_X = uint16(4744) + If_622_X_620_X = uint16(4745) + If_622_X_621_X = uint16(4746) ) diff --git a/pkg/segment/seg_test.go b/pkg/segment/seg_test.go index f2ece3bf69..fc9f20cdae 100644 --- a/pkg/segment/seg_test.go +++ b/pkg/segment/seg_test.go @@ -151,6 +151,86 @@ func TestPathSegmentAddASEntry(t *testing.T) { } } +func TestPathSegmentOneEntry(t *testing.T) { + asEntries := []ASEntry{ + { + Local: as110, + Next: 0, + MTU: 1500, + HopEntry: HopEntry{ + HopField: HopField{ + ConsIngress: 0, + ConsEgress: 0, + ExpTime: 63, + MAC: [path.MacLen]byte{0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, + }, + IngressMTU: 1442, + }, + PeerEntries: []PeerEntry{ + { + Peer: as211, + PeerInterface: 2112, + PeerMTU: 1501, + HopField: HopField{ + ConsIngress: 1221, + ConsEgress: 0, + ExpTime: 60, + MAC: [path.MacLen]byte{0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, + }, + }, { + Peer: as311, + PeerInterface: 3112, + PeerMTU: 1502, + HopField: HopField{ + ConsIngress: 1231, + ConsEgress: 0, + ExpTime: 59, + MAC: [path.MacLen]byte{0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, + }, + }, + }, + }, + } + var keyPairs []keyPair + for range asEntries { + keyPairs = append(keyPairs, newKeyPair(t)) + } + + ps, err := CreateSegment(time.Now(), 1337) + require.NoError(t, err) + + for i, entry := range asEntries { + id, fullID := ps.ID(), ps.FullID() + err := ps.AddASEntry(context.Background(), entry, keyPairs[i]) + require.NoErrorf(t, err, "index: %d", i) + + // Check that adding an AS entry modifies the segment id. + newID, newFullID := ps.ID(), ps.FullID() + assert.NotEqual(t, id, newID) + assert.NotEqual(t, fullID, newFullID) + + } + + for i, kp := range keyPairs { + err := ps.VerifyASEntry(context.Background(), kp, i) + require.NoErrorf(t, err, "index: %d", i) + } + + c, err := SegmentFromPB(PathSegmentToPB(ps)) + require.NoError(t, err) + assert.Equal(t, c, ps) + for i, kp := range keyPairs { + err := c.VerifyASEntry(context.Background(), kp, i) + require.NoErrorf(t, err, "index: %d", i) + } + + ps.ASEntries[0].Signed.Signature[3] ^= 0xFF + for i, kp := range keyPairs { + err := ps.VerifyASEntry(context.Background(), kp, i) + assert.Errorf(t, err, "index: %d", i) + } +} + type keyPair struct { pubKey crypto.PublicKey privKey crypto.Signer diff --git a/private/path/combinator/BUILD.bazel b/private/path/combinator/BUILD.bazel index 86a0ab5858..a8081db011 100644 --- a/private/path/combinator/BUILD.bazel +++ b/private/path/combinator/BUILD.bazel @@ -30,6 +30,7 @@ go_test( "combinator_test.go", "expiry_test.go", "export_test.go", + "peering_test.go", "staticinfo_accumulator_test.go", ], data = glob(["testdata/**"]), diff --git a/private/path/combinator/graph.go b/private/path/combinator/graph.go index a545e98057..5e300d3862 100644 --- a/private/path/combinator/graph.go +++ b/private/path/combinator/graph.go @@ -106,13 +106,27 @@ func (g *dmg) traverseSegment(segment *inputSegment) { // Directly process core segments, because we're not interested in // shortcuts. Add edge from last entry IA to first entry IA. + // Additionally, extract peering edges from the last ASEntry so that + // core ASes can be the source of peering shortcuts (core→down path). if segment.Type == proto.PathSegType_core { + lastIdx := len(asEntries) - 1 + lastIA := asEntries[lastIdx].Local g.AddEdge( - vertexFromIA(asEntries[len(asEntries)-1].Local), + vertexFromIA(lastIA), vertexFromIA(asEntries[0].Local), segment, &edge{Weight: len(asEntries) - 1}, ) + for peerIdx, peer := range asEntries[lastIdx].PeerEntries { + ingress := iface.ID(peer.HopField.ConsIngress) + remote := iface.ID(peer.PeerInterface) + g.AddEdge( + vertexFromIA(lastIA), + vertexFromPeering(lastIA, ingress, peer.Peer, remote), + segment, + &edge{Weight: 0, Shortcut: lastIdx, Peer: peerIdx + 1}, + ) + } return } @@ -201,7 +215,7 @@ func (g *dmg) GetPaths(src, dst vertex) []*pathSolution { for nextVertex, edgeList := range g.Adjacencies[currentPathSolution.currentVertex] { for segment, e := range edgeList { - // Makes sure the the segment would be valid in a path. + // Makes sure the segment would be valid in a path. if !validNextSeg(currentPathSolution.currentSeg, segment) { continue } diff --git a/private/path/combinator/peering_test.go b/private/path/combinator/peering_test.go new file mode 100644 index 0000000000..9f51cf6e3d --- /dev/null +++ b/private/path/combinator/peering_test.go @@ -0,0 +1,565 @@ +// Copyright 2026 SCION Association +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package combinator_test + +import ( + "os" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/private/xtest/graph" + seg "github.com/scionproto/scion/pkg/segment" + "github.com/scionproto/scion/private/path/combinator" +) + +// TestPeering tests peering path discovery with minimal segment sets. +func TestPeering(t *testing.T) { + ctrl := gomock.NewController(t) + g := graph.NewFromDescription(ctrl, graph.BigGraphDescription) + + testCases := []struct { + Name string + FileName string + SrcIA addr.IA + DstIA addr.IA + Ups []*seg.PathSegment + Cores []*seg.PathSegment + Downs []*seg.PathSegment + }{ + { + Name: "core 120 to core 410 via peering", + FileName: "peering_120_to_410.txt", + SrcIA: addr.MustParseIA("1-ff00:0:120"), + DstIA: addr.MustParseIA("4-ff00:0:410"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("4-ff00:0:410")), + }, + }, + { + Name: "core 410 to core 120 via peering", + FileName: "peering_410_to_120.txt", + SrcIA: addr.MustParseIA("4-ff00:0:410"), + DstIA: addr.MustParseIA("1-ff00:0:120"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_410_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("1-ff00:0:120")), + }, + }, + { + Name: "non-core 123 to core 410 via peering", + FileName: "peering_123_to_410.txt", + SrcIA: addr.MustParseIA("1-ff00:0:123"), + DstIA: addr.MustParseIA("4-ff00:0:410"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("4-ff00:0:410")), + }, + }, + { + Name: "core 410 to non-core 123 via peering", + FileName: "peering_410_to_123.txt", + SrcIA: addr.MustParseIA("4-ff00:0:410"), + DstIA: addr.MustParseIA("1-ff00:0:123"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_410_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + }, + { + Name: "non-core 122 to core 310 via peering", + FileName: "peering_122_to_310.txt", + SrcIA: addr.MustParseIA("1-ff00:0:122"), + DstIA: addr.MustParseIA("3-ff00:0:310"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("3-ff00:0:310")), + }, + }, + { + Name: "non-core 111 to core 210 via peering", + FileName: "peering_111_to_210.txt", + SrcIA: addr.MustParseIA("1-ff00:0:111"), + DstIA: addr.MustParseIA("2-ff00:0:210"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_111_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("2-ff00:0:210")), + }, + }, + { + Name: "non-core 123 to non-core 411 via peering", + FileName: "peering_123_to_411.txt", + SrcIA: addr.MustParseIA("1-ff00:0:123"), + DstIA: addr.MustParseIA("4-ff00:0:411"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_411_X}), + }, + }, + { + Name: "non-core 411 to non-core 123 via peering", + FileName: "peering_411_to_123.txt", + SrcIA: addr.MustParseIA("4-ff00:0:411"), + DstIA: addr.MustParseIA("1-ff00:0:123"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_411_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + }, + { + Name: "non-core 122 to non-core 311 via peering", + FileName: "peering_122_to_311.txt", + SrcIA: addr.MustParseIA("1-ff00:0:122"), + DstIA: addr.MustParseIA("3-ff00:0:311"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_311_X}), + }, + }, + { + Name: "non-core 111 to non-core 211 via peering", + FileName: "peering_111_to_211.txt", + SrcIA: addr.MustParseIA("1-ff00:0:111"), + DstIA: addr.MustParseIA("2-ff00:0:211"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_111_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_210_X_211_X}), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := combinator.Combine(tc.SrcIA, tc.DstIA, tc.Ups, tc.Cores, tc.Downs, false) + txtResult := writePaths(result) + t.Logf("Paths from %s to %s:\n%s", tc.SrcIA, tc.DstIA, txtResult.String()) + if *update { + err := os.WriteFile(xtest.ExpandPath(tc.FileName), txtResult.Bytes(), 0644) + require.NoError(t, err) + } + expected, err := os.ReadFile(xtest.ExpandPath(tc.FileName)) + assert.NoError(t, err) + assert.Equal(t, string(expected), txtResult.String()) + }) + } +} + +// TestPeeringFull tests peering path discovery with complete segment sets. +// This shows all possible paths including both peering and non-peering routes, +// similar to what "scion showpaths" displays. +func TestPeeringFull(t *testing.T) { + ctrl := gomock.NewController(t) + g := graph.NewFromDescription(ctrl, graph.BigGraphDescription) + + testCases := []struct { + Name string + FileName string + SrcIA addr.IA + DstIA addr.IA + Ups []*seg.PathSegment + Cores []*seg.PathSegment + Downs []*seg.PathSegment + }{ + { + Name: "non-core 111 to core 210 full", + FileName: "peering_111_to_210_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:111"), + DstIA: addr.MustParseIA("2-ff00:0:210"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_111_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_210_X_110_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("2-ff00:0:210")), + }, + }, + { + Name: "core 210 to non-core 111 full", + FileName: "peering_210_to_111_full.txt", + SrcIA: addr.MustParseIA("2-ff00:0:210"), + DstIA: addr.MustParseIA("1-ff00:0:111"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_210_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_111_X}), + }, + }, + { + Name: "core 120 to core 410 full", + FileName: "peering_120_to_410_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:120"), + DstIA: addr.MustParseIA("4-ff00:0:410"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_310_X, graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("4-ff00:0:410")), + }, + }, + { + Name: "core 410 to core 120 full", + FileName: "peering_410_to_120_full.txt", + SrcIA: addr.MustParseIA("4-ff00:0:410"), + DstIA: addr.MustParseIA("1-ff00:0:120"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_310_X, graph.If_310_X_410_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("1-ff00:0:120")), + }, + }, + { + Name: "non-core 123 to core 410 full", + FileName: "peering_123_to_410_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:123"), + DstIA: addr.MustParseIA("4-ff00:0:410"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_310_X, graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("4-ff00:0:410")), + }, + }, + { + Name: "core 410 to non-core 123 full", + FileName: "peering_410_to_123_full.txt", + SrcIA: addr.MustParseIA("4-ff00:0:410"), + DstIA: addr.MustParseIA("1-ff00:0:123"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_310_X, graph.If_310_X_410_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + }, + { + Name: "non-core 122 to core 310 full", + FileName: "peering_122_to_310_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:122"), + DstIA: addr.MustParseIA("3-ff00:0:310"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("3-ff00:0:310")), + }, + }, + { + Name: "core 310 to non-core 122 full", + FileName: "peering_310_to_122_full.txt", + SrcIA: addr.MustParseIA("3-ff00:0:310"), + DstIA: addr.MustParseIA("1-ff00:0:122"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_310_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + }, + { + Name: "non-core 122 to non-core 311 full", + FileName: "peering_122_to_311_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:122"), + DstIA: addr.MustParseIA("3-ff00:0:311"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_311_X}), + }, + }, + { + Name: "non-core 311 to non-core 122 full", + FileName: "peering_311_to_122_full.txt", + SrcIA: addr.MustParseIA("3-ff00:0:311"), + DstIA: addr.MustParseIA("1-ff00:0:122"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_310_X_311_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_310_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + }, + { + Name: "non-core 111 to non-core 211 full", + FileName: "peering_111_to_211_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:111"), + DstIA: addr.MustParseIA("2-ff00:0:211"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_111_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_210_X_110_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_210_X_211_X}), + }, + }, + { + Name: "non-core 211 to non-core 111 full", + FileName: "peering_211_to_111_full.txt", + SrcIA: addr.MustParseIA("2-ff00:0:211"), + DstIA: addr.MustParseIA("1-ff00:0:111"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_210_X_211_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_210_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_110_X_111_X}), + }, + }, + { + Name: "non-core 123 to non-core 411 full", + FileName: "peering_123_to_411_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:123"), + DstIA: addr.MustParseIA("4-ff00:0:411"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_310_X, graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_411_X}), + }, + }, + { + Name: "non-core 411 to non-core 123 full", + FileName: "peering_411_to_123_full.txt", + SrcIA: addr.MustParseIA("4-ff00:0:411"), + DstIA: addr.MustParseIA("1-ff00:0:123"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_411_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_310_X, graph.If_310_X_410_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X, graph.If_121_X_123_X}), + }, + }, + { + Name: "non-core 121 to non-core 122 full", + FileName: "peering_121_to_122_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:121"), + DstIA: addr.MustParseIA("1-ff00:0:122"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + }, + { + Name: "non-core 122 to non-core 121 full", + FileName: "peering_122_to_121_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:122"), + DstIA: addr.MustParseIA("1-ff00:0:121"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_122_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_121_X}), + }, + }, + { + Name: "non-core 611 to non-core 621 full", + FileName: "peering_611_to_621_full.txt", + SrcIA: addr.MustParseIA("6-ff00:0:611"), + DstIA: addr.MustParseIA("6-ff00:0:621"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_611_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_620_X_610_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_620_X_621_X}), + }, + }, + { + Name: "non-core 621 to non-core 611 full", + FileName: "peering_621_to_611_full.txt", + SrcIA: addr.MustParseIA("6-ff00:0:621"), + DstIA: addr.MustParseIA("6-ff00:0:611"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_620_X_621_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_620_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_611_X}), + }, + }, + { + Name: "non-core 611 to non-core 612 full", + FileName: "peering_611_to_612_full.txt", + SrcIA: addr.MustParseIA("6-ff00:0:611"), + DstIA: addr.MustParseIA("6-ff00:0:612"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_611_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_612_X}), + }, + }, + { + Name: "non-core 612 to non-core 611 full", + FileName: "peering_612_to_611_full.txt", + SrcIA: addr.MustParseIA("6-ff00:0:612"), + DstIA: addr.MustParseIA("6-ff00:0:611"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_612_X}), + }, + Cores: []*seg.PathSegment{}, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_611_X}), + }, + }, + { + Name: "non-core 611 to core 620 full", + FileName: "peering_611_to_620_full.txt", + SrcIA: addr.MustParseIA("6-ff00:0:611"), + DstIA: addr.MustParseIA("6-ff00:0:620"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_611_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_620_X_610_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("6-ff00:0:620")), + }, + }, + { + // Core→Non-core: core segment [610,620] has peer entries at AS 620. + Name: "core 620 to non-core 611 full", + FileName: "peering_620_to_611_full.txt", + SrcIA: addr.MustParseIA("6-ff00:0:620"), + DstIA: addr.MustParseIA("6-ff00:0:611"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_620_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_610_X_611_X}), + }, + }, + { + Name: "non-core 411 to core 120 full", + FileName: "peering_411_to_120_full.txt", + SrcIA: addr.MustParseIA("4-ff00:0:411"), + DstIA: addr.MustParseIA("1-ff00:0:120"), + Ups: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_411_X}), + }, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_120_X_310_X, graph.If_310_X_410_X}), + }, + Downs: []*seg.PathSegment{ + g.PeeringBeacon(addr.MustParseIA("1-ff00:0:120")), + }, + }, + { + Name: "core 120 to non-core 411 full", + FileName: "peering_120_to_411_full.txt", + SrcIA: addr.MustParseIA("1-ff00:0:120"), + DstIA: addr.MustParseIA("4-ff00:0:411"), + Ups: []*seg.PathSegment{}, + Cores: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_310_X, graph.If_310_X_120_X}), + }, + Downs: []*seg.PathSegment{ + g.Beacon([]uint16{graph.If_410_X_411_X}), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := combinator.Combine(tc.SrcIA, tc.DstIA, tc.Ups, tc.Cores, tc.Downs, false) + txtResult := writePaths(result) + t.Logf("Paths from %s to %s:\n%s", tc.SrcIA, tc.DstIA, txtResult.String()) + if *update { + err := os.WriteFile(xtest.ExpandPath(tc.FileName), txtResult.Bytes(), 0644) + require.NoError(t, err) + } + expected, err := os.ReadFile(xtest.ExpandPath(tc.FileName)) + assert.NoError(t, err) + assert.Equal(t, string(expected), txtResult.String()) + }) + } +} diff --git a/private/path/combinator/testdata/peering_111_to_210.txt b/private/path/combinator/testdata/peering_111_to_210.txt new file mode 100644 index 0000000000..3e15ed4fd8 --- /dev/null +++ b/private/path/combinator/testdata/peering_111_to_210.txt @@ -0,0 +1,10 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=2021 OutIF=0 + IF CP + HF InIF=2120 OutIF=0 + Interfaces: + 1-ff00:0:111#2021 + 2-ff00:0:210#2120 diff --git a/private/path/combinator/testdata/peering_111_to_210_full.txt b/private/path/combinator/testdata/peering_111_to_210_full.txt new file mode 100644 index 0000000000..cf759ba6d4 --- /dev/null +++ b/private/path/combinator/testdata/peering_111_to_210_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=2021 OutIF=0 + IF CP + HF InIF=2120 OutIF=0 + Interfaces: + 1-ff00:0:111#2021 + 2-ff00:0:210#2120 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=2011 OutIF=0 + HF InIF=0 OutIF=1120 + IF .. + HF InIF=1121 OutIF=0 + HF InIF=0 OutIF=2111 + Interfaces: + 1-ff00:0:111#2011 + 1-ff00:0:110#1120 + 1-ff00:0:110#1121 + 2-ff00:0:210#2111 diff --git a/private/path/combinator/testdata/peering_111_to_211.txt b/private/path/combinator/testdata/peering_111_to_211.txt new file mode 100644 index 0000000000..dde413912a --- /dev/null +++ b/private/path/combinator/testdata/peering_111_to_211.txt @@ -0,0 +1,23 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=2035 OutIF=0 + IF CP + HF InIF=3520 OutIF=0 + Interfaces: + 1-ff00:0:111#2035 + 2-ff00:0:211#3520 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=2021 OutIF=0 + IF CP + HF InIF=2120 OutIF=2135 + HF InIF=3521 OutIF=0 + Interfaces: + 1-ff00:0:111#2021 + 2-ff00:0:210#2120 + 2-ff00:0:210#2135 + 2-ff00:0:211#3521 diff --git a/private/path/combinator/testdata/peering_111_to_211_full.txt b/private/path/combinator/testdata/peering_111_to_211_full.txt new file mode 100644 index 0000000000..29bc68a1e6 --- /dev/null +++ b/private/path/combinator/testdata/peering_111_to_211_full.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=2035 OutIF=0 + IF CP + HF InIF=3520 OutIF=0 + Interfaces: + 1-ff00:0:111#2035 + 2-ff00:0:211#3520 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=2021 OutIF=0 + IF CP + HF InIF=2120 OutIF=2135 + HF InIF=3521 OutIF=0 + Interfaces: + 1-ff00:0:111#2021 + 2-ff00:0:210#2120 + 2-ff00:0:210#2135 + 2-ff00:0:211#3521 +Path #2: + Weight: 3 + Fields: + IF .. + HF InIF=2011 OutIF=0 + HF InIF=0 OutIF=1120 + IF .. + HF InIF=1121 OutIF=0 + HF InIF=0 OutIF=2111 + IF C. + HF InIF=0 OutIF=2135 + HF InIF=3521 OutIF=0 + Interfaces: + 1-ff00:0:111#2011 + 1-ff00:0:110#1120 + 1-ff00:0:110#1121 + 2-ff00:0:210#2111 + 2-ff00:0:210#2135 + 2-ff00:0:211#3521 diff --git a/private/path/combinator/testdata/peering_120_to_410.txt b/private/path/combinator/testdata/peering_120_to_410.txt new file mode 100644 index 0000000000..2da7131aa1 --- /dev/null +++ b/private/path/combinator/testdata/peering_120_to_410.txt @@ -0,0 +1,10 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1240 OutIF=0 + IF CP + HF InIF=4012 OutIF=0 + Interfaces: + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 diff --git a/private/path/combinator/testdata/peering_120_to_410_full.txt b/private/path/combinator/testdata/peering_120_to_410_full.txt new file mode 100644 index 0000000000..0ca5e9e0fc --- /dev/null +++ b/private/path/combinator/testdata/peering_120_to_410_full.txt @@ -0,0 +1,22 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1240 OutIF=0 + IF CP + HF InIF=4012 OutIF=0 + Interfaces: + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=1236 OutIF=0 + HF InIF=3640 OutIF=3612 + HF InIF=0 OutIF=4036 + Interfaces: + 1-ff00:0:120#1236 + 3-ff00:0:310#3612 + 3-ff00:0:310#3640 + 4-ff00:0:410#4036 diff --git a/private/path/combinator/testdata/peering_120_to_411_full.txt b/private/path/combinator/testdata/peering_120_to_411_full.txt new file mode 100644 index 0000000000..5cad7bf128 --- /dev/null +++ b/private/path/combinator/testdata/peering_120_to_411_full.txt @@ -0,0 +1,30 @@ +Path #0: + Weight: 2 + Fields: + IF .P + HF InIF=1240 OutIF=0 + IF CP + HF InIF=4012 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 +Path #1: + Weight: 3 + Fields: + IF .. + HF InIF=1236 OutIF=0 + HF InIF=3640 OutIF=3612 + HF InIF=0 OutIF=4036 + IF C. + HF InIF=0 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:120#1236 + 3-ff00:0:310#3612 + 3-ff00:0:310#3640 + 4-ff00:0:410#4036 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 diff --git a/private/path/combinator/testdata/peering_121_to_122_full.txt b/private/path/combinator/testdata/peering_121_to_122_full.txt new file mode 100644 index 0000000000..a2cc14ad8d --- /dev/null +++ b/private/path/combinator/testdata/peering_121_to_122_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1518 OutIF=0 + IF CP + HF InIF=1815 OutIF=0 + Interfaces: + 1-ff00:0:121#1518 + 1-ff00:0:122#1815 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=1512 OutIF=0 + HF InIF=0 OutIF=1215 + IF C. + HF InIF=0 OutIF=1218 + HF InIF=1812 OutIF=0 + Interfaces: + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1218 + 1-ff00:0:122#1812 diff --git a/private/path/combinator/testdata/peering_122_to_121_full.txt b/private/path/combinator/testdata/peering_122_to_121_full.txt new file mode 100644 index 0000000000..f05fa6bb97 --- /dev/null +++ b/private/path/combinator/testdata/peering_122_to_121_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1815 OutIF=0 + IF CP + HF InIF=1518 OutIF=0 + Interfaces: + 1-ff00:0:122#1815 + 1-ff00:0:121#1518 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=1812 OutIF=0 + HF InIF=0 OutIF=1218 + IF C. + HF InIF=0 OutIF=1215 + HF InIF=1512 OutIF=0 + Interfaces: + 1-ff00:0:122#1812 + 1-ff00:0:120#1218 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 diff --git a/private/path/combinator/testdata/peering_122_to_310.txt b/private/path/combinator/testdata/peering_122_to_310.txt new file mode 100644 index 0000000000..e888575b01 --- /dev/null +++ b/private/path/combinator/testdata/peering_122_to_310.txt @@ -0,0 +1,10 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1836 OutIF=0 + IF CP + HF InIF=3618 OutIF=0 + Interfaces: + 1-ff00:0:122#1836 + 3-ff00:0:310#3618 diff --git a/private/path/combinator/testdata/peering_122_to_310_full.txt b/private/path/combinator/testdata/peering_122_to_310_full.txt new file mode 100644 index 0000000000..247c216a6f --- /dev/null +++ b/private/path/combinator/testdata/peering_122_to_310_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1836 OutIF=0 + IF CP + HF InIF=3618 OutIF=0 + Interfaces: + 1-ff00:0:122#1836 + 3-ff00:0:310#3618 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=1812 OutIF=0 + HF InIF=0 OutIF=1218 + IF .. + HF InIF=1236 OutIF=0 + HF InIF=0 OutIF=3612 + Interfaces: + 1-ff00:0:122#1812 + 1-ff00:0:120#1218 + 1-ff00:0:120#1236 + 3-ff00:0:310#3612 diff --git a/private/path/combinator/testdata/peering_122_to_311.txt b/private/path/combinator/testdata/peering_122_to_311.txt new file mode 100644 index 0000000000..82e219a1bd --- /dev/null +++ b/private/path/combinator/testdata/peering_122_to_311.txt @@ -0,0 +1,23 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1839 OutIF=0 + IF CP + HF InIF=3918 OutIF=0 + Interfaces: + 1-ff00:0:122#1839 + 3-ff00:0:311#3918 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=1836 OutIF=0 + IF CP + HF InIF=3618 OutIF=3639 + HF InIF=3936 OutIF=0 + Interfaces: + 1-ff00:0:122#1836 + 3-ff00:0:310#3618 + 3-ff00:0:310#3639 + 3-ff00:0:311#3936 diff --git a/private/path/combinator/testdata/peering_122_to_311_full.txt b/private/path/combinator/testdata/peering_122_to_311_full.txt new file mode 100644 index 0000000000..6a45ff017d --- /dev/null +++ b/private/path/combinator/testdata/peering_122_to_311_full.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=1839 OutIF=0 + IF CP + HF InIF=3918 OutIF=0 + Interfaces: + 1-ff00:0:122#1839 + 3-ff00:0:311#3918 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=1836 OutIF=0 + IF CP + HF InIF=3618 OutIF=3639 + HF InIF=3936 OutIF=0 + Interfaces: + 1-ff00:0:122#1836 + 3-ff00:0:310#3618 + 3-ff00:0:310#3639 + 3-ff00:0:311#3936 +Path #2: + Weight: 3 + Fields: + IF .. + HF InIF=1812 OutIF=0 + HF InIF=0 OutIF=1218 + IF .. + HF InIF=1236 OutIF=0 + HF InIF=0 OutIF=3612 + IF C. + HF InIF=0 OutIF=3639 + HF InIF=3936 OutIF=0 + Interfaces: + 1-ff00:0:122#1812 + 1-ff00:0:120#1218 + 1-ff00:0:120#1236 + 3-ff00:0:310#3612 + 3-ff00:0:310#3639 + 3-ff00:0:311#3936 diff --git a/private/path/combinator/testdata/peering_123_to_410.txt b/private/path/combinator/testdata/peering_123_to_410.txt new file mode 100644 index 0000000000..5523d77e18 --- /dev/null +++ b/private/path/combinator/testdata/peering_123_to_410.txt @@ -0,0 +1,26 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3840 OutIF=0 + IF CP + HF InIF=4038 OutIF=0 + Interfaces: + 1-ff00:0:123#3840 + 4-ff00:0:410#4038 +Path #1: + Weight: 3 + Fields: + IF .P + HF InIF=3815 OutIF=0 + HF InIF=1512 OutIF=1538 + HF InIF=1240 OutIF=1215 + IF CP + HF InIF=4012 OutIF=0 + Interfaces: + 1-ff00:0:123#3815 + 1-ff00:0:121#1538 + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 diff --git a/private/path/combinator/testdata/peering_123_to_410_full.txt b/private/path/combinator/testdata/peering_123_to_410_full.txt new file mode 100644 index 0000000000..595599d351 --- /dev/null +++ b/private/path/combinator/testdata/peering_123_to_410_full.txt @@ -0,0 +1,46 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3840 OutIF=0 + IF CP + HF InIF=4038 OutIF=0 + Interfaces: + 1-ff00:0:123#3840 + 4-ff00:0:410#4038 +Path #1: + Weight: 3 + Fields: + IF .P + HF InIF=3815 OutIF=0 + HF InIF=1512 OutIF=1538 + HF InIF=1240 OutIF=1215 + IF CP + HF InIF=4012 OutIF=0 + Interfaces: + 1-ff00:0:123#3815 + 1-ff00:0:121#1538 + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 +Path #2: + Weight: 4 + Fields: + IF .. + HF InIF=3815 OutIF=0 + HF InIF=1512 OutIF=1538 + HF InIF=0 OutIF=1215 + IF .. + HF InIF=1236 OutIF=0 + HF InIF=3640 OutIF=3612 + HF InIF=0 OutIF=4036 + Interfaces: + 1-ff00:0:123#3815 + 1-ff00:0:121#1538 + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1236 + 3-ff00:0:310#3612 + 3-ff00:0:310#3640 + 4-ff00:0:410#4036 diff --git a/private/path/combinator/testdata/peering_123_to_411.txt b/private/path/combinator/testdata/peering_123_to_411.txt new file mode 100644 index 0000000000..4296ca6203 --- /dev/null +++ b/private/path/combinator/testdata/peering_123_to_411.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3841 OutIF=0 + IF CP + HF InIF=4138 OutIF=0 + Interfaces: + 1-ff00:0:123#3841 + 4-ff00:0:411#4138 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=3840 OutIF=0 + IF CP + HF InIF=4038 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:123#3840 + 4-ff00:0:410#4038 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 +Path #2: + Weight: 4 + Fields: + IF .P + HF InIF=3815 OutIF=0 + HF InIF=1512 OutIF=1538 + HF InIF=1240 OutIF=1215 + IF CP + HF InIF=4012 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:123#3815 + 1-ff00:0:121#1538 + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 diff --git a/private/path/combinator/testdata/peering_123_to_411_full.txt b/private/path/combinator/testdata/peering_123_to_411_full.txt new file mode 100644 index 0000000000..48b5ec2652 --- /dev/null +++ b/private/path/combinator/testdata/peering_123_to_411_full.txt @@ -0,0 +1,67 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3841 OutIF=0 + IF CP + HF InIF=4138 OutIF=0 + Interfaces: + 1-ff00:0:123#3841 + 4-ff00:0:411#4138 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=3840 OutIF=0 + IF CP + HF InIF=4038 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:123#3840 + 4-ff00:0:410#4038 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 +Path #2: + Weight: 4 + Fields: + IF .P + HF InIF=3815 OutIF=0 + HF InIF=1512 OutIF=1538 + HF InIF=1240 OutIF=1215 + IF CP + HF InIF=4012 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:123#3815 + 1-ff00:0:121#1538 + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1240 + 4-ff00:0:410#4012 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 +Path #3: + Weight: 5 + Fields: + IF .. + HF InIF=3815 OutIF=0 + HF InIF=1512 OutIF=1538 + HF InIF=0 OutIF=1215 + IF .. + HF InIF=1236 OutIF=0 + HF InIF=3640 OutIF=3612 + HF InIF=0 OutIF=4036 + IF C. + HF InIF=0 OutIF=4041 + HF InIF=4140 OutIF=0 + Interfaces: + 1-ff00:0:123#3815 + 1-ff00:0:121#1538 + 1-ff00:0:121#1512 + 1-ff00:0:120#1215 + 1-ff00:0:120#1236 + 3-ff00:0:310#3612 + 3-ff00:0:310#3640 + 4-ff00:0:410#4036 + 4-ff00:0:410#4041 + 4-ff00:0:411#4140 diff --git a/private/path/combinator/testdata/peering_210_to_111_full.txt b/private/path/combinator/testdata/peering_210_to_111_full.txt new file mode 100644 index 0000000000..09f477dc05 --- /dev/null +++ b/private/path/combinator/testdata/peering_210_to_111_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=2120 OutIF=0 + IF CP + HF InIF=2021 OutIF=0 + Interfaces: + 2-ff00:0:210#2120 + 1-ff00:0:111#2021 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=2111 OutIF=0 + HF InIF=0 OutIF=1121 + IF C. + HF InIF=0 OutIF=1120 + HF InIF=2011 OutIF=0 + Interfaces: + 2-ff00:0:210#2111 + 1-ff00:0:110#1121 + 1-ff00:0:110#1120 + 1-ff00:0:111#2011 diff --git a/private/path/combinator/testdata/peering_211_to_111_full.txt b/private/path/combinator/testdata/peering_211_to_111_full.txt new file mode 100644 index 0000000000..c502e9768d --- /dev/null +++ b/private/path/combinator/testdata/peering_211_to_111_full.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3520 OutIF=0 + IF CP + HF InIF=2035 OutIF=0 + Interfaces: + 2-ff00:0:211#3520 + 1-ff00:0:111#2035 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=3521 OutIF=0 + HF InIF=2120 OutIF=2135 + IF CP + HF InIF=2021 OutIF=0 + Interfaces: + 2-ff00:0:211#3521 + 2-ff00:0:210#2135 + 2-ff00:0:210#2120 + 1-ff00:0:111#2021 +Path #2: + Weight: 3 + Fields: + IF .. + HF InIF=3521 OutIF=0 + HF InIF=0 OutIF=2135 + IF .. + HF InIF=2111 OutIF=0 + HF InIF=0 OutIF=1121 + IF C. + HF InIF=0 OutIF=1120 + HF InIF=2011 OutIF=0 + Interfaces: + 2-ff00:0:211#3521 + 2-ff00:0:210#2135 + 2-ff00:0:210#2111 + 1-ff00:0:110#1121 + 1-ff00:0:110#1120 + 1-ff00:0:111#2011 diff --git a/private/path/combinator/testdata/peering_310_to_122_full.txt b/private/path/combinator/testdata/peering_310_to_122_full.txt new file mode 100644 index 0000000000..84798e89f9 --- /dev/null +++ b/private/path/combinator/testdata/peering_310_to_122_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3618 OutIF=0 + IF CP + HF InIF=1836 OutIF=0 + Interfaces: + 3-ff00:0:310#3618 + 1-ff00:0:122#1836 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=3612 OutIF=0 + HF InIF=0 OutIF=1236 + IF C. + HF InIF=0 OutIF=1218 + HF InIF=1812 OutIF=0 + Interfaces: + 3-ff00:0:310#3612 + 1-ff00:0:120#1236 + 1-ff00:0:120#1218 + 1-ff00:0:122#1812 diff --git a/private/path/combinator/testdata/peering_311_to_122_full.txt b/private/path/combinator/testdata/peering_311_to_122_full.txt new file mode 100644 index 0000000000..771c2b01f5 --- /dev/null +++ b/private/path/combinator/testdata/peering_311_to_122_full.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=3918 OutIF=0 + IF CP + HF InIF=1839 OutIF=0 + Interfaces: + 3-ff00:0:311#3918 + 1-ff00:0:122#1839 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=3936 OutIF=0 + HF InIF=3618 OutIF=3639 + IF CP + HF InIF=1836 OutIF=0 + Interfaces: + 3-ff00:0:311#3936 + 3-ff00:0:310#3639 + 3-ff00:0:310#3618 + 1-ff00:0:122#1836 +Path #2: + Weight: 3 + Fields: + IF .. + HF InIF=3936 OutIF=0 + HF InIF=0 OutIF=3639 + IF .. + HF InIF=3612 OutIF=0 + HF InIF=0 OutIF=1236 + IF C. + HF InIF=0 OutIF=1218 + HF InIF=1812 OutIF=0 + Interfaces: + 3-ff00:0:311#3936 + 3-ff00:0:310#3639 + 3-ff00:0:310#3612 + 1-ff00:0:120#1236 + 1-ff00:0:120#1218 + 1-ff00:0:122#1812 diff --git a/private/path/combinator/testdata/peering_410_to_120.txt b/private/path/combinator/testdata/peering_410_to_120.txt new file mode 100644 index 0000000000..a18ee49981 --- /dev/null +++ b/private/path/combinator/testdata/peering_410_to_120.txt @@ -0,0 +1,10 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4012 OutIF=0 + IF CP + HF InIF=1240 OutIF=0 + Interfaces: + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 diff --git a/private/path/combinator/testdata/peering_410_to_120_full.txt b/private/path/combinator/testdata/peering_410_to_120_full.txt new file mode 100644 index 0000000000..420c77fa63 --- /dev/null +++ b/private/path/combinator/testdata/peering_410_to_120_full.txt @@ -0,0 +1,22 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4012 OutIF=0 + IF CP + HF InIF=1240 OutIF=0 + Interfaces: + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=4036 OutIF=0 + HF InIF=3612 OutIF=3640 + HF InIF=0 OutIF=1236 + Interfaces: + 4-ff00:0:410#4036 + 3-ff00:0:310#3640 + 3-ff00:0:310#3612 + 1-ff00:0:120#1236 diff --git a/private/path/combinator/testdata/peering_410_to_123.txt b/private/path/combinator/testdata/peering_410_to_123.txt new file mode 100644 index 0000000000..86e8ab51c5 --- /dev/null +++ b/private/path/combinator/testdata/peering_410_to_123.txt @@ -0,0 +1,26 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4038 OutIF=0 + IF CP + HF InIF=3840 OutIF=0 + Interfaces: + 4-ff00:0:410#4038 + 1-ff00:0:123#3840 +Path #1: + Weight: 3 + Fields: + IF .P + HF InIF=4012 OutIF=0 + IF CP + HF InIF=1240 OutIF=1215 + HF InIF=1512 OutIF=1538 + HF InIF=3815 OutIF=0 + Interfaces: + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 + 1-ff00:0:121#1538 + 1-ff00:0:123#3815 diff --git a/private/path/combinator/testdata/peering_410_to_123_full.txt b/private/path/combinator/testdata/peering_410_to_123_full.txt new file mode 100644 index 0000000000..5c829d13ec --- /dev/null +++ b/private/path/combinator/testdata/peering_410_to_123_full.txt @@ -0,0 +1,46 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4038 OutIF=0 + IF CP + HF InIF=3840 OutIF=0 + Interfaces: + 4-ff00:0:410#4038 + 1-ff00:0:123#3840 +Path #1: + Weight: 3 + Fields: + IF .P + HF InIF=4012 OutIF=0 + IF CP + HF InIF=1240 OutIF=1215 + HF InIF=1512 OutIF=1538 + HF InIF=3815 OutIF=0 + Interfaces: + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 + 1-ff00:0:121#1538 + 1-ff00:0:123#3815 +Path #2: + Weight: 4 + Fields: + IF .. + HF InIF=4036 OutIF=0 + HF InIF=3612 OutIF=3640 + HF InIF=0 OutIF=1236 + IF C. + HF InIF=0 OutIF=1215 + HF InIF=1512 OutIF=1538 + HF InIF=3815 OutIF=0 + Interfaces: + 4-ff00:0:410#4036 + 3-ff00:0:310#3640 + 3-ff00:0:310#3612 + 1-ff00:0:120#1236 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 + 1-ff00:0:121#1538 + 1-ff00:0:123#3815 diff --git a/private/path/combinator/testdata/peering_411_to_120_full.txt b/private/path/combinator/testdata/peering_411_to_120_full.txt new file mode 100644 index 0000000000..3f446d96e4 --- /dev/null +++ b/private/path/combinator/testdata/peering_411_to_120_full.txt @@ -0,0 +1,30 @@ +Path #0: + Weight: 2 + Fields: + IF .P + HF InIF=4140 OutIF=0 + HF InIF=4012 OutIF=4041 + IF CP + HF InIF=1240 OutIF=0 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 +Path #1: + Weight: 3 + Fields: + IF .. + HF InIF=4140 OutIF=0 + HF InIF=0 OutIF=4041 + IF .. + HF InIF=4036 OutIF=0 + HF InIF=3612 OutIF=3640 + HF InIF=0 OutIF=1236 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4036 + 3-ff00:0:310#3640 + 3-ff00:0:310#3612 + 1-ff00:0:120#1236 diff --git a/private/path/combinator/testdata/peering_411_to_123.txt b/private/path/combinator/testdata/peering_411_to_123.txt new file mode 100644 index 0000000000..4adcd4715c --- /dev/null +++ b/private/path/combinator/testdata/peering_411_to_123.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4138 OutIF=0 + IF CP + HF InIF=3841 OutIF=0 + Interfaces: + 4-ff00:0:411#4138 + 1-ff00:0:123#3841 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=4140 OutIF=0 + HF InIF=4038 OutIF=4041 + IF CP + HF InIF=3840 OutIF=0 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4038 + 1-ff00:0:123#3840 +Path #2: + Weight: 4 + Fields: + IF .P + HF InIF=4140 OutIF=0 + HF InIF=4012 OutIF=4041 + IF CP + HF InIF=1240 OutIF=1215 + HF InIF=1512 OutIF=1538 + HF InIF=3815 OutIF=0 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 + 1-ff00:0:121#1538 + 1-ff00:0:123#3815 diff --git a/private/path/combinator/testdata/peering_411_to_123_full.txt b/private/path/combinator/testdata/peering_411_to_123_full.txt new file mode 100644 index 0000000000..7452390ae1 --- /dev/null +++ b/private/path/combinator/testdata/peering_411_to_123_full.txt @@ -0,0 +1,67 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4138 OutIF=0 + IF CP + HF InIF=3841 OutIF=0 + Interfaces: + 4-ff00:0:411#4138 + 1-ff00:0:123#3841 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=4140 OutIF=0 + HF InIF=4038 OutIF=4041 + IF CP + HF InIF=3840 OutIF=0 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4038 + 1-ff00:0:123#3840 +Path #2: + Weight: 4 + Fields: + IF .P + HF InIF=4140 OutIF=0 + HF InIF=4012 OutIF=4041 + IF CP + HF InIF=1240 OutIF=1215 + HF InIF=1512 OutIF=1538 + HF InIF=3815 OutIF=0 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4012 + 1-ff00:0:120#1240 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 + 1-ff00:0:121#1538 + 1-ff00:0:123#3815 +Path #3: + Weight: 5 + Fields: + IF .. + HF InIF=4140 OutIF=0 + HF InIF=0 OutIF=4041 + IF .. + HF InIF=4036 OutIF=0 + HF InIF=3612 OutIF=3640 + HF InIF=0 OutIF=1236 + IF C. + HF InIF=0 OutIF=1215 + HF InIF=1512 OutIF=1538 + HF InIF=3815 OutIF=0 + Interfaces: + 4-ff00:0:411#4140 + 4-ff00:0:410#4041 + 4-ff00:0:410#4036 + 3-ff00:0:310#3640 + 3-ff00:0:310#3612 + 1-ff00:0:120#1236 + 1-ff00:0:120#1215 + 1-ff00:0:121#1512 + 1-ff00:0:121#1538 + 1-ff00:0:123#3815 diff --git a/private/path/combinator/testdata/peering_611_to_612_full.txt b/private/path/combinator/testdata/peering_611_to_612_full.txt new file mode 100644 index 0000000000..ab5dff002a --- /dev/null +++ b/private/path/combinator/testdata/peering_611_to_612_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4344 OutIF=0 + IF CP + HF InIF=4443 OutIF=0 + Interfaces: + 6-ff00:0:611#4344 + 6-ff00:0:612#4443 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=4342 OutIF=0 + HF InIF=0 OutIF=4243 + IF C. + HF InIF=0 OutIF=4244 + HF InIF=4442 OutIF=0 + Interfaces: + 6-ff00:0:611#4342 + 6-ff00:0:610#4243 + 6-ff00:0:610#4244 + 6-ff00:0:612#4442 diff --git a/private/path/combinator/testdata/peering_611_to_620_full.txt b/private/path/combinator/testdata/peering_611_to_620_full.txt new file mode 100644 index 0000000000..268c60d48b --- /dev/null +++ b/private/path/combinator/testdata/peering_611_to_620_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4345 OutIF=0 + IF CP + HF InIF=4543 OutIF=0 + Interfaces: + 6-ff00:0:611#4345 + 6-ff00:0:620#4543 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=4342 OutIF=0 + HF InIF=0 OutIF=4243 + IF .. + HF InIF=4245 OutIF=0 + HF InIF=0 OutIF=4542 + Interfaces: + 6-ff00:0:611#4342 + 6-ff00:0:610#4243 + 6-ff00:0:610#4245 + 6-ff00:0:620#4542 diff --git a/private/path/combinator/testdata/peering_611_to_621_full.txt b/private/path/combinator/testdata/peering_611_to_621_full.txt new file mode 100644 index 0000000000..b9c7fc1872 --- /dev/null +++ b/private/path/combinator/testdata/peering_611_to_621_full.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4346 OutIF=0 + IF CP + HF InIF=4643 OutIF=0 + Interfaces: + 6-ff00:0:611#4346 + 6-ff00:0:621#4643 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=4345 OutIF=0 + IF CP + HF InIF=4543 OutIF=4546 + HF InIF=4645 OutIF=0 + Interfaces: + 6-ff00:0:611#4345 + 6-ff00:0:620#4543 + 6-ff00:0:620#4546 + 6-ff00:0:621#4645 +Path #2: + Weight: 3 + Fields: + IF .. + HF InIF=4342 OutIF=0 + HF InIF=0 OutIF=4243 + IF .. + HF InIF=4245 OutIF=0 + HF InIF=0 OutIF=4542 + IF C. + HF InIF=0 OutIF=4546 + HF InIF=4645 OutIF=0 + Interfaces: + 6-ff00:0:611#4342 + 6-ff00:0:610#4243 + 6-ff00:0:610#4245 + 6-ff00:0:620#4542 + 6-ff00:0:620#4546 + 6-ff00:0:621#4645 diff --git a/private/path/combinator/testdata/peering_612_to_611_full.txt b/private/path/combinator/testdata/peering_612_to_611_full.txt new file mode 100644 index 0000000000..66ff76d800 --- /dev/null +++ b/private/path/combinator/testdata/peering_612_to_611_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4443 OutIF=0 + IF CP + HF InIF=4344 OutIF=0 + Interfaces: + 6-ff00:0:612#4443 + 6-ff00:0:611#4344 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=4442 OutIF=0 + HF InIF=0 OutIF=4244 + IF C. + HF InIF=0 OutIF=4243 + HF InIF=4342 OutIF=0 + Interfaces: + 6-ff00:0:612#4442 + 6-ff00:0:610#4244 + 6-ff00:0:610#4243 + 6-ff00:0:611#4342 diff --git a/private/path/combinator/testdata/peering_620_to_611_full.txt b/private/path/combinator/testdata/peering_620_to_611_full.txt new file mode 100644 index 0000000000..f3104897a7 --- /dev/null +++ b/private/path/combinator/testdata/peering_620_to_611_full.txt @@ -0,0 +1,24 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4543 OutIF=0 + IF CP + HF InIF=4345 OutIF=0 + Interfaces: + 6-ff00:0:620#4543 + 6-ff00:0:611#4345 +Path #1: + Weight: 2 + Fields: + IF .. + HF InIF=4542 OutIF=0 + HF InIF=0 OutIF=4245 + IF C. + HF InIF=0 OutIF=4243 + HF InIF=4342 OutIF=0 + Interfaces: + 6-ff00:0:620#4542 + 6-ff00:0:610#4245 + 6-ff00:0:610#4243 + 6-ff00:0:611#4342 diff --git a/private/path/combinator/testdata/peering_621_to_611_full.txt b/private/path/combinator/testdata/peering_621_to_611_full.txt new file mode 100644 index 0000000000..dc860113a5 --- /dev/null +++ b/private/path/combinator/testdata/peering_621_to_611_full.txt @@ -0,0 +1,42 @@ +Path #0: + Weight: 1 + Fields: + IF .P + HF InIF=4643 OutIF=0 + IF CP + HF InIF=4346 OutIF=0 + Interfaces: + 6-ff00:0:621#4643 + 6-ff00:0:611#4346 +Path #1: + Weight: 2 + Fields: + IF .P + HF InIF=4645 OutIF=0 + HF InIF=4543 OutIF=4546 + IF CP + HF InIF=4345 OutIF=0 + Interfaces: + 6-ff00:0:621#4645 + 6-ff00:0:620#4546 + 6-ff00:0:620#4543 + 6-ff00:0:611#4345 +Path #2: + Weight: 3 + Fields: + IF .. + HF InIF=4645 OutIF=0 + HF InIF=0 OutIF=4546 + IF .. + HF InIF=4542 OutIF=0 + HF InIF=0 OutIF=4245 + IF C. + HF InIF=0 OutIF=4243 + HF InIF=4342 OutIF=0 + Interfaces: + 6-ff00:0:621#4645 + 6-ff00:0:620#4546 + 6-ff00:0:620#4542 + 6-ff00:0:610#4245 + 6-ff00:0:610#4243 + 6-ff00:0:611#4342 diff --git a/private/segment/segfetcher/splitter.go b/private/segment/segfetcher/splitter.go index d5ac43da28..e75d878f15 100644 --- a/private/segment/segfetcher/splitter.go +++ b/private/segment/segfetcher/splitter.go @@ -23,6 +23,20 @@ import ( "github.com/scionproto/scion/private/trust" ) +// ctxKey is used for context keys in this package. +type ctxKey string + +// SkipOneHopKey is a context key that, when set, instructs the splitter to skip +// creating one-hop segment requests. This is used to avoid infinite recursion +// when the dstProvider needs to find a path to a remote core AS for forwarding +// a one-hop segment request. +const SkipOneHopKey ctxKey = "skipOneHop" + +// SkipOneHop returns true if one-hop segment requests should be skipped. +func SkipOneHop(ctx context.Context) bool { + return ctx.Value(SkipOneHopKey) != nil +} + // Splitter splits a path request into set of segment requests. type Splitter interface { // Split splits a path request from the local AS to dst into a set of segment requests. @@ -70,6 +84,9 @@ func (s *MultiSegmentSplitter) Split(ctx context.Context, dst addr.IA) (Requests return nil, err } + // Check if we should skip one-hop segment requests (to avoid recursion in dstProvider) + skipOneHop := SkipOneHop(ctx) + switch { case !srcCore && !dstCore: if !singleCore.IsZero() { @@ -78,29 +95,48 @@ func (s *MultiSegmentSplitter) Split(ctx context.Context, dst addr.IA) (Requests {Src: singleCore, Dst: dst, SegType: Down}, }, nil } - return Requests{ + reqs := Requests{ {Src: src, Dst: toWildCard(src), SegType: Up}, {Src: toWildCard(src), Dst: toWildCard(dst), SegType: Core}, {Src: toWildCard(dst), Dst: dst, SegType: Down}, - }, nil + } + if !skipOneHop { + reqs = s.addOneHopRequests(ctx, reqs, src, dst, srcCore, dstCore) + } + return reqs, nil case !srcCore && dstCore: if (src.ISD() == dst.ISD() && dst.IsWildcard()) || singleCore.Equal(dst) { return Requests{{Src: src, Dst: dst, SegType: Up}}, nil } - return Requests{ + reqs := Requests{ {Src: src, Dst: toWildCard(src), SegType: Up}, {Src: toWildCard(src), Dst: dst, SegType: Core}, - }, nil + } + if !skipOneHop { + reqs = s.addOneHopRequests(ctx, reqs, src, dst, srcCore, dstCore) + } + return reqs, nil case srcCore && !dstCore: if singleCore.Equal(src) { return Requests{{Src: src, Dst: dst, SegType: Down}}, nil } - return Requests{ + reqs := Requests{ {Src: src, Dst: toWildCard(dst), SegType: Core}, {Src: toWildCard(dst), Dst: dst, SegType: Down}, - }, nil + } + if !skipOneHop { + reqs = s.addOneHopRequests(ctx, reqs, src, dst, srcCore, dstCore) + } + return reqs, nil default: - return Requests{{Src: src, Dst: dst, SegType: Core}}, nil + // srcCore && dstCore + reqs := Requests{ + {Src: src, Dst: dst, SegType: Core}, + } + if !skipOneHop { + reqs = s.addOneHopRequests(ctx, reqs, src, dst, srcCore, dstCore) + } + return reqs, nil } } @@ -142,6 +178,28 @@ func (s *MultiSegmentSplitter) isCore(ctx context.Context, dst addr.IA) (bool, e return isCore, nil } +// addOneHopRequests appends one-hop segment requests for peering path discovery. +// One-hop segments are Down segments that carry peer entries for core ASes, +// enabling the combinator to build peering shortcuts on the destination side. +// Source-side peering edges come from core segment processing in the combinator. +func (s *MultiSegmentSplitter) addOneHopRequests( + ctx context.Context, + reqs Requests, + src, dst addr.IA, + srcCore, dstCore bool, +) Requests { + if dstCore { + reqs = append(reqs, Request{Src: dst, Dst: dst, SegType: seg.TypeDown}) + } else if srcCore || src.ISD() != dst.ISD() { + dstCores, _ := s.Inspector.ByAttributes(ctx, dst.ISD(), trust.Core) + for _, c := range dstCores { + reqs = append(reqs, Request{Src: c, Dst: c, SegType: seg.TypeDown}) + } + } + + return reqs +} + func toWildCard(ia addr.IA) addr.IA { return addr.MustIAFrom(ia.ISD(), 0) } diff --git a/private/segment/segfetcher/splitter_test.go b/private/segment/segfetcher/splitter_test.go index 336e18b970..8c972587f3 100644 --- a/private/segment/segfetcher/splitter_test.go +++ b/private/segment/segfetcher/splitter_test.go @@ -44,7 +44,7 @@ func TestRequestSplitter(t *testing.T) { return ok, nil }, ).AnyTimes() - inspector.EXPECT().ByAttributes(gomock.Any(), addr.ISD(1), trust.Core).DoAndReturn( + inspector.EXPECT().ByAttributes(gomock.Any(), gomock.Any(), trust.Core).DoAndReturn( func(_ context.Context, isd addr.ISD, _ trust.Attribute) ([]addr.IA, error) { var result []addr.IA for ia := range cores { @@ -67,6 +67,8 @@ func TestRequestSplitter(t *testing.T) { ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: core_110}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_110, Dst: core_110}, }, }, "Up wildcard": { @@ -82,14 +84,19 @@ func TestRequestSplitter(t *testing.T) { ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: core_210}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, "Up Core non-local wildcard": { + // Wildcards are considered "core" by isCore(), so dstCore=true LocalIA: non_core_111, Dst: isd2, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: isd2, Dst: isd2}, }, }, "Down local": { @@ -98,6 +105,10 @@ func TestRequestSplitter(t *testing.T) { ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: isd1}, segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_111}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_110, Dst: core_110}, + segfetcher.Request{SegType: Down, Src: core_120, Dst: core_120}, + segfetcher.Request{SegType: Down, Src: core_130, Dst: core_130}, }, }, "Down non-local": { @@ -106,6 +117,8 @@ func TestRequestSplitter(t *testing.T) { ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: isd2}, segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, "Core local": { @@ -113,6 +126,8 @@ func TestRequestSplitter(t *testing.T) { Dst: core_130, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: core_130}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_130, Dst: core_130}, }, }, "Core non-local": { @@ -120,16 +135,23 @@ func TestRequestSplitter(t *testing.T) { Dst: core_210, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: core_210}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, "Core non-local wildcard": { + // Wildcards are considered "core" by isCore(), so this goes to default case LocalIA: core_110, Dst: isd2, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: isd2}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: isd2, Dst: isd2}, }, }, "Up down local": { + // !srcCore && !dstCore, same ISD: no one-hop requests needed + // (source-side peering comes from core segments in the combinator) LocalIA: non_core_111, Dst: non_core_112, ExpectedSet: segfetcher.Requests{ @@ -145,6 +167,8 @@ func TestRequestSplitter(t *testing.T) { segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2}, segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, } @@ -164,7 +188,7 @@ func TestRequestSplitter(t *testing.T) { assert.Contains(t, err.Error(), test.ExpectedErrMsg) } else { assert.NoError(t, err) - assert.Equal(t, test.ExpectedSet, requests) + assert.ElementsMatch(t, test.ExpectedSet, requests) } }) } @@ -181,7 +205,7 @@ func TestRequestSplitter(t *testing.T) { return ok, nil }, ).AnyTimes() - inspector.EXPECT().ByAttributes(gomock.Any(), addr.ISD(1), trust.Core).DoAndReturn( + inspector.EXPECT().ByAttributes(gomock.Any(), gomock.Any(), trust.Core).DoAndReturn( func(_ context.Context, isd addr.ISD, _ trust.Attribute) ([]addr.IA, error) { var result []addr.IA for ia := range cores { @@ -199,6 +223,7 @@ func TestRequestSplitter(t *testing.T) { ExpectedErrMsg string }{ "Up": { + // Single core in ISD1, returns early via singleCore path LocalIA: non_core_111, Dst: core_110, ExpectedSet: segfetcher.Requests{ @@ -218,17 +243,23 @@ func TestRequestSplitter(t *testing.T) { ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: core_210}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, "Up Core non-local wildcard": { + // Wildcards are considered "core" by isCore(), so dstCore=true LocalIA: non_core_111, Dst: isd2, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: isd2, Dst: isd2}, }, }, "Down local": { + // Single core in ISD1, returns early via singleCore path LocalIA: core_110, Dst: non_core_111, ExpectedSet: segfetcher.Requests{ @@ -241,6 +272,8 @@ func TestRequestSplitter(t *testing.T) { ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: isd2}, segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, "Core non-local": { @@ -248,16 +281,22 @@ func TestRequestSplitter(t *testing.T) { Dst: core_210, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: core_210}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, "Core non-local wildcard": { + // Wildcards are considered "core" by isCore(), so this goes to default case LocalIA: core_110, Dst: isd2, ExpectedSet: segfetcher.Requests{ segfetcher.Request{SegType: Core, Src: core_110, Dst: isd2}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: isd2, Dst: isd2}, }, }, "Up down local": { + // Single core in ISD1, returns early via singleCore path LocalIA: non_core_111, Dst: non_core_112, ExpectedSet: segfetcher.Requests{ @@ -272,6 +311,8 @@ func TestRequestSplitter(t *testing.T) { segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}, segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2}, segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211}, + // One-hop requests for peering (destination-side only) + segfetcher.Request{SegType: Down, Src: core_210, Dst: core_210}, }, }, } @@ -289,7 +330,7 @@ func TestRequestSplitter(t *testing.T) { assert.Contains(t, err.Error(), test.ExpectedErrMsg) } else { assert.NoError(t, err) - assert.Equal(t, test.ExpectedSet, requests) + assert.ElementsMatch(t, test.ExpectedSet, requests) } }) } diff --git a/topology/BUILD.bazel b/topology/BUILD.bazel index 8fb90070df..f6b37f91ba 100644 --- a/topology/BUILD.bazel +++ b/topology/BUILD.bazel @@ -16,5 +16,7 @@ exports_files( [ "tiny.topo", "tiny4.topo", + "testdata/big.topo", + "big.topo", ], ) diff --git a/topology/testdata/big.topo b/topology/testdata/big.topo index 2f1b84edaf..7364ca75f3 100644 --- a/topology/testdata/big.topo +++ b/topology/testdata/big.topo @@ -13,6 +13,12 @@ ASes: "4-ff00:0:410": {core: true, voting: true, authoritative: true, issuing: true} "4-ff00:0:411": {cert_issuer: "4-ff00:0:410"} "5-ff00:0:510": {core: true, voting: true, authoritative: true, issuing: true} + "6-ff00:0:610": {core: true, voting: true, authoritative: true, issuing: true} + "6-ff00:0:620": {core: true, voting: true, authoritative: true, issuing: true} + "6-ff00:0:611": {cert_issuer: "6-ff00:0:610"} + "6-ff00:0:612": {cert_issuer: "6-ff00:0:610"} + "6-ff00:0:621": {cert_issuer: "6-ff00:0:620"} + "6-ff00:0:622": {cert_issuer: "6-ff00:0:620"} links: - {a: "1-ff00:0:110", b: "1-ff00:0:120", linkAtoB: CORE} - {a: "1-ff00:0:110", b: "2-ff00:0:210", linkAtoB: CORE} @@ -22,6 +28,7 @@ links: - {a: "1-ff00:0:120", b: "1-ff00:0:121", linkAtoB: CHILD} - {a: "1-ff00:0:120", b: "1-ff00:0:122", linkAtoB: CHILD} - {a: "1-ff00:0:120", b: "3-ff00:0:310", linkAtoB: CORE} + - {a: "1-ff00:0:120", b: "4-ff00:0:410", linkAtoB: PEER} - {a: "1-ff00:0:120", b: "5-ff00:0:510", linkAtoB: CORE} - {a: "1-ff00:0:121", b: "1-ff00:0:123", linkAtoB: CHILD} - {a: "1-ff00:0:121", b: "1-ff00:0:122", linkAtoB: PEER} @@ -30,6 +37,15 @@ links: - {a: "1-ff00:0:123", b: "4-ff00:0:410", linkAtoB: PEER} - {a: "1-ff00:0:123", b: "4-ff00:0:411", linkAtoB: PEER} - {a: "2-ff00:0:210", b: "2-ff00:0:211", linkAtoB: CHILD} + - {a: "2-ff00:0:210", b: "6-ff00:0:610", linkAtoB: CORE} - {a: "3-ff00:0:310", b: "4-ff00:0:410", linkAtoB: CORE} - {a: "3-ff00:0:310", b: "3-ff00:0:311", linkAtoB: CHILD} - {a: "4-ff00:0:410", b: "4-ff00:0:411", linkAtoB: CHILD} + - {a: "6-ff00:0:610", b: "6-ff00:0:620", linkAtoB: CORE} + - {a: "6-ff00:0:610", b: "6-ff00:0:611", linkAtoB: CHILD} + - {a: "6-ff00:0:610", b: "6-ff00:0:612", linkAtoB: CHILD} + - {a: "6-ff00:0:620", b: "6-ff00:0:621", linkAtoB: CHILD} + - {a: "6-ff00:0:620", b: "6-ff00:0:622", linkAtoB: CHILD} + - {a: "6-ff00:0:611", b: "6-ff00:0:620", linkAtoB: PEER} + - {a: "6-ff00:0:611", b: "6-ff00:0:621", linkAtoB: PEER} + - {a: "6-ff00:0:611", b: "6-ff00:0:612", linkAtoB: PEER} diff --git a/topology/testdata/big.topo.png b/topology/testdata/big.topo.png new file mode 100644 index 0000000000..a32e48d6c5 Binary files /dev/null and b/topology/testdata/big.topo.png differ