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
67 changes: 67 additions & 0 deletions pkg/ipam/strictvl3ipam/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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"

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

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

type strictVl3IPAMServer struct {
vl3IPAMs []*vl3.IPAM
}

// 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, newVl3IPAMServer func(context.Context, *vl3.IPAM) networkservice.NetworkServiceServer, vl3IPAMs ...*vl3.IPAM) networkservice.NetworkServiceServer {
elements := []networkservice.NetworkServiceServer{&strictVl3IPAMServer{vl3IPAMs: vl3IPAMs}}
for _, ipam := range vl3IPAMs {
elements = append(elements, newVl3IPAMServer(ctx, ipam))
}
return next.NewNetworkServiceServer(elements...)
}

func (s *strictVl3IPAMServer) areAddressesValid(addresses []string) bool {
if len(addresses) == 0 {
return true
}

for _, addr := range addresses {
for _, ipam := range s.vl3IPAMs {
if ipam.ContainsNetString(addr) {
return true
}
}
}
return false
}

func (s *strictVl3IPAMServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
if !s.areAddressesValid(request.GetConnection().GetContext().GetIpContext().GetDstIpAddrs()) {
request.Connection.Context.IpContext = &networkservice.IPContext{}
NikitaSkrynnik marked this conversation as resolved.
Show resolved Hide resolved
}
return next.Server(ctx).Request(ctx, request)
}

func (s *strictVl3IPAMServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) {
return next.Server(ctx).Close(ctx, conn)
}
136 changes: 95 additions & 41 deletions 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 @@ -31,12 +31,12 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/goleak"

"github.com/networkservicemesh/api/pkg/api/ipam"
"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/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 +45,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 All @@ -71,13 +72,12 @@ func Test_NSC_ConnectsTo_vl3NSE(t *testing.T) {

nseReg := defaultRegistryEndpoint(nsReg.Name)

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

serverPrefixCh <- &ipam.PrefixResponse{Prefix: "10.0.0.1/24"}
dnsServerIPCh := make(chan net.IP, 1)
dnsServerIPCh <- net.ParseIP("127.0.0.1")

var ipam vl3.IPAM
ipam.Reset(ctx, "10.0.0.1/24", []string{})

_ = domain.Nodes[0].NewEndpoint(
ctx,
nseReg,
Expand All @@ -86,7 +86,7 @@ func Test_NSC_ConnectsTo_vl3NSE(t *testing.T) {
dnsServerIPCh,
vl3dns.WithDomainSchemes("{{ index .Labels \"podName\" }}.{{ .NetworkService }}."),
vl3dns.WithDNSPort(40053)),
vl3.NewServer(ctx, serverPrefixCh),
vl3.NewServer(ctx, &ipam),
)

resolver := net.Resolver{
Expand Down Expand Up @@ -155,16 +155,13 @@ func Test_vl3NSE_ConnectsTo_vl3NSE(t *testing.T) {
require.NoError(t, err)

nseReg := defaultRegistryEndpoint(nsReg.Name)

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

serverPrefixCh <- &ipam.PrefixResponse{Prefix: "10.0.0.1/24"}

var dnsConfigs = new(genericsync.Map[string, []*networkservice.DNSConfig])
dnsServerIPCh := make(chan net.IP, 1)
dnsServerIPCh <- net.ParseIP("0.0.0.0")

var serverIpam vl3.IPAM
serverIpam.Reset(ctx, "10.0.0.1/24", []string{})

_ = domain.Nodes[0].NewEndpoint(
ctx,
nseReg,
Expand All @@ -178,7 +175,7 @@ func Test_vl3NSE_ConnectsTo_vl3NSE(t *testing.T) {
vl3dns.WithConfigs(dnsConfigs),
vl3dns.WithDNSPort(40053),
),
vl3.NewServer(ctx, serverPrefixCh),
vl3.NewServer(ctx, &serverIpam),
)

resolver := net.Resolver{
Expand All @@ -189,11 +186,9 @@ func Test_vl3NSE_ConnectsTo_vl3NSE(t *testing.T) {
},
}

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

clientPrefixCh <- &ipam.PrefixResponse{Prefix: "127.0.0.1/32"}
nsc := domain.Nodes[0].NewClient(ctx, sandbox.GenerateTestToken, client.WithAdditionalFunctionality(vl3dns.NewClient(net.ParseIP("127.0.0.1"), dnsConfigs), vl3.NewClient(ctx, clientPrefixCh)))
var clientIpam vl3.IPAM
clientIpam.Reset(ctx, "127.0.0.1/32", []string{})
nsc := domain.Nodes[0].NewClient(ctx, sandbox.GenerateTestToken, client.WithAdditionalFunctionality(vl3dns.NewClient(net.ParseIP("127.0.0.1"), dnsConfigs), vl3.NewClient(ctx, &clientIpam)))

req := defaultRequest(nsReg.Name)
req.Connection.Id = uuid.New().String()
Expand Down Expand Up @@ -247,13 +242,11 @@ func Test_NSC_GetsVl3DnsAddressDelay(t *testing.T) {
require.NoError(t, err)

nseReg := defaultRegistryEndpoint(nsReg.Name)

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

serverPrefixCh <- &ipam.PrefixResponse{Prefix: "10.0.0.1/24"}
dnsServerIPCh := make(chan net.IP, 1)

var ipam vl3.IPAM
ipam.Reset(ctx, "10.0.0.1/24", []string{})

_ = domain.Nodes[0].NewEndpoint(
ctx,
nseReg,
Expand All @@ -262,7 +255,7 @@ func Test_NSC_GetsVl3DnsAddressDelay(t *testing.T) {
dnsServerIPCh,
vl3dns.WithDomainSchemes("{{ index .Labels \"podName\" }}.{{ .NetworkService }}."),
vl3dns.WithDNSPort(40053)),
vl3.NewServer(ctx, serverPrefixCh))
vl3.NewServer(ctx, &ipam))

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

Expand Down Expand Up @@ -295,21 +288,19 @@ func Test_vl3NSE_ConnectsTo_Itself(t *testing.T) {
require.NoError(t, err)

nseReg := defaultRegistryEndpoint(nsReg.Name)

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

serverPrefixCh <- &ipam.PrefixResponse{Prefix: "10.0.0.1/24"}
dnsServerIPCh := make(chan net.IP, 1)

var ipam vl3.IPAM
ipam.Reset(ctx, "10.0.0.1/24", []string{})

_ = domain.Nodes[0].NewEndpoint(
ctx,
nseReg,
sandbox.GenerateTestToken,
vl3dns.NewServer(ctx,
dnsServerIPCh,
vl3dns.WithDNSPort(40053)),
vl3.NewServer(ctx, serverPrefixCh))
vl3.NewServer(ctx, &ipam))

// Connection to itself. This allows us to assign a dns address to ourselves.
nsc := domain.Nodes[0].NewClient(ctx, sandbox.GenerateTestToken, client.WithName(nseReg.Name))
Expand Down Expand Up @@ -349,15 +340,14 @@ func Test_Interdomain_vl3_dns(t *testing.T) {
NetworkServiceNames: []string{nsReg.Name},
}

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

serverPrefixCh <- &ipam.PrefixResponse{Prefix: "10.0.0.1/24"}
dnsServerIPCh := make(chan net.IP, 1)
dnsServerIPCh <- net.ParseIP("127.0.0.1")

var ipam vl3.IPAM
ipam.Reset(ctx, "10.0.0.1/24", []string{})

cluster2.Nodes[0].NewEndpoint(ctx, nseReg, sandbox.GenerateTestToken,
vl3.NewServer(ctx, serverPrefixCh),
vl3.NewServer(ctx, &ipam),
vl3dns.NewServer(ctx,
dnsServerIPCh,
vl3dns.WithDNSPort(40053),
Expand Down Expand Up @@ -450,15 +440,14 @@ func Test_FloatingInterdomain_vl3_dns(t *testing.T) {
NetworkServiceNames: []string{"vl3"},
}

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

serverPrefixCh <- &ipam.PrefixResponse{Prefix: "10.0.0.1/24"}
dnsServerIPCh := make(chan net.IP, 1)
dnsServerIPCh <- net.ParseIP("127.0.0.1")

var ipam vl3.IPAM
ipam.Reset(ctx, "10.0.0.1/24", []string{})

cluster2.Nodes[0].NewEndpoint(ctx, nseReg, sandbox.GenerateTestToken,
vl3.NewServer(ctx, serverPrefixCh),
vl3.NewServer(ctx, &ipam),
vl3dns.NewServer(ctx,
dnsServerIPCh,
vl3dns.WithDNSPort(40053),
Expand Down Expand Up @@ -509,3 +498,68 @@ 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*5)
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)

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

var serverIpam vl3.IPAM
serverIpam.Reset(ctx, prefix1, []string{})

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

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))

serverIpam.Reset(ctx, prefix2, []string{})

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
}
24 changes: 6 additions & 18 deletions pkg/networkservice/connectioncontext/ipcontext/vl3/client.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 All @@ -25,7 +25,6 @@

"github.com/edwarnicke/serialize"
"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/ipam"
"github.com/networkservicemesh/api/pkg/api/networkservice"
"google.golang.org/grpc"

Expand All @@ -34,7 +33,7 @@
)

type vl3Client struct {
pool vl3IPAM
pool *IPAM
chainContext context.Context
executor serialize.Executor
subscriptions []chan struct{}
Expand All @@ -44,29 +43,18 @@
//
// Produces refresh on prefix update.
// Requires begin and metdata chain elements.
func NewClient(chainContext context.Context, prefixCh <-chan *ipam.PrefixResponse) networkservice.NetworkServiceClient {
func NewClient(chainContext context.Context, pool *IPAM) networkservice.NetworkServiceClient {
if chainContext == nil {
panic("chainContext can not be nil")
}
if prefixCh == nil {
panic("prefixCh can not be nil")
if pool == nil {
panic("vl3IPAM pool can not be nil")

Check warning on line 51 in pkg/networkservice/connectioncontext/ipcontext/vl3/client.go

View check run for this annotation

Codecov / codecov/patch

pkg/networkservice/connectioncontext/ipcontext/vl3/client.go#L51

Added line #L51 was not covered by tests
}
var r = &vl3Client{
chainContext: chainContext,
pool: pool,
}

go func() {
for update := range prefixCh {
prefixResp := update
r.executor.AsyncExec(func() {
r.pool.reset(chainContext, prefixResp.GetPrefix(), prefixResp.GetExcludePrefixes())
for _, sub := range r.subscriptions {
sub <- struct{}{}
}
})
}
}()

return r
}

Expand Down
Loading
Loading