Skip to content

Commit

Permalink
Split swapIP into client and server (#1314)
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
  • Loading branch information
glazychev-art authored Jun 29, 2022
1 parent 3277a17 commit a8211c5
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 16 deletions.
3 changes: 3 additions & 0 deletions pkg/networkservice/chains/nsmgrproxy/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ func NewServer(ctx context.Context, regURL, proxyURL *url.URL, tokenGenerator to
client.WithDialOptions(opts.dialOptions...),
client.WithDialTimeout(opts.dialTimeout),
client.WithoutRefresh(),
client.WithAdditionalFunctionality(
swapip.NewClient(opts.openMapIPChannel(ctx)),
),
),
),
),
Expand Down
90 changes: 90 additions & 0 deletions pkg/networkservice/common/swapip/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2022 Cisco 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 swapip provides chain element to swapping fields of remote mechanisms such as common.SrcIP and common.DstIP
// from internal to external and vice versa on response.
package swapip

import (
"context"
"sync/atomic"

"google.golang.org/grpc"

"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/common"

"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
)

type swapIPClient struct {
internalToExternalMap *atomic.Value
}

// NewClient creates new swap chain element. Expects public IP address of node
func NewClient(updateIPMapCh <-chan map[string]string) networkservice.NetworkServiceClient {
var v = new(atomic.Value)
v.Store(map[string]string{})
go func() {
for data := range updateIPMapCh {
v.Store(data)
}
}()
return &swapIPClient{internalToExternalMap: v}
}

func (i *swapIPClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
internalToExternalMap := i.internalToExternalMap.Load().(map[string]string)
mechanisms := request.GetMechanismPreferences()
var isSourceSide bool

if m := request.GetConnection().GetMechanism(); m != nil {
mechanisms = append(mechanisms, m)
}

for _, m := range mechanisms {
params := m.GetParameters()
if params != nil {
srcIP, ok := params[common.SrcIP]
if !ok {
continue
}
isSourceSide = params[common.SrcOriginalIP] == ""
if isSourceSide {
params[common.SrcIP], params[common.SrcOriginalIP] = internalToExternalMap[srcIP], srcIP
}
}
}

resp, err := next.Client(ctx).Request(ctx, request, opts...)
if err != nil {
return nil, err
}

params := resp.GetMechanism().GetParameters()
if params != nil {
if isSourceSide {
params[common.SrcIP], params[common.SrcOriginalIP] = params[common.SrcOriginalIP], ""
}
}

return resp, err
}

func (i *swapIPClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
return next.Client(ctx).Close(ctx, conn, opts...)
}
101 changes: 101 additions & 0 deletions pkg/networkservice/common/swapip/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) 2022 Cisco 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 swapip_test

import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"

"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/common"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"

"github.com/networkservicemesh/sdk/pkg/networkservice/common/swapip"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkrequest"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkresponse"
"github.com/networkservicemesh/sdk/pkg/tools/fs"
)

// nolint:goconst
func TestSwapIPClient_Request(t *testing.T) {
defer goleak.VerifyNone(t)

p1 := filepath.Join(t.TempDir(), "map-ip-1.yaml")
p2 := filepath.Join(t.TempDir(), "map-ip-2.yaml")

ctx, cancel := context.WithTimeout(context.Background(), time.Second*10000)
defer cancel()

err := ioutil.WriteFile(p1, []byte(`172.16.2.10: 172.16.1.10`), os.ModePerm)
require.NoError(t, err)

err = ioutil.WriteFile(p2, []byte(`172.16.2.100: 172.16.1.100`), os.ModePerm)
require.NoError(t, err)

ch1 := convertBytesChToMapCh(fs.WatchFile(ctx, p1))
ch2 := convertBytesChToMapCh(fs.WatchFile(ctx, p2))

var testChain = next.NewNetworkServiceClient(
/* Source side */
checkresponse.NewClient(t, func(t *testing.T, c *networkservice.Connection) {
require.Equal(t, "172.16.2.10", c.Mechanism.Parameters[common.SrcIP])
require.Equal(t, "", c.Mechanism.Parameters[common.SrcOriginalIP])
require.Equal(t, "172.16.2.100", c.Mechanism.Parameters[common.DstIP])
require.Equal(t, "172.16.2.100", c.Mechanism.Parameters[common.DstOriginalIP])
}),
swapip.NewClient(ch1),
checkrequest.NewClient(t, func(t *testing.T, r *networkservice.NetworkServiceRequest) {
require.Equal(t, "172.16.1.10", r.Connection.Mechanism.Parameters[common.SrcIP])
require.Equal(t, "172.16.2.10", r.Connection.Mechanism.Parameters[common.SrcOriginalIP])
}),
/* Destination side */
checkresponse.NewClient(t, func(t *testing.T, c *networkservice.Connection) {
require.Equal(t, "172.16.1.10", c.Mechanism.Parameters[common.SrcIP])
require.Equal(t, "172.16.2.10", c.Mechanism.Parameters[common.SrcOriginalIP])
}),
swapip.NewClient(ch2),
checkrequest.NewClient(t, func(t *testing.T, r *networkservice.NetworkServiceRequest) {
r.Connection.Mechanism.Parameters[common.DstIP] = "172.16.2.100"
r.Connection.Mechanism.Parameters[common.DstOriginalIP] = "172.16.2.100"
}),
)

r := &networkservice.NetworkServiceRequest{
Connection: &networkservice.Connection{
Mechanism: &networkservice.Mechanism{
Parameters: map[string]string{
common.SrcIP: "172.16.2.10",
},
},
},
}

time.Sleep(time.Second / 4)

resp, err := testChain.Request(context.Background(), r)
require.NoError(t, err)

// refresh
_, err = testChain.Request(ctx, &networkservice.NetworkServiceRequest{Connection: resp})
require.NoError(t, err)
}
24 changes: 13 additions & 11 deletions pkg/networkservice/common/swapip/server.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
// Copyright (c) 2022 Cisco and/or its affiliates.
//
// Copyright (c) 2021-2022 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
Expand Down Expand Up @@ -36,7 +38,7 @@ type swapIPServer struct {
func (i *swapIPServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
internalToExternalMap := i.internalToExternalMap.Load().(map[string]string)
mechanisms := request.GetMechanismPreferences()
var isSorceSide bool
var isSourceSide bool

if m := request.GetConnection().GetMechanism(); m != nil {
mechanisms = append(mechanisms, m)
Expand All @@ -45,13 +47,15 @@ func (i *swapIPServer) Request(ctx context.Context, request *networkservice.Netw
for _, m := range mechanisms {
params := m.GetParameters()
if params != nil {
isSorceSide = m.GetParameters()[common.SrcOriginalIP] == ""
if isSorceSide {
srcIP := params[common.SrcIP]
params[common.SrcIP], params[common.SrcOriginalIP] = internalToExternalMap[srcIP], srcIP
_, ok := params[common.SrcIP]
if !ok {
continue
}
isSourceSide = params[common.SrcOriginalIP] == ""
if !isSourceSide {
params[common.DstOriginalIP] = ""
params[common.DstIP] = ""
}
params[common.DstOriginalIP] = ""
params[common.DstIP] = ""
}
}

Expand All @@ -64,9 +68,7 @@ func (i *swapIPServer) Request(ctx context.Context, request *networkservice.Netw
params := resp.GetMechanism().GetParameters()

if params != nil {
if isSorceSide {
params[common.SrcIP], params[common.SrcOriginalIP] = params[common.SrcOriginalIP], ""
} else {
if !isSourceSide {
dstIP := params[common.DstIP]
params[common.DstIP], params[common.DstOriginalIP] = internalToExternalMap[dstIP], dstIP
}
Expand Down
14 changes: 9 additions & 5 deletions pkg/networkservice/common/swapip/server_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
// Copyright (c) 2022 Cisco and/or its affiliates.
//
// Copyright (c) 2021-2022 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
Expand Down Expand Up @@ -56,17 +58,19 @@ func TestSwapIPServer_Request(t *testing.T) {
ch2 := convertBytesChToMapCh(fs.WatchFile(ctx, p2))

var testChain = next.NewNetworkServiceServer(
/* Source side */
checkresponse.NewServer(t, func(t *testing.T, c *networkservice.Connection) {
require.Equal(t, "172.16.2.10", c.Mechanism.Parameters[common.SrcIP])
require.Equal(t, "", c.Mechanism.Parameters[common.SrcOriginalIP])
require.Equal(t, "172.16.1.100", c.Mechanism.Parameters[common.DstIP])
require.Equal(t, "172.16.2.100", c.Mechanism.Parameters[common.DstOriginalIP])
c.Mechanism.Parameters[common.SrcOriginalIP] = ""
}),
swapip.NewServer(ch1),
checkrequest.NewServer(t, func(t *testing.T, r *networkservice.NetworkServiceRequest) {
require.Equal(t, "172.16.1.10", r.Connection.Mechanism.Parameters[common.SrcIP])
require.Equal(t, "172.16.2.10", r.Connection.Mechanism.Parameters[common.SrcOriginalIP])
require.Equal(t, "172.16.2.10", r.Connection.Mechanism.Parameters[common.SrcIP])
require.Equal(t, "", r.Connection.Mechanism.Parameters[common.SrcOriginalIP])
r.Connection.Mechanism.Parameters[common.SrcOriginalIP] = "172.16.2.10"
}),
/* Destination side */
checkresponse.NewServer(t, func(t *testing.T, c *networkservice.Connection) {
require.Equal(t, "172.16.1.100", c.Mechanism.Parameters[common.DstIP])
require.Equal(t, "172.16.2.100", c.Mechanism.Parameters[common.DstOriginalIP])
Expand Down
57 changes: 57 additions & 0 deletions pkg/networkservice/utils/checks/checkresponse/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2022 Cisco and/or its affiliates.
//
// Copyright (c) 2021-2022 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 checkresponse - provides networkservice chain elements to check the response received from the next element in the chain
package checkresponse

import (
"context"
"testing"

"google.golang.org/grpc"

"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"

"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
)

type checkResponseClient struct {
*testing.T
check func(*testing.T, *networkservice.Connection)
}

// NewClient - returns NetworkServiceClient chain elements to check the response received from the next element in the chain
// t - *testing.T for checks
// check - function to check the Connnection
func NewClient(t *testing.T, check func(*testing.T, *networkservice.Connection)) networkservice.NetworkServiceClient {
return &checkResponseClient{
T: t,
check: check,
}
}

func (c *checkResponseClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
resp, err := next.Client(ctx).Request(ctx, request, opts...)
c.check(c.T, resp)
return resp, err
}

func (c *checkResponseClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
return next.Client(ctx).Close(ctx, conn, opts...)
}

0 comments on commit a8211c5

Please sign in to comment.