diff --git a/pkg/networkservice/chains/forwarder/server.go b/pkg/networkservice/chains/forwarder/server.go index 1d53416c..0a866b7a 100644 --- a/pkg/networkservice/chains/forwarder/server.go +++ b/pkg/networkservice/chains/forwarder/server.go @@ -63,6 +63,7 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/tag" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/up" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/xconnect" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" ) // Connection aggregates the api.Connection and api.ChannelProvider interfaces @@ -87,6 +88,12 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, vppConn for _, opt := range options { opt(opts) } + + // Create a function, that will dump NSM interfaces and apply to them onDump func + dumper := func(onDump dumptool.OnDumpFn, isClient bool) error { + return dumptool.DumpVppInterfaces(ctx, vppConn, opts.name, isClient, onDump) + } + nseClient := registryclient.NewNetworkServiceEndpointRegistryClient(ctx, opts.clientURL, registryclient.WithNSEAdditionalFunctionality( registryrecvfd.NewNetworkServiceEndpointRegistryClient(), @@ -113,7 +120,7 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, vppConn memif.MECHANISM: memif.NewServer(ctx, vppConn, memif.WithDirectMemif(), memif.WithChangeNetNS()), - kernel.MECHANISM: kernel.NewServer(vppConn), + kernel.MECHANISM: kernel.NewServer(vppConn, kernel.WithDump(dumper)), vxlan.MECHANISM: vxlan.NewServer(vppConn, tunnelIP, opts.vxlanOpts...), wireguard.MECHANISM: wireguard.NewServer(vppConn, tunnelIP), }), @@ -136,7 +143,7 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, vppConn memif.NewClient(vppConn, memif.WithChangeNetNS(), ), - kernel.NewClient(vppConn), + kernel.NewClient(vppConn, kernel.WithDump(dumper)), vxlan.NewClient(vppConn, tunnelIP, opts.vxlanOpts...), wireguard.NewClient(vppConn, tunnelIP), vlan.NewClient(vppConn, opts.domain2Device), diff --git a/pkg/networkservice/mechanisms/kernel/client.go b/pkg/networkservice/mechanisms/kernel/client.go index 69c18d8a..7e3c78ca 100644 --- a/pkg/networkservice/mechanisms/kernel/client.go +++ b/pkg/networkservice/mechanisms/kernel/client.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Cisco and/or its affiliates. +// Copyright (c) 2020-2022 Cisco and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -30,9 +30,14 @@ import ( ) // NewClient - returns a new Client chain element implementing the kernel mechanism with vpp -func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { +func NewClient(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceClient { + o := &options{} + for _, opt := range opts { + opt(o) + } + if _, err := os.Stat(vnetFilename); err == nil { - return kerneltap.NewClient(vppConn) + return kerneltap.NewClient(vppConn, kerneltap.WithDump(o.dump)) } return kernelvethpair.NewClient(vppConn) } diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/client.go b/pkg/networkservice/mechanisms/kernel/kerneltap/client.go index 3002f3b6..9fe8b793 100644 --- a/pkg/networkservice/mechanisms/kernel/kerneltap/client.go +++ b/pkg/networkservice/mechanisms/kernel/kerneltap/client.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Cisco and/or its affiliates. +// Copyright (c) 2020-2022 Cisco and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -39,7 +39,18 @@ type kernelTapClient struct { } // NewClient - return a new Client chain element implementing the kernel mechanism with vpp using tapv2 -func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { +func NewClient(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceClient { + o := &options{} + for _, opt := range opts { + opt(o) + } + + if o.dump != nil { + if err := o.dump(onDump, false); err != nil { + log.FromContext(context.Background()).Error(err) + } + } + return &kernelTapClient{ vppConn: vppConn, } diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/common.go b/pkg/networkservice/mechanisms/kernel/kerneltap/common.go index 2eb9af18..56eda7aa 100644 --- a/pkg/networkservice/mechanisms/kernel/kerneltap/common.go +++ b/pkg/networkservice/mechanisms/kernel/kerneltap/common.go @@ -147,18 +147,29 @@ func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Conne if !ok { return nil } - now := time.Now() - _, err := tapv2.NewServiceClient(vppConn).TapDeleteV2(ctx, &tapv2.TapDeleteV2{ - SwIfIndex: swIfIndex, - }) - if err != nil { - return errors.Wrapf(err, "unable to delete connection with SwIfIndex %v", swIfIndex) - } - log.FromContext(ctx). - WithField("SwIfIndex", swIfIndex). - WithField("duration", time.Since(now)). - WithField("vppapi", "TapDeleteV2").Debug("completed") - return nil + return delVpp(ctx, vppConn, swIfIndex) + } + return nil +} + +func delVpp(ctx context.Context, vppConn api.Connection, swIfIndex interface_types.InterfaceIndex) error { + now := time.Now() + _, err := tapv2.NewServiceClient(vppConn).TapDeleteV2(ctx, &tapv2.TapDeleteV2{ + SwIfIndex: swIfIndex, + }) + if err != nil { + return errors.Wrapf(err, "unable to delete connection with SwIfIndex %v", swIfIndex) + } + log.FromContext(ctx). + WithField("SwIfIndex", swIfIndex). + WithField("duration", time.Since(now)). + WithField("vppapi", "TapDeleteV2").Debug("completed") + return nil +} + +func onDump(ctx context.Context, vppConn api.Connection, details *interfaces.SwInterfaceDetails) error { + if details.InterfaceDevType == DevTypeTap { + return delVpp(ctx, vppConn, details.SwIfIndex) } return nil } diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/constants.go b/pkg/networkservice/mechanisms/kernel/kerneltap/constants.go index eaeb6183..45439792 100644 --- a/pkg/networkservice/mechanisms/kernel/kerneltap/constants.go +++ b/pkg/networkservice/mechanisms/kernel/kerneltap/constants.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Cisco and/or its affiliates. +// Copyright (c) 2020-2022 Cisco and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,4 +21,7 @@ import "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/kern const ( // MECHANISM string MECHANISM = kernel.MECHANISM + + // DevTypeTap - tap interface dev type + DevTypeTap = "virtio" ) diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/options.go b/pkg/networkservice/mechanisms/kernel/kerneltap/options.go new file mode 100644 index 00000000..fdb9259a --- /dev/null +++ b/pkg/networkservice/mechanisms/kernel/kerneltap/options.go @@ -0,0 +1,35 @@ +// 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. + +// +build linux + +package kerneltap + +import "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + +type options struct { + dump dumptool.DumpNSMFn +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump dumptool.DumpNSMFn) Option { + return func(o *options) { + o.dump = dump + } +} diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/server.go b/pkg/networkservice/mechanisms/kernel/kerneltap/server.go index 9436bbf8..3f234a40 100644 --- a/pkg/networkservice/mechanisms/kernel/kerneltap/server.go +++ b/pkg/networkservice/mechanisms/kernel/kerneltap/server.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Cisco and/or its affiliates. +// Copyright (c) 2020-2022 Cisco and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -37,7 +37,18 @@ type kernelTapServer struct { } // NewServer - return a new Server chain element implementing the kernel mechanism with vpp using tapv2 -func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer { +func NewServer(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceServer { + o := &options{} + for _, opt := range opts { + opt(o) + } + + if o.dump != nil { + if err := o.dump(onDump, true); err != nil { + log.FromContext(context.Background()).Error(err) + } + } + return &kernelTapServer{ vppConn: vppConn, } diff --git a/pkg/networkservice/mechanisms/kernel/options.go b/pkg/networkservice/mechanisms/kernel/options.go new file mode 100644 index 00000000..2504ec2a --- /dev/null +++ b/pkg/networkservice/mechanisms/kernel/options.go @@ -0,0 +1,35 @@ +// 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. + +// +build linux + +package kernel + +import "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + +type options struct { + dump dumptool.DumpNSMFn +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump dumptool.DumpNSMFn) Option { + return func(o *options) { + o.dump = dump + } +} diff --git a/pkg/networkservice/mechanisms/kernel/server.go b/pkg/networkservice/mechanisms/kernel/server.go index 1f967f5a..465e244d 100644 --- a/pkg/networkservice/mechanisms/kernel/server.go +++ b/pkg/networkservice/mechanisms/kernel/server.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Cisco and/or its affiliates. +// Copyright (c) 2020-2022 Cisco and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -30,9 +30,14 @@ import ( ) // NewServer return a NetworkServiceServer chain element that correctly handles the kernel Mechanism -func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer { +func NewServer(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceServer { + o := &options{} + for _, opt := range opts { + opt(o) + } + if _, err := os.Stat(vnetFilename); err == nil { - return kerneltap.NewServer(vppConn) + return kerneltap.NewServer(vppConn, kerneltap.WithDump(o.dump)) } return kernelvethpair.NewServer(vppConn) } diff --git a/pkg/networkservice/tag/common.go b/pkg/networkservice/tag/common.go index 6b22d198..2fd34f54 100644 --- a/pkg/networkservice/tag/common.go +++ b/pkg/networkservice/tag/common.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" "github.com/networkservicemesh/sdk-vpp/pkg/tools/ifindex" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/tagtool" ) func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { @@ -36,16 +37,21 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co } now := time.Now() + tag := tagtool.ConvertToString(&tagtool.Tag{ + TagPrefix: conn.Path.PathSegments[conn.Path.Index].Name, + ConnID: conn.GetId(), + IsClient: isClient, + }) if _, err := interfaces.NewServiceClient(vppConn).SwInterfaceTagAddDel(ctx, &interfaces.SwInterfaceTagAddDel{ IsAdd: true, SwIfIndex: swIfIndex, - Tag: conn.GetId(), + Tag: tag, }); err != nil { return errors.WithStack(err) } log.FromContext(ctx). WithField("swIfIndex", swIfIndex). - WithField("tag", conn.GetId()). + WithField("tag", tag). WithField("duration", time.Since(now)). WithField("vppapi", "SwInterfaceTagAddDel").Debug("completed") return nil diff --git a/pkg/tools/dumptool/dumptool.go b/pkg/tools/dumptool/dumptool.go new file mode 100644 index 00000000..8e4793d0 --- /dev/null +++ b/pkg/tools/dumptool/dumptool.go @@ -0,0 +1,66 @@ +// 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 dumptool provides utilities for vpp interfaces dump +package dumptool + +import ( + "context" + "io" + + "git.fd.io/govpp.git/api" + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/networkservicemesh/sdk/pkg/tools/log" + "github.com/pkg/errors" + + "github.com/networkservicemesh/sdk-vpp/pkg/tools/tagtool" +) + +// DumpNSMFn - dumps NSM interfaces applies onDump func for each of them +type DumpNSMFn func(onDump OnDumpFn, isClient bool) error + +// OnDumpFn - action with dumped NSM interfaces +type OnDumpFn func(ctx context.Context, vppConn api.Connection, details *interfaces.SwInterfaceDetails) error + +// DumpVppInterfaces - dumps vpp interfaces by tag. +// - onDump - determines what to do if we found an NSM interface during the dump +func DumpVppInterfaces(ctx context.Context, vppConn api.Connection, tagPrefix string, isClient bool, onDump OnDumpFn) error { + client, err := interfaces.NewServiceClient(vppConn).SwInterfaceDump(ctx, &interfaces.SwInterfaceDump{}) + if err != nil { + return errors.Wrap(err, "SwInterfaceDump error") + } + defer func() { _ = client.Close() }() + + for { + details, err := client.Recv() + if err == io.EOF || details == nil { + break + } + + t, err := tagtool.ConvertFromString(details.Tag) + if err != nil { + continue + } + if t.TagPrefix != tagPrefix || t.IsClient != isClient { + continue + } + + if err := onDump(ctx, vppConn, details); err != nil { + log.FromContext(ctx).Error(err) + } + } + return nil +} diff --git a/pkg/tools/tagtool/tagtool.go b/pkg/tools/tagtool/tagtool.go new file mode 100644 index 00000000..7199b410 --- /dev/null +++ b/pkg/tools/tagtool/tagtool.go @@ -0,0 +1,71 @@ +// 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 tagtool provides some utilities for converting NSM interface tag +package tagtool + +import ( + "strings" + + "github.com/pkg/errors" +) + +const ( + clientIdentifier = "c" + serverIdentifier = "s" + delimiter = "_" +) + +// Tag - represent an NSM tag +type Tag struct { + TagPrefix string + ConnID string + IsClient bool +} + +// ConvertToString - converts Tag to the string format: { forwarder-name }_{ identifier }_{ connID } +func ConvertToString(t *Tag) string { + isClientStr := clientIdentifier + if !t.IsClient { + isClientStr = serverIdentifier + } + return t.TagPrefix + delimiter + isClientStr + delimiter + t.ConnID +} + +// ConvertFromString - converts from string to Tag +func ConvertFromString(tag string) (t *Tag, err error) { + if tag == "" { + return nil, errors.New("tag is empty") + } + substrs := strings.Split(tag, delimiter) + if len(substrs) != 3 { + return nil, errors.New("tag part count mismatch") + } + + t = &Tag{ + TagPrefix: substrs[0], + ConnID: substrs[2], + } + switch substrs[1] { + case clientIdentifier: + t.IsClient = true + case serverIdentifier: + t.IsClient = false + default: + return nil, errors.New("identifier mismatch") + } + return t, nil +}