diff --git a/control/cmd/control/main.go b/control/cmd/control/main.go index d481cd607c..926bfd45b5 100644 --- a/control/cmd/control/main.go +++ b/control/cmd/control/main.go @@ -202,9 +202,16 @@ func realMain(ctx context.Context) error { nc := infraenv.NetworkConfig{ IA: topo.IA(), + // Public: (Historical name) The TCP/IP:port address for this (control) service. Public: topo.ControlServiceAddress(globalCfg.General.ID), + // ServiceResolution: The UDP/SCION address of the service "redirector". It is hosted by this + // (control service) process but as a separate service. + ServiceResolution: topo.ServiceResolutionAddress(globalCfg.General.ID), ReconnectToDispatcher: globalCfg.General.ReconnectToDispatcher, QUIC: infraenv.QUIC{ + // Address: the QUIC/SCION address of this service. See QUICStack() for how address + // assigned. This can be configured statically and can be identical to the Public address. + // If not configured: a dynamic port is assigned. Address: globalCfg.QUIC.Address, TLSVerifier: trust.NewTLSCryptoVerifier(trustDB), GetCertificate: cs.NewTLSCertificateLoader( diff --git a/control/hiddenpaths.go b/control/hiddenpaths.go index f0036c1f10..2d5372d431 100644 --- a/control/hiddenpaths.go +++ b/control/hiddenpaths.go @@ -80,26 +80,27 @@ func (c HiddenPathConfigurator) Setup(location string) (*HiddenPathRegistrationC }) if roles.Registry { log.Info("Starting hidden path authoritative and registration server") - hspb.RegisterAuthoritativeHiddenSegmentLookupServiceServer(c.InterASQUICServer, - &hpgrpc.AuthoritativeSegmentServer{ + hsSegmentService := &hpgrpc.AuthoritativeSegmentServer{ Lookup: c.localAuthServer(groups), Verifier: c.Verifier, - }) - hspb.RegisterHiddenSegmentRegistrationServiceServer(c.InterASQUICServer, - &hpgrpc.RegistrationServer{ - Registry: hiddenpath.RegistryServer{ - Groups: groups, - DB: &hiddenpath.Storer{ - DB: c.PathDB, - }, - Verifier: hiddenpath.VerifierAdapter{ - Verifier: c.Verifier, - }, - LocalIA: c.LocalIA, + } + hspb.RegisterAuthoritativeHiddenSegmentLookupServiceServer(c.InterASQUICServer, hsSegmentService) + hspb.RegisterAuthoritativeHiddenSegmentLookupServiceServer(c.IntraASTCPServer, hsSegmentService) + hsRegistrationService := &hpgrpc.RegistrationServer{ + Registry: hiddenpath.RegistryServer{ + Groups: groups, + DB: &hiddenpath.Storer{ + DB: c.PathDB, }, - Verifier: c.Verifier, + Verifier: hiddenpath.VerifierAdapter{ + Verifier: c.Verifier, + }, + LocalIA: c.LocalIA, }, - ) + Verifier: c.Verifier, + } + hspb.RegisterHiddenSegmentRegistrationServiceServer(c.InterASQUICServer, hsRegistrationService) + hspb.RegisterHiddenSegmentRegistrationServiceServer(c.IntraASTCPServer, hsRegistrationService) } if !roles.Writer { return nil, nil diff --git a/private/app/appnet/addr_test.go b/private/app/appnet/addr_test.go index df81943a42..408a1abca3 100644 --- a/private/app/appnet/addr_test.go +++ b/private/app/appnet/addr_test.go @@ -80,8 +80,8 @@ func TestRedirectQUIC(t *testing.T) { a, r, err := aw.RedirectToQUIC(context.Background(), tc.input) tc.assertErr(t, err) - assert.Equal(t, a, tc.wantAddr) - assert.Equal(t, r, tc.wantRedirect) + assert.Equal(t, tc.wantAddr, a) + assert.Equal(t, tc.wantRedirect, r) }) } @@ -97,9 +97,7 @@ func TestRedirectQUIC(t *testing.T) { router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil) path.EXPECT().Dataplane().Return(snetpath.SCION{}) path.EXPECT().UnderlayNextHop().Return(&net.UDPAddr{IP: net.ParseIP("10.1.1.1")}) - path.EXPECT().Metadata().Return(&snet.PathMetadata{ - Interfaces: make([]snet.PathInterface, 1), // just non-empty - }) + path.EXPECT().Interfaces().Return(make([]snet.PathInterface, 1)) // just non-empty aw := infraenv.AddressRewriter{ Router: router, @@ -134,9 +132,7 @@ func TestRedirectQUIC(t *testing.T) { router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil) path.EXPECT().Dataplane().Return(snetpath.SCION{}) path.EXPECT().UnderlayNextHop().Return(&net.UDPAddr{IP: net.ParseIP("10.1.1.1")}) - path.EXPECT().Metadata().Return(&snet.PathMetadata{ - Interfaces: make([]snet.PathInterface, 1), // just non-empty - }) + path.EXPECT().Interfaces().Return(make([]snet.PathInterface, 1)) // just non-empty aw := infraenv.AddressRewriter{ Router: router, @@ -166,7 +162,7 @@ func TestRedirectQUIC(t *testing.T) { router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil) path.EXPECT().Dataplane().Return(snetpath.SCION{}) path.EXPECT().UnderlayNextHop().Return(&net.UDPAddr{IP: net.ParseIP("10.1.1.1")}) - path.EXPECT().Metadata().Return(&snet.PathMetadata{}) + path.EXPECT().Interfaces().Return([]snet.PathInterface{}) svcRouter := mock_infraenv.NewMockSVCResolver(ctrl) svcRouter.EXPECT().GetUnderlay(addr.SvcCS).Return( &net.UDPAddr{IP: net.ParseIP("10.1.1.1")}, nil, @@ -182,7 +178,7 @@ func TestRedirectQUIC(t *testing.T) { want := &snet.SVCAddr{ SVC: addr.SvcCS, NextHop: &net.UDPAddr{IP: net.ParseIP("10.1.1.1")}, - Path: snetpath.SCION{}, + Path: snetpath.Empty{}, } a, r, err := aw.RedirectToQUIC(context.Background(), input) assert.NoError(t, err) @@ -225,7 +221,7 @@ func TestBuildFullAddress(t *testing.T) { SVC: addr.SvcCS, } a, err := aw.BuildFullAddress(context.Background(), input) - assert.Equal(t, a, input) + assert.Equal(t, input, a) assert.NoError(t, err) }) @@ -243,9 +239,7 @@ func TestBuildFullAddress(t *testing.T) { path := mock_snet.NewMockPath(ctrl) path.EXPECT().Dataplane().Return(snetpath.SCION{}) path.EXPECT().UnderlayNextHop().Return(&net.UDPAddr{}) - path.EXPECT().Metadata().Return(&snet.PathMetadata{ - Interfaces: make([]snet.PathInterface, 1), // just non-empty - }) + path.EXPECT().Interfaces().Return(make([]snet.PathInterface, 1)) // just non-empty router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil) input := &snet.SVCAddr{IA: remoteIA, SVC: addr.SvcCS, Path: snetpath.Empty{}} a, err := aw.BuildFullAddress(context.Background(), input) @@ -255,7 +249,7 @@ func TestBuildFullAddress(t *testing.T) { NextHop: &net.UDPAddr{}, SVC: addr.SvcCS, } - assert.Equal(t, a, want) + assert.Equal(t, want, a) assert.NoError(t, err) }) @@ -277,7 +271,7 @@ func TestBuildFullAddress(t *testing.T) { svcRouter.EXPECT().GetUnderlay(addr.SvcCS).Return(underlayAddr, nil) path := mock_snet.NewMockPath(ctrl) - path.EXPECT().Metadata().Return(&snet.PathMetadata{}) + path.EXPECT().Interfaces().Return([]snet.PathInterface{}) path.EXPECT().Dataplane() path.EXPECT().UnderlayNextHop() router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil) @@ -285,8 +279,8 @@ func TestBuildFullAddress(t *testing.T) { input := &snet.SVCAddr{IA: localIA, SVC: addr.SvcCS, Path: snetpath.Empty{}} a, err := aw.BuildFullAddress(context.Background(), input) - want := &snet.SVCAddr{IA: localIA, NextHop: underlayAddr, SVC: addr.SvcCS} - assert.Equal(t, a, want) + want := &snet.SVCAddr{IA: localIA, NextHop: underlayAddr, SVC: addr.SvcCS, Path: snetpath.Empty{}} + assert.Equal(t, want, a) assert.NoError(t, err) }) @@ -304,7 +298,7 @@ func TestBuildFullAddress(t *testing.T) { svcRouter.EXPECT().GetUnderlay(addr.SvcCS).Return(nil, errors.New("err")) path := mock_snet.NewMockPath(ctrl) - path.EXPECT().Metadata().Return(&snet.PathMetadata{}) + path.EXPECT().Interfaces().Return([]snet.PathInterface{}) path.EXPECT().Dataplane() path.EXPECT().UnderlayNextHop() router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil) @@ -388,9 +382,9 @@ func TestResolve(t *testing.T) { } initResolver(resolver, tc.ResolverSetup) p, a, redirect, err := aw.ResolveSVC(context.Background(), path, tc.input) - assert.Equal(t, p, tc.wantPath) - assert.Equal(t, a.String(), tc.want.String()) - assert.Equal(t, redirect, tc.wantQUICRedirect) + assert.Equal(t, tc.wantPath, p) + assert.Equal(t, tc.want.String(), a.String()) + assert.Equal(t, tc.wantQUICRedirect, redirect) tc.assertErr(t, err) }) } @@ -448,7 +442,7 @@ func TestParseReply(t *testing.T) { if err != nil { return } - assert.Equal(t, a.String(), tc.want.String()) + assert.Equal(t, tc.want.String(), a.String()) }) } } diff --git a/private/app/appnet/infraenv.go b/private/app/appnet/infraenv.go index 595cd1cc01..4cfb3d9c10 100644 --- a/private/app/appnet/infraenv.go +++ b/private/app/appnet/infraenv.go @@ -64,6 +64,8 @@ type NetworkConfig struct { // Public is the Internet-reachable address in the case where the service // is behind NAT. Public *net.UDPAddr + // ServiceResolution is the address of the service resolver. + ServiceResolution *net.UDPAddr // ReconnectToDispatcher sets up sockets that automatically reconnect if // the dispatcher closes the connection (e.g., if the dispatcher goes // down). @@ -266,7 +268,7 @@ func (nc *NetworkConfig) initSvcRedirect(quicAddress string) (func(), error) { Dispatcher: packetDispatcher, Metrics: nc.SCIONNetworkMetrics, } - conn, err := network.Listen(context.Background(), "udp", nc.Public, addr.SvcWildcard) + conn, err := network.Listen(context.Background(), "udp", nc.ServiceResolution, addr.SvcWildcard) if err != nil { return nil, serrors.WrapStr("listening on SCION", err, "addr", nc.Public) } diff --git a/private/topology/interface.go b/private/topology/interface.go index f6440c1770..f72cfcaa94 100644 --- a/private/topology/interface.go +++ b/private/topology/interface.go @@ -42,9 +42,15 @@ type Topology interface { InterfaceIDs() []common.IFIDType // PublicAddress gets the public address of a server with the requested type and name, and nil - // if no such server exists. + // if no such server exists. The service type is specified as a addr.SVC and retricted to + // addr.SvcDS and addr.SvcCS. PublicAddress(svc addr.SVC, name string) *net.UDPAddr + // PublicAddressByType gets the public address of a server with the requested type and name, and nil + // if no such server exists. This support more types than PublicAddress. That is, all the types + // specified by topology.ServiceType. + PublicAddressByType(svc ServiceType, name string) *net.UDPAddr + // Anycast returns the address for an arbitrary server of the requested type. Anycast(svc addr.SVC) (*net.UDPAddr, error) // Multicast returns all addresses for the requested type. @@ -203,6 +209,14 @@ func (t *topologyS) BR(name string) (BRInfo, bool) { return br, ok } +func (t *topologyS) PublicAddressByType(svc ServiceType, name string) *net.UDPAddr { + topoAddr, err := t.Topology.GetTopoAddr(name, svc) + if err != nil { + return nil + } + return topoAddr.SCIONAddress +} + func (t *topologyS) PublicAddress(svc addr.SVC, name string) *net.UDPAddr { topoAddr := t.topoAddress(svc, name) if topoAddr == nil { diff --git a/private/topology/json/json.go b/private/topology/json/json.go index 08e7cf333d..1f231b53cb 100644 --- a/private/topology/json/json.go +++ b/private/topology/json/json.go @@ -83,6 +83,7 @@ type Topology struct { HiddenSegmentLookup map[string]*ServerInfo `json:"hidden_segment_lookup_service,omitempty"` HiddenSegmentReg map[string]*ServerInfo `json:"hidden_segment_registration_service,omitempty"` SIG map[string]*GatewayInfo `json:"sigs,omitempty"` + ServiceResolution map[string]*ServerInfo `json:"service_resolution,omitempty"` } // ServerInfo contains the information for a SCION application running in the local AS. diff --git a/private/topology/reload.go b/private/topology/reload.go index 74f974681f..1436704d22 100644 --- a/private/topology/reload.go +++ b/private/topology/reload.go @@ -167,6 +167,13 @@ func (l *Loader) ControlServiceAddress(id string) *net.UDPAddr { return l.topo.PublicAddress(addr.SvcCS, id) } +func (l *Loader) ServiceResolutionAddress(id string) *net.UDPAddr { + l.mtx.Lock() + defer l.mtx.Unlock() + + return l.topo.PublicAddressByType(ServiceResolution, id) +} + // TODO(lukedirtwalker): remove error and simplify struct in the return type. func (l *Loader) Gateways() ([]GatewayInfo, error) { l.mtx.Lock() diff --git a/private/topology/servicetype.go b/private/topology/servicetype.go index bdfa9b21c6..acf80ccf60 100644 --- a/private/topology/servicetype.go +++ b/private/topology/servicetype.go @@ -28,6 +28,7 @@ const ( Gateway HiddenSegmentLookup HiddenSegmentRegistration + ServiceResolution ) func (t ServiceType) String() string { @@ -64,6 +65,8 @@ func ServiceTypeFromString(s string) ServiceType { return HiddenSegmentLookup case "hiddensegmentregistration": return HiddenSegmentRegistration + case "serviceresolution": + return ServiceResolution default: return Unknown } diff --git a/private/topology/topology.go b/private/topology/topology.go index 56bc023210..81372a9e78 100644 --- a/private/topology/topology.go +++ b/private/topology/topology.go @@ -72,6 +72,7 @@ type ( HiddenSegmentLookup IDAddrMap HiddenSegmentRegistration IDAddrMap SIG map[string]GatewayInfo + ServiceResolution IDAddrMap } // GatewayInfo describes a scion gateway. @@ -146,6 +147,7 @@ func NewRWTopology() *RWTopology { HiddenSegmentRegistration: make(IDAddrMap), SIG: make(map[string]GatewayInfo), IFInfoMap: make(IfInfoMap), + ServiceResolution: make(IDAddrMap), } } @@ -309,6 +311,10 @@ func (t *RWTopology) populateServices(raw *jsontopo.Topology) error { if err != nil { return serrors.WrapStr("unable to extract hidden segment registration address", err) } + t.ServiceResolution, err = svcMapFromRaw(raw.ServiceResolution) + if err != nil { + return serrors.WrapStr("unable to extract service resolution address", err) + } return nil } @@ -363,6 +369,8 @@ func (t *RWTopology) getSvcInfo(svc ServiceType) (*svcInfo, error) { m[k] = *v.CtrlAddr } return &svcInfo{idTopoAddrMap: m}, nil + case ServiceResolution: + return &svcInfo{idTopoAddrMap: t.ServiceResolution}, nil default: return nil, serrors.New("unsupported service type", "type", svc) } @@ -385,9 +393,10 @@ func (t *RWTopology) Copy() *RWTopology { CS: t.CS.copy(), DS: t.DS.copy(), - SIG: copySIGMap(t.SIG), HiddenSegmentLookup: t.HiddenSegmentLookup.copy(), HiddenSegmentRegistration: t.HiddenSegmentRegistration.copy(), + SIG: copySIGMap(t.SIG), + ServiceResolution: t.ServiceResolution.copy(), } }