Skip to content

Commit

Permalink
vl3 chain element that resets IPContext configuration depending on ds…
Browse files Browse the repository at this point in the history
…t and src addresses (#1588)

* add strict ipam for vl3 networks

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* fix chain element

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* rename chain element

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* fix go linter issues

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* apply review comments

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* remove all channels, use ipPools only

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* rerun CI

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* fix review comments

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* fix review comments

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* fix go linter issues

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* revert changes

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

* revert some changes + add mutex to ContainsNetString

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>

---------

Signed-off-by: NikitaSkrynnik <nikita.skrynnik@xored.com>
  • Loading branch information
NikitaSkrynnik authored Mar 19, 2024
1 parent b1a3e26 commit 3027f90
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 160 deletions.
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{}
}
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 @@ import (

"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 @@ import (
)

type vl3Client struct {
pool vl3IPAM
pool *IPAM
chainContext context.Context
executor serialize.Executor
subscriptions []chan struct{}
Expand All @@ -44,29 +43,18 @@ type vl3Client struct {
//
// 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")
}
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

0 comments on commit 3027f90

Please sign in to comment.