diff --git a/pkg/networkservice/chains/nsmgr/server.go b/pkg/networkservice/chains/nsmgr/server.go index 29a6856f79..8ae094ff4e 100644 --- a/pkg/networkservice/chains/nsmgr/server.go +++ b/pkg/networkservice/chains/nsmgr/server.go @@ -22,6 +22,7 @@ package nsmgr import ( "context" + "github.com/networkservicemesh/sdk/pkg/networkservice/common/filtermechanisms" "github.com/networkservicemesh/sdk/pkg/networkservice/common/interpose" "github.com/networkservicemesh/api/pkg/api/networkservice" @@ -72,6 +73,7 @@ type nsmgrServer struct { func NewServer(ctx context.Context, nsmRegistration *registryapi.NetworkServiceEndpoint, authzServer networkservice.NetworkServiceServer, tokenGenerator token.GeneratorFunc, registryCC grpc.ClientConnInterface, clientDialOptions ...grpc.DialOption) Nsmgr { rv := &nsmgrServer{} + var urlsRegistryServer registryapi.NetworkServiceEndpointRegistryServer var localbypassRegistryServer registryapi.NetworkServiceEndpointRegistryServer nsRegistry := newRemoteNSServer(registryCC) @@ -100,6 +102,7 @@ func NewServer(ctx context.Context, nsmRegistration *registryapi.NetworkServiceE localbypass.NewServer(&localbypassRegistryServer), excludedprefixes.NewServer(ctx), interpose.NewServer(nsmRegistration.Name, &interposeRegistry), + filtermechanisms.NewServer(&urlsRegistryServer), connect.NewServer( ctx, client.NewClientFactory(nsmRegistration.Name, @@ -111,6 +114,7 @@ func NewServer(ctx context.Context, nsmRegistration *registryapi.NetworkServiceE nsChain := chain_registry.NewNetworkServiceRegistryServer(nsRegistry) nseChain := chain_registry.NewNetworkServiceEndpointRegistryServer( + urlsRegistryServer, interposeRegistry, // Store cross connect NSEs localbypassRegistryServer, // Store endpoint Id to EndpointURL for local access. seturl.NewNetworkServiceEndpointRegistryServer(nsmRegistration.Url), // Remember endpoint URL diff --git a/pkg/networkservice/common/filtermechanisms/client.go b/pkg/networkservice/common/filtermechanisms/client.go deleted file mode 100644 index 7defe86f0f..0000000000 --- a/pkg/networkservice/common/filtermechanisms/client.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2019-2020 VMware, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// 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. - -// TODO find a better shorter name for this package - -// Package filtermechanisms filters out remote mechanisms if communicating to/from a unix file socket, -// filters out local mechanisms otherwise. -package filtermechanisms - -import ( - "context" - - "github.com/networkservicemesh/sdk/pkg/networkservice/common/clienturl" - - "github.com/golang/protobuf/ptypes/empty" - "google.golang.org/grpc" - - "github.com/networkservicemesh/api/pkg/api/networkservice" - "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls" - - "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" -) - -type filterMechanismsClient struct{} - -// NewClient - filters out remote mechanisms if connecting to a server over a unix file socket, otherwise filters -// out local mechanisms -func NewClient() networkservice.NetworkServiceClient { - return &filterMechanismsClient{} -} - -func (f *filterMechanismsClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { - u := clienturl.ClientURL(ctx) - if u.Scheme == clienturl.UnixURLScheme { - var mechanisms []*networkservice.Mechanism - for _, mechanism := range request.GetMechanismPreferences() { - if mechanism.Cls == cls.LOCAL { - mechanisms = append(mechanisms, mechanism) - } - } - request.MechanismPreferences = mechanisms - return next.Client(ctx).Request(ctx, request, opts...) - } - var mechanisms []*networkservice.Mechanism - for _, mechanism := range request.GetMechanismPreferences() { - if mechanism.Cls == cls.REMOTE { - mechanisms = append(mechanisms, mechanism) - } - } - request.MechanismPreferences = mechanisms - return next.Client(ctx).Request(ctx, request, opts...) -} - -func (f *filterMechanismsClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { - return next.Client(ctx).Close(ctx, conn, opts...) -} diff --git a/pkg/networkservice/common/filtermechanisms/client_test.go b/pkg/networkservice/common/filtermechanisms/client_test.go deleted file mode 100644 index c71d890a60..0000000000 --- a/pkg/networkservice/common/filtermechanisms/client_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2020 Doc.ai and/or its affiliates. -// -// SPDX-License-Identifier: Apache-2.0 -// -// 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 filtermechanisms_test provides a tests for package 'filtermechanisms' -package filtermechanisms_test - -import ( - "context" - "net/url" - "testing" - - "github.com/networkservicemesh/sdk/pkg/networkservice/common/clienturl" - - "github.com/networkservicemesh/api/pkg/api/networkservice" - "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls" - "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/kernel" - "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/memif" - "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/srv6" - "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vxlan" - "github.com/stretchr/testify/assert" - "go.uber.org/goleak" - - "github.com/networkservicemesh/sdk/pkg/networkservice/common/filtermechanisms" - "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" - "github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkrequest" -) - -func request() *networkservice.NetworkServiceRequest { - return &networkservice.NetworkServiceRequest{ - Connection: &networkservice.Connection{}, - MechanismPreferences: []*networkservice.Mechanism{ - { - Cls: cls.LOCAL, - Type: memif.MECHANISM, - }, - { - Cls: cls.LOCAL, - Type: kernel.MECHANISM, - }, - { - Cls: cls.REMOTE, - Type: srv6.MECHANISM, - }, - { - Cls: cls.REMOTE, - Type: vxlan.MECHANISM, - }, - { - Cls: "NOT_A_CLS", - Type: "NOT_A_TYPE", - }, - }, - } -} - -func TestNewClient_FilterUnixType(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) - ctx := clienturl.WithClientURL(context.Background(), &url.URL{ - Scheme: "unix", - Path: "/var/run/nse-1.sock", - }) - client := next.NewNetworkServiceClient( - filtermechanisms.NewClient(), - checkrequest.NewClient(t, func(t *testing.T, serviceRequest *networkservice.NetworkServiceRequest) { - expected := []*networkservice.Mechanism{ - { - Cls: cls.LOCAL, - Type: memif.MECHANISM, - }, - { - Cls: cls.LOCAL, - Type: kernel.MECHANISM, - }, - } - assert.Equal(t, expected, serviceRequest.GetMechanismPreferences()) - }), - ) - _, err := client.Request(ctx, request()) - assert.Nil(t, err) -} - -func TestNewClient_FilterNonUnixType(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) - ctx := clienturl.WithClientURL(context.Background(), &url.URL{ - Scheme: "ipv4", - Path: "192.168.0.1", - }) - client := next.NewNetworkServiceClient( - filtermechanisms.NewClient(), - checkrequest.NewClient(t, func(t *testing.T, serviceRequest *networkservice.NetworkServiceRequest) { - expected := []*networkservice.Mechanism{ - { - Cls: cls.REMOTE, - Type: srv6.MECHANISM, - }, - { - Cls: cls.REMOTE, - Type: vxlan.MECHANISM, - }, - } - assert.Equal(t, expected, serviceRequest.GetMechanismPreferences()) - }), - ) - _, err := client.Request(ctx, request()) - assert.Nil(t, err) -} diff --git a/pkg/networkservice/common/filtermechanisms/server.go b/pkg/networkservice/common/filtermechanisms/server.go index c422477bf9..d38ed5fa14 100644 --- a/pkg/networkservice/common/filtermechanisms/server.go +++ b/pkg/networkservice/common/filtermechanisms/server.go @@ -20,42 +20,33 @@ import ( "context" "github.com/golang/protobuf/ptypes/empty" - "google.golang.org/grpc/peer" - "github.com/networkservicemesh/api/pkg/api/networkservice" "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls" + "github.com/networkservicemesh/api/pkg/api/registry" "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" + "github.com/networkservicemesh/sdk/pkg/registry/common/endpointurls" + "github.com/networkservicemesh/sdk/pkg/tools/clienturl" ) -type filterMechanismsServer struct{} +type filterMechanismsServer struct { + urls endpointurls.Set +} // NewServer - filters out remote mechanisms if connection is received from a unix file socket, otherwise filters // out local mechanisms -func NewServer() networkservice.NetworkServiceServer { - return &filterMechanismsServer{} +func NewServer(registryServer *registry.NetworkServiceEndpointRegistryServer) networkservice.NetworkServiceServer { + result := &filterMechanismsServer{} + *registryServer = endpointurls.NewNetworkServiceEndpointRegistryServer(&result.urls) + return result } func (f *filterMechanismsServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) { - p, ok := peer.FromContext(ctx) - if ok { - if p.Addr.Network() == "unix" { - var mechanisms []*networkservice.Mechanism - for _, mechanism := range request.GetMechanismPreferences() { - if mechanism.Cls == cls.LOCAL { - mechanisms = append(mechanisms, mechanism) - } - } - request.MechanismPreferences = mechanisms - return next.Server(ctx).Request(ctx, request) - } - var mechanisms []*networkservice.Mechanism - for _, mechanism := range request.GetMechanismPreferences() { - if mechanism.Cls == cls.REMOTE { - mechanisms = append(mechanisms, mechanism) - } - } - request.MechanismPreferences = mechanisms + u := clienturl.ClientURL(ctx) + if _, ok := f.urls.Load(*u); ok { + request.MechanismPreferences = filterMechanismsByCls(request.GetMechanismPreferences(), cls.LOCAL) + } else { + request.MechanismPreferences = filterMechanismsByCls(request.GetMechanismPreferences(), cls.REMOTE) } return next.Server(ctx).Request(ctx, request) } @@ -63,3 +54,12 @@ func (f *filterMechanismsServer) Request(ctx context.Context, request *networkse func (f *filterMechanismsServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { return next.Server(ctx).Close(ctx, conn) } +func filterMechanismsByCls(mechanisms []*networkservice.Mechanism, mechanismCls string) []*networkservice.Mechanism { + var result []*networkservice.Mechanism + for _, mechanism := range mechanisms { + if mechanism.Cls == mechanismCls { + mechanisms = append(mechanisms, mechanism) + } + } + return result +} diff --git a/pkg/networkservice/common/filtermechanisms/server_test.go b/pkg/networkservice/common/filtermechanisms/server_test.go index 13ecbde63f..14279c1e76 100644 --- a/pkg/networkservice/common/filtermechanisms/server_test.go +++ b/pkg/networkservice/common/filtermechanisms/server_test.go @@ -1,24 +1,8 @@ -// Copyright (c) 2020 Doc.ai and/or its affiliates. -// -// SPDX-License-Identifier: Apache-2.0 -// -// 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 filtermechanisms_test import ( "context" - "net" + "net/url" "testing" "github.com/networkservicemesh/api/pkg/api/networkservice" @@ -27,66 +11,74 @@ import ( "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/memif" "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/srv6" "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vxlan" - "github.com/stretchr/testify/assert" - "go.uber.org/goleak" - "google.golang.org/grpc/peer" + "github.com/networkservicemesh/api/pkg/api/registry" + "github.com/stretchr/testify/require" "github.com/networkservicemesh/sdk/pkg/networkservice/common/filtermechanisms" - "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" - "github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkrequest" + "github.com/networkservicemesh/sdk/pkg/tools/clienturl" ) -func TestNewServer_FilterUnixType(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) - ctx := peer.NewContext(context.Background(), &peer.Peer{ - Addr: &net.UnixAddr{ - Name: "/var/run/nse-1.sock", - Net: "unix", - }, - }) - server := next.NewNetworkServiceServer( - filtermechanisms.NewServer(), - checkrequest.NewServer(t, func(t *testing.T, serviceRequest *networkservice.NetworkServiceRequest) { - expected := []*networkservice.Mechanism{ +func TestFilterMechanismsServer_Request(t *testing.T) { + request := func() *networkservice.NetworkServiceRequest { + return &networkservice.NetworkServiceRequest{ + MechanismPreferences: []*networkservice.Mechanism{ { - Cls: cls.LOCAL, - Type: memif.MECHANISM, + Cls: cls.REMOTE, + Type: srv6.MECHANISM, + }, + { + Cls: cls.REMOTE, + Type: vxlan.MECHANISM, }, { Cls: cls.LOCAL, Type: kernel.MECHANISM, }, - } - assert.Equal(t, expected, serviceRequest.GetMechanismPreferences()) - }), - ) - _, err := server.Request(ctx, request()) - assert.Nil(t, err) -} - -func TestNewServer_FilterNonUnixType(t *testing.T) { - defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) - ctx := peer.NewContext(context.Background(), &peer.Peer{ - Addr: &net.IPAddr{ - IP: net.IP{192, 168, 0, 1}, - }, - }) - server := next.NewNetworkServiceServer( - filtermechanisms.NewServer(), - checkrequest.NewServer(t, func(t *testing.T, serviceRequest *networkservice.NetworkServiceRequest) { - expected := []*networkservice.Mechanism{ { - Cls: cls.REMOTE, - Type: srv6.MECHANISM, + Cls: cls.LOCAL, + Type: memif.MECHANISM, }, + }, + } + } + samples := []struct { + Name string + ClientURL *url.URL + RegisterURLs []url.URL + ClsResult string + }{ + { + Name: "Local mechanisms", + ClientURL: &url.URL{Scheme: "tcp", Host: "test1"}, + RegisterURLs: []url.URL{ { - Cls: cls.REMOTE, - Type: vxlan.MECHANISM, + Scheme: "tcp", + Host: "test1", }, - } - assert.Equal(t, expected, serviceRequest.GetMechanismPreferences()) - }), - ) - _, err := server.Request(ctx, request()) - assert.Nil(t, err) + }, + ClsResult: cls.LOCAL, + }, + { + Name: "Remote mechanisms", + ClientURL: &url.URL{Scheme: "tcp", Host: "test1"}, + ClsResult: cls.LOCAL, + }, + } + + for _, sample := range samples { + var registryServer registry.NetworkServiceEndpointRegistryServer + s := filtermechanisms.NewServer(®istryServer) + for _, u := range sample.RegisterURLs { + registryServer.Register(context.Background(), ®istry.NetworkServiceEndpoint{ + Url: u.String(), + }) + } + ctx := clienturl.WithClientURL(context.Background(), sample.ClientURL) + req := request() + _, err := s.Request(ctx, req) + require.NoError(t, err) + for _, m := range req.MechanismPreferences { + require.Equal(t, sample.ClsResult, m.Cls, "filtermechanisms chain element should properly filter mechanisms") + } + } } diff --git a/pkg/registry/common/endpointurls/gen.go b/pkg/registry/common/endpointurls/gen.go new file mode 100644 index 0000000000..034cb39a9b --- /dev/null +++ b/pkg/registry/common/endpointurls/gen.go @@ -0,0 +1,11 @@ +package endpointurls + +import ( + "sync" +) + +//go:generate go-syncmap -output sync_set.gen.go -type Set + +// Set is like a Go map[url.URL]struct{} but is safe for concurrent use +// by multiple goroutines without additional locking or coordination +type Set sync.Map diff --git a/pkg/registry/common/endpointurls/server.go b/pkg/registry/common/endpointurls/server.go new file mode 100644 index 0000000000..c8a0776665 --- /dev/null +++ b/pkg/registry/common/endpointurls/server.go @@ -0,0 +1,37 @@ +package endpointurls + +import ( + "context" + "net/url" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/networkservicemesh/api/pkg/api/registry" + + "github.com/networkservicemesh/sdk/pkg/registry/core/next" +) + +type endpointURLsServer struct { + set *Set +} + +func (e *endpointURLsServer) Register(ctx context.Context, endpoint *registry.NetworkServiceEndpoint) (*registry.NetworkServiceEndpoint, error) { + if u, err := url.Parse(endpoint.Url); err == nil { + e.set.Store(*u, struct{}{}) + } + return next.NetworkServiceEndpointRegistryServer(ctx).Register(ctx, endpoint) +} + +func (e *endpointURLsServer) Find(query *registry.NetworkServiceEndpointQuery, server registry.NetworkServiceEndpointRegistry_FindServer) error { + return next.NetworkServiceEndpointRegistryServer(server.Context()).Find(query, server) +} + +func (e *endpointURLsServer) Unregister(ctx context.Context, endpoint *registry.NetworkServiceEndpoint) (*empty.Empty, error) { + if u, err := url.Parse(endpoint.Url); err == nil { + e.set.Delete(*u) + } + return next.NetworkServiceEndpointRegistryServer(ctx).Unregister(ctx, endpoint) +} + +func NewNetworkServiceEndpointRegistryServer(set *Set) registry.NetworkServiceEndpointRegistryServer { + return &endpointURLsServer{set: set} +}