From 40e047e5d2f0fe2ca443f9c9d4f9b2b30f654a79 Mon Sep 17 00:00:00 2001 From: jelte <46663111+jeltevanbommel@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:40:04 +0200 Subject: [PATCH] FABRID base version (#170) Control plane features: - Definition of FABRID policies and connection points on which they are available - CS loads policies and appends maps for policy indices and connection points as detachable extensions to PCBs - Remote CSes cache FABRID maps - SD fetches detached extensions on request Data plane features: - Fabrid and Identifier HBH extensions for sending FABRID traffic - WithFabrid option for choosing paths based on the fabrid query - Fabrid dataplane path which sets HBH extensions - BR fetches DRKey secret values and fabrid config on startup - BR validates and updates FABRID HVFs Tools: - topology: --fabrid flag enables DRKey and fabrid for local topology - ping: select fabrid policies with --fabridquery flag - end2end: run integration test with --fabrid to test path validation Demo: 1. `./scion.sh topology --fabrid` (docker: `./scion.sh topology -d --endhosts --fabrid`) 2. `./scion.sh run` 3. `./bin/end2end_integration --fabrid` (docker: `./bin/end2end_integration -d --fabrid`) 4. `./scion.sh stop` Upstream changes: - https://github.com/scionproto/scion/issues/4628 --- .github/workflows/gobra.yml | 2 +- .../router_benchmark/conf/topology.json | 5 + antlr/BUILD.bazel | 7 + antlr/PathPolicyConstraints.g4 | 72 + antlr/generate.sh | 1 + antlr/pathpolicyconstraints/BUILD.bazel | 30 + .../pathpolicyconstraints_base_listener.go | 143 ++ .../pathpolicyconstraints_lexer.go | 148 ++ .../pathpolicyconstraints_listener.go | 129 ++ .../pathpolicyconstraints_parser.go | 2017 +++++++++++++++++ control/BUILD.bazel | 1 + control/beaconing/BUILD.bazel | 2 + control/beaconing/extender.go | 20 + control/cmd/control/BUILD.bazel | 3 + control/cmd/control/main.go | 29 + control/config/BUILD.bazel | 5 + control/config/config.go | 30 + control/config/fabrid.go | 152 ++ control/config/fabrid_test.go | 174 ++ control/config/sample.go | 36 + control/fabrid/BUILD.bazel | 34 + control/fabrid/fabrid_manager.go | 172 ++ control/fabrid/fabrid_manager_test.go | 368 +++ control/fabrid/grpc/BUILD.bazel | 43 + control/fabrid/grpc/fabrid_service.go | 157 ++ control/fabrid/grpc/fabrid_service_test.go | 415 ++++ control/fabrid/grpc/fetcher.go | 178 ++ control/fabrid/grpc/fetcher_test.go | 171 ++ control/fabrid/grpc/mock_grpc/BUILD.bazel | 22 + control/fabrid/grpc/mock_grpc/mock.go | 67 + control/fabrid/mpls_map.go | 104 + control/fabrid/mpls_map_test.go | 236 ++ .../testdata/correct/1-global_example.yml | 11 + .../testdata/correct/2-global_example.yml | 10 + .../testdata/correct/3-local_example.yml | 17 + .../testdata/correct/55-local_example.yml | 13 + .../testdata/mixed/1-global_example.yml | 11 + control/fabrid/testdata/mixed/invalid.yml | 1 + control/mgmtapi/testdata/TestAPI_beacon_blob | 2 +- .../testdata/TestAPI_beacon_id_prefix_blob | 2 +- control/tasks.go | 3 + daemon/cmd/daemon/main.go | 23 +- daemon/daemon.go | 2 + daemon/drkey/BUILD.bazel | 5 +- daemon/drkey/client_engine_fabrid.go | 72 + daemon/internal/servers/BUILD.bazel | 6 + daemon/internal/servers/grpc.go | 19 +- daemon/internal/servers/grpc_fabrid.go | 195 ++ doc/command/scion/scion_ping.rst | 2 + doc/dev/design/FABRID.rst | 24 +- docker/BUILD.bazel | 4 + docker/endhost.bzl | 79 + docker/files/endhost.sh | 18 + pkg/addr/fmt.go | 2 +- pkg/daemon/BUILD.bazel | 2 + pkg/daemon/apitypes.go | 5 +- pkg/daemon/daemon.go | 2 + pkg/daemon/grpc.go | 15 +- pkg/daemon/grpc_fabrid.go | 93 + pkg/daemon/helper/BUILD.bazel | 16 + pkg/daemon/helper/protobuf.go | 163 ++ pkg/drkey/BUILD.bazel | 1 + pkg/drkey/drkey.go | 1 + pkg/drkey/drkey_fabrid.go | 38 + pkg/experimental/fabrid/BUILD.bazel | 8 + pkg/experimental/fabrid/crypto/BUILD.bazel | 33 + .../fabrid/crypto/fabrid_crypto.go | 267 +++ .../fabrid/crypto/fabrid_crypto_test.go | 280 +++ pkg/experimental/fabrid/defs.go | 33 + .../fabrid/graphutils/BUILD.bazel | 14 + pkg/experimental/fabrid/graphutils/maps.go | 101 + pkg/grpc/BUILD.bazel | 1 + pkg/grpc/dialer_drkey.go | 69 + pkg/private/xtest/graph/BUILD.bazel | 2 + pkg/private/xtest/graph/graph.go | 3 +- pkg/private/xtest/graph/graph_fabrid.go | 34 + .../control_plane/experimental/fabrid.pb.go | 1627 +++++++++++++ .../experimental/fabrid_extensions.pb.go | 590 +++++ .../seg_detached_extensions_fabrid.pb.go | 162 ++ pkg/proto/control_plane/seg_extensions.pb.go | 336 +-- pkg/proto/daemon/BUILD.bazel | 1 + pkg/proto/daemon/daemon.pb.go | 278 ++- pkg/proto/daemon/daemon_fabrid.pb.go | 531 +++++ pkg/proto/drkey/drkey.pb.go | 17 +- pkg/segment/BUILD.bazel | 3 + pkg/segment/extensions/digest/BUILD.bazel | 1 + pkg/segment/extensions/digest/digest.go | 35 +- pkg/segment/extensions/digest/digest_test.go | 27 +- pkg/segment/extensions/fabrid/BUILD.bazel | 22 + pkg/segment/extensions/fabrid/fabrid.go | 349 +++ pkg/segment/extensions/fabrid/fabrid_test.go | 598 +++++ pkg/segment/extensions_test.go | 4 + pkg/segment/unsigned.go | 49 +- pkg/segment/unsigned_test.go | 22 +- pkg/slayers/extension/BUILD.bazel | 28 + pkg/slayers/extension/fabrid.go | 207 ++ pkg/slayers/extension/fabrid_test.go | 209 ++ pkg/slayers/extension/identifier.go | 99 + pkg/slayers/extension/identifier_test.go | 142 ++ pkg/slayers/extn.go | 2 + pkg/snet/BUILD.bazel | 2 + pkg/snet/packet.go | 46 +- pkg/snet/path.go | 19 +- pkg/snet/path/BUILD.bazel | 6 + pkg/snet/path/fabrid.go | 221 ++ pkg/snet/path/path_ext.go | 36 + pkg/snet/path_fabrid.go | 85 + pkg/snet/reply_pather.go | 4 + private/app/path/BUILD.bazel | 1 + private/app/path/path.go | 210 +- .../api/testdata/segments-blob-by-id.txt | 8 +- private/path/combinator/BUILD.bazel | 1 + private/path/combinator/graph.go | 15 +- private/path/fabridquery/BUILD.bazel | 40 + private/path/fabridquery/matchlist.go | 69 + private/path/fabridquery/matchlist_test.go | 79 + private/path/fabridquery/parser.go | 266 +++ private/path/fabridquery/query.go | 225 ++ private/path/fabridquery/query_test.go | 173 ++ .../control_plane/experimental/v1/BUILD.bazel | 7 +- .../experimental/v1/fabrid.proto | 143 ++ .../experimental/v1/fabrid_extensions.proto | 78 + .../v1/seg_detached_extensions_fabrid.proto | 26 + proto/control_plane/v1/seg_extensions.proto | 14 + proto/daemon/v1/BUILD.bazel | 2 + proto/daemon/v1/daemon.proto | 11 + proto/daemon/v1/daemon_fabrid.proto | 67 + proto/drkey/v1/drkey.proto | 2 + router/BUILD.bazel | 9 + router/cmd/router/BUILD.bazel | 1 + router/cmd/router/main.go | 28 + router/config/config.go | 19 +- router/connector_fabrid.go | 30 + router/control/BUILD.bazel | 9 + router/control/conf.go | 3 + router/control/drkey.go | 133 ++ router/control/fetcher.go | 221 ++ router/dataplane.go | 91 +- router/dataplane_fabrid.go | 208 ++ router/dataplane_internal_fabrid_test.go | 166 ++ router/dataplane_test.go | 687 ++++++ router/export_test.go | 6 + router/mock_router/mock.go | 14 + scion/cmd/scion/ping.go | 97 +- tools/end2end/BUILD.bazel | 11 +- tools/end2end/fabrid.go | 204 ++ tools/end2end/main.go | 99 +- tools/end2end_integration/main.go | 28 +- tools/integration/BUILD.bazel | 1 + tools/integration/endhost_integration.go | 129 ++ tools/topogen.py | 5 + tools/topology/config.py | 20 +- tools/topology/docker_utils.py | 36 + tools/topology/endhost.py | 28 + tools/topology/go.py | 77 +- tools/topology/topo.py | 6 + tools/wireshark/scion.lua | 58 + 157 files changed, 15856 insertions(+), 438 deletions(-) create mode 100644 antlr/PathPolicyConstraints.g4 create mode 100644 antlr/pathpolicyconstraints/BUILD.bazel create mode 100644 antlr/pathpolicyconstraints/pathpolicyconstraints_base_listener.go create mode 100644 antlr/pathpolicyconstraints/pathpolicyconstraints_lexer.go create mode 100644 antlr/pathpolicyconstraints/pathpolicyconstraints_listener.go create mode 100644 antlr/pathpolicyconstraints/pathpolicyconstraints_parser.go create mode 100644 control/config/fabrid.go create mode 100644 control/config/fabrid_test.go create mode 100644 control/fabrid/BUILD.bazel create mode 100644 control/fabrid/fabrid_manager.go create mode 100644 control/fabrid/fabrid_manager_test.go create mode 100644 control/fabrid/grpc/BUILD.bazel create mode 100644 control/fabrid/grpc/fabrid_service.go create mode 100644 control/fabrid/grpc/fabrid_service_test.go create mode 100644 control/fabrid/grpc/fetcher.go create mode 100644 control/fabrid/grpc/fetcher_test.go create mode 100644 control/fabrid/grpc/mock_grpc/BUILD.bazel create mode 100644 control/fabrid/grpc/mock_grpc/mock.go create mode 100644 control/fabrid/mpls_map.go create mode 100644 control/fabrid/mpls_map_test.go create mode 100644 control/fabrid/testdata/correct/1-global_example.yml create mode 100644 control/fabrid/testdata/correct/2-global_example.yml create mode 100644 control/fabrid/testdata/correct/3-local_example.yml create mode 100644 control/fabrid/testdata/correct/55-local_example.yml create mode 100644 control/fabrid/testdata/mixed/1-global_example.yml create mode 100644 control/fabrid/testdata/mixed/invalid.yml create mode 100644 daemon/drkey/client_engine_fabrid.go create mode 100644 daemon/internal/servers/grpc_fabrid.go create mode 100644 docker/endhost.bzl create mode 100644 docker/files/endhost.sh create mode 100644 pkg/daemon/grpc_fabrid.go create mode 100644 pkg/daemon/helper/BUILD.bazel create mode 100644 pkg/daemon/helper/protobuf.go create mode 100644 pkg/drkey/drkey_fabrid.go create mode 100644 pkg/experimental/fabrid/BUILD.bazel create mode 100644 pkg/experimental/fabrid/crypto/BUILD.bazel create mode 100644 pkg/experimental/fabrid/crypto/fabrid_crypto.go create mode 100644 pkg/experimental/fabrid/crypto/fabrid_crypto_test.go create mode 100644 pkg/experimental/fabrid/defs.go create mode 100644 pkg/experimental/fabrid/graphutils/BUILD.bazel create mode 100644 pkg/experimental/fabrid/graphutils/maps.go create mode 100644 pkg/grpc/dialer_drkey.go create mode 100644 pkg/private/xtest/graph/graph_fabrid.go create mode 100755 pkg/proto/control_plane/experimental/fabrid.pb.go create mode 100755 pkg/proto/control_plane/experimental/fabrid_extensions.pb.go create mode 100755 pkg/proto/control_plane/experimental/seg_detached_extensions_fabrid.pb.go create mode 100644 pkg/proto/daemon/daemon_fabrid.pb.go create mode 100644 pkg/segment/extensions/fabrid/BUILD.bazel create mode 100644 pkg/segment/extensions/fabrid/fabrid.go create mode 100644 pkg/segment/extensions/fabrid/fabrid_test.go create mode 100644 pkg/slayers/extension/BUILD.bazel create mode 100644 pkg/slayers/extension/fabrid.go create mode 100644 pkg/slayers/extension/fabrid_test.go create mode 100644 pkg/slayers/extension/identifier.go create mode 100644 pkg/slayers/extension/identifier_test.go create mode 100644 pkg/snet/path/fabrid.go create mode 100644 pkg/snet/path/path_ext.go create mode 100644 pkg/snet/path_fabrid.go create mode 100644 private/path/fabridquery/BUILD.bazel create mode 100644 private/path/fabridquery/matchlist.go create mode 100644 private/path/fabridquery/matchlist_test.go create mode 100644 private/path/fabridquery/parser.go create mode 100644 private/path/fabridquery/query.go create mode 100644 private/path/fabridquery/query_test.go create mode 100644 proto/control_plane/experimental/v1/fabrid.proto create mode 100644 proto/control_plane/experimental/v1/fabrid_extensions.proto create mode 100644 proto/control_plane/experimental/v1/seg_detached_extensions_fabrid.proto create mode 100644 proto/daemon/v1/daemon_fabrid.proto create mode 100644 router/connector_fabrid.go create mode 100644 router/control/drkey.go create mode 100644 router/control/fetcher.go create mode 100644 router/dataplane_fabrid.go create mode 100644 router/dataplane_internal_fabrid_test.go create mode 100644 tools/end2end/fabrid.go create mode 100644 tools/integration/endhost_integration.go create mode 100644 tools/topology/endhost.py diff --git a/.github/workflows/gobra.yml b/.github/workflows/gobra.yml index 4dadbab9e8..5d544611d1 100644 --- a/.github/workflows/gobra.yml +++ b/.github/workflows/gobra.yml @@ -33,7 +33,7 @@ jobs: caching: '1' statsFile: ${{ env.statsFile }} - name: Upload the verification report - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: verification_stats.json path: ${{ env.statsFile }} diff --git a/acceptance/router_benchmark/conf/topology.json b/acceptance/router_benchmark/conf/topology.json index 6e48ac2cf4..1a09d0857e 100644 --- a/acceptance/router_benchmark/conf/topology.json +++ b/acceptance/router_benchmark/conf/topology.json @@ -5,6 +5,11 @@ "isd_as": "1-ff00:0:1", "mtu": 1400, "dispatched_ports": "1024-65535", + "control_service": { + "cs1": { + "addr": "10.123.10.3:30252" + } + }, "border_routers": { "br1a": { "internal_addr": "10.123.10.1:30042", diff --git a/antlr/BUILD.bazel b/antlr/BUILD.bazel index 1853d69656..dc333f4ceb 100644 --- a/antlr/BUILD.bazel +++ b/antlr/BUILD.bazel @@ -13,3 +13,10 @@ antlr( language = "Go", package = "sequence", ) + +antlr( + name = "pathpolicyconstraints", + srcs = ["PathPolicyConstraints.g4"], + language = "Go", + package = "pathpolicyconstraints", +) diff --git a/antlr/PathPolicyConstraints.g4 b/antlr/PathPolicyConstraints.g4 new file mode 100644 index 0000000000..6d9b2b0a99 --- /dev/null +++ b/antlr/PathPolicyConstraints.g4 @@ -0,0 +1,72 @@ + +grammar PathPolicyConstraints; + +fragment HEXA: [1-9a-fA-F][0-9a-fA-F]* | '0'; + +WHITESPACE: [ \t\r\n]+ -> skip; +ZERO: '0'; +NUM: [1-9][0-9]*; +WILDCARDAS: '-' '0'; +LEGACYAS: '-' NUM; +AS: '-' HEXA '_' HEXA '_' HEXA; +HASH: '#'; + +QUESTIONMARK: '?'; +ELSE: ':'; + +PLUS: '+'; +LPAR: '('; +RPAR: ')'; +LBRACE: '{'; +RBRACE: '}'; + +MONKEYTAIL: '@'; +GLOBALPOLICY: 'G'; +LOCALPOLICY: 'L'; +REJECT: 'REJECT'; + +start + : expression + ; + +query + : LBRACE expression QUESTIONMARK expression ELSE expression RBRACE #IfElse + | LBRACE expression QUESTIONMARK expression RBRACE #If + ; + +expression + : LPAR expression RPAR # Parens + | left=expression PLUS right=expression #ExpressionConcat + | identifier # ExpressionIdentifier + | query #ExpressionQuery + ; + +identifier: isd as HASH iface ',' iface MONKEYTAIL onepolicy; + +isd + : ZERO # WildcardISD + | NUM # ISD + ; + +as + : WILDCARDAS # WildcardAS + | LEGACYAS # LegacyAS + | AS # AS + ; + +iface + : ZERO # WildcardIFace + | NUM # IFace + ; + +onepolicy + : GLOBALPOLICY policyindex # GlobalPolicy + | LOCALPOLICY policyindex # LocalPolicy + | ZERO # WildcardPolicy + | REJECT # Reject + ; + +policyindex + : NUM # PolicyIndex + ; + diff --git a/antlr/generate.sh b/antlr/generate.sh index ad40f8236e..8899f38151 100755 --- a/antlr/generate.sh +++ b/antlr/generate.sh @@ -23,3 +23,4 @@ function generate { generate traffic_class generate sequence +generate pathpolicyconstraints diff --git a/antlr/pathpolicyconstraints/BUILD.bazel b/antlr/pathpolicyconstraints/BUILD.bazel new file mode 100644 index 0000000000..9ba976d431 --- /dev/null +++ b/antlr/pathpolicyconstraints/BUILD.bazel @@ -0,0 +1,30 @@ +load("//tools/lint:go.bzl", "go_library") +load("//tools/lint:go_config.bzl", "go_lint_config") +load("@apple_rules_lint//lint:defs.bzl", "package_lint_config") + +go_lint_config( + name = "go_lint_config", + exclude_filter = [ + "pathpolicyconstraints_parser.go", + ], + visibility = [ + "//visibility:public", + ], +) + +package_lint_config({ + "go": ":go_lint_config", +}) + +go_library( + name = "go_default_library", + srcs = [ + "pathpolicyconstraints_base_listener.go", + "pathpolicyconstraints_lexer.go", + "pathpolicyconstraints_listener.go", + "pathpolicyconstraints_parser.go", + ], + importpath = "github.com/scionproto/scion/antlr/pathpolicyconstraints", + visibility = ["//visibility:public"], + deps = ["@com_github_antlr_antlr4_runtime_go_antlr//:go_default_library"], +) diff --git a/antlr/pathpolicyconstraints/pathpolicyconstraints_base_listener.go b/antlr/pathpolicyconstraints/pathpolicyconstraints_base_listener.go new file mode 100644 index 0000000000..06865b816d --- /dev/null +++ b/antlr/pathpolicyconstraints/pathpolicyconstraints_base_listener.go @@ -0,0 +1,143 @@ +// File generated by ANTLR. DO NOT EDIT. + +package pathpolicyconstraints // PathPolicyConstraints +import "github.com/antlr/antlr4/runtime/Go/antlr" + +// BasePathPolicyConstraintsListener is a complete listener for a parse tree produced by PathPolicyConstraintsParser. +type BasePathPolicyConstraintsListener struct{} + +var _ PathPolicyConstraintsListener = &BasePathPolicyConstraintsListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BasePathPolicyConstraintsListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BasePathPolicyConstraintsListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BasePathPolicyConstraintsListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BasePathPolicyConstraintsListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterStart is called when production start is entered. +func (s *BasePathPolicyConstraintsListener) EnterStart(ctx *StartContext) {} + +// ExitStart is called when production start is exited. +func (s *BasePathPolicyConstraintsListener) ExitStart(ctx *StartContext) {} + +// EnterIfElse is called when production IfElse is entered. +func (s *BasePathPolicyConstraintsListener) EnterIfElse(ctx *IfElseContext) {} + +// ExitIfElse is called when production IfElse is exited. +func (s *BasePathPolicyConstraintsListener) ExitIfElse(ctx *IfElseContext) {} + +// EnterIf is called when production If is entered. +func (s *BasePathPolicyConstraintsListener) EnterIf(ctx *IfContext) {} + +// ExitIf is called when production If is exited. +func (s *BasePathPolicyConstraintsListener) ExitIf(ctx *IfContext) {} + +// EnterParens is called when production Parens is entered. +func (s *BasePathPolicyConstraintsListener) EnterParens(ctx *ParensContext) {} + +// ExitParens is called when production Parens is exited. +func (s *BasePathPolicyConstraintsListener) ExitParens(ctx *ParensContext) {} + +// EnterExpressionIdentifier is called when production ExpressionIdentifier is entered. +func (s *BasePathPolicyConstraintsListener) EnterExpressionIdentifier(ctx *ExpressionIdentifierContext) { +} + +// ExitExpressionIdentifier is called when production ExpressionIdentifier is exited. +func (s *BasePathPolicyConstraintsListener) ExitExpressionIdentifier(ctx *ExpressionIdentifierContext) { +} + +// EnterExpressionConcat is called when production ExpressionConcat is entered. +func (s *BasePathPolicyConstraintsListener) EnterExpressionConcat(ctx *ExpressionConcatContext) {} + +// ExitExpressionConcat is called when production ExpressionConcat is exited. +func (s *BasePathPolicyConstraintsListener) ExitExpressionConcat(ctx *ExpressionConcatContext) {} + +// EnterExpressionQuery is called when production ExpressionQuery is entered. +func (s *BasePathPolicyConstraintsListener) EnterExpressionQuery(ctx *ExpressionQueryContext) {} + +// ExitExpressionQuery is called when production ExpressionQuery is exited. +func (s *BasePathPolicyConstraintsListener) ExitExpressionQuery(ctx *ExpressionQueryContext) {} + +// EnterIdentifier is called when production identifier is entered. +func (s *BasePathPolicyConstraintsListener) EnterIdentifier(ctx *IdentifierContext) {} + +// ExitIdentifier is called when production identifier is exited. +func (s *BasePathPolicyConstraintsListener) ExitIdentifier(ctx *IdentifierContext) {} + +// EnterWildcardISD is called when production WildcardISD is entered. +func (s *BasePathPolicyConstraintsListener) EnterWildcardISD(ctx *WildcardISDContext) {} + +// ExitWildcardISD is called when production WildcardISD is exited. +func (s *BasePathPolicyConstraintsListener) ExitWildcardISD(ctx *WildcardISDContext) {} + +// EnterISD is called when production ISD is entered. +func (s *BasePathPolicyConstraintsListener) EnterISD(ctx *ISDContext) {} + +// ExitISD is called when production ISD is exited. +func (s *BasePathPolicyConstraintsListener) ExitISD(ctx *ISDContext) {} + +// EnterWildcardAS is called when production WildcardAS is entered. +func (s *BasePathPolicyConstraintsListener) EnterWildcardAS(ctx *WildcardASContext) {} + +// ExitWildcardAS is called when production WildcardAS is exited. +func (s *BasePathPolicyConstraintsListener) ExitWildcardAS(ctx *WildcardASContext) {} + +// EnterLegacyAS is called when production LegacyAS is entered. +func (s *BasePathPolicyConstraintsListener) EnterLegacyAS(ctx *LegacyASContext) {} + +// ExitLegacyAS is called when production LegacyAS is exited. +func (s *BasePathPolicyConstraintsListener) ExitLegacyAS(ctx *LegacyASContext) {} + +// EnterAS is called when production AS is entered. +func (s *BasePathPolicyConstraintsListener) EnterAS(ctx *ASContext) {} + +// ExitAS is called when production AS is exited. +func (s *BasePathPolicyConstraintsListener) ExitAS(ctx *ASContext) {} + +// EnterWildcardIFace is called when production WildcardIFace is entered. +func (s *BasePathPolicyConstraintsListener) EnterWildcardIFace(ctx *WildcardIFaceContext) {} + +// ExitWildcardIFace is called when production WildcardIFace is exited. +func (s *BasePathPolicyConstraintsListener) ExitWildcardIFace(ctx *WildcardIFaceContext) {} + +// EnterIFace is called when production IFace is entered. +func (s *BasePathPolicyConstraintsListener) EnterIFace(ctx *IFaceContext) {} + +// ExitIFace is called when production IFace is exited. +func (s *BasePathPolicyConstraintsListener) ExitIFace(ctx *IFaceContext) {} + +// EnterGlobalPolicy is called when production GlobalPolicy is entered. +func (s *BasePathPolicyConstraintsListener) EnterGlobalPolicy(ctx *GlobalPolicyContext) {} + +// ExitGlobalPolicy is called when production GlobalPolicy is exited. +func (s *BasePathPolicyConstraintsListener) ExitGlobalPolicy(ctx *GlobalPolicyContext) {} + +// EnterLocalPolicy is called when production LocalPolicy is entered. +func (s *BasePathPolicyConstraintsListener) EnterLocalPolicy(ctx *LocalPolicyContext) {} + +// ExitLocalPolicy is called when production LocalPolicy is exited. +func (s *BasePathPolicyConstraintsListener) ExitLocalPolicy(ctx *LocalPolicyContext) {} + +// EnterWildcardPolicy is called when production WildcardPolicy is entered. +func (s *BasePathPolicyConstraintsListener) EnterWildcardPolicy(ctx *WildcardPolicyContext) {} + +// ExitWildcardPolicy is called when production WildcardPolicy is exited. +func (s *BasePathPolicyConstraintsListener) ExitWildcardPolicy(ctx *WildcardPolicyContext) {} + +// EnterReject is called when production Reject is entered. +func (s *BasePathPolicyConstraintsListener) EnterReject(ctx *RejectContext) {} + +// ExitReject is called when production Reject is exited. +func (s *BasePathPolicyConstraintsListener) ExitReject(ctx *RejectContext) {} + +// EnterPolicyIndex is called when production PolicyIndex is entered. +func (s *BasePathPolicyConstraintsListener) EnterPolicyIndex(ctx *PolicyIndexContext) {} + +// ExitPolicyIndex is called when production PolicyIndex is exited. +func (s *BasePathPolicyConstraintsListener) ExitPolicyIndex(ctx *PolicyIndexContext) {} diff --git a/antlr/pathpolicyconstraints/pathpolicyconstraints_lexer.go b/antlr/pathpolicyconstraints/pathpolicyconstraints_lexer.go new file mode 100644 index 0000000000..8927f64c4a --- /dev/null +++ b/antlr/pathpolicyconstraints/pathpolicyconstraints_lexer.go @@ -0,0 +1,148 @@ +// File generated by ANTLR. DO NOT EDIT. + +package pathpolicyconstraints + +import ( + "fmt" + "unicode" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = unicode.IsLetter + +var serializedLexerAtn = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 21, 113, + 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, + 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, + 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, + 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 3, 2, 3, 2, 3, 3, + 3, 3, 7, 3, 48, 10, 3, 12, 3, 14, 3, 51, 11, 3, 3, 3, 5, 3, 54, 10, 3, + 3, 4, 6, 4, 57, 10, 4, 13, 4, 14, 4, 58, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, + 3, 6, 7, 6, 67, 10, 6, 12, 6, 14, 6, 70, 11, 6, 3, 7, 3, 7, 3, 7, 3, 8, + 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, + 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, + 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, + 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 2, 2, 22, 3, 3, 5, 2, 7, 4, 9, 5, + 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, + 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 3, 2, 7, 5, 2, 51, + 59, 67, 72, 99, 104, 5, 2, 50, 59, 67, 72, 99, 104, 5, 2, 11, 12, 15, 15, + 34, 34, 3, 2, 51, 59, 3, 2, 50, 59, 2, 115, 2, 3, 3, 2, 2, 2, 2, 7, 3, + 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, + 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, + 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, + 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, + 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 3, 43, 3, 2, 2, 2, 5, 53, 3, 2, + 2, 2, 7, 56, 3, 2, 2, 2, 9, 62, 3, 2, 2, 2, 11, 64, 3, 2, 2, 2, 13, 71, + 3, 2, 2, 2, 15, 74, 3, 2, 2, 2, 17, 77, 3, 2, 2, 2, 19, 84, 3, 2, 2, 2, + 21, 86, 3, 2, 2, 2, 23, 88, 3, 2, 2, 2, 25, 90, 3, 2, 2, 2, 27, 92, 3, + 2, 2, 2, 29, 94, 3, 2, 2, 2, 31, 96, 3, 2, 2, 2, 33, 98, 3, 2, 2, 2, 35, + 100, 3, 2, 2, 2, 37, 102, 3, 2, 2, 2, 39, 104, 3, 2, 2, 2, 41, 106, 3, + 2, 2, 2, 43, 44, 7, 46, 2, 2, 44, 4, 3, 2, 2, 2, 45, 49, 9, 2, 2, 2, 46, + 48, 9, 3, 2, 2, 47, 46, 3, 2, 2, 2, 48, 51, 3, 2, 2, 2, 49, 47, 3, 2, 2, + 2, 49, 50, 3, 2, 2, 2, 50, 54, 3, 2, 2, 2, 51, 49, 3, 2, 2, 2, 52, 54, + 7, 50, 2, 2, 53, 45, 3, 2, 2, 2, 53, 52, 3, 2, 2, 2, 54, 6, 3, 2, 2, 2, + 55, 57, 9, 4, 2, 2, 56, 55, 3, 2, 2, 2, 57, 58, 3, 2, 2, 2, 58, 56, 3, + 2, 2, 2, 58, 59, 3, 2, 2, 2, 59, 60, 3, 2, 2, 2, 60, 61, 8, 4, 2, 2, 61, + 8, 3, 2, 2, 2, 62, 63, 7, 50, 2, 2, 63, 10, 3, 2, 2, 2, 64, 68, 9, 5, 2, + 2, 65, 67, 9, 6, 2, 2, 66, 65, 3, 2, 2, 2, 67, 70, 3, 2, 2, 2, 68, 66, + 3, 2, 2, 2, 68, 69, 3, 2, 2, 2, 69, 12, 3, 2, 2, 2, 70, 68, 3, 2, 2, 2, + 71, 72, 7, 47, 2, 2, 72, 73, 7, 50, 2, 2, 73, 14, 3, 2, 2, 2, 74, 75, 7, + 47, 2, 2, 75, 76, 5, 11, 6, 2, 76, 16, 3, 2, 2, 2, 77, 78, 7, 47, 2, 2, + 78, 79, 5, 5, 3, 2, 79, 80, 7, 97, 2, 2, 80, 81, 5, 5, 3, 2, 81, 82, 7, + 97, 2, 2, 82, 83, 5, 5, 3, 2, 83, 18, 3, 2, 2, 2, 84, 85, 7, 37, 2, 2, + 85, 20, 3, 2, 2, 2, 86, 87, 7, 65, 2, 2, 87, 22, 3, 2, 2, 2, 88, 89, 7, + 60, 2, 2, 89, 24, 3, 2, 2, 2, 90, 91, 7, 45, 2, 2, 91, 26, 3, 2, 2, 2, + 92, 93, 7, 42, 2, 2, 93, 28, 3, 2, 2, 2, 94, 95, 7, 43, 2, 2, 95, 30, 3, + 2, 2, 2, 96, 97, 7, 125, 2, 2, 97, 32, 3, 2, 2, 2, 98, 99, 7, 127, 2, 2, + 99, 34, 3, 2, 2, 2, 100, 101, 7, 66, 2, 2, 101, 36, 3, 2, 2, 2, 102, 103, + 7, 73, 2, 2, 103, 38, 3, 2, 2, 2, 104, 105, 7, 78, 2, 2, 105, 40, 3, 2, + 2, 2, 106, 107, 7, 84, 2, 2, 107, 108, 7, 71, 2, 2, 108, 109, 7, 76, 2, + 2, 109, 110, 7, 71, 2, 2, 110, 111, 7, 69, 2, 2, 111, 112, 7, 86, 2, 2, + 112, 42, 3, 2, 2, 2, 7, 2, 49, 53, 58, 68, 3, 8, 2, 2, +} + +var lexerChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", +} + +var lexerModeNames = []string{ + "DEFAULT_MODE", +} + +var lexerLiteralNames = []string{ + "", "','", "", "'0'", "", "", "", "", "'#'", "'?'", "':'", "'+'", "'('", + "')'", "'{'", "'}'", "'@'", "'G'", "'L'", "'REJECT'", +} + +var lexerSymbolicNames = []string{ + "", "", "WHITESPACE", "ZERO", "NUM", "WILDCARDAS", "LEGACYAS", "AS", "HASH", + "QUESTIONMARK", "ELSE", "PLUS", "LPAR", "RPAR", "LBRACE", "RBRACE", "MONKEYTAIL", + "GLOBALPOLICY", "LOCALPOLICY", "REJECT", +} + +var lexerRuleNames = []string{ + "T__0", "HEXA", "WHITESPACE", "ZERO", "NUM", "WILDCARDAS", "LEGACYAS", + "AS", "HASH", "QUESTIONMARK", "ELSE", "PLUS", "LPAR", "RPAR", "LBRACE", + "RBRACE", "MONKEYTAIL", "GLOBALPOLICY", "LOCALPOLICY", "REJECT", +} + +type PathPolicyConstraintsLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +// NewPathPolicyConstraintsLexer produces a new lexer instance for the optional input antlr.CharStream. +// +// The *PathPolicyConstraintsLexer instance produced may be reused by calling the SetInputStream method. +// The initial lexer configuration is expensive to construct, and the object is not thread-safe; +// however, if used within a Golang sync.Pool, the construction cost amortizes well and the +// objects can be used in a thread-safe manner. +func NewPathPolicyConstraintsLexer(input antlr.CharStream) *PathPolicyConstraintsLexer { + l := new(PathPolicyConstraintsLexer) + lexerDeserializer := antlr.NewATNDeserializer(nil) + lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn) + lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState)) + for index, ds := range lexerAtn.DecisionToState { + lexerDecisionToDFA[index] = antlr.NewDFA(ds, index) + } + l.BaseLexer = antlr.NewBaseLexer(input) + l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache()) + + l.channelNames = lexerChannelNames + l.modeNames = lexerModeNames + l.RuleNames = lexerRuleNames + l.LiteralNames = lexerLiteralNames + l.SymbolicNames = lexerSymbolicNames + l.GrammarFileName = "PathPolicyConstraints.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// PathPolicyConstraintsLexer tokens. +const ( + PathPolicyConstraintsLexerT__0 = 1 + PathPolicyConstraintsLexerWHITESPACE = 2 + PathPolicyConstraintsLexerZERO = 3 + PathPolicyConstraintsLexerNUM = 4 + PathPolicyConstraintsLexerWILDCARDAS = 5 + PathPolicyConstraintsLexerLEGACYAS = 6 + PathPolicyConstraintsLexerAS = 7 + PathPolicyConstraintsLexerHASH = 8 + PathPolicyConstraintsLexerQUESTIONMARK = 9 + PathPolicyConstraintsLexerELSE = 10 + PathPolicyConstraintsLexerPLUS = 11 + PathPolicyConstraintsLexerLPAR = 12 + PathPolicyConstraintsLexerRPAR = 13 + PathPolicyConstraintsLexerLBRACE = 14 + PathPolicyConstraintsLexerRBRACE = 15 + PathPolicyConstraintsLexerMONKEYTAIL = 16 + PathPolicyConstraintsLexerGLOBALPOLICY = 17 + PathPolicyConstraintsLexerLOCALPOLICY = 18 + PathPolicyConstraintsLexerREJECT = 19 +) diff --git a/antlr/pathpolicyconstraints/pathpolicyconstraints_listener.go b/antlr/pathpolicyconstraints/pathpolicyconstraints_listener.go new file mode 100644 index 0000000000..11ae254a3e --- /dev/null +++ b/antlr/pathpolicyconstraints/pathpolicyconstraints_listener.go @@ -0,0 +1,129 @@ +// File generated by ANTLR. DO NOT EDIT. + +package pathpolicyconstraints // PathPolicyConstraints +import "github.com/antlr/antlr4/runtime/Go/antlr" + +// PathPolicyConstraintsListener is a complete listener for a parse tree produced by PathPolicyConstraintsParser. +type PathPolicyConstraintsListener interface { + antlr.ParseTreeListener + + // EnterStart is called when entering the start production. + EnterStart(c *StartContext) + + // EnterIfElse is called when entering the IfElse production. + EnterIfElse(c *IfElseContext) + + // EnterIf is called when entering the If production. + EnterIf(c *IfContext) + + // EnterParens is called when entering the Parens production. + EnterParens(c *ParensContext) + + // EnterExpressionIdentifier is called when entering the ExpressionIdentifier production. + EnterExpressionIdentifier(c *ExpressionIdentifierContext) + + // EnterExpressionConcat is called when entering the ExpressionConcat production. + EnterExpressionConcat(c *ExpressionConcatContext) + + // EnterExpressionQuery is called when entering the ExpressionQuery production. + EnterExpressionQuery(c *ExpressionQueryContext) + + // EnterIdentifier is called when entering the identifier production. + EnterIdentifier(c *IdentifierContext) + + // EnterWildcardISD is called when entering the WildcardISD production. + EnterWildcardISD(c *WildcardISDContext) + + // EnterISD is called when entering the ISD production. + EnterISD(c *ISDContext) + + // EnterWildcardAS is called when entering the WildcardAS production. + EnterWildcardAS(c *WildcardASContext) + + // EnterLegacyAS is called when entering the LegacyAS production. + EnterLegacyAS(c *LegacyASContext) + + // EnterAS is called when entering the AS production. + EnterAS(c *ASContext) + + // EnterWildcardIFace is called when entering the WildcardIFace production. + EnterWildcardIFace(c *WildcardIFaceContext) + + // EnterIFace is called when entering the IFace production. + EnterIFace(c *IFaceContext) + + // EnterGlobalPolicy is called when entering the GlobalPolicy production. + EnterGlobalPolicy(c *GlobalPolicyContext) + + // EnterLocalPolicy is called when entering the LocalPolicy production. + EnterLocalPolicy(c *LocalPolicyContext) + + // EnterWildcardPolicy is called when entering the WildcardPolicy production. + EnterWildcardPolicy(c *WildcardPolicyContext) + + // EnterReject is called when entering the Reject production. + EnterReject(c *RejectContext) + + // EnterPolicyIndex is called when entering the PolicyIndex production. + EnterPolicyIndex(c *PolicyIndexContext) + + // ExitStart is called when exiting the start production. + ExitStart(c *StartContext) + + // ExitIfElse is called when exiting the IfElse production. + ExitIfElse(c *IfElseContext) + + // ExitIf is called when exiting the If production. + ExitIf(c *IfContext) + + // ExitParens is called when exiting the Parens production. + ExitParens(c *ParensContext) + + // ExitExpressionIdentifier is called when exiting the ExpressionIdentifier production. + ExitExpressionIdentifier(c *ExpressionIdentifierContext) + + // ExitExpressionConcat is called when exiting the ExpressionConcat production. + ExitExpressionConcat(c *ExpressionConcatContext) + + // ExitExpressionQuery is called when exiting the ExpressionQuery production. + ExitExpressionQuery(c *ExpressionQueryContext) + + // ExitIdentifier is called when exiting the identifier production. + ExitIdentifier(c *IdentifierContext) + + // ExitWildcardISD is called when exiting the WildcardISD production. + ExitWildcardISD(c *WildcardISDContext) + + // ExitISD is called when exiting the ISD production. + ExitISD(c *ISDContext) + + // ExitWildcardAS is called when exiting the WildcardAS production. + ExitWildcardAS(c *WildcardASContext) + + // ExitLegacyAS is called when exiting the LegacyAS production. + ExitLegacyAS(c *LegacyASContext) + + // ExitAS is called when exiting the AS production. + ExitAS(c *ASContext) + + // ExitWildcardIFace is called when exiting the WildcardIFace production. + ExitWildcardIFace(c *WildcardIFaceContext) + + // ExitIFace is called when exiting the IFace production. + ExitIFace(c *IFaceContext) + + // ExitGlobalPolicy is called when exiting the GlobalPolicy production. + ExitGlobalPolicy(c *GlobalPolicyContext) + + // ExitLocalPolicy is called when exiting the LocalPolicy production. + ExitLocalPolicy(c *LocalPolicyContext) + + // ExitWildcardPolicy is called when exiting the WildcardPolicy production. + ExitWildcardPolicy(c *WildcardPolicyContext) + + // ExitReject is called when exiting the Reject production. + ExitReject(c *RejectContext) + + // ExitPolicyIndex is called when exiting the PolicyIndex production. + ExitPolicyIndex(c *PolicyIndexContext) +} diff --git a/antlr/pathpolicyconstraints/pathpolicyconstraints_parser.go b/antlr/pathpolicyconstraints/pathpolicyconstraints_parser.go new file mode 100644 index 0000000000..6644ae29e9 --- /dev/null +++ b/antlr/pathpolicyconstraints/pathpolicyconstraints_parser.go @@ -0,0 +1,2017 @@ +// File generated by ANTLR. DO NOT EDIT. + +package pathpolicyconstraints // PathPolicyConstraints +import ( + "fmt" + "reflect" + "strconv" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = reflect.Copy +var _ = strconv.Itoa + +var parserATN = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 21, 88, 4, + 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, + 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 37, 10, + 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 46, 10, 4, 3, 4, 3, + 4, 3, 4, 7, 4, 51, 10, 4, 12, 4, 14, 4, 54, 11, 4, 3, 5, 3, 5, 3, 5, 3, + 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 5, 6, 67, 10, 6, 3, 7, 3, + 7, 3, 7, 5, 7, 72, 10, 7, 3, 8, 3, 8, 5, 8, 76, 10, 8, 3, 9, 3, 9, 3, 9, + 3, 9, 3, 9, 3, 9, 5, 9, 84, 10, 9, 3, 10, 3, 10, 3, 10, 2, 3, 6, 11, 2, + 4, 6, 8, 10, 12, 14, 16, 18, 2, 2, 2, 89, 2, 20, 3, 2, 2, 2, 4, 36, 3, + 2, 2, 2, 6, 45, 3, 2, 2, 2, 8, 55, 3, 2, 2, 2, 10, 66, 3, 2, 2, 2, 12, + 71, 3, 2, 2, 2, 14, 75, 3, 2, 2, 2, 16, 83, 3, 2, 2, 2, 18, 85, 3, 2, 2, + 2, 20, 21, 5, 6, 4, 2, 21, 3, 3, 2, 2, 2, 22, 23, 7, 16, 2, 2, 23, 24, + 5, 6, 4, 2, 24, 25, 7, 11, 2, 2, 25, 26, 5, 6, 4, 2, 26, 27, 7, 12, 2, + 2, 27, 28, 5, 6, 4, 2, 28, 29, 7, 17, 2, 2, 29, 37, 3, 2, 2, 2, 30, 31, + 7, 16, 2, 2, 31, 32, 5, 6, 4, 2, 32, 33, 7, 11, 2, 2, 33, 34, 5, 6, 4, + 2, 34, 35, 7, 17, 2, 2, 35, 37, 3, 2, 2, 2, 36, 22, 3, 2, 2, 2, 36, 30, + 3, 2, 2, 2, 37, 5, 3, 2, 2, 2, 38, 39, 8, 4, 1, 2, 39, 40, 7, 14, 2, 2, + 40, 41, 5, 6, 4, 2, 41, 42, 7, 15, 2, 2, 42, 46, 3, 2, 2, 2, 43, 46, 5, + 8, 5, 2, 44, 46, 5, 4, 3, 2, 45, 38, 3, 2, 2, 2, 45, 43, 3, 2, 2, 2, 45, + 44, 3, 2, 2, 2, 46, 52, 3, 2, 2, 2, 47, 48, 12, 5, 2, 2, 48, 49, 7, 13, + 2, 2, 49, 51, 5, 6, 4, 6, 50, 47, 3, 2, 2, 2, 51, 54, 3, 2, 2, 2, 52, 50, + 3, 2, 2, 2, 52, 53, 3, 2, 2, 2, 53, 7, 3, 2, 2, 2, 54, 52, 3, 2, 2, 2, + 55, 56, 5, 10, 6, 2, 56, 57, 5, 12, 7, 2, 57, 58, 7, 10, 2, 2, 58, 59, + 5, 14, 8, 2, 59, 60, 7, 3, 2, 2, 60, 61, 5, 14, 8, 2, 61, 62, 7, 18, 2, + 2, 62, 63, 5, 16, 9, 2, 63, 9, 3, 2, 2, 2, 64, 67, 7, 5, 2, 2, 65, 67, + 7, 6, 2, 2, 66, 64, 3, 2, 2, 2, 66, 65, 3, 2, 2, 2, 67, 11, 3, 2, 2, 2, + 68, 72, 7, 7, 2, 2, 69, 72, 7, 8, 2, 2, 70, 72, 7, 9, 2, 2, 71, 68, 3, + 2, 2, 2, 71, 69, 3, 2, 2, 2, 71, 70, 3, 2, 2, 2, 72, 13, 3, 2, 2, 2, 73, + 76, 7, 5, 2, 2, 74, 76, 7, 6, 2, 2, 75, 73, 3, 2, 2, 2, 75, 74, 3, 2, 2, + 2, 76, 15, 3, 2, 2, 2, 77, 78, 7, 19, 2, 2, 78, 84, 5, 18, 10, 2, 79, 80, + 7, 20, 2, 2, 80, 84, 5, 18, 10, 2, 81, 84, 7, 5, 2, 2, 82, 84, 7, 21, 2, + 2, 83, 77, 3, 2, 2, 2, 83, 79, 3, 2, 2, 2, 83, 81, 3, 2, 2, 2, 83, 82, + 3, 2, 2, 2, 84, 17, 3, 2, 2, 2, 85, 86, 7, 6, 2, 2, 86, 19, 3, 2, 2, 2, + 9, 36, 45, 52, 66, 71, 75, 83, +} +var literalNames = []string{ + "", "','", "", "'0'", "", "", "", "", "'#'", "'?'", "':'", "'+'", "'('", + "')'", "'{'", "'}'", "'@'", "'G'", "'L'", "'REJECT'", +} +var symbolicNames = []string{ + "", "", "WHITESPACE", "ZERO", "NUM", "WILDCARDAS", "LEGACYAS", "AS", "HASH", + "QUESTIONMARK", "ELSE", "PLUS", "LPAR", "RPAR", "LBRACE", "RBRACE", "MONKEYTAIL", + "GLOBALPOLICY", "LOCALPOLICY", "REJECT", +} + +var ruleNames = []string{ + "start", "query", "expression", "identifier", "isd", "as", "iface", "onepolicy", + "policyindex", +} + +type PathPolicyConstraintsParser struct { + *antlr.BaseParser +} + +// NewPathPolicyConstraintsParser produces a new parser instance for the optional input antlr.TokenStream. +// +// The *PathPolicyConstraintsParser instance produced may be reused by calling the SetInputStream method. +// The initial parser configuration is expensive to construct, and the object is not thread-safe; +// however, if used within a Golang sync.Pool, the construction cost amortizes well and the +// objects can be used in a thread-safe manner. +func NewPathPolicyConstraintsParser(input antlr.TokenStream) *PathPolicyConstraintsParser { + this := new(PathPolicyConstraintsParser) + deserializer := antlr.NewATNDeserializer(nil) + deserializedATN := deserializer.DeserializeFromUInt16(parserATN) + decisionToDFA := make([]*antlr.DFA, len(deserializedATN.DecisionToState)) + for index, ds := range deserializedATN.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(ds, index) + } + this.BaseParser = antlr.NewBaseParser(input) + + this.Interpreter = antlr.NewParserATNSimulator(this, deserializedATN, decisionToDFA, antlr.NewPredictionContextCache()) + this.RuleNames = ruleNames + this.LiteralNames = literalNames + this.SymbolicNames = symbolicNames + this.GrammarFileName = "PathPolicyConstraints.g4" + + return this +} + +// PathPolicyConstraintsParser tokens. +const ( + PathPolicyConstraintsParserEOF = antlr.TokenEOF + PathPolicyConstraintsParserT__0 = 1 + PathPolicyConstraintsParserWHITESPACE = 2 + PathPolicyConstraintsParserZERO = 3 + PathPolicyConstraintsParserNUM = 4 + PathPolicyConstraintsParserWILDCARDAS = 5 + PathPolicyConstraintsParserLEGACYAS = 6 + PathPolicyConstraintsParserAS = 7 + PathPolicyConstraintsParserHASH = 8 + PathPolicyConstraintsParserQUESTIONMARK = 9 + PathPolicyConstraintsParserELSE = 10 + PathPolicyConstraintsParserPLUS = 11 + PathPolicyConstraintsParserLPAR = 12 + PathPolicyConstraintsParserRPAR = 13 + PathPolicyConstraintsParserLBRACE = 14 + PathPolicyConstraintsParserRBRACE = 15 + PathPolicyConstraintsParserMONKEYTAIL = 16 + PathPolicyConstraintsParserGLOBALPOLICY = 17 + PathPolicyConstraintsParserLOCALPOLICY = 18 + PathPolicyConstraintsParserREJECT = 19 +) + +// PathPolicyConstraintsParser rules. +const ( + PathPolicyConstraintsParserRULE_start = 0 + PathPolicyConstraintsParserRULE_query = 1 + PathPolicyConstraintsParserRULE_expression = 2 + PathPolicyConstraintsParserRULE_identifier = 3 + PathPolicyConstraintsParserRULE_isd = 4 + PathPolicyConstraintsParserRULE_as = 5 + PathPolicyConstraintsParserRULE_iface = 6 + PathPolicyConstraintsParserRULE_onepolicy = 7 + PathPolicyConstraintsParserRULE_policyindex = 8 +) + +// IStartContext is an interface to support dynamic dispatch. +type IStartContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsStartContext differentiates from other interfaces. + IsStartContext() +} + +type StartContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyStartContext() *StartContext { + var p = new(StartContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_start + return p +} + +func (*StartContext) IsStartContext() {} + +func NewStartContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StartContext { + var p = new(StartContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_start + + return p +} + +func (s *StartContext) GetParser() antlr.Parser { return s.parser } + +func (s *StartContext) Expression() IExpressionContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExpressionContext) +} + +func (s *StartContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *StartContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *StartContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterStart(s) + } +} + +func (s *StartContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitStart(s) + } +} + +func (p *PathPolicyConstraintsParser) Start() (localctx IStartContext) { + this := p + _ = this + + localctx = NewStartContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, PathPolicyConstraintsParserRULE_start) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(18) + p.expression(0) + } + + return localctx +} + +// IQueryContext is an interface to support dynamic dispatch. +type IQueryContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsQueryContext differentiates from other interfaces. + IsQueryContext() +} + +type QueryContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyQueryContext() *QueryContext { + var p = new(QueryContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_query + return p +} + +func (*QueryContext) IsQueryContext() {} + +func NewQueryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *QueryContext { + var p = new(QueryContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_query + + return p +} + +func (s *QueryContext) GetParser() antlr.Parser { return s.parser } + +func (s *QueryContext) CopyFrom(ctx *QueryContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *QueryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *QueryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type IfElseContext struct { + *QueryContext +} + +func NewIfElseContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IfElseContext { + var p = new(IfElseContext) + + p.QueryContext = NewEmptyQueryContext() + p.parser = parser + p.CopyFrom(ctx.(*QueryContext)) + + return p +} + +func (s *IfElseContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IfElseContext) LBRACE() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserLBRACE, 0) +} + +func (s *IfElseContext) AllExpression() []IExpressionContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem()) + var tst = make([]IExpressionContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IExpressionContext) + } + } + + return tst +} + +func (s *IfElseContext) Expression(i int) IExpressionContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IExpressionContext) +} + +func (s *IfElseContext) QUESTIONMARK() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserQUESTIONMARK, 0) +} + +func (s *IfElseContext) ELSE() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserELSE, 0) +} + +func (s *IfElseContext) RBRACE() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserRBRACE, 0) +} + +func (s *IfElseContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterIfElse(s) + } +} + +func (s *IfElseContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitIfElse(s) + } +} + +type IfContext struct { + *QueryContext +} + +func NewIfContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IfContext { + var p = new(IfContext) + + p.QueryContext = NewEmptyQueryContext() + p.parser = parser + p.CopyFrom(ctx.(*QueryContext)) + + return p +} + +func (s *IfContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IfContext) LBRACE() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserLBRACE, 0) +} + +func (s *IfContext) AllExpression() []IExpressionContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem()) + var tst = make([]IExpressionContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IExpressionContext) + } + } + + return tst +} + +func (s *IfContext) Expression(i int) IExpressionContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IExpressionContext) +} + +func (s *IfContext) QUESTIONMARK() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserQUESTIONMARK, 0) +} + +func (s *IfContext) RBRACE() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserRBRACE, 0) +} + +func (s *IfContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterIf(s) + } +} + +func (s *IfContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitIf(s) + } +} + +func (p *PathPolicyConstraintsParser) Query() (localctx IQueryContext) { + this := p + _ = this + + localctx = NewQueryContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, PathPolicyConstraintsParserRULE_query) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(34) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 0, p.GetParserRuleContext()) { + case 1: + localctx = NewIfElseContext(p, localctx) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(20) + p.Match(PathPolicyConstraintsParserLBRACE) + } + { + p.SetState(21) + p.expression(0) + } + { + p.SetState(22) + p.Match(PathPolicyConstraintsParserQUESTIONMARK) + } + { + p.SetState(23) + p.expression(0) + } + { + p.SetState(24) + p.Match(PathPolicyConstraintsParserELSE) + } + { + p.SetState(25) + p.expression(0) + } + { + p.SetState(26) + p.Match(PathPolicyConstraintsParserRBRACE) + } + + case 2: + localctx = NewIfContext(p, localctx) + p.EnterOuterAlt(localctx, 2) + { + p.SetState(28) + p.Match(PathPolicyConstraintsParserLBRACE) + } + { + p.SetState(29) + p.expression(0) + } + { + p.SetState(30) + p.Match(PathPolicyConstraintsParserQUESTIONMARK) + } + { + p.SetState(31) + p.expression(0) + } + { + p.SetState(32) + p.Match(PathPolicyConstraintsParserRBRACE) + } + + } + + return localctx +} + +// IExpressionContext is an interface to support dynamic dispatch. +type IExpressionContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsExpressionContext differentiates from other interfaces. + IsExpressionContext() +} + +type ExpressionContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyExpressionContext() *ExpressionContext { + var p = new(ExpressionContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_expression + return p +} + +func (*ExpressionContext) IsExpressionContext() {} + +func NewExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExpressionContext { + var p = new(ExpressionContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_expression + + return p +} + +func (s *ExpressionContext) GetParser() antlr.Parser { return s.parser } + +func (s *ExpressionContext) CopyFrom(ctx *ExpressionContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *ExpressionContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type ParensContext struct { + *ExpressionContext +} + +func NewParensContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ParensContext { + var p = new(ParensContext) + + p.ExpressionContext = NewEmptyExpressionContext() + p.parser = parser + p.CopyFrom(ctx.(*ExpressionContext)) + + return p +} + +func (s *ParensContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ParensContext) LPAR() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserLPAR, 0) +} + +func (s *ParensContext) Expression() IExpressionContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExpressionContext) +} + +func (s *ParensContext) RPAR() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserRPAR, 0) +} + +func (s *ParensContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterParens(s) + } +} + +func (s *ParensContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitParens(s) + } +} + +type ExpressionIdentifierContext struct { + *ExpressionContext +} + +func NewExpressionIdentifierContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ExpressionIdentifierContext { + var p = new(ExpressionIdentifierContext) + + p.ExpressionContext = NewEmptyExpressionContext() + p.parser = parser + p.CopyFrom(ctx.(*ExpressionContext)) + + return p +} + +func (s *ExpressionIdentifierContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExpressionIdentifierContext) Identifier() IIdentifierContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IIdentifierContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IIdentifierContext) +} + +func (s *ExpressionIdentifierContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterExpressionIdentifier(s) + } +} + +func (s *ExpressionIdentifierContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitExpressionIdentifier(s) + } +} + +type ExpressionConcatContext struct { + *ExpressionContext + left IExpressionContext + right IExpressionContext +} + +func NewExpressionConcatContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ExpressionConcatContext { + var p = new(ExpressionConcatContext) + + p.ExpressionContext = NewEmptyExpressionContext() + p.parser = parser + p.CopyFrom(ctx.(*ExpressionContext)) + + return p +} + +func (s *ExpressionConcatContext) GetLeft() IExpressionContext { return s.left } + +func (s *ExpressionConcatContext) GetRight() IExpressionContext { return s.right } + +func (s *ExpressionConcatContext) SetLeft(v IExpressionContext) { s.left = v } + +func (s *ExpressionConcatContext) SetRight(v IExpressionContext) { s.right = v } + +func (s *ExpressionConcatContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExpressionConcatContext) PLUS() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserPLUS, 0) +} + +func (s *ExpressionConcatContext) AllExpression() []IExpressionContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem()) + var tst = make([]IExpressionContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IExpressionContext) + } + } + + return tst +} + +func (s *ExpressionConcatContext) Expression(i int) IExpressionContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IExpressionContext) +} + +func (s *ExpressionConcatContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterExpressionConcat(s) + } +} + +func (s *ExpressionConcatContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitExpressionConcat(s) + } +} + +type ExpressionQueryContext struct { + *ExpressionContext +} + +func NewExpressionQueryContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ExpressionQueryContext { + var p = new(ExpressionQueryContext) + + p.ExpressionContext = NewEmptyExpressionContext() + p.parser = parser + p.CopyFrom(ctx.(*ExpressionContext)) + + return p +} + +func (s *ExpressionQueryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExpressionQueryContext) Query() IQueryContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IQueryContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IQueryContext) +} + +func (s *ExpressionQueryContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterExpressionQuery(s) + } +} + +func (s *ExpressionQueryContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitExpressionQuery(s) + } +} + +func (p *PathPolicyConstraintsParser) Expression() (localctx IExpressionContext) { + return p.expression(0) +} + +func (p *PathPolicyConstraintsParser) expression(_p int) (localctx IExpressionContext) { + this := p + _ = this + + var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext() + _parentState := p.GetState() + localctx = NewExpressionContext(p, p.GetParserRuleContext(), _parentState) + var _prevctx IExpressionContext = localctx + var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning. + _startState := 4 + p.EnterRecursionRule(localctx, 4, PathPolicyConstraintsParserRULE_expression, _p) + + defer func() { + p.UnrollRecursionContexts(_parentctx) + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + var _alt int + + p.EnterOuterAlt(localctx, 1) + p.SetState(43) + p.GetErrorHandler().Sync(p) + + switch p.GetTokenStream().LA(1) { + case PathPolicyConstraintsParserLPAR: + localctx = NewParensContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + + { + p.SetState(37) + p.Match(PathPolicyConstraintsParserLPAR) + } + { + p.SetState(38) + p.expression(0) + } + { + p.SetState(39) + p.Match(PathPolicyConstraintsParserRPAR) + } + + case PathPolicyConstraintsParserZERO, PathPolicyConstraintsParserNUM: + localctx = NewExpressionIdentifierContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(41) + p.Identifier() + } + + case PathPolicyConstraintsParserLBRACE: + localctx = NewExpressionQueryContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(42) + p.Query() + } + + default: + panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + } + p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1)) + p.SetState(50) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 2, p.GetParserRuleContext()) + + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + if p.GetParseListeners() != nil { + p.TriggerExitRuleEvent() + } + _prevctx = localctx + localctx = NewExpressionConcatContext(p, NewExpressionContext(p, _parentctx, _parentState)) + localctx.(*ExpressionConcatContext).left = _prevctx + + p.PushNewRecursionContext(localctx, _startState, PathPolicyConstraintsParserRULE_expression) + p.SetState(45) + + if !(p.Precpred(p.GetParserRuleContext(), 3)) { + panic(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 3)", "")) + } + { + p.SetState(46) + p.Match(PathPolicyConstraintsParserPLUS) + } + { + p.SetState(47) + + var _x = p.expression(4) + + localctx.(*ExpressionConcatContext).right = _x + } + + } + p.SetState(52) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 2, p.GetParserRuleContext()) + } + + return localctx +} + +// IIdentifierContext is an interface to support dynamic dispatch. +type IIdentifierContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsIdentifierContext differentiates from other interfaces. + IsIdentifierContext() +} + +type IdentifierContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIdentifierContext() *IdentifierContext { + var p = new(IdentifierContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_identifier + return p +} + +func (*IdentifierContext) IsIdentifierContext() {} + +func NewIdentifierContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IdentifierContext { + var p = new(IdentifierContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_identifier + + return p +} + +func (s *IdentifierContext) GetParser() antlr.Parser { return s.parser } + +func (s *IdentifierContext) Isd() IIsdContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IIsdContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IIsdContext) +} + +func (s *IdentifierContext) As() IAsContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IAsContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IAsContext) +} + +func (s *IdentifierContext) HASH() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserHASH, 0) +} + +func (s *IdentifierContext) AllIface() []IIfaceContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IIfaceContext)(nil)).Elem()) + var tst = make([]IIfaceContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IIfaceContext) + } + } + + return tst +} + +func (s *IdentifierContext) Iface(i int) IIfaceContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IIfaceContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IIfaceContext) +} + +func (s *IdentifierContext) MONKEYTAIL() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserMONKEYTAIL, 0) +} + +func (s *IdentifierContext) Onepolicy() IOnepolicyContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IOnepolicyContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IOnepolicyContext) +} + +func (s *IdentifierContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IdentifierContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *IdentifierContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterIdentifier(s) + } +} + +func (s *IdentifierContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitIdentifier(s) + } +} + +func (p *PathPolicyConstraintsParser) Identifier() (localctx IIdentifierContext) { + this := p + _ = this + + localctx = NewIdentifierContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, PathPolicyConstraintsParserRULE_identifier) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(53) + p.Isd() + } + { + p.SetState(54) + p.As() + } + { + p.SetState(55) + p.Match(PathPolicyConstraintsParserHASH) + } + { + p.SetState(56) + p.Iface() + } + { + p.SetState(57) + p.Match(PathPolicyConstraintsParserT__0) + } + { + p.SetState(58) + p.Iface() + } + { + p.SetState(59) + p.Match(PathPolicyConstraintsParserMONKEYTAIL) + } + { + p.SetState(60) + p.Onepolicy() + } + + return localctx +} + +// IIsdContext is an interface to support dynamic dispatch. +type IIsdContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsIsdContext differentiates from other interfaces. + IsIsdContext() +} + +type IsdContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIsdContext() *IsdContext { + var p = new(IsdContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_isd + return p +} + +func (*IsdContext) IsIsdContext() {} + +func NewIsdContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IsdContext { + var p = new(IsdContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_isd + + return p +} + +func (s *IsdContext) GetParser() antlr.Parser { return s.parser } + +func (s *IsdContext) CopyFrom(ctx *IsdContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *IsdContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IsdContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type WildcardISDContext struct { + *IsdContext +} + +func NewWildcardISDContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *WildcardISDContext { + var p = new(WildcardISDContext) + + p.IsdContext = NewEmptyIsdContext() + p.parser = parser + p.CopyFrom(ctx.(*IsdContext)) + + return p +} + +func (s *WildcardISDContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *WildcardISDContext) ZERO() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserZERO, 0) +} + +func (s *WildcardISDContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterWildcardISD(s) + } +} + +func (s *WildcardISDContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitWildcardISD(s) + } +} + +type ISDContext struct { + *IsdContext +} + +func NewISDContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ISDContext { + var p = new(ISDContext) + + p.IsdContext = NewEmptyIsdContext() + p.parser = parser + p.CopyFrom(ctx.(*IsdContext)) + + return p +} + +func (s *ISDContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ISDContext) NUM() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserNUM, 0) +} + +func (s *ISDContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterISD(s) + } +} + +func (s *ISDContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitISD(s) + } +} + +func (p *PathPolicyConstraintsParser) Isd() (localctx IIsdContext) { + this := p + _ = this + + localctx = NewIsdContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 8, PathPolicyConstraintsParserRULE_isd) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(64) + p.GetErrorHandler().Sync(p) + + switch p.GetTokenStream().LA(1) { + case PathPolicyConstraintsParserZERO: + localctx = NewWildcardISDContext(p, localctx) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(62) + p.Match(PathPolicyConstraintsParserZERO) + } + + case PathPolicyConstraintsParserNUM: + localctx = NewISDContext(p, localctx) + p.EnterOuterAlt(localctx, 2) + { + p.SetState(63) + p.Match(PathPolicyConstraintsParserNUM) + } + + default: + panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + } + + return localctx +} + +// IAsContext is an interface to support dynamic dispatch. +type IAsContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsAsContext differentiates from other interfaces. + IsAsContext() +} + +type AsContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyAsContext() *AsContext { + var p = new(AsContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_as + return p +} + +func (*AsContext) IsAsContext() {} + +func NewAsContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AsContext { + var p = new(AsContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_as + + return p +} + +func (s *AsContext) GetParser() antlr.Parser { return s.parser } + +func (s *AsContext) CopyFrom(ctx *AsContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *AsContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AsContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type ASContext struct { + *AsContext +} + +func NewASContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ASContext { + var p = new(ASContext) + + p.AsContext = NewEmptyAsContext() + p.parser = parser + p.CopyFrom(ctx.(*AsContext)) + + return p +} + +func (s *ASContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ASContext) AS() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserAS, 0) +} + +func (s *ASContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterAS(s) + } +} + +func (s *ASContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitAS(s) + } +} + +type LegacyASContext struct { + *AsContext +} + +func NewLegacyASContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *LegacyASContext { + var p = new(LegacyASContext) + + p.AsContext = NewEmptyAsContext() + p.parser = parser + p.CopyFrom(ctx.(*AsContext)) + + return p +} + +func (s *LegacyASContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LegacyASContext) LEGACYAS() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserLEGACYAS, 0) +} + +func (s *LegacyASContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterLegacyAS(s) + } +} + +func (s *LegacyASContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitLegacyAS(s) + } +} + +type WildcardASContext struct { + *AsContext +} + +func NewWildcardASContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *WildcardASContext { + var p = new(WildcardASContext) + + p.AsContext = NewEmptyAsContext() + p.parser = parser + p.CopyFrom(ctx.(*AsContext)) + + return p +} + +func (s *WildcardASContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *WildcardASContext) WILDCARDAS() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserWILDCARDAS, 0) +} + +func (s *WildcardASContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterWildcardAS(s) + } +} + +func (s *WildcardASContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitWildcardAS(s) + } +} + +func (p *PathPolicyConstraintsParser) As() (localctx IAsContext) { + this := p + _ = this + + localctx = NewAsContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 10, PathPolicyConstraintsParserRULE_as) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(69) + p.GetErrorHandler().Sync(p) + + switch p.GetTokenStream().LA(1) { + case PathPolicyConstraintsParserWILDCARDAS: + localctx = NewWildcardASContext(p, localctx) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(66) + p.Match(PathPolicyConstraintsParserWILDCARDAS) + } + + case PathPolicyConstraintsParserLEGACYAS: + localctx = NewLegacyASContext(p, localctx) + p.EnterOuterAlt(localctx, 2) + { + p.SetState(67) + p.Match(PathPolicyConstraintsParserLEGACYAS) + } + + case PathPolicyConstraintsParserAS: + localctx = NewASContext(p, localctx) + p.EnterOuterAlt(localctx, 3) + { + p.SetState(68) + p.Match(PathPolicyConstraintsParserAS) + } + + default: + panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + } + + return localctx +} + +// IIfaceContext is an interface to support dynamic dispatch. +type IIfaceContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsIfaceContext differentiates from other interfaces. + IsIfaceContext() +} + +type IfaceContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIfaceContext() *IfaceContext { + var p = new(IfaceContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_iface + return p +} + +func (*IfaceContext) IsIfaceContext() {} + +func NewIfaceContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IfaceContext { + var p = new(IfaceContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_iface + + return p +} + +func (s *IfaceContext) GetParser() antlr.Parser { return s.parser } + +func (s *IfaceContext) CopyFrom(ctx *IfaceContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *IfaceContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IfaceContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type IFaceContext struct { + *IfaceContext +} + +func NewIFaceContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IFaceContext { + var p = new(IFaceContext) + + p.IfaceContext = NewEmptyIfaceContext() + p.parser = parser + p.CopyFrom(ctx.(*IfaceContext)) + + return p +} + +func (s *IFaceContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IFaceContext) NUM() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserNUM, 0) +} + +func (s *IFaceContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterIFace(s) + } +} + +func (s *IFaceContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitIFace(s) + } +} + +type WildcardIFaceContext struct { + *IfaceContext +} + +func NewWildcardIFaceContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *WildcardIFaceContext { + var p = new(WildcardIFaceContext) + + p.IfaceContext = NewEmptyIfaceContext() + p.parser = parser + p.CopyFrom(ctx.(*IfaceContext)) + + return p +} + +func (s *WildcardIFaceContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *WildcardIFaceContext) ZERO() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserZERO, 0) +} + +func (s *WildcardIFaceContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterWildcardIFace(s) + } +} + +func (s *WildcardIFaceContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitWildcardIFace(s) + } +} + +func (p *PathPolicyConstraintsParser) Iface() (localctx IIfaceContext) { + this := p + _ = this + + localctx = NewIfaceContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 12, PathPolicyConstraintsParserRULE_iface) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(73) + p.GetErrorHandler().Sync(p) + + switch p.GetTokenStream().LA(1) { + case PathPolicyConstraintsParserZERO: + localctx = NewWildcardIFaceContext(p, localctx) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(71) + p.Match(PathPolicyConstraintsParserZERO) + } + + case PathPolicyConstraintsParserNUM: + localctx = NewIFaceContext(p, localctx) + p.EnterOuterAlt(localctx, 2) + { + p.SetState(72) + p.Match(PathPolicyConstraintsParserNUM) + } + + default: + panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + } + + return localctx +} + +// IOnepolicyContext is an interface to support dynamic dispatch. +type IOnepolicyContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsOnepolicyContext differentiates from other interfaces. + IsOnepolicyContext() +} + +type OnepolicyContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyOnepolicyContext() *OnepolicyContext { + var p = new(OnepolicyContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_onepolicy + return p +} + +func (*OnepolicyContext) IsOnepolicyContext() {} + +func NewOnepolicyContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OnepolicyContext { + var p = new(OnepolicyContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_onepolicy + + return p +} + +func (s *OnepolicyContext) GetParser() antlr.Parser { return s.parser } + +func (s *OnepolicyContext) CopyFrom(ctx *OnepolicyContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *OnepolicyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *OnepolicyContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type RejectContext struct { + *OnepolicyContext +} + +func NewRejectContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *RejectContext { + var p = new(RejectContext) + + p.OnepolicyContext = NewEmptyOnepolicyContext() + p.parser = parser + p.CopyFrom(ctx.(*OnepolicyContext)) + + return p +} + +func (s *RejectContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *RejectContext) REJECT() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserREJECT, 0) +} + +func (s *RejectContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterReject(s) + } +} + +func (s *RejectContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitReject(s) + } +} + +type LocalPolicyContext struct { + *OnepolicyContext +} + +func NewLocalPolicyContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *LocalPolicyContext { + var p = new(LocalPolicyContext) + + p.OnepolicyContext = NewEmptyOnepolicyContext() + p.parser = parser + p.CopyFrom(ctx.(*OnepolicyContext)) + + return p +} + +func (s *LocalPolicyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LocalPolicyContext) LOCALPOLICY() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserLOCALPOLICY, 0) +} + +func (s *LocalPolicyContext) Policyindex() IPolicyindexContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IPolicyindexContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IPolicyindexContext) +} + +func (s *LocalPolicyContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterLocalPolicy(s) + } +} + +func (s *LocalPolicyContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitLocalPolicy(s) + } +} + +type GlobalPolicyContext struct { + *OnepolicyContext +} + +func NewGlobalPolicyContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *GlobalPolicyContext { + var p = new(GlobalPolicyContext) + + p.OnepolicyContext = NewEmptyOnepolicyContext() + p.parser = parser + p.CopyFrom(ctx.(*OnepolicyContext)) + + return p +} + +func (s *GlobalPolicyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *GlobalPolicyContext) GLOBALPOLICY() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserGLOBALPOLICY, 0) +} + +func (s *GlobalPolicyContext) Policyindex() IPolicyindexContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IPolicyindexContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IPolicyindexContext) +} + +func (s *GlobalPolicyContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterGlobalPolicy(s) + } +} + +func (s *GlobalPolicyContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitGlobalPolicy(s) + } +} + +type WildcardPolicyContext struct { + *OnepolicyContext +} + +func NewWildcardPolicyContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *WildcardPolicyContext { + var p = new(WildcardPolicyContext) + + p.OnepolicyContext = NewEmptyOnepolicyContext() + p.parser = parser + p.CopyFrom(ctx.(*OnepolicyContext)) + + return p +} + +func (s *WildcardPolicyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *WildcardPolicyContext) ZERO() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserZERO, 0) +} + +func (s *WildcardPolicyContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterWildcardPolicy(s) + } +} + +func (s *WildcardPolicyContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitWildcardPolicy(s) + } +} + +func (p *PathPolicyConstraintsParser) Onepolicy() (localctx IOnepolicyContext) { + this := p + _ = this + + localctx = NewOnepolicyContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 14, PathPolicyConstraintsParserRULE_onepolicy) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(81) + p.GetErrorHandler().Sync(p) + + switch p.GetTokenStream().LA(1) { + case PathPolicyConstraintsParserGLOBALPOLICY: + localctx = NewGlobalPolicyContext(p, localctx) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(75) + p.Match(PathPolicyConstraintsParserGLOBALPOLICY) + } + { + p.SetState(76) + p.Policyindex() + } + + case PathPolicyConstraintsParserLOCALPOLICY: + localctx = NewLocalPolicyContext(p, localctx) + p.EnterOuterAlt(localctx, 2) + { + p.SetState(77) + p.Match(PathPolicyConstraintsParserLOCALPOLICY) + } + { + p.SetState(78) + p.Policyindex() + } + + case PathPolicyConstraintsParserZERO: + localctx = NewWildcardPolicyContext(p, localctx) + p.EnterOuterAlt(localctx, 3) + { + p.SetState(79) + p.Match(PathPolicyConstraintsParserZERO) + } + + case PathPolicyConstraintsParserREJECT: + localctx = NewRejectContext(p, localctx) + p.EnterOuterAlt(localctx, 4) + { + p.SetState(80) + p.Match(PathPolicyConstraintsParserREJECT) + } + + default: + panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + } + + return localctx +} + +// IPolicyindexContext is an interface to support dynamic dispatch. +type IPolicyindexContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsPolicyindexContext differentiates from other interfaces. + IsPolicyindexContext() +} + +type PolicyindexContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyPolicyindexContext() *PolicyindexContext { + var p = new(PolicyindexContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = PathPolicyConstraintsParserRULE_policyindex + return p +} + +func (*PolicyindexContext) IsPolicyindexContext() {} + +func NewPolicyindexContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PolicyindexContext { + var p = new(PolicyindexContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = PathPolicyConstraintsParserRULE_policyindex + + return p +} + +func (s *PolicyindexContext) GetParser() antlr.Parser { return s.parser } + +func (s *PolicyindexContext) CopyFrom(ctx *PolicyindexContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *PolicyindexContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *PolicyindexContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type PolicyIndexContext struct { + *PolicyindexContext +} + +func NewPolicyIndexContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *PolicyIndexContext { + var p = new(PolicyIndexContext) + + p.PolicyindexContext = NewEmptyPolicyindexContext() + p.parser = parser + p.CopyFrom(ctx.(*PolicyindexContext)) + + return p +} + +func (s *PolicyIndexContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *PolicyIndexContext) NUM() antlr.TerminalNode { + return s.GetToken(PathPolicyConstraintsParserNUM, 0) +} + +func (s *PolicyIndexContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.EnterPolicyIndex(s) + } +} + +func (s *PolicyIndexContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(PathPolicyConstraintsListener); ok { + listenerT.ExitPolicyIndex(s) + } +} + +func (p *PathPolicyConstraintsParser) Policyindex() (localctx IPolicyindexContext) { + this := p + _ = this + + localctx = NewPolicyindexContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 16, PathPolicyConstraintsParserRULE_policyindex) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + localctx = NewPolicyIndexContext(p, localctx) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(83) + p.Match(PathPolicyConstraintsParserNUM) + } + + return localctx +} + +func (p *PathPolicyConstraintsParser) Sempred(localctx antlr.RuleContext, ruleIndex, predIndex int) bool { + switch ruleIndex { + case 2: + var t *ExpressionContext = nil + if localctx != nil { + t = localctx.(*ExpressionContext) + } + return p.Expression_Sempred(t, predIndex) + + default: + panic("No predicate with index: " + fmt.Sprint(ruleIndex)) + } +} + +func (p *PathPolicyConstraintsParser) Expression_Sempred(localctx antlr.RuleContext, predIndex int) bool { + this := p + _ = this + + switch predIndex { + case 0: + return p.Precpred(p.GetParserRuleContext(), 3) + + default: + panic("No predicate with index: " + fmt.Sprint(predIndex)) + } +} diff --git a/control/BUILD.bazel b/control/BUILD.bazel index 57b4489eb5..eacfa8f689 100644 --- a/control/BUILD.bazel +++ b/control/BUILD.bazel @@ -19,6 +19,7 @@ go_library( "//control/beaconing/grpc:go_default_library", "//control/config:go_default_library", "//control/drkey:go_default_library", + "//control/fabrid:go_default_library", "//control/ifstate:go_default_library", "//control/segreq:go_default_library", "//control/trust:go_default_library", diff --git a/control/beaconing/BUILD.bazel b/control/beaconing/BUILD.bazel index 48714517f5..9aa48ca6ba 100644 --- a/control/beaconing/BUILD.bazel +++ b/control/beaconing/BUILD.bazel @@ -17,6 +17,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//control/beacon:go_default_library", + "//control/fabrid:go_default_library", "//control/ifstate:go_default_library", "//pkg/addr:go_default_library", "//pkg/log:go_default_library", @@ -30,6 +31,7 @@ go_library( "//pkg/segment:go_default_library", "//pkg/segment/extensions/digest:go_default_library", "//pkg/segment/extensions/epic:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "//pkg/segment/extensions/staticinfo:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/snet:go_default_library", diff --git a/control/beaconing/extender.go b/control/beaconing/extender.go index 4ea5e99704..60e1e22c86 100644 --- a/control/beaconing/extender.go +++ b/control/beaconing/extender.go @@ -20,6 +20,7 @@ import ( "hash" "time" + "github.com/scionproto/scion/control/fabrid" "github.com/scionproto/scion/control/ifstate" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/log" @@ -31,6 +32,7 @@ import ( seg "github.com/scionproto/scion/pkg/segment" "github.com/scionproto/scion/pkg/segment/extensions/digest" "github.com/scionproto/scion/pkg/segment/extensions/epic" + fabridext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/private/trust" ) @@ -86,6 +88,8 @@ type DefaultExtender struct { // is below the maximum expiration time. This happens when the signer expiration time is lower // than the maximum segment expiration time. SegmentExpirationDeficient metrics.Gauge + // Fabrid includes FABRID policy maps into the PCBs. + Fabrid *fabrid.FabridManager } // Extend extends the beacon with hop fields. @@ -196,6 +200,22 @@ func (s *DefaultExtender) Extend( Epic: d, } } + if s.Fabrid != nil { + f := &fabridext.Detached{ + SupportedIndicesMap: s.Fabrid.SupportedIndicesMap, + IndexIdentiferMap: s.Fabrid.IndexIdentifierMap, + } + asEntry.UnsignedExtensions.FabridDetached = f + + d := digest.Digest{Digest: f.Hash()} + if s.EPIC { + asEntry.Extensions.Digests.Fabrid = d + } else { + asEntry.Extensions.Digests = &digest.Extension{ + Fabrid: d, + } + } + } if err := pseg.AddASEntry(ctx, asEntry, signer); err != nil { return err diff --git a/control/cmd/control/BUILD.bazel b/control/cmd/control/BUILD.bazel index 9bf133cb52..e4677c84e1 100644 --- a/control/cmd/control/BUILD.bazel +++ b/control/cmd/control/BUILD.bazel @@ -17,6 +17,8 @@ go_library( "//control/config:go_default_library", "//control/drkey:go_default_library", "//control/drkey/grpc:go_default_library", + "//control/fabrid:go_default_library", + "//control/fabrid/grpc:go_default_library", "//control/ifstate:go_default_library", "//control/mgmtapi:go_default_library", "//control/onehop:go_default_library", @@ -34,6 +36,7 @@ go_library( "//pkg/private/prom:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/proto/control_plane:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", "//pkg/proto/discovery:go_default_library", "//pkg/scrypto:go_default_library", "//pkg/scrypto/cppki:go_default_library", diff --git a/control/cmd/control/main.go b/control/cmd/control/main.go index 7b36a7e28d..d44ff4ae1d 100644 --- a/control/cmd/control/main.go +++ b/control/cmd/control/main.go @@ -43,6 +43,8 @@ import ( "github.com/scionproto/scion/control/config" "github.com/scionproto/scion/control/drkey" drkeygrpc "github.com/scionproto/scion/control/drkey/grpc" + "github.com/scionproto/scion/control/fabrid" + fabridgrpc "github.com/scionproto/scion/control/fabrid/grpc" "github.com/scionproto/scion/control/ifstate" api "github.com/scionproto/scion/control/mgmtapi" "github.com/scionproto/scion/control/onehop" @@ -60,6 +62,7 @@ import ( "github.com/scionproto/scion/pkg/private/prom" "github.com/scionproto/scion/pkg/private/serrors" cppb "github.com/scionproto/scion/pkg/proto/control_plane" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" dpb "github.com/scionproto/scion/pkg/proto/discovery" "github.com/scionproto/scion/pkg/scrypto" "github.com/scionproto/scion/pkg/scrypto/cppki" @@ -149,6 +152,16 @@ func realMain(ctx context.Context) error { revCache := storage.NewRevocationStorage() defer revCache.Close() + var fabridMgr *fabrid.FabridManager + if globalCfg.Fabrid.Enabled { + fabridMgr = fabrid.NewFabridManager(topo.InterfaceIDs(), + globalCfg.Fabrid.RemoteCacheValidity.Duration) + err = fabridMgr.Load(globalCfg.Fabrid.Path) + if err != nil { + return serrors.WrapStr("initializing FABRID", err) + } + } + pathDB, err := storage.NewPathStorage(globalCfg.PathDB) if err != nil { return serrors.WrapStr("initializing path storage", err) @@ -334,6 +347,21 @@ func realMain(ctx context.Context) error { BeaconsHandled: libmetrics.NewPromCounter(metrics.BeaconingReceivedTotal), }, }) + // Handle fabrid map and policy requests + if globalCfg.Fabrid.Enabled { + polFetcher := fabridgrpc.BasicFabridControlPlaneFetcher{ + Dialer: &libgrpc.QUICDialer{ + Rewriter: nc.AddressRewriter(), + Dialer: quicStack.Dialer, + }, + Router: segreq.NewRouter(fetcherCfg), + MaxRetries: 20, + } + + f := &fabridgrpc.Server{FabridManager: fabridMgr, Fetcher: &polFetcher} + experimental.RegisterFABRIDIntraServiceServer(tcpServer, f) + experimental.RegisterFABRIDInterServiceServer(quicServer, f) + } // Handle segment lookup authLookupServer := &segreqgrpc.LookupServer{ @@ -801,6 +829,7 @@ func realMain(ctx context.Context) error { HiddenPathRegistrationCfg: hpWriterCfg, AllowIsdLoop: isdLoopAllowed, EPIC: globalCfg.BS.EPIC, + Fabrid: fabridMgr, }) if err != nil { return serrors.WrapStr("starting periodic tasks", err) diff --git a/control/config/BUILD.bazel b/control/config/BUILD.bazel index 06d97aa550..39955efd54 100644 --- a/control/config/BUILD.bazel +++ b/control/config/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "bs_sample.go", "config.go", "drkey.go", + "fabrid.go", "sample.go", ], importpath = "github.com/scionproto/scion/control/config", @@ -15,6 +16,7 @@ go_library( "//pkg/log:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "//private/config:go_default_library", "//private/env:go_default_library", "//private/mgmtapi:go_default_library", @@ -29,11 +31,13 @@ go_test( srcs = [ "config_test.go", "drkey_test.go", + "fabrid_test.go", ], embed = [":go_default_library"], deps = [ "//pkg/drkey:go_default_library", "//pkg/log/logtest:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "//private/env/envtest:go_default_library", "//private/mgmtapi/jwtauth:go_default_library", "//private/mgmtapi/mgmtapitest:go_default_library", @@ -42,5 +46,6 @@ go_test( "@com_github_pelletier_go_toml_v2//:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", "@com_github_stretchr_testify//require:go_default_library", + "@in_gopkg_yaml_v2//:go_default_library", ], ) diff --git a/control/config/config.go b/control/config/config.go index d0c98e8d9c..c9c4431bf3 100644 --- a/control/config/config.go +++ b/control/config/config.go @@ -44,6 +44,9 @@ const ( DefaultQueryInterval = 5 * time.Minute // DefaultMaxASValidity is the default validity period for renewed AS certificates. DefaultMaxASValidity = 3 * 24 * time.Hour + // DefaultFabridRemoteCacheValidity is the default validity period for a policy description + // fetched from a remote AS. + DefaultFabridRemoteCacheValidity = 3 * time.Hour ) var _ config.Config = (*Config)(nil) @@ -64,6 +67,7 @@ type Config struct { CA CA `toml:"ca,omitempty"` TrustEngine trustengine.Config `toml:"trustengine,omitempty"` DRKey DRKeyConfig `toml:"drkey,omitempty"` + Fabrid FabridConfig `toml:"fabrid,omitempty"` } // InitDefaults initializes the default values for all parts of the config. @@ -83,6 +87,7 @@ func (cfg *Config) InitDefaults() { &cfg.CA, &cfg.TrustEngine, &cfg.DRKey, + &cfg.Fabrid, ) } @@ -140,6 +145,7 @@ func (cfg *Config) Sample(dst io.Writer, path config.Path, _ config.CtxMap) { &cfg.CA, &cfg.TrustEngine, &cfg.DRKey, + &cfg.Fabrid, ) } @@ -342,3 +348,27 @@ func (cfg *CAService) Sample(dst io.Writer, _ config.Path, _ config.CtxMap) { func (cfg *CAService) ConfigName() string { return "service" } + +// FabridConfig contains the configuration for Fabrid on this AS and points to the policies. +type FabridConfig struct { + // Enabled specifies whether the AS should support Fabrid. + Enabled bool `toml:"enabled,omitempty"` + // Path to the folder containing the Fabrid yaml policies + Path string `toml:"path,omitempty"` + // RemoteCacheValidity specifies how long a remote policy should be cached locally, i.e. + // the duration that the identifier->description mapping is stored. + RemoteCacheValidity util.DurWrap `toml:"remote_cache_validity,omitempty"` +} + +func (cfg *FabridConfig) InitDefaults() { + if cfg.RemoteCacheValidity.Duration == 0 { + cfg.RemoteCacheValidity.Duration = DefaultFabridRemoteCacheValidity + } +} +func (cfg *FabridConfig) Sample(dst io.Writer, _ config.Path, _ config.CtxMap) { + config.WriteString(dst, fabridConfigSample) +} + +func (cfg *FabridConfig) ConfigName() string { + return "fabrid" +} diff --git a/control/config/fabrid.go b/control/config/fabrid.go new file mode 100644 index 0000000000..d9e5bf15bf --- /dev/null +++ b/control/config/fabrid.go @@ -0,0 +1,152 @@ +// Copyright 2024 ETH Zurich +// +// 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 config + +import ( + "io" + "net" + "slices" + "strings" + + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" + "github.com/scionproto/scion/private/config" +) + +type FABRIDPolicy struct { + IsLocalPolicy bool `yaml:"local,omitempty"` + LocalIdentifier uint32 `yaml:"local_identifier,omitempty"` + LocalDescription string `yaml:"local_description,omitempty"` + GlobalIdentifier uint32 `yaml:"global_identifier,omitempty"` + SupportedBy []FABRIDConnectionPoints `yaml:"connections,omitempty"` +} + +// Validate validates that all values are parsable. +func (cfg *FABRIDPolicy) Validate(asInterfaceIDs []uint16) error { + for _, connectionPoints := range cfg.SupportedBy { + if err := connectionPoints.Validate(asInterfaceIDs); err != nil { + return serrors.WrapStr("Failed to validate connection points", err) + } + } + if cfg.IsLocalPolicy { + if cfg.LocalIdentifier == 0 { + return serrors.New("Local policy identifier missing.") + } else if cfg.LocalDescription == "" { + return serrors.New("Local policy description missing.") + } else if cfg.GlobalIdentifier != 0 { + return serrors.New("Unexpected global identifier", + "global_identifier", cfg.GlobalIdentifier) + } + } else { + if cfg.GlobalIdentifier == 0 { + return serrors.New("Global policy identifier missing.") + } else if cfg.LocalDescription != "" { + return serrors.New("Unexpected local description", + "local_description", cfg.LocalDescription) + } else if cfg.LocalIdentifier != 0 { + return serrors.New("Unexpected local identifier", + "local_identifier", cfg.LocalIdentifier) + } + } + + return nil +} + +// Sample writes a config sample to the writer. +func (cfg *FABRIDPolicy) Sample(dst io.Writer, path config.Path, ctx config.CtxMap) { + config.WriteString(dst, fabridLocalPolicySample) +} + +type FABRIDConnectionPoints struct { + Ingress FABRIDConnectionPoint `yaml:"ingress,omitempty"` + Egress FABRIDConnectionPoint `yaml:"egress,omitempty"` + MPLSLabel uint32 `yaml:"mpls_label,omitempty"` +} + +// Validate validates that all values are parsable. +func (cfg *FABRIDConnectionPoints) Validate(asInterfaceIDs []uint16) error { + if cfg.Ingress.Type != fabrid.Interface && cfg.Ingress.Type != fabrid.Wildcard { + return serrors.New("FABRID policies are only supported from an interface to an IP" + + " range or other interface.") + } else if cfg.Ingress.Type == fabrid.Interface && cfg.Egress.Type == fabrid.Interface && cfg. + Ingress.Interface == cfg.Egress.Interface { + return serrors.New("Interfaces should be distinct.") + } + if err := cfg.Ingress.Validate(asInterfaceIDs); err != nil { + return serrors.WrapStr("Failed to validate ingress connection point", err) + } + if err := cfg.Egress.Validate(asInterfaceIDs); err != nil { + return serrors.WrapStr("Failed to validate egress connection point", err) + } + + return nil +} + +// FABRIDConnectionPoint describes a specific interface, or an IP range. A FABRID policy can be +// supported on a pair of connection points. +type FABRIDConnectionPoint struct { + Type fabrid.ConnectionPointType `yaml:"type,omitempty"` + IPAddress string `yaml:"ip,omitempty"` + Prefix uint8 `yaml:"prefix,omitempty"` + Interface uint16 `yaml:"interface,omitempty"` +} + +// Validate validates that all values are parsable. +func (cfg *FABRIDConnectionPoint) Validate(asInterfaceIDs []uint16) error { + switch strings.ToLower(string(cfg.Type)) { + case string(fabrid.Wildcard): + cfg.Type = fabrid.Wildcard + case string(fabrid.IPv4Range): + cfg.Type = fabrid.IPv4Range + if net.ParseIP(cfg.IPAddress).To4() == nil { + return serrors.New("Invalid IPv4 address for connection point", + "ip", cfg.IPAddress) + } else if cfg.Prefix > 32 { + return serrors.New("IPv4 prefix too large", + "ip", cfg.IPAddress, "prefix", cfg.Prefix) + } + case string(fabrid.IPv6Range): + cfg.Type = fabrid.IPv6Range + ip := net.ParseIP(cfg.IPAddress) + if ip == nil || len(ip) != net.IPv6len { + return serrors.New("Invalid IPv6 address for connection point", + "ip", cfg.IPAddress) + } else if cfg.Prefix > 128 { + return serrors.New("IPv6 prefix too large", + "ip", cfg.IPAddress, "prefix", cfg.Prefix) + } + case string(fabrid.Interface): + cfg.Type = fabrid.Interface + if cfg.Interface == 0 { + return serrors.New("Interface ID missing", "type", cfg.Type) + } + if !slices.Contains(asInterfaceIDs, cfg.Interface) { + return serrors.New("Interface does not exist", "interface", cfg.Interface) + } + default: + return serrors.New("Unknown FABRID connection point", "type", cfg.Type) + } + + if cfg.Type != fabrid.Interface && cfg.Interface != 0 { + return serrors.New("Unexpected interface ID", "type", cfg.Type) + } else if cfg.Type != fabrid.IPv4Range && cfg.Type != fabrid.IPv6Range { + if cfg.IPAddress != "" { + return serrors.New("Unexpected IP address", "type", cfg.Type, "ip", cfg.IPAddress) + } else if cfg.Prefix != 0 { + return serrors.New("Unexpected prefix", "type", cfg.Type, "prefix", cfg.Prefix) + } + } + return nil +} diff --git a/control/config/fabrid_test.go b/control/config/fabrid_test.go new file mode 100644 index 0000000000..31ea9f4141 --- /dev/null +++ b/control/config/fabrid_test.go @@ -0,0 +1,174 @@ +// Copyright 2024 ETH Zurich +// +// 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 config_test + +import ( + "bytes" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" + + "github.com/scionproto/scion/control/config" + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +func TestFabridSample(t *testing.T) { + var sample bytes.Buffer + pol := &config.FABRIDPolicy{} + pol.Sample(&sample, nil, nil) + err := yaml.UnmarshalStrict(sample.Bytes(), pol) + fmt.Println(sample.String()) + assert.NoError(t, err) + err = pol.Validate([]uint16{1}) + assert.NoError(t, err) +} + +func TestIPRangePolicyWithInvalidIP(t *testing.T) { + cp := config.FABRIDConnectionPoint{ + Type: fabrid.IPv4Range, + IPAddress: "192.168.5", + Prefix: 24, + } + err := cp.Validate([]uint16{}) + assert.ErrorContains(t, err, "Invalid IPv4 address for connection point") +} + +func TestFabridPolicyValidation(t *testing.T) { + tests := map[string]struct { + Policy string + assert assert.ErrorAssertionFunc + }{ + "valid": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv4 + ip: 192.168.2.1 + prefix: 24 +local: true +local_identifier: 55 +local_description: Fabrid Example Policy`, + assert: assert.NoError, + }, + "invalid interface": { + Policy: `connections: + - ingress: + type: interface + interface: 0 + egress: + type: ipv4 + ip: 192.168.2.1 + prefix: 24 +local: true +local_identifier: 55 +local_description: Fabrid Example Policy`, + assert: assert.Error, + }, + "invalid prefix ipv4": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv4 + ip: 192.168.2.1 + prefix: 33 +local: true +local_identifier: 55 +local_description: Fabrid Example Policy`, + assert: assert.Error, + }, + "valid prefix ipv6": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv6 + ip: 2001::1a2b + prefix: 33 +local: true +local_identifier: 55 +local_description: Fabrid Example Policy`, + assert: assert.NoError, + }, + "invalid prefix ipv6": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv6 + ip: 2001::1a2b + prefix: 129 +local: true +local_identifier: 55 +local_description: Fabrid Example Policy`, + assert: assert.Error, + }, + "missing local identifier": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv6 + ip: 2001::1a2b + prefix: 33 +local: true +local_description: Fabrid Example Policy`, + assert: assert.Error, + }, + "missing local description": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv6 + ip: 2001::1a2b + prefix: 33 +local: true +local_identifier: 55`, + assert: assert.Error, + }, + "missing global identifier": { + Policy: `connections: + - ingress: + type: interface + interface: 1 + egress: + type: ipv6 + ip: 2001::1a2b + prefix: 33 +local: false`, + assert: assert.Error, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + pol := &config.FABRIDPolicy{} + err := yaml.UnmarshalStrict([]byte(tc.Policy), pol) + require.NoError(t, err) + err = pol.Validate([]uint16{1}) + tc.assert(t, err) + }) + } +} diff --git a/control/config/sample.go b/control/config/sample.go index 54f9181f64..81f7210bb8 100644 --- a/control/config/sample.go +++ b/control/config/sample.go @@ -72,3 +72,39 @@ const drkeySecretValueHostListSample = ` # The list of hosts authorized to get a SV per protocol. scmp = [ "127.0.0.1", "127.0.0.2"] ` + +const fabridLocalPolicySample = ` +# Bool indicating whether the policy is a global or local policy. +local: true +# The identifier that the policy has locally in the AS +local_identifier: 55 +# A description which other ASes can fetch, describing the policy +local_description: Fabrid Example Policy +# A list of connections to which this policy applies +connections: + # Every connection has an ingress and an egress point: + - ingress: + # The type of the connection point, can be "ipv4", "ipv6" or "interface" + type: interface + # If the type is set to "interface", specify the specific interface + interface: 1 + egress: + # The type of the connection point, can be "ipv4", "ipv6" or "interface" + type: ipv6 + # If the type is set to "ipv4" or "ipv6", specify the IP range using a IP and prefix + ip: 2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b + # The prefix of the IP mask, has to be smaller than 32 for IPv4, smaller than 128 for + # IPv6 + prefix: 100 + # If the type is set to "interface", specify the specific interface, e.g. + # interface: 1 + # Every connnection can have a different mpls label they use to enable the policy: + mpls_label: 1 +` + +const fabridConfigSample = ` +# Whether Fabrid is enabled on this AS +enabled = true +# Folder in which the fabrid policies are stored +path = "gen/ASff00_0_110/fabrid/" +` diff --git a/control/fabrid/BUILD.bazel b/control/fabrid/BUILD.bazel new file mode 100644 index 0000000000..021937a772 --- /dev/null +++ b/control/fabrid/BUILD.bazel @@ -0,0 +1,34 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "fabrid_manager.go", + "mpls_map.go", + ], + importpath = "github.com/scionproto/scion/control/fabrid", + visibility = ["//visibility:public"], + deps = [ + "//control/config:go_default_library", + "//pkg/addr:go_default_library", + "//pkg/log:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", + "@in_gopkg_yaml_v2//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "fabrid_manager_test.go", + "mpls_map_test.go", + ], + data = glob(["testdata/**"]), + embed = [":go_default_library"], + deps = [ + "//control/config:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], +) diff --git a/control/fabrid/fabrid_manager.go b/control/fabrid/fabrid_manager.go new file mode 100644 index 0000000000..3b88172f85 --- /dev/null +++ b/control/fabrid/fabrid_manager.go @@ -0,0 +1,172 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import ( + "os" + "path/filepath" + "time" + + "gopkg.in/yaml.v2" + + "github.com/scionproto/scion/control/config" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/serrors" + fabrid_ext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +const MaxFabridPolicies = 255 + +type RemotePolicyIdentifier struct { + ISDAS uint64 + Identifier uint32 +} + +type RemotePolicyDescription struct { + Description string + Expires time.Time +} +type RemoteMap struct { + Digest []byte + fabrid_ext.Detached +} + +type FabridManager struct { + autoIncrIndex int + asInterfaceIDs []uint16 + SupportedIndicesMap fabrid_ext.SupportedIndicesMap + IndexIdentifierMap fabrid_ext.IndexIdentifierMap + IdentifierDescriptionMap map[uint32]string + MPLSMap *MplsMaps + RemotePolicyCache map[RemotePolicyIdentifier]RemotePolicyDescription + RemoteMapsCache map[addr.IA]RemoteMap + RemoteCacheValidity time.Duration +} + +func NewFabridManager(asInterfaceIDs []uint16, remoteCacheValidity time.Duration) *FabridManager { + fb := &FabridManager{ + SupportedIndicesMap: map[fabrid_ext.ConnectionPair][]uint8{}, + IndexIdentifierMap: map[uint8]*fabrid_ext.PolicyIdentifier{}, + IdentifierDescriptionMap: map[uint32]string{}, + MPLSMap: NewMplsMaps(), + RemotePolicyCache: map[RemotePolicyIdentifier]RemotePolicyDescription{}, + RemoteMapsCache: map[addr.IA]RemoteMap{}, + RemoteCacheValidity: remoteCacheValidity, + autoIncrIndex: 1, + asInterfaceIDs: asInterfaceIDs, + } + return fb +} + +func (f *FabridManager) Reload(policiesPath string) error { + f.IndexIdentifierMap = make(map[uint8]*fabrid_ext.PolicyIdentifier) + f.SupportedIndicesMap = make(map[fabrid_ext.ConnectionPair][]uint8) + f.MPLSMap = NewMplsMaps() + f.autoIncrIndex = 1 + return f.Load(policiesPath) +} + +func (f *FabridManager) Load(policiesPath string) error { + if err := filepath.Walk(policiesPath, f.parseAndAdd); err != nil { + return serrors.WrapStr("Unable to read the fabrid policies in folder", err, + "path", policiesPath) + } + f.MPLSMap.UpdateHash() + return nil +} + +func (f *FabridManager) parseAndAdd(path string, fi os.FileInfo, err error) error { + if err != nil { + return nil + } + if fi.IsDir() { // Makes sure that the current file is not a directory + return nil + } + + if f.autoIncrIndex > MaxFabridPolicies { + return serrors.New("Amount of FABRID policies exceeds limit.") + } + b, err := os.ReadFile(path) + if err != nil { + return serrors.WrapStr("Unable to read the fabrid policy in file", err, "path", path) + } + pol := &config.FABRIDPolicy{} + if err := yaml.UnmarshalStrict(b, pol); err != nil { + return serrors.WrapStr("Unable to parse policy", err) + } + + if err := pol.Validate(f.asInterfaceIDs); err != nil { + return serrors.WrapStr("Unable to validate policy", err, "path", path) + } + + return f.addPolicy(pol) +} + +func (f *FabridManager) addPolicy(pol *config.FABRIDPolicy) error { + policyIdx := uint8(f.autoIncrIndex) + f.autoIncrIndex++ + + if pol.IsLocalPolicy { + f.IndexIdentifierMap[policyIdx] = &fabrid_ext.PolicyIdentifier{ + IsLocal: true, + Identifier: pol.LocalIdentifier, + } + f.IdentifierDescriptionMap[pol.LocalIdentifier] = pol.LocalDescription + } else { + f.IndexIdentifierMap[policyIdx] = &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: pol.GlobalIdentifier, + } + } + + for _, connection := range pol.SupportedBy { + ig, err := createConnectionPoint(connection.Ingress) + if err != nil { + return err + } + eg, err := createConnectionPoint(connection.Egress) + if err != nil { + return err + } + ie := fabrid_ext.ConnectionPair{ + Ingress: ig, + Egress: eg, + } + f.MPLSMap.AddConnectionPoint(ie, connection.MPLSLabel, policyIdx) + f.SupportedIndicesMap[ie] = append(f.SupportedIndicesMap[ie], policyIdx) + } + + log.Debug("Loaded FABRID policy", "pol", pol) + return nil +} + +func createConnectionPoint(connection config.FABRIDConnectionPoint) (fabrid_ext.ConnectionPoint, + error) { + if connection.Type == fabrid_ext.Interface { + return fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.Interface, + InterfaceId: connection.Interface, + }, nil + } else if connection.Type == fabrid_ext.IPv4Range || connection.Type == fabrid_ext.IPv6Range { + return fabrid_ext.IPConnectionPointFromString(connection.IPAddress, + uint32(connection.Prefix), connection.Type), nil + } else if connection.Type == fabrid_ext.Wildcard { + return fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.Wildcard, + }, nil + } + return fabrid_ext.ConnectionPoint{}, serrors.New("Unsupported connection type") +} diff --git a/control/fabrid/fabrid_manager_test.go b/control/fabrid/fabrid_manager_test.go new file mode 100644 index 0000000000..ccc78f108a --- /dev/null +++ b/control/fabrid/fabrid_manager_test.go @@ -0,0 +1,368 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/control/config" + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +func TestLoadInvalidPolicies(t *testing.T) { + fm := NewFabridManager([]uint16{1, 2}, 5*time.Second) + err := fm.Load("testdata/mixed") + require.ErrorContains(t, err, "Unable to parse policy") +} + +func TestLoadPolicyWithNonExistingInterfaces(t *testing.T) { + fm := NewFabridManager([]uint16{1}, 5*time.Second) + err := fm.Load("testdata/correct") + require.ErrorContains(t, err, "Interface does not exist") +} + +func TestLoadPolicies(t *testing.T) { + testcases := map[string]struct { + CP []config.FABRIDConnectionPoints + Local bool + Description string + Identifier uint32 + }{ + "1-global_example.yml": { + Local: false, + Identifier: 1102, + CP: []config.FABRIDConnectionPoints{{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 2, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 1, + }, + MPLSLabel: 1, + }}, + }, + "2-global_example.yml": { + Local: false, + Identifier: 1102, + CP: []config.FABRIDConnectionPoints{{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 2, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 1, + }, + MPLSLabel: 2, + }}, + }, + "3-local_example.yml": { + Local: true, + Description: "Fabrid Example Policy", + Identifier: 1103, + CP: []config.FABRIDConnectionPoints{{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 2, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 1, + }, + MPLSLabel: 5, + }, { + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 2, + }, + MPLSLabel: 3, + }}, + }, + "55-local_example.yml": { + Local: true, + Description: "Fabrid Example Policy 2", + Identifier: 11055, + CP: []config.FABRIDConnectionPoints{{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 2, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.IPv4Range, + IPAddress: "192.168.5.1", + Prefix: 24, + }, + MPLSLabel: 55, + }}, + }, + } + + fm := NewFabridManager([]uint16{1, 2}, 5*time.Second) + fm.autoIncrIndex = 1 + err := fm.Load("testdata/correct") + require.NoError(t, err) + require.Equal(t, 4, len(fm.IndexIdentifierMap)) + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + + if tc.Local { + require.Equal(t, tc.Description, fm.IdentifierDescriptionMap[tc.Identifier]) + } + policyIdx := uint8(0) + for k, v := range fm.IndexIdentifierMap { + if ((tc.Local && v.IsLocal) || (!tc.Local && !v. + IsLocal)) && v.Identifier == tc.Identifier { + policyIdx = k + } + } + require.NotEqual(t, 0, policyIdx) + for _, cp := range tc.CP { + ig, err := createConnectionPoint(cp.Ingress) + require.NoError(t, err) + eg, err := createConnectionPoint(cp.Egress) + require.NoError(t, err) + ie := fabrid.ConnectionPair{ + Ingress: ig, + Egress: eg, + } + fmt.Println(ie) + fmt.Println(fm.SupportedIndicesMap) + require.Contains(t, fm.SupportedIndicesMap[ie], policyIdx) + } + }) + } + +} + +func TestAddPolicy(t *testing.T) { + cp1 := config.FABRIDConnectionPoints{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.IPv4Range, + IPAddress: "192.168.1.1", + Prefix: 24, + }, + MPLSLabel: 12, + } + cp2 := config.FABRIDConnectionPoints{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 3, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 5, + }, + MPLSLabel: 13, + } + cp3 := config.FABRIDConnectionPoints{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 7, + }, + MPLSLabel: 14, + } + + cp4 := config.FABRIDConnectionPoints{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.Wildcard, + }, + MPLSLabel: 15, + } + cp5 := config.FABRIDConnectionPoints{ + Ingress: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 5, + }, + Egress: config.FABRIDConnectionPoint{ + Type: fabrid.IPv4Range, + IPAddress: "192.168.1.1", + Prefix: 24, + }, + MPLSLabel: 16, + } + testCases := map[string]struct { + Policy config.FABRIDPolicy + Local bool + }{ + "Global Policy": { + Policy: config.FABRIDPolicy{ + IsLocalPolicy: false, + GlobalIdentifier: 1, + SupportedBy: []config.FABRIDConnectionPoints{ + cp1, cp2, cp3, cp4, cp5, + }, + }, + Local: false, + }, + "Local Policy": { + Policy: config.FABRIDPolicy{ + IsLocalPolicy: true, + LocalIdentifier: 4, + LocalDescription: "test policy", + SupportedBy: []config.FABRIDConnectionPoints{ + cp1, cp2, cp3, cp4, cp5, + }, + }, + Local: true, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + fm := NewFabridManager([]uint16{3, 5, 7}, 5*time.Second) + oldIndex := fm.autoIncrIndex + err := fm.addPolicy(&tc.Policy) + newIndex := fm.autoIncrIndex + require.NoError(t, err) + //Check if the policy index has updated: + require.NotEqual(t, oldIndex, newIndex) + if tc.Local { + //Check if the policy is in the IdentifierDescriptionMap + require.Equal(t, fm.IdentifierDescriptionMap[tc.Policy.LocalIdentifier], + tc.Policy.LocalDescription) + } + // Check if the policy has been correctly inserted into the IndexIdentifierMap + indexIdentifierMapEntry := fm.IndexIdentifierMap[uint8(oldIndex)] + if tc.Local { + require.Equal(t, tc.Policy.LocalIdentifier, indexIdentifierMapEntry.Identifier) + require.True(t, indexIdentifierMapEntry.IsLocal) + } else { + + require.Equal(t, tc.Policy.GlobalIdentifier, indexIdentifierMapEntry.Identifier) + require.False(t, indexIdentifierMapEntry.IsLocal) + } + //Check if the policy has been inserted correctly into the MPLS Maps: + cp1_mpls_key := 1<<31 + uint32(oldIndex) // IP + cp2_mpls_key := uint64(cp2.Ingress.Interface)<<24 + uint64(cp2.Egress. + Interface)<<8 + uint64(oldIndex) + cp3_mpls_key := uint64(1)<<63 + uint64(cp3.Egress.Interface)<<8 + uint64(oldIndex) + cp4_mpls_key := uint64(1)<<63 + uint64(oldIndex) + cp5_mpls_key := uint32(cp5.Ingress.Interface)<<8 + uint32(oldIndex) // IP + + require.Equal(t, fm.MPLSMap.IPPoliciesMap[cp1_mpls_key][0].MPLSLabel, cp1.MPLSLabel) + require.Equal(t, fm.MPLSMap.InterfacePoliciesMap[cp2_mpls_key], cp2.MPLSLabel) + require.Equal(t, fm.MPLSMap.InterfacePoliciesMap[cp3_mpls_key], cp3.MPLSLabel) + require.Equal(t, fm.MPLSMap.InterfacePoliciesMap[cp4_mpls_key], cp4.MPLSLabel) + require.Equal(t, fm.MPLSMap.IPPoliciesMap[cp5_mpls_key][0].MPLSLabel, cp5.MPLSLabel) + + //Check if the policy has been added to the SupportedIndicesMap + for _, cp := range tc.Policy.SupportedBy { + ig, err := createConnectionPoint(cp.Ingress) + require.NoError(t, err) + eg, err := createConnectionPoint(cp.Egress) + require.NoError(t, err) + require.Contains(t, fm.SupportedIndicesMap, fabrid.ConnectionPair{ + Ingress: ig, Egress: eg, + }) + require.Equal(t, uint8(oldIndex), fm.SupportedIndicesMap[fabrid.ConnectionPair{ + Ingress: ig, Egress: eg, + }][0]) + } + }) + } + +} +func TestCreateConnectionPoint(t *testing.T) { + testCases := map[string]struct { + connection config.FABRIDConnectionPoint + expectedType fabrid.ConnectionPointType + expectedIP string + expectedPrefix uint32 + expectedInterface uint16 + isError bool + }{ + "ValidInterfaceType": { + connection: config.FABRIDConnectionPoint{ + Type: fabrid.Interface, + Interface: 15, + }, + expectedType: fabrid.Interface, + expectedInterface: 15, + isError: false, + }, + "ValidIPv4Range": { + connection: config.FABRIDConnectionPoint{ + Type: fabrid.IPv4Range, + IPAddress: "192.168.1.1", + Prefix: 24, + }, + expectedType: fabrid.IPv4Range, + expectedIP: "192.168.1.0", + expectedPrefix: 24, + isError: false, + }, + "ValidIPv6Range": { + connection: config.FABRIDConnectionPoint{ + Type: fabrid.IPv6Range, + IPAddress: "2001:db8::2", + Prefix: 56, + }, + expectedType: fabrid.IPv6Range, + expectedIP: "2001:db8::", + expectedPrefix: 56, + isError: false, + }, + "ValidWildcard": { + connection: config.FABRIDConnectionPoint{ + Type: fabrid.Wildcard, + }, + expectedType: fabrid.Wildcard, + isError: false, + }, + "Invalid": { + connection: config.FABRIDConnectionPoint{ + Type: "Invalid", + }, + isError: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + result, err := createConnectionPoint(tc.connection) + if tc.isError { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.expectedType, result.Type) + if result.Type == fabrid.IPv4Range || result.Type == fabrid.IPv6Range { + require.Equal(t, tc.expectedIP, result.IP) + require.Equal(t, tc.expectedPrefix, result.Prefix) + } else if result.Type == fabrid.Interface { + require.Equal(t, tc.expectedInterface, result.InterfaceId) + } + + }) + } +} diff --git a/control/fabrid/grpc/BUILD.bazel b/control/fabrid/grpc/BUILD.bazel new file mode 100644 index 0000000000..cf41f516aa --- /dev/null +++ b/control/fabrid/grpc/BUILD.bazel @@ -0,0 +1,43 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "fabrid_service.go", + "fetcher.go", + ], + importpath = "github.com/scionproto/scion/control/fabrid/grpc", + visibility = ["//visibility:public"], + deps = [ + "//control/fabrid:go_default_library", + "//pkg/addr:go_default_library", + "//pkg/grpc:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", + "//pkg/snet:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "fabrid_service_test.go", + "fetcher_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//control/fabrid:go_default_library", + "//control/fabrid/grpc/mock_grpc:go_default_library", + "//pkg/addr:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/private/xtest:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", + "//pkg/snet:go_default_library", + "//pkg/snet/mock_snet:go_default_library", + "@com_github_golang_mock//gomock:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], +) diff --git a/control/fabrid/grpc/fabrid_service.go b/control/fabrid/grpc/fabrid_service.go new file mode 100644 index 0000000000..ad0c672e77 --- /dev/null +++ b/control/fabrid/grpc/fabrid_service.go @@ -0,0 +1,157 @@ +// Copyright 2024 ETH Zurich +// +// 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 grpc + +import ( + "bytes" + "context" + "time" + + "github.com/scionproto/scion/control/fabrid" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + fabridext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +type Server struct { + FabridManager *fabrid.FabridManager + Fetcher FabridControlPlaneFetcher +} + +func (s Server) mplsIPMapToPB() map[uint32]*experimental.MPLSIPArray { + mplsIpMap := make(map[uint32]*experimental.MPLSIPArray) + for i, entry := range s.FabridManager.MPLSMap.IPPoliciesMap { + if _, exists := mplsIpMap[i]; !exists { + mplsIpMap[i] = &experimental.MPLSIPArray{Entry: make([]*experimental.MPLSIP, 0, + len(entry))} + } + for _, iprange := range entry { + mplsIpMap[i].Entry = append(mplsIpMap[i].Entry, &experimental.MPLSIP{ + MplsLabel: iprange.MPLSLabel, + Ip: iprange.IP, + Prefix: iprange.Prefix, + }) + } + } + return mplsIpMap +} + +func (s Server) MPLSMap(ctx context.Context, request *experimental.MPLSMapRequest) (*experimental. + MPLSMapResponse, error) { + if bytes.Equal(request.Hash, s.FabridManager.MPLSMap.CurrentHash) { + return &experimental.MPLSMapResponse{Update: false}, nil + } + // Create the map of mpls labelks for + return &experimental.MPLSMapResponse{ + Update: true, + Hash: s.FabridManager.MPLSMap.CurrentHash, + MplsInterfacePoliciesMap: s.FabridManager.MPLSMap.InterfacePoliciesMap, + MplsIpMap: s.mplsIPMapToPB(), + }, nil +} + +func (s Server) RemotePolicyDescription(ctx context.Context, + request *experimental.RemotePolicyDescriptionRequest) ( + *experimental.RemotePolicyDescriptionResponse, error) { + //TODO(jvanbommel): In a future PR we will add a third description map, which maps identifiers + // to descriptions, protecting this data by adding it into the digest in the signed AS entry. + identifier := fabrid.RemotePolicyIdentifier{ + ISDAS: request.IsdAs, + Identifier: request.PolicyIdentifier, + } + if val, ok := s.FabridManager.RemotePolicyCache[identifier]; ok && val.Expires.After( + time.Now()) { + return &experimental.RemotePolicyDescriptionResponse{Description: val.Description}, nil + } + + policy, err := s.Fetcher.GetRemotePolicy(ctx, addr.IA(request.IsdAs), request.PolicyIdentifier) + if err != nil { + return &experimental.RemotePolicyDescriptionResponse{}, err + } + + s.FabridManager.RemotePolicyCache[identifier] = fabrid.RemotePolicyDescription{ + Description: policy.Description, + Expires: time.Now().Add(s.FabridManager.RemoteCacheValidity), + } + + return &experimental.RemotePolicyDescriptionResponse{Description: policy.Description}, nil +} + +func (s Server) RemoteMaps(ctx context.Context, request *experimental.RemoteMapsRequest) ( + *experimental.RemoteMapsResponse, error) { + + if val, ok := s.FabridManager.RemoteMapsCache[addr.IA(request.IsdAs)]; ok && bytes.Equal(val. + Digest, request.Digest) { + return &experimental.RemoteMapsResponse{ + Maps: &experimental.FABRIDDetachableMaps{ + SupportedIndicesMap: fabridext.SupportedIndicesMapToPB(val.SupportedIndicesMap), + IndexIdentifierMap: fabridext.IndexIdentifierMapToPB(val.IndexIdentiferMap), + }, + }, nil + } + + maps, err := s.Fetcher.GetRemoteMaps(ctx, addr.IA(request.IsdAs)) + if err != nil { + return &experimental.RemoteMapsResponse{}, err + } + detached := fabridext.Detached{ + SupportedIndicesMap: fabridext.SupportedIndicesMapFromPB(maps.Maps.SupportedIndicesMap), + IndexIdentiferMap: fabridext.IndexIdentifierMapFromPB(maps.Maps.IndexIdentifierMap), + } + s.FabridManager.RemoteMapsCache[addr.IA(request.IsdAs)] = fabrid.RemoteMap{ + Detached: detached, + Digest: detached.Hash(), + } + + return &experimental.RemoteMapsResponse{Maps: maps.Maps}, nil +} + +func (s Server) SupportedIndicesMap(_ context.Context, + _ *experimental.SupportedIndicesMapRequest) (*experimental.SupportedIndicesMapResponse, error) { + return &experimental.SupportedIndicesMapResponse{ + SupportedIndicesMap: fabridext.SupportedIndicesMapToPB(s.FabridManager.SupportedIndicesMap), + }, nil +} + +func (s Server) IndexIdentifierMap(_ context.Context, _ *experimental.IndexIdentifierMapRequest) ( + *experimental.IndexIdentifierMapResponse, error) { + + return &experimental.IndexIdentifierMapResponse{ + IndexIdentifierMap: fabridext.IndexIdentifierMapToPB(s.FabridManager.IndexIdentifierMap), + }, nil +} + +func (s Server) DetachedMaps(_ context.Context, _ *experimental.DetachedMapsRequest) ( + *experimental.DetachedMapsResponse, error) { + return &experimental.DetachedMapsResponse{ + Maps: &experimental.FABRIDDetachableMaps{ + SupportedIndicesMap: fabridext.SupportedIndicesMapToPB(s.FabridManager. + SupportedIndicesMap), + IndexIdentifierMap: fabridext.IndexIdentifierMapToPB(s.FabridManager. + IndexIdentifierMap), + }, + }, nil +} + +func (s Server) LocalPolicyDescription(_ context.Context, + request *experimental.LocalPolicyDescriptionRequest) ( + *experimental.LocalPolicyDescriptionResponse, error) { + + if descr, ok := s.FabridManager.IdentifierDescriptionMap[request.PolicyIdentifier]; ok { + return &experimental.LocalPolicyDescriptionResponse{ + Description: descr}, nil + } + return &experimental.LocalPolicyDescriptionResponse{}, errNotFound +} diff --git a/control/fabrid/grpc/fabrid_service_test.go b/control/fabrid/grpc/fabrid_service_test.go new file mode 100644 index 0000000000..189154af3e --- /dev/null +++ b/control/fabrid/grpc/fabrid_service_test.go @@ -0,0 +1,415 @@ +// Copyright 2024 ETH Zurich +// +// 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 grpc + +import ( + "context" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/control/fabrid" + "github.com/scionproto/scion/control/fabrid/grpc/mock_grpc" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + fabrid_ext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +func TestRemotePolicyDescription(t *testing.T) { + ia := xtest.MustParseIA("1-ff00:00:100") + // Separating these out, as otherwise the line length is too much for the linter. + rpi1 := fabrid.RemotePolicyIdentifier{ISDAS: uint64(ia), Identifier: 56} + rpi2 := fabrid.RemotePolicyIdentifier{ISDAS: uint64(ia), Identifier: 57} + notPresentCache := map[fabrid.RemotePolicyIdentifier]fabrid.RemotePolicyDescription{} + presentExpiredCache := map[fabrid.RemotePolicyIdentifier]fabrid.RemotePolicyDescription{ + rpi1: { + Description: "Test Policy Cached", + Expires: time.Now().Add(-5 * time.Hour), + }, + } + presentNotExpiredCache := map[fabrid.RemotePolicyIdentifier]fabrid.RemotePolicyDescription{ + rpi2: { + Description: "Test Policy Cached", + Expires: time.Now().Add(10 * time.Hour), + }} + tests := map[string]struct { + LocalCache map[fabrid.RemotePolicyIdentifier]fabrid.RemotePolicyDescription + PolicyIdentifier uint32 + PolicyAtRemote bool + ExpectedFetcherCalls int + ExpectedAssert assert.ErrorAssertionFunc + ExpectedResult string + }{ + "not present in cache": { + LocalCache: notPresentCache, + PolicyIdentifier: 55, + PolicyAtRemote: true, + ExpectedFetcherCalls: 1, + ExpectedAssert: assert.NoError, + ExpectedResult: "Test Policy", + }, + "present in cache but expired": { + LocalCache: presentExpiredCache, + PolicyIdentifier: 56, + PolicyAtRemote: true, + ExpectedFetcherCalls: 1, + ExpectedAssert: assert.NoError, + ExpectedResult: "Test Policy 2", + }, + "present in cache and not expired": { + LocalCache: presentNotExpiredCache, + PolicyIdentifier: 57, + PolicyAtRemote: true, + ExpectedFetcherCalls: 0, + ExpectedAssert: assert.NoError, + ExpectedResult: "Test Policy Cached", + }, + "not present at remote": { + LocalCache: notPresentCache, + PolicyIdentifier: 58, + PolicyAtRemote: false, + ExpectedFetcherCalls: 1, + ExpectedAssert: assert.Error, + ExpectedResult: "", + }, + } + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fetcher := mock_grpc.NewMockFabridControlPlaneFetcher(ctrl) + fetcher.EXPECT().GetRemotePolicy(gomock.Any(), ia, + tc.PolicyIdentifier).Times(tc.ExpectedFetcherCalls).DoAndReturn( + func(ctx context.Context, + remoteIA addr.IA, + remotePolicyIdentifier uint32) (*experimental.RemotePolicyDescriptionResponse, + error) { + if tc.PolicyAtRemote { + return &experimental.RemotePolicyDescriptionResponse{ + Description: tc.ExpectedResult}, nil + } + return &experimental.RemotePolicyDescriptionResponse{}, serrors.New( + "remote policy fetch fetch failed", + "try", 1, + "peer", remoteIA, + "err", errNotFound, + ) + }) + server := Server{ + FabridManager: &fabrid.FabridManager{ + RemotePolicyCache: tc.LocalCache, + }, + Fetcher: fetcher, + } + descr, err := server.RemotePolicyDescription(context.Background(), + &experimental.RemotePolicyDescriptionRequest{ + PolicyIdentifier: tc.PolicyIdentifier, + IsdAs: uint64(ia), + }) + + tc.ExpectedAssert(t, err) + if tc.ExpectedResult != "" { + require.Equal(t, tc.ExpectedResult, descr.Description) + } + }) + } +} +func TestRemoteMaps_ExistingCacheEntry(t *testing.T) { + ia := xtest.MustParseIA("1-ff00:00:100") + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + fetcher := mock_grpc.NewMockFabridControlPlaneFetcher(ctrl) + manager := &fabrid.FabridManager{ + RemoteMapsCache: make(map[addr.IA]fabrid.RemoteMap), + } + server := Server{Fetcher: fetcher, FabridManager: manager} + expectedMaps := fabrid_ext.Detached{ + SupportedIndicesMap: fabrid_ext.SupportedIndicesMap{ + fabrid_ext.ConnectionPair{ + Ingress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 15}}, + IndexIdentiferMap: fabrid_ext.IndexIdentifierMap{ + 2: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &fabrid_ext.PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + }, + } + manager.RemoteMapsCache[ia] = fabrid.RemoteMap{ + Digest: []byte{0x01, 0x02, 0x03, 0x04}, + Detached: expectedMaps, + } + + fetcher.EXPECT().GetRemoteMaps(gomock.Any(), ia).Times(0) + + request := &experimental.RemoteMapsRequest{IsdAs: uint64(ia), Digest: []byte{0x01, 0x02, + 0x03, 0x04}} + response, err := server.RemoteMaps(ctx, request) + + assert.Nil(t, err) + assert.Equal(t, expectedMaps.SupportedIndicesMap, fabrid_ext.SupportedIndicesMapFromPB( + response.Maps.SupportedIndicesMap)) + assert.Equal(t, expectedMaps.IndexIdentiferMap, fabrid_ext.IndexIdentifierMapFromPB( + response.Maps.IndexIdentifierMap)) +} + +func TestRemoteMaps_NonExistingCacheEntry(t *testing.T) { + ia := xtest.MustParseIA("1-ff00:00:100") + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + fetcher := mock_grpc.NewMockFabridControlPlaneFetcher(ctrl) + manager := &fabrid.FabridManager{ + RemoteMapsCache: make(map[addr.IA]fabrid.RemoteMap), + } + server := Server{Fetcher: fetcher, FabridManager: manager} + expectedMaps := fabrid_ext.Detached{ + SupportedIndicesMap: fabrid_ext.SupportedIndicesMap{ + fabrid_ext.ConnectionPair{ + Ingress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 15}}, + IndexIdentiferMap: fabrid_ext.IndexIdentifierMap{ + 2: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &fabrid_ext.PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + }, + } + + fetcher.EXPECT().GetRemoteMaps(gomock.Any(), ia).Times(1).DoAndReturn(func( + ctx context.Context, + remoteIA addr.IA, + ) (*experimental.DetachedMapsResponse, error) { + return &experimental.DetachedMapsResponse{ + + Maps: &experimental.FABRIDDetachableMaps{ + SupportedIndicesMap: fabrid_ext.SupportedIndicesMapToPB(expectedMaps. + SupportedIndicesMap), + IndexIdentifierMap: fabrid_ext.IndexIdentifierMapToPB(expectedMaps. + IndexIdentiferMap), + }, + }, nil + }) + + request := &experimental.RemoteMapsRequest{IsdAs: uint64(ia), Digest: expectedMaps.Hash()} + response, err := server.RemoteMaps(ctx, request) + + assert.Nil(t, err) + assert.Equal(t, expectedMaps.SupportedIndicesMap, fabrid_ext.SupportedIndicesMapFromPB( + response.Maps.SupportedIndicesMap)) + assert.Equal(t, expectedMaps.IndexIdentiferMap, fabrid_ext.IndexIdentifierMapFromPB( + response.Maps.IndexIdentifierMap)) + + //Check if the request is cached. + assert.Equal(t, manager.RemoteMapsCache[ia].Digest, expectedMaps.Hash()) + assert.Equal(t, manager.RemoteMapsCache[ia].SupportedIndicesMap, + expectedMaps.SupportedIndicesMap) + assert.Equal(t, manager.RemoteMapsCache[ia].IndexIdentiferMap, expectedMaps.IndexIdentiferMap) + // The request should now be cached, fetch again to test. + response, err = server.RemoteMaps(ctx, request) + + assert.Nil(t, err) + assert.Equal(t, expectedMaps.SupportedIndicesMap, fabrid_ext.SupportedIndicesMapFromPB( + response.Maps.SupportedIndicesMap)) + assert.Equal(t, expectedMaps.IndexIdentiferMap, fabrid_ext.IndexIdentifierMapFromPB( + response.Maps.IndexIdentifierMap)) +} + +func TestSupportedIndicesMap(t *testing.T) { + supportedIndices := fabrid_ext.SupportedIndicesMap{ + fabrid_ext.ConnectionPair{ + Ingress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 15}} + + server := Server{ + FabridManager: &fabrid.FabridManager{ + SupportedIndicesMap: supportedIndices, + }, + } + indices, err := server.SupportedIndicesMap( + context.Background(), + &experimental.SupportedIndicesMapRequest{}, + ) + require.NoError(t, err) + require.Equal(t, supportedIndices, + fabrid_ext.SupportedIndicesMapFromPB(indices.SupportedIndicesMap)) +} + +func TestIndexIdentifierMap(t *testing.T) { + indexIdentifierMap := fabrid_ext.IndexIdentifierMap{ + 2: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &fabrid_ext.PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + } + + server := Server{ + FabridManager: &fabrid.FabridManager{ + IndexIdentifierMap: indexIdentifierMap, + }, + } + identifiers, err := server.IndexIdentifierMap( + context.Background(), + &experimental.IndexIdentifierMapRequest{}, + ) + require.NoError(t, err) + require.Equal(t, indexIdentifierMap, + fabrid_ext.IndexIdentifierMapFromPB(identifiers.IndexIdentifierMap)) +} + +func TestLocalPolicyDescription(t *testing.T) { + tests := map[string]struct { + Identifier uint32 + IdentifierDescriptionMap map[uint32]string + ExpectedDescription string + Assert assert.ErrorAssertionFunc + }{ + "nonexistent local policy": { + Identifier: 56, + IdentifierDescriptionMap: map[uint32]string{ + 56: "Test Policy", + }, + ExpectedDescription: "Test Policy", + Assert: assert.NoError, + }, + "existent local policy": { + Identifier: 56, + IdentifierDescriptionMap: map[uint32]string{}, + ExpectedDescription: "", + Assert: assert.Error, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server := Server{ + FabridManager: &fabrid.FabridManager{ + IdentifierDescriptionMap: tc.IdentifierDescriptionMap, + }, + } + description, err := server.LocalPolicyDescription(context.Background(), + &experimental.LocalPolicyDescriptionRequest{ + PolicyIdentifier: tc.Identifier, + }) + tc.Assert(t, err) + if err == nil { + require.Equal(t, tc.ExpectedDescription, description.Description) + } + }) + } +} + +func TestMPLSMap(t *testing.T) { + baseMPLSMap := fabrid.MplsMaps{ + InterfacePoliciesMap: map[uint64]uint32{1: 3001, 2: 2030, 3: 200, 255: 1999}, + CurrentHash: nil, + } + baseMPLSMap.UpdateHash() + + tests := map[string]struct { + RequesterHash []byte + ExpectedUpdate bool + }{ + "no hash": { + RequesterHash: nil, + ExpectedUpdate: true, + }, + "outdated hash": { + RequesterHash: []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x30, 0x31, 0x32}, + ExpectedUpdate: true, + }, + "up to date hash": { + RequesterHash: baseMPLSMap.CurrentHash, + ExpectedUpdate: false, + }, + } + + server := Server{ + FabridManager: &fabrid.FabridManager{ + MPLSMap: &baseMPLSMap, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + resp, err := server.MPLSMap(context.Background(), + &experimental.MPLSMapRequest{ + Hash: tc.RequesterHash, + }) + require.NoError(t, err) + require.Equal(t, tc.ExpectedUpdate, resp.Update) + if resp.Update { + require.Equal(t, server.FabridManager.MPLSMap.InterfacePoliciesMap, + resp.MplsInterfacePoliciesMap) + } + }) + } +} diff --git a/control/fabrid/grpc/fetcher.go b/control/fabrid/grpc/fetcher.go new file mode 100644 index 0000000000..85314894f9 --- /dev/null +++ b/control/fabrid/grpc/fetcher.go @@ -0,0 +1,178 @@ +// Copyright 2024 ETH Zurich +// +// 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 grpc + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/scionproto/scion/pkg/addr" + libgrpc "github.com/scionproto/scion/pkg/grpc" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + "github.com/scionproto/scion/pkg/snet" +) + +const ( + defaultRPCDialTimeout time.Duration = 2 * time.Second +) + +var errNotReachable = serrors.New("remote not reachable") +var errNotFound = serrors.New("FABRID local policy is not found") + +type FabridControlPlaneFetcher interface { + GetRemotePolicy(ctx context.Context, remoteIA addr.IA, + remotePolicyIdentifier uint32) (*experimental.RemotePolicyDescriptionResponse, error) + GetRemoteMaps(ctx context.Context, remoteIA addr.IA) (*experimental.DetachedMapsResponse, error) +} + +type BasicFabridControlPlaneFetcher struct { + Dialer libgrpc.Dialer + Router snet.Router + MaxRetries int + + errorPaths map[snet.PathFingerprint]struct{} +} + +func (f *BasicFabridControlPlaneFetcher) GetRemoteMaps( + ctx context.Context, + remoteIA addr.IA, +) (*experimental.DetachedMapsResponse, error) { + var errList serrors.List + f.errorPaths = make(map[snet.PathFingerprint]struct{}) + for i := 0; i < f.MaxRetries; i++ { + rep, err := f.attemptFetchRemote(ctx, remoteIA, func(ctx context.Context, + client experimental.FABRIDInterServiceClient) (interface{}, error) { + return client.DetachedMaps(ctx, &experimental.DetachedMapsRequest{}) + }) + if errors.Is(err, errNotReachable) || (err != nil && strings.Contains(err.Error(), + errNotFound.Error())) { + return &experimental.DetachedMapsResponse{}, serrors.New( + "remote maps fetch fetch failed", "try", i+1, "peer", remoteIA, + "err", err) + } else if err != nil { + errList = append(errList, + serrors.WrapStr("fetching policy", err, "try", i+1, "peer", remoteIA), + ) + continue + } + maps, ok := rep.(*experimental.DetachedMapsResponse) + if !ok { + return &experimental.DetachedMapsResponse{}, serrors.New( + "remote policy fetch fetch failed, invalid response", "try", i+1, + "peer", remoteIA) + } + return maps, nil + } + return &experimental.DetachedMapsResponse{}, serrors.WrapStr( + "reached max retry attempts fetching remote policy", + errList, + ) +} + +func (f *BasicFabridControlPlaneFetcher) GetRemotePolicy( + ctx context.Context, + remoteIA addr.IA, + remotePolicyIdentifier uint32, +) (*experimental.RemotePolicyDescriptionResponse, error) { + var errList serrors.List + f.errorPaths = make(map[snet.PathFingerprint]struct{}) + for i := 0; i < f.MaxRetries; i++ { + rep, err := f.attemptFetchRemote(ctx, remoteIA, func(ctx context.Context, + client experimental.FABRIDInterServiceClient) (interface{}, error) { + return client.LocalPolicyDescription(ctx, + &experimental.LocalPolicyDescriptionRequest{ + PolicyIdentifier: remotePolicyIdentifier}) + }) + if errors.Is(err, errNotReachable) || (err != nil && strings.Contains(err.Error(), + errNotFound.Error())) { + return &experimental.RemotePolicyDescriptionResponse{}, serrors.New( + "remote policy fetch fetch failed", "try", i+1, "peer", remoteIA, + "err", err) + } else if err != nil { + errList = append(errList, + serrors.WrapStr("fetching policy", err, "try", i+1, "peer", remoteIA), + ) + continue + } + policyDescr, ok := rep.(*experimental.LocalPolicyDescriptionResponse) + if !ok { + return &experimental.RemotePolicyDescriptionResponse{}, serrors.New( + "remote policy fetch fetch failed, invalid response", "try", i+1, + "peer", remoteIA) + } + return &experimental.RemotePolicyDescriptionResponse{Description: policyDescr. + Description}, nil + } + return &experimental.RemotePolicyDescriptionResponse{}, serrors.WrapStr( + "reached max retry attempts fetching remote policy", + errList, + ) +} + +func (f *BasicFabridControlPlaneFetcher) attemptFetchRemote( + ctx context.Context, + srcIA addr.IA, + fetchingFunc func(ctx context.Context, client experimental.FABRIDInterServiceClient) ( + interface{}, error), +) (interface{}, error) { + + path, err := f.pathToDst(ctx, srcIA) + if err != nil { + return nil, err + } + remote := &snet.SVCAddr{ + IA: srcIA, + Path: path.Dataplane(), + NextHop: path.UnderlayNextHop(), + SVC: addr.SvcCS, + } + dialCtx, cancelF := context.WithTimeout(ctx, defaultRPCDialTimeout) + defer cancelF() + conn, err := f.Dialer.Dial(dialCtx, remote) + if err != nil { + return nil, serrors.WrapStr("dialing", err) + } + defer conn.Close() + client := experimental.NewFABRIDInterServiceClient(conn) + rep, err := fetchingFunc(ctx, client) + if err != nil { + return nil, serrors.WrapStr("requesting ", err) + } + return rep, nil +} + +func (f *BasicFabridControlPlaneFetcher) pathToDst(ctx context.Context, dst addr.IA) (snet.Path, + error) { + paths, err := f.Router.AllRoutes(ctx, dst) + if err != nil { + return nil, serrors.Wrap(errNotReachable, err) + } + if len(paths) == 0 { + return nil, errNotReachable + } + for _, p := range paths { + if _, ok := f.errorPaths[snet.Fingerprint(p)]; ok { + continue + } + f.errorPaths[snet.Fingerprint(p)] = struct{}{} + return p, nil + } + // we've tried out all the paths; we reset the map to retry them. + f.errorPaths = make(map[snet.PathFingerprint]struct{}) + return paths[0], nil +} diff --git a/control/fabrid/grpc/fetcher_test.go b/control/fabrid/grpc/fetcher_test.go new file mode 100644 index 0000000000..863768559f --- /dev/null +++ b/control/fabrid/grpc/fetcher_test.go @@ -0,0 +1,171 @@ +// Copyright 2024 ETH Zurich +// +// 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 grpc_test + +import ( + "context" + "net" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/control/fabrid" + "github.com/scionproto/scion/control/fabrid/grpc" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + fabrid_ext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/pkg/snet/mock_snet" +) + +func TestFetchRemotePolicy(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + path := mock_snet.NewMockPath(ctrl) + path.EXPECT().Metadata().AnyTimes().Return(&snet.PathMetadata{ + Interfaces: []snet.PathInterface{}, + }) + path.EXPECT().Dataplane().AnyTimes().Return(nil) + path.EXPECT().UnderlayNextHop().AnyTimes().Return(&net.UDPAddr{}) + + router := mock_snet.NewMockRouter(ctrl) + router.EXPECT().AllRoutes(gomock.Any(), gomock.Any()).AnyTimes().Return([]snet.Path{path}, nil) + tests := map[string]struct { + IdentifierDescriptions map[uint32]string + RequestedPolicy uint32 + Assert assert.ErrorAssertionFunc + PostCheck func(t *testing.T, + response *experimental.RemotePolicyDescriptionResponse) + }{ + "existing": { + IdentifierDescriptions: map[uint32]string{ + 33: "Test Policy", + 45: "Second Test Policy", + }, + RequestedPolicy: 33, + Assert: assert.NoError, + PostCheck: func(t *testing.T, response *experimental.RemotePolicyDescriptionResponse) { + require.Equal(t, response.Description, "Test Policy") + }, + }, + "nonexistent": { + IdentifierDescriptions: map[uint32]string{ + 33: "Test Policy", + 45: "Second Test Policy", + }, + RequestedPolicy: 55, + Assert: assert.Error, + PostCheck: func(t *testing.T, + response *experimental.RemotePolicyDescriptionResponse) { + }, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server := xtest.NewGRPCService() + experimental.RegisterFABRIDInterServiceServer(server.Server(), grpc.Server{ + FabridManager: &fabrid.FabridManager{ + IdentifierDescriptionMap: tc.IdentifierDescriptions, + }, + Fetcher: &grpc.BasicFabridControlPlaneFetcher{}, + }) + server.Start(t) + + fetcher := grpc.BasicFabridControlPlaneFetcher{ + Dialer: server, + Router: router, + MaxRetries: 1, + } + + policy, err := fetcher.GetRemotePolicy(context.Background(), + xtest.MustParseIA("1-ff00:0:111"), tc.RequestedPolicy) + tc.Assert(t, err) + tc.PostCheck(t, policy) + }) + } +} + +func TestFetchRemoteMap(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + path := mock_snet.NewMockPath(ctrl) + path.EXPECT().Metadata().AnyTimes().Return(&snet.PathMetadata{ + Interfaces: []snet.PathInterface{}, + }) + path.EXPECT().Dataplane().AnyTimes().Return(nil) + path.EXPECT().UnderlayNextHop().AnyTimes().Return(&net.UDPAddr{}) + + router := mock_snet.NewMockRouter(ctrl) + router.EXPECT().AllRoutes(gomock.Any(), gomock.Any()).AnyTimes().Return([]snet.Path{path}, nil) + maps := fabrid_ext.Detached{ + SupportedIndicesMap: fabrid_ext.SupportedIndicesMap{ + fabrid_ext.ConnectionPair{ + Ingress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: fabrid_ext.ConnectionPoint{ + Type: fabrid_ext.Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 15}}, + IndexIdentiferMap: fabrid_ext.IndexIdentifierMap{ + 2: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &fabrid_ext.PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &fabrid_ext.PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + }, + } + t.Run("fabrid manager returns maps", func(t *testing.T) { + server := xtest.NewGRPCService() + experimental.RegisterFABRIDInterServiceServer(server.Server(), grpc.Server{ + FabridManager: &fabrid.FabridManager{ + SupportedIndicesMap: maps.SupportedIndicesMap, + IndexIdentifierMap: maps.IndexIdentiferMap, + }, + Fetcher: &grpc.BasicFabridControlPlaneFetcher{}, + }) + server.Start(t) + + fetcher := grpc.BasicFabridControlPlaneFetcher{ + Dialer: server, + Router: router, + MaxRetries: 1, + } + + fetchedMaps, err := fetcher.GetRemoteMaps(context.Background(), + xtest.MustParseIA("1-ff00:0:111")) + assert.NoError(t, err) + require.Equal(t, fetchedMaps.Maps.SupportedIndicesMap, + fabrid_ext.SupportedIndicesMapToPB(maps.SupportedIndicesMap)) + require.Equal(t, fetchedMaps.Maps.IndexIdentifierMap, + fabrid_ext.IndexIdentifierMapToPB(maps.IndexIdentiferMap)) + + }) + +} diff --git a/control/fabrid/grpc/mock_grpc/BUILD.bazel b/control/fabrid/grpc/mock_grpc/BUILD.bazel new file mode 100644 index 0000000000..4621d48c02 --- /dev/null +++ b/control/fabrid/grpc/mock_grpc/BUILD.bazel @@ -0,0 +1,22 @@ +load("//tools/lint:go.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "gomock") + +gomock( + name = "go_default_mock", + out = "mock.go", + interfaces = ["FabridControlPlaneFetcher"], + library = "//control/fabrid/grpc:go_default_library", + package = "mock_grpc", +) + +go_library( + name = "go_default_library", + srcs = ["mock.go"], + importpath = "github.com/scionproto/scion/control/fabrid/grpc/mock_grpc", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", + "@com_github_golang_mock//gomock:go_default_library", + ], +) diff --git a/control/fabrid/grpc/mock_grpc/mock.go b/control/fabrid/grpc/mock_grpc/mock.go new file mode 100644 index 0000000000..aab563927c --- /dev/null +++ b/control/fabrid/grpc/mock_grpc/mock.go @@ -0,0 +1,67 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/scionproto/scion/control/fabrid/grpc (interfaces: FabridControlPlaneFetcher) + +// Package mock_grpc is a generated GoMock package. +package mock_grpc + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + addr "github.com/scionproto/scion/pkg/addr" + experimental "github.com/scionproto/scion/pkg/proto/control_plane/experimental" +) + +// MockFabridControlPlaneFetcher is a mock of FabridControlPlaneFetcher interface. +type MockFabridControlPlaneFetcher struct { + ctrl *gomock.Controller + recorder *MockFabridControlPlaneFetcherMockRecorder +} + +// MockFabridControlPlaneFetcherMockRecorder is the mock recorder for MockFabridControlPlaneFetcher. +type MockFabridControlPlaneFetcherMockRecorder struct { + mock *MockFabridControlPlaneFetcher +} + +// NewMockFabridControlPlaneFetcher creates a new mock instance. +func NewMockFabridControlPlaneFetcher(ctrl *gomock.Controller) *MockFabridControlPlaneFetcher { + mock := &MockFabridControlPlaneFetcher{ctrl: ctrl} + mock.recorder = &MockFabridControlPlaneFetcherMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFabridControlPlaneFetcher) EXPECT() *MockFabridControlPlaneFetcherMockRecorder { + return m.recorder +} + +// GetRemoteMaps mocks base method. +func (m *MockFabridControlPlaneFetcher) GetRemoteMaps(arg0 context.Context, arg1 addr.IA) (*experimental.DetachedMapsResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRemoteMaps", arg0, arg1) + ret0, _ := ret[0].(*experimental.DetachedMapsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRemoteMaps indicates an expected call of GetRemoteMaps. +func (mr *MockFabridControlPlaneFetcherMockRecorder) GetRemoteMaps(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteMaps", reflect.TypeOf((*MockFabridControlPlaneFetcher)(nil).GetRemoteMaps), arg0, arg1) +} + +// GetRemotePolicy mocks base method. +func (m *MockFabridControlPlaneFetcher) GetRemotePolicy(arg0 context.Context, arg1 addr.IA, arg2 uint32) (*experimental.RemotePolicyDescriptionResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRemotePolicy", arg0, arg1, arg2) + ret0, _ := ret[0].(*experimental.RemotePolicyDescriptionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRemotePolicy indicates an expected call of GetRemotePolicy. +func (mr *MockFabridControlPlaneFetcherMockRecorder) GetRemotePolicy(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemotePolicy", reflect.TypeOf((*MockFabridControlPlaneFetcher)(nil).GetRemotePolicy), arg0, arg1, arg2) +} diff --git a/control/fabrid/mpls_map.go b/control/fabrid/mpls_map.go new file mode 100644 index 0000000000..c64b407f50 --- /dev/null +++ b/control/fabrid/mpls_map.go @@ -0,0 +1,104 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import ( + "encoding/binary" + "hash/fnv" + "sort" + + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +type PolicyIPRange struct { + MPLSLabel uint32 + IP []byte + Prefix uint32 +} + +type MplsMaps struct { + IPPoliciesMap map[uint32][]PolicyIPRange + InterfacePoliciesMap map[uint64]uint32 + CurrentHash []byte +} + +func NewMplsMaps() *MplsMaps { + return &MplsMaps{ + IPPoliciesMap: make(map[uint32][]PolicyIPRange), + InterfacePoliciesMap: make(map[uint64]uint32), + CurrentHash: []byte{}, + } +} + +func (m *MplsMaps) AddConnectionPoint(ie fabrid.ConnectionPair, mplsLabel uint32, policyIdx uint8) { + if mplsLabel == 0 { + return + } + if ie.Egress.Type == fabrid.IPv4Range || ie.Egress. + Type == fabrid.IPv6Range { // Egress is IP network: + key := 1<<31 + uint32(policyIdx) // Wildcard ingress interface + if ie.Ingress.Type == fabrid.Interface { // Specified ingress interface + key = uint32(ie.Ingress.InterfaceId)<<8 + uint32(policyIdx) + } + m.IPPoliciesMap[key] = append(m.IPPoliciesMap[key], PolicyIPRange{ + IP: ie.Egress.IPNetwork().IP, + Prefix: ie.Egress.Prefix, + MPLSLabel: mplsLabel}) + } else { + egIf := uint64(0) + if ie.Egress.Type == fabrid.Interface { + egIf = uint64(ie.Egress.InterfaceId) + } + // Wildcard ingress interface: + key := 1<<63 + egIf<<8 + uint64(policyIdx) + if ie.Ingress.Type == fabrid.Interface { // Specified ingress interface + key = uint64(ie.Ingress.InterfaceId)<<24 + egIf<<8 + uint64(policyIdx) + } + m.InterfacePoliciesMap[key] = mplsLabel + } +} + +func sortedKeys[K uint32 | uint64, V any](m map[K]V) []K { + keys := make([]K, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + + return keys +} + +// This method is to be called after all inserts and removes from the internal map and calculates +// the hash for the MPLS map according to the entries. The order of insertion is not relevant here. +func (m *MplsMaps) UpdateHash() { + h := fnv.New64() + for _, polIdx := range sortedKeys(m.IPPoliciesMap) { + _ = binary.Write(h, binary.BigEndian, polIdx) + for _, ipRange := range m.IPPoliciesMap[polIdx] { + _ = binary.Write(h, binary.BigEndian, ipRange.MPLSLabel) + _, _ = h.Write(ipRange.IP) + _ = binary.Write(h, binary.BigEndian, ipRange.Prefix) + } + } + + for _, polIdx := range sortedKeys(m.InterfacePoliciesMap) { + _ = binary.Write(h, binary.BigEndian, polIdx) + _ = binary.Write(h, binary.BigEndian, m.InterfacePoliciesMap[polIdx]) + } + m.CurrentHash = h.Sum(nil) +} diff --git a/control/fabrid/mpls_map_test.go b/control/fabrid/mpls_map_test.go new file mode 100644 index 0000000000..36dcd9b7fb --- /dev/null +++ b/control/fabrid/mpls_map_test.go @@ -0,0 +1,236 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" +) + +func TestEqualHashDifferentInsertionOrders(t *testing.T) { + type insertionInput struct { + CP fabrid.ConnectionPair + mplsLabel uint32 + polIdx uint8 + } + + input := []insertionInput{ + { + CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 10, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 20, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 3, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 3, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 3, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.IPv4Range, + IP: "192.168.1.0", + Prefix: 24, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.IPv4Range, + IP: "192.168.1.0", + Prefix: 24, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + } + + m := NewMplsMaps() + for _, i := range input { + m.AddConnectionPoint(i.CP, i.mplsLabel, i.polIdx) + } + m.UpdateHash() + baseHash := m.CurrentHash + rand.New(rand.NewSource(42)) + + for round := 0; round < 10; round++ { + m = NewMplsMaps() + rand.Shuffle(len(input), func(i, j int) { input[i], input[j] = input[j], input[i] }) + for _, i := range input { + m.AddConnectionPoint(i.CP, i.mplsLabel, i.polIdx) + } + + m.UpdateHash() + require.Equal(t, baseHash, m.CurrentHash) + } +} + +func TestHashChanges(t *testing.T) { + + type insertionInput struct { + CP fabrid.ConnectionPair + mplsLabel uint32 + polIdx uint8 + } + + input := []insertionInput{ + { + CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 10, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 20, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 3, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 3, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 3, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.IPv4Range, + IP: "192.168.1.0", + Prefix: 24, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.IPv4Range, + IP: "192.168.1.0", + Prefix: 24, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + + {CP: fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Wildcard, + }, + }, + mplsLabel: 21902, + polIdx: 20, + }, + } + + rand.New(rand.NewSource(42)) + + for round := 0; round < 10; round++ { + m := NewMplsMaps() + rand.Shuffle(len(input), func(i, j int) { input[i], input[j] = input[j], input[i] }) + prevHashes := make([][]byte, len(input)) + for j, i := range input { + m.AddConnectionPoint(i.CP, i.mplsLabel, i.polIdx) + m.UpdateHash() + require.NotContains(t, prevHashes, m.CurrentHash) + prevHashes[j] = m.CurrentHash + } + } +} diff --git a/control/fabrid/testdata/correct/1-global_example.yml b/control/fabrid/testdata/correct/1-global_example.yml new file mode 100644 index 0000000000..6e8a60e756 --- /dev/null +++ b/control/fabrid/testdata/correct/1-global_example.yml @@ -0,0 +1,11 @@ +connections: + - ingress: + type: interface + interface: 2 + egress: + type: interface + interface: 1 + mpls_label: 1 +local: false +global_identifier: 1101 + diff --git a/control/fabrid/testdata/correct/2-global_example.yml b/control/fabrid/testdata/correct/2-global_example.yml new file mode 100644 index 0000000000..83d25f3d17 --- /dev/null +++ b/control/fabrid/testdata/correct/2-global_example.yml @@ -0,0 +1,10 @@ +connections: + - ingress: + type: interface + interface: 2 + egress: + type: interface + interface: 1 + mpls_label: 2 +local: false +global_identifier: 1102 diff --git a/control/fabrid/testdata/correct/3-local_example.yml b/control/fabrid/testdata/correct/3-local_example.yml new file mode 100644 index 0000000000..9ed9ae0faa --- /dev/null +++ b/control/fabrid/testdata/correct/3-local_example.yml @@ -0,0 +1,17 @@ +connections: + - ingress: + type: interface + interface: 2 + egress: + type: interface + interface: 1 + mpls_label: 5 + - ingress: + type: wildcard + egress: + type: interface + interface: 2 + mpls_label: 3 +local: true +local_identifier: 1103 +local_description: Fabrid Example Policy diff --git a/control/fabrid/testdata/correct/55-local_example.yml b/control/fabrid/testdata/correct/55-local_example.yml new file mode 100644 index 0000000000..5c82dc9a9f --- /dev/null +++ b/control/fabrid/testdata/correct/55-local_example.yml @@ -0,0 +1,13 @@ +connections: + - ingress: + type: interface + interface: 2 + egress: + type: ipv4 + ip: 192.168.5.1 + prefix: 24 + mpls_label: 55 +local: true +local_identifier: 11055 +local_description: Fabrid Example Policy 2 + diff --git a/control/fabrid/testdata/mixed/1-global_example.yml b/control/fabrid/testdata/mixed/1-global_example.yml new file mode 100644 index 0000000000..6e8a60e756 --- /dev/null +++ b/control/fabrid/testdata/mixed/1-global_example.yml @@ -0,0 +1,11 @@ +connections: + - ingress: + type: interface + interface: 2 + egress: + type: interface + interface: 1 + mpls_label: 1 +local: false +global_identifier: 1101 + diff --git a/control/fabrid/testdata/mixed/invalid.yml b/control/fabrid/testdata/mixed/invalid.yml new file mode 100644 index 0000000000..fdcf008db7 --- /dev/null +++ b/control/fabrid/testdata/mixed/invalid.yml @@ -0,0 +1 @@ +invalid policy diff --git a/control/mgmtapi/testdata/TestAPI_beacon_blob b/control/mgmtapi/testdata/TestAPI_beacon_blob index dd0286378c..77fe6406c6 100644 --- a/control/mgmtapi/testdata/TestAPI_beacon_blob +++ b/control/mgmtapi/testdata/TestAPI_beacon_blob @@ -1,3 +1,3 @@ -----BEGIN PATH SEGMENT----- -EgASAA== +EgISABICEgA= -----END PATH SEGMENT----- diff --git a/control/mgmtapi/testdata/TestAPI_beacon_id_prefix_blob b/control/mgmtapi/testdata/TestAPI_beacon_id_prefix_blob index dd0286378c..77fe6406c6 100644 --- a/control/mgmtapi/testdata/TestAPI_beacon_id_prefix_blob +++ b/control/mgmtapi/testdata/TestAPI_beacon_id_prefix_blob @@ -1,3 +1,3 @@ -----BEGIN PATH SEGMENT----- -EgASAA== +EgISABICEgA= -----END PATH SEGMENT----- diff --git a/control/tasks.go b/control/tasks.go index 4de2588cc6..9db370f6b2 100644 --- a/control/tasks.go +++ b/control/tasks.go @@ -23,6 +23,7 @@ import ( "github.com/scionproto/scion/control/beacon" "github.com/scionproto/scion/control/beaconing" "github.com/scionproto/scion/control/drkey" + "github.com/scionproto/scion/control/fabrid" "github.com/scionproto/scion/control/ifstate" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/experimental/hiddenpath" @@ -60,6 +61,7 @@ type TasksConfig struct { Inspector trust.Inspector Metrics *Metrics DRKeyEngine *drkey.ServiceEngine + Fabrid *fabrid.FabridManager MACGen func() hash.Hash StaticInfo func() *beaconing.StaticInfoCfg @@ -217,6 +219,7 @@ func (t *TasksConfig) extender( MTU: mtu, MaxExpTime: func() uint8 { return maxExp() }, StaticInfo: t.StaticInfo, + Fabrid: t.Fabrid, Task: task, EPIC: t.EPIC, SegmentExpirationDeficient: func() metrics.Gauge { diff --git a/daemon/cmd/daemon/main.go b/daemon/cmd/daemon/main.go index 5b3f7c3e05..06c911182d 100644 --- a/daemon/cmd/daemon/main.go +++ b/daemon/cmd/daemon/main.go @@ -202,14 +202,24 @@ func realMain(ctx context.Context) error { }, } defer level2DB.Close() - - drkeyFetcher := &sd_grpc.Fetcher{ - Dialer: dialer, + localAddrIPString, _, err := net.SplitHostPort(globalCfg.SD.Address) + if err != nil { + return serrors.WrapStr("resolve local address", err) + } + localAddrIP := net.ParseIP(localAddrIPString) + localAddr := &net.TCPAddr{ + IP: localAddrIP, + Port: 0, } drkeyClientEngine = &sd_drkey.ClientEngine{ - IA: topo.IA(), - DB: level2DB, - Fetcher: drkeyFetcher, + IA: topo.IA(), + DB: level2DB, + Fetcher: &sd_grpc.Fetcher{ + Dialer: &libgrpc.FixedLocalIPTCPDialer{ + LocalAddr: localAddr, + SvcResolver: dialer.SvcResolver, + }, + }, } cleaners := drkeyClientEngine.CreateStorageCleaners() for _, cleaner := range cleaners { @@ -275,6 +285,7 @@ func realMain(ctx context.Context) error { Cfg: globalCfg.SD, }, ), + Dialer: dialer, Engine: engine, RevCache: revCache, DRKeyClient: drkeyClientEngine, diff --git a/daemon/daemon.go b/daemon/daemon.go index 30c92918bc..0e61171754 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -114,6 +114,7 @@ type ServerConfig struct { Engine trust.Engine Topology servers.Topology DRKeyClient *drkey.ClientEngine + Dialer libgrpc.Dialer } // NewServer constructs a daemon API server. @@ -129,6 +130,7 @@ func NewServer(cfg ServerConfig) *servers.DaemonServer { ASInspector: cfg.Engine.Inspector, RevCache: cfg.RevCache, DRKeyClient: cfg.DRKeyClient, + Dialer: cfg.Dialer, Metrics: servers.Metrics{ PathsRequests: servers.RequestMetrics{ Requests: metrics.NewPromCounterFrom(prometheus.CounterOpts{ diff --git a/daemon/drkey/BUILD.bazel b/daemon/drkey/BUILD.bazel index 95dcc79a92..140993f8f1 100644 --- a/daemon/drkey/BUILD.bazel +++ b/daemon/drkey/BUILD.bazel @@ -2,7 +2,10 @@ load("//tools/lint:go.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["client_engine.go"], + srcs = [ + "client_engine.go", + "client_engine_fabrid.go", + ], importpath = "github.com/scionproto/scion/daemon/drkey", visibility = ["//visibility:public"], deps = [ diff --git a/daemon/drkey/client_engine_fabrid.go b/daemon/drkey/client_engine_fabrid.go new file mode 100644 index 0000000000..0aaf752741 --- /dev/null +++ b/daemon/drkey/client_engine_fabrid.go @@ -0,0 +1,72 @@ +// Copyright 2024 ETH Zurich +// +// 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 drkey + +import ( + "context" + "time" + + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/private/serrors" +) + +// For all ASHost Keys and for the HostHost Key, it checks whether the keys are in the database. +// If this is the case, those keys are returned. If not, the keys are requested from CS. +func (e *ClientEngine) FabridKeys(ctx context.Context, meta drkey.FabridKeysMeta, +) (drkey.FabridKeysResponse, error) { + now := time.Now() + hostHostKey := drkey.FabridKey{} + if meta.DstHost != nil && len(meta.PathASes) > 0 { + key, err := e.GetHostHostKey(ctx, drkey.HostHostMeta{ + ProtoId: drkey.FABRID, + Validity: now, + SrcIA: meta.DstAS, + SrcHost: *meta.DstHost, + DstIA: meta.SrcAS, + DstHost: meta.SrcHost, + }) + if err != nil { + return drkey.FabridKeysResponse{}, serrors.WrapStr("prepare FABRID host-host key", err) + } + hostHostKey = drkey.FabridKey{ + Epoch: key.Epoch, + AS: meta.DstAS, + Key: key.Key, + } + } + asHostKeys := make([]drkey.FabridKey, 0, len(meta.PathASes)) + for _, as := range meta.PathASes { + key, err := e.GetASHostKey(ctx, drkey.ASHostMeta{ + ProtoId: drkey.FABRID, + Validity: now, + SrcIA: as, + DstIA: meta.SrcAS, + DstHost: meta.SrcHost, + }) + if err != nil { + return drkey.FabridKeysResponse{}, serrors.WrapStr("prepare FABRID AS-host key", err) + } + asHostKeys = append(asHostKeys, drkey.FabridKey{ + Epoch: key.Epoch, + AS: as, + Key: key.Key, + }) + } + + return drkey.FabridKeysResponse{ + ASHostKeys: asHostKeys, + PathKey: hostHostKey, + }, nil +} diff --git a/daemon/internal/servers/BUILD.bazel b/daemon/internal/servers/BUILD.bazel index 9bf6640324..e0a0e42474 100644 --- a/daemon/internal/servers/BUILD.bazel +++ b/daemon/internal/servers/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "grpc.go", + "grpc_fabrid.go", "metrics.go", ], importpath = "github.com/scionproto/scion/daemon/internal/servers", @@ -13,6 +14,9 @@ go_library( "//daemon/fetcher:go_default_library", "//pkg/addr:go_default_library", "//pkg/drkey:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/experimental/fabrid/graphutils:go_default_library", + "//pkg/grpc:go_default_library", "//pkg/log:go_default_library", "//pkg/metrics:go_default_library", "//pkg/private/common:go_default_library", @@ -21,7 +25,9 @@ go_library( "//pkg/private/prom:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", "//pkg/proto/daemon:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", "//private/revcache:go_default_library", diff --git a/daemon/internal/servers/grpc.go b/daemon/internal/servers/grpc.go index 8f1d929b5f..54243cf3be 100644 --- a/daemon/internal/servers/grpc.go +++ b/daemon/internal/servers/grpc.go @@ -30,6 +30,7 @@ import ( "github.com/scionproto/scion/daemon/fetcher" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/drkey" + libgrpc "github.com/scionproto/scion/pkg/grpc" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/ctrl/path_mgmt" @@ -62,8 +63,8 @@ type DaemonServer struct { RevCache revcache.RevCache ASInspector trust.Inspector DRKeyClient *drkey_daemon.ClientEngine - - Metrics Metrics + Dialer libgrpc.Dialer + Metrics Metrics foregroundPathDedupe singleflight.Group backgroundPathDedupe singleflight.Group @@ -102,6 +103,13 @@ func (s *DaemonServer) paths(ctx context.Context, "src", srcIA, "dst", dstIA, "refresh", req.Refresh) return nil, err } + if req.FetchFabridDetachedMaps { + detachedHops := findDetachedHops(paths) + if len(detachedHops) > 0 { + log.Info("Detached hops found", "hops", len(detachedHops)) + updateFabridInfo(ctx, s.Dialer, detachedHops) + } + } reply := &sdpb.PathsResponse{} for _, p := range paths { reply.Paths = append(reply.Paths, pathToPB(p)) @@ -165,7 +173,10 @@ func pathToPB(path snet.Path) *sdpb.Path { if nextHop := path.UnderlayNextHop(); nextHop != nil { nextHopStr = nextHop.String() } - + fabridInfo := make([]*sdpb.FabridInfo, len(meta.FabridInfo)) + for i, v := range meta.FabridInfo { + fabridInfo[i] = fabridInfoToPB(&v) + } epicAuths := &sdpb.EpicAuths{ AuthPhvf: append([]byte(nil), meta.EpicAuths.AuthPHVF...), AuthLhvf: append([]byte(nil), meta.EpicAuths.AuthLHVF...), @@ -187,8 +198,8 @@ func pathToPB(path snet.Path) *sdpb.Path { InternalHops: meta.InternalHops, Notes: meta.Notes, EpicAuths: epicAuths, + FabridInfo: fabridInfo, } - } func linkTypeToPB(lt snet.LinkType) sdpb.LinkType { diff --git a/daemon/internal/servers/grpc_fabrid.go b/daemon/internal/servers/grpc_fabrid.go new file mode 100644 index 0000000000..a0f7c3f1f2 --- /dev/null +++ b/daemon/internal/servers/grpc_fabrid.go @@ -0,0 +1,195 @@ +// Copyright 2024 ETH Zurich +// +// 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 servers + +import ( + "context" + "time" + + timestamppb "github.com/golang/protobuf/ptypes/timestamp" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid" + fabrid_utils "github.com/scionproto/scion/pkg/experimental/fabrid/graphutils" + libgrpc "github.com/scionproto/scion/pkg/grpc" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + sdpb "github.com/scionproto/scion/pkg/proto/daemon" + fabrid_ext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" + "github.com/scionproto/scion/pkg/snet" +) + +type tempHopInfo struct { + IA addr.IA + Meta *snet.PathMetadata + fiIdx int + Ingress uint16 + Egress uint16 +} + +// updateFabridInfo updates the FABRID info that is contained in the path Metadata for detached +// hops, by fetching the corresponding FABRID maps from the corresponding AS. +func updateFabridInfo(ctx context.Context, dialer libgrpc.Dialer, detachedHops []tempHopInfo) { + conn, err := dialer.Dial(ctx, &snet.SVCAddr{SVC: addr.SvcCS}) + if err != nil { + log.FromCtx(ctx).Debug("Dialing CS failed", "err", err) + } + defer conn.Close() + client := experimental.NewFABRIDIntraServiceClient(conn) + fabridMaps := make(map[addr.IA]fabrid_utils.FabridMapEntry) + for _, detachedHop := range detachedHops { + if _, ok := fabridMaps[detachedHop.IA]; !ok { + fabridMaps[detachedHop.IA] = fetchMaps(ctx, detachedHop.IA, client, + detachedHop.Meta.FabridInfo[detachedHop.fiIdx].Digest) + } + detachedHop.Meta.FabridInfo[detachedHop.fiIdx] = *fabrid_utils. + GetFabridInfoForIntfs(detachedHop.IA, detachedHop.Ingress, detachedHop.Egress, + fabridMaps, true) + } +} + +// findDetachedHops finds the hops where the FABRID maps have been detached in a given list of +// paths. +func findDetachedHops(paths []snet.Path) []tempHopInfo { + detachedHops := make([]tempHopInfo, 0) + for _, p := range paths { + if p.Metadata().FabridInfo[0].Enabled && p.Metadata().FabridInfo[0].Detached { + detachedHops = append(detachedHops, tempHopInfo{ + IA: p.Metadata().Interfaces[0].IA, + Meta: p.Metadata(), + fiIdx: 0, + Ingress: 0, + Egress: uint16(p.Metadata().Interfaces[0].ID), + }) + } + for i := 1; i < len(p.Metadata().Interfaces)-1; i += 2 { + if p.Metadata().FabridInfo[(i+1)/2].Enabled && + p.Metadata().FabridInfo[(i+1)/2].Detached { + detachedHops = append(detachedHops, tempHopInfo{ + IA: p.Metadata().Interfaces[i].IA, + Meta: p.Metadata(), + fiIdx: (i + 1) / 2, + Ingress: uint16(p.Metadata().Interfaces[i].ID), + Egress: uint16(p.Metadata().Interfaces[i+1].ID), + }) + } + } + if p.Metadata().FabridInfo[len(p.Metadata().Interfaces)/2].Enabled && + p.Metadata().FabridInfo[len(p.Metadata().Interfaces)/2].Detached { + detachedHops = append(detachedHops, tempHopInfo{ + IA: p.Metadata().Interfaces[len(p.Metadata().Interfaces)-1].IA, + Meta: p.Metadata(), + fiIdx: len(p.Metadata().Interfaces) / 2, + Ingress: uint16(p.Metadata().Interfaces[len(p.Metadata().Interfaces)-1].ID), + Egress: 0, + }) + } + } + return detachedHops +} + +// fetchMaps retrieves FABRID maps from the Control Service for a given ISD-AS. +// It uses the provided client to communicate with the Control Service and returns a FabridMapEntry +// to be used directly in the combinator. +func fetchMaps(ctx context.Context, ia addr.IA, client experimental.FABRIDIntraServiceClient, + digest []byte) fabrid_utils.FabridMapEntry { + maps, err := client.RemoteMaps(ctx, &experimental.RemoteMapsRequest{ + Digest: digest, + IsdAs: uint64(ia), + }) + if err != nil || maps.Maps == nil { + log.FromCtx(ctx).Debug("Retrieving remote map from CS failed", "err", err, "ia", + ia) + return fabrid_utils.FabridMapEntry{} + } + + detached := fabrid_ext.Detached{ + SupportedIndicesMap: fabrid_ext.SupportedIndicesMapFromPB(maps.Maps.SupportedIndicesMap), + IndexIdentiferMap: fabrid_ext.IndexIdentifierMapFromPB(maps.Maps.IndexIdentifierMap), + } + return fabrid_utils.FabridMapEntry{ + Map: &detached, + Ts: time.Now(), + Digest: []byte{}, // leave empty, it can be calculated using detached.Hash() + } +} + +func fabridPolicyToPB(fp *fabrid.Policy) *sdpb.FabridPolicy { + return &sdpb.FabridPolicy{ + PolicyIdentifier: &experimental.FABRIDPolicyIdentifier{ + PolicyIsLocal: fp.IsLocal, + PolicyIdentifier: fp.Identifier, + }, + PolicyIndex: uint32(fp.Index), + } +} + +func fabridInfoToPB(fi *snet.FabridInfo) *sdpb.FabridInfo { + pbPolicies := make([]*sdpb.FabridPolicy, len(fi.Policies)) + for i, fp := range fi.Policies { + pbPolicies[i] = fabridPolicyToPB(fp) + } + return &sdpb.FabridInfo{ + Enabled: fi.Enabled, + Digest: fi.Digest, + Policies: pbPolicies, + Detached: fi.Detached, + } +} + +func (s *DaemonServer) FabridKeys(ctx context.Context, req *sdpb.FabridKeysRequest, +) (*sdpb.FabridKeysResponse, error) { + if s.DRKeyClient == nil { + return nil, serrors.New("DRKey is not available") + } + pathASes := make([]addr.IA, 0, len(req.PathAses)) + for _, as := range req.PathAses { + pathASes = append(pathASes, addr.IA(as)) + } + resp, err := s.DRKeyClient.FabridKeys(ctx, drkey.FabridKeysMeta{ + SrcAS: s.DRKeyClient.IA, + SrcHost: req.SrcHost, + DstHost: req.DstHost, + PathASes: pathASes, + DstAS: addr.IA(req.DstAs), + }) + if err != nil { + return nil, serrors.WrapStr("getting fabrid keys from client store", err) + } + fabridKeys := make([]*sdpb.FabridKeyResponse, 0, len(resp.ASHostKeys)) + for i := range resp.ASHostKeys { + key := resp.ASHostKeys[i] + fabridKeys = append(fabridKeys, &sdpb.FabridKeyResponse{ + EpochBegin: ×tamppb.Timestamp{Seconds: key.Epoch.NotBefore.Unix()}, + EpochEnd: ×tamppb.Timestamp{Seconds: key.Epoch.NotAfter.Unix()}, + Key: key.Key[:], + }) + } + + var hostHostKey *sdpb.FabridKeyResponse = nil + if req.DstHost != nil { + hostHostKey = &sdpb.FabridKeyResponse{ + EpochBegin: ×tamppb.Timestamp{Seconds: resp.PathKey.Epoch.NotBefore.Unix()}, + EpochEnd: ×tamppb.Timestamp{Seconds: resp.PathKey.Epoch.NotAfter.Unix()}, + Key: resp.PathKey.Key[:], + } + } + return &sdpb.FabridKeysResponse{ + AsHostKeys: fabridKeys, + HostHostKey: hostHostKey, + }, nil +} diff --git a/doc/command/scion/scion_ping.rst b/doc/command/scion/scion_ping.rst index 313d8b24cc..2c8050d044 100644 --- a/doc/command/scion/scion_ping.rst +++ b/doc/command/scion/scion_ping.rst @@ -88,6 +88,8 @@ Options -c, --count uint16 total number of packets to send --epic Enable EPIC for path probing. + --fabridquery string the query for policies that the path must adhere to + --fetch-detached Fetch FABRID maps for hops where they have been detached. This increases the overhead of path finding. (default true) --format string Specify the output format (human|json|yaml) (default "human") --healthy-only only use healthy paths -h, --help help for ping diff --git a/doc/dev/design/FABRID.rst b/doc/dev/design/FABRID.rst index 73ea04cc76..9a8e0c361c 100644 --- a/doc/dev/design/FABRID.rst +++ b/doc/dev/design/FABRID.rst @@ -20,7 +20,7 @@ This can also be seen as an enhancement for keeping traffic within a certain jur only along devices located in a specific country, because a single AS could cover multiple countries. .. figure:: fig/FABRID/NetworkTopology.png - + Example network topology. Different colors for intra-AS routers indicate different manufacturers. H01 in AS01 and H02 in AS02 want to communicate with each other. @@ -128,7 +128,7 @@ The Identifier Option always has a length of 8 bytes and looks like: Timestamp The 27 bit timestamp referring to the packet's transmission time with 1 millisecond precision relative to the timestamp of the first :ref:`InfoField ` of the SCION header. - + Packet ID The 32 bit packet ID that, together with the timestamp, uniquely identifies a source endhost's packet. @@ -315,7 +315,7 @@ The following list explains the most important maps used in the FABRID service: Prefix uint32 InterfaceID uint16 } - + - Index identifiers A policy index is to be embedded in the HBH extension and therefore has to be minimal in size. The size of a policy index is 8 bits, whereas identifiers can be a multiple of this (especially global identifiers). @@ -370,9 +370,9 @@ A custom language is used to make a selection out of the available paths and pol Applying a policy refers to selecting that specific policy for that hop when sending a FABRID packet. In case of multiple matches, the first match (from left to right) will be selected. Parts of this hop identifier may be a wildcard, such that the identifier can match with multiple hops in the path. - An identifier is structured as follows: + An identifier is structured as follows: ``ISD-AS#IGIF,EGIF@POLICY``, - where + where * ISD can be either the ISD number (e.g. ``1``), or a wildcard (``0``). * AS can be either the AS number seperated by underscores (e.g. ``ff00_0_110``) or a wildcard (``0``). @@ -380,11 +380,11 @@ A custom language is used to make a selection out of the available paths and pol * EGIF can be either the egress interface number (e.g. ``41``), or a wildcard (``0``). * POLICY can be either the policy to apply, where a local policy is denoted as ``L`` + the policy identifier (e.g. ``L100``) and a global policy is denoted by ``G`` + the policy identifier (e.g. ``G100``), a wildcard (``0``), or a rejection ``REJECT``. - Rejection means that this path should not be chosen. + Rejection means that this path should not be chosen. * **Concatenations** - Multiple identifiers can be combined by using a concatenation. Concatenations are created by the ``+`` symbol. + Multiple identifiers can be combined by using a concatenation. Concatenations are created by the ``+`` symbol. Example: @@ -418,18 +418,18 @@ A custom language is used to make a selection out of the available paths and pol The same applies for the ``EXPRESSION_IF_FALSE`` branch. Example: - + There is a specific policy that signals that the middleboxes in this AS are from a specific manufacturer, e.g. ``G150``. This manufacturer is known to have a security vulnerability that allows malicious users to intercept traffic. The traffic to be sent is highly confidential, so the path should not be used. In this case the query ``{0-0#0,0@G150 ? 0-0#0,0@REJECT : 0-0#0,0@0}`` can be used. - ``G150`` in this case is a blacklisted policy. + ``G150`` in this case is a blacklisted policy. (An alternative is a whitelist, where a user would specify all manufacturers that are allowed, i.e. ``G151``, ``G152``, ``G153``: ``0-0#0,0@G151 + 0-0#0,0@G152 + 0-0#0,0@G153 + 0-0#0,0@REJECT}``) **Evaluation Order** The language is evaluated left to right, for each hop only a single policy can be applied. -The first identifier match applies the policy, so the order of the query is important. +The first identifier match applies the policy, so the order of the query is important. Example: @@ -529,7 +529,7 @@ Example of a list of connection points: egress: type: interface interface: 1 - mpls_label: 1 + mpls_label: 1 - ingress: type: interface interface: 2 @@ -671,4 +671,4 @@ And in a second stage: - Full FABRID with path validation also at source -- FABRID Intra-AS emulation for SCIONLab \ No newline at end of file +- FABRID Intra-AS emulation for SCIONLab diff --git a/docker/BUILD.bazel b/docker/BUILD.bazel index ec356b2378..ed6d2c6ff9 100644 --- a/docker/BUILD.bazel +++ b/docker/BUILD.bazel @@ -1,6 +1,7 @@ load("@rules_debian_packages//debian_packages:defs.bzl", "debian_packages_lockfile") load(":scion_app.bzl", "scion_app_base", "scion_app_image") load(":tester.bzl", "scion_tester_image") +load(":endhost.bzl", "scion_endhost_image") load(":labels.bzl", "scion_labels") filegroup( @@ -17,6 +18,7 @@ filegroup( filegroup( name = "test", srcs = [ + ":endhost.tar", ":tester.tar", ], ) @@ -94,3 +96,5 @@ debian_packages_lockfile( ) scion_tester_image() + +scion_endhost_image() diff --git a/docker/endhost.bzl b/docker/endhost.bzl new file mode 100644 index 0000000000..3604af4ee7 --- /dev/null +++ b/docker/endhost.bzl @@ -0,0 +1,79 @@ +load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file") +load("@rules_oci//oci:defs.bzl", "oci_image", "oci_tarball") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("@tester_debian10_packages//:packages.bzl", "debian_package_layer") + +def scion_endhost_image(): + pkg_tar( + name = "endhost_layer_packages", + deps = [ + debian_package_layer("bridge-utils"), + debian_package_layer("iperf3"), + debian_package_layer("iptables"), + debian_package_layer("netcat-openbsd"), + debian_package_layer("openssh-server"), + debian_package_layer("openssh-client"), + debian_package_layer("procps"), + debian_package_layer("telnet"), + debian_package_layer("tshark"), + debian_package_layer("wget"), + ], + ) + + pkg_tar( + name = "endhost_layer_bin", + srcs = [ + "//tools/end2end:end2end", + "//scion/cmd/scion", + "//scion-pki/cmd/scion-pki:scion-pki", + "//daemon/cmd/daemon", + ], + package_dir = "share/bin", + ) + + pkg_tar( + name = "endhost_layer_tools_integration", + srcs = [ + "//tools/integration:bin_wrapper.sh", + ], + package_dir = "share/tools/integration", + ) + + pkg_tar( + name = "endhost_layer_share", + srcs = native.glob(["files/*"]), + package_dir = "share", + ) + + oci_image( + name = "endhost", + base = "@debian10", + env = { + "TZ": "UTC", + "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/share/bin", + }, + workdir = "/share", + cmd = ["tail", "-f", "/dev/null"], + tars = [ + ":endhost_layer_packages", + ":endhost_layer_share", + ":endhost_layer_tools_integration", + ":endhost_layer_bin", + ], + labels = ":labels", + visibility = ["//visibility:public"], + ) + oci_tarball( + name = "endhost.load", + format = "docker", + image = "endhost", + repo_tags = ["scion/endhost:latest"], + ) + + # see comment on scion_app.bzl + copy_file( + name = "endhost.tarball", + src = "endhost.load", + out = "endhost.tar", + visibility = ["//visibility:public"], + ) diff --git a/docker/files/endhost.sh b/docker/files/endhost.sh new file mode 100644 index 0000000000..33aef7a5df --- /dev/null +++ b/docker/files/endhost.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +term() { + exit 0 +} + +trap term TERM + +/share/bin/daemon --config /etc/scion/endhost.toml & + +echo "Endhost started" + +# Wake up from sleep once in a while so that SIGTERM is handled. +while : +do + sleep 0.1 +done diff --git a/pkg/addr/fmt.go b/pkg/addr/fmt.go index ed9b2d9de1..12b094d23f 100644 --- a/pkg/addr/fmt.go +++ b/pkg/addr/fmt.go @@ -65,7 +65,7 @@ func ParseFormattedAS(as string, opts ...FormatOption) (AS, error) { } as = trimmed } - return parseAS(as, o.separator) + return ParseASSep(as, o.separator) } // FormatIA formats the ISD-AS. diff --git a/pkg/daemon/BUILD.bazel b/pkg/daemon/BUILD.bazel index 8a8ca23be8..4c245e9fe4 100644 --- a/pkg/daemon/BUILD.bazel +++ b/pkg/daemon/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "apitypes.go", "daemon.go", "grpc.go", + "grpc_fabrid.go", "metrics.go", ], importpath = "github.com/scionproto/scion/pkg/daemon", @@ -14,6 +15,7 @@ go_library( "//pkg/addr:go_default_library", "//pkg/daemon/internal/metrics:go_default_library", "//pkg/drkey:go_default_library", + "//pkg/experimental/fabrid:go_default_library", "//pkg/grpc:go_default_library", "//pkg/metrics:go_default_library", "//pkg/private/common:go_default_library", diff --git a/pkg/daemon/apitypes.go b/pkg/daemon/apitypes.go index 4b4564bef8..0ecb283267 100644 --- a/pkg/daemon/apitypes.go +++ b/pkg/daemon/apitypes.go @@ -27,8 +27,9 @@ import ( ) type PathReqFlags struct { - Refresh bool - Hidden bool + Refresh bool + Hidden bool + FetchFabridDetachedMaps bool } // ASInfo provides information about the local AS. diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index a9ba3397ee..edd6994cd9 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -88,6 +88,8 @@ type Connector interface { DRKeyGetHostASKey(ctx context.Context, meta drkey.HostASMeta) (drkey.HostASKey, error) // DRKeyGetHostHostKey requests a Host-Host Key from the daemon. DRKeyGetHostHostKey(ctx context.Context, meta drkey.HostHostMeta) (drkey.HostHostKey, error) + // FabridKeys requests FABRID DRKeys for all provided ASes and the path validation key + FabridKeys(ctx context.Context, meta drkey.FabridKeysMeta) (drkey.FabridKeysResponse, error) // Close shuts down the connection to the daemon. Close() error } diff --git a/pkg/daemon/grpc.go b/pkg/daemon/grpc.go index ec4e16fdb0..3b37fbc5db 100644 --- a/pkg/daemon/grpc.go +++ b/pkg/daemon/grpc.go @@ -110,10 +110,11 @@ func (c grpcConn) Paths(ctx context.Context, dst, src addr.IA, client := sdpb.NewDaemonServiceClient(c.conn) response, err := client.Paths(ctx, &sdpb.PathsRequest{ - SourceIsdAs: uint64(src), - DestinationIsdAs: uint64(dst), - Hidden: f.Hidden, - Refresh: f.Refresh, + SourceIsdAs: uint64(src), + DestinationIsdAs: uint64(dst), + Hidden: f.Hidden, + Refresh: f.Refresh, + FetchFabridDetachedMaps: f.FetchFabridDetachedMaps, }) if err != nil { c.metrics.incPaths(err) @@ -281,6 +282,11 @@ func convertPath(p *sdpb.Path, dst addr.IA) (path.Path, error) { linkType[i] = linkTypeFromPB(v) } + fabridInfo := make([]snet.FabridInfo, len(p.FabridInfo)) + for i, v := range p.FabridInfo { + fabridInfo[i] = fabridInfoFromPB(v) + } + res := path.Path{ Src: interfaces[0].IA, Dst: dst, @@ -299,6 +305,7 @@ func convertPath(p *sdpb.Path, dst addr.IA) (path.Path, error) { LinkType: linkType, InternalHops: p.InternalHops, Notes: p.Notes, + FabridInfo: fabridInfo, }, } diff --git a/pkg/daemon/grpc_fabrid.go b/pkg/daemon/grpc_fabrid.go new file mode 100644 index 0000000000..3983c5a1b2 --- /dev/null +++ b/pkg/daemon/grpc_fabrid.go @@ -0,0 +1,93 @@ +// Copyright 2024 ETH Zurich +// +// 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 daemon + +import ( + "context" + + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid" + sdpb "github.com/scionproto/scion/pkg/proto/daemon" + "github.com/scionproto/scion/pkg/scrypto/cppki" + "github.com/scionproto/scion/pkg/snet" +) + +func fabridInfoFromPB(fi *sdpb.FabridInfo) snet.FabridInfo { + pbPolicies := make([]*fabrid.Policy, len(fi.Policies)) + for i, fp := range fi.Policies { + pbPolicies[i] = &fabrid.Policy{ + IsLocal: fp.PolicyIdentifier.PolicyIsLocal, + Identifier: fp.PolicyIdentifier.PolicyIdentifier, + Index: fabrid.PolicyID(fp.PolicyIndex), + } + } + return snet.FabridInfo{ + Enabled: fi.Enabled, + Policies: pbPolicies, + Digest: fi.Digest, + Detached: fi.Detached, + } +} + +// Returns all the ASHost DRKeys for the ASes inside the meta.PathAS +func (c grpcConn) FabridKeys(ctx context.Context, meta drkey.FabridKeysMeta, +) (drkey.FabridKeysResponse, error) { + + client := sdpb.NewDaemonServiceClient((c.conn)) + pathASes := make([]uint64, 0, len(meta.PathASes)) + for i := 0; i < len(meta.PathASes); i++ { + pathASes = append(pathASes, uint64(meta.PathASes[i])) + } + resp, err := client.FabridKeys(ctx, &sdpb.FabridKeysRequest{ + SrcHost: meta.SrcHost, + PathAses: pathASes, + DstAs: uint64(meta.DstAS), + DstHost: meta.DstHost, + }) + if err != nil { + return drkey.FabridKeysResponse{}, err + } + asHostKeys := make([]drkey.FabridKey, 0, len(resp.AsHostKeys)) + for i, key := range resp.AsHostKeys { + epoch := drkey.Epoch{ + Validity: cppki.Validity{ + NotBefore: key.EpochBegin.AsTime(), + NotAfter: key.EpochEnd.AsTime(), + }, + } + asHostKeys = append(asHostKeys, drkey.FabridKey{ + Epoch: epoch, + AS: meta.PathASes[i], + Key: drkey.Key(key.Key), + }) + } + var hostHostKey drkey.FabridKey = drkey.FabridKey{} + if resp.HostHostKey != nil { + hostHostKey = drkey.FabridKey{ + Epoch: drkey.Epoch{ + Validity: cppki.Validity{ + NotBefore: resp.HostHostKey.EpochBegin.AsTime(), + NotAfter: resp.HostHostKey.EpochEnd.AsTime(), + }, + }, + AS: meta.DstAS, + Key: drkey.Key(resp.HostHostKey.Key), + } + } + return drkey.FabridKeysResponse{ + ASHostKeys: asHostKeys, + PathKey: hostHostKey, + }, nil +} diff --git a/pkg/daemon/helper/BUILD.bazel b/pkg/daemon/helper/BUILD.bazel new file mode 100644 index 0000000000..5d42ff3c5c --- /dev/null +++ b/pkg/daemon/helper/BUILD.bazel @@ -0,0 +1,16 @@ +load("//tools/lint:go.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["protobuf.go"], + importpath = "github.com/scionproto/scion/pkg/daemon/helper", + visibility = ["//visibility:public"], + deps = [ + "//pkg/drkey:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/proto/control_plane:go_default_library", + "//pkg/proto/drkey:go_default_library", + "//pkg/scrypto/cppki:go_default_library", + "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", + ], +) diff --git a/pkg/daemon/helper/protobuf.go b/pkg/daemon/helper/protobuf.go new file mode 100644 index 0000000000..76e8066c02 --- /dev/null +++ b/pkg/daemon/helper/protobuf.go @@ -0,0 +1,163 @@ +// Copyright 2022 ETH Zurich +// +// 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 protobuf + +import ( + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/private/serrors" + cppb "github.com/scionproto/scion/pkg/proto/control_plane" + drkeypb "github.com/scionproto/scion/pkg/proto/drkey" + "github.com/scionproto/scion/pkg/scrypto/cppki" +) + +func AsHostMetaToProtoRequest(meta drkey.ASHostMeta) *cppb.DRKeyASHostRequest { + return &cppb.DRKeyASHostRequest{ + ValTime: timestamppb.New(meta.Validity), + ProtocolId: drkeypb.Protocol(meta.ProtoId), + DstIa: uint64(meta.DstIA), + SrcIa: uint64(meta.SrcIA), + DstHost: meta.DstHost, + } +} + +func GetASHostKeyFromReply( + rep *cppb.DRKeyASHostResponse, + meta drkey.ASHostMeta, +) (drkey.ASHostKey, error) { + + err := rep.EpochBegin.CheckValid() + if err != nil { + return drkey.ASHostKey{}, serrors.WrapStr("invalid EpochBegin from response", err) + } + err = rep.EpochEnd.CheckValid() + if err != nil { + return drkey.ASHostKey{}, serrors.WrapStr("invalid EpochEnd from response", err) + } + epoch := drkey.Epoch{ + Validity: cppki.Validity{ + NotBefore: rep.EpochBegin.AsTime(), + NotAfter: rep.EpochEnd.AsTime(), + }, + } + + returningKey := drkey.ASHostKey{ + ProtoId: meta.ProtoId, + SrcIA: meta.SrcIA, + DstIA: meta.DstIA, + Epoch: epoch, + DstHost: meta.DstHost, + } + + if len(rep.Key) != 16 { + return drkey.ASHostKey{}, serrors.New("key size in reply is not 16 bytes", + "len", len(rep.Key)) + } + copy(returningKey.Key[:], rep.Key) + return returningKey, nil +} + +func HostASMetaToProtoRequest(meta drkey.HostASMeta) *cppb.DRKeyHostASRequest { + return &cppb.DRKeyHostASRequest{ + ValTime: timestamppb.New(meta.Validity), + ProtocolId: drkeypb.Protocol(meta.ProtoId), + DstIa: uint64(meta.DstIA), + SrcIa: uint64(meta.SrcIA), + SrcHost: meta.SrcHost, + } +} + +func GetHostASKeyFromReply( + rep *cppb.DRKeyHostASResponse, + meta drkey.HostASMeta, +) (drkey.HostASKey, error) { + + err := rep.EpochBegin.CheckValid() + if err != nil { + return drkey.HostASKey{}, serrors.WrapStr("invalid EpochBegin from response", err) + } + err = rep.EpochEnd.CheckValid() + if err != nil { + return drkey.HostASKey{}, serrors.WrapStr("invalid EpochEnd from response", err) + } + epoch := drkey.Epoch{ + Validity: cppki.Validity{ + NotBefore: rep.EpochBegin.AsTime(), + NotAfter: rep.EpochEnd.AsTime(), + }, + } + + returningKey := drkey.HostASKey{ + ProtoId: meta.ProtoId, + SrcIA: meta.SrcIA, + DstIA: meta.DstIA, + Epoch: epoch, + SrcHost: meta.SrcHost, + } + if len(rep.Key) != 16 { + return drkey.HostASKey{}, serrors.New("key size in reply is not 16 bytes", + "len", len(rep.Key)) + } + copy(returningKey.Key[:], rep.Key) + return returningKey, nil +} + +func HostHostMetaToProtoRequest(meta drkey.HostHostMeta) *cppb.DRKeyHostHostRequest { + return &cppb.DRKeyHostHostRequest{ + ValTime: timestamppb.New(meta.Validity), + ProtocolId: drkeypb.Protocol(meta.ProtoId), + DstIa: uint64(meta.DstIA), + SrcIa: uint64(meta.SrcIA), + DstHost: meta.DstHost, + SrcHost: meta.SrcHost, + } +} + +func GetHostHostKeyFromReply( + rep *cppb.DRKeyHostHostResponse, + meta drkey.HostHostMeta, +) (drkey.HostHostKey, error) { + + err := rep.EpochBegin.CheckValid() + if err != nil { + return drkey.HostHostKey{}, serrors.WrapStr("invalid EpochBegin from response", err) + } + err = rep.EpochEnd.CheckValid() + if err != nil { + return drkey.HostHostKey{}, serrors.WrapStr("invalid EpochEnd from response", err) + } + epoch := drkey.Epoch{ + Validity: cppki.Validity{ + NotBefore: rep.EpochBegin.AsTime(), + NotAfter: rep.EpochEnd.AsTime(), + }, + } + + returningKey := drkey.HostHostKey{ + ProtoId: meta.ProtoId, + SrcIA: meta.SrcIA, + DstIA: meta.DstIA, + Epoch: epoch, + SrcHost: meta.SrcHost, + DstHost: meta.DstHost, + } + if len(rep.Key) != 16 { + return drkey.HostHostKey{}, serrors.New("key size in reply is not 16 bytes", + "len", len(rep.Key)) + } + copy(returningKey.Key[:], rep.Key) + return returningKey, nil +} diff --git a/pkg/drkey/BUILD.bazel b/pkg/drkey/BUILD.bazel index 45d90b984d..1998b90997 100644 --- a/pkg/drkey/BUILD.bazel +++ b/pkg/drkey/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "db.go", "drkey.go", + "drkey_fabrid.go", "protocol.go", ], importpath = "github.com/scionproto/scion/pkg/drkey", diff --git a/pkg/drkey/drkey.go b/pkg/drkey/drkey.go index 23811a88fe..9e8bdfb3ab 100644 --- a/pkg/drkey/drkey.go +++ b/pkg/drkey/drkey.go @@ -33,6 +33,7 @@ import ( const ( Generic = Protocol(pb.Protocol_PROTOCOL_GENERIC_UNSPECIFIED) SCMP = Protocol(pb.Protocol_PROTOCOL_SCMP) + FABRID = Protocol(pb.Protocol_PROTOCOL_FABRID) ) // Epoch represents a validity period. diff --git a/pkg/drkey/drkey_fabrid.go b/pkg/drkey/drkey_fabrid.go new file mode 100644 index 0000000000..713504e323 --- /dev/null +++ b/pkg/drkey/drkey_fabrid.go @@ -0,0 +1,38 @@ +// Copyright 2024 ETH Zurich +// +// 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 drkey + +import "github.com/scionproto/scion/pkg/addr" + +type FabridKeysMeta struct { + SrcAS addr.IA + SrcHost string + // ASes on the path. Don't have to be in order + PathASes []addr.IA + // Field is optional. If DstHost is nil, no path-key will be fetched + DstHost *string + DstAS addr.IA +} + +type FabridKey struct { + Epoch Epoch + AS addr.IA + Key Key +} + +type FabridKeysResponse struct { + ASHostKeys []FabridKey + PathKey FabridKey +} diff --git a/pkg/experimental/fabrid/BUILD.bazel b/pkg/experimental/fabrid/BUILD.bazel new file mode 100644 index 0000000000..a279593881 --- /dev/null +++ b/pkg/experimental/fabrid/BUILD.bazel @@ -0,0 +1,8 @@ +load("//tools/lint:go.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["defs.go"], + importpath = "github.com/scionproto/scion/pkg/experimental/fabrid", + visibility = ["//visibility:public"], +) diff --git a/pkg/experimental/fabrid/crypto/BUILD.bazel b/pkg/experimental/fabrid/crypto/BUILD.bazel new file mode 100644 index 0000000000..626297da42 --- /dev/null +++ b/pkg/experimental/fabrid/crypto/BUILD.bazel @@ -0,0 +1,33 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["fabrid_crypto.go"], + importpath = "github.com/scionproto/scion/pkg/experimental/fabrid/crypto", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/drkey:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/slayers:go_default_library", + "//pkg/slayers/extension:go_default_library", + "//pkg/snet:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["fabrid_crypto_test.go"], + deps = [ + ":go_default_library", + "//pkg/addr:go_default_library", + "//pkg/drkey:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/private/common:go_default_library", + "//pkg/slayers:go_default_library", + "//pkg/slayers/extension:go_default_library", + "//pkg/snet:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + ], +) diff --git a/pkg/experimental/fabrid/crypto/fabrid_crypto.go b/pkg/experimental/fabrid/crypto/fabrid_crypto.go new file mode 100644 index 0000000000..b10200607f --- /dev/null +++ b/pkg/experimental/fabrid/crypto/fabrid_crypto.go @@ -0,0 +1,267 @@ +// Copyright 2024 ETH Zurich +// +// 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 crypto + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/binary" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" + ext "github.com/scionproto/scion/pkg/slayers/extension" + "github.com/scionproto/scion/pkg/snet" +) + +const FabridMacInputSize int = 46 + +// MAC input: +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Identifier (8B) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Cons Ingress (2B) | Cons Egress (2B) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |ePolicyID(1B)|sHostLen(1B)| SrcHostAddr (4-16 B) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +func computeFabridHVF(f *ext.FabridHopfieldMetadata, id *ext.IdentifierOption, + s *slayers.SCION, tmpBuffer []byte, resultBuffer []byte, + key []byte, ingress uint16, egress uint16) error { + if len(key) != 16 { + return serrors.New("Wrong key length", "expected", 16, "actual", len(key)) + } + if len(tmpBuffer) < FabridMacInputSize { + return serrors.New("tmpBuffer too small", "expected", + FabridMacInputSize, "actual", len(tmpBuffer)) + } + if len(resultBuffer) < 16 { + return serrors.New("resultBuffer too small", "expected", + 16, "actual", len(resultBuffer)) + } + + if err := id.Serialize(tmpBuffer[0:8]); err != nil { + return err + } + srcAddr := s.RawSrcAddr + requiredLen := 14 + len(srcAddr) + binary.BigEndian.PutUint16(tmpBuffer[8:10], ingress) + binary.BigEndian.PutUint16(tmpBuffer[10:12], egress) + tmpBuffer[12] = f.EncryptedPolicyID + tmpBuffer[13] = byte(s.SrcAddrType.Length()) + copy(tmpBuffer[14:requiredLen], srcAddr) + + if err := macBlock(key, tmpBuffer[30:46], tmpBuffer[:requiredLen], + resultBuffer[:]); err != nil { + return err + } + return nil +} + +// Computes and sets the base HVF for the provided FABRID Hopfield +func ComputeBaseHVF(f *ext.FabridHopfieldMetadata, id *ext.IdentifierOption, + s *slayers.SCION, tmpBuffer []byte, key []byte, ingress uint16, egress uint16) error { + computedHVF := make([]byte, 16) + err := computeFabridHVF(f, id, s, tmpBuffer, computedHVF, key, ingress, egress) + if err != nil { + return err + } + computedHVF[0] &= 0x3f // ignore first two (left) bits + copy(f.HopValidationField[:], computedHVF[0:3]) + return nil +} + +// Computes and sets the verified HVF for the provided FABRID Hopfield +func ComputeVerifiedHVF(f *ext.FabridHopfieldMetadata, id *ext.IdentifierOption, + s *slayers.SCION, tmpBuffer []byte, key []byte, ingress uint16, egress uint16) error { + computedHVF := make([]byte, 16) + err := computeFabridHVF(f, id, s, tmpBuffer, computedHVF, key, ingress, egress) + if err != nil { + return err + } + computedHVF[3] &= 0x3f // ignore first two (left) bits + copy(f.HopValidationField[:], computedHVF[3:6]) + return nil +} + +// VerifyAndUpdate recomputes the FABRID HVF and verifies that the provided HVF +// matches the base HVF. In that case it overwrites the provided HVF with the verified HVF. +func VerifyAndUpdate(f *ext.FabridHopfieldMetadata, id *ext.IdentifierOption, + s *slayers.SCION, tmpBuffer []byte, key []byte, ingress uint16, egress uint16) error { + computedHVF := make([]byte, 16) + err := computeFabridHVF(f, id, s, tmpBuffer, computedHVF, key, ingress, egress) + if err != nil { + return err + } + computedHVF[0] &= 0x3f // ignore first two (left) bits + if !bytes.Equal(computedHVF[:3], f.HopValidationField[:]) { + return serrors.New("HVF is not valid") + } + computedHVF[3] &= 0x3f // ignore first two (left) bits + copy(f.HopValidationField[:], computedHVF[3:6]) + return nil +} + +func calcPolicyEncryptionMask(key []byte, id *ext.IdentifierOption) ([]byte, error) { + cipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + buf := make([]byte, aes.BlockSize) + if err = id.Serialize(buf); err != nil { + return nil, err + } + cipher.Encrypt(buf, buf) + return buf, nil +} + +// ComputePolicyID acts as a decryptor for an encrypted FABRID policy index. +func ComputePolicyID(f *ext.FabridHopfieldMetadata, id *ext.IdentifierOption, + key []byte) (fabrid.PolicyID, error) { + + buf, err := calcPolicyEncryptionMask(key, id) + if err != nil { + return 0, err + } + return fabrid.PolicyID(f.EncryptedPolicyID ^ buf[0]), nil +} + +// EncryptPolicyID encrypts a FABRID policy index. +func EncryptPolicyID(f fabrid.PolicyID, id *ext.IdentifierOption, + key []byte) (uint8, error) { + + buf, err := calcPolicyEncryptionMask(key, id) + if err != nil { + return 0, err + } + return uint8(f) ^ buf[0], nil +} + +// VerifyPathValidator recomputes the path validator from the updated HVFs and compares it +// with the path validator in the packet. Returns the secret 5th byte of the computed validator. +// `tmpBuffer` requires at least (numHops*3 rounded up to next multiple of 16) + 16 bytes +func VerifyPathValidator(f *ext.FabridOption, tmpBuffer []byte, pathKey []byte) (uint8, error) { + inputLength := 3 * len(f.HopfieldMetadata) + requiredBufferLength := 16 + (inputLength+15)&^15 + if len(tmpBuffer) < requiredBufferLength { + return 0, serrors.New("tmpBuffer length is invalid", "expected", + requiredBufferLength, + "actual", len(tmpBuffer)) + } + for i, meta := range f.HopfieldMetadata { + copy(tmpBuffer[16+i*3:16+(i+1)*3], meta.HopValidationField[:3]) + } + err := macBlock(pathKey, tmpBuffer[:16], tmpBuffer[16:16+inputLength], tmpBuffer[16:]) + if err != nil { + return 0, err + } + validationNumber := tmpBuffer[20] + if !bytes.Equal(tmpBuffer[16:20], f.PathValidator[:]) { + return validationNumber, serrors.New("Path validator is not valid", + "validator", base64.StdEncoding.EncodeToString(f.PathValidator[:]), + "computed", base64.StdEncoding.EncodeToString(tmpBuffer[16:20])) + } + return validationNumber, nil +} + +// InitValidators sets all HVFs of the FABRID option and computes the +// path validator. +func InitValidators(f *ext.FabridOption, id *ext.IdentifierOption, s *slayers.SCION, + tmpBuffer []byte, pathKey *drkey.FabridKey, asHostKeys map[addr.IA]*drkey.FabridKey, + asAsKeys map[addr.IA]*drkey.FabridKey, hops []snet.HopInterface) error { + + outBuffer := make([]byte, 16) + var pathValInputLength int + var pathValBuffer []byte + if pathKey != nil { + pathValInputLength = 3 * len(f.HopfieldMetadata) + pathValBuffer = make([]byte, (pathValInputLength+15)&^15) + } + for i, meta := range f.HopfieldMetadata { + if meta.FabridEnabled { + var key *drkey.FabridKey + var found bool + if meta.ASLevelKey { + key, found = asAsKeys[hops[i].IA] + } else { + key, found = asHostKeys[hops[i].IA] + } + if !found { + return serrors.New("InitValidators expected AS to AS key but was not in"+ + " dictionary", "AS", hops[i].IA) + } + + err := computeFabridHVF(meta, id, s, tmpBuffer, outBuffer, key.Key[:], + uint16(hops[i].IgIf), uint16(hops[i].EgIf)) + if err != nil { + return err + } + outBuffer[0] &= 0x3f // ignore first two (left) bits + outBuffer[3] &= 0x3f // ignore first two (left) bits + copy(meta.HopValidationField[:3], outBuffer[:3]) + if pathKey != nil { + copy(pathValBuffer[i*3:(i+1)*3], outBuffer[3:6]) + } + } + } + if pathKey != nil { + err := macBlock(pathKey.Key[:], tmpBuffer[:16], pathValBuffer[:pathValInputLength], + pathValBuffer) + if err != nil { + return err + } + copy(f.PathValidator[:4], pathValBuffer[:4]) + } + return nil +} + +var zeroBlock [16]byte + +func macBlock(key []byte, tmp []byte, src []byte, dst []byte) error { + block, err := aes.NewCipher(key) + if err != nil { + return serrors.WrapStr("unable to initialize AES cipher", err) + } + if len(dst) < 16 { + return serrors.New("Dst length is invalid", "expected", 16, "actual", len(dst)) + } + if len(src) == 0 { + return serrors.New("Src length cannot be 0") + } + if len(tmp) < 16 { + return serrors.New("tmp length is invalid", "expected", 16, "actual", len(tmp)) + } + encryptor := cipher.NewCBCEncrypter(block, zeroBlock[:]) + paddingLength := (16 - len(src)%16) % 16 + blockCount := len(src) / block.BlockSize() + + if blockCount != 0 { + encryptor.CryptBlocks(dst, src[:16*blockCount]) + } + if paddingLength != 0 { + copy(tmp, src[16*blockCount:]) + copy(tmp[16-paddingLength:], zeroBlock[:paddingLength]) + encryptor.CryptBlocks(dst, tmp) + } + return nil +} diff --git a/pkg/experimental/fabrid/crypto/fabrid_crypto_test.go b/pkg/experimental/fabrid/crypto/fabrid_crypto_test.go new file mode 100644 index 0000000000..a1c4732892 --- /dev/null +++ b/pkg/experimental/fabrid/crypto/fabrid_crypto_test.go @@ -0,0 +1,280 @@ +// Copyright 2024 ETH Zurich +// +// 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 crypto_test + +import ( + crand "crypto/rand" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/experimental/fabrid/crypto" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" + "github.com/scionproto/scion/pkg/snet" +) + +func TestEncryptPolicyID(t *testing.T) { + unixNow := uint32(time.Now().Unix()) + + id := &extension.IdentifierOption{ + Timestamp: time.Unix(int64(unixNow), 10*int64(time.Millisecond)), + PacketID: 0xaa, + BaseTimestamp: unixNow, + } + // test with 64 different randomly chosen keys and policyIDs + for i := 0; i < 64; i++ { + policyID := fabrid.PolicyID(rand.Uint32()) + key := generateRandomBytes(16) + encPolicyID, err := crypto.EncryptPolicyID(policyID, id, key) + assert.NoError(t, err) + meta := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + HopValidationField: [3]byte{}, + } + computedPolicyID, err := crypto.ComputePolicyID(meta, id, key) + assert.NoError(t, err) + assert.Equal(t, policyID, computedPolicyID) + } +} + +func generateRandomBytes(len int) []byte { + b := make([]byte, len) + _, _ = crand.Read(b) + return b +} + +func TestFailedValidation(t *testing.T) { + type test struct { + name string + runTest func(t *testing.T) + } + tests := []test{ + { + name: "manipulated hopfield leads to failed path validator", + runTest: func(t *testing.T) { + unixNow := uint32(time.Now().Unix()) + tmpBuffer := make([]byte, (extension.MaxSupportedFabridHops*3+15)&^15+16) + id := &extension.IdentifierOption{ + Timestamp: time.Unix(int64(unixNow), 10*int64(time.Millisecond)), + PacketID: rand.Uint32(), + BaseTimestamp: unixNow, + } + s := &slayers.SCION{ + RawSrcAddr: generateRandomBytes(4), + SrcIA: addr.MustIAFrom(1, 1), + } + f := &extension.FabridOption{} + f.HopfieldMetadata = append(f.HopfieldMetadata, + &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: uint8(rand.Uint32()), + FabridEnabled: true, + ASLevelKey: rand.Intn(2) == 0, + }) + pathKey := &drkey.FabridKey{ + Key: drkey.Key{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + } + asHostKeys := make(map[addr.IA]*drkey.FabridKey) + asAsKeys := make(map[addr.IA]*drkey.FabridKey) + hops := make([]snet.HopInterface, len(f.HopfieldMetadata)) + + for i := 0; i < len(f.HopfieldMetadata); i++ { + hops[i] = snet.HopInterface{ + IgIf: common.IFIDType(rand.Int()), + EgIf: common.IFIDType(rand.Int()), + Policies: nil, + } + if i == 0 { + hops[i].IA = addr.MustIAFrom(1, 1) + } else { + hops[i].IA = addr.IA(rand.Int()) + } + keyBytes := drkey.Key{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + if f.HopfieldMetadata[i].ASLevelKey { + asAsKeys[hops[i].IA] = &drkey.FabridKey{Key: keyBytes} + } else { + asHostKeys[hops[i].IA] = &drkey.FabridKey{Key: keyBytes} + } + } + + err := crypto.InitValidators(f, id, s, tmpBuffer, pathKey, asHostKeys, + asAsKeys, hops) + assert.NoError(t, err) + + for i, meta := range f.HopfieldMetadata { + if meta.FabridEnabled { + if meta.ASLevelKey { + key := asAsKeys[hops[i].IA] + err = crypto.VerifyAndUpdate(meta, id, s, tmpBuffer, key.Key[:], + uint16(hops[i].IgIf), uint16(hops[i].EgIf)) + } else { + key := asHostKeys[hops[i].IA] + err = crypto.VerifyAndUpdate(meta, id, s, tmpBuffer, key.Key[:], + uint16(hops[i].IgIf), uint16(hops[i].EgIf), + ) + } + + assert.NoError(t, err) + } + } + _, err = crypto.VerifyPathValidator(f, tmpBuffer, pathKey.Key[:]) + assert.NoError(t, err) + // until now we are in the success case. But now we modify a HVF to simulate + // adversarial actions and make sure that the path validator fails + f.HopfieldMetadata[0].HopValidationField = [3]byte{0, 0, 0} + _, err = crypto.VerifyPathValidator(f, tmpBuffer, pathKey.Key[:]) + assert.ErrorContains(t, err, "Path validator is not valid") + }, + }, + { + name: "verify hopfield fails for wrong value", + runTest: func(t *testing.T) { + f := &extension.FabridOption{ + HopfieldMetadata: []*extension.FabridHopfieldMetadata{ + { + FabridEnabled: true, + HopValidationField: [3]byte{1, 2, 3}, + }, + }, + } + id := &extension.IdentifierOption{} + s := &slayers.SCION{} + tmpBuffer := make([]byte, 128) + hfKey := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + err := crypto.VerifyAndUpdate(f.HopfieldMetadata[0], id, s, tmpBuffer, hfKey, 1, 2) + assert.ErrorContains(t, err, "HVF is not valid") + }, + }, + } + for _, tc := range tests { + func(tc test) { + t.Run(tc.name, tc.runTest) + }(tc) + } +} + +func TestSuccessfullValidators(t *testing.T) { + type test struct { + name string + rawSrcAddr []byte + srcIA addr.IA + } + unixNow := uint32(time.Now().Unix()) + + tmpBuffer := make([]byte, (extension.MaxSupportedFabridHops*3+15)&^15+16) + tests := []test{ + { + name: "random 16 byte src addr", + rawSrcAddr: generateRandomBytes(16), + srcIA: addr.IA(rand.Uint64()), + }, + { + name: "random 12 byte src addr", + rawSrcAddr: generateRandomBytes(12), + srcIA: addr.IA(rand.Uint64()), + }, + { + name: "random 8 byte src addr", + rawSrcAddr: generateRandomBytes(8), + srcIA: addr.IA(rand.Uint64()), + }, + { + name: "random 4 byte src addr", + rawSrcAddr: generateRandomBytes(4), + srcIA: addr.IA(rand.Uint64()), + }, + } + + for _, tc := range tests { + func(tc test) { + t.Run(tc.name, func(t *testing.T) { + id := &extension.IdentifierOption{ + Timestamp: time.Unix(int64(unixNow), 10*int64(time.Millisecond)), + PacketID: rand.Uint32(), + BaseTimestamp: unixNow, + } + s := &slayers.SCION{ + RawSrcAddr: tc.rawSrcAddr, + SrcIA: tc.srcIA, + } + f := &extension.FabridOption{} + for j := uint8(1); j <= extension.MaxSupportedFabridHops; j++ { + f.HopfieldMetadata = append(f.HopfieldMetadata, + &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: uint8(rand.Uint32()), + FabridEnabled: rand.Intn(2) == 0, + ASLevelKey: rand.Intn(2) == 0, + }) + keyBytes := drkey.Key{} + copy(keyBytes[:], generateRandomBytes(16)) + pathKey := &drkey.FabridKey{Key: keyBytes} + asHostKeys := make(map[addr.IA]*drkey.FabridKey) + asAsKeys := make(map[addr.IA]*drkey.FabridKey) + hops := make([]snet.HopInterface, len(f.HopfieldMetadata)) + + for i := 0; i < len(f.HopfieldMetadata); i++ { + hops[i] = snet.HopInterface{ + IgIf: common.IFIDType(rand.Int()), + EgIf: common.IFIDType(rand.Int()), + Policies: nil, + } + if i == 0 { + hops[i].IA = tc.srcIA + } else { + hops[i].IA = addr.IA(rand.Int()) + } + keyBytes = drkey.Key{} + copy(keyBytes[:], generateRandomBytes(16)) + if f.HopfieldMetadata[i].ASLevelKey { + asAsKeys[hops[i].IA] = &drkey.FabridKey{Key: keyBytes} + } else { + asHostKeys[hops[i].IA] = &drkey.FabridKey{Key: keyBytes} + } + } + + err := crypto.InitValidators(f, id, s, tmpBuffer, pathKey, asHostKeys, + asAsKeys, hops) + assert.NoError(t, err) + + for i, meta := range f.HopfieldMetadata { + if meta.FabridEnabled { + + if meta.ASLevelKey { + key := asAsKeys[hops[i].IA] + err = crypto.VerifyAndUpdate(meta, id, s, tmpBuffer, key.Key[:], + uint16(hops[i].IgIf), uint16(hops[i].EgIf)) + } else { + key := asHostKeys[hops[i].IA] + err = crypto.VerifyAndUpdate(meta, id, s, tmpBuffer, key.Key[:], + uint16(hops[i].IgIf), uint16(hops[i].EgIf), + ) + } + + assert.NoError(t, err) + } + } + _, err = crypto.VerifyPathValidator(f, tmpBuffer, pathKey.Key[:]) + assert.NoError(t, err) + } + }) + }(tc) + } +} diff --git a/pkg/experimental/fabrid/defs.go b/pkg/experimental/fabrid/defs.go new file mode 100644 index 0000000000..50c1be0b47 --- /dev/null +++ b/pkg/experimental/fabrid/defs.go @@ -0,0 +1,33 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import "fmt" + +type PolicyID uint8 + +type Policy struct { + IsLocal bool + Identifier uint32 + Index PolicyID +} + +func (fpi *Policy) String() string { + if fpi.IsLocal { + return fmt.Sprintf("L%d", fpi.Identifier) + } else { + return fmt.Sprintf("G%d", fpi.Identifier) + } +} diff --git a/pkg/experimental/fabrid/graphutils/BUILD.bazel b/pkg/experimental/fabrid/graphutils/BUILD.bazel new file mode 100644 index 0000000000..dd0add83ca --- /dev/null +++ b/pkg/experimental/fabrid/graphutils/BUILD.bazel @@ -0,0 +1,14 @@ +load("//tools/lint:go.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["maps.go"], + importpath = "github.com/scionproto/scion/pkg/experimental/fabrid/graphutils", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", + "//pkg/snet:go_default_library", + ], +) diff --git a/pkg/experimental/fabrid/graphutils/maps.go b/pkg/experimental/fabrid/graphutils/maps.go new file mode 100644 index 0000000000..131f251e9f --- /dev/null +++ b/pkg/experimental/fabrid/graphutils/maps.go @@ -0,0 +1,101 @@ +// Copyright 2024 ETH Zurich +// +// 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 graphutils + +import ( + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/experimental/fabrid" + fabrid_ext "github.com/scionproto/scion/pkg/segment/extensions/fabrid" + "github.com/scionproto/scion/pkg/snet" +) + +// We go through the list of ASEntries and store for each IA a pointer to the FABRID +// Map found in the ASEntries' extensions. If there is already a map stored, check the info time, +// and replace with the newer FABRID maps. This results in a map[IA]FabridMapEntry, which can be +// used to find the policies that are available for each of the interface pairs on the path. +type FabridMapEntry struct { + Map *fabrid_ext.Detached + Ts time.Time + // The Digest of the Fabrid Maps, this can be empty. + Digest []byte +} + +func CollectFabridPolicies(ifaces []snet.PathInterface, + maps map[addr.IA]FabridMapEntry) []snet.FabridInfo { + + switch { + case len(ifaces)%2 != 0: + return []snet.FabridInfo{} + case len(ifaces) == 0: + return []snet.FabridInfo{} + default: + fabridInfo := make([]snet.FabridInfo, len(ifaces)/2+1) + fabridInfo[0] = *GetFabridInfoForIntfs(ifaces[0].IA, 0, uint16(ifaces[0].ID), maps, false) + for i := 1; i < len(ifaces)-1; i += 2 { + fabridInfo[(i+1)/2] = *GetFabridInfoForIntfs(ifaces[i].IA, uint16(ifaces[i].ID), + uint16(ifaces[i+1].ID), maps, false) + } + fabridInfo[len(ifaces)/2] = *GetFabridInfoForIntfs(ifaces[len(ifaces)-1].IA, + uint16(ifaces[len(ifaces)-1].ID), 0, maps, true) + return fabridInfo + } +} + +func GetFabridInfoForIntfs(ia addr.IA, ig, eg uint16, maps map[addr.IA]FabridMapEntry, + allowIpPolicies bool) *snet.FabridInfo { + policies := make([]*fabrid.Policy, 0) + fabridMap, exist := maps[ia] + if !exist { + return &snet.FabridInfo{ + Enabled: false, + Policies: policies, + Digest: []byte{}, + Detached: false, + } + } else if fabridMap.Map == nil { + return &snet.FabridInfo{ + Enabled: true, + Policies: policies, + Digest: fabridMap.Digest, + Detached: len(fabridMap.Digest) > 0, + } + } + for k, v := range fabridMap.Map.SupportedIndicesMap { + if !k.Matches(ig, eg, allowIpPolicies) { + continue + } + for _, policy := range v { + val, ok := fabridMap.Map.IndexIdentiferMap[policy] + if !ok { + continue + } + policies = append(policies, &fabrid.Policy{ + IsLocal: val.IsLocal, + Identifier: val.Identifier, + Index: fabrid.PolicyID(policy), + }) + + } + } + + return &snet.FabridInfo{ + Enabled: true, + Policies: policies, + Digest: fabridMap.Digest, + Detached: false, + } +} diff --git a/pkg/grpc/BUILD.bazel b/pkg/grpc/BUILD.bazel index e82853d5ff..2f2a4d3d06 100644 --- a/pkg/grpc/BUILD.bazel +++ b/pkg/grpc/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "creds.go", "dialer.go", + "dialer_drkey.go", "interceptor.go", ], importpath = "github.com/scionproto/scion/pkg/grpc", diff --git a/pkg/grpc/dialer_drkey.go b/pkg/grpc/dialer_drkey.go new file mode 100644 index 0000000000..ac69a3c5bb --- /dev/null +++ b/pkg/grpc/dialer_drkey.go @@ -0,0 +1,69 @@ +// Copyright 2024 ETH Zurich +// +// 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 grpc + +import ( + "context" + "net" + + "google.golang.org/grpc" + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/resolver/manual" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/snet" +) + +// The FixedLocalIPTCPDialer behaves the same as the TCP Dialer but allows for setting a local +// IP address. Without this it would not be possible to call IP sensitive endpoints like +// control service drkey. +type FixedLocalIPTCPDialer struct { + LocalAddr *net.TCPAddr + SvcResolver func(addr.SVC) []resolver.Address +} + +func (d *FixedLocalIPTCPDialer) Dial(ctx context.Context, dst net.Addr) (*grpc.ClientConn, error) { + dialer := func(ctx context.Context, addr string) (net.Conn, error) { + csAddr, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + return nil, err + } + return net.DialTCP("tcp", d.LocalAddr, csAddr) + } + if v, ok := dst.(*snet.SVCAddr); ok { + targets := d.SvcResolver(v.SVC) + r := manual.NewBuilderWithScheme("svc") + r.InitialState(resolver.State{Addresses: targets}) + if len(targets) == 0 { + return nil, serrors.New("could not resolve") + } + return grpc.DialContext(ctx, r.Scheme()+":///"+v.SVC.BaseString(), + grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"round_robin":{}}]}`), + grpc.WithInsecure(), + grpc.WithContextDialer(dialer), + grpc.WithResolvers(r), + UnaryClientInterceptor(), + StreamClientInterceptor(), + ) + } else { + return grpc.DialContext(ctx, dst.String(), + grpc.WithInsecure(), + grpc.WithContextDialer(dialer), + UnaryClientInterceptor(), + StreamClientInterceptor(), + ) + } +} diff --git a/pkg/private/xtest/graph/BUILD.bazel b/pkg/private/xtest/graph/BUILD.bazel index d5907cf9e5..b5031b2114 100644 --- a/pkg/private/xtest/graph/BUILD.bazel +++ b/pkg/private/xtest/graph/BUILD.bazel @@ -5,11 +5,13 @@ go_library( srcs = [ "default_gen.go", "graph.go", + "graph_fabrid.go", ], importpath = "github.com/scionproto/scion/pkg/private/xtest/graph", visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", + "//pkg/experimental/fabrid:go_default_library", "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/proto/control_plane:go_default_library", diff --git a/pkg/private/xtest/graph/graph.go b/pkg/private/xtest/graph/graph.go index 1672cbf684..bc34543665 100644 --- a/pkg/private/xtest/graph/graph.go +++ b/pkg/private/xtest/graph/graph.go @@ -654,7 +654,8 @@ func generateStaticInfo(g *Graph, ia addr.IA, inIF, outIF uint16) *staticinfo.Ex carbonIntensity.Intra[common.IFIDType(ifid)] = g.CarbonIntensity(ifid, outIF) } if ifid == outIF || g.isPeer[ifid] { - carbonIntensity.Inter[common.IFIDType(ifid)] = g.CarbonIntensity(ifid, g.links[ifid]) + carbonIntensity.Inter[common.IFIDType(ifid)] = + g.CarbonIntensity(ifid, g.links[ifid]) } } } diff --git a/pkg/private/xtest/graph/graph_fabrid.go b/pkg/private/xtest/graph/graph_fabrid.go new file mode 100644 index 0000000000..04683abddb --- /dev/null +++ b/pkg/private/xtest/graph/graph_fabrid.go @@ -0,0 +1,34 @@ +// Copyright 2024 ETH Zurich +// +// 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 graph + +import "github.com/scionproto/scion/pkg/experimental/fabrid" + +// FabridPolicy returns an arbitrary set of policies between between two interfaces of an AS. +func (g *Graph) FabridPolicy(a, b uint16) []*fabrid.Policy { + if g.parents[a] != g.parents[b] && a != 0 && b != 0 { + panic("interfaces must be in the same AS") + } + amtOfPols := int(a*b%10 + 3) + policies := make([]*fabrid.Policy, amtOfPols) + for i := 0; i < amtOfPols; i++ { + policies[i] = &fabrid.Policy{ + IsLocal: false, + Identifier: (uint32(a)*uint32(b)*uint32(i)*39 + uint32(i) + 1) % 20000, + Index: fabrid.PolicyID(a * b), + } + } + return policies +} diff --git a/pkg/proto/control_plane/experimental/fabrid.pb.go b/pkg/proto/control_plane/experimental/fabrid.pb.go new file mode 100755 index 0000000000..8b2f8f1195 --- /dev/null +++ b/pkg/proto/control_plane/experimental/fabrid.pb.go @@ -0,0 +1,1627 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.21.10 +// source: proto/control_plane/experimental/v1/fabrid.proto + +package experimental + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type MPLSIPArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Entry []*MPLSIP `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` +} + +func (x *MPLSIPArray) Reset() { + *x = MPLSIPArray{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MPLSIPArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MPLSIPArray) ProtoMessage() {} + +func (x *MPLSIPArray) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MPLSIPArray.ProtoReflect.Descriptor instead. +func (*MPLSIPArray) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{0} +} + +func (x *MPLSIPArray) GetEntry() []*MPLSIP { + if x != nil { + return x.Entry + } + return nil +} + +type MPLSIP struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MplsLabel uint32 `protobuf:"varint,1,opt,name=mpls_label,json=mplsLabel,proto3" json:"mpls_label,omitempty"` + Ip []byte `protobuf:"bytes,2,opt,name=ip,proto3" json:"ip,omitempty"` + Prefix uint32 `protobuf:"varint,3,opt,name=prefix,proto3" json:"prefix,omitempty"` +} + +func (x *MPLSIP) Reset() { + *x = MPLSIP{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MPLSIP) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MPLSIP) ProtoMessage() {} + +func (x *MPLSIP) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MPLSIP.ProtoReflect.Descriptor instead. +func (*MPLSIP) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{1} +} + +func (x *MPLSIP) GetMplsLabel() uint32 { + if x != nil { + return x.MplsLabel + } + return 0 +} + +func (x *MPLSIP) GetIp() []byte { + if x != nil { + return x.Ip + } + return nil +} + +func (x *MPLSIP) GetPrefix() uint32 { + if x != nil { + return x.Prefix + } + return 0 +} + +type MPLSMapRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (x *MPLSMapRequest) Reset() { + *x = MPLSMapRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MPLSMapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MPLSMapRequest) ProtoMessage() {} + +func (x *MPLSMapRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MPLSMapRequest.ProtoReflect.Descriptor instead. +func (*MPLSMapRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{2} +} + +func (x *MPLSMapRequest) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +type MPLSMapResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Update bool `protobuf:"varint,1,opt,name=update,proto3" json:"update,omitempty"` + Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` + MplsInterfacePoliciesMap map[uint64]uint32 `protobuf:"bytes,3,rep,name=mpls_interface_policies_map,json=mplsInterfacePoliciesMap,proto3" json:"mpls_interface_policies_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + MplsIpMap map[uint32]*MPLSIPArray `protobuf:"bytes,4,rep,name=mpls_ip_map,json=mplsIpMap,proto3" json:"mpls_ip_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *MPLSMapResponse) Reset() { + *x = MPLSMapResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MPLSMapResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MPLSMapResponse) ProtoMessage() {} + +func (x *MPLSMapResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MPLSMapResponse.ProtoReflect.Descriptor instead. +func (*MPLSMapResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{3} +} + +func (x *MPLSMapResponse) GetUpdate() bool { + if x != nil { + return x.Update + } + return false +} + +func (x *MPLSMapResponse) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *MPLSMapResponse) GetMplsInterfacePoliciesMap() map[uint64]uint32 { + if x != nil { + return x.MplsInterfacePoliciesMap + } + return nil +} + +func (x *MPLSMapResponse) GetMplsIpMap() map[uint32]*MPLSIPArray { + if x != nil { + return x.MplsIpMap + } + return nil +} + +type SupportedIndicesMapRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SupportedIndicesMapRequest) Reset() { + *x = SupportedIndicesMapRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SupportedIndicesMapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SupportedIndicesMapRequest) ProtoMessage() {} + +func (x *SupportedIndicesMapRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SupportedIndicesMapRequest.ProtoReflect.Descriptor instead. +func (*SupportedIndicesMapRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{4} +} + +type SupportedIndicesMapResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SupportedIndicesMap []*FABRIDIndexMapEntry `protobuf:"bytes,1,rep,name=supported_indices_map,json=supportedIndicesMap,proto3" json:"supported_indices_map,omitempty"` +} + +func (x *SupportedIndicesMapResponse) Reset() { + *x = SupportedIndicesMapResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SupportedIndicesMapResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SupportedIndicesMapResponse) ProtoMessage() {} + +func (x *SupportedIndicesMapResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SupportedIndicesMapResponse.ProtoReflect.Descriptor instead. +func (*SupportedIndicesMapResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{5} +} + +func (x *SupportedIndicesMapResponse) GetSupportedIndicesMap() []*FABRIDIndexMapEntry { + if x != nil { + return x.SupportedIndicesMap + } + return nil +} + +type IndexIdentifierMapRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *IndexIdentifierMapRequest) Reset() { + *x = IndexIdentifierMapRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IndexIdentifierMapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IndexIdentifierMapRequest) ProtoMessage() {} + +func (x *IndexIdentifierMapRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IndexIdentifierMapRequest.ProtoReflect.Descriptor instead. +func (*IndexIdentifierMapRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{6} +} + +type IndexIdentifierMapResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IndexIdentifierMap map[uint32]*FABRIDPolicyIdentifier `protobuf:"bytes,1,rep,name=index_identifier_map,json=indexIdentifierMap,proto3" json:"index_identifier_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *IndexIdentifierMapResponse) Reset() { + *x = IndexIdentifierMapResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IndexIdentifierMapResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IndexIdentifierMapResponse) ProtoMessage() {} + +func (x *IndexIdentifierMapResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IndexIdentifierMapResponse.ProtoReflect.Descriptor instead. +func (*IndexIdentifierMapResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{7} +} + +func (x *IndexIdentifierMapResponse) GetIndexIdentifierMap() map[uint32]*FABRIDPolicyIdentifier { + if x != nil { + return x.IndexIdentifierMap + } + return nil +} + +type RemotePolicyDescriptionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PolicyIdentifier uint32 `protobuf:"varint,1,opt,name=policy_identifier,json=policyIdentifier,proto3" json:"policy_identifier,omitempty"` + IsdAs uint64 `protobuf:"varint,2,opt,name=isd_as,json=isdAs,proto3" json:"isd_as,omitempty"` +} + +func (x *RemotePolicyDescriptionRequest) Reset() { + *x = RemotePolicyDescriptionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemotePolicyDescriptionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemotePolicyDescriptionRequest) ProtoMessage() {} + +func (x *RemotePolicyDescriptionRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemotePolicyDescriptionRequest.ProtoReflect.Descriptor instead. +func (*RemotePolicyDescriptionRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{8} +} + +func (x *RemotePolicyDescriptionRequest) GetPolicyIdentifier() uint32 { + if x != nil { + return x.PolicyIdentifier + } + return 0 +} + +func (x *RemotePolicyDescriptionRequest) GetIsdAs() uint64 { + if x != nil { + return x.IsdAs + } + return 0 +} + +type RemotePolicyDescriptionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *RemotePolicyDescriptionResponse) Reset() { + *x = RemotePolicyDescriptionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemotePolicyDescriptionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemotePolicyDescriptionResponse) ProtoMessage() {} + +func (x *RemotePolicyDescriptionResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemotePolicyDescriptionResponse.ProtoReflect.Descriptor instead. +func (*RemotePolicyDescriptionResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{9} +} + +func (x *RemotePolicyDescriptionResponse) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +type LocalPolicyDescriptionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PolicyIdentifier uint32 `protobuf:"varint,1,opt,name=policy_identifier,json=policyIdentifier,proto3" json:"policy_identifier,omitempty"` +} + +func (x *LocalPolicyDescriptionRequest) Reset() { + *x = LocalPolicyDescriptionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LocalPolicyDescriptionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LocalPolicyDescriptionRequest) ProtoMessage() {} + +func (x *LocalPolicyDescriptionRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LocalPolicyDescriptionRequest.ProtoReflect.Descriptor instead. +func (*LocalPolicyDescriptionRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{10} +} + +func (x *LocalPolicyDescriptionRequest) GetPolicyIdentifier() uint32 { + if x != nil { + return x.PolicyIdentifier + } + return 0 +} + +type LocalPolicyDescriptionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *LocalPolicyDescriptionResponse) Reset() { + *x = LocalPolicyDescriptionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LocalPolicyDescriptionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LocalPolicyDescriptionResponse) ProtoMessage() {} + +func (x *LocalPolicyDescriptionResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LocalPolicyDescriptionResponse.ProtoReflect.Descriptor instead. +func (*LocalPolicyDescriptionResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{11} +} + +func (x *LocalPolicyDescriptionResponse) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +type RemoteMapsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` + IsdAs uint64 `protobuf:"varint,2,opt,name=isd_as,json=isdAs,proto3" json:"isd_as,omitempty"` +} + +func (x *RemoteMapsRequest) Reset() { + *x = RemoteMapsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoteMapsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteMapsRequest) ProtoMessage() {} + +func (x *RemoteMapsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoteMapsRequest.ProtoReflect.Descriptor instead. +func (*RemoteMapsRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{12} +} + +func (x *RemoteMapsRequest) GetDigest() []byte { + if x != nil { + return x.Digest + } + return nil +} + +func (x *RemoteMapsRequest) GetIsdAs() uint64 { + if x != nil { + return x.IsdAs + } + return 0 +} + +type RemoteMapsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Maps *FABRIDDetachableMaps `protobuf:"bytes,1,opt,name=maps,proto3" json:"maps,omitempty"` +} + +func (x *RemoteMapsResponse) Reset() { + *x = RemoteMapsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoteMapsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteMapsResponse) ProtoMessage() {} + +func (x *RemoteMapsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoteMapsResponse.ProtoReflect.Descriptor instead. +func (*RemoteMapsResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{13} +} + +func (x *RemoteMapsResponse) GetMaps() *FABRIDDetachableMaps { + if x != nil { + return x.Maps + } + return nil +} + +type DetachedMapsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DetachedMapsRequest) Reset() { + *x = DetachedMapsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetachedMapsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetachedMapsRequest) ProtoMessage() {} + +func (x *DetachedMapsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetachedMapsRequest.ProtoReflect.Descriptor instead. +func (*DetachedMapsRequest) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{14} +} + +type DetachedMapsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Maps *FABRIDDetachableMaps `protobuf:"bytes,1,opt,name=maps,proto3" json:"maps,omitempty"` +} + +func (x *DetachedMapsResponse) Reset() { + *x = DetachedMapsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetachedMapsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetachedMapsResponse) ProtoMessage() {} + +func (x *DetachedMapsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetachedMapsResponse.ProtoReflect.Descriptor instead. +func (*DetachedMapsResponse) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP(), []int{15} +} + +func (x *DetachedMapsResponse) GetMaps() *FABRIDDetachableMaps { + if x != nil { + return x.Maps + } + return nil +} + +var File_proto_control_plane_experimental_v1_fabrid_proto protoreflect.FileDescriptor + +var file_proto_control_plane_experimental_v1_fabrid_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x23, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x1a, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x62, + 0x72, 0x69, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x0b, 0x4d, 0x50, 0x4c, 0x53, 0x49, 0x50, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x50, 0x4c, 0x53, 0x49, 0x50, 0x52, + 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x4f, 0x0a, 0x06, 0x4d, 0x50, 0x4c, 0x53, 0x49, 0x50, + 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x70, 0x6c, 0x73, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, + 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x24, 0x0a, 0x0e, 0x4d, 0x50, 0x4c, 0x53, 0x4d, + 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0xf3, 0x03, + 0x0a, 0x0f, 0x4d, 0x50, 0x4c, 0x53, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x91, 0x01, + 0x0a, 0x1b, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x52, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, + 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x50, 0x4c, 0x53, 0x4d, 0x61, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x70, 0x6c, 0x73, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x6d, 0x70, 0x6c, 0x73, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x61, + 0x70, 0x12, 0x63, 0x0a, 0x0b, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x69, 0x70, 0x5f, 0x6d, 0x61, 0x70, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x50, 0x4c, + 0x53, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x70, 0x6c, + 0x73, 0x49, 0x70, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6d, 0x70, 0x6c, + 0x73, 0x49, 0x70, 0x4d, 0x61, 0x70, 0x1a, 0x4b, 0x0a, 0x1d, 0x4d, 0x70, 0x6c, 0x73, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x6e, 0x0a, 0x0e, 0x4d, 0x70, 0x6c, 0x73, 0x49, 0x70, 0x4d, 0x61, 0x70, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x50, 0x4c, + 0x53, 0x49, 0x50, 0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x1b, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6c, 0x0a, 0x15, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x38, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x22, + 0x1b, 0x0a, 0x19, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xad, 0x02, 0x0a, + 0x1a, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x14, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x57, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x12, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x1a, 0x82, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x51, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, + 0x44, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x64, 0x0a, 0x1e, + 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, + 0x0a, 0x11, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x69, + 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x73, 0x64, + 0x41, 0x73, 0x22, 0x43, 0x0a, 0x1f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4c, 0x0a, 0x1d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x10, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x42, 0x0a, 0x1e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x42, 0x0a, 0x11, 0x52, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x73, 0x64, 0x41, 0x73, 0x22, 0x63, 0x0a, + 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x44, 0x65, + 0x74, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x04, 0x6d, 0x61, + 0x70, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x4d, 0x61, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x65, 0x0a, 0x14, 0x44, 0x65, 0x74, + 0x61, 0x63, 0x68, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x44, 0x65, 0x74, 0x61, + 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x04, 0x6d, 0x61, 0x70, 0x73, + 0x32, 0xf9, 0x04, 0x0a, 0x12, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x9a, 0x01, 0x0a, 0x13, 0x53, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x12, + 0x3f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x40, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x3e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x85, + 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x73, 0x12, + 0x38, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x4d, 0x61, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa3, 0x01, 0x0a, 0x16, 0x4c, 0x6f, 0x63, 0x61, 0x6c, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x42, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, + 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xb6, 0x03, 0x0a, + 0x12, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0xa6, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x43, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x44, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7f, 0x0a, 0x0a, + 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x12, 0x36, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, + 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x76, 0x0a, + 0x07, 0x4d, 0x50, 0x4c, 0x53, 0x4d, 0x61, 0x70, 0x12, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x50, 0x4c, 0x53, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x50, 0x4c, 0x53, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, + 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_proto_control_plane_experimental_v1_fabrid_proto_rawDescOnce sync.Once + file_proto_control_plane_experimental_v1_fabrid_proto_rawDescData = file_proto_control_plane_experimental_v1_fabrid_proto_rawDesc +) + +func file_proto_control_plane_experimental_v1_fabrid_proto_rawDescGZIP() []byte { + file_proto_control_plane_experimental_v1_fabrid_proto_rawDescOnce.Do(func() { + file_proto_control_plane_experimental_v1_fabrid_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_control_plane_experimental_v1_fabrid_proto_rawDescData) + }) + return file_proto_control_plane_experimental_v1_fabrid_proto_rawDescData +} + +var file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_proto_control_plane_experimental_v1_fabrid_proto_goTypes = []interface{}{ + (*MPLSIPArray)(nil), // 0: proto.control_plane.experimental.v1.MPLSIPArray + (*MPLSIP)(nil), // 1: proto.control_plane.experimental.v1.MPLSIP + (*MPLSMapRequest)(nil), // 2: proto.control_plane.experimental.v1.MPLSMapRequest + (*MPLSMapResponse)(nil), // 3: proto.control_plane.experimental.v1.MPLSMapResponse + (*SupportedIndicesMapRequest)(nil), // 4: proto.control_plane.experimental.v1.SupportedIndicesMapRequest + (*SupportedIndicesMapResponse)(nil), // 5: proto.control_plane.experimental.v1.SupportedIndicesMapResponse + (*IndexIdentifierMapRequest)(nil), // 6: proto.control_plane.experimental.v1.IndexIdentifierMapRequest + (*IndexIdentifierMapResponse)(nil), // 7: proto.control_plane.experimental.v1.IndexIdentifierMapResponse + (*RemotePolicyDescriptionRequest)(nil), // 8: proto.control_plane.experimental.v1.RemotePolicyDescriptionRequest + (*RemotePolicyDescriptionResponse)(nil), // 9: proto.control_plane.experimental.v1.RemotePolicyDescriptionResponse + (*LocalPolicyDescriptionRequest)(nil), // 10: proto.control_plane.experimental.v1.LocalPolicyDescriptionRequest + (*LocalPolicyDescriptionResponse)(nil), // 11: proto.control_plane.experimental.v1.LocalPolicyDescriptionResponse + (*RemoteMapsRequest)(nil), // 12: proto.control_plane.experimental.v1.RemoteMapsRequest + (*RemoteMapsResponse)(nil), // 13: proto.control_plane.experimental.v1.RemoteMapsResponse + (*DetachedMapsRequest)(nil), // 14: proto.control_plane.experimental.v1.DetachedMapsRequest + (*DetachedMapsResponse)(nil), // 15: proto.control_plane.experimental.v1.DetachedMapsResponse + nil, // 16: proto.control_plane.experimental.v1.MPLSMapResponse.MplsInterfacePoliciesMapEntry + nil, // 17: proto.control_plane.experimental.v1.MPLSMapResponse.MplsIpMapEntry + nil, // 18: proto.control_plane.experimental.v1.IndexIdentifierMapResponse.IndexIdentifierMapEntry + (*FABRIDIndexMapEntry)(nil), // 19: proto.control_plane.experimental.v1.FABRIDIndexMapEntry + (*FABRIDDetachableMaps)(nil), // 20: proto.control_plane.experimental.v1.FABRIDDetachableMaps + (*FABRIDPolicyIdentifier)(nil), // 21: proto.control_plane.experimental.v1.FABRIDPolicyIdentifier +} +var file_proto_control_plane_experimental_v1_fabrid_proto_depIdxs = []int32{ + 1, // 0: proto.control_plane.experimental.v1.MPLSIPArray.entry:type_name -> proto.control_plane.experimental.v1.MPLSIP + 16, // 1: proto.control_plane.experimental.v1.MPLSMapResponse.mpls_interface_policies_map:type_name -> proto.control_plane.experimental.v1.MPLSMapResponse.MplsInterfacePoliciesMapEntry + 17, // 2: proto.control_plane.experimental.v1.MPLSMapResponse.mpls_ip_map:type_name -> proto.control_plane.experimental.v1.MPLSMapResponse.MplsIpMapEntry + 19, // 3: proto.control_plane.experimental.v1.SupportedIndicesMapResponse.supported_indices_map:type_name -> proto.control_plane.experimental.v1.FABRIDIndexMapEntry + 18, // 4: proto.control_plane.experimental.v1.IndexIdentifierMapResponse.index_identifier_map:type_name -> proto.control_plane.experimental.v1.IndexIdentifierMapResponse.IndexIdentifierMapEntry + 20, // 5: proto.control_plane.experimental.v1.RemoteMapsResponse.maps:type_name -> proto.control_plane.experimental.v1.FABRIDDetachableMaps + 20, // 6: proto.control_plane.experimental.v1.DetachedMapsResponse.maps:type_name -> proto.control_plane.experimental.v1.FABRIDDetachableMaps + 0, // 7: proto.control_plane.experimental.v1.MPLSMapResponse.MplsIpMapEntry.value:type_name -> proto.control_plane.experimental.v1.MPLSIPArray + 21, // 8: proto.control_plane.experimental.v1.IndexIdentifierMapResponse.IndexIdentifierMapEntry.value:type_name -> proto.control_plane.experimental.v1.FABRIDPolicyIdentifier + 4, // 9: proto.control_plane.experimental.v1.FABRIDInterService.SupportedIndicesMap:input_type -> proto.control_plane.experimental.v1.SupportedIndicesMapRequest + 6, // 10: proto.control_plane.experimental.v1.FABRIDInterService.IndexIdentifierMap:input_type -> proto.control_plane.experimental.v1.IndexIdentifierMapRequest + 14, // 11: proto.control_plane.experimental.v1.FABRIDInterService.DetachedMaps:input_type -> proto.control_plane.experimental.v1.DetachedMapsRequest + 10, // 12: proto.control_plane.experimental.v1.FABRIDInterService.LocalPolicyDescription:input_type -> proto.control_plane.experimental.v1.LocalPolicyDescriptionRequest + 8, // 13: proto.control_plane.experimental.v1.FABRIDIntraService.RemotePolicyDescription:input_type -> proto.control_plane.experimental.v1.RemotePolicyDescriptionRequest + 12, // 14: proto.control_plane.experimental.v1.FABRIDIntraService.RemoteMaps:input_type -> proto.control_plane.experimental.v1.RemoteMapsRequest + 2, // 15: proto.control_plane.experimental.v1.FABRIDIntraService.MPLSMap:input_type -> proto.control_plane.experimental.v1.MPLSMapRequest + 5, // 16: proto.control_plane.experimental.v1.FABRIDInterService.SupportedIndicesMap:output_type -> proto.control_plane.experimental.v1.SupportedIndicesMapResponse + 7, // 17: proto.control_plane.experimental.v1.FABRIDInterService.IndexIdentifierMap:output_type -> proto.control_plane.experimental.v1.IndexIdentifierMapResponse + 15, // 18: proto.control_plane.experimental.v1.FABRIDInterService.DetachedMaps:output_type -> proto.control_plane.experimental.v1.DetachedMapsResponse + 11, // 19: proto.control_plane.experimental.v1.FABRIDInterService.LocalPolicyDescription:output_type -> proto.control_plane.experimental.v1.LocalPolicyDescriptionResponse + 9, // 20: proto.control_plane.experimental.v1.FABRIDIntraService.RemotePolicyDescription:output_type -> proto.control_plane.experimental.v1.RemotePolicyDescriptionResponse + 13, // 21: proto.control_plane.experimental.v1.FABRIDIntraService.RemoteMaps:output_type -> proto.control_plane.experimental.v1.RemoteMapsResponse + 3, // 22: proto.control_plane.experimental.v1.FABRIDIntraService.MPLSMap:output_type -> proto.control_plane.experimental.v1.MPLSMapResponse + 16, // [16:23] is the sub-list for method output_type + 9, // [9:16] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_proto_control_plane_experimental_v1_fabrid_proto_init() } +func file_proto_control_plane_experimental_v1_fabrid_proto_init() { + if File_proto_control_plane_experimental_v1_fabrid_proto != nil { + return + } + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_init() + if !protoimpl.UnsafeEnabled { + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MPLSIPArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MPLSIP); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MPLSMapRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MPLSMapResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SupportedIndicesMapRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SupportedIndicesMapResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IndexIdentifierMapRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IndexIdentifierMapResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemotePolicyDescriptionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemotePolicyDescriptionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LocalPolicyDescriptionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LocalPolicyDescriptionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoteMapsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoteMapsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetachedMapsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetachedMapsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_control_plane_experimental_v1_fabrid_proto_rawDesc, + NumEnums: 0, + NumMessages: 19, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_proto_control_plane_experimental_v1_fabrid_proto_goTypes, + DependencyIndexes: file_proto_control_plane_experimental_v1_fabrid_proto_depIdxs, + MessageInfos: file_proto_control_plane_experimental_v1_fabrid_proto_msgTypes, + }.Build() + File_proto_control_plane_experimental_v1_fabrid_proto = out.File + file_proto_control_plane_experimental_v1_fabrid_proto_rawDesc = nil + file_proto_control_plane_experimental_v1_fabrid_proto_goTypes = nil + file_proto_control_plane_experimental_v1_fabrid_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// FABRIDInterServiceClient is the client API for FABRIDInterService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type FABRIDInterServiceClient interface { + SupportedIndicesMap(ctx context.Context, in *SupportedIndicesMapRequest, opts ...grpc.CallOption) (*SupportedIndicesMapResponse, error) + IndexIdentifierMap(ctx context.Context, in *IndexIdentifierMapRequest, opts ...grpc.CallOption) (*IndexIdentifierMapResponse, error) + DetachedMaps(ctx context.Context, in *DetachedMapsRequest, opts ...grpc.CallOption) (*DetachedMapsResponse, error) + LocalPolicyDescription(ctx context.Context, in *LocalPolicyDescriptionRequest, opts ...grpc.CallOption) (*LocalPolicyDescriptionResponse, error) +} + +type fABRIDInterServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFABRIDInterServiceClient(cc grpc.ClientConnInterface) FABRIDInterServiceClient { + return &fABRIDInterServiceClient{cc} +} + +func (c *fABRIDInterServiceClient) SupportedIndicesMap(ctx context.Context, in *SupportedIndicesMapRequest, opts ...grpc.CallOption) (*SupportedIndicesMapResponse, error) { + out := new(SupportedIndicesMapResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDInterService/SupportedIndicesMap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fABRIDInterServiceClient) IndexIdentifierMap(ctx context.Context, in *IndexIdentifierMapRequest, opts ...grpc.CallOption) (*IndexIdentifierMapResponse, error) { + out := new(IndexIdentifierMapResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDInterService/IndexIdentifierMap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fABRIDInterServiceClient) DetachedMaps(ctx context.Context, in *DetachedMapsRequest, opts ...grpc.CallOption) (*DetachedMapsResponse, error) { + out := new(DetachedMapsResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDInterService/DetachedMaps", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fABRIDInterServiceClient) LocalPolicyDescription(ctx context.Context, in *LocalPolicyDescriptionRequest, opts ...grpc.CallOption) (*LocalPolicyDescriptionResponse, error) { + out := new(LocalPolicyDescriptionResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDInterService/LocalPolicyDescription", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FABRIDInterServiceServer is the server API for FABRIDInterService service. +type FABRIDInterServiceServer interface { + SupportedIndicesMap(context.Context, *SupportedIndicesMapRequest) (*SupportedIndicesMapResponse, error) + IndexIdentifierMap(context.Context, *IndexIdentifierMapRequest) (*IndexIdentifierMapResponse, error) + DetachedMaps(context.Context, *DetachedMapsRequest) (*DetachedMapsResponse, error) + LocalPolicyDescription(context.Context, *LocalPolicyDescriptionRequest) (*LocalPolicyDescriptionResponse, error) +} + +// UnimplementedFABRIDInterServiceServer can be embedded to have forward compatible implementations. +type UnimplementedFABRIDInterServiceServer struct { +} + +func (*UnimplementedFABRIDInterServiceServer) SupportedIndicesMap(context.Context, *SupportedIndicesMapRequest) (*SupportedIndicesMapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SupportedIndicesMap not implemented") +} +func (*UnimplementedFABRIDInterServiceServer) IndexIdentifierMap(context.Context, *IndexIdentifierMapRequest) (*IndexIdentifierMapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IndexIdentifierMap not implemented") +} +func (*UnimplementedFABRIDInterServiceServer) DetachedMaps(context.Context, *DetachedMapsRequest) (*DetachedMapsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DetachedMaps not implemented") +} +func (*UnimplementedFABRIDInterServiceServer) LocalPolicyDescription(context.Context, *LocalPolicyDescriptionRequest) (*LocalPolicyDescriptionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LocalPolicyDescription not implemented") +} + +func RegisterFABRIDInterServiceServer(s *grpc.Server, srv FABRIDInterServiceServer) { + s.RegisterService(&_FABRIDInterService_serviceDesc, srv) +} + +func _FABRIDInterService_SupportedIndicesMap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SupportedIndicesMapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDInterServiceServer).SupportedIndicesMap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDInterService/SupportedIndicesMap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDInterServiceServer).SupportedIndicesMap(ctx, req.(*SupportedIndicesMapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FABRIDInterService_IndexIdentifierMap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(IndexIdentifierMapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDInterServiceServer).IndexIdentifierMap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDInterService/IndexIdentifierMap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDInterServiceServer).IndexIdentifierMap(ctx, req.(*IndexIdentifierMapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FABRIDInterService_DetachedMaps_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DetachedMapsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDInterServiceServer).DetachedMaps(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDInterService/DetachedMaps", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDInterServiceServer).DetachedMaps(ctx, req.(*DetachedMapsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FABRIDInterService_LocalPolicyDescription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LocalPolicyDescriptionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDInterServiceServer).LocalPolicyDescription(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDInterService/LocalPolicyDescription", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDInterServiceServer).LocalPolicyDescription(ctx, req.(*LocalPolicyDescriptionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _FABRIDInterService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "proto.control_plane.experimental.v1.FABRIDInterService", + HandlerType: (*FABRIDInterServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SupportedIndicesMap", + Handler: _FABRIDInterService_SupportedIndicesMap_Handler, + }, + { + MethodName: "IndexIdentifierMap", + Handler: _FABRIDInterService_IndexIdentifierMap_Handler, + }, + { + MethodName: "DetachedMaps", + Handler: _FABRIDInterService_DetachedMaps_Handler, + }, + { + MethodName: "LocalPolicyDescription", + Handler: _FABRIDInterService_LocalPolicyDescription_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/control_plane/experimental/v1/fabrid.proto", +} + +// FABRIDIntraServiceClient is the client API for FABRIDIntraService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type FABRIDIntraServiceClient interface { + RemotePolicyDescription(ctx context.Context, in *RemotePolicyDescriptionRequest, opts ...grpc.CallOption) (*RemotePolicyDescriptionResponse, error) + RemoteMaps(ctx context.Context, in *RemoteMapsRequest, opts ...grpc.CallOption) (*RemoteMapsResponse, error) + MPLSMap(ctx context.Context, in *MPLSMapRequest, opts ...grpc.CallOption) (*MPLSMapResponse, error) +} + +type fABRIDIntraServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFABRIDIntraServiceClient(cc grpc.ClientConnInterface) FABRIDIntraServiceClient { + return &fABRIDIntraServiceClient{cc} +} + +func (c *fABRIDIntraServiceClient) RemotePolicyDescription(ctx context.Context, in *RemotePolicyDescriptionRequest, opts ...grpc.CallOption) (*RemotePolicyDescriptionResponse, error) { + out := new(RemotePolicyDescriptionResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDIntraService/RemotePolicyDescription", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fABRIDIntraServiceClient) RemoteMaps(ctx context.Context, in *RemoteMapsRequest, opts ...grpc.CallOption) (*RemoteMapsResponse, error) { + out := new(RemoteMapsResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDIntraService/RemoteMaps", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fABRIDIntraServiceClient) MPLSMap(ctx context.Context, in *MPLSMapRequest, opts ...grpc.CallOption) (*MPLSMapResponse, error) { + out := new(MPLSMapResponse) + err := c.cc.Invoke(ctx, "/proto.control_plane.experimental.v1.FABRIDIntraService/MPLSMap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FABRIDIntraServiceServer is the server API for FABRIDIntraService service. +type FABRIDIntraServiceServer interface { + RemotePolicyDescription(context.Context, *RemotePolicyDescriptionRequest) (*RemotePolicyDescriptionResponse, error) + RemoteMaps(context.Context, *RemoteMapsRequest) (*RemoteMapsResponse, error) + MPLSMap(context.Context, *MPLSMapRequest) (*MPLSMapResponse, error) +} + +// UnimplementedFABRIDIntraServiceServer can be embedded to have forward compatible implementations. +type UnimplementedFABRIDIntraServiceServer struct { +} + +func (*UnimplementedFABRIDIntraServiceServer) RemotePolicyDescription(context.Context, *RemotePolicyDescriptionRequest) (*RemotePolicyDescriptionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemotePolicyDescription not implemented") +} +func (*UnimplementedFABRIDIntraServiceServer) RemoteMaps(context.Context, *RemoteMapsRequest) (*RemoteMapsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoteMaps not implemented") +} +func (*UnimplementedFABRIDIntraServiceServer) MPLSMap(context.Context, *MPLSMapRequest) (*MPLSMapResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MPLSMap not implemented") +} + +func RegisterFABRIDIntraServiceServer(s *grpc.Server, srv FABRIDIntraServiceServer) { + s.RegisterService(&_FABRIDIntraService_serviceDesc, srv) +} + +func _FABRIDIntraService_RemotePolicyDescription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemotePolicyDescriptionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDIntraServiceServer).RemotePolicyDescription(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDIntraService/RemotePolicyDescription", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDIntraServiceServer).RemotePolicyDescription(ctx, req.(*RemotePolicyDescriptionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FABRIDIntraService_RemoteMaps_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoteMapsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDIntraServiceServer).RemoteMaps(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDIntraService/RemoteMaps", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDIntraServiceServer).RemoteMaps(ctx, req.(*RemoteMapsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FABRIDIntraService_MPLSMap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MPLSMapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FABRIDIntraServiceServer).MPLSMap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.control_plane.experimental.v1.FABRIDIntraService/MPLSMap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FABRIDIntraServiceServer).MPLSMap(ctx, req.(*MPLSMapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _FABRIDIntraService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "proto.control_plane.experimental.v1.FABRIDIntraService", + HandlerType: (*FABRIDIntraServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RemotePolicyDescription", + Handler: _FABRIDIntraService_RemotePolicyDescription_Handler, + }, + { + MethodName: "RemoteMaps", + Handler: _FABRIDIntraService_RemoteMaps_Handler, + }, + { + MethodName: "MPLSMap", + Handler: _FABRIDIntraService_MPLSMap_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/control_plane/experimental/v1/fabrid.proto", +} diff --git a/pkg/proto/control_plane/experimental/fabrid_extensions.pb.go b/pkg/proto/control_plane/experimental/fabrid_extensions.pb.go new file mode 100755 index 0000000000..9c7a3ab80e --- /dev/null +++ b/pkg/proto/control_plane/experimental/fabrid_extensions.pb.go @@ -0,0 +1,590 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.21.10 +// source: proto/control_plane/experimental/v1/fabrid_extensions.proto + +package experimental + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FABRIDConnectionType int32 + +const ( + FABRIDConnectionType_FABRID_CONNECTION_TYPE_UNSPECIFIED FABRIDConnectionType = 0 + FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV4_RANGE FABRIDConnectionType = 1 + FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV6_RANGE FABRIDConnectionType = 2 + FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE FABRIDConnectionType = 3 + FABRIDConnectionType_FABRID_CONNECTION_TYPE_WILDCARD FABRIDConnectionType = 4 +) + +// Enum value maps for FABRIDConnectionType. +var ( + FABRIDConnectionType_name = map[int32]string{ + 0: "FABRID_CONNECTION_TYPE_UNSPECIFIED", + 1: "FABRID_CONNECTION_TYPE_IPV4_RANGE", + 2: "FABRID_CONNECTION_TYPE_IPV6_RANGE", + 3: "FABRID_CONNECTION_TYPE_INTERFACE", + 4: "FABRID_CONNECTION_TYPE_WILDCARD", + } + FABRIDConnectionType_value = map[string]int32{ + "FABRID_CONNECTION_TYPE_UNSPECIFIED": 0, + "FABRID_CONNECTION_TYPE_IPV4_RANGE": 1, + "FABRID_CONNECTION_TYPE_IPV6_RANGE": 2, + "FABRID_CONNECTION_TYPE_INTERFACE": 3, + "FABRID_CONNECTION_TYPE_WILDCARD": 4, + } +) + +func (x FABRIDConnectionType) Enum() *FABRIDConnectionType { + p := new(FABRIDConnectionType) + *p = x + return p +} + +func (x FABRIDConnectionType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FABRIDConnectionType) Descriptor() protoreflect.EnumDescriptor { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_enumTypes[0].Descriptor() +} + +func (FABRIDConnectionType) Type() protoreflect.EnumType { + return &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_enumTypes[0] +} + +func (x FABRIDConnectionType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FABRIDConnectionType.Descriptor instead. +func (FABRIDConnectionType) EnumDescriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP(), []int{0} +} + +type FABRIDDetachableMaps struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SupportedIndicesMap []*FABRIDIndexMapEntry `protobuf:"bytes,1,rep,name=supported_indices_map,json=supportedIndicesMap,proto3" json:"supported_indices_map,omitempty"` + IndexIdentifierMap map[uint32]*FABRIDPolicyIdentifier `protobuf:"bytes,2,rep,name=index_identifier_map,json=indexIdentifierMap,proto3" json:"index_identifier_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *FABRIDDetachableMaps) Reset() { + *x = FABRIDDetachableMaps{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FABRIDDetachableMaps) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FABRIDDetachableMaps) ProtoMessage() {} + +func (x *FABRIDDetachableMaps) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FABRIDDetachableMaps.ProtoReflect.Descriptor instead. +func (*FABRIDDetachableMaps) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP(), []int{0} +} + +func (x *FABRIDDetachableMaps) GetSupportedIndicesMap() []*FABRIDIndexMapEntry { + if x != nil { + return x.SupportedIndicesMap + } + return nil +} + +func (x *FABRIDDetachableMaps) GetIndexIdentifierMap() map[uint32]*FABRIDPolicyIdentifier { + if x != nil { + return x.IndexIdentifierMap + } + return nil +} + +type FABRIDPolicyIdentifier struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PolicyIsLocal bool `protobuf:"varint,1,opt,name=policy_is_local,json=policyIsLocal,proto3" json:"policy_is_local,omitempty"` + PolicyIdentifier uint32 `protobuf:"varint,2,opt,name=policy_identifier,json=policyIdentifier,proto3" json:"policy_identifier,omitempty"` +} + +func (x *FABRIDPolicyIdentifier) Reset() { + *x = FABRIDPolicyIdentifier{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FABRIDPolicyIdentifier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FABRIDPolicyIdentifier) ProtoMessage() {} + +func (x *FABRIDPolicyIdentifier) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FABRIDPolicyIdentifier.ProtoReflect.Descriptor instead. +func (*FABRIDPolicyIdentifier) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP(), []int{1} +} + +func (x *FABRIDPolicyIdentifier) GetPolicyIsLocal() bool { + if x != nil { + return x.PolicyIsLocal + } + return false +} + +func (x *FABRIDPolicyIdentifier) GetPolicyIdentifier() uint32 { + if x != nil { + return x.PolicyIdentifier + } + return 0 +} + +type FABRIDIndexMapEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IePair *FABRIDIngressEgressPair `protobuf:"bytes,1,opt,name=ie_pair,json=iePair,proto3" json:"ie_pair,omitempty"` + SupportedPolicyIndices []uint32 `protobuf:"varint,2,rep,packed,name=supported_policy_indices,json=supportedPolicyIndices,proto3" json:"supported_policy_indices,omitempty"` +} + +func (x *FABRIDIndexMapEntry) Reset() { + *x = FABRIDIndexMapEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FABRIDIndexMapEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FABRIDIndexMapEntry) ProtoMessage() {} + +func (x *FABRIDIndexMapEntry) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FABRIDIndexMapEntry.ProtoReflect.Descriptor instead. +func (*FABRIDIndexMapEntry) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP(), []int{2} +} + +func (x *FABRIDIndexMapEntry) GetIePair() *FABRIDIngressEgressPair { + if x != nil { + return x.IePair + } + return nil +} + +func (x *FABRIDIndexMapEntry) GetSupportedPolicyIndices() []uint32 { + if x != nil { + return x.SupportedPolicyIndices + } + return nil +} + +type FABRIDIngressEgressPair struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ingress *FABRIDConnectionPoint `protobuf:"bytes,1,opt,name=ingress,proto3" json:"ingress,omitempty"` + Egress *FABRIDConnectionPoint `protobuf:"bytes,2,opt,name=egress,proto3" json:"egress,omitempty"` +} + +func (x *FABRIDIngressEgressPair) Reset() { + *x = FABRIDIngressEgressPair{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FABRIDIngressEgressPair) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FABRIDIngressEgressPair) ProtoMessage() {} + +func (x *FABRIDIngressEgressPair) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FABRIDIngressEgressPair.ProtoReflect.Descriptor instead. +func (*FABRIDIngressEgressPair) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP(), []int{3} +} + +func (x *FABRIDIngressEgressPair) GetIngress() *FABRIDConnectionPoint { + if x != nil { + return x.Ingress + } + return nil +} + +func (x *FABRIDIngressEgressPair) GetEgress() *FABRIDConnectionPoint { + if x != nil { + return x.Egress + } + return nil +} + +type FABRIDConnectionPoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type FABRIDConnectionType `protobuf:"varint,1,opt,name=type,proto3,enum=proto.control_plane.experimental.v1.FABRIDConnectionType" json:"type,omitempty"` + IpAddress []byte `protobuf:"bytes,2,opt,name=ip_address,json=ipAddress,proto3" json:"ip_address,omitempty"` + IpPrefix uint32 `protobuf:"varint,3,opt,name=ip_prefix,json=ipPrefix,proto3" json:"ip_prefix,omitempty"` + Interface uint64 `protobuf:"varint,4,opt,name=interface,proto3" json:"interface,omitempty"` +} + +func (x *FABRIDConnectionPoint) Reset() { + *x = FABRIDConnectionPoint{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FABRIDConnectionPoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FABRIDConnectionPoint) ProtoMessage() {} + +func (x *FABRIDConnectionPoint) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FABRIDConnectionPoint.ProtoReflect.Descriptor instead. +func (*FABRIDConnectionPoint) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP(), []int{4} +} + +func (x *FABRIDConnectionPoint) GetType() FABRIDConnectionType { + if x != nil { + return x.Type + } + return FABRIDConnectionType_FABRID_CONNECTION_TYPE_UNSPECIFIED +} + +func (x *FABRIDConnectionPoint) GetIpAddress() []byte { + if x != nil { + return x.IpAddress + } + return nil +} + +func (x *FABRIDConnectionPoint) GetIpPrefix() uint32 { + if x != nil { + return x.IpPrefix + } + return 0 +} + +func (x *FABRIDConnectionPoint) GetInterface() uint64 { + if x != nil { + return x.Interface + } + return 0 +} + +var File_proto_control_plane_experimental_v1_fabrid_extensions_proto protoreflect.FileDescriptor + +var file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDesc = []byte{ + 0x0a, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x5f, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, + 0x76, 0x31, 0x22, 0x8f, 0x03, 0x0a, 0x14, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x44, 0x65, 0x74, + 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x15, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x4d, 0x61, 0x70, 0x12, 0x83, 0x01, 0x0a, 0x14, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x5f, 0x6d, + 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x51, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x41, 0x42, 0x52, 0x49, 0x44, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4d, + 0x61, 0x70, 0x73, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x1a, + 0x82, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x51, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6d, 0x0a, 0x16, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x26, + 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, + 0x73, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x10, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x22, 0xa6, 0x01, 0x0a, 0x13, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x55, 0x0a, 0x07, 0x69, + 0x65, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x69, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x16, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xc3, 0x01, 0x0a, + 0x17, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x69, 0x72, 0x12, 0x54, 0x0a, 0x07, 0x69, 0x6e, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x52, + 0x0a, 0x06, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x67, 0x72, 0x65, + 0x73, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4d, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, + 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x70, + 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, + 0x70, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x2a, 0xd7, 0x01, 0x0a, 0x14, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, + 0x0a, 0x22, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, + 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x49, 0x50, 0x56, 0x34, 0x5f, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x01, 0x12, 0x25, 0x0a, + 0x21, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x50, 0x56, 0x36, 0x5f, 0x52, 0x41, 0x4e, + 0x47, 0x45, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x5f, 0x43, + 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, + 0x4e, 0x54, 0x45, 0x52, 0x46, 0x41, 0x43, 0x45, 0x10, 0x03, 0x12, 0x23, 0x0a, 0x1f, 0x46, 0x41, + 0x42, 0x52, 0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, 0x52, 0x44, 0x10, 0x04, 0x42, + 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, + 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescOnce sync.Once + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescData = file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDesc +) + +func file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescGZIP() []byte { + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescOnce.Do(func() { + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescData) + }) + return file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDescData +} + +var file_proto_control_plane_experimental_v1_fabrid_extensions_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_proto_control_plane_experimental_v1_fabrid_extensions_proto_goTypes = []interface{}{ + (FABRIDConnectionType)(0), // 0: proto.control_plane.experimental.v1.FABRIDConnectionType + (*FABRIDDetachableMaps)(nil), // 1: proto.control_plane.experimental.v1.FABRIDDetachableMaps + (*FABRIDPolicyIdentifier)(nil), // 2: proto.control_plane.experimental.v1.FABRIDPolicyIdentifier + (*FABRIDIndexMapEntry)(nil), // 3: proto.control_plane.experimental.v1.FABRIDIndexMapEntry + (*FABRIDIngressEgressPair)(nil), // 4: proto.control_plane.experimental.v1.FABRIDIngressEgressPair + (*FABRIDConnectionPoint)(nil), // 5: proto.control_plane.experimental.v1.FABRIDConnectionPoint + nil, // 6: proto.control_plane.experimental.v1.FABRIDDetachableMaps.IndexIdentifierMapEntry +} +var file_proto_control_plane_experimental_v1_fabrid_extensions_proto_depIdxs = []int32{ + 3, // 0: proto.control_plane.experimental.v1.FABRIDDetachableMaps.supported_indices_map:type_name -> proto.control_plane.experimental.v1.FABRIDIndexMapEntry + 6, // 1: proto.control_plane.experimental.v1.FABRIDDetachableMaps.index_identifier_map:type_name -> proto.control_plane.experimental.v1.FABRIDDetachableMaps.IndexIdentifierMapEntry + 4, // 2: proto.control_plane.experimental.v1.FABRIDIndexMapEntry.ie_pair:type_name -> proto.control_plane.experimental.v1.FABRIDIngressEgressPair + 5, // 3: proto.control_plane.experimental.v1.FABRIDIngressEgressPair.ingress:type_name -> proto.control_plane.experimental.v1.FABRIDConnectionPoint + 5, // 4: proto.control_plane.experimental.v1.FABRIDIngressEgressPair.egress:type_name -> proto.control_plane.experimental.v1.FABRIDConnectionPoint + 0, // 5: proto.control_plane.experimental.v1.FABRIDConnectionPoint.type:type_name -> proto.control_plane.experimental.v1.FABRIDConnectionType + 2, // 6: proto.control_plane.experimental.v1.FABRIDDetachableMaps.IndexIdentifierMapEntry.value:type_name -> proto.control_plane.experimental.v1.FABRIDPolicyIdentifier + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_proto_control_plane_experimental_v1_fabrid_extensions_proto_init() } +func file_proto_control_plane_experimental_v1_fabrid_extensions_proto_init() { + if File_proto_control_plane_experimental_v1_fabrid_extensions_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FABRIDDetachableMaps); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FABRIDPolicyIdentifier); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FABRIDIndexMapEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FABRIDIngressEgressPair); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FABRIDConnectionPoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDesc, + NumEnums: 1, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_control_plane_experimental_v1_fabrid_extensions_proto_goTypes, + DependencyIndexes: file_proto_control_plane_experimental_v1_fabrid_extensions_proto_depIdxs, + EnumInfos: file_proto_control_plane_experimental_v1_fabrid_extensions_proto_enumTypes, + MessageInfos: file_proto_control_plane_experimental_v1_fabrid_extensions_proto_msgTypes, + }.Build() + File_proto_control_plane_experimental_v1_fabrid_extensions_proto = out.File + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_rawDesc = nil + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_goTypes = nil + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_depIdxs = nil +} diff --git a/pkg/proto/control_plane/experimental/seg_detached_extensions_fabrid.pb.go b/pkg/proto/control_plane/experimental/seg_detached_extensions_fabrid.pb.go new file mode 100755 index 0000000000..9b4e706ec4 --- /dev/null +++ b/pkg/proto/control_plane/experimental/seg_detached_extensions_fabrid.pb.go @@ -0,0 +1,162 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.21.10 +// source: proto/control_plane/experimental/v1/seg_detached_extensions_fabrid.proto + +package experimental + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FABRIDDetachedExtension struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Maps *FABRIDDetachableMaps `protobuf:"bytes,1,opt,name=maps,proto3" json:"maps,omitempty"` +} + +func (x *FABRIDDetachedExtension) Reset() { + *x = FABRIDDetachedExtension{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FABRIDDetachedExtension) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FABRIDDetachedExtension) ProtoMessage() {} + +func (x *FABRIDDetachedExtension) ProtoReflect() protoreflect.Message { + mi := &file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FABRIDDetachedExtension.ProtoReflect.Descriptor instead. +func (*FABRIDDetachedExtension) Descriptor() ([]byte, []int) { + return file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescGZIP(), []int{0} +} + +func (x *FABRIDDetachedExtension) GetMaps() *FABRIDDetachableMaps { + if x != nil { + return x.Maps + } + return nil +} + +var File_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto protoreflect.FileDescriptor + +var file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDesc = []byte{ + 0x0a, 0x48, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, + 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x66, 0x61, + 0x62, 0x72, 0x69, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x1a, + 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x68, 0x0a, 0x17, + 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x04, 0x6d, 0x61, 0x70, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x41, 0x42, 0x52, + 0x49, 0x44, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x70, 0x73, + 0x52, 0x04, 0x6d, 0x61, 0x70, 0x73, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, + 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescOnce sync.Once + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescData = file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDesc +) + +func file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescGZIP() []byte { + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescOnce.Do(func() { + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescData) + }) + return file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDescData +} + +var file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_goTypes = []interface{}{ + (*FABRIDDetachedExtension)(nil), // 0: proto.control_plane.experimental.v1.FABRIDDetachedExtension + (*FABRIDDetachableMaps)(nil), // 1: proto.control_plane.experimental.v1.FABRIDDetachableMaps +} +var file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_depIdxs = []int32{ + 1, // 0: proto.control_plane.experimental.v1.FABRIDDetachedExtension.maps:type_name -> proto.control_plane.experimental.v1.FABRIDDetachableMaps + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_init() } +func file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_init() { + if File_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto != nil { + return + } + file_proto_control_plane_experimental_v1_fabrid_extensions_proto_init() + if !protoimpl.UnsafeEnabled { + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FABRIDDetachedExtension); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_goTypes, + DependencyIndexes: file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_depIdxs, + MessageInfos: file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_msgTypes, + }.Build() + File_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto = out.File + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_rawDesc = nil + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_goTypes = nil + file_proto_control_plane_experimental_v1_seg_detached_extensions_fabrid_proto_depIdxs = nil +} diff --git a/pkg/proto/control_plane/seg_extensions.pb.go b/pkg/proto/control_plane/seg_extensions.pb.go index 8e6b6e165d..5b0ddb9162 100644 --- a/pkg/proto/control_plane/seg_extensions.pb.go +++ b/pkg/proto/control_plane/seg_extensions.pb.go @@ -511,7 +511,8 @@ type DigestExtension struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Epic *DigestExtension_Digest `protobuf:"bytes,1000,opt,name=epic,proto3" json:"epic,omitempty"` + Epic *DigestExtension_Digest `protobuf:"bytes,1000,opt,name=epic,proto3" json:"epic,omitempty"` + Fabrid *DigestExtension_Digest `protobuf:"bytes,1001,opt,name=fabrid,proto3" json:"fabrid,omitempty"` } func (x *DigestExtension) Reset() { @@ -553,12 +554,20 @@ func (x *DigestExtension) GetEpic() *DigestExtension_Digest { return nil } +func (x *DigestExtension) GetFabrid() *DigestExtension_Digest { + if x != nil { + return x.Fabrid + } + return nil +} + type PathSegmentUnsignedExtensions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Epic *experimental.EPICDetachedExtension `protobuf:"bytes,1000,opt,name=epic,proto3" json:"epic,omitempty"` + Epic *experimental.EPICDetachedExtension `protobuf:"bytes,1000,opt,name=epic,proto3" json:"epic,omitempty"` + Fabrid *experimental.FABRIDDetachedExtension `protobuf:"bytes,1001,opt,name=fabrid,proto3" json:"fabrid,omitempty"` } func (x *PathSegmentUnsignedExtensions) Reset() { @@ -600,6 +609,13 @@ func (x *PathSegmentUnsignedExtensions) GetEpic() *experimental.EPICDetachedExte return nil } +func (x *PathSegmentUnsignedExtensions) GetFabrid() *experimental.FABRIDDetachedExtension { + if x != nil { + return x.Fabrid + } + return nil +} + type DigestExtension_Digest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -658,159 +674,174 @@ var file_proto_control_plane_v1_seg_extensions_proto_rawDesc = []byte{ 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf7, 0x01, 0x0a, 0x15, 0x50, 0x61, 0x74, - 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x4c, 0x0a, 0x0b, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, - 0x69, 0x64, 0x64, 0x65, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x0a, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x42, - 0x0a, 0x07, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x64, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x73, 0x22, 0x32, 0x0a, 0x13, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x50, 0x61, 0x74, 0x68, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, - 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, - 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x22, 0x89, 0x06, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3d, - 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x43, 0x0a, - 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x12, 0x56, 0x0a, 0x10, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x63, 0x61, 0x72, 0x62, 0x6f, - 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x03, 0x67, 0x65, - 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x67, - 0x65, 0x6f, 0x12, 0x56, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x62, 0x0a, 0x0d, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x3d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x48, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, + 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, + 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xf7, 0x01, 0x0a, 0x15, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4c, 0x0a, 0x0b, 0x68, 0x69, + 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x50, + 0x61, 0x74, 0x68, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x68, 0x69, + 0x64, 0x64, 0x65, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x42, 0x0a, 0x07, 0x64, 0x69, 0x67, 0x65, + 0x73, 0x74, 0x73, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x22, 0x32, 0x0a, 0x13, + 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, + 0x22, 0x89, 0x06, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, + 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x43, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x56, 0x0a, 0x10, + 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x03, 0x67, 0x65, 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x34, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x49, + 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x47, + 0x65, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x67, 0x65, 0x6f, 0x12, 0x56, 0x0a, 0x09, + 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, + 0x6e, 0x66, 0x6f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x6e, + 0x6b, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x62, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x1a, 0x5e, 0x0a, 0x08, + 0x47, 0x65, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, + 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x0d, + 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3f, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, - 0x74, 0x65, 0x1a, 0x5e, 0x0a, 0x08, 0x47, 0x65, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, - 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x0d, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x3f, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x6f, 0x70, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x8d, 0x02, 0x0a, 0x0b, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8d, 0x02, 0x0a, + 0x0b, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x05, + 0x69, 0x6e, 0x74, 0x72, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x12, 0x44, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, - 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x12, 0x44, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, + 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x72, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x93, 0x02, 0x0a, + 0x0d, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, + 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x12, 0x46, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x93, 0x02, 0x0a, 0x0d, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, - 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x72, 0x61, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x12, 0x46, 0x0a, 0x05, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x49, 0x6e, - 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x38, - 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa5, 0x02, 0x0a, 0x13, 0x43, 0x61, 0x72, - 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x4c, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x36, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, - 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, - 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x12, 0x4c, - 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, - 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, - 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0xa5, 0x02, 0x0a, 0x13, 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4c, 0x0a, 0x05, 0x69, 0x6e, + 0x74, 0x72, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x12, 0x4c, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, + 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x05, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x64, 0x0a, 0x0e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, - 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x02, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x78, 0x0a, 0x0f, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x04, 0x65, 0x70, 0x69, - 0x63, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x52, 0x04, 0x65, 0x70, 0x69, 0x63, 0x1a, 0x20, - 0x0a, 0x06, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, - 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, - 0x22, 0x70, 0x0a, 0x1d, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x55, - 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x4f, 0x0a, 0x04, 0x65, 0x70, 0x69, 0x63, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, - 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x50, 0x49, 0x43, 0x44, 0x65, 0x74, 0x61, 0x63, - 0x68, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x65, 0x70, - 0x69, 0x63, 0x2a, 0x6c, 0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, - 0x0a, 0x15, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x49, 0x4e, - 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12, - 0x17, 0x0a, 0x13, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, - 0x54, 0x49, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x49, 0x4e, 0x4b, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x4e, 0x45, 0x54, 0x10, 0x03, - 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x64, 0x0a, 0x0e, 0x47, 0x65, + 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, + 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, + 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x6c, 0x6f, 0x6e, + 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x22, 0xc1, 0x01, 0x0a, 0x0f, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x04, 0x65, 0x70, 0x69, 0x63, 0x18, 0xe8, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x52, 0x04, 0x65, 0x70, 0x69, 0x63, 0x12, 0x47, 0x0a, 0x06, 0x66, 0x61, 0x62, + 0x72, 0x69, 0x64, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x52, 0x06, 0x66, 0x61, 0x62, 0x72, + 0x69, 0x64, 0x1a, 0x20, 0x0a, 0x06, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x22, 0xc7, 0x01, 0x0a, 0x1d, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4f, 0x0a, 0x04, 0x65, 0x70, 0x69, 0x63, 0x18, 0xe8, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, + 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x50, 0x49, 0x43, + 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x04, 0x65, 0x70, 0x69, 0x63, 0x12, 0x55, 0x0a, 0x06, 0x66, 0x61, 0x62, 0x72, 0x69, + 0x64, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x41, 0x42, 0x52, 0x49, 0x44, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x2a, 0x6c, + 0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x49, + 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4c, + 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x48, + 0x4f, 0x50, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x4e, 0x45, 0x54, 0x10, 0x03, 0x42, 0x35, 0x5a, 0x33, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -848,7 +879,8 @@ var file_proto_control_plane_v1_seg_extensions_proto_goTypes = []interface{}{ nil, // 17: proto.control_plane.v1.CarbonIntensityInfo.IntraEntry nil, // 18: proto.control_plane.v1.CarbonIntensityInfo.InterEntry (*DigestExtension_Digest)(nil), // 19: proto.control_plane.v1.DigestExtension.Digest - (*experimental.EPICDetachedExtension)(nil), // 20: proto.control_plane.experimental.v1.EPICDetachedExtension + (*experimental.EPICDetachedExtension)(nil), // 20: proto.control_plane.experimental.v1.EPICDetachedExtension + (*experimental.FABRIDDetachedExtension)(nil), // 21: proto.control_plane.experimental.v1.FABRIDDetachedExtension } var file_proto_control_plane_v1_seg_extensions_proto_depIdxs = []int32{ 3, // 0: proto.control_plane.v1.PathSegmentExtensions.static_info:type_name -> proto.control_plane.v1.StaticInfoExtension @@ -867,14 +899,16 @@ var file_proto_control_plane_v1_seg_extensions_proto_depIdxs = []int32{ 17, // 13: proto.control_plane.v1.CarbonIntensityInfo.intra:type_name -> proto.control_plane.v1.CarbonIntensityInfo.IntraEntry 18, // 14: proto.control_plane.v1.CarbonIntensityInfo.inter:type_name -> proto.control_plane.v1.CarbonIntensityInfo.InterEntry 19, // 15: proto.control_plane.v1.DigestExtension.epic:type_name -> proto.control_plane.v1.DigestExtension.Digest - 20, // 16: proto.control_plane.v1.PathSegmentUnsignedExtensions.epic:type_name -> proto.control_plane.experimental.v1.EPICDetachedExtension - 7, // 17: proto.control_plane.v1.StaticInfoExtension.GeoEntry.value:type_name -> proto.control_plane.v1.GeoCoordinates - 0, // 18: proto.control_plane.v1.StaticInfoExtension.LinkTypeEntry.value:type_name -> proto.control_plane.v1.LinkType - 19, // [19:19] is the sub-list for method output_type - 19, // [19:19] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 19, // 16: proto.control_plane.v1.DigestExtension.fabrid:type_name -> proto.control_plane.v1.DigestExtension.Digest + 20, // 17: proto.control_plane.v1.PathSegmentUnsignedExtensions.epic:type_name -> proto.control_plane.experimental.v1.EPICDetachedExtension + 21, // 18: proto.control_plane.v1.PathSegmentUnsignedExtensions.fabrid:type_name -> proto.control_plane.experimental.v1.FABRIDDetachedExtension + 7, // 19: proto.control_plane.v1.StaticInfoExtension.GeoEntry.value:type_name -> proto.control_plane.v1.GeoCoordinates + 0, // 20: proto.control_plane.v1.StaticInfoExtension.LinkTypeEntry.value:type_name -> proto.control_plane.v1.LinkType + 21, // [21:21] is the sub-list for method output_type + 21, // [21:21] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name } func init() { file_proto_control_plane_v1_seg_extensions_proto_init() } diff --git a/pkg/proto/daemon/BUILD.bazel b/pkg/proto/daemon/BUILD.bazel index 97a549ec9c..d4ee98c55e 100644 --- a/pkg/proto/daemon/BUILD.bazel +++ b/pkg/proto/daemon/BUILD.bazel @@ -7,6 +7,7 @@ go_proto_library( proto = "//proto/daemon/v1:daemon", visibility = ["//visibility:public"], deps = [ + "//pkg/proto/control_plane/experimental:go_default_library", "//pkg/proto/drkey:go_default_library", ], ) diff --git a/pkg/proto/daemon/daemon.pb.go b/pkg/proto/daemon/daemon.pb.go index 71f10bf2b5..1b2bcfa487 100644 --- a/pkg/proto/daemon/daemon.pb.go +++ b/pkg/proto/daemon/daemon.pb.go @@ -85,10 +85,11 @@ type PathsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SourceIsdAs uint64 `protobuf:"varint,1,opt,name=source_isd_as,json=sourceIsdAs,proto3" json:"source_isd_as,omitempty"` - DestinationIsdAs uint64 `protobuf:"varint,2,opt,name=destination_isd_as,json=destinationIsdAs,proto3" json:"destination_isd_as,omitempty"` - Refresh bool `protobuf:"varint,3,opt,name=refresh,proto3" json:"refresh,omitempty"` - Hidden bool `protobuf:"varint,4,opt,name=hidden,proto3" json:"hidden,omitempty"` + SourceIsdAs uint64 `protobuf:"varint,1,opt,name=source_isd_as,json=sourceIsdAs,proto3" json:"source_isd_as,omitempty"` + DestinationIsdAs uint64 `protobuf:"varint,2,opt,name=destination_isd_as,json=destinationIsdAs,proto3" json:"destination_isd_as,omitempty"` + Refresh bool `protobuf:"varint,3,opt,name=refresh,proto3" json:"refresh,omitempty"` + Hidden bool `protobuf:"varint,4,opt,name=hidden,proto3" json:"hidden,omitempty"` + FetchFabridDetachedMaps bool `protobuf:"varint,5,opt,name=fetch_fabrid_detached_maps,json=fetchFabridDetachedMaps,proto3" json:"fetch_fabrid_detached_maps,omitempty"` } func (x *PathsRequest) Reset() { @@ -151,6 +152,13 @@ func (x *PathsRequest) GetHidden() bool { return false } +func (x *PathsRequest) GetFetchFabridDetachedMaps() bool { + if x != nil { + return x.FetchFabridDetachedMaps + } + return false +} + type PathsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -216,6 +224,7 @@ type Path struct { Notes []string `protobuf:"bytes,11,rep,name=notes,proto3" json:"notes,omitempty"` EpicAuths *EpicAuths `protobuf:"bytes,12,opt,name=epic_auths,json=epicAuths,proto3" json:"epic_auths,omitempty"` CarbonIntensity []int64 `protobuf:"varint,13,rep,packed,name=carbon_intensity,json=carbonIntensity,proto3" json:"carbon_intensity,omitempty"` + FabridInfo []*FabridInfo `protobuf:"bytes,14,rep,name=fabrid_info,json=fabridInfo,proto3" json:"fabrid_info,omitempty"` } func (x *Path) Reset() { @@ -341,6 +350,13 @@ func (x *Path) GetCarbonIntensity() []int64 { return nil } +func (x *Path) GetFabridInfo() []*FabridInfo { + if x != nil { + return x.FabridInfo + } + return nil +} + type EpicAuths struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1577,56 +1593,66 @@ var file_proto_daemon_v1_daemon_proto_rawDesc = []byte{ 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x72, - 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x92, 0x01, 0x0a, 0x0c, 0x50, 0x61, - 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x73, 0x64, 0x41, 0x73, 0x12, 0x2c, - 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x73, - 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x22, 0x3c, - 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x23, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x5f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, + 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x22, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x73, + 0x64, 0x41, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, + 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x12, 0x3b, 0x0a, 0x1a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x66, 0x61, 0x62, + 0x72, 0x69, 0x64, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x70, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x66, 0x65, 0x74, 0x63, 0x68, 0x46, 0x61, + 0x62, 0x72, 0x69, 0x64, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x73, + 0x22, 0x3c, 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0xfd, + 0x04, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c, + 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x03, 0x67, 0x65, 0x6f, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, + 0x65, 0x73, 0x52, 0x03, 0x67, 0x65, 0x6f, 0x12, 0x36, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6e, + 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x48, 0x6f, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x70, + 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0xbf, 0x04, 0x0a, - 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x6d, 0x74, 0x75, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x33, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c, 0x61, 0x74, - 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x12, 0x31, 0x0a, 0x03, 0x67, 0x65, 0x6f, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x03, 0x67, 0x65, 0x6f, 0x12, 0x36, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x0a, - 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x6f, - 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x70, 0x69, 0x63, - 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x70, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x52, 0x09, 0x65, 0x70, 0x69, 0x63, 0x41, 0x75, - 0x74, 0x68, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0f, 0x63, - 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x22, 0x45, + 0x2e, 0x45, 0x70, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x52, 0x09, 0x65, 0x70, 0x69, 0x63, + 0x41, 0x75, 0x74, 0x68, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x5f, + 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x03, 0x52, + 0x0f, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, + 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x0a, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x45, 0x0a, 0x09, 0x45, 0x70, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x68, 0x76, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x50, 0x68, 0x76, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, @@ -1779,7 +1805,7 @@ var file_proto_daemon_v1_daemon_proto_rawDesc = []byte{ 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x50, - 0x45, 0x4e, 0x5f, 0x4e, 0x45, 0x54, 0x10, 0x03, 0x32, 0x9f, 0x06, 0x0a, 0x0d, 0x44, 0x61, 0x65, + 0x45, 0x4e, 0x5f, 0x4e, 0x45, 0x54, 0x10, 0x03, 0x32, 0xf8, 0x06, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, @@ -1829,11 +1855,16 @@ var file_proto_daemon_v1_daemon_proto_rawDesc = []byte{ 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0a, 0x46, 0x61, + 0x62, 0x72, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x62, 0x72, 0x69, + 0x64, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x61, 0x62, 0x72, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, + 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1881,8 +1912,11 @@ var file_proto_daemon_v1_daemon_proto_goTypes = []interface{}{ nil, // 27: proto.daemon.v1.ServicesResponse.ServicesEntry (*timestamppb.Timestamp)(nil), // 28: google.protobuf.Timestamp (*durationpb.Duration)(nil), // 29: google.protobuf.Duration - (drkey.Protocol)(0), // 30: proto.drkey.v1.Protocol - (*emptypb.Empty)(nil), // 31: google.protobuf.Empty + (*FabridInfo)(nil), // 30: proto.daemon.v1.FabridInfo + (drkey.Protocol)(0), // 31: proto.drkey.v1.Protocol + (*emptypb.Empty)(nil), // 32: google.protobuf.Empty + (*FabridKeysRequest)(nil), // 33: proto.daemon.v1.FabridKeysRequest + (*FabridKeysResponse)(nil), // 34: proto.daemon.v1.FabridKeysResponse } var file_proto_daemon_v1_daemon_proto_depIdxs = []int32{ 3, // 0: proto.daemon.v1.PathsResponse.paths:type_name -> proto.daemon.v1.Path @@ -1893,47 +1927,50 @@ var file_proto_daemon_v1_daemon_proto_depIdxs = []int32{ 6, // 5: proto.daemon.v1.Path.geo:type_name -> proto.daemon.v1.GeoCoordinates 0, // 6: proto.daemon.v1.Path.link_type:type_name -> proto.daemon.v1.LinkType 4, // 7: proto.daemon.v1.Path.epic_auths:type_name -> proto.daemon.v1.EpicAuths - 26, // 8: proto.daemon.v1.InterfacesResponse.interfaces:type_name -> proto.daemon.v1.InterfacesResponse.InterfacesEntry - 16, // 9: proto.daemon.v1.Interface.address:type_name -> proto.daemon.v1.Underlay - 27, // 10: proto.daemon.v1.ServicesResponse.services:type_name -> proto.daemon.v1.ServicesResponse.ServicesEntry - 15, // 11: proto.daemon.v1.ListService.services:type_name -> proto.daemon.v1.Service - 28, // 12: proto.daemon.v1.DRKeyHostASRequest.val_time:type_name -> google.protobuf.Timestamp - 30, // 13: proto.daemon.v1.DRKeyHostASRequest.protocol_id:type_name -> proto.drkey.v1.Protocol - 28, // 14: proto.daemon.v1.DRKeyHostASResponse.epoch_begin:type_name -> google.protobuf.Timestamp - 28, // 15: proto.daemon.v1.DRKeyHostASResponse.epoch_end:type_name -> google.protobuf.Timestamp - 28, // 16: proto.daemon.v1.DRKeyASHostRequest.val_time:type_name -> google.protobuf.Timestamp - 30, // 17: proto.daemon.v1.DRKeyASHostRequest.protocol_id:type_name -> proto.drkey.v1.Protocol - 28, // 18: proto.daemon.v1.DRKeyASHostResponse.epoch_begin:type_name -> google.protobuf.Timestamp - 28, // 19: proto.daemon.v1.DRKeyASHostResponse.epoch_end:type_name -> google.protobuf.Timestamp - 28, // 20: proto.daemon.v1.DRKeyHostHostRequest.val_time:type_name -> google.protobuf.Timestamp - 30, // 21: proto.daemon.v1.DRKeyHostHostRequest.protocol_id:type_name -> proto.drkey.v1.Protocol - 28, // 22: proto.daemon.v1.DRKeyHostHostResponse.epoch_begin:type_name -> google.protobuf.Timestamp - 28, // 23: proto.daemon.v1.DRKeyHostHostResponse.epoch_end:type_name -> google.protobuf.Timestamp - 11, // 24: proto.daemon.v1.InterfacesResponse.InterfacesEntry.value:type_name -> proto.daemon.v1.Interface - 14, // 25: proto.daemon.v1.ServicesResponse.ServicesEntry.value:type_name -> proto.daemon.v1.ListService - 1, // 26: proto.daemon.v1.DaemonService.Paths:input_type -> proto.daemon.v1.PathsRequest - 7, // 27: proto.daemon.v1.DaemonService.AS:input_type -> proto.daemon.v1.ASRequest - 9, // 28: proto.daemon.v1.DaemonService.Interfaces:input_type -> proto.daemon.v1.InterfacesRequest - 12, // 29: proto.daemon.v1.DaemonService.Services:input_type -> proto.daemon.v1.ServicesRequest - 17, // 30: proto.daemon.v1.DaemonService.NotifyInterfaceDown:input_type -> proto.daemon.v1.NotifyInterfaceDownRequest - 31, // 31: proto.daemon.v1.DaemonService.PortRange:input_type -> google.protobuf.Empty - 22, // 32: proto.daemon.v1.DaemonService.DRKeyASHost:input_type -> proto.daemon.v1.DRKeyASHostRequest - 20, // 33: proto.daemon.v1.DaemonService.DRKeyHostAS:input_type -> proto.daemon.v1.DRKeyHostASRequest - 24, // 34: proto.daemon.v1.DaemonService.DRKeyHostHost:input_type -> proto.daemon.v1.DRKeyHostHostRequest - 2, // 35: proto.daemon.v1.DaemonService.Paths:output_type -> proto.daemon.v1.PathsResponse - 8, // 36: proto.daemon.v1.DaemonService.AS:output_type -> proto.daemon.v1.ASResponse - 10, // 37: proto.daemon.v1.DaemonService.Interfaces:output_type -> proto.daemon.v1.InterfacesResponse - 13, // 38: proto.daemon.v1.DaemonService.Services:output_type -> proto.daemon.v1.ServicesResponse - 18, // 39: proto.daemon.v1.DaemonService.NotifyInterfaceDown:output_type -> proto.daemon.v1.NotifyInterfaceDownResponse - 19, // 40: proto.daemon.v1.DaemonService.PortRange:output_type -> proto.daemon.v1.PortRangeResponse - 23, // 41: proto.daemon.v1.DaemonService.DRKeyASHost:output_type -> proto.daemon.v1.DRKeyASHostResponse - 21, // 42: proto.daemon.v1.DaemonService.DRKeyHostAS:output_type -> proto.daemon.v1.DRKeyHostASResponse - 25, // 43: proto.daemon.v1.DaemonService.DRKeyHostHost:output_type -> proto.daemon.v1.DRKeyHostHostResponse - 35, // [35:44] is the sub-list for method output_type - 26, // [26:35] is the sub-list for method input_type - 26, // [26:26] is the sub-list for extension type_name - 26, // [26:26] is the sub-list for extension extendee - 0, // [0:26] is the sub-list for field type_name + 30, // 8: proto.daemon.v1.Path.fabrid_info:type_name -> proto.daemon.v1.FabridInfo + 26, // 9: proto.daemon.v1.InterfacesResponse.interfaces:type_name -> proto.daemon.v1.InterfacesResponse.InterfacesEntry + 16, // 10: proto.daemon.v1.Interface.address:type_name -> proto.daemon.v1.Underlay + 27, // 11: proto.daemon.v1.ServicesResponse.services:type_name -> proto.daemon.v1.ServicesResponse.ServicesEntry + 15, // 12: proto.daemon.v1.ListService.services:type_name -> proto.daemon.v1.Service + 28, // 13: proto.daemon.v1.DRKeyHostASRequest.val_time:type_name -> google.protobuf.Timestamp + 31, // 14: proto.daemon.v1.DRKeyHostASRequest.protocol_id:type_name -> proto.drkey.v1.Protocol + 28, // 15: proto.daemon.v1.DRKeyHostASResponse.epoch_begin:type_name -> google.protobuf.Timestamp + 28, // 16: proto.daemon.v1.DRKeyHostASResponse.epoch_end:type_name -> google.protobuf.Timestamp + 28, // 17: proto.daemon.v1.DRKeyASHostRequest.val_time:type_name -> google.protobuf.Timestamp + 31, // 18: proto.daemon.v1.DRKeyASHostRequest.protocol_id:type_name -> proto.drkey.v1.Protocol + 28, // 19: proto.daemon.v1.DRKeyASHostResponse.epoch_begin:type_name -> google.protobuf.Timestamp + 28, // 20: proto.daemon.v1.DRKeyASHostResponse.epoch_end:type_name -> google.protobuf.Timestamp + 28, // 21: proto.daemon.v1.DRKeyHostHostRequest.val_time:type_name -> google.protobuf.Timestamp + 31, // 22: proto.daemon.v1.DRKeyHostHostRequest.protocol_id:type_name -> proto.drkey.v1.Protocol + 28, // 23: proto.daemon.v1.DRKeyHostHostResponse.epoch_begin:type_name -> google.protobuf.Timestamp + 28, // 24: proto.daemon.v1.DRKeyHostHostResponse.epoch_end:type_name -> google.protobuf.Timestamp + 11, // 25: proto.daemon.v1.InterfacesResponse.InterfacesEntry.value:type_name -> proto.daemon.v1.Interface + 14, // 26: proto.daemon.v1.ServicesResponse.ServicesEntry.value:type_name -> proto.daemon.v1.ListService + 1, // 27: proto.daemon.v1.DaemonService.Paths:input_type -> proto.daemon.v1.PathsRequest + 7, // 28: proto.daemon.v1.DaemonService.AS:input_type -> proto.daemon.v1.ASRequest + 9, // 29: proto.daemon.v1.DaemonService.Interfaces:input_type -> proto.daemon.v1.InterfacesRequest + 12, // 30: proto.daemon.v1.DaemonService.Services:input_type -> proto.daemon.v1.ServicesRequest + 17, // 31: proto.daemon.v1.DaemonService.NotifyInterfaceDown:input_type -> proto.daemon.v1.NotifyInterfaceDownRequest + 32, // 32: proto.daemon.v1.DaemonService.PortRange:input_type -> google.protobuf.Empty + 22, // 33: proto.daemon.v1.DaemonService.DRKeyASHost:input_type -> proto.daemon.v1.DRKeyASHostRequest + 20, // 34: proto.daemon.v1.DaemonService.DRKeyHostAS:input_type -> proto.daemon.v1.DRKeyHostASRequest + 24, // 35: proto.daemon.v1.DaemonService.DRKeyHostHost:input_type -> proto.daemon.v1.DRKeyHostHostRequest + 33, // 36: proto.daemon.v1.DaemonService.FabridKeys:input_type -> proto.daemon.v1.FabridKeysRequest + 2, // 37: proto.daemon.v1.DaemonService.Paths:output_type -> proto.daemon.v1.PathsResponse + 8, // 38: proto.daemon.v1.DaemonService.AS:output_type -> proto.daemon.v1.ASResponse + 10, // 39: proto.daemon.v1.DaemonService.Interfaces:output_type -> proto.daemon.v1.InterfacesResponse + 13, // 40: proto.daemon.v1.DaemonService.Services:output_type -> proto.daemon.v1.ServicesResponse + 18, // 41: proto.daemon.v1.DaemonService.NotifyInterfaceDown:output_type -> proto.daemon.v1.NotifyInterfaceDownResponse + 19, // 42: proto.daemon.v1.DaemonService.PortRange:output_type -> proto.daemon.v1.PortRangeResponse + 23, // 43: proto.daemon.v1.DaemonService.DRKeyASHost:output_type -> proto.daemon.v1.DRKeyASHostResponse + 21, // 44: proto.daemon.v1.DaemonService.DRKeyHostAS:output_type -> proto.daemon.v1.DRKeyHostASResponse + 25, // 45: proto.daemon.v1.DaemonService.DRKeyHostHost:output_type -> proto.daemon.v1.DRKeyHostHostResponse + 34, // 46: proto.daemon.v1.DaemonService.FabridKeys:output_type -> proto.daemon.v1.FabridKeysResponse + 37, // [37:47] is the sub-list for method output_type + 27, // [27:37] is the sub-list for method input_type + 27, // [27:27] is the sub-list for extension type_name + 27, // [27:27] is the sub-list for extension extendee + 0, // [0:27] is the sub-list for field type_name } func init() { file_proto_daemon_v1_daemon_proto_init() } @@ -1941,6 +1978,7 @@ func file_proto_daemon_v1_daemon_proto_init() { if File_proto_daemon_v1_daemon_proto != nil { return } + file_proto_daemon_v1_daemon_fabrid_proto_init() if !protoimpl.UnsafeEnabled { file_proto_daemon_v1_daemon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PathsRequest); i { @@ -2285,6 +2323,7 @@ type DaemonServiceClient interface { DRKeyASHost(ctx context.Context, in *DRKeyASHostRequest, opts ...grpc.CallOption) (*DRKeyASHostResponse, error) DRKeyHostAS(ctx context.Context, in *DRKeyHostASRequest, opts ...grpc.CallOption) (*DRKeyHostASResponse, error) DRKeyHostHost(ctx context.Context, in *DRKeyHostHostRequest, opts ...grpc.CallOption) (*DRKeyHostHostResponse, error) + FabridKeys(ctx context.Context, in *FabridKeysRequest, opts ...grpc.CallOption) (*FabridKeysResponse, error) } type daemonServiceClient struct { @@ -2376,6 +2415,15 @@ func (c *daemonServiceClient) DRKeyHostHost(ctx context.Context, in *DRKeyHostHo return out, nil } +func (c *daemonServiceClient) FabridKeys(ctx context.Context, in *FabridKeysRequest, opts ...grpc.CallOption) (*FabridKeysResponse, error) { + out := new(FabridKeysResponse) + err := c.cc.Invoke(ctx, "/proto.daemon.v1.DaemonService/FabridKeys", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DaemonServiceServer is the server API for DaemonService service. type DaemonServiceServer interface { Paths(context.Context, *PathsRequest) (*PathsResponse, error) @@ -2387,6 +2435,7 @@ type DaemonServiceServer interface { DRKeyASHost(context.Context, *DRKeyASHostRequest) (*DRKeyASHostResponse, error) DRKeyHostAS(context.Context, *DRKeyHostASRequest) (*DRKeyHostASResponse, error) DRKeyHostHost(context.Context, *DRKeyHostHostRequest) (*DRKeyHostHostResponse, error) + FabridKeys(context.Context, *FabridKeysRequest) (*FabridKeysResponse, error) } // UnimplementedDaemonServiceServer can be embedded to have forward compatible implementations. @@ -2420,6 +2469,9 @@ func (*UnimplementedDaemonServiceServer) DRKeyHostAS(context.Context, *DRKeyHost func (*UnimplementedDaemonServiceServer) DRKeyHostHost(context.Context, *DRKeyHostHostRequest) (*DRKeyHostHostResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DRKeyHostHost not implemented") } +func (*UnimplementedDaemonServiceServer) FabridKeys(context.Context, *FabridKeysRequest) (*FabridKeysResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FabridKeys not implemented") +} func RegisterDaemonServiceServer(s *grpc.Server, srv DaemonServiceServer) { s.RegisterService(&_DaemonService_serviceDesc, srv) @@ -2587,6 +2639,24 @@ func _DaemonService_DRKeyHostHost_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _DaemonService_FabridKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FabridKeysRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).FabridKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.daemon.v1.DaemonService/FabridKeys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).FabridKeys(ctx, req.(*FabridKeysRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _DaemonService_serviceDesc = grpc.ServiceDesc{ ServiceName: "proto.daemon.v1.DaemonService", HandlerType: (*DaemonServiceServer)(nil), @@ -2627,6 +2697,10 @@ var _DaemonService_serviceDesc = grpc.ServiceDesc{ MethodName: "DRKeyHostHost", Handler: _DaemonService_DRKeyHostHost_Handler, }, + { + MethodName: "FabridKeys", + Handler: _DaemonService_FabridKeys_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto/daemon/v1/daemon.proto", diff --git a/pkg/proto/daemon/daemon_fabrid.pb.go b/pkg/proto/daemon/daemon_fabrid.pb.go new file mode 100644 index 0000000000..7c03833634 --- /dev/null +++ b/pkg/proto/daemon/daemon_fabrid.pb.go @@ -0,0 +1,531 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.21.10 +// source: proto/daemon/v1/daemon_fabrid.proto + +package daemon + +import ( + experimental "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FabridInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` + Policies []*FabridPolicy `protobuf:"bytes,3,rep,name=policies,proto3" json:"policies,omitempty"` + Detached bool `protobuf:"varint,4,opt,name=detached,proto3" json:"detached,omitempty"` +} + +func (x *FabridInfo) Reset() { + *x = FabridInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FabridInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FabridInfo) ProtoMessage() {} + +func (x *FabridInfo) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FabridInfo.ProtoReflect.Descriptor instead. +func (*FabridInfo) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_daemon_fabrid_proto_rawDescGZIP(), []int{0} +} + +func (x *FabridInfo) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *FabridInfo) GetDigest() []byte { + if x != nil { + return x.Digest + } + return nil +} + +func (x *FabridInfo) GetPolicies() []*FabridPolicy { + if x != nil { + return x.Policies + } + return nil +} + +func (x *FabridInfo) GetDetached() bool { + if x != nil { + return x.Detached + } + return false +} + +type FabridPolicy struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PolicyIdentifier *experimental.FABRIDPolicyIdentifier `protobuf:"bytes,1,opt,name=policy_identifier,json=policyIdentifier,proto3" json:"policy_identifier,omitempty"` + PolicyIndex uint32 `protobuf:"varint,2,opt,name=policy_index,json=policyIndex,proto3" json:"policy_index,omitempty"` +} + +func (x *FabridPolicy) Reset() { + *x = FabridPolicy{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FabridPolicy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FabridPolicy) ProtoMessage() {} + +func (x *FabridPolicy) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FabridPolicy.ProtoReflect.Descriptor instead. +func (*FabridPolicy) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_daemon_fabrid_proto_rawDescGZIP(), []int{1} +} + +func (x *FabridPolicy) GetPolicyIdentifier() *experimental.FABRIDPolicyIdentifier { + if x != nil { + return x.PolicyIdentifier + } + return nil +} + +func (x *FabridPolicy) GetPolicyIndex() uint32 { + if x != nil { + return x.PolicyIndex + } + return 0 +} + +type FabridKeysRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SrcHost string `protobuf:"bytes,1,opt,name=src_host,json=srcHost,proto3" json:"src_host,omitempty"` + DstAs uint64 `protobuf:"varint,2,opt,name=dst_as,json=dstAs,proto3" json:"dst_as,omitempty"` + PathAses []uint64 `protobuf:"varint,3,rep,packed,name=path_ases,json=pathAses,proto3" json:"path_ases,omitempty"` + DstHost *string `protobuf:"bytes,4,opt,name=dst_host,json=dstHost,proto3,oneof" json:"dst_host,omitempty"` +} + +func (x *FabridKeysRequest) Reset() { + *x = FabridKeysRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FabridKeysRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FabridKeysRequest) ProtoMessage() {} + +func (x *FabridKeysRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FabridKeysRequest.ProtoReflect.Descriptor instead. +func (*FabridKeysRequest) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_daemon_fabrid_proto_rawDescGZIP(), []int{2} +} + +func (x *FabridKeysRequest) GetSrcHost() string { + if x != nil { + return x.SrcHost + } + return "" +} + +func (x *FabridKeysRequest) GetDstAs() uint64 { + if x != nil { + return x.DstAs + } + return 0 +} + +func (x *FabridKeysRequest) GetPathAses() []uint64 { + if x != nil { + return x.PathAses + } + return nil +} + +func (x *FabridKeysRequest) GetDstHost() string { + if x != nil && x.DstHost != nil { + return *x.DstHost + } + return "" +} + +type FabridKeyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EpochBegin *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=epoch_begin,json=epochBegin,proto3" json:"epoch_begin,omitempty"` + EpochEnd *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=epoch_end,json=epochEnd,proto3" json:"epoch_end,omitempty"` + Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *FabridKeyResponse) Reset() { + *x = FabridKeyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FabridKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FabridKeyResponse) ProtoMessage() {} + +func (x *FabridKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FabridKeyResponse.ProtoReflect.Descriptor instead. +func (*FabridKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_daemon_fabrid_proto_rawDescGZIP(), []int{3} +} + +func (x *FabridKeyResponse) GetEpochBegin() *timestamppb.Timestamp { + if x != nil { + return x.EpochBegin + } + return nil +} + +func (x *FabridKeyResponse) GetEpochEnd() *timestamppb.Timestamp { + if x != nil { + return x.EpochEnd + } + return nil +} + +func (x *FabridKeyResponse) GetKey() []byte { + if x != nil { + return x.Key + } + return nil +} + +type FabridKeysResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AsHostKeys []*FabridKeyResponse `protobuf:"bytes,1,rep,name=as_host_keys,json=asHostKeys,proto3" json:"as_host_keys,omitempty"` + HostHostKey *FabridKeyResponse `protobuf:"bytes,2,opt,name=host_host_key,json=hostHostKey,proto3,oneof" json:"host_host_key,omitempty"` +} + +func (x *FabridKeysResponse) Reset() { + *x = FabridKeysResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FabridKeysResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FabridKeysResponse) ProtoMessage() {} + +func (x *FabridKeysResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FabridKeysResponse.ProtoReflect.Descriptor instead. +func (*FabridKeysResponse) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_daemon_fabrid_proto_rawDescGZIP(), []int{4} +} + +func (x *FabridKeysResponse) GetAsHostKeys() []*FabridKeyResponse { + if x != nil { + return x.AsHostKeys + } + return nil +} + +func (x *FabridKeysResponse) GetHostHostKey() *FabridKeyResponse { + if x != nil { + return x.HostHostKey + } + return nil +} + +var File_proto_daemon_v1_daemon_fabrid_proto protoreflect.FileDescriptor + +var file_proto_daemon_v1_daemon_fabrid_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, + 0x31, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x64, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x62, + 0x72, 0x69, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x0a, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x22, 0x9b, 0x01, 0x0a, + 0x0c, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x68, 0x0a, + 0x11, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x65, + 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x41, 0x42, 0x52, 0x49, 0x44, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x10, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x8f, 0x01, 0x0a, 0x11, 0x46, + 0x61, 0x62, 0x72, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x72, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x64, + 0x73, 0x74, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x73, 0x74, + 0x41, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x61, 0x73, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x70, 0x61, 0x74, 0x68, 0x41, 0x73, 0x65, 0x73, 0x12, + 0x1e, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x07, 0x64, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x88, 0x01, 0x01, 0x42, + 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x9b, 0x01, 0x0a, + 0x11, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x67, 0x69, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, + 0x37, 0x0a, 0x09, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, + 0x65, 0x70, 0x6f, 0x63, 0x68, 0x45, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xb9, 0x01, 0x0a, 0x12, 0x46, + 0x61, 0x62, 0x72, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x61, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6b, 0x65, 0x79, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x61, 0x73, 0x48, + 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x5f, + 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x61, 0x62, 0x72, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, + 0x79, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_daemon_v1_daemon_fabrid_proto_rawDescOnce sync.Once + file_proto_daemon_v1_daemon_fabrid_proto_rawDescData = file_proto_daemon_v1_daemon_fabrid_proto_rawDesc +) + +func file_proto_daemon_v1_daemon_fabrid_proto_rawDescGZIP() []byte { + file_proto_daemon_v1_daemon_fabrid_proto_rawDescOnce.Do(func() { + file_proto_daemon_v1_daemon_fabrid_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_daemon_v1_daemon_fabrid_proto_rawDescData) + }) + return file_proto_daemon_v1_daemon_fabrid_proto_rawDescData +} + +var file_proto_daemon_v1_daemon_fabrid_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_proto_daemon_v1_daemon_fabrid_proto_goTypes = []interface{}{ + (*FabridInfo)(nil), // 0: proto.daemon.v1.FabridInfo + (*FabridPolicy)(nil), // 1: proto.daemon.v1.FabridPolicy + (*FabridKeysRequest)(nil), // 2: proto.daemon.v1.FabridKeysRequest + (*FabridKeyResponse)(nil), // 3: proto.daemon.v1.FabridKeyResponse + (*FabridKeysResponse)(nil), // 4: proto.daemon.v1.FabridKeysResponse + (*experimental.FABRIDPolicyIdentifier)(nil), // 5: proto.control_plane.experimental.v1.FABRIDPolicyIdentifier + (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp +} +var file_proto_daemon_v1_daemon_fabrid_proto_depIdxs = []int32{ + 1, // 0: proto.daemon.v1.FabridInfo.policies:type_name -> proto.daemon.v1.FabridPolicy + 5, // 1: proto.daemon.v1.FabridPolicy.policy_identifier:type_name -> proto.control_plane.experimental.v1.FABRIDPolicyIdentifier + 6, // 2: proto.daemon.v1.FabridKeyResponse.epoch_begin:type_name -> google.protobuf.Timestamp + 6, // 3: proto.daemon.v1.FabridKeyResponse.epoch_end:type_name -> google.protobuf.Timestamp + 3, // 4: proto.daemon.v1.FabridKeysResponse.as_host_keys:type_name -> proto.daemon.v1.FabridKeyResponse + 3, // 5: proto.daemon.v1.FabridKeysResponse.host_host_key:type_name -> proto.daemon.v1.FabridKeyResponse + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_proto_daemon_v1_daemon_fabrid_proto_init() } +func file_proto_daemon_v1_daemon_fabrid_proto_init() { + if File_proto_daemon_v1_daemon_fabrid_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FabridInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FabridPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FabridKeysRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FabridKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FabridKeysResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_proto_daemon_v1_daemon_fabrid_proto_msgTypes[4].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_daemon_v1_daemon_fabrid_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_daemon_v1_daemon_fabrid_proto_goTypes, + DependencyIndexes: file_proto_daemon_v1_daemon_fabrid_proto_depIdxs, + MessageInfos: file_proto_daemon_v1_daemon_fabrid_proto_msgTypes, + }.Build() + File_proto_daemon_v1_daemon_fabrid_proto = out.File + file_proto_daemon_v1_daemon_fabrid_proto_rawDesc = nil + file_proto_daemon_v1_daemon_fabrid_proto_goTypes = nil + file_proto_daemon_v1_daemon_fabrid_proto_depIdxs = nil +} diff --git a/pkg/proto/drkey/drkey.pb.go b/pkg/proto/drkey/drkey.pb.go index 3b31cb7307..52d98f0d50 100644 --- a/pkg/proto/drkey/drkey.pb.go +++ b/pkg/proto/drkey/drkey.pb.go @@ -25,6 +25,7 @@ type Protocol int32 const ( Protocol_PROTOCOL_GENERIC_UNSPECIFIED Protocol = 0 Protocol_PROTOCOL_SCMP Protocol = 1 + Protocol_PROTOCOL_FABRID Protocol = 2 ) // Enum value maps for Protocol. @@ -32,10 +33,12 @@ var ( Protocol_name = map[int32]string{ 0: "PROTOCOL_GENERIC_UNSPECIFIED", 1: "PROTOCOL_SCMP", + 2: "PROTOCOL_FABRID", } Protocol_value = map[string]int32{ "PROTOCOL_GENERIC_UNSPECIFIED": 0, "PROTOCOL_SCMP": 1, + "PROTOCOL_FABRID": 2, } ) @@ -71,15 +74,17 @@ var File_proto_drkey_v1_drkey_proto protoreflect.FileDescriptor var file_proto_drkey_v1_drkey_proto_rawDesc = []byte{ 0x0a, 0x1a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2a, 0x4b, 0x0a, 0x08, + 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2a, 0x60, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x49, 0x43, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x52, - 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x53, 0x43, 0x4d, 0x50, 0x10, 0x01, 0x22, 0x0a, 0x08, - 0x80, 0x80, 0x04, 0x10, 0xff, 0xff, 0xff, 0xff, 0x07, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x53, 0x43, 0x4d, 0x50, 0x10, 0x01, 0x12, 0x13, 0x0a, + 0x0f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x46, 0x41, 0x42, 0x52, 0x49, 0x44, + 0x10, 0x02, 0x22, 0x0a, 0x08, 0x80, 0x80, 0x04, 0x10, 0xff, 0xff, 0xff, 0xff, 0x07, 0x42, 0x2d, + 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, + 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/segment/BUILD.bazel b/pkg/segment/BUILD.bazel index c4185122dd..89d1ab38ce 100644 --- a/pkg/segment/BUILD.bazel +++ b/pkg/segment/BUILD.bazel @@ -20,10 +20,12 @@ go_library( "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", "//pkg/proto/control_plane:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", "//pkg/proto/crypto:go_default_library", "//pkg/scrypto/signed:go_default_library", "//pkg/segment/extensions/digest:go_default_library", "//pkg/segment/extensions/epic:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "//pkg/segment/extensions/staticinfo:go_default_library", "//pkg/slayers/path:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", @@ -49,6 +51,7 @@ go_test( "//pkg/scrypto/signed:go_default_library", "//pkg/segment/extensions/digest:go_default_library", "//pkg/segment/extensions/epic:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "//pkg/slayers/path:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", "@com_github_stretchr_testify//require:go_default_library", diff --git a/pkg/segment/extensions/digest/BUILD.bazel b/pkg/segment/extensions/digest/BUILD.bazel index a174a73076..8ad5c39fdd 100644 --- a/pkg/segment/extensions/digest/BUILD.bazel +++ b/pkg/segment/extensions/digest/BUILD.bazel @@ -17,6 +17,7 @@ go_test( deps = [ ":go_default_library", "//pkg/segment/extensions/epic:go_default_library", + "//pkg/segment/extensions/fabrid:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", ], ) diff --git a/pkg/segment/extensions/digest/digest.go b/pkg/segment/extensions/digest/digest.go index 1a41352029..700733b2ca 100644 --- a/pkg/segment/extensions/digest/digest.go +++ b/pkg/segment/extensions/digest/digest.go @@ -30,25 +30,36 @@ type Digest struct { const DigestLength = 16 type Extension struct { - // Epic dentoes the digest of the EpicDetachedExtension + // Epic denotes the digest of the EpicDetachedExtension Epic Digest + // Fabrid denotes the hash of both the supported indices map, as well as the + // index to identifier map. + Fabrid Digest } func ExtensionFromPB(d *cppb.DigestExtension) *Extension { if d == nil { return nil } + var epic Digest + var fabrid Digest if d.Epic == nil { - return &Extension{ - Epic: Digest{}, - } + epic = Digest{} + } else { + e := make([]byte, DigestLength) + copy(e, d.Epic.Digest) + epic = Digest{Digest: e} + } + if d.Fabrid == nil { + fabrid = Digest{} + } else { + e := make([]byte, DigestLength) + copy(e, d.Fabrid.Digest) + fabrid = Digest{Digest: e} } - e := make([]byte, DigestLength) - copy(e, d.Epic.Digest) return &Extension{ - Epic: Digest{ - Digest: e, - }, + Epic: epic, + Fabrid: fabrid, } } @@ -58,10 +69,16 @@ func ExtensionToPB(d *Extension) *cppb.DigestExtension { } e := make([]byte, DigestLength) copy(e, d.Epic.Digest) + + f := make([]byte, DigestLength) + copy(f, d.Fabrid.Digest) return &cppb.DigestExtension{ Epic: &cppb.DigestExtension_Digest{ Digest: e, }, + Fabrid: &cppb.DigestExtension_Digest{ + Digest: f, + }, } } diff --git a/pkg/segment/extensions/digest/digest_test.go b/pkg/segment/extensions/digest/digest_test.go index 2ad1d385b9..19f2fe077c 100644 --- a/pkg/segment/extensions/digest/digest_test.go +++ b/pkg/segment/extensions/digest/digest_test.go @@ -21,6 +21,7 @@ import ( "github.com/scionproto/scion/pkg/segment/extensions/digest" "github.com/scionproto/scion/pkg/segment/extensions/epic" + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" ) func TestDecodeEncode(t *testing.T) { @@ -34,6 +35,24 @@ func TestDecodeEncode(t *testing.T) { AuthHopEntry: hop, AuthPeerEntries: peers, } + fd := &fabrid.Detached{ + SupportedIndicesMap: fabrid.SupportedIndicesMap{ + fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.IPv4Range, + IP: "192.168.2.100", + Prefix: 22, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 44, + }}: []uint8{1}}, + IndexIdentiferMap: fabrid.IndexIdentifierMap{ + 1: &fabrid.PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }}, + } var d digest.Digest input, err := ed.DigestInput() @@ -42,8 +61,14 @@ func TestDecodeEncode(t *testing.T) { err = d.Validate(input) assert.NoError(t, err) + var fabridDigest digest.Digest + fabridDigest.Set(fd.Hash()) + err = fabridDigest.Validate(fd.Hash()) + assert.NoError(t, err) + dig := &digest.Extension{ - Epic: d, + Epic: d, + Fabrid: fabridDigest, } dig2 := digest.ExtensionFromPB(digest.ExtensionToPB(dig)) assert.Equal(t, dig, dig2) diff --git a/pkg/segment/extensions/fabrid/BUILD.bazel b/pkg/segment/extensions/fabrid/BUILD.bazel new file mode 100644 index 0000000000..8397738817 --- /dev/null +++ b/pkg/segment/extensions/fabrid/BUILD.bazel @@ -0,0 +1,22 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["fabrid.go"], + importpath = "github.com/scionproto/scion/pkg/segment/extensions/fabrid", + visibility = ["//visibility:public"], + deps = [ + "//pkg/proto/control_plane/experimental:go_default_library", + "//pkg/segment/extensions/digest:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["fabrid_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/proto/control_plane/experimental:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + ], +) diff --git a/pkg/segment/extensions/fabrid/fabrid.go b/pkg/segment/extensions/fabrid/fabrid.go new file mode 100644 index 0000000000..659b2627f5 --- /dev/null +++ b/pkg/segment/extensions/fabrid/fabrid.go @@ -0,0 +1,349 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import ( + "crypto/sha256" + "encoding/binary" + "fmt" + "net" + "sort" + + fabridpb "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + "github.com/scionproto/scion/pkg/segment/extensions/digest" +) + +type SupportedIndicesMap map[ConnectionPair][]uint8 + +type IndexIdentifierMap map[uint8]*PolicyIdentifier + +type Detached struct { + SupportedIndicesMap SupportedIndicesMap + IndexIdentiferMap IndexIdentifierMap +} + +func (ind *SupportedIndicesMap) SortedKeys() []ConnectionPair { + orderedKeysSupIndex := make([]ConnectionPair, 0, len(*ind)) + for k := range *ind { + orderedKeysSupIndex = append(orderedKeysSupIndex, k) + } + sort.Slice(orderedKeysSupIndex, func(i int, j int) bool { + if orderedKeysSupIndex[i].Ingress != orderedKeysSupIndex[j].Ingress { + return orderedKeysSupIndex[i].Ingress.Less(orderedKeysSupIndex[j].Ingress) + } + return orderedKeysSupIndex[i].Egress.Less(orderedKeysSupIndex[j].Egress) + }) + return orderedKeysSupIndex +} + +func (id *IndexIdentifierMap) SortedKeys() []uint8 { + orderedKeys := make([]uint8, 0, len(*id)) + for k := range *id { + orderedKeys = append(orderedKeys, k) + } + sort.Slice(orderedKeys, func(i int, j int) bool { + return orderedKeys[i] < orderedKeys[j] + }) + return orderedKeys +} + +type ConnectionPointType string + +const ( + IPv4Range ConnectionPointType = "ipv4" + IPv6Range ConnectionPointType = "ipv6" + Interface ConnectionPointType = "interface" + Wildcard ConnectionPointType = "wildcard" +) + +type ConnectionPoint struct { + Type ConnectionPointType + IP string // Stored as a string to allow it to be key of a map. + Prefix uint32 + InterfaceId uint16 +} + +// IPConnectionPointFromString parses a string IP address and Prefix, then masks the IP with the +// prefix and returns a ConnectionPoint with the parsed IP, type and prefix. +func IPConnectionPointFromString(IP string, Prefix uint32, + Type ConnectionPointType) ConnectionPoint { + var m net.IPMask + if Type == IPv4Range { + m = net.CIDRMask(int(Prefix), 8*net.IPv4len) + } else if Type == IPv6Range { + m = net.CIDRMask(int(Prefix), 8*net.IPv6len) + } + return ConnectionPoint{Type: Type, IP: net.ParseIP(IP).Mask(m).String(), Prefix: Prefix} +} + +func (c *ConnectionPoint) Less(d ConnectionPoint) bool { + if c.Type != d.Type { + return c.Type < d.Type + } + if c.IP != d.IP { + return c.IP < d.IP + } + if c.Prefix != d.Prefix { + return c.Prefix < d.Prefix + } + return c.InterfaceId < d.InterfaceId +} + +func (c *ConnectionPoint) IPNetwork() *net.IPNet { + if c.Type == IPv4Range { + m := net.CIDRMask(int(c.Prefix), 8*net.IPv4len) + return &net.IPNet{IP: net.ParseIP(c.IP).Mask(m), Mask: m} + } else if c.Type == IPv6Range { + m := net.CIDRMask(int(c.Prefix), 8*net.IPv6len) + return &net.IPNet{IP: net.ParseIP(c.IP).Mask(m), Mask: m} + } + return &net.IPNet{} +} +func (c *ConnectionPoint) MatchesIF(intf uint16) bool { + return (c.Type == Interface && c.InterfaceId == intf) || c.Type == Wildcard +} + +type ConnectionPair struct { + Ingress ConnectionPoint + Egress ConnectionPoint +} + +func (c *ConnectionPair) Matches(ingress, egress uint16, allowIpPolicies bool) bool { + match := c.Ingress.MatchesIF(ingress) && c.Egress.MatchesIF(egress) + if allowIpPolicies { + match = match || (c.Ingress.MatchesIF(ingress) && egress == 0 && + (c.Egress.Type == IPv4Range || c.Egress.Type == IPv6Range)) + } + return match +} + +type PolicyIdentifier struct { + IsLocal bool + Identifier uint32 +} + +func PolicyIdentifierToPB(identifier *PolicyIdentifier) *fabridpb.FABRIDPolicyIdentifier { + return &fabridpb.FABRIDPolicyIdentifier{ + PolicyIsLocal: identifier.IsLocal, + PolicyIdentifier: identifier.Identifier, + } +} + +func PolicyIdentifierFromPB(identifier *fabridpb.FABRIDPolicyIdentifier) *PolicyIdentifier { + return &PolicyIdentifier{ + IsLocal: identifier.PolicyIsLocal, + Identifier: identifier.PolicyIdentifier, + } +} + +func ConnectionPointToPB(point ConnectionPoint) *fabridpb.FABRIDConnectionPoint { + switch point.Type { + case IPv4Range: + return &fabridpb.FABRIDConnectionPoint{ + Type: fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV4_RANGE, + IpAddress: point.IPNetwork().IP, + IpPrefix: point.Prefix, + } + case IPv6Range: + return &fabridpb.FABRIDConnectionPoint{ + Type: fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV6_RANGE, + IpAddress: point.IPNetwork().IP, + IpPrefix: point.Prefix, + } + case Interface: + return &fabridpb.FABRIDConnectionPoint{ + Type: fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE, + Interface: uint64(point.InterfaceId), + } + case Wildcard: + return &fabridpb.FABRIDConnectionPoint{ + Type: fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_WILDCARD, + } + default: + return &fabridpb.FABRIDConnectionPoint{} + } +} + +func ConnectionPointFromPB(point *fabridpb.FABRIDConnectionPoint) ConnectionPoint { + switch point.Type { + case fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV4_RANGE: + return ConnectionPoint{ + Type: IPv4Range, + IP: net.IP(point.IpAddress).String(), + Prefix: point.IpPrefix, + } + case fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV6_RANGE: + return ConnectionPoint{ + Type: IPv6Range, + IP: net.IP(point.IpAddress).String(), + Prefix: point.IpPrefix, + } + case fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE: + return ConnectionPoint{ + Type: Interface, + InterfaceId: uint16(point.Interface), + } + case fabridpb.FABRIDConnectionType_FABRID_CONNECTION_TYPE_WILDCARD: + return ConnectionPoint{Type: Wildcard} + default: + return ConnectionPoint{} + } +} + +func SupportedIndicesMapToPB(indicesMap SupportedIndicesMap) []*fabridpb.FABRIDIndexMapEntry { + supIndices := make([]*fabridpb.FABRIDIndexMapEntry, 0) + for ie, indices := range indicesMap { + indicesu32 := make([]uint32, len(indices)) + for i, index := range indices { + indicesu32[i] = uint32(index) + } + supIndices = append(supIndices, &fabridpb.FABRIDIndexMapEntry{ + IePair: &fabridpb.FABRIDIngressEgressPair{ + Ingress: ConnectionPointToPB(ie.Ingress), + Egress: ConnectionPointToPB(ie.Egress), + }, + SupportedPolicyIndices: indicesu32, + }) + + } + return supIndices +} + +func SupportedIndicesMapFromPB(indicesMap []*fabridpb.FABRIDIndexMapEntry) SupportedIndicesMap { + supIndices := make(SupportedIndicesMap, 0) + for _, entry := range indicesMap { + indicesu8 := make([]uint8, len(entry.SupportedPolicyIndices)) + for i, index := range entry.SupportedPolicyIndices { + indicesu8[i] = uint8(index) + } + supIndices[ConnectionPair{ + Ingress: ConnectionPointFromPB(entry.IePair.Ingress), + Egress: ConnectionPointFromPB(entry.IePair.Egress), + }] = indicesu8 + + } + return supIndices +} + +func IndexIdentifierMapToPB(identifierMap IndexIdentifierMap) map[uint32]*fabridpb. + FABRIDPolicyIdentifier { + identMap := make(map[uint32]*fabridpb.FABRIDPolicyIdentifier, len(identifierMap)) + for index, identifier := range identifierMap { + identMap[uint32(index)] = PolicyIdentifierToPB(identifier) + } + return identMap +} + +func IndexIdentifierMapFromPB(identifierMap map[uint32]*fabridpb. + FABRIDPolicyIdentifier) IndexIdentifierMap { + identMap := make(IndexIdentifierMap, len(identifierMap)) + for index, identifier := range identifierMap { + identMap[uint8(index)] = PolicyIdentifierFromPB(identifier) + } + return identMap +} + +func DetachedToPB(detached *Detached) *fabridpb.FABRIDDetachedExtension { + if detached == nil { + return &fabridpb.FABRIDDetachedExtension{} + } + return &fabridpb.FABRIDDetachedExtension{ + Maps: &fabridpb.FABRIDDetachableMaps{ + SupportedIndicesMap: SupportedIndicesMapToPB(detached.SupportedIndicesMap), + IndexIdentifierMap: IndexIdentifierMapToPB(detached.IndexIdentiferMap), + }, + } +} + +func DetachedFromPB(detached *fabridpb.FABRIDDetachedExtension) *Detached { + if detached == nil || detached.Maps == nil { + return nil + } + return &Detached{ + SupportedIndicesMap: SupportedIndicesMapFromPB(detached.Maps.SupportedIndicesMap), + IndexIdentiferMap: IndexIdentifierMapFromPB(detached.Maps.IndexIdentifierMap), + } +} + +func (d *Detached) String() string { + base := " indexIdentifierMap: [" + for _, k := range d.IndexIdentiferMap.SortedKeys() { + base += fmt.Sprintf("{ index: %d, is_local: %t, identifier: %d }", k, + d.IndexIdentiferMap[k].IsLocal, d.IndexIdentiferMap[k].Identifier) + } + base += "], supportedIndicesMap: [" + + for _, k := range d.SupportedIndicesMap.SortedKeys() { + base += fmt.Sprintf("{ ingress: { type: %s ", k.Ingress.Type) + if k.Ingress.Type == Interface { + base += fmt.Sprintf("interfaceId: %d }", k.Ingress.InterfaceId) + } else if k.Ingress.Type == IPv4Range || k.Ingress.Type == IPv6Range { + base += fmt.Sprintf("ip: %s, prefix: %d }", k.Ingress.IP, k.Ingress.Prefix) + } else if k.Ingress.Type == Wildcard { + base += "wildcard }" + } + + base += fmt.Sprintf("} egress : { type: %s ", k.Egress.Type) + if k.Egress.Type == Interface { + base += fmt.Sprintf("interfaceId: %d } ", k.Egress.InterfaceId) + } else if k.Egress.Type == IPv4Range || k.Egress.Type == IPv6Range { + base += fmt.Sprintf("ip: %s, prefix: %d } ", k.Egress.IP, k.Egress.Prefix) + } else if k.Ingress.Type == Wildcard { + base += "wildcard }" + } + base += " supports: [" + supported := d.SupportedIndicesMap[k] + sort.Slice(supported, func(i int, j int) bool { + return supported[i] < supported[j] + }) + for _, z := range supported { + base += fmt.Sprintf("%d, ", z) + } + base += "] } " + } + return base +} + +func (d *Detached) Hash() []byte { + h := sha256.New() + for _, k := range d.IndexIdentiferMap.SortedKeys() { + _ = binary.Write(h, binary.BigEndian, k) + _ = binary.Write(h, binary.BigEndian, d.IndexIdentiferMap[k].IsLocal) + _ = binary.Write(h, binary.BigEndian, d.IndexIdentiferMap[k].Identifier) + } + for _, k := range d.SupportedIndicesMap.SortedKeys() { + _ = binary.Write(h, binary.BigEndian, k.Ingress.Type) + if k.Ingress.Type == Interface { + _ = binary.Write(h, binary.BigEndian, k.Ingress.InterfaceId) + } else if k.Ingress.Type == IPv4Range || k.Ingress.Type == IPv6Range { + _ = binary.Write(h, binary.BigEndian, k.Ingress.Prefix) + _ = binary.Write(h, binary.BigEndian, k.Ingress.IP) + } + + _ = binary.Write(h, binary.BigEndian, k.Egress.Type) + if k.Egress.Type == Interface { + _ = binary.Write(h, binary.BigEndian, k.Egress.InterfaceId) + } else if k.Egress.Type == IPv4Range || k.Egress.Type == IPv6Range { + _ = binary.Write(h, binary.BigEndian, k.Egress.Prefix) + _ = binary.Write(h, binary.BigEndian, k.Egress.IP) + } + supported := d.SupportedIndicesMap[k] + sort.Slice(supported, func(i int, j int) bool { + return supported[i] < supported[j] + }) + _ = binary.Write(h, binary.BigEndian, supported) + } + return h.Sum(nil)[0:digest.DigestLength] +} diff --git a/pkg/segment/extensions/fabrid/fabrid_test.go b/pkg/segment/extensions/fabrid/fabrid_test.go new file mode 100644 index 0000000000..1fa9a8b278 --- /dev/null +++ b/pkg/segment/extensions/fabrid/fabrid_test.go @@ -0,0 +1,598 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabrid + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" +) + +func TestSupportedIndicesSortedKeys(t *testing.T) { + connectionPairs := []ConnectionPair{ + { + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 25, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 25, + }, + }, + { + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 31, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 28, + }, + }, + { + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 11, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 25, + }, + }, + { + Ingress: ConnectionPoint{ + Type: IPv4Range, + Prefix: 24, + IP: "192.168.2.100", + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 2, + }, + }, + { + Ingress: ConnectionPoint{ + Type: IPv4Range, + Prefix: 24, + IP: "192.168.2.101", + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 2, + }, + }, + { + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 2, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + Prefix: 24, + IP: "192.168.2.101", + }, + }, + { + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 2, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + Prefix: 24, + IP: "192.168.2.101", + }, + }, + { + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 3, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + Prefix: 24, + IP: "192.168.2.101", + }, + }, + } + tests := map[string]struct { + Input SupportedIndicesMap + Expected []ConnectionPair + }{ + "base": { + Input: SupportedIndicesMap{ + connectionPairs[0]: []uint8{0}, + connectionPairs[1]: []uint8{1}, + connectionPairs[2]: []uint8{2}, + connectionPairs[3]: []uint8{3}, + connectionPairs[4]: []uint8{4}, + connectionPairs[5]: []uint8{5}, + connectionPairs[6]: []uint8{5}, // 6 is a duplicate of 5. + connectionPairs[7]: []uint8{7}, + }, + }, + "reversed": { + Input: SupportedIndicesMap{ + connectionPairs[7]: []uint8{7}, + connectionPairs[6]: []uint8{5}, // 6 is a duplicate of 5. + connectionPairs[5]: []uint8{5}, + connectionPairs[4]: []uint8{4}, + connectionPairs[3]: []uint8{3}, + connectionPairs[2]: []uint8{2}, + connectionPairs[1]: []uint8{1}, + connectionPairs[0]: []uint8{0}, + }, + }, + "random": { + Input: SupportedIndicesMap{ + connectionPairs[4]: []uint8{4}, + connectionPairs[2]: []uint8{2}, + connectionPairs[6]: []uint8{5}, // 6 is a duplicate of 5. + connectionPairs[1]: []uint8{1}, + connectionPairs[3]: []uint8{3}, + connectionPairs[7]: []uint8{7}, + connectionPairs[0]: []uint8{0}, + connectionPairs[5]: []uint8{5}, + }, + }, + "random-duplicates": { + Input: SupportedIndicesMap{ + connectionPairs[2]: []uint8{2}, + connectionPairs[4]: []uint8{4}, + connectionPairs[2]: []uint8{2}, + connectionPairs[3]: []uint8{3}, + connectionPairs[6]: []uint8{5}, // 6 is a duplicate of 5. + connectionPairs[1]: []uint8{1}, + connectionPairs[3]: []uint8{3}, + connectionPairs[7]: []uint8{7}, + connectionPairs[3]: []uint8{3}, + connectionPairs[0]: []uint8{0}, + connectionPairs[5]: []uint8{5}, + }, + }, + "random-duplicates-copy": { + Input: SupportedIndicesMap{ + connectionPairs[7]: []uint8{7}, + connectionPairs[4]: []uint8{4}, + connectionPairs[2]: []uint8{2}, + connectionPairs[3]: []uint8{3}, + connectionPairs[6]: []uint8{5}, // 6 is a duplicate of 5. + connectionPairs[0]: []uint8{0}, + connectionPairs[2]: []uint8{2}, + connectionPairs[1]: []uint8{1}, + connectionPairs[5]: []uint8{5}, + connectionPairs[3]: []uint8{3}, + connectionPairs[5]: []uint8{5}, + connectionPairs[3]: []uint8{3}, + }, + }, + } + input := tests["base"].Input + baseSorted := input.SortedKeys() + baseOrder := make([]uint8, 0, len(tests["base"].Input)) + for _, connectionPair := range baseSorted { + fmt.Println(tests["base"].Input[connectionPair][0]) + baseOrder = append(baseOrder, tests["base"].Input[connectionPair][0]) + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + order := make([]uint8, 0, len(tc.Input)) + for _, connectionPair := range tc.Input.SortedKeys() { + order = append(order, tc.Input[connectionPair][0]) + } + assert.Equal(t, baseOrder, order) + }) + } +} + +func TestConnectionPointFromString(t *testing.T) { + type TestCase struct { + IP string + Prefix uint32 + Type ConnectionPointType + } + tests := map[string]struct { + Eq1 TestCase + Eq2 TestCase + }{ + "ipv4": {TestCase{ + IP: "192.168.2.101", + Prefix: 24, + Type: IPv4Range, + }, TestCase{ + IP: "192.168.2.100", + Prefix: 24, + Type: IPv4Range, + }}, + "ipv4-2": {TestCase{ + IP: "192.168.2.101", + Prefix: 24, + Type: IPv4Range, + }, TestCase{ + IP: "192.168.2.100", + Prefix: 24, + Type: IPv4Range, + }}, + "ipv4-3": {TestCase{ + IP: "192.168.3.155", + Prefix: 16, + Type: IPv4Range, + }, TestCase{ + IP: "192.168.2.100", + Prefix: 16, + Type: IPv4Range, + }}, + "ipv4-4": {TestCase{ + IP: "192.168.3.101", + Prefix: 2, + Type: IPv4Range, + }, TestCase{ + IP: "192.168.3.102", + Prefix: 2, + Type: IPv4Range, + }}, + "ipv6-1": {TestCase{ + IP: "2001:0db8:85a3::8a2e:0370:7334", + Prefix: 1, + Type: IPv6Range, + }, TestCase{ + IP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + Prefix: 1, + Type: IPv6Range, + }}, + "ipv6-2": {TestCase{ + IP: "2001:0db8:85a3::8a2e:0370:7338", + Prefix: 24, + Type: IPv6Range, + }, TestCase{ + IP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + Prefix: 24, + Type: IPv6Range, + }}, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(t, IPConnectionPointFromString(tc.Eq1.IP, tc.Eq1.Prefix, tc.Eq1.Type).IP, + IPConnectionPointFromString(tc.Eq2.IP, tc.Eq2.Prefix, tc.Eq2.Type).IP) + }) + } +} + +func TestDetachedToFromPB(t *testing.T) { + tests := map[string]struct { + Expected *Detached + Input *experimental.FABRIDDetachedExtension + }{ + "nil": {}, + "index-identifiers only": { + Expected: &Detached{ + SupportedIndicesMap: SupportedIndicesMap{}, + IndexIdentiferMap: IndexIdentifierMap{ + 2: &PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + }, + }, + Input: &experimental.FABRIDDetachedExtension{Maps: &experimental. + FABRIDDetachableMaps{ + IndexIdentifierMap: map[uint32]*experimental.FABRIDPolicyIdentifier{ + 2: { + PolicyIsLocal: false, + PolicyIdentifier: 22, + }, + 8: { + PolicyIsLocal: true, + PolicyIdentifier: 1, + }, + 15: { + PolicyIsLocal: false, + PolicyIdentifier: 50, + }, + }, + }}, + }, + "index-identifiers and supported indices": { + Expected: &Detached{ + SupportedIndicesMap: SupportedIndicesMap{ + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 15}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 6, + }, + }: []uint8{2, 15}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 9, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.55.0", + Prefix: 24, + }, + }: []uint8{2, 15}, + }, + IndexIdentiferMap: IndexIdentifierMap{ + 2: &PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + }, + }, + Input: &experimental.FABRIDDetachedExtension{Maps: &experimental. + FABRIDDetachableMaps{ + SupportedIndicesMap: []*experimental.FABRIDIndexMapEntry{ + { + IePair: &experimental.FABRIDIngressEgressPair{ + Ingress: &experimental.FABRIDConnectionPoint{ + Type: experimental. + FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE, + Interface: 9, + }, + Egress: &experimental.FABRIDConnectionPoint{ + Type: experimental. + FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV4_RANGE, + IpAddress: []byte{192, 168, 55, 0}, + IpPrefix: 24, + }, + }, + SupportedPolicyIndices: []uint32{2, 15}, + }, { + IePair: &experimental.FABRIDIngressEgressPair{ + Ingress: &experimental.FABRIDConnectionPoint{ + Type: experimental. + FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE, + Interface: 5, + }, + Egress: &experimental.FABRIDConnectionPoint{ + Type: experimental. + FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE, + Interface: 6, + }, + }, + SupportedPolicyIndices: []uint32{2, 15}, + }, { + IePair: &experimental.FABRIDIngressEgressPair{ + Ingress: &experimental.FABRIDConnectionPoint{ + Type: experimental. + FABRIDConnectionType_FABRID_CONNECTION_TYPE_IPV4_RANGE, + IpAddress: []byte{192, 168, 2, 0}, + IpPrefix: 24, + }, + Egress: &experimental.FABRIDConnectionPoint{ + Type: experimental. + FABRIDConnectionType_FABRID_CONNECTION_TYPE_INTERFACE, + Interface: 5, + }, + }, + SupportedPolicyIndices: []uint32{2, 8, 15}, + }, + }, + IndexIdentifierMap: map[uint32]*experimental.FABRIDPolicyIdentifier{ + 2: { + PolicyIsLocal: false, + PolicyIdentifier: 22, + }, + 8: { + PolicyIsLocal: true, + PolicyIdentifier: 1, + }, + 15: { + PolicyIsLocal: false, + PolicyIdentifier: 50, + }, + }, + }}, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.Expected, DetachedFromPB(tc.Input)) + assert.Equal(t, tc.Expected, DetachedFromPB(DetachedToPB(tc.Expected))) + }) + } + +} +func TestHash(t *testing.T) { + eq1 := &Detached{ + SupportedIndicesMap: SupportedIndicesMap{ + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 15}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 6, + }, + }: []uint8{2, 15}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 9, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.55.0", + Prefix: 24, + }, + }: []uint8{2, 15}, + }, + IndexIdentiferMap: IndexIdentifierMap{ + 2: &PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + 8: &PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 15: &PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + }, + } + eq2 := &Detached{ + SupportedIndicesMap: SupportedIndicesMap{ + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 9, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.55.0", + Prefix: 24, + }, + }: []uint8{15, 2}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 6, + }, + }: []uint8{15, 2}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + }: []uint8{15, 8, 2}, + }, + IndexIdentiferMap: IndexIdentifierMap{ + 15: &PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + 8: &PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 2: &PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + }, + } + eq3 := &Detached{ + SupportedIndicesMap: SupportedIndicesMap{ + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 9, + }, + Egress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.55.0", + Prefix: 24, + }, + }: []uint8{2, 15}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 6, + }, + }: []uint8{2, 15}, + ConnectionPair{ + Ingress: ConnectionPoint{ + Type: IPv4Range, + IP: "192.168.2.0", + Prefix: 24, + }, + Egress: ConnectionPoint{ + Type: Interface, + InterfaceId: 5, + }, + }: []uint8{2, 8, 1}, // Difference to eq2. + }, + IndexIdentiferMap: IndexIdentifierMap{ + 15: &PolicyIdentifier{ + IsLocal: false, + Identifier: 50, + }, + 8: &PolicyIdentifier{ + IsLocal: true, + Identifier: 1, + }, + 2: &PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }, + }, + } + assert.Equal(t, eq1.Hash(), eq2.Hash()) + assert.NotEqual(t, eq1.Hash(), eq3.Hash()) +} diff --git a/pkg/segment/extensions_test.go b/pkg/segment/extensions_test.go index 48b10a4651..d1f3d15549 100644 --- a/pkg/segment/extensions_test.go +++ b/pkg/segment/extensions_test.go @@ -24,10 +24,14 @@ import ( func TestDecodeEncodeEpicDigest(t *testing.T) { h := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + h2 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} dig := &digest.Extension{ Epic: digest.Digest{ Digest: h, }, + Fabrid: digest.Digest{ + Digest: h2, + }, } ext := Extensions{ Digests: dig, diff --git a/pkg/segment/unsigned.go b/pkg/segment/unsigned.go index 79c67c7860..137284904a 100644 --- a/pkg/segment/unsigned.go +++ b/pkg/segment/unsigned.go @@ -15,15 +15,22 @@ package segment import ( + "bytes" + "encoding/hex" + "github.com/scionproto/scion/pkg/private/serrors" cppb "github.com/scionproto/scion/pkg/proto/control_plane" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" "github.com/scionproto/scion/pkg/segment/extensions/epic" + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" ) type UnsignedExtensions struct { // EpicDetached contains the detachable epic authenticators. It is nil // if it was detached (or never added). EpicDetached *epic.Detached + // FabridDetached contains the detachable fabrid maps. It is nil if it was detached + FabridDetached *fabrid.Detached } func UnsignedExtensionsFromPB(ue *cppb.PathSegmentUnsignedExtensions) UnsignedExtensions { @@ -31,16 +38,30 @@ func UnsignedExtensionsFromPB(ue *cppb.PathSegmentUnsignedExtensions) UnsignedEx return UnsignedExtensions{} } return UnsignedExtensions{ - EpicDetached: epic.DetachedFromPB(ue.Epic), + EpicDetached: epic.DetachedFromPB(ue.Epic), + FabridDetached: fabrid.DetachedFromPB(ue.Fabrid), } } func UnsignedExtensionsToPB(ue UnsignedExtensions) *cppb.PathSegmentUnsignedExtensions { + var e *experimental.EPICDetachedExtension + var f *experimental.FABRIDDetachedExtension + if ue.EpicDetached == nil { - return nil + e = nil + } else { + e = epic.DetachedToPB(ue.EpicDetached) } + + if ue.FabridDetached == nil { + f = nil + } else { + f = fabrid.DetachedToPB(ue.FabridDetached) + } + return &cppb.PathSegmentUnsignedExtensions{ - Epic: epic.DetachedToPB(ue.EpicDetached), + Epic: e, + Fabrid: f, } } @@ -71,5 +92,27 @@ func checkUnsignedExtensions(ue *UnsignedExtensions, e *Extensions) error { return err } } + + if ue.FabridDetached != nil { + hasSupportedIndices := len(ue.FabridDetached.SupportedIndicesMap) > 0 + hasIndexIdentifiers := len(ue.FabridDetached.IndexIdentiferMap) > 0 + fabridDigest := e.Digests != nil && len(e.Digests.Fabrid.Digest) != 0 + if hasSupportedIndices && !hasIndexIdentifiers { + // An AS may announce index->identifiers that are not used in the supported indices map. + //However, announcing supported policy indices without specifying the identifiers is + //invalid. + return serrors.New("fabrid maps are malformed") + } + + if fabridDigest { + if digest := ue.FabridDetached.Hash(); !bytes.Equal(e.Digests.Fabrid.Digest, digest) { + return serrors.New("fabrid digest validation failed", + "calculated", hex.EncodeToString(e.Digests.Fabrid.Digest), + "stored", hex.EncodeToString(digest)) + } + } else { + return serrors.New("fabrid maps present, but hash is not") + } + } return nil } diff --git a/pkg/segment/unsigned_test.go b/pkg/segment/unsigned_test.go index 9d67a63466..931e6347f2 100644 --- a/pkg/segment/unsigned_test.go +++ b/pkg/segment/unsigned_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/scionproto/scion/pkg/segment/extensions/epic" + "github.com/scionproto/scion/pkg/segment/extensions/fabrid" ) func TestDecodeEncode(t *testing.T) { @@ -33,9 +34,28 @@ func TestDecodeEncode(t *testing.T) { AuthHopEntry: hop, AuthPeerEntries: peers, } + fd := &fabrid.Detached{ + SupportedIndicesMap: fabrid.SupportedIndicesMap{ + fabrid.ConnectionPair{ + Ingress: fabrid.ConnectionPoint{ + Type: fabrid.IPv4Range, + IP: "192.168.0.0", + Prefix: 22, + }, + Egress: fabrid.ConnectionPoint{ + Type: fabrid.Interface, + InterfaceId: 44, + }}: []uint8{1}}, + IndexIdentiferMap: fabrid.IndexIdentifierMap{ + 1: &fabrid.PolicyIdentifier{ + IsLocal: false, + Identifier: 22, + }}, + } ue := UnsignedExtensions{ - EpicDetached: ed, + EpicDetached: ed, + FabridDetached: fd, } ue2 := UnsignedExtensionsFromPB( UnsignedExtensionsToPB(ue)) diff --git a/pkg/slayers/extension/BUILD.bazel b/pkg/slayers/extension/BUILD.bazel new file mode 100644 index 0000000000..8c0cb605be --- /dev/null +++ b/pkg/slayers/extension/BUILD.bazel @@ -0,0 +1,28 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "fabrid.go", + "identifier.go", + ], + importpath = "github.com/scionproto/scion/pkg/slayers/extension", + visibility = ["//visibility:public"], + deps = [ + "//pkg/private/serrors:go_default_library", + "//pkg/slayers:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "fabrid_test.go", + "identifier_test.go", + ], + deps = [ + ":go_default_library", + "//pkg/slayers:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + ], +) diff --git a/pkg/slayers/extension/fabrid.go b/pkg/slayers/extension/fabrid.go new file mode 100644 index 0000000000..5cfd2849ba --- /dev/null +++ b/pkg/slayers/extension/fabrid.go @@ -0,0 +1,207 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +// The FABRID option format is as follows: +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NextHdr | ExtLen | OptType = 4 | OptLen | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Enc PolicyID |F|A| Hop Validation Field | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Enc PolicyID |F|A| Hop Validation Field | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | .... | | | .... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Enc PolicyID |F|A| Hop Validation Field | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Path Validator | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +package extension + +import ( + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" +) + +const baseFabridLen uint8 = 4 +const FabridMetadataLen int = 4 +const MaxSupportedFabridHops = uint8(62) + +// The FABRID option requires the Identifier option to be present in the HBH header +// extension and defined before the FABRID option. +type FabridOption struct { + HopfieldMetadata []*FabridHopfieldMetadata + PathValidator [4]byte +} + +type FabridHopfieldMetadata struct { + EncryptedPolicyID uint8 + FabridEnabled bool + ASLevelKey bool + HopValidationField [3]byte +} + +func (f *FabridHopfieldMetadata) DecodeFabridHopfieldMetadata(b []byte) error { + if len(b) < FabridMetadataLen { + return serrors.New("Buffer too small to decode metadata", + "is", len(b), "expected", FabridMetadataLen) + } + f.decodeFabridHopfieldMetadata(b) + return nil +} + +func (f *FabridHopfieldMetadata) decodeFabridHopfieldMetadata(b []byte) { + f.EncryptedPolicyID = b[0] + copy(f.HopValidationField[:], b[1:4]) + if b[1]&0x80 > 0 { + f.FabridEnabled = true + if b[1]&0x40 > 0 { + f.ASLevelKey = true + } + } + f.HopValidationField[0] &= 0x3f +} + +func (f *FabridHopfieldMetadata) SerializeTo(b []byte) error { + if len(b) < FabridMetadataLen { + return serrors.New("Buffer too small to serialize metadata", + "is", len(b), "expected", FabridMetadataLen) + } + f.serializeTo(b) + return nil +} + +func (f *FabridHopfieldMetadata) serializeTo(b []byte) { + b[0] = f.EncryptedPolicyID + copy(b[1:4], f.HopValidationField[:]) + b[1] &= 0x3f // clear the first two (left) bits of the HVF + if f.FabridEnabled { + b[1] |= 0x80 + if f.ASLevelKey { + b[1] |= 0x40 + } + } +} + +func (f *FabridOption) validate(b []byte, currHf uint8, numHfs uint8) error { + if f == nil { + return serrors.New("Fabrid option must not be nil") + } + if len(b) < int(FabridOptionLen(numHfs)) { + return serrors.New("Raw Fabrid option too short", "is", len(b), + "expected", FabridOptionLen(numHfs)) + } + if numHfs > MaxSupportedFabridHops { + // The size of FABRID is limited to 255 bytes because of the HBH option length field + // 4 bytes + 62 * 4 bytes = 252 bytes + return serrors.New("Fabrid is not supported for paths consisting of more than 62 hopfields") + } + if currHf >= numHfs { + return serrors.New("Current HF is >= the number of HFs", "current HF", + currHf, "num hops", numHfs) + } + return nil +} + +// DecodeForHF decodes only the metadata of the current hop and stores it in f.HopfieldMetadata[0]. +// The PathValidator will not be decoded. +func (f *FabridOption) DecodeForHF(b []byte, currHf uint8, numHfs uint8) error { + if err := f.validate(b, currHf, numHfs); err != nil { + return err + } + byteIndex := int(currHf) * FabridMetadataLen + md := &FabridHopfieldMetadata{} + md.decodeFabridHopfieldMetadata(b[byteIndex : byteIndex+FabridMetadataLen]) + f.HopfieldMetadata = []*FabridHopfieldMetadata{ + md, + } + return nil +} + +// DecodeFull decodes the full FABRID extension including the PathValidator. +func (f *FabridOption) DecodeFull(b []byte, numHfs uint8) error { + if err := f.validate(b, 0, numHfs); err != nil { + return err + } + byteIndex := 0 + f.HopfieldMetadata = make([]*FabridHopfieldMetadata, numHfs) + for i := 0; i < int(numHfs); i++ { + md := &FabridHopfieldMetadata{} + md.decodeFabridHopfieldMetadata(b[byteIndex : byteIndex+FabridMetadataLen]) + f.HopfieldMetadata[i] = md + byteIndex += FabridMetadataLen + } + copy(f.PathValidator[:], b[byteIndex:byteIndex+4]) + return nil +} + +func (f *FabridOption) SerializeTo(b []byte) error { + if f == nil { + return serrors.New("Fabrid option must not be nil") + } + if len(b) < int(FabridOptionLen(uint8(len(f.HopfieldMetadata)))) { + return serrors.New("Buffer too short", "is", len(b), + "expected", FabridOptionLen(uint8(len(f.HopfieldMetadata)))) + } + if len(f.HopfieldMetadata) > int(MaxSupportedFabridHops) { + // The size of FABRID is limited to 255 bytes because of the HBH option length field + // 4 bytes + 62 * 4 bytes = 252 bytes + return serrors.New("Fabrid is not supported for paths consisting of more than 62 hopfields") + } + byteIndex := 0 + for _, md := range f.HopfieldMetadata { + md.serializeTo(b[byteIndex : byteIndex+4]) + byteIndex += 4 + } + copy(b[byteIndex:byteIndex+4], f.PathValidator[:]) + return nil +} + +// FabridOptionLen returns the number of bytes it takes to store the FABRID HBH option consisting +// of a certain number of hopfields. +func FabridOptionLen(numHopfields uint8) uint8 { + return baseFabridLen + numHopfields*uint8(FabridMetadataLen) +} + +// ParseFabridOptionFullExtension parses the full FABRID HBH extension including the PathValidator. +func ParseFabridOptionFullExtension(o *slayers.HopByHopOption, numHfs uint8) ( + *FabridOption, error) { + + if o.OptType != slayers.OptTypeFabrid { + return nil, + serrors.New("Wrong option type", "expected", + slayers.OptTypeFabrid, "actual", o.OptType) + } + f := &FabridOption{} + if err := f.DecodeFull(o.OptData, numHfs); err != nil { + return nil, err + } + return f, nil +} + +// ParseFabridOptionCurrentHop parses only the metadata of the current hop and stores it in +// f.HopfieldMetadata[0]. The PathValidator will not be decoded. +func ParseFabridOptionCurrentHop(o *slayers.HopByHopOption, currHf uint8, numHfs uint8) ( + *FabridOption, error) { + + if o.OptType != slayers.OptTypeFabrid { + return nil, + serrors.New("Wrong option type", "expected", slayers.OptTypeFabrid, "actual", o.OptType) + } + f := &FabridOption{} + if err := f.DecodeForHF(o.OptData, currHf, numHfs); err != nil { + return nil, err + } + return f, nil +} diff --git a/pkg/slayers/extension/fabrid_test.go b/pkg/slayers/extension/fabrid_test.go new file mode 100644 index 0000000000..f86a21328e --- /dev/null +++ b/pkg/slayers/extension/fabrid_test.go @@ -0,0 +1,209 @@ +// Copyright 2024 ETH Zurich +// +// 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 extension_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" +) + +func TestFabridDecode(t *testing.T) { + type test struct { + name string + o *slayers.HopByHopOption + currHf uint8 + numHfs uint8 + validate func(*extension.FabridOption, error, *testing.T) + } + tests := []test{ + { + name: "wrong option type", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeIdentifier, + OptData: make([]byte, 8), + }, + currHf: 1, + numHfs: 1, + validate: func(fo *extension.FabridOption, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "Raw fabrid too short", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeFabrid, + OptData: make([]byte, 11), + }, + currHf: 1, + numHfs: 2, + validate: func(fo *extension.FabridOption, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "Raw fabrid parses with 62 HF", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeFabrid, + OptData: make([]byte, 252), + }, + currHf: 1, + numHfs: extension.MaxSupportedFabridHops, + validate: func(fo *extension.FabridOption, err error, t *testing.T) { + assert.NoError(t, err) + }, + }, + { + name: "Raw fabrid fails parsing 63 HF", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeFabrid, + OptData: make([]byte, 1000), + }, + currHf: 1, + numHfs: extension.MaxSupportedFabridHops + 1, + validate: func(fo *extension.FabridOption, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "Parses fabrid correctly", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeFabrid, + OptData: []byte{ + 0x66, 0x37, 0x88, 0x99, + 0xaa, 0x8b, 0xcc, 0xdd, + 0xaa, 0xc1, 0x01, 0x01, + 0x22, 0x33, 0x44, 0x55, + }, + }, + currHf: 1, + numHfs: 3, + validate: func(fo *extension.FabridOption, err error, t *testing.T) { + assert.NoError(t, err) + assert.Equal(t, 3, len(fo.HopfieldMetadata)) + assert.Equal(t, uint8(0x66), fo.HopfieldMetadata[0].EncryptedPolicyID) + assert.Equal(t, [3]byte{0x37, 0x88, 0x99}, + fo.HopfieldMetadata[0].HopValidationField) + assert.Equal(t, false, fo.HopfieldMetadata[0].FabridEnabled) + assert.Equal(t, false, fo.HopfieldMetadata[0].ASLevelKey) + assert.Equal(t, uint8(0xaa), fo.HopfieldMetadata[1].EncryptedPolicyID) + assert.Equal(t, [3]byte{0x0b, 0xcc, 0xdd}, + fo.HopfieldMetadata[1].HopValidationField) + assert.Equal(t, true, fo.HopfieldMetadata[1].FabridEnabled) + assert.Equal(t, false, fo.HopfieldMetadata[1].ASLevelKey) + assert.Equal(t, uint8(0xaa), fo.HopfieldMetadata[2].EncryptedPolicyID) + assert.Equal(t, [3]byte{0x01, 0x01, 0x01}, + fo.HopfieldMetadata[2].HopValidationField) + assert.Equal(t, true, fo.HopfieldMetadata[2].FabridEnabled) + assert.Equal(t, true, fo.HopfieldMetadata[2].ASLevelKey) + assert.Equal(t, [4]byte{0x22, 0x33, 0x44, 0x55}, fo.PathValidator) + }, + }, + } + + for _, tc := range tests { + func(tc test) { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + f, err := extension.ParseFabridOptionFullExtension(tc.o, tc.numHfs) + tc.validate(f, err, t) + }) + }(tc) + } +} + +func TestFabridSerialize(t *testing.T) { + type test struct { + name string + fabrid *extension.FabridOption + buffer []byte + validate func([]byte, error, *testing.T) + } + + tests := []test{ + { + name: "Fabrid option is nil", + fabrid: nil, + validate: func(b []byte, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "Buffer too small", + fabrid: &extension.FabridOption{ + HopfieldMetadata: make([]*extension.FabridHopfieldMetadata, 1), + }, + buffer: make([]byte, 7), + validate: func(b []byte, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "Too many hops", + fabrid: &extension.FabridOption{ + HopfieldMetadata: make([]*extension.FabridHopfieldMetadata, + extension.MaxSupportedFabridHops+1), + }, + buffer: make([]byte, 256), + validate: func(b []byte, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "Fabrid serializes correctly", + fabrid: &extension.FabridOption{ + HopfieldMetadata: []*extension.FabridHopfieldMetadata{ + { + EncryptedPolicyID: 0x11, + HopValidationField: [3]byte{0x22, 0x33, 0x44}, + }, + { + EncryptedPolicyID: 0xaa, + FabridEnabled: true, + HopValidationField: [3]byte{0x0b, 0xcc, 0xdd}, + }, + { + EncryptedPolicyID: 0xaa, + FabridEnabled: true, + ASLevelKey: true, + HopValidationField: [3]byte{0x01, 0x01, 0x01}, + }, + }, + PathValidator: [4]byte{0x11, 0x22, 0x33, 0x44}, + }, + buffer: make([]byte, 16), + validate: func(b []byte, err error, t *testing.T) { + assert.NoError(t, err) + assert.Equal(t, []byte{0x11, 0x22, 0x33, 0x44}, b[0:4]) //HF1 without F or A + assert.Equal(t, []byte{0xaa, 0x8b, 0xcc, 0xdd}, b[4:8]) //HF2 with F without A + assert.Equal(t, []byte{0xaa, 0xc1, 0x01, 0x01}, b[8:12]) //HF3 with F and A + assert.Equal(t, []byte{0x11, 0x22, 0x33, 0x44}, b[12:16]) //Path validator + }, + }, + } + + for _, tc := range tests { + func(tc test) { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := tc.fabrid.SerializeTo(tc.buffer) + tc.validate(tc.buffer, err, t) + }) + }(tc) + } +} diff --git a/pkg/slayers/extension/identifier.go b/pkg/slayers/extension/identifier.go new file mode 100644 index 0000000000..a018315e39 --- /dev/null +++ b/pkg/slayers/extension/identifier.go @@ -0,0 +1,99 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +// The Identifier option format is as follows: +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NextHdr | ExtLen | OptType = 3 | OptLen = 8 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | R R R R R | Timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Packet ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +package extension + +import ( + "encoding/binary" + "time" + + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" +) + +const identifierLength int = 8 + +type IdentifierOption struct { + // Timestamp with 1 ms precision + Timestamp time.Time + // The packet ID + PacketID uint32 + // The base timestamp. Usually the timestamp of the first info field. + BaseTimestamp uint32 +} + +// GetRelativeTimestamp returns the time difference between id.Timestamp and id.BaseTimestamp +// as milliseconds. +func (id *IdentifierOption) GetRelativeTimestamp() uint32 { + return uint32(id.Timestamp.UnixMilli()-int64(id.BaseTimestamp)*1000) & 0x7FFFFFF +} + +func (id *IdentifierOption) decodeTimestampFromBytes(b []byte) { + relativeTimestamp := binary.BigEndian.Uint32(b) & 0x7FFFFFF // take only the right 27bit + ts := uint64(relativeTimestamp) + 1000*uint64(id.BaseTimestamp) + id.Timestamp = time.Unix(0, int64(time.Millisecond)*int64(ts)) +} + +func (id *IdentifierOption) serializeTimestampTo(b []byte) { + binary.BigEndian.PutUint32(b, id.GetRelativeTimestamp()) +} + +func (id *IdentifierOption) decode(b []byte) error { + if len(b) < identifierLength { + return serrors.New("raw data too short", "expected", identifierLength, "actual", len(b)) + } + id.decodeTimestampFromBytes(b[0:4]) + id.PacketID = binary.BigEndian.Uint32(b[4:8]) + return nil +} + +// Serialize writes the packet ID and the relative timestamp to a byte slice. +// Requires that id.Timestamp, id.PacketID and id.BaseTimestamp are set accordingly. +func (id *IdentifierOption) Serialize(b []byte) error { + if id == nil { + return serrors.New("identifier option must not be nil") + } + if len(b) < identifierLength { + return serrors.New("buffer too short", "expected", identifierLength, "actual", len(b)) + } + id.serializeTimestampTo(b[0:4]) + binary.BigEndian.PutUint32(b[4:8], id.PacketID) + return nil +} + +// ParseIdentifierOption parses the Identifier HBH extension. +func ParseIdentifierOption(o *slayers.HopByHopOption, baseTimestamp uint32) ( + *IdentifierOption, error) { + if o.OptType != slayers.OptTypeIdentifier { + return nil, + serrors.New("Wrong option type", "expected", slayers.OptTypeIdentifier, + "actual", o.OptType) + } + identifier := &IdentifierOption{ + BaseTimestamp: baseTimestamp, + } + if err := identifier.decode(o.OptData); err != nil { + return nil, err + } + return identifier, nil +} diff --git a/pkg/slayers/extension/identifier_test.go b/pkg/slayers/extension/identifier_test.go new file mode 100644 index 0000000000..477c6ccc74 --- /dev/null +++ b/pkg/slayers/extension/identifier_test.go @@ -0,0 +1,142 @@ +// Copyright 2024 ETH Zurich +// +// 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 extension_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" +) + +func TestIdentifierDecode(t *testing.T) { + type test struct { + name string + o *slayers.HopByHopOption + baseTimestamp uint32 + validate func(*extension.IdentifierOption, error, *testing.T) + } + unixNow := uint32(time.Now().Unix()) + tests := []test{ + { + name: "wrong option type", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeFabrid, + OptData: []byte{ + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + }, + }, + baseTimestamp: unixNow, + validate: func(id *extension.IdentifierOption, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "raw data too short", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeIdentifier, + OptData: []byte{ + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + }, + }, + baseTimestamp: unixNow, + validate: func(id *extension.IdentifierOption, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "correct timestamp", + o: &slayers.HopByHopOption{ + OptType: slayers.OptTypeIdentifier, + OptData: []byte{ + 0xff, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x03, 0x04, + }, + }, + baseTimestamp: unixNow, + validate: func(id *extension.IdentifierOption, err error, t *testing.T) { + assert.NoError(t, err) + expectedTime := int64(unixNow)*1000 + 0x7000001 + assert.Equal(t, expectedTime, id.Timestamp.UnixMilli()) + assert.Equal(t, uint32(0x01020304), id.PacketID) + }, + }, + } + for _, tc := range tests { + func(tc test) { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + id, err := extension.ParseIdentifierOption(tc.o, tc.baseTimestamp) + tc.validate(id, err, t) + }) + }(tc) + } +} + +func TestIdentifierSerialize(t *testing.T) { + type test struct { + name string + identifier *extension.IdentifierOption + buffer []byte + validate func([]byte, error, *testing.T) + } + unixNow := uint32(time.Now().Unix()) + tests := []test{ + { + name: "identifier is nil", + identifier: nil, + buffer: make([]byte, 100), + validate: func(b []byte, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "buffer too small", + identifier: &extension.IdentifierOption{}, + buffer: make([]byte, 7), + validate: func(b []byte, err error, t *testing.T) { + assert.Error(t, err) + }, + }, + { + name: "correct serialization", + identifier: &extension.IdentifierOption{ + Timestamp: time.Unix(int64(unixNow), 0x7000001*int64(time.Millisecond)), + PacketID: 0xaabbccdd, + BaseTimestamp: unixNow, + }, + buffer: make([]byte, 8), + validate: func(b []byte, err error, t *testing.T) { + assert.NoError(t, err) + assert.Equal(t, []byte{0x7, 0x00, 0x00, 0x01}, b[0:4]) //the timestamp + assert.Equal(t, []byte{0xaa, 0xbb, 0xcc, 0xdd}, b[4:8]) + }, + }, + } + for _, tc := range tests { + func(tc test) { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := tc.identifier.Serialize(tc.buffer) + tc.validate(tc.buffer, err, t) + }) + }(tc) + } +} diff --git a/pkg/slayers/extn.go b/pkg/slayers/extn.go index 36e21f3406..fded8696a8 100644 --- a/pkg/slayers/extn.go +++ b/pkg/slayers/extn.go @@ -34,6 +34,8 @@ const ( OptTypePad1 OptionType = iota OptTypePadN OptTypeAuthenticator + OptTypeIdentifier + OptTypeFabrid ) type tlvOption struct { diff --git a/pkg/snet/BUILD.bazel b/pkg/snet/BUILD.bazel index 568cc98d7c..6d5e1e103e 100644 --- a/pkg/snet/BUILD.bazel +++ b/pkg/snet/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "packet.go", "packet_conn.go", "path.go", + "path_fabrid.go", "reader.go", "reply_pather.go", "router.go", @@ -21,6 +22,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", + "//pkg/experimental/fabrid:go_default_library", "//pkg/log:go_default_library", "//pkg/metrics/v2:go_default_library", "//pkg/private/common:go_default_library", diff --git a/pkg/snet/packet.go b/pkg/snet/packet.go index 31f7592398..7e6a89b2f8 100644 --- a/pkg/snet/packet.go +++ b/pkg/snet/packet.go @@ -360,6 +360,10 @@ func (r RawPath) SetPath(s *slayers.SCION) error { return serrors.New("snet.RawPath does not support SetPath") } +func (r RawPath) SetExtensions(s *slayers.SCION, p *PacketInfo) error { + return serrors.New("snet.RawPath does not support SetExtensions") +} + // Packet describes a SCION packet. type Packet struct { Bytes @@ -370,8 +374,8 @@ type Packet struct { func (p *Packet) Decode() error { var ( scionLayer slayers.SCION - hbhLayer slayers.HopByHopExtnSkipper - e2eLayer slayers.EndToEndExtnSkipper + hbhLayer slayers.HopByHopExtn + e2eLayer slayers.EndToEndExtn udpLayer slayers.UDP scmpLayer slayers.SCMP ) @@ -379,7 +383,7 @@ func (p *Packet) Decode() error { slayers.LayerTypeSCION, &scionLayer, &hbhLayer, &e2eLayer, &udpLayer, &scmpLayer, ) parser.IgnoreUnsupported = true - decoded := make([]gopacket.LayerType, 0, 4) + decoded := make([]gopacket.LayerType, 0, 6) if err := parser.DecodeLayers(p.Bytes, &decoded); err != nil { return err } @@ -411,6 +415,12 @@ func (p *Packet) Decode() error { } } p.Path = rpath + if len(hbhLayer.Options) != 0 { + p.HbhExtension = &hbhLayer + } + if len(e2eLayer.Options) != 0 { + p.E2eExtension = &e2eLayer + } switch l4 { case slayers.LayerTypeSCIONUDP: @@ -581,10 +591,36 @@ func (p *Packet) Serialize() error { if err := p.Path.SetPath(&scionLayer); err != nil { return serrors.WrapStr("setting path", err) } + if err := p.Path.SetExtensions(&scionLayer, &p.PacketInfo); err != nil { + return serrors.WrapStr("error setting the header extensions", err) + } packetLayers = append(packetLayers, &scionLayer) + if p.HbhExtension != nil { + packetLayers = append(packetLayers, p.HbhExtension) + } + if p.E2eExtension != nil { + packetLayers = append(packetLayers, p.E2eExtension) + } + packetLayers = append(packetLayers, p.Payload.toLayers(&scionLayer)...) + if p.HbhExtension != nil { + tmp := scionLayer.NextHdr + scionLayer.NextHdr = slayers.HopByHopClass + p.HbhExtension.NextHdr = tmp + + if p.E2eExtension != nil { + tmp = p.HbhExtension.NextHdr + p.HbhExtension.NextHdr = slayers.End2EndClass + p.E2eExtension.NextHdr = tmp + } + } else if p.E2eExtension != nil { + tmp := scionLayer.NextHdr + scionLayer.NextHdr = slayers.End2EndClass + p.E2eExtension.NextHdr = tmp + } + buffer := gopacket.NewSerializeBuffer() options := gopacket.SerializeOptions{ ComputeChecksums: true, @@ -616,5 +652,7 @@ type PacketInfo struct { // Path contains a SCION forwarding path. This field must not be nil. Path DataplanePath // Payload is the Payload of the message. - Payload Payload + Payload Payload + HbhExtension *slayers.HopByHopExtn + E2eExtension *slayers.EndToEndExtn } diff --git a/pkg/snet/path.go b/pkg/snet/path.go index 4fcf7b5837..db851bbb2c 100644 --- a/pkg/snet/path.go +++ b/pkg/snet/path.go @@ -22,6 +22,7 @@ import ( "time" "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/experimental/fabrid" "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/slayers" ) @@ -41,6 +42,8 @@ type DataplanePath interface { // SetPath sets the path in the SCION header. It assumes that all the fields // except the path and path type are set correctly. SetPath(scion *slayers.SCION) error + + SetExtensions(s *slayers.SCION, p *PacketInfo) error } // Path is an abstract representation of a path. Most applications do not need @@ -151,13 +154,26 @@ type PathMetadata struct { // EpicAuths contains the EPIC authenticators. EpicAuths EpicAuths + + // FabridInfo contains information about the FABRID policies and support for each hop. + FabridInfo []FabridInfo } func (pm *PathMetadata) Copy() *PathMetadata { if pm == nil { return nil } - + fabridInfoCopy := make([]FabridInfo, len(pm.FabridInfo)) + for i := range pm.FabridInfo { + fabridInfoCopy[i] = FabridInfo{ + Enabled: pm.FabridInfo[i].Enabled, + Policies: make([]*fabrid.Policy, len(pm.FabridInfo[i].Policies)), + Digest: make([]byte, len(pm.FabridInfo[i].Digest)), + Detached: pm.FabridInfo[i].Detached, + } + copy(fabridInfoCopy[i].Policies, pm.FabridInfo[i].Policies) + copy(fabridInfoCopy[i].Digest, pm.FabridInfo[i].Digest) + } return &PathMetadata{ Interfaces: append(pm.Interfaces[:0:0], pm.Interfaces...), MTU: pm.MTU, @@ -169,6 +185,7 @@ func (pm *PathMetadata) Copy() *PathMetadata { LinkType: append(pm.LinkType[:0:0], pm.LinkType...), InternalHops: append(pm.InternalHops[:0:0], pm.InternalHops...), Notes: append(pm.Notes[:0:0], pm.Notes...), + FabridInfo: fabridInfoCopy, EpicAuths: EpicAuths{ AuthPHVF: append([]byte(nil), pm.EpicAuths.AuthPHVF...), AuthLHVF: append([]byte(nil), pm.EpicAuths.AuthLHVF...), diff --git a/pkg/snet/path/BUILD.bazel b/pkg/snet/path/BUILD.bazel index 351e08c2e3..4a0a2fdcdb 100644 --- a/pkg/snet/path/BUILD.bazel +++ b/pkg/snet/path/BUILD.bazel @@ -5,18 +5,24 @@ go_library( srcs = [ "empty.go", "epic.go", + "fabrid.go", "onehop.go", "path.go", + "path_ext.go", "scion.go", ], importpath = "github.com/scionproto/scion/pkg/snet/path", visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", + "//pkg/drkey:go_default_library", "//pkg/experimental/epic:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/experimental/fabrid/crypto:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", "//pkg/slayers:go_default_library", + "//pkg/slayers/extension:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", diff --git a/pkg/snet/path/fabrid.go b/pkg/snet/path/fabrid.go new file mode 100644 index 0000000000..839693faf3 --- /dev/null +++ b/pkg/snet/path/fabrid.go @@ -0,0 +1,221 @@ +// Copyright 2024 ETH Zurich +// +// 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 path + +import ( + "context" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/experimental/fabrid/crypto" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" + "github.com/scionproto/scion/pkg/slayers/path/scion" + "github.com/scionproto/scion/pkg/snet" +) + +type FabridConfig struct { + LocalIA addr.IA + LocalAddr string + DestinationIA addr.IA + DestinationAddr string +} + +type FABRID struct { + Raw []byte + keys map[addr.IA]*drkey.FabridKey + pathKey *drkey.FabridKey + getFabridKeys func(context.Context, drkey.FabridKeysMeta) (drkey.FabridKeysResponse, error) + conf *FabridConfig + counter uint32 + baseTimestamp uint32 + tmpBuffer []byte + identifierBuffer []byte + fabridBuffer []byte + policyIDs []*fabrid.PolicyID + numHops int + hops []snet.HopInterface +} + +func NewFABRIDDataplanePath(p SCION, hops []snet.HopInterface, policyIDs []*fabrid.PolicyID, + conf *FabridConfig) (*FABRID, error) { + numHops := len(hops) + var decoded scion.Decoded + if err := decoded.DecodeFromBytes(p.Raw); err != nil { + return nil, serrors.WrapStr("decoding path", err) + } + keys := make(map[addr.IA]*drkey.FabridKey) + if len(policyIDs) == 0 { // If no policies are provided, use empty policy for all hops + policyIDs = make([]*fabrid.PolicyID, numHops) + } else if len(policyIDs) != numHops { + return nil, serrors.New("Amount of policy ids does not match the amount of hops in " + + "the path.") + } + var pathKey *drkey.FabridKey + if hops[numHops-1].FabridEnabled { + pathKey = &drkey.FabridKey{} + } + f := &FABRID{ + hops: hops, + numHops: numHops, + conf: conf, + keys: keys, + pathKey: pathKey, + tmpBuffer: make([]byte, 64), + identifierBuffer: make([]byte, 8), + fabridBuffer: make([]byte, 8+4*numHops), + Raw: append([]byte(nil), p.Raw...), + policyIDs: policyIDs, + } + + // Get ingress/egress IFs and IAs from path interfaces + for i, hop := range hops { + if policyIDs[i] != nil { + f.keys[hop.IA] = &drkey.FabridKey{} + } + } + f.baseTimestamp = decoded.InfoFields[0].Timestamp + return f, nil +} + +func (f *FABRID) RegisterDRKeyFetcher( + getFabridKeys func(context.Context, drkey.FabridKeysMeta) (drkey.FabridKeysResponse, error)) { + + f.getFabridKeys = getFabridKeys +} + +func (f *FABRID) SetPath(s *slayers.SCION) error { + var sp scion.Raw + if err := sp.DecodeFromBytes(f.Raw); err != nil { + return err + } + s.Path, s.PathType = &sp, sp.Type() + return nil +} +func (f *FABRID) SetExtensions(s *slayers.SCION, p *snet.PacketInfo) error { + if s == nil { + return serrors.New("scion layer is nil") + } + if p == nil { + return serrors.New("packet info is nil") + } + if f.getFabridKeys == nil { + return serrors.New("drkey not correctly configured") + } + if p.HbhExtension == nil { + p.HbhExtension = &slayers.HopByHopExtn{} + } + now := time.Now().Truncate(time.Millisecond) + err := f.renewExpiredKeys(now) + if err != nil { + return serrors.WrapStr("While obtaining fabrid keys", err) + } + identifierOption := &extension.IdentifierOption{ + Timestamp: now, + BaseTimestamp: f.baseTimestamp, + PacketID: f.counter, + } + fabridOption := &extension.FabridOption{ + HopfieldMetadata: make([]*extension.FabridHopfieldMetadata, f.numHops), + } + for i := 0; i < f.numHops; i++ { + meta := &extension.FabridHopfieldMetadata{} + if f.policyIDs[i] != nil && f.keys[f.hops[i].IA] != nil { + meta.FabridEnabled = true + key := f.keys[f.hops[i].IA].Key + encPolicyID, err := crypto.EncryptPolicyID(*f.policyIDs[i], identifierOption, key[:]) + if err != nil { + return serrors.WrapStr("encrypting policy ID", err) + } + meta.EncryptedPolicyID = encPolicyID + } + fabridOption.HopfieldMetadata[i] = meta + } + err = crypto.InitValidators(fabridOption, identifierOption, s, f.tmpBuffer, f.pathKey, + f.keys, nil, f.hops) + if err != nil { + return serrors.WrapStr("initializing validators failed", err) + } + err = identifierOption.Serialize(f.identifierBuffer) + if err != nil { + return serrors.WrapStr("serializing identifier", err) + } + err = fabridOption.SerializeTo(f.fabridBuffer) + if err != nil { + return serrors.WrapStr("serializing fabrid option", err) + } + fabridLength := 4 + 4*f.numHops + p.HbhExtension.Options = append(p.HbhExtension.Options, + &slayers.HopByHopOption{ + OptType: slayers.OptTypeIdentifier, + OptData: f.identifierBuffer, + OptDataLen: 8, + ActualLength: 8, + }, + &slayers.HopByHopOption{ + OptType: slayers.OptTypeFabrid, + OptData: f.fabridBuffer[:fabridLength], + OptDataLen: uint8(fabridLength), + ActualLength: fabridLength, + }) + f.counter++ + return nil +} + +// This function iterates over all on-path ASes and checks whether the keys are still valid. +// If it finds expired keys it marks them as expired and renews all expired keys at once. +func (f *FABRID) renewExpiredKeys(t time.Time) error { + var expiredAses []addr.IA = nil + for ia, key := range f.keys { + if key.Epoch.NotAfter.Before(t) { + // key is expired, mark as expired + if expiredAses == nil { + expiredAses = make([]addr.IA, 0, len(f.keys)) + } + expiredAses = append(expiredAses, ia) + } + } + isPathKeyExpired := false + if f.pathKey != nil { + isPathKeyExpired = f.pathKey.Epoch.NotAfter.Before(t) + } + if expiredAses != nil || isPathKeyExpired { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + meta := drkey.FabridKeysMeta{ + SrcAS: f.conf.LocalIA, + SrcHost: f.conf.LocalAddr, + PathASes: expiredAses, + DstAS: f.conf.DestinationIA, + } + if isPathKeyExpired { + meta.DstHost = &f.conf.DestinationAddr + } + keys, err := f.getFabridKeys(ctx, meta) + if err != nil { + return err + } + if isPathKeyExpired { + f.pathKey = &keys.PathKey + } + for i := range keys.ASHostKeys { + f.keys[keys.ASHostKeys[i].AS] = &keys.ASHostKeys[i] + } + } + return nil +} diff --git a/pkg/snet/path/path_ext.go b/pkg/snet/path/path_ext.go new file mode 100644 index 0000000000..eb5ef860c4 --- /dev/null +++ b/pkg/snet/path/path_ext.go @@ -0,0 +1,36 @@ +// Copyright 2024 ETH Zurich +// +// 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 path + +import ( + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/snet" +) + +func (e Empty) SetExtensions(s *slayers.SCION, p *snet.PacketInfo) error { + return nil +} + +func (e *EPIC) SetExtensions(s *slayers.SCION, p *snet.PacketInfo) error { + return nil +} + +func (p OneHop) SetExtensions(s *slayers.SCION, pi *snet.PacketInfo) error { + return nil +} + +func (p SCION) SetExtensions(s *slayers.SCION, pi *snet.PacketInfo) error { + return nil +} diff --git a/pkg/snet/path_fabrid.go b/pkg/snet/path_fabrid.go new file mode 100644 index 0000000000..d479c3e0e1 --- /dev/null +++ b/pkg/snet/path_fabrid.go @@ -0,0 +1,85 @@ +// Copyright 2024 ETH Zurich +// +// 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 snet + +import ( + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/private/common" +) + +// HopInterface represents a single hop on the path +type HopInterface struct { + // IgIf represents the ingress interface ID for a hop in the path. + IgIf common.IFIDType + // EgIf represents the ingress interface ID for a hop in the path. + EgIf common.IFIDType + // IA is the ISD AS identifier of the hop. + IA addr.IA + // FabridEnabled indicates whether FABRID is enabled on this hop. + FabridEnabled bool + // Policies are the FABRID Policies that are supported by this hop. + Policies []*fabrid.Policy +} + +type FabridInfo struct { + // Enabled contains a boolean indicating whether the hop supports FABRID. + Enabled bool + // Policies Contains the policy identifiers that can be used on this hop + Policies []*fabrid.Policy + // Digest contains the FABRID digest for the AS. This is used when the + // FABRID extension is detached. + Digest []byte + // Detached indicates whether the FABRID maps have been detached from the PCB for this hop. + // This can happen as the PCB is propagated, or when the AS does not add the detachable FABRID + // extension. + Detached bool +} + +func (pm *PathMetadata) Hops() []HopInterface { + ifaces := pm.Interfaces + fabrid := pm.FabridInfo + switch { + case len(ifaces)%2 != 0 || (len(fabrid) != len(ifaces)/2+1): + return []HopInterface{} + case len(ifaces) == 0 || len(fabrid) == 0: + return []HopInterface{} + default: + hops := make([]HopInterface, 0, len(ifaces)/2+1) + hops = append(hops, HopInterface{ + IA: ifaces[0].IA, + IgIf: 0, + EgIf: ifaces[0].ID, + FabridEnabled: fabrid[0].Enabled, + Policies: fabrid[0].Policies}) + for i := 1; i < len(ifaces)-1; i += 2 { + hops = append(hops, HopInterface{ + IA: ifaces[i].IA, + IgIf: ifaces[i].ID, + EgIf: ifaces[i+1].ID, + FabridEnabled: fabrid[(i+1)/2].Enabled, + Policies: fabrid[(i+1)/2].Policies, + }) + } + hops = append(hops, HopInterface{ + IA: ifaces[len(ifaces)-1].IA, + IgIf: ifaces[len(ifaces)-1].ID, + EgIf: 0, + FabridEnabled: fabrid[len(ifaces)/2].Enabled, + Policies: fabrid[len(ifaces)/2].Policies, + }) + return hops + } +} diff --git a/pkg/snet/reply_pather.go b/pkg/snet/reply_pather.go index 62e8e53d78..d081260bcf 100644 --- a/pkg/snet/reply_pather.go +++ b/pkg/snet/reply_pather.go @@ -58,3 +58,7 @@ func (p RawReplyPath) SetPath(s *slayers.SCION) error { s.Path, s.PathType = p.Path, p.Path.Type() return nil } + +func (p RawReplyPath) SetExtensions(s *slayers.SCION, pi *PacketInfo) error { + return nil +} diff --git a/private/app/path/BUILD.bazel b/private/app/path/BUILD.bazel index d6df278e2a..856852af91 100644 --- a/private/app/path/BUILD.bazel +++ b/private/app/path/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", "//private/app/path/pathprobe:go_default_library", + "//private/path/fabridquery:go_default_library", "//private/path/pathpol:go_default_library", "@com_github_fatih_color//:go_default_library", ], diff --git a/private/app/path/path.go b/private/app/path/path.go index 96666805e3..921f1c4e3f 100644 --- a/private/app/path/path.go +++ b/private/app/path/path.go @@ -34,6 +34,7 @@ import ( "github.com/scionproto/scion/pkg/snet" snetpath "github.com/scionproto/scion/pkg/snet/path" "github.com/scionproto/scion/private/app/path/pathprobe" + "github.com/scionproto/scion/private/path/fabridquery" "github.com/scionproto/scion/private/path/pathpol" ) @@ -79,7 +80,7 @@ func Choose( ) (snet.Path, error) { o := applyOption(opts) - paths, err := fetchPaths(ctx, conn, remote, o.refresh, o.seq) + paths, err := fetchPaths(ctx, conn, remote, o.refresh, o.seq, o.fetchDetachedFabridMaps) if err != nil { return nil, serrors.WrapStr("fetching paths", err) } @@ -101,6 +102,45 @@ func Choose( } paths = epicPaths } + if o.fabrid != nil { + query, err := fabridquery.ParseFabridQuery(o.fabrid.Query) + if err != nil { + return nil, serrors.WrapStr("parsing fabrid query", err) + } + + for _, p := range paths { + scionPath, isSCIONPath := p.Dataplane().(snetpath.SCION) + if !isSCIONPath { + continue + } + hopIntfs := p.Metadata().Hops() + ml := fabridquery.MatchList{ + SelectedPolicies: make([]*fabridquery.Policy, len(hopIntfs)), + } + _, pols := query.Evaluate(hopIntfs, &ml) + + if !pols.Accepted() { + continue + } + fabridPath, err := snetpath.NewFABRIDDataplanePath(scionPath, p.Metadata().Hops(), + pols.Policies(), &o.fabrid.FabridConfig) + if err != nil { + return nil, serrors.WrapStr("creating fabrid path from scion path", err) + } + resPath := snetpath.Path{Src: p.Source(), Dst: p.Destination(), + DataplanePath: fabridPath, NextHop: p.UnderlayNextHop(), Meta: *p.Metadata()} + + if o.fabrid.PrintSelectedPolicies { + cs := DefaultColorScheme(false) + fmt.Printf("Using selected FABRID policies:\n %s\n\n", cs.FabridPath(resPath, + ml.SelectedPolicies)) + } + return resPath, nil + } + return nil, serrors.New( + fmt.Sprintf("no fabrid paths available satisfying query '%s'", + o.fabrid.Query)) + } if o.probeCfg != nil { paths, err = filterUnhealthy(ctx, paths, remote, conn, o.probeCfg, o.epic) if err != nil { @@ -110,11 +150,16 @@ func Choose( return nil, serrors.New("no healthy paths available") } } + var selectedPath snet.Path if o.interactive { - return printAndChoose(paths, remote, o.colorScheme) + selectedPath, err = printAndChoose(paths, remote, o.colorScheme) + if err != nil { + return selectedPath, err + } + } else { + selectedPath = paths[rand.Intn(len(paths))] } - - return paths[rand.Intn(len(paths))], nil + return selectedPath, nil } func filterUnhealthy( @@ -170,9 +215,11 @@ func fetchPaths( remote addr.IA, refresh bool, seq string, + fetchFabridDetachedMaps bool, ) ([]snet.Path, error) { - allPaths, err := conn.Paths(ctx, remote, 0, daemon.PathReqFlags{Refresh: refresh}) + allPaths, err := conn.Paths(ctx, remote, 0, daemon.PathReqFlags{Refresh: refresh, + FetchFabridDetachedMaps: fetchFabridDetachedMaps}) if err != nil { return nil, serrors.WrapStr("retrieving paths", err) } @@ -224,36 +271,42 @@ func printAndChoose(paths []snet.Path, remote addr.IA, cs ColorScheme) (snet.Pat // ColorScheme allows customizing the path coloring. type ColorScheme struct { - Header *color.Color - Keys *color.Color - Values *color.Color - Link *color.Color - Intf *color.Color - Good *color.Color - Bad *color.Color + Header *color.Color + Keys *color.Color + Values *color.Color + Link *color.Color + GlobalPolicy *color.Color + LocalPolicy *color.Color + Intf *color.Color + Good *color.Color + Bad *color.Color } func DefaultColorScheme(disable bool) ColorScheme { if disable { noColor := color.New() return ColorScheme{ - Header: noColor, - Keys: noColor, - Values: noColor, - Link: noColor, - Intf: noColor, - Good: noColor, - Bad: noColor, + Header: noColor, + Keys: noColor, + Values: noColor, + GlobalPolicy: noColor, + LocalPolicy: noColor, + Link: noColor, + Intf: noColor, + Good: noColor, + Bad: noColor, } } return ColorScheme{ - Header: color.New(color.FgHiBlack), - Keys: color.New(color.FgHiCyan), - Values: color.New(), - Link: color.New(color.FgHiMagenta), - Intf: color.New(color.FgYellow), - Good: color.New(color.FgGreen), - Bad: color.New(color.FgRed), + Header: color.New(color.FgHiBlack), + Keys: color.New(color.FgHiCyan), + Values: color.New(), + Link: color.New(color.FgHiMagenta), + Intf: color.New(color.FgYellow), + Good: color.New(color.FgGreen), + Bad: color.New(color.FgRed), + GlobalPolicy: color.New(color.FgHiBlue), + LocalPolicy: color.New(color.FgHiGreen), } } @@ -271,34 +324,103 @@ func (cs ColorScheme) KeyValues(kv ...string) []string { } return entries } +func (cs ColorScheme) Policies(policies []snet.FabridInfo, idx int) string { + if len(policies) < idx { + return "" + } + if policies[idx].Enabled && len(policies[idx].Policies) == 0 { + return cs.GlobalPolicy.Sprintf("~ZERO~") + } + policyStr := make([]string, len(policies[idx].Policies)) + for i, v := range policies[idx].Policies { + if v.IsLocal { + policyStr[i] = cs.LocalPolicy.Sprintf(v.String()) + } else { + policyStr[i] = cs.GlobalPolicy.Sprintf(v.String()) + } + } + return fmt.Sprintf("~%s~", strings.Join(policyStr, ",")) +} + +func (cs ColorScheme) SelectedPolicy(policy *fabridquery.Policy) string { + if policy == nil { + return "" + } else if policy.Type == fabridquery.WILDCARD_POLICY_TYPE || policy. + Type == fabridquery.REJECT_POLICY_TYPE { + return cs.GlobalPolicy.Sprintf("~ZERO~") + } else if policy.IsLocal { + return cs.LocalPolicy.Sprintf("~%s~", policy.String()) + } else { + return cs.GlobalPolicy.Sprintf("~%s~", policy.String()) + } +} + +// FabridPath prints the path with only the selected policies for each hop. +func (cs ColorScheme) FabridPath(path snet.Path, policies []*fabridquery.Policy) string { + if path == nil { + return "" + } + intfs := path.Metadata().Interfaces + if len(intfs) == 0 { + return "" + } + var hops []string + intf := intfs[0] + hops = append(hops, cs.Values.Sprintf("%s %s %s", + cs.Values.Sprint(intf.IA), + cs.SelectedPolicy(policies[0]), + cs.Intf.Sprint(intf.ID), + )) + for i := 1; i < len(intfs)-1; i += 2 { + inIntf := intfs[i] + outIntf := intfs[i+1] + hops = append(hops, cs.Values.Sprintf("%s %s %s %s", + cs.Intf.Sprint(inIntf.ID), + cs.Values.Sprint(inIntf.IA), + cs.SelectedPolicy(policies[(i+1)/2]), + cs.Intf.Sprint(outIntf.ID), + )) + } + intf = intfs[len(intfs)-1] + hops = append(hops, cs.Values.Sprintf("%s %s %s", + cs.Intf.Sprint(intf.ID), + cs.Values.Sprint(intf.IA), + cs.SelectedPolicy(policies[len(intfs)/2]), + )) + return fmt.Sprintf("[%s]", strings.Join(hops, cs.Link.Sprintf(">"))) +} func (cs ColorScheme) Path(path snet.Path) string { if path == nil { return "" } intfs := path.Metadata().Interfaces + policies := path.Metadata().FabridInfo if len(intfs) == 0 { return "" } var hops []string intf := intfs[0] - hops = append(hops, cs.Values.Sprintf("%s %s", + hops = append(hops, cs.Values.Sprintf("%s %s %s", cs.Values.Sprint(intf.IA), + cs.Policies(policies, 0), cs.Intf.Sprint(intf.ID), )) for i := 1; i < len(intfs)-1; i += 2 { inIntf := intfs[i] outIntf := intfs[i+1] - hops = append(hops, cs.Values.Sprintf("%s %s %s", + hops = append(hops, cs.Values.Sprintf("%s %s %s %s", cs.Intf.Sprint(inIntf.ID), cs.Values.Sprint(inIntf.IA), + cs.Policies(policies, (i+1)/2), cs.Intf.Sprint(outIntf.ID), )) } intf = intfs[len(intfs)-1] - hops = append(hops, cs.Values.Sprintf("%s %s", + hops = append(hops, cs.Values.Sprintf("%s %s %s", cs.Intf.Sprint(intf.ID), cs.Values.Sprint(intf.IA), + cs.Policies(policies, len(intfs)/2), )) return fmt.Sprintf("[%s]", strings.Join(hops, cs.Link.Sprintf(">"))) } @@ -311,13 +433,20 @@ type ProbeConfig struct { SCIONPacketConnMetrics snet.SCIONPacketConnMetrics } +type FABRIDQuery struct { + Query string + FabridConfig snetpath.FabridConfig + PrintSelectedPolicies bool +} type options struct { - interactive bool - refresh bool - seq string - colorScheme ColorScheme - probeCfg *ProbeConfig - epic bool + interactive bool + refresh bool + seq string + colorScheme ColorScheme + probeCfg *ProbeConfig + epic bool + fetchDetachedFabridMaps bool + fabrid *FABRIDQuery } type Option func(o *options) @@ -365,3 +494,14 @@ func WithEPIC(epic bool) Option { o.epic = epic } } + +func WithFABRID(f *FABRIDQuery) Option { + return func(o *options) { + o.fabrid = f + } +} +func WithFetchDetachedFabridMaps(value bool) Option { + return func(o *options) { + o.fetchDetachedFabridMaps = value + } +} diff --git a/private/mgmtapi/segments/api/testdata/segments-blob-by-id.txt b/private/mgmtapi/segments/api/testdata/segments-blob-by-id.txt index 06f02b0fb4..e11f16775b 100644 --- a/private/mgmtapi/segments/api/testdata/segments-blob-by-id.txt +++ b/private/mgmtapi/segments/api/testdata/segments-blob-by-id.txt @@ -1,6 +1,6 @@ -----BEGIN PATH SEGMENT----- -CgkI8eCagAYQuQoSLwotCiAKBggDEgJpZBIWCJCCgICA4H8aDAoKEAEiBhERERER -ERIJc2lnbmF0dXJlEjEKLwoiCgYIAxICaWQSGAiRgoCAgOB/Gg4KDAgBEAIiBhIS -EhISEhIJc2lnbmF0dXJlEi8KLQogCgYIAxICaWQSFgiTgoCAgOB/GgwKCggCIgYT -ExMTExMSCXNpZ25hdHVyZQ== +CgkI8eCagAYQuQoSMQotCiAKBggDEgJpZBIWCJCCgICA4H8aDAoKEAEiBhERERER +ERIJc2lnbmF0dXJlEgASMwovCiIKBggDEgJpZBIYCJGCgICA4H8aDgoMCAEQAiIG +EhISEhISEglzaWduYXR1cmUSABIxCi0KIAoGCAMSAmlkEhYIk4KAgIDgfxoMCgoI +AiIGExMTExMTEglzaWduYXR1cmUSAA== -----END PATH SEGMENT----- diff --git a/private/path/combinator/BUILD.bazel b/private/path/combinator/BUILD.bazel index fdd55e4adf..f03deeb2ab 100644 --- a/private/path/combinator/BUILD.bazel +++ b/private/path/combinator/BUILD.bazel @@ -11,6 +11,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", + "//pkg/experimental/fabrid/graphutils:go_default_library", "//pkg/private/common:go_default_library", "//pkg/private/ctrl/path_mgmt/proto:go_default_library", "//pkg/private/util:go_default_library", diff --git a/private/path/combinator/graph.go b/private/path/combinator/graph.go index e3ed0cd808..07e08efb47 100644 --- a/private/path/combinator/graph.go +++ b/private/path/combinator/graph.go @@ -23,6 +23,7 @@ import ( "time" "github.com/scionproto/scion/pkg/addr" + fabrid_utils "github.com/scionproto/scion/pkg/experimental/fabrid/graphutils" "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/ctrl/path_mgmt/proto" "github.com/scionproto/scion/pkg/private/util" @@ -313,6 +314,7 @@ type pathSolution struct { func (solution *pathSolution) Path() Path { mtu := ^uint16(0) var segments segmentList + fabridMaps := make(map[addr.IA]fabrid_utils.FabridMapEntry) var epicPathAuths [][]byte for _, solEdge := range solution.edges { var hops []path.HopField @@ -379,6 +381,16 @@ func (solution *pathSolution) Path() Path { pathASEntries = append(pathASEntries, asEntry) epicSegAuths = append(epicSegAuths, epicAuth) + fabridMap, exists := fabridMaps[asEntry.Local] + if (!exists || fabridMap.Ts.Before(solEdge.segment.Info.Timestamp)) && asEntry. + Extensions.Digests != nil { + fabridMaps[asEntry.Local] = fabrid_utils.FabridMapEntry{ + Map: asEntry.UnsignedExtensions.FabridDetached, + Digest: asEntry.Extensions.Digests.Fabrid.Digest, + Ts: solEdge.segment.Info.Timestamp, + } + } + mtu = minUint16(mtu, uint16(asEntry.MTU)) if forwardingLinkMtu != 0 { // The first HE in a segment has MTU 0, so we ignore those @@ -413,7 +425,7 @@ func (solution *pathSolution) Path() Path { interfaces := segments.Interfaces() asEntries := segments.ASEntries() staticInfo := collectMetadata(interfaces, asEntries) - + fabridInfo := fabrid_utils.CollectFabridPolicies(interfaces, fabridMaps) path := Path{ SCIONPath: segments.ScionPath(), Metadata: snet.PathMetadata{ @@ -427,6 +439,7 @@ func (solution *pathSolution) Path() Path { LinkType: staticInfo.LinkType, InternalHops: staticInfo.InternalHops, Notes: staticInfo.Notes, + FabridInfo: fabridInfo, }, Weight: solution.cost, } diff --git a/private/path/fabridquery/BUILD.bazel b/private/path/fabridquery/BUILD.bazel new file mode 100644 index 0000000000..176d36b7a7 --- /dev/null +++ b/private/path/fabridquery/BUILD.bazel @@ -0,0 +1,40 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "matchlist.go", + "parser.go", + "query.go", + ], + importpath = "github.com/scionproto/scion/private/path/fabridquery", + visibility = ["//visibility:public"], + deps = [ + "//antlr/pathpolicyconstraints:go_default_library", + "//pkg/addr:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/private/common:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/snet:go_default_library", + "@com_github_antlr_antlr4_runtime_go_antlr//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "matchlist_test.go", + "query_test.go", + ], + deps = [ + ":go_default_library", + "//pkg/addr:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/private/common:go_default_library", + "//pkg/private/xtest:go_default_library", + "//pkg/private/xtest/graph:go_default_library", + "//pkg/snet:go_default_library", + "@com_github_golang_mock//gomock:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], +) diff --git a/private/path/fabridquery/matchlist.go b/private/path/fabridquery/matchlist.go new file mode 100644 index 0000000000..063c0645d7 --- /dev/null +++ b/private/path/fabridquery/matchlist.go @@ -0,0 +1,69 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabridquery + +import ( + "github.com/scionproto/scion/pkg/experimental/fabrid" +) + +type MatchList struct { + SelectedPolicies []*Policy +} + +// Copy creates a copy of the MatchList object, including the list of selected policies. +func (ml MatchList) Copy() *MatchList { + duplicate := make([]*Policy, len(ml.SelectedPolicies)) + copy(duplicate, ml.SelectedPolicies) + return &MatchList{duplicate} +} + +// StorePolicy only stores a policy if there has not been one already set for the hop. +func (ml MatchList) StorePolicy(hop int, policy *Policy) { + if ml.SelectedPolicies[hop] == nil { + ml.SelectedPolicies[hop] = policy + } +} + +// Accepted checks if all hops have at least a policy assigned, which is not the rejection policy +func (ml MatchList) Accepted() bool { + for _, policy := range ml.SelectedPolicies { + if policy != nil && policy.Type == REJECT_POLICY_TYPE { + return false + } + } + return true +} + +// Policies returns a slice of PolicyIDs representing the policies used in each hop of +// the MatchList object. A zero PolicyID is used for a nil selected policy, and a zero +// or reject PolicyID is used when a wildcard or reject policy is selected. +// For other policies, the PolicyID is obtained from the selected policy's Policy.Index field. +// It also prints the index and policy details of each hop to console. +// The returned slice has the same length as the MatchList.SelectedPolicies slice. +// func (ml MatchList) Policies() (pols []*fabrid.PolicyID) {} +func (ml MatchList) Policies() (pols []*fabrid.PolicyID) { + pols = make([]*fabrid.PolicyID, len(ml.SelectedPolicies)) + for i, selected := range ml.SelectedPolicies { + if selected == nil { + pols[i] = nil + } else if selected.Type == WILDCARD_POLICY_TYPE || selected.Type == REJECT_POLICY_TYPE { + zeroPol := fabrid.PolicyID(0) + pols[i] = &zeroPol + } else { + pols[i] = &selected.Policy.Index + } + } + return pols +} diff --git a/private/path/fabridquery/matchlist_test.go b/private/path/fabridquery/matchlist_test.go new file mode 100644 index 0000000000..29cd4dcb97 --- /dev/null +++ b/private/path/fabridquery/matchlist_test.go @@ -0,0 +1,79 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabridquery_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/private/path/fabridquery" +) + +func TestAddExistingHop(t *testing.T) { + ml := fabridquery.MatchList{SelectedPolicies: make([]*fabridquery.Policy, 5)} + polIdx := 1 + ml.StorePolicy(1, &fabridquery.Policy{ + Type: fabridquery.STANDARD_POLICY_TYPE, + Policy: &fabrid.Policy{ + IsLocal: false, + Identifier: 100, + Index: fabrid.PolicyID(polIdx), + }, + }) + + ml.StorePolicy(1, &fabridquery.Policy{ + Type: fabridquery.REJECT_POLICY_TYPE, + }) + require.Equal(t, fabrid.PolicyID(polIdx), *ml.Policies()[1]) +} + +func TestRejectedHop(t *testing.T) { + ml := fabridquery.MatchList{SelectedPolicies: make([]*fabridquery.Policy, 5)} + polIdx := 1 + + ml.StorePolicy(1, &fabridquery.Policy{ + Type: fabridquery.REJECT_POLICY_TYPE, + }) + ml.StorePolicy(1, &fabridquery.Policy{ + Type: fabridquery.STANDARD_POLICY_TYPE, + Policy: &fabrid.Policy{ + IsLocal: false, + Identifier: 100, + Index: fabrid.PolicyID(polIdx), + }, + }) + require.NotEqual(t, fabrid.PolicyID(polIdx), *ml.Policies()[1]) + require.False(t, ml.Accepted()) +} + +func TestAcceptedHop(t *testing.T) { + ml := fabridquery.MatchList{SelectedPolicies: make([]*fabridquery.Policy, 6)} + for i := 0; i < 6; i++ { + ml.StorePolicy(i, &fabridquery.Policy{ + Type: fabridquery.STANDARD_POLICY_TYPE, + Policy: &fabrid.Policy{ + IsLocal: false, + Identifier: uint32(100 + i), + Index: fabrid.PolicyID(200 + i), + }, + }) + } + require.True(t, ml.Accepted()) + for i, pol := range ml.Policies() { + require.Equal(t, fabrid.PolicyID(200+i), *pol) + } +} diff --git a/private/path/fabridquery/parser.go b/private/path/fabridquery/parser.go new file mode 100644 index 0000000000..358ffd4a21 --- /dev/null +++ b/private/path/fabridquery/parser.go @@ -0,0 +1,266 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabridquery + +import ( + "fmt" + "strconv" + + "github.com/antlr/antlr4/runtime/Go/antlr" + + "github.com/scionproto/scion/antlr/pathpolicyconstraints" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/experimental/fabrid" +) + +type errorListener struct { + *antlr.DefaultErrorListener + msg string +} + +func (l *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, + column int, msg string, e antlr.RecognitionException) { + + l.msg += fmt.Sprintf("%d:%d %s\n", line, column, msg) +} + +type pathpolicyConstraintsListener struct { + *pathpolicyconstraints.BasePathPolicyConstraintsListener + stack []interface{} +} + +func (l *pathpolicyConstraintsListener) push(s interface{}) { + l.stack = append(l.stack, s) +} + +func (l *pathpolicyConstraintsListener) pop() interface{} { + var result interface{} + if len(l.stack) == 0 { + result = "X" + } else { + result = l.stack[len(l.stack)-1] + l.stack = l.stack[:len(l.stack)-1] + } + return result +} + +func (l *pathpolicyConstraintsListener) ExitIf(c *pathpolicyconstraints.IfContext) { + t, ok := l.pop().(Expressions) + if !ok { + return + } + q, ok := l.pop().(Expressions) + if !ok { + return + } + l.push(Query{q, t, Nop{}}) + +} + +func (l *pathpolicyConstraintsListener) ExitIfElse(c *pathpolicyconstraints.IfElseContext) { + f, ok := l.pop().(Expressions) + if !ok { + return + } + t, ok := l.pop().(Expressions) + if !ok { + return + } + q, ok := l.pop().(Expressions) + if !ok { + return + } + l.push(Query{q, t, f}) + +} + +func (l *pathpolicyConstraintsListener) ExitExpressionQuery(c *pathpolicyconstraints. + ExpressionQueryContext) { + + id, ok := l.pop().(Query) + if !ok { + return + } + l.push(Expression{id}) +} + +func (l *pathpolicyConstraintsListener) ExitExpressionIdentifier(c *pathpolicyconstraints. + ExpressionIdentifierContext) { + + id, ok := l.pop().(Identifier) + if !ok { + return + } + l.push(Expression{id}) +} + +func (l *pathpolicyConstraintsListener) ExitExpressionConcat(c *pathpolicyconstraints. + ExpressionConcatContext) { + + right := l.pop().(Expressions) + left := l.pop().(Expressions) + l.push(ConcatExpression{left, right}) +} + +func (l *pathpolicyConstraintsListener) ExitParens(c *pathpolicyconstraints.ParensContext) { +} + +// ExitIdentifier is called when exiting the identifier production. +func (l *pathpolicyConstraintsListener) ExitIdentifier(c *pathpolicyconstraints. + IdentifierContext) { + + policy, ok := l.pop().(Policy) + if !ok { + return + } + egIface, ok := l.pop().(Interface) + if !ok { + return + } + igIface, ok := l.pop().(Interface) + if !ok { + return + } + as, ok := l.pop().(AS) + if !ok { + return + } + isd, ok := l.pop().(ISD) + if !ok { + return + } + l.push(Identifier{ + Isd: isd, + As: as, + IgIntf: igIface, + EgIntf: egIface, + Policy: policy, + }) +} + +// ExitWildcardISD is called when exiting the WildcardISD production. +func (l *pathpolicyConstraintsListener) ExitWildcardISD(c *pathpolicyconstraints. + WildcardISDContext) { + + l.push(ISD{Wildcard: true}) +} + +// ExitISD is called when exiting the ISD production. +func (l *pathpolicyConstraintsListener) ExitISD(c *pathpolicyconstraints.ISDContext) { + n, err := strconv.Atoi(c.GetText()) + if err != nil { + l.push(ISD{Wildcard: true}) + return + } + l.push(ISD{Wildcard: false, Number: n}) +} + +// ExitWildcardAS is called when exiting the WildcardAS production. +func (l *pathpolicyConstraintsListener) ExitWildcardAS(c *pathpolicyconstraints. + WildcardASContext) { + l.push(AS{Wildcard: true}) + +} + +// ExitLegacyAS is called when exiting the LegacyAS production. +func (l *pathpolicyConstraintsListener) ExitLegacyAS(c *pathpolicyconstraints.LegacyASContext) { + as, err := addr.ParseASSep(c.GetText()[1:], "_") + if err != nil { + c.SetException(antlr.NewFailedPredicateException(c.GetParser(), c.GetText(), err.Error())) + } + l.push(AS{ASN: as}) + +} + +// ExitAS is called when exiting the AS production. +func (l *pathpolicyConstraintsListener) ExitAS(c *pathpolicyconstraints.ASContext) { + as, err := addr.ParseASSep(c.GetText()[1:], "_") + if err != nil { + c.SetException(antlr.NewFailedPredicateException(c.GetParser(), c.GetText(), err.Error())) + } + l.push(AS{ASN: as}) +} + +// ExitWildcardIFace is called when exiting the WildcardIFace production. +func (l *pathpolicyConstraintsListener) ExitWildcardIFace(c *pathpolicyconstraints. + WildcardIFaceContext) { + + l.push(Interface{Wildcard: true}) +} + +// ExitIFace is called when exiting the IFace production. +func (l *pathpolicyConstraintsListener) ExitIFace(c *pathpolicyconstraints.IFaceContext) { + n, err := strconv.Atoi(c.GetText()) + if err != nil { + l.push(Interface{Wildcard: true}) + return + } + l.push(Interface{Wildcard: false, Number: n}) +} + +// ExitGlobalPolicy is called when exiting the GlobalPolicy production. +func (l *pathpolicyConstraintsListener) ExitGlobalPolicy(c *pathpolicyconstraints. + GlobalPolicyContext) { + + n, err := strconv.Atoi(c.GetText()[1:]) + if err != nil { + l.push(Policy{Type: WILDCARD_POLICY_TYPE}) + return + } + l.push(Policy{ + Type: STANDARD_POLICY_TYPE, + Policy: &fabrid.Policy{ + IsLocal: false, + Identifier: uint32(n), + }, + }) +} + +// ExitLocalPolicy is called when exiting the LocalPolicy production. +func (l *pathpolicyConstraintsListener) ExitLocalPolicy(c *pathpolicyconstraints. + LocalPolicyContext) { + + n, err := strconv.Atoi(c.GetText()[1:]) + if err != nil { + l.push(Policy{Type: WILDCARD_POLICY_TYPE}) + return + } + l.push(Policy{ + Type: STANDARD_POLICY_TYPE, + Policy: &fabrid.Policy{ + IsLocal: true, + Identifier: uint32(n), + }, + }) +} + +// ExitWildcardPolicy is called when exiting the WildcardPolicy production. +func (l *pathpolicyConstraintsListener) ExitWildcardPolicy(c *pathpolicyconstraints. + WildcardPolicyContext) { + + l.push(Policy{Type: WILDCARD_POLICY_TYPE}) +} + +// ExitWildcardPolicy is called when exiting the WildcardPolicy production. +func (l *pathpolicyConstraintsListener) ExitReject(c *pathpolicyconstraints.RejectContext) { + + l.push(Policy{Type: REJECT_POLICY_TYPE}) +} + +// ExitPolicyIndex is called when exiting the PolicyIndex production. +func (l *pathpolicyConstraintsListener) ExitPolicyIndex(c *pathpolicyconstraints. + PolicyIndexContext) { + // UNUSED +} diff --git a/private/path/fabridquery/query.go b/private/path/fabridquery/query.go new file mode 100644 index 0000000000..6ff37b2b73 --- /dev/null +++ b/private/path/fabridquery/query.go @@ -0,0 +1,225 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabridquery + +import ( + "fmt" + + "github.com/antlr/antlr4/runtime/Go/antlr" + + "github.com/scionproto/scion/antlr/pathpolicyconstraints" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/snet" +) + +type TypedNumber struct { + Wildcard bool + Number int +} + +type ISD TypedNumber + +func (i ISD) Matches(ia addr.ISD) bool { + return i.Wildcard || ia == addr.ISD(i.Number) +} + +func (i ISD) String() string { + if i.Wildcard { + return "*" + } + return fmt.Sprintf("%d", i.Number) + +} + +type AS struct { + Wildcard bool + ASN addr.AS +} + +func (a AS) Matches(iaas addr.AS) bool { + return a.Wildcard || iaas == a.ASN +} + +func (a AS) String() string { + if a.Wildcard { + return "*" + } + return a.ASN.String() +} + +type Interface TypedNumber + +func (i Interface) Matches(intf common.IFIDType) bool { + return i.Wildcard || intf == common.IFIDType(i.Number) +} + +func (i Interface) String() string { + if i.Wildcard { + return "*" + } + return fmt.Sprintf("%d", i.Number) +} + +const ( + WILDCARD_POLICY_TYPE = iota + REJECT_POLICY_TYPE + STANDARD_POLICY_TYPE +) + +type Policy struct { + Type uint8 + *fabrid.Policy +} + +func (p Policy) String() string { + if p.Type == WILDCARD_POLICY_TYPE { + return "*" + } else if p.Type == REJECT_POLICY_TYPE { + return "reject" + } else if p.Type == STANDARD_POLICY_TYPE { + return p.Policy.String() + } + return "unknown" +} + +type Expressions interface { + Evaluate([]snet.HopInterface, *MatchList) (bool, *MatchList) + String() string +} + +type Identifier struct { + Isd ISD + As AS + IgIntf Interface + EgIntf Interface + Policy Policy +} + +func (i Identifier) String() string { + return fmt.Sprintf("{ Isd %s, As %s, IgIntf %s, EgIntf %s, Policy %s }", i.Isd, i.As, i.IgIntf, + i.EgIntf, i.Policy) +} + +func (i Identifier) Evaluate(pi []snet.HopInterface, ml *MatchList) (bool, *MatchList) { + matched := false + for idx, p := range pi { + // Check if ISD, AS and interfaces match between the query and a hop in the path. + if !(i.Isd.Matches(p.IA.ISD()) && i.As.Matches(p.IA.AS()) && i.IgIntf.Matches(p.IgIf) && + i.EgIntf.Matches(p.EgIf)) { + continue + } + // If so and the query sets a wildcard or reject policy, assign this and continue evaluating + // the query + if (i.Policy.Type == WILDCARD_POLICY_TYPE && p.FabridEnabled) || i.Policy. + Type == REJECT_POLICY_TYPE { + ml.StorePolicy(idx, &i.Policy) + } + + if i.Policy.Type == WILDCARD_POLICY_TYPE || i.Policy.Type == REJECT_POLICY_TYPE { + matched = true + continue + } + // Check if the query's policy matches a policy that is available for this hop. + for _, pol := range p.Policies { + if pol.Identifier == i.Policy.Identifier && i.Policy.Policy.IsLocal == pol.IsLocal { + + ml.StorePolicy(idx, &Policy{ + Type: STANDARD_POLICY_TYPE, + Policy: pol, + }) + matched = true + } + } + + } + return matched, ml +} + +type Expression struct { + Expressions +} + +func (e Expression) Evaluate(pi []snet.HopInterface, ml *MatchList) (bool, *MatchList) { + return e.Expressions.Evaluate(pi, ml) +} + +type Query struct { + Q Expressions + T Expressions + F Expressions +} + +func (q Query) String() string { + return fmt.Sprintf(" Query { Query %s, True %s, False %s } ", q.Q, q.T, q.F) +} + +func (q Query) Evaluate(pi []snet.HopInterface, ml *MatchList) (bool, *MatchList) { + mlOriginal := ml.Copy() + qRes, _ := q.Q.Evaluate(pi, mlOriginal) + if qRes { + return q.T.Evaluate(pi, ml) + } + return q.F.Evaluate(pi, ml) +} + +type ConcatExpression struct { + Left Expressions + Right Expressions +} + +func (e ConcatExpression) String() string { + return fmt.Sprintf(" Concat { Left %s, Right %s } ", e.Left, e.Right) +} + +func (e ConcatExpression) Evaluate(pi []snet.HopInterface, ml *MatchList) (bool, *MatchList) { + left, mlLeft := e.Left.Evaluate(pi, ml) + right, mlRight := e.Right.Evaluate(pi, mlLeft) + return left && right, mlRight +} + +type Nop struct{} + +func (n Nop) String() string { + return "Nop" +} + +func (n Nop) Evaluate(_ []snet.HopInterface, list *MatchList) (bool, *MatchList) { + return true, list +} + +func ParseFabridQuery(input string) (Expressions, error) { + istream := antlr.NewInputStream(input) + lexer := pathpolicyconstraints.NewPathPolicyConstraintsLexer(istream) + lexer.RemoveErrorListeners() + errListener := &errorListener{} + lexer.AddErrorListener(errListener) + tstream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) + parser := pathpolicyconstraints.NewPathPolicyConstraintsParser(tstream) + parser.RemoveErrorListeners() + parser.AddErrorListener(errListener) + listener := pathpolicyConstraintsListener{} + antlr.ParseTreeWalkerDefault.Walk(&listener, parser.Start()) + if errListener.msg != "" || (len(listener.stack) != 1) { + return nil, serrors.New(errListener.msg) + } + expr, ok := listener.stack[0].(Expressions) + if !ok { + return nil, serrors.New("Not a valid query") + } + return expr, nil +} diff --git a/private/path/fabridquery/query_test.go b/private/path/fabridquery/query_test.go new file mode 100644 index 0000000000..c1a5c52185 --- /dev/null +++ b/private/path/fabridquery/query_test.go @@ -0,0 +1,173 @@ +// Copyright 2024 ETH Zurich +// +// 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 fabridquery_test + +import ( + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/private/xtest/graph" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/private/path/fabridquery" +) + +type PathProvider struct { + g *graph.Graph +} + +func NewPathProvider(ctrl *gomock.Controller) PathProvider { + return PathProvider{ + g: graph.NewDefaultGraph(ctrl), + } +} + +func (p PathProvider) GetHops(src, dst addr.IA) [][]snet.HopInterface { + result := [][]snet.HopInterface{} + paths := p.g.GetPaths(src.String(), dst.String()) + for _, ifids := range paths { + pathIntfs := make([]snet.PathInterface, 0, len(ifids)) + for _, ifid := range ifids { + ia := p.g.GetParent(ifid) + pathIntfs = append(pathIntfs, snet.PathInterface{ + IA: ia, + ID: common.IFIDType(ifid), + }) + } + fabridInfo := make([]snet.FabridInfo, 0, len(pathIntfs)/2) + fabridInfo = append(fabridInfo, snet.FabridInfo{ + Enabled: true, + Detached: false, + Digest: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16}, + Policies: []*fabrid.Policy{}, + }) + for i := 1; i < len(pathIntfs)-1; i += 2 { + fabridInfo = append(fabridInfo, snet.FabridInfo{ + Enabled: true, + Detached: false, + Digest: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16}, + Policies: p.g.FabridPolicy(uint16(pathIntfs[i].ID), + uint16(pathIntfs[i+1].ID)), + }) + } + fabridInfo = append(fabridInfo, snet.FabridInfo{ + Enabled: true, + Detached: false, + Digest: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16}, + Policies: p.g.FabridPolicy(uint16(pathIntfs[len( + pathIntfs)-1].ID), 0), + }) + metadata := snet.PathMetadata{ + Interfaces: pathIntfs, + FabridInfo: fabridInfo, + } + result = append(result, metadata.Hops()) + } + return result +} + +func TestParseFabridQuery(t *testing.T) { + tests := map[string]struct { + Input string + ExpPolicies []fabrid.PolicyID + Src addr.IA + Dst addr.IA + ExpectError bool + Accept bool + NilPolicies []bool + }{ + "Wildcard": { + Input: "0-0#0,0@0", + Src: xtest.MustParseIA("1-ff00:0:133"), + Dst: xtest.MustParseIA("1-ff00:0:131"), + ExpPolicies: []fabrid.PolicyID{ + fabrid.PolicyID(0), + fabrid.PolicyID(0), + fabrid.PolicyID(0), + }, + NilPolicies: []bool{false, false, false}, + Accept: true, + ExpectError: false, + }, + "Reject All": { + Input: "0-0#0,0@REJECT", + Src: xtest.MustParseIA("1-ff00:0:133"), + Dst: xtest.MustParseIA("1-ff00:0:131"), + ExpPolicies: []fabrid.PolicyID{ + fabrid.PolicyID(0), + fabrid.PolicyID(0), + fabrid.PolicyID(0), + }, + NilPolicies: []bool{false, false, false}, + Accept: false, + ExpectError: false, + }, + "Global policy": { + Input: "0-0#0,0@G1", + Src: xtest.MustParseIA("1-ff00:0:133"), + Dst: xtest.MustParseIA("1-ff00:0:131"), + ExpPolicies: []fabrid.PolicyID{ + fabrid.PolicyID(0x28), + fabrid.PolicyID(0x28), + fabrid.PolicyID(0x0), + }, + NilPolicies: []bool{true, false, false}, + Accept: true, + ExpectError: false, + }, + //TODO(jvanbommel): extend + } + ctrl := gomock.NewController(t) + defer ctrl.Finish() + pp := NewPathProvider(ctrl) + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + hops := pp.GetHops(tc.Src, tc.Dst)[0] + expr, err := fabridquery.ParseFabridQuery(tc.Input) + if tc.ExpectError { + require.Error(t, err) + return + } else { + require.NoError(t, err) + } + ml := fabridquery.MatchList{ + SelectedPolicies: make([]*fabridquery.Policy, len(hops)), + } + _, resMl := expr.Evaluate(hops, &ml) + fmt.Println(resMl.SelectedPolicies) + require.Equal(t, tc.Accept, resMl.Accepted()) + if !tc.Accept { + return + } + pols := resMl.Policies() + for i, pol := range tc.ExpPolicies { + if tc.NilPolicies[i] { + require.True(t, pols[i] == nil) + continue + } + require.Equal(t, pol, *pols[i]) + } + }) + } +} diff --git a/proto/control_plane/experimental/v1/BUILD.bazel b/proto/control_plane/experimental/v1/BUILD.bazel index d5ef433680..14793ddbbc 100644 --- a/proto/control_plane/experimental/v1/BUILD.bazel +++ b/proto/control_plane/experimental/v1/BUILD.bazel @@ -2,7 +2,12 @@ load("@rules_proto//proto:defs.bzl", "proto_library") proto_library( name = "experimental", - srcs = ["seg_detached_extensions.proto"], + srcs = [ + "fabrid.proto", + "fabrid_extensions.proto", + "seg_detached_extensions.proto", + "seg_detached_extensions_fabrid.proto", + ], visibility = ["//visibility:public"], deps = [ "//proto/crypto/v1:crypto", diff --git a/proto/control_plane/experimental/v1/fabrid.proto b/proto/control_plane/experimental/v1/fabrid.proto new file mode 100644 index 0000000000..baa96c9d54 --- /dev/null +++ b/proto/control_plane/experimental/v1/fabrid.proto @@ -0,0 +1,143 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +syntax = "proto3"; + +option go_package = "github.com/scionproto/scion/pkg/proto/control_plane/experimental"; + +package proto.control_plane.experimental.v1; + +import "proto/control_plane/experimental/v1/fabrid_extensions.proto"; + +service FABRIDInterService{ + // Gets the I-map for a local AS, mapping interfaces to supported policy indices + rpc SupportedIndicesMap(SupportedIndicesMapRequest) returns (SupportedIndicesMapResponse) {} + // Gets the D-Map for a local AS, mapping policy indices to policy identifiers + rpc IndexIdentifierMap(IndexIdentifierMapRequest) returns (IndexIdentifierMapResponse) {} + // Gets the detached maps (both the supported indices map and index identifier map) + rpc DetachedMaps(DetachedMapsRequest) returns (DetachedMapsResponse) {} + // Gets a string description for a local policy + rpc LocalPolicyDescription(LocalPolicyDescriptionRequest) returns + (LocalPolicyDescriptionResponse) {} +} + +service FABRIDIntraService { + // Used by a host inside the AS to request a policy description for another AS. The control + // service will request the policy description from the remote AS if it is unknown to the + // control service. + rpc RemotePolicyDescription(RemotePolicyDescriptionRequest) returns + (RemotePolicyDescriptionResponse) {} + // Used by a host in the AS to request the supported indices map, as well as the index + // identifier map for a remote AS. + rpc RemoteMaps(RemoteMapsRequest) returns (RemoteMapsResponse) {} + // Used by border routers in the AS to request the mapping of supported policy indices to local MPLS labels + rpc MPLSMap(MPLSMapRequest) returns (MPLSMapResponse) {} +} + +message MPLSIPArray { + // An entry of the MPLS Map, represented as an array to maintain the order. + repeated MPLSIP entry = 1; +} + +message MPLSIP { + // The MPLS label to apply to a packet that is sent to a specific policy index that has a + // specific (AS-local) destination IP + uint32 mpls_label = 1; + // Specifies the IP which the packet has to match in addition to matching the specific policy + // index, before applying the MPLS Label + bytes ip = 2; + // In order to support more than one IP per MPLSIP mapping, a prefix is used to denote a range + // of IP addresses + uint32 prefix = 3; +} + +message MPLSMapRequest { + // The MPLS map is only updated if the hash of the map already saved at the router is mismatched + // with the current active MPLS map. This field is optional, if empty, the active map is + // always sent in response. + bytes hash = 1; +} + +message MPLSMapResponse { + // If true the endpoint should update its MPLS label map, if false it is already up to date + bool update = 1; + // The hash of the current active MPLS map on the control service + bytes hash = 2; + // The mapping for policy indices (uint8) to MPLS labels (uint32) that is used for + // connections where the egress is an interface. + map mpls_interface_policies_map = 3; + // The mapping for policies to a specific MPLS label in cases where a packet is forwarded + // intra-AS to a specified IP range: + map mpls_ip_map = 4; +} + +message SupportedIndicesMapRequest { } + +message SupportedIndicesMapResponse { + // Maps a pair of ingress and egress points (i.e. interfaces, or ip ranges) + // to a given local 8-bit policy index. The policy index is used in the + // dataplane and can be mapped to the corresponding policy identifier using the D-map. + repeated FABRIDIndexMapEntry supported_indices_map = 1; +} + +message IndexIdentifierMapRequest { } + +message IndexIdentifierMapResponse { + // An AS-local policy index is mapped to a local or global policy identifier + // using the index_identifier_map (D-map). + map index_identifier_map = 1; +} +message RemotePolicyDescriptionRequest { + // The identifier for the policy + uint32 policy_identifier = 1; + // Remote ISD-AS of the non-global policy identifier + uint64 isd_as = 2; +} + +message RemotePolicyDescriptionResponse { + // A description of the local policy. + string description = 1; +} + +message LocalPolicyDescriptionRequest { + // The identifier for the policy + uint32 policy_identifier = 1; +} + +message LocalPolicyDescriptionResponse { + // A description of the local policy. + string description = 1; +} + +message RemoteMapsRequest { + // The digest corresponding to the maps that the end host is requesting, the end host + // receives this digest in a PCB. + bytes digest = 1; + // Remote ISD-AS + uint64 isd_as = 2; +} + +message RemoteMapsResponse { + // The maps that the remote AS has detached from the PCB + FABRIDDetachableMaps maps = 1; +} + +message DetachedMapsRequest { } + +message DetachedMapsResponse { + // The maps that the local AS has detached from the PCB + FABRIDDetachableMaps maps = 1; +} + + diff --git a/proto/control_plane/experimental/v1/fabrid_extensions.proto b/proto/control_plane/experimental/v1/fabrid_extensions.proto new file mode 100644 index 0000000000..75d5f50664 --- /dev/null +++ b/proto/control_plane/experimental/v1/fabrid_extensions.proto @@ -0,0 +1,78 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +syntax = "proto3"; + +option go_package = "github.com/scionproto/scion/pkg/proto/control_plane/experimental"; + +package proto.control_plane.experimental.v1; + +message FABRIDDetachableMaps { + // Maps a pair of ingress and egress points (i.e. interfaces, or ip ranges) + // to a given local 16-bit policy index. The policy index is used in the + // dataplane + repeated FABRIDIndexMapEntry supported_indices_map = 1; + // The policy index is mapped to a policy identifier using the + // index_identifier_map (D-map) + map index_identifier_map = 2; +} + +message FABRIDPolicyIdentifier { + // Flag to specify local policies + bool policy_is_local = 1; + // The identifier for the policy + uint32 policy_identifier = 2; +} + +message FABRIDIndexMapEntry { + // The ingress and egress connection point pair that supports a set of + // policies + FABRIDIngressEgressPair ie_pair = 1; + // The indices of the policies that are supported by the ingress and + // egress pair. + repeated uint32 supported_policy_indices = 2; +} + +enum FABRIDConnectionType { + // Unspecified connection type + FABRID_CONNECTION_TYPE_UNSPECIFIED = 0; + // IPv4 Range + FABRID_CONNECTION_TYPE_IPV4_RANGE = 1; + // IPv6 Range + FABRID_CONNECTION_TYPE_IPV6_RANGE = 2; + // AS egress or ingress interface + FABRID_CONNECTION_TYPE_INTERFACE = 3; + // Wildcard, policy from any interface + FABRID_CONNECTION_TYPE_WILDCARD = 4; +} +message FABRIDIngressEgressPair { + // Specifies the IP range or interface traffic is coming from, in order to satisfy + // a given policy. + FABRIDConnectionPoint ingress = 1; + // Specifies the destination of traffic which satisfies the policy. + FABRIDConnectionPoint egress = 2; +} + +message FABRIDConnectionPoint { + // The type of the ingress/egress point, which can either be an + // interface, or an IP Range. + FABRIDConnectionType type = 1; + // When the type is IPv4 or IPv6 range, specify the IP and subnet mask for the range here. + bytes ip_address = 2; + // IP prefix length, as in CIDR notation. + uint32 ip_prefix = 3; + // The interface for the interface type. + uint64 interface = 4; + +} diff --git a/proto/control_plane/experimental/v1/seg_detached_extensions_fabrid.proto b/proto/control_plane/experimental/v1/seg_detached_extensions_fabrid.proto new file mode 100644 index 0000000000..35145e44d3 --- /dev/null +++ b/proto/control_plane/experimental/v1/seg_detached_extensions_fabrid.proto @@ -0,0 +1,26 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +syntax = "proto3"; + +option go_package = "github.com/scionproto/scion/pkg/proto/control_plane/experimental"; + +package proto.control_plane.experimental.v1; + +import "proto/control_plane/experimental/v1/fabrid_extensions.proto"; + +message FABRIDDetachedExtension { + // The I and D-map of FABRID + proto.control_plane.experimental.v1.FABRIDDetachableMaps maps = 1; +} \ No newline at end of file diff --git a/proto/control_plane/v1/seg_extensions.proto b/proto/control_plane/v1/seg_extensions.proto index fb50e4f0c1..7ff907cc9d 100644 --- a/proto/control_plane/v1/seg_extensions.proto +++ b/proto/control_plane/v1/seg_extensions.proto @@ -19,6 +19,7 @@ option go_package = "github.com/scionproto/scion/pkg/proto/control_plane"; package proto.control_plane.v1; import "proto/control_plane/experimental/v1/seg_detached_extensions.proto"; +import "proto/control_plane/experimental/v1/seg_detached_extensions_fabrid.proto"; message PathSegmentExtensions { // Optional static info extension. @@ -111,7 +112,9 @@ message BandwidthInfo { // all paths (cross-over, shortcut, peering) based on this ASEntry. // All values are in grams of CO2 emitted per terabyte of traffic. message CarbonIntensityInfo { + // The intra-AS carbon intensity info map intra = 1; + // The inter-AS carbon intensity info map inter = 2; } @@ -156,9 +159,20 @@ message DigestExtension { // total number of authenticators used in the hash. // Digest epic = 1000; + // The digest of the detached FABRID extension. The hash input is defined as follows: + // + // input = I-map || D-Map + // + // Here, 'I-map' is the mapping from connection points (e.g. interfaces or ip ranges) + // to the supported policy indices. + // Conversely, 'D-map' maps policy indices to local or global policy identifiers. + Digest fabrid = 1001; } + message PathSegmentUnsignedExtensions { // Optional EPIC extension. proto.control_plane.experimental.v1.EPICDetachedExtension epic = 1000; + // Optional FABRID extension + proto.control_plane.experimental.v1.FABRIDDetachedExtension fabrid = 1001; } diff --git a/proto/daemon/v1/BUILD.bazel b/proto/daemon/v1/BUILD.bazel index 8e53589e34..005e42f1fa 100644 --- a/proto/daemon/v1/BUILD.bazel +++ b/proto/daemon/v1/BUILD.bazel @@ -4,9 +4,11 @@ proto_library( name = "daemon", srcs = [ "daemon.proto", + "daemon_fabrid.proto", ], visibility = ["//visibility:public"], deps = [ + "//proto/control_plane/experimental/v1:experimental", "//proto/drkey/v1:drkey", "@com_google_protobuf//:duration_proto", "@com_google_protobuf//:empty_proto", diff --git a/proto/daemon/v1/daemon.proto b/proto/daemon/v1/daemon.proto index 0697195fc1..243ad9ea3e 100644 --- a/proto/daemon/v1/daemon.proto +++ b/proto/daemon/v1/daemon.proto @@ -22,6 +22,7 @@ import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "proto/drkey/v1/drkey.proto"; +import "proto/daemon/v1/daemon_fabrid.proto"; service DaemonService { // Return a set of paths to the requested destination. @@ -44,6 +45,8 @@ service DaemonService { rpc DRKeyHostAS (DRKeyHostASRequest) returns (DRKeyHostASResponse) {} // DRKeyHostHost returns a key that matches the request. rpc DRKeyHostHost (DRKeyHostHostRequest) returns (DRKeyHostHostResponse) {} + // FabridKeys returns the DRKeys for FABRID + rpc FabridKeys (proto.daemon.v1.FabridKeysRequest) returns (proto.daemon.v1.FabridKeysResponse) {} } message PathsRequest { @@ -56,6 +59,9 @@ message PathsRequest { bool refresh = 3; // Request hidden paths instead of standard paths. bool hidden = 4; + // Choose to have the daemon fetch any detached fabrid maps to fill the policies attribute in + // the metadata. + bool fetch_fabrid_detached_maps = 5; } message PathsResponse { @@ -114,6 +120,11 @@ message Path { // Entry i describes the carbon intensity between interface i and i+1. // Consequently, there are N-1 entries for N interfaces. repeated int64 carbon_intensity = 13; + // FabridInfo contains the FABRID-specific information for each hop on the path, specifically + // whether the AS in the hop supports FABRID, the possible policies, and whether the AS's FABRID + // maps were detached during beaconing (requiring manual fetching by the end host to fill the + // missing policies list). + repeated proto.daemon.v1.FabridInfo fabrid_info = 14; } message EpicAuths { diff --git a/proto/daemon/v1/daemon_fabrid.proto b/proto/daemon/v1/daemon_fabrid.proto new file mode 100644 index 0000000000..e84670158b --- /dev/null +++ b/proto/daemon/v1/daemon_fabrid.proto @@ -0,0 +1,67 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +syntax = "proto3"; + +option go_package = "github.com/scionproto/scion/pkg/proto/daemon"; + +package proto.daemon.v1; + +import "google/protobuf/timestamp.proto"; +import "proto/control_plane/experimental/v1/fabrid_extensions.proto"; + +message FabridInfo { + // Enabled describes whether the hop supports Fabrid. + bool enabled = 1; + // Digest contains the digest for the FABRID map of this hop. + bytes digest = 2; + // Policies contains the fabrid policy identifiers that are supported for this hop. + repeated FabridPolicy policies = 3; + // Detached indicates whether the FABRID map has been detached for this hop. + bool detached = 4; +} + +message FabridPolicy { + // The identifier for the policy, either local or global: + proto.control_plane.experimental.v1.FABRIDPolicyIdentifier policy_identifier = 1; + // The local index, this may differ between similar policy identifiers, as it is specific to a hop. + uint32 policy_index = 2; +} + +message FabridKeysRequest { + // The source host + string src_host = 1; + // The destination AS + uint64 dst_as = 2; + // A list of ASes for which the AS-Host Key should be fetched + repeated uint64 path_ases = 3; + // The destination host. Only required if the path key should be fetched too + optional string dst_host = 4; +} + +message FabridKeyResponse { + // Begin of validity period of DRKey. + google.protobuf.Timestamp epoch_begin = 1; + // End of validity period of DRKey. + google.protobuf.Timestamp epoch_end = 2; + // Level2 key. + bytes key = 3; +} + +message FabridKeysResponse { + // The FABRID AS-Host DRKeys + repeated FabridKeyResponse as_host_keys = 1; + // The FABRID path key + optional FabridKeyResponse host_host_key = 2; +} \ No newline at end of file diff --git a/proto/drkey/v1/drkey.proto b/proto/drkey/v1/drkey.proto index 560fe309cd..910c8e5e83 100644 --- a/proto/drkey/v1/drkey.proto +++ b/proto/drkey/v1/drkey.proto @@ -23,6 +23,8 @@ enum Protocol{ PROTOCOL_GENERIC_UNSPECIFIED = 0; // SCMP protocol PROTOCOL_SCMP = 1; + // FABRID protocol + PROTOCOL_FABRID = 2; reserved 65536 to max; // only 16-bit values allowed } diff --git a/router/BUILD.bazel b/router/BUILD.bazel index f60cbb7b19..c25ff8798a 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -4,7 +4,9 @@ go_library( name = "go_default_library", srcs = [ "connector.go", + "connector_fabrid.go", "dataplane.go", + "dataplane_fabrid.go", "fnv1aCheap.go", "metrics.go", "svc.go", @@ -15,6 +17,7 @@ go_library( "//pkg/addr:go_default_library", "//pkg/drkey:go_default_library", "//pkg/experimental/epic:go_default_library", + "//pkg/experimental/fabrid/crypto:go_default_library", "//pkg/log:go_default_library", "//pkg/private/common:go_default_library", "//pkg/private/processmetrics:go_default_library", @@ -22,6 +25,7 @@ go_library( "//pkg/private/util:go_default_library", "//pkg/scrypto:go_default_library", "//pkg/slayers:go_default_library", + "//pkg/slayers/extension:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", @@ -45,6 +49,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "dataplane_internal_fabrid_test.go", "dataplane_internal_test.go", "dataplane_test.go", "export_test.go", @@ -53,13 +58,17 @@ go_test( embed = [":go_default_library"], deps = [ "//pkg/addr:go_default_library", + "//pkg/drkey:go_default_library", "//pkg/experimental/epic:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/experimental/fabrid/crypto:go_default_library", "//pkg/private/ptr:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", "//pkg/private/xtest:go_default_library", "//pkg/scrypto:go_default_library", "//pkg/slayers:go_default_library", + "//pkg/slayers/extension:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", diff --git a/router/cmd/router/BUILD.bazel b/router/cmd/router/BUILD.bazel index f1014ba346..53c1a82475 100644 --- a/router/cmd/router/BUILD.bazel +++ b/router/cmd/router/BUILD.bazel @@ -7,6 +7,7 @@ go_library( importpath = "github.com/scionproto/scion/router/cmd/router", visibility = ["//visibility:private"], deps = [ + "//pkg/addr:go_default_library", "//pkg/log:go_default_library", "//pkg/private/serrors:go_default_library", "//private/app:go_default_library", diff --git a/router/cmd/router/main.go b/router/cmd/router/main.go index 6a43e58796..3976edb3b7 100644 --- a/router/cmd/router/main.go +++ b/router/cmd/router/main.go @@ -26,6 +26,7 @@ import ( "github.com/go-chi/cors" "golang.org/x/sync/errgroup" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/private/app" @@ -61,6 +62,7 @@ func realMain(ctx context.Context) error { DataPlane: router.DataPlane{ Metrics: metrics, ExperimentalSCMPAuthentication: globalCfg.Features.ExperimentalSCMPAuthentication, + DRKeyProvider: &control.DRKeyProvider{}, }, ReceiveBufferSize: globalCfg.Router.ReceiveBufferSize, SendBufferSize: globalCfg.Router.SendBufferSize, @@ -126,6 +128,32 @@ func realMain(ctx context.Context) error { defer log.HandlePanic() return globalCfg.Metrics.ServePrometheus(errCtx) }) + dp.DataPlane.DRKeyProvider.Init() + controlServiceAddr, err := controlConfig.Topo.Anycast(addr.SvcCS) + if err != nil { + return err + } + fetcher, err := control.NewFetcher(controlConfig.BR.InternalAddr.Addr().String(), + controlServiceAddr.String(), dp) + if err != nil { + return err + } + if len(globalCfg.Router.DRKey) != 0 { + go func() { + defer log.HandlePanic() + fetcher.StartSecretUpdater(globalCfg.Router.DRKey) + }() + } + if globalCfg.Router.Fabrid { + go func() { + defer log.HandlePanic() + interfaces := make([]uint16, len(controlConfig.BR.IFIDs)) + for i, iface := range controlConfig.BR.IFIDs { + interfaces[i] = uint16(iface) + } + fetcher.StartFabridPolicyFetcher() + }() + } g.Go(func() error { defer log.HandlePanic() runConfig := &router.RunConfig{ diff --git a/router/config/config.go b/router/config/config.go index eeb0308fa1..60a460a6d9 100644 --- a/router/config/config.go +++ b/router/config/config.go @@ -20,6 +20,7 @@ package config import ( "io" "runtime" + "slices" "time" "github.com/scionproto/scion/pkg/log" @@ -42,12 +43,14 @@ type Config struct { } type RouterConfig struct { - ReceiveBufferSize int `toml:"receive_buffer_size,omitempty"` - SendBufferSize int `toml:"send_buffer_size,omitempty"` - NumProcessors int `toml:"num_processors,omitempty"` - NumSlowPathProcessors int `toml:"num_slow_processors,omitempty"` - BatchSize int `toml:"batch_size,omitempty"` - BFD BFD `toml:"bfd,omitempty"` + ReceiveBufferSize int `toml:"receive_buffer_size,omitempty"` + SendBufferSize int `toml:"send_buffer_size,omitempty"` + NumProcessors int `toml:"num_processors,omitempty"` + NumSlowPathProcessors int `toml:"num_slow_processors,omitempty"` + BatchSize int `toml:"batch_size,omitempty"` + DRKey []string `toml:"drkey,omitempty"` + Fabrid bool `toml:"fabrid,omitempty"` + BFD BFD `toml:"bfd,omitempty"` // TODO: These two values were introduced to override the port range for // configured router in the context of acceptance tests. However, this // introduces two sources for the port configuration. We should remove this @@ -85,6 +88,10 @@ func (cfg *RouterConfig) Validate() error { if cfg.NumSlowPathProcessors < 1 { return serrors.New("Provided router config is invalid. NumSlowPathProcessors < 1") } + if cfg.Fabrid && !slices.Contains(cfg.DRKey, "FABRID") { + return serrors.New("Provided router config is invalid." + + "Enabling FABRID requires adding it to the DRKey protocols.") + } if cfg.DispatchedPortStart != nil { if cfg.DispatchedPortEnd == nil { return serrors.New("provided router config is invalid. " + diff --git a/router/connector_fabrid.go b/router/connector_fabrid.go new file mode 100644 index 0000000000..ea72442c30 --- /dev/null +++ b/router/connector_fabrid.go @@ -0,0 +1,30 @@ +// Copyright 2024 ETH Zurich +// +// 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 router + +import "github.com/scionproto/scion/router/control" + +func (c *Connector) AddDRKeySecret(protocolID int32, sv control.SecretValue) error { + c.mtx.Lock() + defer c.mtx.Unlock() + return c.DataPlane.DRKeyProvider.AddSecret(protocolID, sv) +} + +func (c *Connector) UpdateFabridPolicies(ipRangePolicies map[uint32][]*control.PolicyIPRange, + interfacePolicies map[uint64]uint32) error { + c.mtx.Lock() + defer c.mtx.Unlock() + return c.DataPlane.UpdateFabridPolicies(ipRangePolicies, interfacePolicies) +} diff --git a/router/control/BUILD.bazel b/router/control/BUILD.bazel index 5cd4a3bee5..9a692fec8c 100644 --- a/router/control/BUILD.bazel +++ b/router/control/BUILD.bazel @@ -4,17 +4,26 @@ go_library( name = "go_default_library", srcs = [ "conf.go", + "drkey.go", + "fetcher.go", "iactx.go", ], importpath = "github.com/scionproto/scion/router/control", visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", + "//pkg/drkey:go_default_library", + "//pkg/drkey/specific:go_default_library", "//pkg/log:go_default_library", "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", + "//pkg/proto/control_plane:go_default_library", + "//pkg/proto/control_plane/experimental:go_default_library", + "//pkg/proto/drkey:go_default_library", "//private/keyconf:go_default_library", "//private/topology:go_default_library", + "@org_golang_google_grpc//:go_default_library", + "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", "@org_golang_x_crypto//pbkdf2:go_default_library", ], ) diff --git a/router/control/conf.go b/router/control/conf.go index 7c4ecd8bc3..afff63dff1 100644 --- a/router/control/conf.go +++ b/router/control/conf.go @@ -38,6 +38,9 @@ type Dataplane interface { DelSvc(ia addr.IA, svc addr.SVC, a *net.UDPAddr) error SetKey(ia addr.IA, index int, key []byte) error SetPortRange(start, end uint16) + AddDRKeySecret(protocolID int32, sv SecretValue) error + UpdateFabridPolicies(ipRangePolicies map[uint32][]*PolicyIPRange, + interfacePolicies map[uint64]uint32) error } // BFD is the configuration for the BFD sessions. diff --git a/router/control/drkey.go b/router/control/drkey.go new file mode 100644 index 0000000000..e5134ff48b --- /dev/null +++ b/router/control/drkey.go @@ -0,0 +1,133 @@ +// Copyright 2024 ETH Zurich +// +// 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 control + +import ( + "errors" + "sync" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey/specific" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/serrors" + pb "github.com/scionproto/scion/pkg/proto/drkey" +) + +const pastValidity time.Duration = time.Second * 5 + +var errDRKeySecretInvalid = errors.New("no valid drkey secret for provided time period") +var errDRKeyNotInitialized = errors.New("drkey not initialized") + +var nullByte = [16]byte{} + +type DRKeyProvider struct { + drKeySecrets [][2]*SecretValue + // determines whether index 0 or index 1 should be overwritten next + drKeySecretNextOverwrite []uint8 + mtx sync.Mutex +} + +// Init initializes the necessary data structures for the DRKeyProvider. +func (d *DRKeyProvider) Init() { + d.mtx.Lock() + defer d.mtx.Unlock() + log.Debug("Initialize DRKey provider") + numDRKeyProtocols := len(pb.Protocol_value) + d.drKeySecrets = make([][2]*SecretValue, numDRKeyProtocols) + for i := 0; i < numDRKeyProtocols; i++ { + d.drKeySecrets[i] = [2]*SecretValue{ + {}, + {}, + } + } + d.drKeySecretNextOverwrite = make([]uint8, numDRKeyProtocols) +} + +// AddSecret registers the DRKey secret value for a particular protocol. +// The DRKeyProvider holds at most 2 secret values for a protocol and when registering a new one +// it will overwrite the older secret value. +func (d *DRKeyProvider) AddSecret(protocolID int32, sv SecretValue) error { + d.mtx.Lock() + defer d.mtx.Unlock() + if d.drKeySecrets == nil { + return errDRKeyNotInitialized + } + if int(protocolID) > len(d.drKeySecrets) { + return serrors.New("Error while adding a new drkey. ProtocolID too large", + "protocolID", protocolID) + } + nextOverwrite := d.drKeySecretNextOverwrite[protocolID] + d.drKeySecrets[protocolID][nextOverwrite] = &sv + log.Debug("Registered new DRKey", "protocol", protocolID, "from", sv.EpochBegin, + "to", sv.EpochEnd) + // switch nextOverwrite from 0 to 1 or from 1 to 0 + d.drKeySecretNextOverwrite[protocolID] = 1 - nextOverwrite + return nil +} + +// Returns the secret value for a protocol that is valid at a specific point in time, +// or an error, if no matching secret value is stored. +func (d *DRKeyProvider) getSecret(protocolID int32, t time.Time) (*SecretValue, error) { + if d.drKeySecrets == nil { + return nil, errDRKeyNotInitialized + } + secrets := d.drKeySecrets[protocolID] + since := time.Since(t) + if since > pastValidity { + return nil, serrors.New("time after validity window", "t", t, "since", since, + "validityPeriod", pastValidity) + } + for _, sv := range secrets { + if t.After(sv.EpochBegin) && sv.EpochEnd.After(t) { + return sv, nil + } + } + return nil, errDRKeySecretInvalid +} + +// DeriveASASKey derives an Level1 DRKey that is valid for a specific protocol and AS at a +// specific point in time, given a matching secret value is stored. +func (d *DRKeyProvider) DeriveASASKey(protocolID int32, t time.Time, srcAS addr.IA) ([16]byte, + error) { + drv := specific.Deriver{} + secret, err := d.getSecret(protocolID, t) + if err != nil { + return nullByte, err + } + asToAsKey, err := drv.DeriveLevel1(srcAS, secret.Key) + if err != nil { + return nullByte, err + } + return asToAsKey, nil +} + +// DeriveASASKey derives an AS-Host DRKey that is valid for a specific protocol and AS-Host at a +// specific point in time, given a matching secret value is stored. +func (d *DRKeyProvider) DeriveASHostKey(protocolID int32, t time.Time, srcAS addr.IA, src string) ( + [16]byte, error) { + + drv := specific.Deriver{} + asToAsKey, err := d.DeriveASASKey(protocolID, t, srcAS) + if err != nil { + return nullByte, err + } + asToHostKey, err := drv.DeriveASHost(src, asToAsKey) + if err != nil { + return nullByte, err + } + + return asToHostKey, nil +} diff --git a/router/control/fetcher.go b/router/control/fetcher.go new file mode 100644 index 0000000000..40dcad94b8 --- /dev/null +++ b/router/control/fetcher.go @@ -0,0 +1,221 @@ +// Copyright 2024 ETH Zürich +// +// 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 control + +import ( + "context" + "net" + "time" + + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/serrors" + cppb "github.com/scionproto/scion/pkg/proto/control_plane" + "github.com/scionproto/scion/pkg/proto/control_plane/experimental" + drpb "github.com/scionproto/scion/pkg/proto/drkey" +) + +type SecretValue struct { + EpochBegin time.Time + EpochEnd time.Time + Key [16]byte +} + +type PolicyIPRange struct { + MPLSLabel uint32 + IPPrefix *net.IPNet +} + +type Fetcher struct { + localAddr *net.TCPAddr + remoteAddr *net.TCPAddr + dp Dataplane +} + +// NewFetcher returns a new fetcher that is used to make queries to the control service. +func NewFetcher(localIP string, csAddr string, dp Dataplane) (*Fetcher, error) { + localAddr := &net.TCPAddr{ + IP: net.ParseIP(localIP), + Port: 0, + } + remoteTcpAddr, err := net.ResolveTCPAddr("tcp", csAddr) + if err != nil { + return nil, err + } + f := &Fetcher{ + localAddr: localAddr, + remoteAddr: remoteTcpAddr, + dp: dp, + } + return f, nil +} + +// StartFabridPolicyFetcher starts the FABRID policy fetcher that fetches the FABRID +// policies from the local control service every 30 minutes. +func (f *Fetcher) StartFabridPolicyFetcher() { + retryAfterErrorDuration := 10 * time.Second + for { + mplsPolicyResp, err := f.queryFabridPolicies() + + if err != nil { + log.Debug("Error while querying the FABRID policies from local control service", + "err", err) + time.Sleep(retryAfterErrorDuration) + continue + } + if !mplsPolicyResp.Update { + time.Sleep(30 * time.Minute) + continue + } + + log.Debug("Updated FABRID policies") + err = f.dp.UpdateFabridPolicies(ipPoliciesMapFromPB(mplsPolicyResp.MplsIpMap), + mplsPolicyResp.MplsInterfacePoliciesMap) + if err != nil { + log.Debug("Error while adding FABRID policies", "err", err) + time.Sleep(retryAfterErrorDuration) + continue + } + + time.Sleep(30 * time.Minute) + } +} + +func ipPoliciesMapFromPB(mplsPolicyResp map[uint32]*experimental. + MPLSIPArray) map[uint32][]*PolicyIPRange { + res := make(map[uint32][]*PolicyIPRange) + for key, ipArray := range mplsPolicyResp { + + for _, ipRange := range ipArray.Entry { + var m net.IPMask + if len(ipRange.Ip) == 4 { + m = net.CIDRMask(int(ipRange.Prefix), 8*net.IPv4len) + } else { + m = net.CIDRMask(int(ipRange.Prefix), 8*net.IPv6len) + } + res[key] = append(res[key], &PolicyIPRange{ + IPPrefix: &net.IPNet{IP: ipRange.Ip, Mask: m}, + MPLSLabel: ipRange.MplsLabel, + }) + } + } + return res +} + +func (f *Fetcher) queryFabridPolicies() (*experimental.MPLSMapResponse, error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + dialer := func(ctx context.Context, addr string) (net.Conn, error) { + return net.DialTCP("tcp", f.localAddr, f.remoteAddr) + } + grpcconn, err := grpc.DialContext(ctx, f.remoteAddr.String(), + grpc.WithInsecure(), grpc.WithContextDialer(dialer)) + if err != nil { + return nil, err + } + defer grpcconn.Close() + client := experimental.NewFABRIDIntraServiceClient(grpcconn) + rep, err := client.MPLSMap(ctx, + &experimental.MPLSMapRequest{ + Hash: nil, + }) + if err != nil { + return nil, serrors.WrapStr("requesting policy", err) + } + return rep, err +} + +// StartSecretUpdater is responsible for querying the local control service to request the +// DRKey secret values for the registered DRKey protocols. (e.g. FABRID, SCMP, ...) +// It will automatically register the received DRKey secrets in the dataplane and start +// prefetching the upcoming secret values 3 minutes before they become valid. +func (f *Fetcher) StartSecretUpdater(protocols []string) { + retryAfterErrorDuration := 5 * time.Second + prefetchTime := time.Minute * 3 + runProtocol := func(protocolID drkey.Protocol) { + // First we make sure that we have a secret that is valid now. + // After that we start prefetching. In case we initially receive a secret value + // that expires before the prefetch time, we prefetch the new secret value immediately. + isPrefetching := false + for { + t := time.Now() + if isPrefetching { + t = t.Add(prefetchTime) + } + sv, err := f.queryASSecret(protocolID, t) + if err != nil { + log.Debug("Error while querying secret value from local control service", + "protocol", protocolID, "err", err) + time.Sleep(retryAfterErrorDuration) + continue + } + err = f.dp.AddDRKeySecret(int32(protocolID), sv) + if err != nil { + log.Debug("Error while adding drkey", "protocol", protocolID, "err", err) + time.Sleep(retryAfterErrorDuration) + continue + } + sleepTime := max(time.Until(sv.EpochEnd)-prefetchTime, 0) + time.Sleep(sleepTime) + isPrefetching = true + } + } + for _, p := range protocols { + pID, ok := drkey.ProtocolStringToId("PROTOCOL_" + p) + if ok { + log.Debug("Register DRKey secret fetcher for", "protocol", p) + go func(protocol drkey.Protocol) { + defer log.HandlePanic() + runProtocol(protocol) + }(pID) + } + } +} + +func (f *Fetcher) queryASSecret( + protocolID drkey.Protocol, minValStart time.Time) (SecretValue, error) { + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + dialer := func(ctx context.Context, addr string) (net.Conn, error) { + return net.DialTCP("tcp", f.localAddr, f.remoteAddr) + } + grpcconn, err := grpc.DialContext(ctx, f.remoteAddr.String(), + grpc.WithInsecure(), grpc.WithContextDialer(dialer)) + if err != nil { + return SecretValue{}, err + } + defer grpcconn.Close() + client := cppb.NewDRKeyIntraServiceClient(grpcconn) + req := &cppb.DRKeySecretValueRequest{ + ProtocolId: drpb.Protocol(protocolID), + ValTime: timestamppb.New(minValStart), + } + res, err := client.DRKeySecretValue(ctx, req) + if err != nil { + return SecretValue{}, err + } + newKey := [16]byte{} + copy(newKey[:16], res.Key[:16]) + sv := SecretValue{ + EpochBegin: res.EpochBegin.AsTime(), + EpochEnd: res.EpochEnd.AsTime(), + Key: newKey, + } + return sv, nil +} diff --git a/router/dataplane.go b/router/dataplane.go index 02324114d5..55b1b2b094 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -38,12 +38,14 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/drkey" libepic "github.com/scionproto/scion/pkg/experimental/epic" + "github.com/scionproto/scion/pkg/experimental/fabrid/crypto" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/processmetrics" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/scrypto" "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/empty" "github.com/scionproto/scion/pkg/slayers/path/epic" @@ -91,24 +93,27 @@ type BatchConn interface { // from multiple sockets, performs routing, and sends them to their destinations // (after updating the path, if that is needed). type DataPlane struct { - interfaces map[uint16]BatchConn - external map[uint16]BatchConn - linkTypes map[uint16]topology.LinkType - neighborIAs map[uint16]addr.IA - peerInterfaces map[uint16]uint16 - internal BatchConn - internalIP netip.Addr - internalNextHops map[uint16]*net.UDPAddr - svc *services - macFactory func() hash.Hash - bfdSessions map[uint16]bfdSession - localIA addr.IA - mtx sync.Mutex - running bool - Metrics *Metrics - forwardingMetrics map[uint16]interfaceMetrics - dispatchedPortStart uint16 - dispatchedPortEnd uint16 + interfaces map[uint16]BatchConn + external map[uint16]BatchConn + linkTypes map[uint16]topology.LinkType + neighborIAs map[uint16]addr.IA + peerInterfaces map[uint16]uint16 + internal BatchConn + internalIP netip.Addr + internalNextHops map[uint16]*net.UDPAddr + svc *services + macFactory func() hash.Hash + bfdSessions map[uint16]bfdSession + localIA addr.IA + mtx sync.Mutex + running bool + Metrics *Metrics + DRKeyProvider *control.DRKeyProvider + fabridPolicyIPRangeMap map[uint32][]*control.PolicyIPRange + fabridPolicyInterfaceMap map[uint64]uint32 + forwardingMetrics map[uint16]interfaceMetrics + dispatchedPortStart uint16 + dispatchedPortEnd uint16 ExperimentalSCMPAuthentication bool @@ -595,6 +600,7 @@ type packet struct { trafficType trafficType // The goods rawPacket []byte + mplsLabel uint8 } type slowPacket struct { @@ -747,6 +753,7 @@ func (d *DataPlane) runProcessor(id int, q <-chan packet, } p.rawPacket = result.OutPkt p.dstAddr = result.OutAddr + p.mplsLabel = uint8(processor.mplsLabel) p.trafficType = result.TrafficType select { case fwCh <- p: @@ -1023,10 +1030,11 @@ func readUpTo(c <-chan packet, n int, needsBlocking bool, pkts []packet) int { func newPacketProcessor(d *DataPlane) *scionPacketProcessor { p := &scionPacketProcessor{ - d: d, - buffer: gopacket.NewSerializeBuffer(), - mac: d.macFactory(), - macInputBuffer: make([]byte, max(path.MACBufferSize, libepic.MACBufferSize)), + d: d, + buffer: gopacket.NewSerializeBuffer(), + mac: d.macFactory(), + macInputBuffer: make([]byte, max(path.MACBufferSize, libepic.MACBufferSize)), + fabridInputBuffer: make([]byte, crypto.FabridMacInputSize), } p.scionLayer.RecyclePaths() return p @@ -1048,9 +1056,13 @@ func (p *scionPacketProcessor) reset() error { p.mac.Reset() p.cachedMac = nil // Reset hbh layer - p.hbhLayer = slayers.HopByHopExtnSkipper{} + p.hbhLayer = slayers.HopByHopExtn{} // Reset e2e layer p.e2eLayer = slayers.EndToEndExtnSkipper{} + p.identifier = nil + p.fabrid = nil + p.mplsLabel = 0 + p.nextHop = nil return nil } @@ -1222,7 +1234,7 @@ type scionPacketProcessor struct { // scionLayer is the SCION gopacket layer. scionLayer slayers.SCION - hbhLayer slayers.HopByHopExtnSkipper + hbhLayer slayers.HopByHopExtn e2eLayer slayers.EndToEndExtnSkipper // last is the last parsed layer, i.e. either &scionLayer, &hbhLayer or &e2eLayer lastLayer gopacket.DecodingLayer @@ -1246,8 +1258,22 @@ type scionPacketProcessor struct { // bfdLayer is reusable buffer for parsing BFD messages bfdLayer layers.BFD + + identifier *extension.IdentifierOption + fabrid *extension.FabridOption + fabridInputBuffer []byte + mplsLabel uint32 + // IP of the next hop. Only valid for the inbound or AS transit cases + nextHop *net.UDPAddr + transitType transitType } +const ( + ingressEgressSameRouter transitType = iota + ingressEgressDifferentRouter + internalTraffic +) + type slowPathType int const ( @@ -1835,6 +1861,12 @@ func (p *scionPacketProcessor) process() (processResult, error) { if err != nil { return r, err } + + p.transitType = internalTraffic + p.nextHop = a + if err := p.processHbhOptions(0); err != nil { + return processResult{}, err + } return processResult{OutAddr: a, OutPkt: p.rawPkt, TrafficType: ttIn}, nil } @@ -1873,6 +1905,10 @@ func (p *scionPacketProcessor) process() (processResult, error) { } egressID := p.egressInterface() if _, ok := p.d.external[egressID]; ok { + p.transitType = ingressEgressSameRouter + if err := p.processHbhOptions(egressID); err != nil { + return processResult{}, err + } // Not ASTransit in if err := p.processEgress(); err != nil { return processResult{}, err @@ -1893,6 +1929,11 @@ func (p *scionPacketProcessor) process() (processResult, error) { } // ASTransit in: pkt leaving this AS through another BR. if a, ok := p.d.internalNextHops[egressID]; ok { + p.transitType = ingressEgressDifferentRouter + p.nextHop = a + if err := p.processHbhOptions(egressID); err != nil { + return processResult{}, err + } return processResult{OutAddr: a, OutPkt: p.rawPkt, TrafficType: ttInTransit}, nil } errCode := slayers.SCMPCodeUnknownHopFieldEgress @@ -2573,6 +2614,8 @@ func nextHdr(layer gopacket.DecodingLayer) slayers.L4ProtocolType { return v.NextHdr case *slayers.EndToEndExtnSkipper: return v.NextHdr + case *slayers.HopByHopExtn: + return v.NextHdr case *slayers.HopByHopExtnSkipper: return v.NextHdr default: diff --git a/router/dataplane_fabrid.go b/router/dataplane_fabrid.go new file mode 100644 index 0000000000..0d0959ee5f --- /dev/null +++ b/router/dataplane_fabrid.go @@ -0,0 +1,208 @@ +// Copyright 2024 ETH Zurich +// +// 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 router + +import ( + "net" + + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid/crypto" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" + "github.com/scionproto/scion/router/control" +) + +type transitType int + +func (d *DataPlane) UpdateFabridPolicies(ipRangePolicies map[uint32][]*control.PolicyIPRange, + interfacePolicies map[uint64]uint32) error { + d.mtx.Lock() + defer d.mtx.Unlock() + // TODO(rohrerj): check for concurrency issues + // when an update happens during reading + d.fabridPolicyIPRangeMap = ipRangePolicies + d.fabridPolicyInterfaceMap = interfacePolicies + return nil +} + +func (p *scionPacketProcessor) processFabrid(egressIF uint16) error { + meta := p.fabrid.HopfieldMetadata[0] + src, err := p.scionLayer.SrcAddr() + if err != nil { + return err + } + var key [16]byte + if p.fabrid.HopfieldMetadata[0].ASLevelKey { + key, err = p.d.DRKeyProvider.DeriveASASKey(int32(drkey.FABRID), p.identifier.Timestamp, + p.scionLayer.SrcIA) + } else { + key, err = p.d.DRKeyProvider.DeriveASHostKey(int32(drkey.FABRID), p.identifier.Timestamp, + p.scionLayer.SrcIA, src.String()) + } + if err != nil { + return err + } + policyID, err := crypto.ComputePolicyID(meta, p.identifier, key[:]) + if err != nil { + return err + } + err = crypto.VerifyAndUpdate(meta, p.identifier, &p.scionLayer, p.fabridInputBuffer, key[:], + p.ingressID, egressIF) + if err != nil { + return err + } + // Check / set MPLS label only if policy ID != 0 + // and only if the packet will be sent within the AS or to another router of the local AS + if policyID != 0 { + var mplsLabel uint32 + switch p.transitType { + case ingressEgressDifferentRouter: + mplsLabel, err = p.d.getFabridMplsLabelForInterface(uint32(p.ingressID), + uint32(policyID), uint32(egressIF)) + case internalTraffic: + mplsLabel, err = p.d.getFabridMplsLabel(uint32(p.ingressID), uint32(policyID), + p.nextHop.IP) + if err != nil { + mplsLabel, err = p.d.getFabridMplsLabelForInterface(uint32(p.ingressID), + uint32(policyID), 0) + } + case ingressEgressSameRouter: + return nil + } + if err != nil { + return err + } + p.mplsLabel = mplsLabel + } + return nil +} + +func (d *DataPlane) getFabridMplsLabelForInterface(ingressID uint32, policyIndex uint32, + egressID uint32) (uint32, error) { + + policyMapKey := uint64(ingressID)<<24 + uint64(egressID)<<8 + uint64(policyIndex) + mplsLabel, found := d.fabridPolicyInterfaceMap[policyMapKey] + if !found { + //lookup default (instead of using the ingressID as part of the key, use a 1 bit as MSB): + policyMapKey = 1<<63 + uint64(egressID)<<8 + uint64(policyIndex) + mplsLabel, found = d.fabridPolicyInterfaceMap[policyMapKey] + if !found { + return 0, serrors.New("Provided policyID is invalid", + "ingress", ingressID, "index", policyIndex, "egress", egressID) + } + } + return mplsLabel, nil +} + +func (d *DataPlane) getFabridMplsLabel(ingressID uint32, policyIndex uint32, + nextHopIP net.IP) (uint32, error) { + + policyMapKey := ingressID<<8 + policyIndex + ipRanges, found := d.fabridPolicyIPRangeMap[policyMapKey] + if !found { + //lookup default (instead of using the ingressID as part of the key, use a 1 bit as MSB): + policyMapKey = 1<<31 + policyIndex + ipRanges, found = d.fabridPolicyIPRangeMap[policyMapKey] + if !found { + return 0, serrors.New("Provided policyID is invalid", + "ingress", ingressID, "index", policyIndex) + } + } + var bestRange *control.PolicyIPRange + for _, r := range ipRanges { + if r.IPPrefix.Contains(nextHopIP) { + if bestRange == nil { + bestRange = r + } else { + bestPrefixLength, _ := bestRange.IPPrefix.Mask.Size() + currentPrefixLength, _ := r.IPPrefix.Mask.Size() + if currentPrefixLength > bestPrefixLength { + bestRange = r + } + } + } + } + if bestRange == nil { + return 0, serrors.New("Provided policy index is not valid for nexthop.", + "index", policyIndex, "next hop IP", nextHopIP) + } + return bestRange.MPLSLabel, nil +} + +func (p *scionPacketProcessor) processHbhOptions(egressIF uint16) error { + var err error + for _, opt := range p.hbhLayer.Options { + switch opt.OptType { + case slayers.OptTypeIdentifier: + if p.identifier != nil { + return serrors.New("Identifier HBH option provided multiple times") + } + // TODO(marcodermatt): Find cleaner solution for getting timestamp of first InfoField + baseTimestamp := p.infoField.Timestamp + if p.path.PathMeta.CurrINF > 0 { + firstInfoField, err := p.path.GetInfoField(0) + if err != nil { + return serrors.New("Failed to parse first InfoField") + } + baseTimestamp = firstInfoField.Timestamp + } + p.identifier, err = extension.ParseIdentifierOption(opt, baseTimestamp) + if err != nil { + return err + } + case slayers.OptTypeFabrid: + if p.fabrid != nil { + return serrors.New("FABRID HBH option provided multiple times") + } + if p.identifier == nil { + return serrors.New("Identifier HBH option must be present when using FABRID") + } + + // Calculate FABRID hop index + currHop := p.path.PathMeta.CurrHF + if !p.infoField.Peer { + currHop -= p.path.PathMeta.CurrINF + } + + // Skip if this is an intermediary egress router + if p.ingressID == 0 && currHop != 0 { + return nil + } + + // Calculate number of FABRID hops + numHFs := p.path.NumHops - p.path.NumINF + 1 + if p.infoField.Peer { + numHFs++ + } + fabrid, err := extension.ParseFabridOptionCurrentHop(opt, currHop, uint8(numHFs)) + if err != nil { + return err + } + if fabrid.HopfieldMetadata[0].FabridEnabled { + p.fabrid = fabrid + if err = p.processFabrid(egressIF); err != nil { + return err + } + if err = fabrid.HopfieldMetadata[0].SerializeTo(opt. + OptData[currHop*4:]); err != nil { + return err + } + } + default: + } + } + return err +} diff --git a/router/dataplane_internal_fabrid_test.go b/router/dataplane_internal_fabrid_test.go new file mode 100644 index 0000000000..c0e30fe052 --- /dev/null +++ b/router/dataplane_internal_fabrid_test.go @@ -0,0 +1,166 @@ +// Copyright 2024 ETH Zurich +// +// 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 router + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/router/control" +) + +func TestFabridPolicies(t *testing.T) { + type testcase struct { + name string + ipRangePolicies map[uint32][]*control.PolicyIPRange + interfacePolicies map[uint64]uint32 + packetIngress uint32 + packetEgress uint32 + useIPRange bool + packetPolicyIndex uint32 + nextHopIP net.IP + expectedMplsLabel uint32 + expectsError bool + } + testcases := []testcase{ + { + name: "ingress and policyindex tuple exists with single ip range", + ipRangePolicies: map[uint32][]*control.PolicyIPRange{ + 0xa<<8 + 0xf: { + &control.PolicyIPRange{ + MPLSLabel: 1, + IPPrefix: xtest.MustParseCIDR(t, "127.0.0.0/24"), + }, + }, + }, + expectedMplsLabel: 1, + packetIngress: 0xa, + packetPolicyIndex: 0xf, + nextHopIP: xtest.MustParseIP(t, "127.0.0.1"), + useIPRange: true, + }, + { + name: "ingress and policyindex tuple doesn't exist", + ipRangePolicies: map[uint32][]*control.PolicyIPRange{}, + expectedMplsLabel: 1, + packetIngress: 0xa, + packetPolicyIndex: 0xf, + nextHopIP: xtest.MustParseIP(t, "127.0.0.1"), + expectsError: true, + useIPRange: true, + }, + { + name: "ingress and policyindex tuple exists with multiple ip ranges", + ipRangePolicies: map[uint32][]*control.PolicyIPRange{ + 0xa<<8 + 0xf: { + &control.PolicyIPRange{ + MPLSLabel: 1, + IPPrefix: xtest.MustParseCIDR(t, "127.0.0.0/24"), + }, + &control.PolicyIPRange{ + MPLSLabel: 2, + IPPrefix: xtest.MustParseCIDR(t, "127.0.0.0/31"), + }, + &control.PolicyIPRange{ + MPLSLabel: 3, + IPPrefix: xtest.MustParseCIDR(t, "127.0.0.0/30"), + }, + &control.PolicyIPRange{ + MPLSLabel: 4, + IPPrefix: xtest.MustParseCIDR(t, "127.0.0.2/32"), + }, + }, + }, + expectedMplsLabel: 2, + packetIngress: 0xa, + packetPolicyIndex: 0xf, + nextHopIP: xtest.MustParseIP(t, "127.0.0.1"), + useIPRange: true, + }, + { + name: "ip range only exists for default value", + ipRangePolicies: map[uint32][]*control.PolicyIPRange{ + 1<<31 + 0xf: { + &control.PolicyIPRange{ + MPLSLabel: 1, + IPPrefix: xtest.MustParseCIDR(t, "127.0.0.0/24"), + }, + }, + }, + expectedMplsLabel: 1, + packetIngress: 0xa, + packetPolicyIndex: 0xf, + nextHopIP: xtest.MustParseIP(t, "127.0.0.1"), + useIPRange: true, + }, + { + name: "mpls label exists for interface map", + interfacePolicies: map[uint64]uint32{ + 1<<24 + 2<<8 + 0xf: 7, + 1<<63 + 2<<8 + 0xf: 5, // default value + }, + expectedMplsLabel: 7, + packetIngress: 1, + packetEgress: 2, + packetPolicyIndex: 0xf, + useIPRange: false, + }, + { + name: "mpls label exists for interface map with default value", + interfacePolicies: map[uint64]uint32{ + 1<<63 + 2<<8 + 0xf: 5, // default value + }, + expectedMplsLabel: 5, + packetIngress: 1, + packetEgress: 2, + packetPolicyIndex: 0xf, + useIPRange: false, + }, + { + name: "mpls label doesnt exist for interface map", + interfacePolicies: map[uint64]uint32{}, + packetIngress: 1, + packetEgress: 2, + packetPolicyIndex: 0xf, + expectsError: true, + useIPRange: false, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + dp := &DataPlane{Metrics: metrics} + err := dp.UpdateFabridPolicies(tc.ipRangePolicies, tc.interfacePolicies) + assert.NoError(t, err) + var mplsLabel uint32 + if tc.useIPRange { + mplsLabel, err = dp.getFabridMplsLabel(tc.packetIngress, tc.packetPolicyIndex, + tc.nextHopIP) + } else { + mplsLabel, err = dp.getFabridMplsLabelForInterface(tc.packetIngress, + tc.packetPolicyIndex, tc.packetEgress) + } + if tc.expectsError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expectedMplsLabel, mplsLabel) + } + }) + } + +} diff --git a/router/dataplane_test.go b/router/dataplane_test.go index 7750bb1861..a0e34e8d5c 100644 --- a/router/dataplane_test.go +++ b/router/dataplane_test.go @@ -33,13 +33,17 @@ import ( "golang.org/x/net/ipv4" "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/drkey" libepic "github.com/scionproto/scion/pkg/experimental/epic" + "github.com/scionproto/scion/pkg/experimental/fabrid" + "github.com/scionproto/scion/pkg/experimental/fabrid/crypto" "github.com/scionproto/scion/pkg/private/ptr" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/private/xtest" "github.com/scionproto/scion/pkg/scrypto" "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/empty" "github.com/scionproto/scion/pkg/slayers/path/epic" @@ -237,6 +241,689 @@ func TestDataPlaneRun(t *testing.T) { testCases := map[string]struct { prepareDP func(*gomock.Controller, chan<- struct{}) *router.DataPlane }{ + "fabrid basic": { + prepareDP: func(ctrl *gomock.Controller, done chan<- struct{}) *router.DataPlane { + ret := &router.DataPlane{Metrics: metrics, DRKeyProvider: &control.DRKeyProvider{}} + ret.DRKeyProvider.Init() + key := []byte("testkey_xxxxxxxx") + dstIA := xtest.MustParseIA("4-ff00:0:411") + dstAddr := addr.MustParseHost("2.2.2.2") + srcIA := xtest.MustParseIA("2-ff00:0:222") + srcAddr := addr.MustParseHost("1.1.1.1") + + asDRKey := [16]byte{ + 0x00, 0x11, 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xff, + } + err := ret.DRKeyProvider.AddSecret(int32(drkey.FABRID), + control.SecretValue{ + Key: asDRKey, + EpochBegin: time.Now().Add(-time.Second), + EpochEnd: time.Now().AddDate(1, 0, 0), + }) + assert.NoError(t, err) + local := xtest.MustParseIA("1-ff00:0:110") + now := time.Now().Truncate(time.Millisecond) + identifier := extension.IdentifierOption{ + Timestamp: now, + PacketID: 0xabcd, + BaseTimestamp: uint32(now.Unix()), + } + + policyID := fabrid.PolicyID(0x0f) + _, ipPrefix, _ := net.ParseCIDR("0.0.0.0/0") + err = ret.UpdateFabridPolicies(map[uint32][]*control.PolicyIPRange{ + // ingress 3 with policy index 0x0f + (3<<8 + 0x0f): { + { + MPLSLabel: 1, + IPPrefix: ipPrefix, + }, + }, + }, nil) + assert.NoError(t, err) + + asToHostKey, err := ret.DRKeyProvider.DeriveASHostKey(int32(drkey.FABRID), now, + srcIA, srcAddr.String()) + assert.NoError(t, err) + encPolicyID, err := crypto.EncryptPolicyID(policyID, &identifier, asToHostKey[:]) + assert.NoError(t, err) + + mExternal := mock_router.NewMockBatchConn(ctrl) + infoField := path.InfoField{SegID: 0x111, ConsDir: true, + Timestamp: util.TimeToSecs(now)} + + mExternal.EXPECT().ReadBatch(gomock.Any()).DoAndReturn( + func(m underlayconn.Messages) (int, error) { + buf := gopacket.NewSerializeBuffer() + p := &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + CurrHF: 1, + SegLen: [3]uint8{3, 0, 0}, + }, + NumINF: 1, + NumHops: 3, + }, + InfoFields: []path.InfoField{ + infoField, + }, + HopFields: []path.HopField{ + {ConsIngress: 1, ConsEgress: 2}, + {ConsIngress: 3, ConsEgress: 4}, + {ConsIngress: 5, ConsEgress: 6}, + }, + } + p.HopFields[1].Mac = computeMAC(t, key, p.InfoFields[0], + p.HopFields[1]) + rawDstAddr := dstAddr.IP().As4() + rawSrcAddr := srcAddr.IP().As4() + s := slayers.SCION{ + NextHdr: slayers.HopByHopClass, + PathType: scion.PathType, + DstIA: dstIA, + SrcIA: srcIA, + SrcAddrType: slayers.T4Ip, + DstAddrType: slayers.T4Ip, + RawSrcAddr: rawSrcAddr[:], + RawDstAddr: rawDstAddr[:], + Path: p, + } + + identifierData := make([]byte, 8) + err = identifier.Serialize(identifierData) + assert.NoError(t, err) + + meta := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + FabridEnabled: true, + } + tmp := make([]byte, 100) + err = crypto.ComputeBaseHVF(meta, &identifier, &s, tmp, asToHostKey[:], + 3, 4) + assert.NoError(t, err) + + fopt := extension.FabridOption{ + HopfieldMetadata: []*extension.FabridHopfieldMetadata{ + {}, + meta, + {}, + }, + } + fabridData := make([]byte, 16) + err = fopt.SerializeTo(fabridData) + assert.NoError(t, err) + + hbh := slayers.HopByHopExtn{ + Options: []*slayers.HopByHopOption{ + { + OptType: slayers.OptTypeIdentifier, + OptData: identifierData, + OptDataLen: uint8(len(identifierData)), + }, + { + OptType: slayers.OptTypeFabrid, + OptData: fabridData, + OptDataLen: uint8(len(fabridData)), + }, + }, + } + err = gopacket.SerializeLayers(buf, + gopacket.SerializeOptions{FixLengths: true}, &s, &hbh) + assert.NoError(t, err) + raw := buf.Bytes() + copy(m[0].Buffers[0], raw) + m[0].N = len(raw) + m[0].Addr = &net.UDPAddr{IP: net.IP{10, 0, 200, 200}} + + return 1, nil + }, + ).Times(1) + mExternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + mExternal.EXPECT().WriteTo(gomock.Any(), + gomock.Any()).Return(0, nil).AnyTimes() + + mExternal2 := mock_router.NewMockBatchConn(ctrl) + mExternal2.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + mExternal2.EXPECT().WriteBatch(gomock.Any(), 0).DoAndReturn( + func(ms underlayconn.Messages, flags int) (int, error) { + if len(ms) != 1 { + assert.Fail(t, "len(ms)!=1", len(ms)) + return 0, nil + } + s := slayers.SCION{} + hbh := slayers.HopByHopExtn{} + _, err := router.DecodeLayers(ms[0].Buffers[0], &s, &hbh) + assert.NoError(t, err) + + containsFabrid := false + containsIdentifier := false + var foundIdentifier *extension.IdentifierOption + var foundFabrid *extension.FabridOption + + baseTs := infoField.Timestamp + for _, hbhOption := range hbh.Options { + switch hbhOption.OptType { + case slayers.OptTypeIdentifier: + containsIdentifier = true + foundIdentifier, err = extension.ParseIdentifierOption(hbhOption, + baseTs) + assert.NoError(t, err) + assert.Equal(t, identifier.Timestamp, foundIdentifier.Timestamp) + assert.Equal(t, identifier.PacketID, foundIdentifier.PacketID) + case slayers.OptTypeFabrid: + containsFabrid = true + if containsIdentifier { + foundFabrid, err = extension.ParseFabridOptionFullExtension( + hbhOption, 3) + assert.NoError(t, err) + meta := foundFabrid.HopfieldMetadata[1] + tmp := make([]byte, 100) + recomputedVerifiedHVF := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + FabridEnabled: true, + } + err = crypto.ComputeVerifiedHVF(recomputedVerifiedHVF, + foundIdentifier, &s, tmp, asToHostKey[:], 3, + 4) + assert.NoError(t, err) + assert.Equal(t, encPolicyID, meta.EncryptedPolicyID) + assert.Equal(t, recomputedVerifiedHVF.HopValidationField, + meta.HopValidationField) + } else { + assert.Fail(t, "identifier not present before "+ + "fabrid") + } + default: + + } + + } + assert.True(t, containsIdentifier) + assert.True(t, containsFabrid) + + done <- struct{}{} + return 1, nil + }).Times(1) + mExternal2.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + + l := control.LinkEnd{ + IA: local, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.100")}, + } + r1 := control.LinkEnd{ + IA: srcIA, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, + } + r2 := control.LinkEnd{ + IA: dstIA, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.300")}, + } + nobfd := control.BFD{Disable: ptr.To(true)} + + _ = ret.AddExternalInterface(3, mExternal, l, r1, nobfd) + _ = ret.AddLinkType(3, topology.Core) + _ = ret.AddExternalInterface(4, mExternal2, l, r2, nobfd) + _ = ret.AddLinkType(4, topology.Core) + + _ = ret.SetIA(local) + _ = ret.SetKey(key) + return ret + }, + }, + "fabrid mpls ingress egress different router": { + prepareDP: func(c1 *gomock.Controller, done chan<- struct{}) *router.DataPlane { + ret := &router.DataPlane{Metrics: metrics, DRKeyProvider: &control.DRKeyProvider{}} + ret.DRKeyProvider.Init() + key := []byte("testkey_xxxxxxxx") + dstIA := xtest.MustParseIA("4-ff00:0:411") + dstAddr := addr.MustParseHost("2.2.2.2") + srcIA := xtest.MustParseIA("2-ff00:0:222") + srcAddr := addr.MustParseHost("1.1.1.1") + + asDRKey := [16]byte{ + 0x00, 0x11, 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xff, + } + _ = ret.DRKeyProvider.AddSecret(int32(drkey.FABRID), + control.SecretValue{ + Key: asDRKey, + EpochBegin: time.Now().Add(-time.Second), + EpochEnd: time.Now().AddDate(1, 0, 0), + }) + local := xtest.MustParseIA("1-ff00:0:110") + now := time.Now().Truncate(time.Millisecond) + identifier := extension.IdentifierOption{ + Timestamp: now, + PacketID: 0xabcd, + BaseTimestamp: uint32(now.Unix()), + } + + policyID := fabrid.PolicyID(0x0f) + err := ret.UpdateFabridPolicies(nil, + map[uint64]uint32{ + 3<<24 + 2<<8 + 0x0f: 7, + }) + assert.NoError(t, err) + + asToHostKey, err := ret.DRKeyProvider.DeriveASHostKey(int32(drkey.FABRID), now, + srcIA, srcAddr.String()) + assert.NoError(t, err) + encPolicyID, err := crypto.EncryptPolicyID(policyID, &identifier, asToHostKey[:]) + assert.NoError(t, err) + + mExternal := mock_router.NewMockBatchConn(ctrl) + infoField := path.InfoField{SegID: 0x111, ConsDir: true, + Timestamp: util.TimeToSecs(now)} + + mExternal.EXPECT().ReadBatch(gomock.Any()).DoAndReturn( + func(m underlayconn.Messages) (int, error) { + buf := gopacket.NewSerializeBuffer() + p := &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + CurrHF: 1, + SegLen: [3]uint8{3, 0, 0}, + }, + NumINF: 1, + NumHops: 3, + }, + InfoFields: []path.InfoField{ + infoField, + }, + HopFields: []path.HopField{ + {ConsIngress: 1, ConsEgress: 2}, + {ConsIngress: 3, ConsEgress: 2}, + {ConsIngress: 5, ConsEgress: 6}, + }, + } + p.HopFields[1].Mac = computeMAC(t, key, p.InfoFields[0], + p.HopFields[1]) + rawDstAddr := dstAddr.IP().As4() + rawSrcAddr := srcAddr.IP().As4() + s := slayers.SCION{ + NextHdr: slayers.HopByHopClass, + PathType: scion.PathType, + DstIA: dstIA, + SrcIA: srcIA, + SrcAddrType: slayers.T4Ip, + DstAddrType: slayers.T4Ip, + RawSrcAddr: rawSrcAddr[:], + RawDstAddr: rawDstAddr[:], + Path: p, + } + + identifierData := make([]byte, 8) + err = identifier.Serialize(identifierData) + assert.NoError(t, err) + + meta := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + FabridEnabled: true, + } + tmp := make([]byte, 100) + err = crypto.ComputeBaseHVF(meta, &identifier, &s, tmp, asToHostKey[:], + 3, 2) + assert.NoError(t, err) + + fopt := extension.FabridOption{ + HopfieldMetadata: []*extension.FabridHopfieldMetadata{ + {}, + meta, + {}, + }, + } + fabridData := make([]byte, 16) + err = fopt.SerializeTo(fabridData) + assert.NoError(t, err) + hbh := slayers.HopByHopExtn{ + Options: []*slayers.HopByHopOption{ + { + OptType: slayers.OptTypeIdentifier, + OptData: identifierData, + OptDataLen: uint8(len(identifierData)), + }, + { + OptType: slayers.OptTypeFabrid, + OptData: fabridData, + OptDataLen: uint8(len(fabridData)), + }, + }, + } + err = gopacket.SerializeLayers(buf, + gopacket.SerializeOptions{FixLengths: true}, &s, &hbh) + assert.NoError(t, err) + raw := buf.Bytes() + copy(m[0].Buffers[0], raw) + m[0].N = len(raw) + m[0].Addr = &net.UDPAddr{IP: net.IP{10, 0, 200, 200}} + + return 1, nil + }, + ).Times(1) + mExternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + mExternal.EXPECT().WriteTo(gomock.Any(), + gomock.Any()).Return(0, nil).AnyTimes() + + mInternal := mock_router.NewMockBatchConn(ctrl) + mInternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + mInternal.EXPECT().WriteBatch(gomock.Any(), 0).DoAndReturn( + func(ms underlayconn.Messages, flags int) (int, error) { + if len(ms) != 1 { + assert.Fail(t, "len(ms)!=1", len(ms)) + return 0, nil + } + s := slayers.SCION{} + hbh := slayers.HopByHopExtn{} + _, err := router.DecodeLayers(ms[0].Buffers[0], &s, &hbh) + assert.NoError(t, err) + + containsFabrid := false + containsIdentifier := false + var foundIdentifier *extension.IdentifierOption + var foundFabrid *extension.FabridOption + + baseTs := infoField.Timestamp + for _, hbhOption := range hbh.Options { + switch hbhOption.OptType { + case slayers.OptTypeIdentifier: + containsIdentifier = true + foundIdentifier, err = extension.ParseIdentifierOption( + hbhOption, baseTs) + assert.NoError(t, err) + assert.Equal(t, identifier.Timestamp, foundIdentifier.Timestamp) + assert.Equal(t, identifier.PacketID, foundIdentifier.PacketID) + case slayers.OptTypeFabrid: + containsFabrid = true + if containsIdentifier { + foundFabrid, err = extension.ParseFabridOptionFullExtension( + hbhOption, 3) + assert.NoError(t, err) + meta := foundFabrid.HopfieldMetadata[1] + tmp := make([]byte, 100) + recomputedVerifiedHVF := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + FabridEnabled: true, + } + err = crypto.ComputeVerifiedHVF(recomputedVerifiedHVF, + foundIdentifier, &s, tmp, asToHostKey[:], 3, + 2) + assert.NoError(t, err) + assert.Equal(t, encPolicyID, meta.EncryptedPolicyID) + assert.Equal(t, recomputedVerifiedHVF.HopValidationField, + meta.HopValidationField) + } else { + assert.Fail(t, "identifier not present "+ + "before fabrid") + } + default: + } + } + assert.True(t, containsIdentifier) + assert.True(t, containsFabrid) + + done <- struct{}{} + return 1, nil + }).Times(1) + _ = ret.AddInternalInterface(mInternal, netip.Addr{}) + + l := control.LinkEnd{ + IA: local, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.100")}, + } + r1 := control.LinkEnd{ + IA: srcIA, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, + } + nobfd := control.BFD{Disable: ptr.To(true)} + + _ = ret.AddExternalInterface(3, mExternal, l, r1, nobfd) + _ = ret.AddLinkType(3, topology.Core) + + _ = ret.SetIA(local) + _ = ret.SetKey(key) + + nextHopAddr := xtest.MustParseUDPAddr(t, "127.0.0.2:8888") + err = ret.AddNextHop(2, l.Addr, nextHopAddr, nobfd, "") + assert.NoError(t, err) + err = ret.AddLinkType(2, topology.Core) + assert.NoError(t, err) + return ret + }, + }, + "fabrid mpls internal traffic": { + prepareDP: func(c1 *gomock.Controller, done chan<- struct{}) *router.DataPlane { + ret := &router.DataPlane{Metrics: metrics, DRKeyProvider: &control.DRKeyProvider{}} + ret.DRKeyProvider.Init() + key := []byte("testkey_xxxxxxxx") + dstIA := xtest.MustParseIA("4-ff00:0:411") + dstAddr := addr.MustParseHost("2.2.2.2") + srcIA := xtest.MustParseIA("2-ff00:0:222") + srcAddr := addr.MustParseHost("1.1.1.1") + + asDRKey := [16]byte{ + 0x00, 0x11, 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xff, + } + _ = ret.DRKeyProvider.AddSecret(int32(drkey.FABRID), + control.SecretValue{ + Key: asDRKey, + EpochBegin: time.Now().Add(-time.Second), + EpochEnd: time.Now().AddDate(1, 0, 0), + }) + local := dstIA + now := time.Now().Truncate(time.Millisecond) + identifier := extension.IdentifierOption{ + Timestamp: now, + PacketID: 0xabcd, + BaseTimestamp: uint32(now.Unix()), + } + + policyID := fabrid.PolicyID(0x0f) + _, ipPrefix, _ := net.ParseCIDR("2.2.2.0/24") + err := ret.UpdateFabridPolicies(map[uint32][]*control.PolicyIPRange{ + // ingress 3 with policy index 0x0f + (3<<8 + 0x0f): { + { + MPLSLabel: 7, + IPPrefix: ipPrefix, + }, + }, + }, nil) + + assert.NoError(t, err) + + asToHostKey, err := ret.DRKeyProvider.DeriveASHostKey(int32(drkey.FABRID), now, + srcIA, srcAddr.String()) + assert.NoError(t, err) + encPolicyID, err := crypto.EncryptPolicyID(policyID, &identifier, asToHostKey[:]) + assert.NoError(t, err) + + mExternal := mock_router.NewMockBatchConn(ctrl) + infoField := path.InfoField{SegID: 0x111, ConsDir: true, + Timestamp: util.TimeToSecs(now)} + + mExternal.EXPECT().ReadBatch(gomock.Any()).DoAndReturn( + func(m underlayconn.Messages) (int, error) { + buf := gopacket.NewSerializeBuffer() + p := &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + CurrHF: 2, + SegLen: [3]uint8{3, 0, 0}, + }, + NumINF: 1, + NumHops: 3, + }, + InfoFields: []path.InfoField{ + infoField, + }, + HopFields: []path.HopField{ + {ConsIngress: 1, ConsEgress: 2}, + {ConsIngress: 3, ConsEgress: 4}, + {ConsIngress: 3, ConsEgress: 0}, + }, + } + p.HopFields[2].Mac = computeMAC(t, key, p.InfoFields[0], + p.HopFields[2]) + rawDstAddr := dstAddr.IP().As4() + rawSrcAddr := srcAddr.IP().As4() + s := slayers.SCION{ + NextHdr: slayers.HopByHopClass, + PathType: scion.PathType, + DstIA: dstIA, + SrcIA: srcIA, + SrcAddrType: slayers.T4Ip, + DstAddrType: slayers.T4Ip, + RawSrcAddr: rawSrcAddr[:], + RawDstAddr: rawDstAddr[:], + Path: p, + } + + identifierData := make([]byte, 8) + err = identifier.Serialize(identifierData) + assert.NoError(t, err) + + meta := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + FabridEnabled: true, + } + tmp := make([]byte, 100) + err = crypto.ComputeBaseHVF(meta, &identifier, &s, tmp, asToHostKey[:], + 3, 0) + assert.NoError(t, err) + + fopt := extension.FabridOption{ + HopfieldMetadata: []*extension.FabridHopfieldMetadata{ + {}, + {}, + meta, + }, + } + fabridData := make([]byte, 16) + err = fopt.SerializeTo(fabridData) + assert.NoError(t, err) + hbh := slayers.HopByHopExtn{ + Options: []*slayers.HopByHopOption{ + { + OptType: slayers.OptTypeIdentifier, + OptData: identifierData, + OptDataLen: uint8(len(identifierData)), + }, + { + OptType: slayers.OptTypeFabrid, + OptData: fabridData, + OptDataLen: uint8(len(fabridData)), + }, + }, + } + err = gopacket.SerializeLayers(buf, + gopacket.SerializeOptions{FixLengths: true}, &s, &hbh) + assert.NoError(t, err) + raw := buf.Bytes() + copy(m[0].Buffers[0], raw) + m[0].N = len(raw) + m[0].Addr = &net.UDPAddr{IP: net.IP{10, 0, 200, 200}} + + return 1, nil + }, + ).Times(1) + mExternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + mExternal.EXPECT().WriteTo(gomock.Any(), gomock.Any()).Return(0, nil). + AnyTimes() + + mInternal := mock_router.NewMockBatchConn(ctrl) + mInternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() + mInternal.EXPECT().WriteBatch(gomock.Any(), 0).DoAndReturn( + func(ms underlayconn.Messages, flags int) (int, error) { + if len(ms) != 1 { + assert.Fail(t, "len(ms)!=1", len(ms)) + return 0, nil + } + s := slayers.SCION{} + hbh := slayers.HopByHopExtn{} + _, err := router.DecodeLayers(ms[0].Buffers[0], &s, &hbh) + assert.NoError(t, err) + + containsFabrid := false + containsIdentifier := false + var foundIdentifier *extension.IdentifierOption + var foundFabrid *extension.FabridOption + + baseTs := infoField.Timestamp + for _, hbhOption := range hbh.Options { + switch hbhOption.OptType { + case slayers.OptTypeIdentifier: + containsIdentifier = true + foundIdentifier, err = extension.ParseIdentifierOption(hbhOption, + baseTs) + assert.NoError(t, err) + assert.Equal(t, identifier.Timestamp, foundIdentifier.Timestamp) + assert.Equal(t, identifier.PacketID, foundIdentifier.PacketID) + case slayers.OptTypeFabrid: + containsFabrid = true + if containsIdentifier { + foundFabrid, err = extension.ParseFabridOptionFullExtension( + hbhOption, 3) + assert.NoError(t, err) + meta := foundFabrid.HopfieldMetadata[2] + tmp := make([]byte, 100) + recomputedVerifiedHVF := &extension.FabridHopfieldMetadata{ + EncryptedPolicyID: encPolicyID, + FabridEnabled: true, + } + err = crypto.ComputeVerifiedHVF(recomputedVerifiedHVF, + foundIdentifier, &s, tmp, asToHostKey[:], + 3, 0) + assert.NoError(t, err) + assert.Equal(t, encPolicyID, meta.EncryptedPolicyID) + assert.Equal(t, recomputedVerifiedHVF.HopValidationField, + meta.HopValidationField) + } else { + assert.Fail(t, + "identifier not present before fabrid") + } + default: + } + } + assert.True(t, containsIdentifier) + assert.True(t, containsFabrid) + + done <- struct{}{} + return 1, nil + }).Times(1) + _ = ret.AddInternalInterface(mInternal, netip.Addr{}) + + l := control.LinkEnd{ + IA: local, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.100")}, + } + r1 := control.LinkEnd{ + IA: srcIA, + Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, + } + nobfd := control.BFD{Disable: ptr.To(true)} + + _ = ret.AddExternalInterface(3, mExternal, l, r1, nobfd) + _ = ret.AddLinkType(3, topology.Core) + + _ = ret.SetIA(local) + _ = ret.SetKey(key) + + nextHopAddr := xtest.MustParseUDPAddr(t, "127.0.0.2:8888") + err = ret.AddNextHop(2, l.Addr, nextHopAddr, nobfd, "") + assert.NoError(t, err) + err = ret.AddLinkType(2, topology.Core) + assert.NoError(t, err) + return ret + }, + }, "route 10 msg from external to internal": { prepareDP: func(ctrl *gomock.Controller, done chan<- struct{}) *router.DataPlane { ret := &router.DataPlane{Metrics: metrics} diff --git a/router/export_test.go b/router/export_test.go index 96faeed914..646192ec3d 100644 --- a/router/export_test.go +++ b/router/export_test.go @@ -19,6 +19,7 @@ import ( "net" "net/netip" + "github.com/google/gopacket" "golang.org/x/net/ipv4" "github.com/scionproto/scion/pkg/addr" @@ -94,3 +95,8 @@ func (d *DataPlane) ProcessPkt(ifID uint16, m *ipv4.Message) (ProcessResult, err func ExtractServices(s *services) map[addr.SVC][]*net.UDPAddr { return s.m } + +func DecodeLayers(data []byte, base gopacket.DecodingLayer, + opts ...gopacket.DecodingLayer) (gopacket.DecodingLayer, error) { + return decodeLayers(data, base, opts...) +} diff --git a/router/mock_router/mock.go b/router/mock_router/mock.go index 06993e27c8..923505da3c 100644 --- a/router/mock_router/mock.go +++ b/router/mock_router/mock.go @@ -64,6 +64,20 @@ func (mr *MockBatchConnMockRecorder) ReadBatch(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadBatch", reflect.TypeOf((*MockBatchConn)(nil).ReadBatch), arg0) } +// SetToS mocks base method. +func (m *MockBatchConn) SetToS(arg0 byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetToS", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetToS indicates an expected call of SetToS. +func (mr *MockBatchConnMockRecorder) SetToS(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetToS", reflect.TypeOf((*MockBatchConn)(nil).SetToS), arg0) +} + // WriteBatch mocks base method. func (m *MockBatchConn) WriteBatch(arg0 conn.Messages, arg1 int) (int, error) { m.ctrl.T.Helper() diff --git a/scion/cmd/scion/ping.go b/scion/cmd/scion/ping.go index d5a7ec3bf1..1f7134ff63 100644 --- a/scion/cmd/scion/ping.go +++ b/scion/cmd/scion/ping.go @@ -70,22 +70,24 @@ type PingUpdate struct { func newPing(pather CommandPather) *cobra.Command { var envFlags flag.SCIONEnvironment var flags struct { - count uint16 - features []string - interactive bool - interval time.Duration - logLevel string - maxMTU bool - noColor bool - refresh bool - healthyOnly bool - sequence string - size uint - pktSize uint - timeout time.Duration - tracer string - epic bool - format string + count uint16 + features []string + interactive bool + interval time.Duration + logLevel string + maxMTU bool + noColor bool + refresh bool + healthyOnly bool + sequence string + size uint + pktSize uint + timeout time.Duration + tracer string + epic bool + format string + fabridQuery string + fetchDetached bool } var cmd = &cobra.Command{ @@ -168,16 +170,43 @@ On other errors, ping will exit with code 2. LocalIP: localIP, })) } - path, err := path.Choose(traceCtx, sd, remote.IA, opts...) + if flags.fabridQuery != "" { + cfg := snetpath.FabridConfig{ + LocalIA: info.IA, + LocalAddr: localIP.String(), + DestinationIA: remote.IA, + DestinationAddr: remote.Host.IP.String(), + } + if localIP == nil { + daemonIPString, _, err := net.SplitHostPort(daemonAddr) + if err != nil { + return err + } + daemonIP, err := net.ResolveIPAddr("ip", daemonIPString) + if err != nil { + return serrors.New("resolving daemon ip", "daemonIP", daemonIPString) + } + localIP = daemonIP.IP + cfg.LocalAddr = daemonIPString + } + opts = append(opts, path.WithFABRID(&path.FABRIDQuery{ + PrintSelectedPolicies: true, + Query: flags.fabridQuery, + FabridConfig: cfg, + })) + opts = append(opts, path.WithFetchDetachedFabridMaps(flags.fetchDetached)) + } + pingPath, err := path.Choose(traceCtx, sd, remote.IA, opts...) + if err != nil { return err } // If the EPIC flag is set, use the EPIC-HP path type if flags.epic { - switch s := path.Dataplane().(type) { + switch s := pingPath.Dataplane().(type) { case snetpath.SCION: - epicPath, err := snetpath.NewEPICDataplanePath(s, path.Metadata().EpicAuths) + epicPath, err := snetpath.NewEPICDataplanePath(s, pingPath.Metadata().EpicAuths) if err != nil { return err } @@ -187,10 +216,19 @@ On other errors, ping will exit with code 2. default: return serrors.New("unsupported path type") } + } else if flags.fabridQuery != "" { + switch s := pingPath.Dataplane().(type) { + case *snetpath.FABRID: + remote.Path = s + s.RegisterDRKeyFetcher(sd.FabridKeys) + + default: + return serrors.New("unsupported path type") + } } else { - remote.Path = path.Dataplane() + remote.Path = pingPath.Dataplane() } - remote.NextHop = path.UnderlayNextHop() + remote.NextHop = pingPath.UnderlayNextHop() // Resolve local IP based on underlay next hop if localIP == nil { @@ -204,7 +242,7 @@ On other errors, ping will exit with code 2. } printf("Resolved local address:\n %s\n", localIP) } - printf("Using path:\n %s\n\n", path) + printf("Using path:\n %s\n\n", pingPath) span.SetTag("src.host", localIP) local := &snet.UDPAddr{ IA: info.IA, @@ -225,7 +263,7 @@ On other errors, ping will exit with code 2. pldSize = int(flags.pktSize - uint(overhead)) } if flags.maxMTU { - mtu := int(path.Metadata().MTU) + mtu := int(pingPath.Metadata().MTU) pldSize, err = calcMaxPldSize(local, remote, mtu) if err != nil { return err @@ -244,18 +282,18 @@ On other errors, ping will exit with code 2. count = math.MaxUint16 } - seq, err := pathpol.GetSequence(path) + seq, err := pathpol.GetSequence(pingPath) if err != nil { return serrors.New("get sequence from used path") } res := Result{ ScionPacketSize: pktSize, Path: Path{ - Fingerprint: snet.Fingerprint(path).String(), - Hops: getHops(path), + Fingerprint: snet.Fingerprint(pingPath).String(), + Hops: getHops(pingPath), Sequence: seq, LocalIP: localIP, - NextHop: path.UnderlayNextHop().String(), + NextHop: pingPath.UnderlayNextHop().String(), }, PayloadSize: pldSize, } @@ -336,6 +374,8 @@ On other errors, ping will exit with code 2. cmd.Flags().BoolVar(&flags.noColor, "no-color", false, "disable colored output") cmd.Flags().DurationVar(&flags.timeout, "timeout", time.Second, "timeout per packet") cmd.Flags().StringVar(&flags.sequence, "sequence", "", app.SequenceUsage) + cmd.Flags().StringVar(&flags.fabridQuery, "fabridquery", "", "the query for policies that the "+ + "path must adhere to") cmd.Flags().BoolVar(&flags.healthyOnly, "healthy-only", false, "only use healthy paths") cmd.Flags().BoolVar(&flags.refresh, "refresh", false, "set refresh flag for path request") cmd.Flags().DurationVar(&flags.interval, "interval", time.Second, "time between packets") @@ -357,6 +397,9 @@ SCMP echo header and payload are equal to the MTU of the path. This flag overrid cmd.Flags().StringVar(&flags.logLevel, "log.level", "", app.LogLevelUsage) cmd.Flags().StringVar(&flags.tracer, "tracing.agent", "", "Tracing agent address") cmd.Flags().BoolVar(&flags.epic, "epic", false, "Enable EPIC for path probing.") + cmd.Flags().BoolVar(&flags.fetchDetached, "fetch-detached", true, + "Fetch FABRID maps for hops where they have been detached. "+ + "This increases the overhead of path finding.") cmd.Flags().StringVar(&flags.format, "format", "human", "Specify the output format (human|json|yaml)") return cmd diff --git a/tools/end2end/BUILD.bazel b/tools/end2end/BUILD.bazel index 6e44df874a..5b730c92b5 100644 --- a/tools/end2end/BUILD.bazel +++ b/tools/end2end/BUILD.bazel @@ -3,16 +3,25 @@ load("//:scion.bzl", "scion_go_binary") go_library( name = "go_default_library", - srcs = ["main.go"], + srcs = [ + "fabrid.go", + "main.go", + ], importpath = "github.com/scionproto/scion/tools/end2end", visibility = ["//visibility:private"], deps = [ "//pkg/addr:go_default_library", "//pkg/daemon:go_default_library", + "//pkg/drkey:go_default_library", + "//pkg/experimental/fabrid:go_default_library", + "//pkg/experimental/fabrid/crypto:go_default_library", "//pkg/log:go_default_library", "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", + "//pkg/slayers:go_default_library", + "//pkg/slayers/extension:go_default_library", + "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/metrics:go_default_library", "//pkg/snet/path:go_default_library", diff --git a/tools/end2end/fabrid.go b/tools/end2end/fabrid.go new file mode 100644 index 0000000000..b4eff77419 --- /dev/null +++ b/tools/end2end/fabrid.go @@ -0,0 +1,204 @@ +// Copyright 2024 ETH Zurich +// +// 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 main + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/experimental/fabrid/crypto" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/extension" + "github.com/scionproto/scion/pkg/slayers/path/scion" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/private/tracing" + integration "github.com/scionproto/scion/tools/integration/integrationlib" +) + +func (s server) handlePingFabrid(conn snet.PacketConn) error { + var p snet.Packet + var ov net.UDPAddr + err := readFromFabrid(conn, &p, &ov) + if err != nil { + return serrors.WrapStr("reading packet", err) + } + + // If the packet is from remote IA, validate the FABRID path + if p.Source.IA != integration.Local.IA { + if p.HbhExtension == nil { + return serrors.New("Missing HBH extension") + } + + // Check extensions for relevant options + var identifierOption *extension.IdentifierOption + var fabridOption *extension.FabridOption + var err error + + for _, opt := range p.HbhExtension.Options { + switch opt.OptType { + case slayers.OptTypeIdentifier: + decoded := scion.Decoded{} + err = decoded.DecodeFromBytes(p.Path.(snet.RawPath).Raw) + if err != nil { + return err + } + baseTimestamp := decoded.InfoFields[0].Timestamp + identifierOption, err = extension.ParseIdentifierOption(opt, baseTimestamp) + if err != nil { + return err + } + case slayers.OptTypeFabrid: + fabridOption, err = extension.ParseFabridOptionFullExtension(opt, + (opt.OptDataLen-4)/4) + if err != nil { + return err + } + } + } + + if identifierOption == nil { + return serrors.New("Missing identifier option") + } + + if fabridOption == nil { + return serrors.New("Missing FABRID option") + } + + meta := drkey.HostHostMeta{ + Validity: identifierOption.Timestamp, + SrcIA: integration.Local.IA, + SrcHost: integration.Local.Host.IP.String(), + DstIA: p.Source.IA, + DstHost: p.Source.Host.IP().String(), + ProtoId: drkey.FABRID, + } + hostHostKey, err := integration.SDConn().DRKeyGetHostHostKey(context.Background(), meta) + if err != nil { + return serrors.WrapStr("getting host key", err) + } + + tmpBuffer := make([]byte, (len(fabridOption.HopfieldMetadata)*3+15)&^15+16) + _, err = crypto.VerifyPathValidator(fabridOption, tmpBuffer, hostHostKey.Key[:]) + if err != nil { + return err + } + } + + udp, ok := p.Payload.(snet.UDPPayload) + if !ok { + return serrors.New("unexpected payload received", + "source", p.Source, + "destination", p.Destination, + "type", common.TypeOf(p.Payload), + ) + } + var pld Ping + if err := json.Unmarshal(udp.Payload, &pld); err != nil { + return serrors.New("invalid payload contents", + "source", p.Source, + "destination", p.Destination, + "data", string(udp.Payload), + ) + } + + spanCtx, err := opentracing.GlobalTracer().Extract( + opentracing.Binary, + bytes.NewReader(pld.Trace), + ) + if err != nil { + return serrors.WrapStr("extracting trace information", err) + } + span, _ := opentracing.StartSpanFromContext( + context.Background(), + "handle_ping", + ext.RPCServerOption(spanCtx), + ) + defer span.Finish() + withTag := func(err error) error { + tracing.Error(span, err) + return err + } + + if pld.Message != ping || !pld.Server.Equal(integration.Local.IA) { + return withTag(serrors.New("unexpected data in payload", + "source", p.Source, + "destination", p.Destination, + "data", pld, + )) + } + log.Info(fmt.Sprintf("Ping received from %s, sending pong.", p.Source)) + raw, err := json.Marshal(Pong{ + Client: p.Source.IA, + Server: integration.Local.IA, + Message: pong, + Trace: pld.Trace, + }) + if err != nil { + return withTag(serrors.WrapStr("packing pong", err)) + } + + p.Destination, p.Source = p.Source, p.Destination + p.Payload = snet.UDPPayload{ + DstPort: udp.SrcPort, + SrcPort: udp.DstPort, + Payload: raw, + } + + // Remove header extension for reverse path + p.HbhExtension = nil + p.E2eExtension = nil + + // reverse path + rpath, ok := p.Path.(snet.RawPath) + if !ok { + return serrors.New("unexpected path", "type", common.TypeOf(p.Path)) + } + replypather := snet.DefaultReplyPather{} + replyPath, err := replypather.ReplyPath(rpath) + if err != nil { + return serrors.WrapStr("creating reply path", err) + } + p.Path = replyPath + // Send pong + if err := conn.WriteTo(&p, &ov); err != nil { + return withTag(serrors.WrapStr("sending reply", err)) + } + log.Info("Sent pong to", "client", p.Destination) + return nil +} + +func readFromFabrid(conn snet.PacketConn, pkt *snet.Packet, ov *net.UDPAddr) error { + err := conn.ReadFrom(pkt, ov) + // Attach more context to error + var opErr *snet.OpError + if !(errors.As(err, &opErr) && opErr.RevInfo() != nil) { + return err + } + return serrors.WithCtx(err, + "isd_as", opErr.RevInfo().IA(), + "interface", opErr.RevInfo().IfID, + ) +} diff --git a/tools/end2end/main.go b/tools/end2end/main.go index d16578140d..fe3bbe4431 100644 --- a/tools/end2end/main.go +++ b/tools/end2end/main.go @@ -37,6 +37,7 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/daemon" + libfabrid "github.com/scionproto/scion/pkg/experimental/fabrid" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/serrors" @@ -73,6 +74,7 @@ var ( scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors epic bool + fabrid bool ) func main() { @@ -109,6 +111,7 @@ func addFlags() { flag.Var(&remote, "remote", "(Mandatory for clients) address to connect to") flag.Var(timeout, "timeout", "The timeout for each attempt") flag.BoolVar(&epic, "epic", false, "Enable EPIC") + flag.BoolVar(&fabrid, "fabrid", false, "Enable FABRID") } func validateFlags() { @@ -123,7 +126,10 @@ func validateFlags() { integration.LogFatal("Invalid timeout provided", "timeout", timeout) } } - log.Info("Flags", "timeout", timeout, "epic", epic, "remote", remote) + if epic && fabrid { + integration.LogFatal("FABRID is incompatible with EPIC") + } + log.Info("Flags", "timeout", timeout, "epic", epic, "fabrid", fabrid, "remote", remote) } type server struct{} @@ -142,22 +148,44 @@ func (s server) run() { PacketConnMetrics: scionPacketConnMetrics, Topology: sdConn, } - conn, err := sn.Listen(context.Background(), "udp", integration.Local.Host) - if err != nil { - integration.LogFatal("Error listening", "err", err) - } - defer conn.Close() - localAddr := conn.LocalAddr().(*snet.UDPAddr) - if len(os.Getenv(libint.GoIntegrationEnv)) > 0 { - // Needed for integration test ready signal. - fmt.Printf("Port=%d\n", localAddr.Host.Port) - fmt.Printf("%s%s\n\n", libint.ReadySignal, integration.Local.IA) - } - log.Info("Listening", "local", fmt.Sprintf("%v:%d", localAddr.Host.IP, localAddr.Host.Port)) - // Receive ping message - for { - if err := s.handlePing(conn); err != nil { - log.Error("Error handling ping", "err", err) + if fabrid { + conn, err := sn.OpenRaw(context.Background(), integration.Local.Host) + if err != nil { + integration.LogFatal("Error listening", "err", err) + } + defer conn.Close() + localAddr := conn.LocalAddr().(*net.UDPAddr) + if len(os.Getenv(libint.GoIntegrationEnv)) > 0 { + // Needed for integration test ready signal. + fmt.Printf("Port=%d\n", localAddr.Port) + fmt.Printf("%s%s\n\n", libint.ReadySignal, integration.Local.IA) + } + log.Info("Listening", "local", + fmt.Sprintf("%v:%d", integration.Local.Host.IP, localAddr.Port)) + // Receive ping message + for { + if err := s.handlePingFabrid(conn); err != nil { + log.Error("Error handling ping", "err", err) + } + } + } else { + conn, err := sn.Listen(context.Background(), "udp", integration.Local.Host) + if err != nil { + integration.LogFatal("Error listening", "err", err) + } + defer conn.Close() + localAddr := conn.LocalAddr().(*snet.UDPAddr) + if len(os.Getenv(libint.GoIntegrationEnv)) > 0 { + // Needed for integration test ready signal. + fmt.Printf("Port=%d\n", localAddr.Host.Port) + fmt.Printf("%s%s\n\n", libint.ReadySignal, integration.Local.IA) + } + log.Info("Listening", "local", fmt.Sprintf("%v:%d", localAddr.Host.IP, localAddr.Host.Port)) + // Receive ping message + for { + if err := s.handlePing(conn); err != nil { + log.Error("Error handling ping", "err", err) + } } } } @@ -368,6 +396,43 @@ func (c *client) getRemote(ctx context.Context, n int) (snet.Path, error) { return nil, err } remote.Path = epicPath + } else if fabrid { + // If the fabrid flag is set, try to create FABRID dataplane path. + if len(path.Metadata().FabridInfo) > 0 { + // Check if fabrid info is available, otherwise the source + // AS does not support fabrid + + scionPath, ok := path.Dataplane().(snetpath.SCION) + if !ok { + return nil, serrors.New("provided path must be of type scion") + } + fabridConfig := &snetpath.FabridConfig{ + LocalIA: integration.Local.IA, + LocalAddr: integration.Local.Host.IP.String(), + DestinationIA: remote.IA, + DestinationAddr: remote.Host.IP.String(), + } + hops := path.Metadata().Hops() + log.Info("Fabrid path", "path", path, "hops", hops) + // Use ZERO policy for all hops with fabrid, to just do path validation + policies := make([]*libfabrid.PolicyID, len(hops)) + zeroPol := libfabrid.PolicyID(0) + for i, hop := range hops { + if hop.FabridEnabled { + policies[i] = &zeroPol + } + } + fabridPath, err := snetpath.NewFABRIDDataplanePath(scionPath, hops, + policies, fabridConfig) + if err != nil { + return nil, serrors.New("Error creating FABRID path", "err", err) + } + remote.Path = fabridPath + fabridPath.RegisterDRKeyFetcher(c.sdConn.FabridKeys) + } else { + log.Info("FABRID flag was set for client in non-FABRID AS. Proceeding without FABRID.") + remote.Path = path.Dataplane() + } } else { remote.Path = path.Dataplane() } diff --git a/tools/end2end_integration/main.go b/tools/end2end_integration/main.go index 5b4ff4cdf2..61540cfe20 100644 --- a/tools/end2end_integration/main.go +++ b/tools/end2end_integration/main.go @@ -44,6 +44,7 @@ var ( cmd string features string epic bool + fabrid bool ) func getCmd() (string, bool) { @@ -76,10 +77,12 @@ func realMain() int { "-local", integration.SrcAddrPattern + ":0", "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, fmt.Sprintf("-epic=%t", epic), + fmt.Sprintf("-fabrid=%t", fabrid), } serverArgs := []string{ "-mode", "server", "-local", integration.DstAddrPattern + ":0", + fmt.Sprintf("-fabrid=%t", fabrid), } if len(features) != 0 { clientArgs = append(clientArgs, "--features", features) @@ -89,9 +92,19 @@ func realMain() int { clientArgs = append(clientArgs, "-sciond", integration.Daemon) serverArgs = append(serverArgs, "-sciond", integration.Daemon) } - - in := integration.NewBinaryIntegration(name, cmd, clientArgs, serverArgs) - pairs, err := getPairs() + var in integration.Integration + if fabrid { + in = integration.NewBinaryEndhostIntegration(name, cmd, clientArgs, serverArgs) + } else { + in = integration.NewBinaryIntegration(name, cmd, clientArgs, serverArgs) + } + var pairs []integration.IAPair + var err error + if fabrid { + pairs, err = getPairs(integration.SDAddr) + } else { + pairs, err = getPairs(integration.CSAddr) + } if err != nil { log.Error("Error selecting tests", "err", err) return 1 @@ -119,6 +132,7 @@ func addFlags() { flag.StringVar(&features, "features", "", fmt.Sprintf("enable development features (%v)", feature.String(&feature.Default{}, "|"))) flag.BoolVar(&epic, "epic", false, "Enable EPIC.") + flag.BoolVar(&fabrid, "fabrid", false, "Enable FABRID.") } // runTests runs the end2end tests for all pairs. In case of an error the @@ -245,6 +259,9 @@ func runTests(in integration.Integration, pairs []integration.IAPair) error { var tester string if *integration.Docker { tester = integration.TesterID(src) + if fabrid { + tester = integration.EndhostID(src) + } } logFile := fmt.Sprintf("%s/client_%s.log", logDir(), @@ -296,6 +313,7 @@ func clientTemplate(progressSock string) integration.Cmd { "-local", integration.SrcAddrPattern + ":0", "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, fmt.Sprintf("-epic=%t", epic), + fmt.Sprintf("-fabrid=%t", fabrid), }, } if len(features) != 0 { @@ -319,8 +337,8 @@ func clientTemplate(progressSock string) integration.Cmd { // This implies that IFF role1 == role2, then h1:h2 pairs are mirrored with h2:h1 and, unless // remote[ISD/AS] is specified, h2:h2 and h1:h1. Not all combinations yield something useful... // caveat emptor. -func getPairs() ([]integration.IAPair, error) { - pairs := integration.IAPairs(integration.CSAddr) +func getPairs(hostAddr integration.HostAddr) ([]integration.IAPair, error) { + pairs := integration.IAPairs(hostAddr) if subset == "all" { return pairs, nil } diff --git a/tools/integration/BUILD.bazel b/tools/integration/BUILD.bazel index cfe67524a7..248052665f 100644 --- a/tools/integration/BUILD.bazel +++ b/tools/integration/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "cmd.go", "docker.go", "done.go", + "endhost_integration.go", "integration.go", "networkalloc.go", ], diff --git a/tools/integration/endhost_integration.go b/tools/integration/endhost_integration.go new file mode 100644 index 0000000000..cdaef55c35 --- /dev/null +++ b/tools/integration/endhost_integration.go @@ -0,0 +1,129 @@ +// Copyright 2024 ETH Zurich +// +// 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 integration + +import ( + "context" + "fmt" + "net" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/private/topology" +) + +type dockerizedEndhostIntegration struct { + *binaryIntegration +} + +func NewBinaryEndhostIntegration(name string, cmd string, clientArgs, + serverArgs []string) Integration { + logDir := filepath.Join(LogDir(), name) + err := os.Mkdir(logDir, os.ModePerm) + if err != nil && !os.IsExist(err) { + log.Error("Failed to create log folder for testrun", "dir", name, "err", err) + return nil + } + bi := &binaryIntegration{ + name: name, + cmd: cmd, + clientArgs: clientArgs, + serverArgs: serverArgs, + logDir: logDir, + } + return dockerizeEndhost(bi) +} + +func dockerizeEndhost(bi *binaryIntegration) Integration { + if *Docker { + return &dockerizedEndhostIntegration{ + binaryIntegration: bi, + } + } + return bi +} + +// StartServer starts a server and blocks until the ReadySignal is received on Stdout. +func (di *dockerizedEndhostIntegration) StartServer(ctx context.Context, dst *snet.UDPAddr) (Waiter, + error) { + bi := *di.binaryIntegration + bi.serverArgs = append(dockerArgs, + append([]string{EndhostID(dst), bi.cmd}, bi.serverArgs...)...) + bi.cmd = dockerCmd + log.Debug(fmt.Sprintf("Starting server for %s in a docker container", + addr.FormatIA(dst.IA, addr.WithFileSeparator())), + ) + return bi.StartServer(ctx, dst) +} + +func (di *dockerizedEndhostIntegration) StartClient(ctx context.Context, + src, dst *snet.UDPAddr) (*BinaryWaiter, error) { + bi := *di.binaryIntegration + bi.clientArgs = append(dockerArgs, + append([]string{EndhostID(src), bi.cmd}, bi.clientArgs...)...) + bi.cmd = dockerCmd + log.Debug(fmt.Sprintf("Starting client for %s in a docker container", + addr.FormatIA(src.IA, addr.WithFileSeparator())), + ) + return bi.StartClient(ctx, src, dst) +} + +func EndhostID(a *snet.UDPAddr) string { + ia := addr.FormatIA(a.IA, addr.WithFileSeparator()) + envID, ok := os.LookupEnv(fmt.Sprintf("endhost_%s", strings.Replace(ia, "-", "_", -1))) + if !ok { + return fmt.Sprintf("endhost_%s", ia) + } + return envID +} + +// SDAddr reads the endhost (dockerized) or scion daemon (normal) host Addr from the topology +// for the specified IA. If the address cannot be found, the CS address is returned. +var SDAddr HostAddr = func(ia addr.IA) *snet.UDPAddr { + if a := loadAddr(ia); a != nil { + return a + } + var name string + if *Docker { + name = "endhost_" + } else { + name = "sd" + } + if raw, err := os.ReadFile(GenFile("networks.conf")); err == nil { + pattern := fmt.Sprintf("%s%s = (.*)", name, addr.FormatIA(ia, addr.WithFileSeparator())) + matches := regexp.MustCompile(pattern).FindSubmatch(raw) + if len(matches) == 2 { + return &snet.UDPAddr{IA: ia, Host: &net.UDPAddr{IP: net.ParseIP(string(matches[1]))}} + } + } + path := GenFile( + filepath.Join( + addr.FormatAS(ia.AS(), addr.WithDefaultPrefix(), addr.WithFileSeparator()), + "topology.json", + ), + ) + topo, err := topology.RWTopologyFromJSONFile(path) + if err != nil { + log.Error("Error loading topology", "err", err) + os.Exit(1) + } + cs := topo.CS["cs"+addr.FormatIA(ia, addr.WithFileSeparator())+"-1"] + return &snet.UDPAddr{IA: ia, Host: cs.SCIONAddress} +} diff --git a/tools/topogen.py b/tools/topogen.py index 8bd1a349b2..403b74a293 100755 --- a/tools/topogen.py +++ b/tools/topogen.py @@ -51,6 +51,11 @@ def add_arguments(parser): parser.add_argument('--sig', action='store_true', help='Generate a SIG per AS (only available with -d, the SIG image needs\ to be built manually e.g. when running acceptance tests)') + parser.add_argument('--fabrid', action='store_true', + help='Enables FABRID and DRKey on all CSes, BRs and SDs') + parser.add_argument('--endhosts', action='store_true', + help='Generates an endhost per AS that contains a scion daemon.\ + Requires the daemon flag.') parser.add_argument('--features', help='Feature flags to enable, a comma separated list\ e.g. foo,bar enables foo and bar feature.') return parser diff --git a/tools/topology/config.py b/tools/topology/config.py index b5af6a6960..b65a16417c 100644 --- a/tools/topology/config.py +++ b/tools/topology/config.py @@ -75,6 +75,13 @@ def __init__(self, args): if self.args.sig and not self.args.docker: logging.critical("Cannot use sig without docker!") sys.exit(1) + if self.args.endhosts and not self.args.docker: + logging.critical("Cannot generate endhosts without the docker flag!") + sys.exit(1) + if self.args.fabrid and self.args.docker and not self.args.endhosts: + logging.critical( + "Cannot generate dockerized fabrid topologies without the endhosts flag!") + sys.exit(1) self.default_mtu = None self._read_defaults() @@ -130,6 +137,8 @@ def _generate_go(self, topo_dicts): args = self._go_args(topo_dicts) go_gen = GoGenerator(args) go_gen.generate_br() + if self.args.endhosts: + go_gen.generate_endhost() go_gen.generate_sciond() go_gen.generate_control_service() go_gen.generate_disp() @@ -196,9 +205,14 @@ def _write_sciond_conf(self, networks: Mapping[IPNetwork, NetworkDescription], o d = dict() for net_desc in networks.values(): for prog, ip_net in net_desc.ip_net.items(): - if prog.startswith("sd"): - ia = prog[2:].replace("_", ":") - d[ia] = str(ip_net.ip) + if self.args.endhosts: + if prog.startswith("endhost_"): + ia = prog[8:].replace("_", ":") + d[ia] = str(ip_net.ip) + else: + if prog.startswith("sd"): + ia = prog[2:].replace("_", ":") + d[ia] = str(ip_net.ip) with open(os.path.join(self.args.output_dir, out_file), mode="w") as f: json.dump(d, f, sort_keys=True, indent=4) diff --git a/tools/topology/docker_utils.py b/tools/topology/docker_utils.py index 6bd9be0b21..ab2f4ba0a5 100644 --- a/tools/topology/docker_utils.py +++ b/tools/topology/docker_utils.py @@ -51,10 +51,46 @@ def __init__(self, args): def generate(self): for topo_id in self.args.topo_dicts: self._test_conf(topo_id) + if self.args.endhosts: + self._endhost_conf(topo_id) if self.args.sig: self._sig_testing_conf() return self.dc_conf + def _endhost_conf(self, topo_id): + cntr_base = '/share' + name = 'endhost_%s' % topo_id.file_fmt() + entry = { + 'image': docker_image(self.args, 'endhost'), + 'privileged': True, + 'entrypoint': 'sh endhost.sh', + 'environment': {}, + 'user': self.user, + 'volumes': [ + self.output_base + '/logs:' + cntr_base + '/logs:rw', + self.output_base + '/gen:' + cntr_base + '/gen:rw', + self.output_base + '/gen-certs:' + cntr_base + '/gen-certs:rw', + self.output_base + '/gen-cache:' + cntr_base + '/cache:rw', + self.output_base + '/gen/AS' + name.split('-')[1] + ":" + '/etc/scion:ro', + ], + } + net = self.args.networks[name][0] + ipv = 'ipv4' + if ipv not in net: + ipv = 'ipv6' + ip = str(net[ipv]) + entry['networks'] = {} + entry['networks'][self.args.bridges[net['net']]] = { + '%s_address' % ipv: ip + } + disp_net = self.args.networks[name][0] + entry['environment']['SCION_LOCAL_ADDR'] = str(disp_net[ipv]) + if ipv == 'ipv4': + entry['environment']['SCION_DAEMON'] = '%s:30255' % str(disp_net[ipv]) + else: + entry['environment']['SCION_DAEMON'] = '[%s]:30255' % str(disp_net[ipv]) + self.dc_conf['services'][name] = entry + def _test_conf(self, topo_id): cntr_base = '/share' name = 'tester_%s' % topo_id.file_fmt() diff --git a/tools/topology/endhost.py b/tools/topology/endhost.py new file mode 100644 index 0000000000..5c92d52ccb --- /dev/null +++ b/tools/topology/endhost.py @@ -0,0 +1,28 @@ +# Copyright 2024 ETH Zurich, Anapaya Systems +# +# 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. + +# Stdlib +from typing import Mapping + +# SCION +from topology.net import NetworkDescription, IPNetwork + + +def endhost_ip(docker, topo_id, + networks: Mapping[IPNetwork, NetworkDescription]): + for net_desc in networks.values(): + for prog, ip_net in net_desc.ip_net.items(): + if prog == 'endhost_%s' % topo_id.file_fmt(): + return ip_net.ip + return None diff --git a/tools/topology/go.py b/tools/topology/go.py index 72a1902097..6e7aabf3c7 100644 --- a/tools/topology/go.py +++ b/tools/topology/go.py @@ -36,6 +36,8 @@ SD_CONFIG_NAME, ) +from topology.endhost import endhost_ip + from topology.net import socket_address_str, NetworkDescription, IPNetwork from topology.monitoring import ( @@ -91,8 +93,13 @@ def _build_br_conf(self, topo_id, ia, base, name, v): 'features': translate_features(self.args.features), 'api': { 'addr': prom_addr(v['internal_addr'], DEFAULT_BR_PROM_PORT+700) - } + }, } + if self.args.fabrid: + raw_entry['router'] = { + 'drkey': ["FABRID"], + 'fabrid': True, + } return raw_entry def generate_control_service(self): @@ -129,10 +136,72 @@ def _build_control_service_conf(self, topo_id, ia, base, name, infra_elem, ca): 'api': self._api_entry(infra_elem, CS_PROM_PORT+700), 'features': translate_features(self.args.features), } + if self.args.fabrid: + fabrid_path = os.path.join(config_dir, 'fabrid-policies') + raw_entry['fabrid'] = { + 'enabled': True, + 'path': fabrid_path, + } + + borderRouterInternalIPs = [] + for _, v in self.args.topo_dicts[topo_id].get("border_routers", {}).items(): + borderRouterInternalIPs.append(v['internal_addr'].rsplit(':', 1)[0].strip('[]')) + raw_entry['drkey'] = { + 'level1_db': { + 'connection': os.path.join(self.db_dir, '%s.drkey-level1.db' % name), + }, + 'secret_value_db': { + 'connection': os.path.join(self.db_dir, '%s.drkey-secret.db' % name), + }, + 'delegation': { + 'FABRID': borderRouterInternalIPs, + } + } + if ca: raw_entry['ca'] = {'mode': 'in-process'} return raw_entry + def generate_endhost(self): + for topo_id, topo in self.args.topo_dicts.items(): + base = topo_id.base_dir(self.args.output_dir) + sciond_conf = self._build_endhost_sciond_conf(topo_id, topo["isd_as"], base) + write_file(os.path.join(base, "endhost.toml"), toml.dumps(sciond_conf)) + + def _build_endhost_sciond_conf(self, topo_id, ia, base): + name = 'endhost_%s' % topo_id.file_fmt() + config_dir = '/etc/scion' if self.args.docker else base + ip = endhost_ip(self.args.docker, topo_id, self.args.networks) + raw_entry = { + 'general': { + 'id': name, + 'config_dir': config_dir, + }, + 'log': self._log_entry(name), + 'trust_db': { + 'connection': os.path.join(self.db_dir, '%s.trust.db' % name), + }, + 'path_db': { + 'connection': os.path.join(self.db_dir, '%s.path.db' % name), + }, + 'sd': { + 'address': socket_address_str(ip, SD_API_PORT), + }, + 'tracing': self._tracing_entry(), + 'metrics': { + 'prometheus': socket_address_str(ip, SCIOND_PROM_PORT) + }, + 'features': translate_features(self.args.features), + 'api': { + 'addr': socket_address_str(ip, SD_API_PORT+700), + }, + } + if self.args.fabrid: + raw_entry['drkey_level2_db'] = { + 'connection': os.path.join(self.db_dir, '%s.drkey_level2.db' % name), + } + return raw_entry + def generate_sciond(self): for topo_id, topo in self.args.topo_dicts.items(): base = topo_id.base_dir(self.args.output_dir) @@ -165,8 +234,12 @@ def _build_sciond_conf(self, topo_id, ia, base): 'features': translate_features(self.args.features), 'api': { 'addr': socket_address_str(ip, SD_API_PORT+700), - } + }, } + if self.args.fabrid: + raw_entry['drkey_level2_db'] = { + 'connection': os.path.join(self.db_dir, '%s.drkey_level2.db' % name), + } return raw_entry def generate_disp(self): diff --git a/tools/topology/topo.py b/tools/topology/topo.py index 9fb39e7ce0..bc16164676 100644 --- a/tools/topology/topo.py +++ b/tools/topology/topo.py @@ -145,6 +145,8 @@ def _register_addrs(self, topo_id, as_conf): self._register_br_entries(topo_id, as_conf) if self.args.sig: self._register_sig(topo_id, as_conf) + if self.args.endhosts: + self._register_endhost(topo_id, as_conf) self._register_sciond(topo_id, as_conf) def _register_srv_entries(self, topo_id, as_conf): @@ -179,6 +181,10 @@ def _register_sig(self, topo_id, as_conf): addr_type = addr_type_from_underlay(as_conf.get('underlay', DEFAULT_UNDERLAY)) self._reg_addr(topo_id, "sig" + topo_id.file_fmt(), addr_type) + def _register_endhost(self, topo_id, as_conf): + addr_type = addr_type_from_underlay(as_conf.get('underlay', DEFAULT_UNDERLAY)) + self._reg_addr(topo_id, "endhost_" + topo_id.file_fmt(), addr_type) + def _register_sciond(self, topo_id, as_conf): addr_type = addr_type_from_underlay(as_conf.get('underlay', DEFAULT_UNDERLAY)) self._reg_addr(topo_id, "sd" + topo_id.file_fmt(), addr_type) diff --git a/tools/wireshark/scion.lua b/tools/wireshark/scion.lua index 5ec622c68b..0acfa10d04 100644 --- a/tools/wireshark/scion.lua +++ b/tools/wireshark/scion.lua @@ -504,6 +504,8 @@ local scion_extn_tlv_option_types = { [0] = "Pad1", [1] = "PadN", [2] = "Packet Authenticator Option", + [3] = "Packet Identifier", + [4] = "Fabrid Extension", } local scion_extn_tlv_option_type = ProtoField.uint8("scion_extn_tlv_option.type", "Type", base.DEC, scion_extn_tlv_option_types) @@ -555,6 +557,8 @@ function scion_extn_tlv_option_dissect(tvbuf, pktinfo, root) local ret_len if tlv["type"]:uint() == 2 then ret_len = scion_packet_authenticator_option_dissect(tvbuf(2, data_len), pktinfo, tree) + elseif tlv["type"]:uint() == 4 then + ret_len = scion_fabrid_extension_dissect(tvbuf(2, data_len), pktinfo, tree) else -- no specific dissector ret_len = data_len @@ -736,6 +740,60 @@ function scion_packet_authenticator_option_dissect(buffer, pktinfo, tree) return length end +scion_fabrid_path_validation_field = Proto("scion_fabrid_path_validation_field", "Path Validation Field") +scion_fabrid_extension = Proto("scion_fabrid_extension", "Hop Validation Field") +scion_fabrid_extension_encrypted_policy_id = ProtoField.uint8("scion_fabrid_extension.encrypted_policy_id", "Encrypted Policy ID", base.HEX) +scion_fabrid_extension_enabled = ProtoField.uint8("scion_fabrid_extension.enabled", "FABRID Enabled", base.DEC, nil, 0x80) +scion_fabrid_extension_as_level_key = ProtoField.uint8("scion_fabrid_extension.as_level_key", "AS-Level key", base.DEC, nil, 0x40) +scion_fabrid_extension_hop_validation_field = ProtoField.uint32("scion_fabrid_extension.hop_validation_field", "Hop Validation Field", base.HEX) +scion_fabrid_path_validation_field_value = ProtoField.uint32("scion_fabrid_path_validation_field.value", "Value", base.HEX) +scion_fabrid_path_validation_field.fields = { + scion_fabrid_path_validation_field_value +} +scion_fabrid_extension.fields = { + scion_fabrid_extension_encrypted_policy_id, + scion_fabrid_extension_enabled, + scion_fabrid_extension_as_level_key, + scion_fabrid_extension_hop_validation_field +} +-- FABRID dissector +function scion_fabrid_extension_dissect(buffer, pktinfo, root) + local length = buffer:len() + if length < 4 then + root:add_proto_expert_info(e_too_short) + return -1 + end + + local offset = 0 + while offset < (buffer:len() - 4) + do + local len = scion_fabrid_hop_validation_dissect(buffer(offset, buffer:len()-offset), pktinfo, root) + if len <= 0 then + return -1 + end + offset = offset + len + end + local tree = root:add(scion_fabrid_path_validation_field, buffer(offset, 4)) + tree:add(scion_fabrid_path_validation_field_value, buffer(offset,4)) + return length +end + +function scion_fabrid_hop_validation_dissect(buffer, pktinfo, root) + local length = buffer:len() + if length < 4 then + tree:add_proto_expert_info(e_too_short) + return -1 + end + + local tree = root:add(scion_fabrid_extension, buffer(0, length)) + tree:add(scion_fabrid_extension_encrypted_policy_id, buffer(0,1)) + tree:add(scion_fabrid_extension_enabled, buffer(1,1)) + tree:add(scion_fabrid_extension_as_level_key, buffer(1,1)) + tree:add(scion_fabrid_extension_hop_validation_field, buffer(1,3)) + + return 4 +end + -- EPIC Path epic_path = Proto("epic_path", "EPIC Path")