Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vl3 chain element that resets IPContext configuration depending on dst and src addresses #1588

Merged
78 changes: 78 additions & 0 deletions pkg/ipam/strictvl3ipam/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) 2024 Cisco and 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 strictvl3ipam provides a networkservice.NetworkService Server chain element that resets IP context configuration out of the settings scope
package strictvl3ipam

import (
"context"
"net"
"sync"

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

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

type strictVl3IPAMServer struct {
ipPool *ippool.IPPool
m sync.Mutex
}

// NewServer - returns a new ipam networkservice.NetworkServiceServer that validates the incoming IP context parameters and resets them based on the validation result.
func NewServer(ctx context.Context, prefixCh <-chan *ipam.PrefixResponse) networkservice.NetworkServiceServer {
NikitaSkrynnik marked this conversation as resolved.
Show resolved Hide resolved
var ipPool = ippool.New(net.IPv4len)

s := &strictVl3IPAMServer{ipPool: ipPool}
go func() {
for prefix := range prefixCh {
s.m.Lock()
s.ipPool = ippool.NewWithNetString(prefix.Prefix)
s.m.Unlock()
}
}()

return s
}

func (n *strictVl3IPAMServer) areAddressesValid(addresses []string) bool {
n.m.Lock()
defer n.m.Unlock()

for _, addr := range addresses {
if !n.ipPool.ContainsNetString(addr) {
NikitaSkrynnik marked this conversation as resolved.
Show resolved Hide resolved
return false
}
}
return true
}

func (n *strictVl3IPAMServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
srcAddrs := request.GetConnection().GetContext().GetIpContext().GetSrcIpAddrs()
dstAddrs := request.GetConnection().GetContext().GetIpContext().GetDstIpAddrs()

if !n.areAddressesValid(srcAddrs) && !n.areAddressesValid(dstAddrs) {
request.Connection.Context.IpContext = &networkservice.IPContext{}
NikitaSkrynnik marked this conversation as resolved.
Show resolved Hide resolved
}
return next.Server(ctx).Request(ctx, request)
}

func (n *strictVl3IPAMServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) {
return next.Server(ctx).Close(ctx, conn)
}
76 changes: 75 additions & 1 deletion pkg/networkservice/chains/nsmgr/vl3_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2022-2023 Cisco and/or its affiliates.
// Copyright (c) 2022-2024 Cisco and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
Expand Down Expand Up @@ -37,6 +37,7 @@ import (
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/kernel"
"github.com/networkservicemesh/api/pkg/api/registry"

"github.com/networkservicemesh/sdk/pkg/ipam/strictvl3ipam"
"github.com/networkservicemesh/sdk/pkg/networkservice/chains/client"
"github.com/networkservicemesh/sdk/pkg/networkservice/connectioncontext/dnscontext/vl3dns"
"github.com/networkservicemesh/sdk/pkg/networkservice/connectioncontext/ipcontext/vl3"
Expand All @@ -45,6 +46,7 @@ import (
"github.com/networkservicemesh/sdk/pkg/tools/dnsutils"
"github.com/networkservicemesh/sdk/pkg/tools/dnsutils/memory"
"github.com/networkservicemesh/sdk/pkg/tools/interdomain"
"github.com/networkservicemesh/sdk/pkg/tools/ippool"
"github.com/networkservicemesh/sdk/pkg/tools/sandbox"
)

Expand Down Expand Up @@ -509,3 +511,75 @@ func Test_FloatingInterdomain_vl3_dns(t *testing.T) {
_, err = resolver.LookupIP(ctx, "ip4", fmt.Sprintf("%s.%s", nscName, searchDomain))
require.Error(t, err)
}

func Test_NSC_ConnectsTo_vl3NSE_With_Invalid_IpContext(t *testing.T) {
t.Cleanup(func() { goleak.VerifyNone(t) })

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

domain := sandbox.NewBuilder(ctx, t).
SetNodesCount(1).
SetNSMgrProxySupplier(nil).
SetRegistryProxySupplier(nil).
Build()

nsRegistryClient := domain.NewNSRegistryClient(ctx, sandbox.GenerateTestToken)

nsReg, err := nsRegistryClient.Register(ctx, defaultRegistryService("vl3"))
require.NoError(t, err)

nseReg := defaultRegistryEndpoint(nsReg.Name)

var serverPrefixCh = make(chan *ipam.PrefixResponse, 1)
var strictIpamPrefixCh = make(chan *ipam.PrefixResponse, 1)
defer close(serverPrefixCh)
defer close(strictIpamPrefixCh)

prefix1 := "10.0.0.0/24"
prefix2 := "10.10.0.0/24"

serverPrefixCh <- &ipam.PrefixResponse{Prefix: prefix1}
strictIpamPrefixCh <- &ipam.PrefixResponse{Prefix: prefix1}

_ = domain.Nodes[0].NewEndpoint(
ctx,
nseReg,
sandbox.GenerateTestToken,
strictvl3ipam.NewServer(ctx, strictIpamPrefixCh),
vl3.NewServer(ctx, serverPrefixCh),
)

nsc := domain.Nodes[0].NewClient(ctx, sandbox.GenerateTestToken)

req := defaultRequest(nsReg.Name)
conn, err := nsc.Request(ctx, req)
require.NoError(t, err)

require.True(t, checkIPContext(conn.Context.IpContext, prefix1))

serverPrefixCh <- &ipam.PrefixResponse{Prefix: prefix2}
strictIpamPrefixCh <- &ipam.PrefixResponse{Prefix: prefix2}

req.Connection = conn
conn, err = nsc.Request(ctx, req)
require.NoError(t, err)

require.False(t, checkIPContext(conn.Context.IpContext, prefix1))
require.True(t, checkIPContext(conn.Context.IpContext, prefix2))
}

func checkIPContext(ipContext *networkservice.IPContext, prefix string) bool {
pool := ippool.NewWithNetString(prefix)
for _, addr := range ipContext.SrcIpAddrs {
if !pool.ContainsNetString(addr) {
return false
}
}
for _, addr := range ipContext.DstIpAddrs {
if !pool.ContainsNetString(addr) {
return false
}
}
return true
}
Loading