diff --git a/go/pkg/hiddenpath/BUILD.bazel b/go/pkg/hiddenpath/BUILD.bazel index 96883bd1e8..606c3fd26f 100644 --- a/go/pkg/hiddenpath/BUILD.bazel +++ b/go/pkg/hiddenpath/BUILD.bazel @@ -50,7 +50,6 @@ go_test( deps = [ "//go/cs/beacon:go_default_library", "//go/cs/beaconing:go_default_library", - "//go/cs/beaconing/mock_beaconing:go_default_library", "//go/cs/ifstate:go_default_library", "//go/lib/addr:go_default_library", "//go/lib/common:go_default_library", diff --git a/go/pkg/hiddenpath/beaconwriter.go b/go/pkg/hiddenpath/beaconwriter.go index 45c43073f3..e8cf56e11a 100644 --- a/go/pkg/hiddenpath/beaconwriter.go +++ b/go/pkg/hiddenpath/beaconwriter.go @@ -32,6 +32,17 @@ import ( "github.com/scionproto/scion/go/lib/serrors" ) +// SegmentRegistration is a registration for hidden segments. +type SegmentRegistration struct { + GroupID GroupID + Seg seg.Meta +} + +// Register is used to register segments to a remote. +type Register interface { + RegisterSegment(context.Context, SegmentRegistration, net.Addr) error +} + // BeaconWriter terminates segments and registers them at remotes. The remotes // can either be a public core segment registry or a hidden segment registry. type BeaconWriter struct { @@ -46,9 +57,9 @@ type BeaconWriter struct { Intfs *ifstate.Interfaces // Extender is used to terminate the beacon. Extender beaconing.Extender - // RPCs are used to send the segment to the remote. For public registrations + // RPC is used to send the segment to the remote. For public registrations // the entry with an empty group ID as key is used. - RPCs map[GroupID]beaconing.RPC + RPC Register // Pather is used to construct paths to the originator of a beacon. Pather beaconing.Pather // RegistrationPolicy is the hidden path registration policy. @@ -100,12 +111,11 @@ func (w *BeaconWriter) Write(ctx context.Context, segments <-chan beacon.BeaconO internalErrors: w.InternalErrors, registered: w.Registered, summary: summary, - segType: seg.TypeDown, hiddenPathGroup: id, resolveRemote: func(ctx context.Context) (net.Addr, error) { return w.AddressResolver.Resolve(ctx, a) }, - rpc: w.RPCs[id], + rpc: w.RPC, } if id.ToUint64() == 0 { // public @@ -136,15 +146,17 @@ type remoteWriter struct { internalErrors metrics.Counter registered metrics.Counter summary *summary - segType seg.Type hiddenPathGroup GroupID resolveRemote func(context.Context) (net.Addr, error) - rpc beaconing.RPC + rpc Register } // run resolves, and writes the segment to the remote registry. func (w *remoteWriter) run(ctx context.Context, bseg beacon.Beacon) { - reg := seg.Meta{Type: w.segType, Segment: bseg.Segment} + reg := SegmentRegistration{ + Seg: seg.Meta{Type: seg.TypeDown, Segment: bseg.Segment}, + GroupID: w.hiddenPathGroup, + } logger := log.FromCtx(ctx) @@ -185,7 +197,7 @@ func (w *remoteWriter) hpGroup() string { } func (w *remoteWriter) segTypeString() string { - s := w.segType.String() + s := seg.TypeDown.String() if w.hiddenPathGroup.ToUint64() != 0 { s = "hidden_" + s } diff --git a/go/pkg/hiddenpath/beaconwriter_test.go b/go/pkg/hiddenpath/beaconwriter_test.go index a95f81aac6..65c468cf47 100644 --- a/go/pkg/hiddenpath/beaconwriter_test.go +++ b/go/pkg/hiddenpath/beaconwriter_test.go @@ -32,7 +32,6 @@ import ( "github.com/scionproto/scion/go/cs/beacon" "github.com/scionproto/scion/go/cs/beaconing" - "github.com/scionproto/scion/go/cs/beaconing/mock_beaconing" "github.com/scionproto/scion/go/cs/ifstate" "github.com/scionproto/scion/go/lib/addr" "github.com/scionproto/scion/go/lib/common" @@ -92,38 +91,38 @@ func TestRemoteBeaconWriterWrite(t *testing.T) { topoProvider := itopotest.TopoProviderFromFile(t, topoNonCore) testCases := map[string]struct { - beacons [][]common.IFIDType - createRPCs func(*testing.T, *gomock.Controller) map[hiddenpath.GroupID]beaconing.RPC - policy hiddenpath.RegistrationPolicy - resolver func(*gomock.Controller) hiddenpath.AddressResolver + beacons [][]common.IFIDType + createRPC func(*testing.T, *gomock.Controller) hiddenpath.Register + policy hiddenpath.RegistrationPolicy + resolver func(*gomock.Controller) hiddenpath.AddressResolver }{ "Only public registration": { beacons: [][]common.IFIDType{ {graph.If_120_X_111_B}, {graph.If_130_B_120_A, graph.If_120_X_111_B}, }, - createRPCs: func(t *testing.T, - ctrl *gomock.Controller) map[hiddenpath.GroupID]beaconing.RPC { + createRPC: func(t *testing.T, + ctrl *gomock.Controller) hiddenpath.Register { - rpc := mock_beaconing.NewMockRPC(ctrl) + rpc := mock_hiddenpath.NewMockRegister(ctrl) rpc.EXPECT().RegisterSegment(gomock.Any(), gomock.Any(), matchSVCCS("1-ff00:0:120")).DoAndReturn( - func(_ context.Context, meta seg.Meta, remote net.Addr) error { - validatePublicSeg(t, meta.Segment, remote.(*snet.SVCAddr), topoProvider) + func(_ context.Context, reg hiddenpath.SegmentRegistration, + remote net.Addr) error { + validatePublicSeg(t, reg.Seg.Segment, remote.(*snet.SVCAddr), topoProvider) return nil }, ) rpc.EXPECT().RegisterSegment(gomock.Any(), gomock.Any(), matchSVCCS("1-ff00:0:130")).DoAndReturn( - func(_ context.Context, meta seg.Meta, remote net.Addr) error { - validatePublicSeg(t, meta.Segment, remote.(*snet.SVCAddr), topoProvider) + func(_ context.Context, reg hiddenpath.SegmentRegistration, + remote net.Addr) error { + validatePublicSeg(t, reg.Seg.Segment, remote.(*snet.SVCAddr), topoProvider) return nil }, ) - return map[hiddenpath.GroupID]beaconing.RPC{ - {}: rpc, - } + return rpc }, policy: hiddenpath.RegistrationPolicy{ uint64(graph.If_111_B_120_X): hiddenpath.InterfacePolicy{ @@ -139,24 +138,21 @@ func TestRemoteBeaconWriterWrite(t *testing.T) { {graph.If_120_X_111_B}, {graph.If_130_B_120_A, graph.If_120_X_111_B}, }, - createRPCs: func(t *testing.T, - ctrl *gomock.Controller) map[hiddenpath.GroupID]beaconing.RPC { - - rpc := mock_beaconing.NewMockRPC(ctrl) + createRPC: func(t *testing.T, + ctrl *gomock.Controller) hiddenpath.Register { + rpc := mock_hiddenpath.NewMockRegister(ctrl) rpc.EXPECT().RegisterSegment(gomock.Any(), gomock.Any(), addrMatcher{udp: &snet.UDPAddr{ IA: xtest.MustParseIA("1-ff00:0:114"), Host: xtest.MustParseUDPAddr(t, "10.1.0.1:404"), }}).Times(2).DoAndReturn( - func(_ context.Context, meta seg.Meta, remote net.Addr) error { - validateHS(t, meta.Segment) + func(_ context.Context, reg hiddenpath.SegmentRegistration, + remote net.Addr) error { + validateHS(t, reg.Seg.Segment) return nil }, ) - - return map[hiddenpath.GroupID]beaconing.RPC{ - mustParseGroupID(t, "ff00:0:140-2"): rpc, - } + return rpc }, policy: hiddenpath.RegistrationPolicy{ uint64(graph.If_111_B_120_X): hiddenpath.InterfacePolicy{ @@ -203,7 +199,7 @@ func TestRemoteBeaconWriterWrite(t *testing.T) { MaxExpTime: func() uint8 { return uint8(beacon.DefaultMaxExpTime) }, StaticInfo: func() *beaconing.StaticInfoCfg { return nil }, }, - RPCs: tc.createRPCs(t, ctrl), + RPC: tc.createRPC(t, ctrl), Pather: addrutil.Pather{ UnderlayNextHop: func(ifID uint16) (*net.UDPAddr, bool) { return topoProvider.Get().UnderlayNextHop2(common.IFIDType(ifID)) diff --git a/go/pkg/hiddenpath/forwarder.go b/go/pkg/hiddenpath/forwarder.go index e36dc42a9b..048c8b7e5c 100644 --- a/go/pkg/hiddenpath/forwarder.go +++ b/go/pkg/hiddenpath/forwarder.go @@ -29,9 +29,9 @@ type Lookuper interface { Segments(context.Context, SegmentRequest) ([]*seg.Meta, error) } -// RPC is used to fetch hidden segments from a remote. +// RPC is used to fetch hidden segments from a remote and to register segments to a remote. type RPC interface { - HiddenSegments(ctx context.Context, req SegmentRequest, dst net.Addr) ([]*seg.Meta, error) + HiddenSegments(context.Context, SegmentRequest, net.Addr) ([]*seg.Meta, error) } // Verifier is used to verify a segments reply. diff --git a/go/pkg/hiddenpath/grpc/BUILD.bazel b/go/pkg/hiddenpath/grpc/BUILD.bazel index 346b1c494e..aa0f692a91 100644 --- a/go/pkg/hiddenpath/grpc/BUILD.bazel +++ b/go/pkg/hiddenpath/grpc/BUILD.bazel @@ -5,12 +5,14 @@ go_library( srcs = [ "discovery.go", "lookup.go", + "register.go", "registry.go", "requester.go", ], importpath = "github.com/scionproto/scion/go/pkg/hiddenpath/grpc", visibility = ["//visibility:public"], deps = [ + "//go/cs/beaconing:go_default_library", "//go/lib/addr:go_default_library", "//go/lib/ctrl/seg:go_default_library", "//go/lib/infra/modules/segfetcher:go_default_library", @@ -18,6 +20,7 @@ go_library( "//go/lib/snet:go_default_library", "//go/pkg/grpc:go_default_library", "//go/pkg/hiddenpath:go_default_library", + "//go/pkg/proto/control_plane:go_default_library", "//go/pkg/proto/discovery:go_default_library", "//go/pkg/proto/hidden_segment:go_default_library", "@org_golang_google_grpc//codes:go_default_library", diff --git a/go/pkg/hiddenpath/grpc/register.go b/go/pkg/hiddenpath/grpc/register.go new file mode 100644 index 0000000000..f0052efc65 --- /dev/null +++ b/go/pkg/hiddenpath/grpc/register.go @@ -0,0 +1,64 @@ +// Copyright 2020 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. + +package grpc + +import ( + "context" + "net" + + "github.com/scionproto/scion/go/cs/beaconing" + "github.com/scionproto/scion/go/lib/ctrl/seg" + libgrpc "github.com/scionproto/scion/go/pkg/grpc" + "github.com/scionproto/scion/go/pkg/hiddenpath" + "github.com/scionproto/scion/go/pkg/proto/control_plane" + hppb "github.com/scionproto/scion/go/pkg/proto/hidden_segment" + hspb "github.com/scionproto/scion/go/pkg/proto/hidden_segment" +) + +// Register can be used to register segments to remotes. +type Register struct { + // Dialer dials a new gRPC connection. + Dialer libgrpc.Dialer + // RegularRegistration is the regular segment registration. + RegularRegistration beaconing.RPC +} + +// RegisterSegment registers the segment at the remote. If the hidden path group +// ID is not defined it is registered via a normal segment registration message +func (s Register) RegisterSegment(ctx context.Context, + reg hiddenpath.SegmentRegistration, remote net.Addr) error { + + if reg.GroupID.ToUint64() == 0 { // do regular public registration + return s.RegularRegistration.RegisterSegment(ctx, reg.Seg, remote) + } + + conn, err := s.Dialer.Dial(ctx, remote) + if err != nil { + return err + } + defer conn.Close() + + client := hspb.NewHiddenSegmentRegistrationServiceClient(conn) + in := &hspb.HiddenSegmentRegistrationRequest{ + GroupId: reg.GroupID.ToUint64(), + Segments: map[int32]*hppb.Segments{ + int32(reg.Seg.Type): {Segments: []*control_plane.PathSegment{ + seg.PathSegmentToPB(reg.Seg.Segment), + }}, + }, + } + _, err = client.HiddenSegmentRegistration(ctx, in, libgrpc.RetryProfile...) + return err +} diff --git a/go/pkg/hiddenpath/mock_hiddenpath/BUILD.bazel b/go/pkg/hiddenpath/mock_hiddenpath/BUILD.bazel index d302b48895..989aeb2501 100644 --- a/go/pkg/hiddenpath/mock_hiddenpath/BUILD.bazel +++ b/go/pkg/hiddenpath/mock_hiddenpath/BUILD.bazel @@ -12,6 +12,7 @@ gomock( "AddressResolver", "Discoverer", "Registry", + "Register", ], library = "//go/pkg/hiddenpath:go_default_library", package = "mock_hiddenpath", diff --git a/go/pkg/hiddenpath/mock_hiddenpath/mock.go b/go/pkg/hiddenpath/mock_hiddenpath/mock.go index 4999c78f50..3f5b7ec5b8 100644 --- a/go/pkg/hiddenpath/mock_hiddenpath/mock.go +++ b/go/pkg/hiddenpath/mock_hiddenpath/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/scionproto/scion/go/pkg/hiddenpath (interfaces: Store,RPC,Verifier,Lookuper,AddressResolver,Discoverer,Registry) +// Source: github.com/scionproto/scion/go/pkg/hiddenpath (interfaces: Store,RPC,Verifier,Lookuper,AddressResolver,Discoverer,Registry,Register) // Package mock_hiddenpath is a generated GoMock package. package mock_hiddenpath @@ -291,3 +291,40 @@ func (mr *MockRegistryMockRecorder) Register(arg0, arg1 interface{}) *gomock.Cal mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockRegistry)(nil).Register), arg0, arg1) } + +// MockRegister is a mock of Register interface +type MockRegister struct { + ctrl *gomock.Controller + recorder *MockRegisterMockRecorder +} + +// MockRegisterMockRecorder is the mock recorder for MockRegister +type MockRegisterMockRecorder struct { + mock *MockRegister +} + +// NewMockRegister creates a new mock instance +func NewMockRegister(ctrl *gomock.Controller) *MockRegister { + mock := &MockRegister{ctrl: ctrl} + mock.recorder = &MockRegisterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRegister) EXPECT() *MockRegisterMockRecorder { + return m.recorder +} + +// RegisterSegment mocks base method +func (m *MockRegister) RegisterSegment(arg0 context.Context, arg1 hiddenpath.SegmentRegistration, arg2 net.Addr) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RegisterSegment", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// RegisterSegment indicates an expected call of RegisterSegment +func (mr *MockRegisterMockRecorder) RegisterSegment(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterSegment", reflect.TypeOf((*MockRegister)(nil).RegisterSegment), arg0, arg1, arg2) +}