diff --git a/pkg/networkservice/chains/forwarder/options.go b/pkg/networkservice/chains/forwarder/options.go index f1bbd175..df4b0805 100644 --- a/pkg/networkservice/chains/forwarder/options.go +++ b/pkg/networkservice/chains/forwarder/options.go @@ -36,6 +36,7 @@ type forwarderOptions struct { authorizeServer networkservice.NetworkServiceServer clientURL *url.URL dialTimeout time.Duration + dumpCleanTimeou time.Duration domain2Device map[string]string statsOpts []stats.Option cleanupOpts []cleanup.Option @@ -77,6 +78,13 @@ func WithDialTimeout(dialTimeout time.Duration) Option { } } +// WithDumpCleanupTimeout sets +func WithDumpCleanupTimeout(timeout time.Duration) Option { + return func(o *forwarderOptions) { + o.dumpCleanTimeou = timeout + } +} + // WithVlanDomain2Device sets vlan option func WithVlanDomain2Device(domain2Device map[string]string) Option { return func(o *forwarderOptions) { diff --git a/pkg/networkservice/chains/forwarder/server.go b/pkg/networkservice/chains/forwarder/server.go index 71f7c7fe..2d5c2672 100644 --- a/pkg/networkservice/chains/forwarder/server.go +++ b/pkg/networkservice/chains/forwarder/server.go @@ -26,6 +26,8 @@ import ( "net/url" "time" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "git.fd.io/govpp.git/api" "github.com/google/uuid" @@ -81,11 +83,18 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, vppConn authorizeServer: authorize.NewServer(authorize.Any()), clientURL: &url.URL{Scheme: "unix", Host: "connect.to.socket"}, dialTimeout: time.Millisecond * 200, + dumpCleanTimeou: time.Minute, domain2Device: make(map[string]string), } for _, opt := range options { opt(opts) } + + dumpOption := &dumptool.DumpOption{ + PodName: opts.name, + Timeout: opts.dumpCleanTimeou, + } + nseClient := registryclient.NewNetworkServiceEndpointRegistryClient(ctx, opts.clientURL, registryclient.WithNSEAdditionalFunctionality( registryrecvfd.NewNetworkServiceEndpointRegistryClient(), @@ -111,9 +120,10 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, vppConn mechanisms.NewServer(map[string]networkservice.NetworkServiceServer{ memif.MECHANISM: memif.NewServer(ctx, vppConn, memif.WithDirectMemif(), - memif.WithChangeNetNS()), - kernel.MECHANISM: kernel.NewServer(vppConn), - vxlan.MECHANISM: vxlan.NewServer(vppConn, tunnelIP, opts.vxlanOpts...), + memif.WithChangeNetNS(), + memif.WithDump(dumpOption)), + kernel.MECHANISM: kernel.NewServer(vppConn, kernel.WithDump(dumpOption)), + vxlan.MECHANISM: vxlan.NewServer(vppConn, tunnelIP, append(opts.vxlanOpts, vxlan.WithDump(dumpOption))...), wireguard.MECHANISM: wireguard.NewServer(vppConn, tunnelIP), }), pinhole.NewServer(vppConn), @@ -134,9 +144,10 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, vppConn // mechanisms memif.NewClient(vppConn, memif.WithChangeNetNS(), + memif.WithDump(dumpOption), ), - kernel.NewClient(vppConn), - vxlan.NewClient(vppConn, tunnelIP, opts.vxlanOpts...), + kernel.NewClient(vppConn, kernel.WithDump(dumpOption)), + vxlan.NewClient(vppConn, tunnelIP, append(opts.vxlanOpts, vxlan.WithDump(dumpOption))...), wireguard.NewClient(vppConn, tunnelIP), vlan.NewClient(vppConn, opts.domain2Device), filtermechanisms.NewClient(), diff --git a/pkg/networkservice/mechanisms/kernel/client.go b/pkg/networkservice/mechanisms/kernel/client.go index 69c18d8a..f4685d58 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 // @@ -19,20 +19,21 @@ package kernel import ( - "os" - "git.fd.io/govpp.git/api" "github.com/networkservicemesh/api/pkg/api/networkservice" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel/kernelvethpair" - - "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel/kerneltap" ) // NewClient - returns a new Client chain element implementing the kernel mechanism with vpp -func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { - if _, err := os.Stat(vnetFilename); err == nil { - return kerneltap.NewClient(vppConn) +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, kerneltap.WithDump(o.dumpOpt)) + //} return kernelvethpair.NewClient(vppConn) } diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/client.go b/pkg/networkservice/mechanisms/kernel/kerneltap/client.go index 3002f3b6..3df8e144 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 // @@ -28,6 +28,7 @@ import ( "github.com/networkservicemesh/api/pkg/api/networkservice" "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" "github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata" "github.com/networkservicemesh/sdk/pkg/tools/log" @@ -36,12 +37,30 @@ import ( type kernelTapClient struct { vppConn api.Connection + dumpMap *dumptool.Map } // 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) + } + + ctx := context.Background() + dumpMap := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + var err error + dumpMap, err = dump(ctx, vppConn, o.dumpOpt.PodName, o.dumpOpt.Timeout, true) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return &kernelTapClient{ vppConn: vppConn, + dumpMap: dumpMap, } } @@ -60,7 +79,7 @@ func (k *kernelTapClient) Request(ctx context.Context, request *networkservice.N return nil, err } - if err := create(ctx, conn, k.vppConn, metadata.IsClient(k)); err != nil { + if err := create(ctx, conn, k.vppConn, k.dumpMap, metadata.IsClient(k)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -75,7 +94,7 @@ func (k *kernelTapClient) Request(ctx context.Context, request *networkservice.N } func (k *kernelTapClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { - err := del(ctx, conn, k.vppConn, metadata.IsClient(k)) + err := del(ctx, conn, k.vppConn, k.dumpMap, metadata.IsClient(k)) if err != nil { log.FromContext(ctx).Error(err) } diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/common.go b/pkg/networkservice/mechanisms/kernel/kerneltap/common.go index 2eb9af18..789e6a3f 100644 --- a/pkg/networkservice/mechanisms/kernel/kerneltap/common.go +++ b/pkg/networkservice/mechanisms/kernel/kerneltap/common.go @@ -22,6 +22,8 @@ import ( "context" "time" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "git.fd.io/govpp.git/api" interfaces "github.com/edwarnicke/govpp/binapi/interface" "github.com/edwarnicke/govpp/binapi/interface_types" @@ -38,8 +40,11 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/tools/mechutils" ) -func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { +func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isClient bool) error { if mechanism := kernel.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } // Construct the netlink handle for the target namespace for this kernel interface handle, err := kernellink.GetNetlinkHandle(mechanism.GetNetNSURL()) if err != nil { @@ -53,7 +58,7 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co } } // Delete the kernel interface if there is one in the target namespace - _ = del(ctx, conn, vppConn, isClient) + _ = del(ctx, conn, vppConn, dumpMap, isClient) nsFilename, err := mechutils.ToNSFilename(mechanism) if err != nil { @@ -141,8 +146,11 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co return nil } -func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { +func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isClient bool) error { if mechanism := kernel.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } swIfIndex, ok := ifindex.LoadAndDelete(ctx, isClient) if !ok { return nil @@ -162,3 +170,21 @@ func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Conne } return nil } + +func dump(ctx context.Context, vppConn api.Connection, podName string, timeout time.Duration, isClient bool) (*dumptool.Map, error) { + return dumptool.DumpInterfaces(ctx, vppConn, podName, timeout, isClient, + /* Function on dump */ + func(details *interfaces.SwInterfaceDetails) (interface{}, error) { + if details.InterfaceDevType == dumptool.DevTypeTap { + return details.SwIfIndex, nil + } + return nil, errors.New("Doesn't match the tap interface") + }, + /* Function on delete */ + func(ifindex interface{}) error { + _, err := tapv2.NewServiceClient(vppConn).TapDeleteV2(ctx, &tapv2.TapDeleteV2{ + SwIfIndex: ifindex.(interface_types.InterfaceIndex), + }) + return err + }) +} diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/options.go b/pkg/networkservice/mechanisms/kernel/kerneltap/options.go new file mode 100644 index 00000000..92c4a855 --- /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 { + dumpOpt *dumptool.DumpOption +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *options) { + o.dumpOpt = dump + } +} diff --git a/pkg/networkservice/mechanisms/kernel/kerneltap/server.go b/pkg/networkservice/mechanisms/kernel/kerneltap/server.go index 9436bbf8..bcd9754c 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 // @@ -23,6 +23,7 @@ import ( "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "github.com/pkg/errors" "github.com/networkservicemesh/api/pkg/api/networkservice" @@ -34,12 +35,30 @@ import ( type kernelTapServer struct { vppConn api.Connection + dumpMap *dumptool.Map } // 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) + } + + ctx := context.Background() + dumpMap := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + var err error + dumpMap, err = dump(ctx, vppConn, o.dumpOpt.PodName, o.dumpOpt.Timeout, false) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return &kernelTapServer{ vppConn: vppConn, + dumpMap: dumpMap, } } @@ -51,7 +70,7 @@ func (k *kernelTapServer) Request(ctx context.Context, request *networkservice.N return nil, err } - if err := create(ctx, conn, k.vppConn, metadata.IsClient(k)); err != nil { + if err := create(ctx, conn, k.vppConn, k.dumpMap, metadata.IsClient(k)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -66,7 +85,7 @@ func (k *kernelTapServer) Request(ctx context.Context, request *networkservice.N } func (k *kernelTapServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { - err := del(ctx, conn, k.vppConn, metadata.IsClient(k)) + err := del(ctx, conn, k.vppConn, k.dumpMap, metadata.IsClient(k)) if err != nil { log.FromContext(ctx).Error(err) } diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/client.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/client.go index cc737797..bb6c0bb3 100644 --- a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/client.go +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/client.go @@ -20,6 +20,8 @@ package afpacket import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "github.com/networkservicemesh/sdk/pkg/tools/log" "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" @@ -34,12 +36,30 @@ import ( type afPacketClient struct { vppConn api.Connection + dumpMap *dumptool.Map } // NewClient - return a new Server chain element implementing the kernel mechanism with vpp using afpacket -func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { +func NewClient(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceClient { + o := &options{} + for _, opt := range opts { + opt(o) + } + + ctx := context.Background() + dumpMap := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + var err error + dumpMap, err = dump(ctx, vppConn, o.dumpOpt.PodName, o.dumpOpt.Timeout, true) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return &afPacketClient{ vppConn: vppConn, + dumpMap: dumpMap, } } @@ -51,7 +71,7 @@ func (a *afPacketClient) Request(ctx context.Context, request *networkservice.Ne return nil, err } - if err := create(ctx, conn, a.vppConn, metadata.IsClient(a)); err != nil { + if err := create(ctx, conn, a.vppConn, a.dumpMap, metadata.IsClient(a)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -66,6 +86,6 @@ func (a *afPacketClient) Request(ctx context.Context, request *networkservice.Ne } func (a *afPacketClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { - _ = del(ctx, conn, a.vppConn, metadata.IsClient(a)) + _ = del(ctx, conn, a.vppConn, a.dumpMap, metadata.IsClient(a)) return next.Client(ctx).Close(ctx, conn, opts...) } diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/common.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/common.go index 1e9ea661..00f0b12d 100644 --- a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/common.go +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/common.go @@ -20,6 +20,9 @@ package afpacket import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "github.com/vishvananda/netlink" + "io" "time" "git.fd.io/govpp.git/api" @@ -38,8 +41,12 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/tools/types" ) -func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { +func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isClient bool) error { if mechanism := kernel.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } + if _, ok := ifindex.Load(ctx, isClient); ok { return nil } @@ -78,8 +85,12 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co return nil } -func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { +func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isClient bool) error { if mechanism := kernel.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } + swIfIndex, ok := ifindex.LoadAndDelete(ctx, isClient) if !ok { return nil @@ -103,3 +114,41 @@ func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Conne } return nil } + +func dump(ctx context.Context, vppConn api.Connection, podName string, timeout time.Duration, isClient bool) (*dumptool.Map, error) { + return dumptool.DumpInterfaces(ctx, vppConn, podName, timeout, isClient, + /* Function on dump */ + func(details *interfaces.SwInterfaceDetails) (interface{}, error) { + if details.InterfaceDevType == dumptool.DevTypeAfPacket { + return details.SwIfIndex, nil + } + return nil, errors.New("Doesn't match the af_packet interface") + }, + /* Function on delete */ + func(val interface{}) error { + swIfIndex := val.(interface_types.InterfaceIndex) + afClient, err := af_packet.NewServiceClient(vppConn).AfPacketDump(ctx, &af_packet.AfPacketDump{}) + if err != nil { + return err + } + defer func() { _ = afClient.Close() }() + + for { + afDetails, err := afClient.Recv() + if err == io.EOF { + break + } + if afDetails == nil || afDetails.SwIfIndex != swIfIndex { + continue + } + + if _, err := af_packet.NewServiceClient(vppConn).AfPacketDelete(ctx, &af_packet.AfPacketDelete{ + HostIfName: afDetails.HostIfName, + }); err != nil { + return err + } + return nil + } + return netlink.LinkDel(val.(netlink.Link)) + }) +} diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/options.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/options.go new file mode 100644 index 00000000..df837ea3 --- /dev/null +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/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 afpacket + +import "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + +type options struct { + dumpOpt *dumptool.DumpOption +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *options) { + o.dumpOpt = dump + } +} diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/server.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/server.go index 7b7c9ce2..195c1eaa 100644 --- a/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/server.go +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/afpacket/server.go @@ -20,6 +20,8 @@ package afpacket import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "github.com/networkservicemesh/sdk/pkg/tools/log" "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" @@ -33,12 +35,30 @@ import ( type afPacketServer struct { vppConn api.Connection + dumpMap *dumptool.Map } // NewServer - return a new Server chain element implementing the kernel mechanism with vpp using afpacket -func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer { +func NewServer(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceServer { + o := &options{} + for _, opt := range opts { + opt(o) + } + + ctx := context.Background() + dumpMap := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + var err error + dumpMap, err = dump(ctx, vppConn, o.dumpOpt.PodName, o.dumpOpt.Timeout, true) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return &afPacketServer{ vppConn: vppConn, + dumpMap: dumpMap, } } @@ -50,7 +70,7 @@ func (a *afPacketServer) Request(ctx context.Context, request *networkservice.Ne return nil, err } - if err := create(ctx, conn, a.vppConn, metadata.IsClient(a)); err != nil { + if err := create(ctx, conn, a.vppConn, a.dumpMap, metadata.IsClient(a)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -65,6 +85,6 @@ func (a *afPacketServer) Request(ctx context.Context, request *networkservice.Ne } func (a *afPacketServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { - _ = del(ctx, conn, a.vppConn, false) + _ = del(ctx, conn, a.vppConn, a.dumpMap,false) return next.Server(ctx).Close(ctx, conn) } diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/client.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/client.go index 8fc3271d..dfeee0d4 100644 --- a/pkg/networkservice/mechanisms/kernel/kernelvethpair/client.go +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/client.go @@ -20,6 +20,7 @@ package kernelvethpair import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" @@ -39,15 +40,34 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel/kernelvethpair/ipneighbor" ) -type kernelVethPairClient struct{} +type kernelVethPairClient struct{ + podName string + dumpMap *dumptool.Map +} // NewClient - return a new Client chain element implementing the kernel mechanism with vpp using a veth pair -func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { +func NewClient(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceClient { + o := &options{} + for _, opt := range opts { + opt(o) + } + + ctx := context.Background() + podName := "forwarder" + dumpMap := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + podName = o.dumpOpt.PodName + dumpMap, _ = dump(ctx, o.dumpOpt.PodName, o.dumpOpt.Timeout, true) + } + return chain.NewNetworkServiceClient( ipneighbor.NewClient(vppConn), afpacket.NewClient(vppConn), mtu.NewClient(), - &kernelVethPairClient{}, + &kernelVethPairClient{ + podName: podName, + dumpMap: dumpMap, + }, ) } @@ -66,7 +86,7 @@ func (k *kernelVethPairClient) Request(ctx context.Context, request *networkserv return nil, err } - if err := create(ctx, conn, metadata.IsClient(k)); err != nil { + if err := create(ctx, conn, k.podName, k.dumpMap, metadata.IsClient(k)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/common.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/common.go index bf33ed27..3f72d52b 100644 --- a/pkg/networkservice/mechanisms/kernel/kernelvethpair/common.go +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/common.go @@ -20,7 +20,7 @@ package kernelvethpair import ( "context" - "fmt" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "time" "github.com/pkg/errors" @@ -38,11 +38,13 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/tools/ethtool" "github.com/networkservicemesh/sdk-vpp/pkg/tools/ifindex" "github.com/networkservicemesh/sdk-vpp/pkg/tools/link" - "github.com/networkservicemesh/sdk-vpp/pkg/tools/mechutils" ) -func create(ctx context.Context, conn *networkservice.Connection, isClient bool) error { +func create(ctx context.Context, conn *networkservice.Connection, podName string, dumpMap *dumptool.Map, isClient bool) error { if mechanism := kernel.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + peer.Store(ctx, isClient, val.(netlink.Link)) + } // Construct the netlink handle for the target namespace for this kernel interface handle, err := kernellink.GetNetlinkHandle(mechanism.GetNetNSURL()) if err != nil { @@ -50,7 +52,7 @@ func create(ctx context.Context, conn *networkservice.Connection, isClient bool) } defer handle.Close() - if _, ok := link.Load(ctx, isClient); ok { + if _, ok := peer.Load(ctx, isClient); ok { if _, err = handle.LinkByName(mechanism.GetInterfaceName()); err == nil { return nil } @@ -70,7 +72,11 @@ func create(ctx context.Context, conn *networkservice.Connection, isClient bool) } ifindex.Delete(ctx, isClient) - alias := mechutils.ToAlias(conn, isClient) + alias := dumptool.ConvertToTag(&dumptool.TagStruct{ + PodName: podName, + ConnID: conn.Id, + IsClient: isClient, + }) la := netlink.NewLinkAttrs() la.Name = randstr.Hex(7) @@ -186,14 +192,14 @@ func create(ctx context.Context, conn *networkservice.Connection, isClient bool) // Set Alias of peerLink now = time.Now() - if err = netlink.LinkSetAlias(peerLink, fmt.Sprintf("veth-%s", alias)); err != nil { + if err = netlink.LinkSetAlias(peerLink, alias); err != nil { _ = netlink.LinkDel(l) _ = netlink.LinkDel(peerLink) return err } log.FromContext(ctx). WithField("link.Name", peerLink.Attrs().Name). - WithField("peerLink", fmt.Sprintf("veth-%s", alias)). + WithField("peerLink", alias). WithField("duration", time.Since(now)). WithField("netlink", "LinkSetAlias").Debug("completed") @@ -235,6 +241,26 @@ func del(ctx context.Context, conn *networkservice.Connection, isClient bool) er return nil } +func dump(ctx context.Context, podName string, timeout time.Duration, isClient bool) (*dumptool.Map, error) { + peerLinks, err := netlink.LinkList() + if err != nil { + return nil, err + } + dumpMap := dumptool.NewMap(ctx, timeout) + for _, peerLink := range peerLinks { + if peerLink.Type() == "veth" { + t, _ := dumptool.ConvertFromTag(peerLink.Attrs().Alias) + if t.IsClient != isClient || t.PodName != podName { + continue + } + dumpMap.Store(t.ConnID, peerLink, func(value interface{}) error { + return netlink.LinkDel(peerLink) + }) + } + } + return dumpMap, nil +} + func linuxIfaceName(ifaceName string) string { if len(ifaceName) <= kernel.LinuxIfMaxLength { return ifaceName diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/options.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/options.go new file mode 100644 index 00000000..144015f9 --- /dev/null +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/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 kernelvethpair + +import "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + +type options struct { + dumpOpt *dumptool.DumpOption +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *options) { + o.dumpOpt = dump + } +} diff --git a/pkg/networkservice/mechanisms/kernel/kernelvethpair/server.go b/pkg/networkservice/mechanisms/kernel/kernelvethpair/server.go index 1247497e..b8e6f2eb 100644 --- a/pkg/networkservice/mechanisms/kernel/kernelvethpair/server.go +++ b/pkg/networkservice/mechanisms/kernel/kernelvethpair/server.go @@ -20,6 +20,7 @@ package kernelvethpair import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" @@ -37,15 +38,34 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel/kernelvethpair/ipneighbor" ) -type kernelVethPairServer struct{} +type kernelVethPairServer struct{ + podName string + dumpMap *dumptool.Map +} // NewServer - return a new Server chain element implementing the kernel mechanism with vpp using a veth pair -func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer { +func NewServer(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceServer { + o := &options{} + for _, opt := range opts { + opt(o) + } + + ctx := context.Background() + podName := "forwarder" + dumpMap := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + podName = o.dumpOpt.PodName + dumpMap, _ = dump(ctx, o.dumpOpt.PodName, o.dumpOpt.Timeout, false) + } + return chain.NewNetworkServiceServer( ipneighbor.NewServer(vppConn), afpacket.NewServer(vppConn), mtu.NewServer(), - &kernelVethPairServer{}, + &kernelVethPairServer{ + podName: podName, + dumpMap: dumpMap, + }, ) } @@ -57,7 +77,7 @@ func (k *kernelVethPairServer) Request(ctx context.Context, request *networkserv return nil, err } - if err := create(ctx, request.GetConnection(), false); err != nil { + if err := create(ctx, request.GetConnection(), k.podName, k.dumpMap, false); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() diff --git a/pkg/networkservice/mechanisms/kernel/options.go b/pkg/networkservice/mechanisms/kernel/options.go new file mode 100644 index 00000000..39231dfa --- /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 { + dumpOpt *dumptool.DumpOption +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *options) { + o.dumpOpt = dump + } +} diff --git a/pkg/networkservice/mechanisms/kernel/server.go b/pkg/networkservice/mechanisms/kernel/server.go index 1f967f5a..538c884a 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 // @@ -19,20 +19,21 @@ package kernel import ( - "os" - "git.fd.io/govpp.git/api" "github.com/networkservicemesh/api/pkg/api/networkservice" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel/kernelvethpair" - - "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel/kerneltap" ) // NewServer return a NetworkServiceServer chain element that correctly handles the kernel Mechanism -func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer { - if _, err := os.Stat(vnetFilename); err == nil { - return kerneltap.NewServer(vppConn) +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, kerneltap.WithDump(o.dumpOpt)) + //} return kernelvethpair.NewServer(vppConn) } diff --git a/pkg/networkservice/mechanisms/memif/client.go b/pkg/networkservice/mechanisms/memif/client.go index 9a3c6ddf..a97eb27e 100644 --- a/pkg/networkservice/mechanisms/memif/client.go +++ b/pkg/networkservice/mechanisms/memif/client.go @@ -24,8 +24,11 @@ import ( "context" "net/url" + "github.com/networkservicemesh/sdk/pkg/tools/log" + "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "github.com/pkg/errors" "google.golang.org/grpc" @@ -41,8 +44,10 @@ import ( type memifClient struct { vppConn api.Connection - changeNetNs bool + changeNetNS bool nsInfo NetNSInfo + + dumpMap *dumptool.Map } // NewClient provides a NetworkServiceClient chain elements that support the memif Mechanism @@ -52,11 +57,23 @@ func NewClient(vppConn api.Connection, options ...Option) networkservice.Network o(opts) } + chainCtx := context.Background() + dumpMap := dumptool.NewMap(chainCtx, 0) + if opts.dumpOpt != nil { + var err error + dumpMap, err = dump(chainCtx, vppConn, opts.dumpOpt.PodName, opts.dumpOpt.Timeout, true) + if err != nil { + log.FromContext(chainCtx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return chain.NewNetworkServiceClient( &memifClient{ vppConn: vppConn, - changeNetNs: opts.changeNetNS, + changeNetNS: opts.changeNetNS, nsInfo: newNetNSInfo(), + dumpMap: dumpMap, }, ) } @@ -64,7 +81,7 @@ func NewClient(vppConn api.Connection, options ...Option) networkservice.Network func (m *memifClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { if !m.updateMechanismPreferences(request) { mechanism := memif.ToMechanism(memif.NewAbstract(m.nsInfo.netNSPath)) - if m.changeNetNs { + if m.changeNetNS { mechanism.SetNetNSURL("") } request.MechanismPreferences = append(request.MechanismPreferences, mechanism.Mechanism) @@ -86,7 +103,7 @@ func (m *memifClient) Request(ctx context.Context, request *networkservice.Netwo } } - if err = create(ctx, conn, m.vppConn, metadata.IsClient(m), m.nsInfo.netNS); err != nil { + if err = create(ctx, conn, m.vppConn, m.dumpMap, metadata.IsClient(m), m.nsInfo.netNS); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -101,7 +118,7 @@ func (m *memifClient) Request(ctx context.Context, request *networkservice.Netwo } func (m *memifClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { - _ = del(ctx, conn, m.vppConn, metadata.IsClient(m)) + _ = del(ctx, conn, m.vppConn, m.dumpMap, metadata.IsClient(m)) return next.Client(ctx).Close(ctx, conn, opts...) } @@ -111,7 +128,7 @@ func (m *memifClient) updateMechanismPreferences(request *networkservice.Network for _, p := range request.GetRequestMechanismPreferences() { if mechanism := memif.ToMechanism(p); mechanism != nil { mechanism.SetNetNSURL((&url.URL{Scheme: memif.FileScheme, Path: m.nsInfo.netNSPath}).String()) - if m.changeNetNs { + if m.changeNetNS { mechanism.SetNetNSURL("") } updated = true diff --git a/pkg/networkservice/mechanisms/memif/client_test.go b/pkg/networkservice/mechanisms/memif/client_test.go index a2c52d81..481bd234 100644 --- a/pkg/networkservice/mechanisms/memif/client_test.go +++ b/pkg/networkservice/mechanisms/memif/client_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Doc.ai and/or its affiliates. +// Copyright (c) 2021-2022 Doc.ai and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -33,7 +33,7 @@ import ( ) func Test_MemifClient_ShouldAppendMechanismIfMemifMechanismMissed(t *testing.T) { - c := chain.NewNetworkServiceClient(metadata.NewClient(), memif.NewClient(nil)) + c := chain.NewNetworkServiceClient(metadata.NewClient(), memif.NewClient(context.Background(), nil)) req := &networkservice.NetworkServiceRequest{ MechanismPreferences: []*networkservice.Mechanism{}, @@ -55,7 +55,7 @@ func Test_MemifClient_ShouldAppendMechanismIfMemifMechanismMissed(t *testing.T) } func Test_MemifClient_ShouldNotDuplicateMechanisms(t *testing.T) { - c := chain.NewNetworkServiceClient(metadata.NewClient(), memif.NewClient(nil)) + c := chain.NewNetworkServiceClient(metadata.NewClient(), memif.NewClient(context.Background(), nil)) req := &networkservice.NetworkServiceRequest{ MechanismPreferences: []*networkservice.Mechanism{ diff --git a/pkg/networkservice/mechanisms/memif/common.go b/pkg/networkservice/mechanisms/memif/common.go index 3fac5276..8c419e0a 100644 --- a/pkg/networkservice/mechanisms/memif/common.go +++ b/pkg/networkservice/mechanisms/memif/common.go @@ -21,12 +21,15 @@ package memif import ( "context" "fmt" + "io" "net/url" "os" "path/filepath" "runtime" "time" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "git.fd.io/govpp.git/api" interfaces "github.com/edwarnicke/govpp/binapi/interface" "github.com/edwarnicke/govpp/binapi/interface_types" @@ -189,8 +192,12 @@ func deleteMemif(ctx context.Context, vppConn api.Connection, isClient bool) err return nil } -func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool, netNS netns.NsHandle) error { +func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isClient bool, netNS netns.NsHandle) error { if mechanism := memifMech.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } + if !isClient { mechanism.SetSocketFilename(socketFile(conn)) } @@ -204,7 +211,7 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co return nil } } - _ = del(ctx, conn, vppConn, isClient) + _ = del(ctx, conn, vppConn, dumpMap, isClient) mode := memif.MEMIF_MODE_API_IP if conn.GetPayload() == payload.Ethernet { @@ -221,8 +228,12 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co return nil } -func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { +func del(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isClient bool) error { if mechanism := memifMech.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } + if err := deleteMemif(ctx, vppConn, isClient); err != nil { return err } @@ -257,3 +268,69 @@ func getVppSocketFilename(mechanism *memifMech.Mechanism, netNS netns.NsHandle) } return mechanism.GetSocketFilename(), nil } + +func dump(ctx context.Context, vppConn api.Connection, podName string, timeout time.Duration, isClient bool) (*dumptool.Map, error) { + return dumptool.DumpInterfaces(ctx, vppConn, podName, timeout, isClient, + /* Function on dump */ + func(details *interfaces.SwInterfaceDetails) (interface{}, error) { + if details.InterfaceDevType == dumptool.DevTypeMemif { + return details.SwIfIndex, nil + } + return nil, errors.New("Doesn't match the memif interface") + }, + /* Function on delete */ + func(ifindex interface{}) error { + /* Find memif interface to determine its SocketID */ + memClient, err := memif.NewServiceClient(vppConn).MemifDump(ctx, &memif.MemifDump{}) + if err != nil { + return err + } + defer func() { _ = memClient.Close() }() + for { + var memDetails *memif.MemifDetails + memDetails, err = memClient.Recv() + if err == io.EOF { + break + } + if memDetails == nil || memDetails.SwIfIndex != ifindex { + continue + } + + _, err = memif.NewServiceClient(vppConn).MemifDelete(ctx, &memif.MemifDelete{ + SwIfIndex: ifindex.(interface_types.InterfaceIndex), + }) + if err != nil { + return err + } + + /* Find memifSocketFilename by SocketID */ + var memSockClient memif.RPCService_MemifSocketFilenameDumpClient + memSockClient, err = memif.NewServiceClient(vppConn).MemifSocketFilenameDump(ctx, &memif.MemifSocketFilenameDump{}) + if err != nil { + return err + } + defer func() { _ = memSockClient.Close() }() + for { + var memSockDetails *memif.MemifSocketFilenameDetails + memSockDetails, err = memSockClient.Recv() + if err == io.EOF { + break + } + if memSockDetails == nil || memSockDetails.SocketID != memDetails.SocketID { + continue + } + _, err = memif.NewServiceClient(vppConn).MemifSocketFilenameAddDelV2(ctx, &memif.MemifSocketFilenameAddDelV2{ + IsAdd: false, + SocketID: memSockDetails.SocketID, + SocketFilename: memSockDetails.SocketFilename, + }) + if err != nil { + return err + } + break + } + break + } + return err + }) +} diff --git a/pkg/networkservice/mechanisms/memif/option.go b/pkg/networkservice/mechanisms/memif/option.go index 30c5e8d1..4c697d75 100644 --- a/pkg/networkservice/mechanisms/memif/option.go +++ b/pkg/networkservice/mechanisms/memif/option.go @@ -16,9 +16,12 @@ package memif +import "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + type memifOptions struct { directMemifEnabled bool changeNetNS bool + dumpOpt *dumptool.DumpOption } // Option is an option for the connect server @@ -37,3 +40,10 @@ func WithChangeNetNS() Option { o.changeNetNS = true } } + +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *memifOptions) { + o.dumpOpt = dump + } +} diff --git a/pkg/networkservice/mechanisms/memif/server.go b/pkg/networkservice/mechanisms/memif/server.go index 0f015dc5..7947a2d8 100644 --- a/pkg/networkservice/mechanisms/memif/server.go +++ b/pkg/networkservice/mechanisms/memif/server.go @@ -22,8 +22,11 @@ import ( "context" "net/url" + "github.com/networkservicemesh/sdk/pkg/tools/log" + "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "github.com/pkg/errors" "github.com/networkservicemesh/api/pkg/api/networkservice" @@ -41,6 +44,8 @@ type memifServer struct { vppConn api.Connection changeNetNS bool nsInfo NetNSInfo + + dumpMap *dumptool.Map } // NewServer provides a NetworkServiceServer chain elements that support the memif Mechanism @@ -55,12 +60,23 @@ func NewServer(chainCtx context.Context, vppConn api.Connection, options ...Opti memifProxyServer = memifproxy.NewServer(chainCtx) } + dumpMap := dumptool.NewMap(chainCtx, 0) + if opts.dumpOpt != nil { + var err error + dumpMap, err = dump(chainCtx, vppConn, opts.dumpOpt.PodName, opts.dumpOpt.Timeout, false) + if err != nil { + log.FromContext(chainCtx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return chain.NewNetworkServiceServer( memifProxyServer, &memifServer{ vppConn: vppConn, changeNetNS: opts.changeNetNS, nsInfo: newNetNSInfo(), + dumpMap: dumpMap, }, ) } @@ -82,7 +98,7 @@ func (m *memifServer) Request(ctx context.Context, request *networkservice.Netwo return conn, nil } - if err = create(ctx, conn, m.vppConn, metadata.IsClient(m), m.nsInfo.netNS); err != nil { + if err = create(ctx, conn, m.vppConn, m.dumpMap, metadata.IsClient(m), m.nsInfo.netNS); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -97,6 +113,6 @@ func (m *memifServer) Request(ctx context.Context, request *networkservice.Netwo } func (m *memifServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { - _ = del(ctx, conn, m.vppConn, metadata.IsClient(m)) + _ = del(ctx, conn, m.vppConn, m.dumpMap, metadata.IsClient(m)) return next.Server(ctx).Close(ctx, conn) } diff --git a/pkg/networkservice/mechanisms/vxlan/client.go b/pkg/networkservice/mechanisms/vxlan/client.go index 6864da67..9bbdc372 100644 --- a/pkg/networkservice/mechanisms/vxlan/client.go +++ b/pkg/networkservice/mechanisms/vxlan/client.go @@ -20,6 +20,7 @@ package vxlan import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "net" "github.com/networkservicemesh/sdk/pkg/tools/log" @@ -43,6 +44,7 @@ import ( type vxlanClient struct { vppConn api.Connection + dumpMap *dumptool.Map } // NewClient - returns a new client for the vxlan remote mechanism @@ -54,9 +56,21 @@ func NewClient(vppConn api.Connection, tunnelIP net.IP, options ...Option) netwo opt(opts) } + ctx := context.Background() + dumpMap := dumptool.NewMap(ctx, 0) + if opts.dumpOpt != nil { + var err error + dumpMap, err = dump(ctx, vppConn, opts.dumpOpt.PodName, opts.dumpOpt.Timeout, true) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return chain.NewNetworkServiceClient( &vxlanClient{ vppConn: vppConn, + dumpMap: dumpMap, }, mtu.NewClient(vppConn, tunnelIP), vni.NewClient(tunnelIP, vni.WithTunnelPort(opts.vxlanPort)), @@ -82,7 +96,7 @@ func (v *vxlanClient) Request(ctx context.Context, request *networkservice.Netwo return nil, err } - if err := addDel(ctx, conn, v.vppConn, true, metadata.IsClient(v)); err != nil { + if err := addDel(ctx, conn, v.vppConn, v.dumpMap,true, metadata.IsClient(v)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -100,8 +114,7 @@ func (v *vxlanClient) Close(ctx context.Context, conn *networkservice.Connection if conn.GetPayload() != payload.Ethernet { return next.Client(ctx).Close(ctx, conn, opts...) } - - if err := addDel(ctx, conn, v.vppConn, false, metadata.IsClient(v)); err != nil { + if err := addDel(ctx, conn, v.vppConn, v.dumpMap, false, metadata.IsClient(v)); err != nil { log.FromContext(ctx).WithField("vxlan", "client").Errorf("error while deleting vxlan connection: %v", err.Error()) } diff --git a/pkg/networkservice/mechanisms/vxlan/common.go b/pkg/networkservice/mechanisms/vxlan/common.go index 741ba9f3..a7c1577f 100644 --- a/pkg/networkservice/mechanisms/vxlan/common.go +++ b/pkg/networkservice/mechanisms/vxlan/common.go @@ -18,6 +18,10 @@ package vxlan import ( "context" + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/edwarnicke/govpp/binapi/interface_types" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "io" "time" "git.fd.io/govpp.git/api" @@ -33,8 +37,12 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/tools/types" ) -func addDel(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isAdd, isClient bool) error { +func addDel(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, dumpMap *dumptool.Map, isAdd, isClient bool) error { if mechanism := vxlanMech.ToMechanism(conn.GetMechanism()); mechanism != nil { + if val, loaded := dumpMap.LoadAndDelete(conn.GetId()); loaded { + ifindex.Store(ctx, isClient, val.(interface_types.InterfaceIndex)) + } + port := mechanism.DstPort() if isClient { port = mechanism.SrcPort() @@ -110,3 +118,50 @@ func addDel(ctx context.Context, conn *networkservice.Connection, vppConn api.Co return nil } + +func dump(ctx context.Context, vppConn api.Connection, podName string, timeout time.Duration, isClient bool) (*dumptool.Map, error) { + return dumptool.DumpInterfaces(ctx, vppConn, podName, timeout, isClient, + /* Function on dump */ + func(details *interfaces.SwInterfaceDetails) (interface{}, error) { + if details.InterfaceDevType == dumptool.DevTypeVxlan { + return details.SwIfIndex, nil + } + return nil, errors.New("Doesn't match the Vxlan interface") + }, + /* Function on delete */ + func(ifindex interface{}) error { + vxClient, err := vxlan.NewServiceClient(vppConn).VxlanTunnelV2Dump(ctx, &vxlan.VxlanTunnelV2Dump{ + SwIfIndex: ifindex.(interface_types.InterfaceIndex), + }) + if err != nil { + return err + } + defer func() { _ = vxClient.Close() }() + + for { + vxDetails, err := vxClient.Recv() + if err == io.EOF { + break + } + if vxDetails == nil { + continue + } + + _, err = vxlan.NewServiceClient(vppConn).VxlanAddDelTunnelV2(ctx, &vxlan.VxlanAddDelTunnelV2{ + IsAdd: false, + Instance: vxDetails.Instance, + SrcAddress: vxDetails.SrcAddress, + DstAddress: vxDetails.DstAddress, + SrcPort: vxDetails.SrcPort, + DstPort: vxDetails.DstPort, + McastSwIfIndex: vxDetails.McastSwIfIndex, + EncapVrfID: vxDetails.EncapVrfID, + DecapNextIndex: vxDetails.DecapNextIndex, + Vni: vxDetails.Vni, + }) + return err + } + return nil + }) +} + diff --git a/pkg/networkservice/mechanisms/vxlan/option.go b/pkg/networkservice/mechanisms/vxlan/option.go index 4b406ea5..932e2b72 100644 --- a/pkg/networkservice/mechanisms/vxlan/option.go +++ b/pkg/networkservice/mechanisms/vxlan/option.go @@ -18,6 +18,11 @@ package vxlan +import ( + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/vxlan/vni" +) + // Option is an option pattern for vxlan server/client type Option func(o *vxlanOptions) @@ -30,6 +35,16 @@ func WithPort(port uint16) Option { } } +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *vxlanOptions) { + o.dumpOpt = dump + } +} + + type vxlanOptions struct { vxlanPort uint16 + vniKeys []*vni.VniKey + dumpOpt *dumptool.DumpOption } diff --git a/pkg/networkservice/mechanisms/vxlan/server.go b/pkg/networkservice/mechanisms/vxlan/server.go index 0411cab4..7ef3956c 100644 --- a/pkg/networkservice/mechanisms/vxlan/server.go +++ b/pkg/networkservice/mechanisms/vxlan/server.go @@ -1,6 +1,6 @@ -// Copyright (c) 2020-2021 Cisco and/or its affiliates. +// Copyright (c) 2020-2022 Cisco and/or its affiliates. // -// Copyright (c) 2021 Nordix Foundation. +// Copyright (c) 2021-2022 Nordix Foundation. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,6 +20,10 @@ package vxlan import ( "context" + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/edwarnicke/govpp/binapi/vxlan" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "io" "net" "github.com/networkservicemesh/sdk/pkg/tools/log" @@ -41,6 +45,7 @@ import ( type vxlanServer struct { vppConn api.Connection + dumpMap *dumptool.Map } // NewServer - returns a new server for the vxlan remote mechanism @@ -52,11 +57,52 @@ func NewServer(vppConn api.Connection, tunnelIP net.IP, options ...Option) netwo opt(opts) } + ctx := context.Background() + dumpMap := dumptool.NewMap(ctx, 0) + dumpVNI := dumptool.NewMap(ctx, 0) + if opts.dumpOpt != nil { + var err error + dumpMap, err = dump(ctx, vppConn, opts.dumpOpt.PodName, opts.dumpOpt.Timeout, false) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + + dumpVNI,_ = dumptool.DumpInterfaces(ctx, vppConn, opts.dumpOpt.PodName, opts.dumpOpt.Timeout, false, + func(details *interfaces.SwInterfaceDetails) (interface{}, error) { + if details.InterfaceDevType == dumptool.DevTypeVxlan { + vxClient, err := vxlan.NewServiceClient(vppConn).VxlanTunnelV2Dump(ctx, &vxlan.VxlanTunnelV2Dump{ + SwIfIndex: details.SwIfIndex, + }) + if err != nil { + return nil, err + } + defer func() { _ = vxClient.Close() }() + + vxDetails, err := vxClient.Recv() + if err == io.EOF || vxDetails == nil { + return nil, nil + } + return vni.NewVniKey(vxDetails.SrcAddress.String(), vxDetails.Vni), nil + } + return nil, nil + }, + nil, + ) + } + + var vniList []*vni.VniKey + dumpVNI.Range(func(key string, value interface{}) bool { + vniList = append(vniList, value.(*vni.VniKey)) + return true + }) + return chain.NewNetworkServiceServer( - vni.NewServer(tunnelIP, vni.WithTunnelPort(opts.vxlanPort)), + vni.NewServer(tunnelIP, vni.WithTunnelPort(opts.vxlanPort), vni.WithVNIKeys(vniList)), mtu.NewServer(vppConn, tunnelIP), &vxlanServer{ vppConn: vppConn, + dumpMap: dumpMap, }, ) } @@ -65,7 +111,6 @@ func (v *vxlanServer) Request(ctx context.Context, request *networkservice.Netwo if request.GetConnection().GetPayload() != payload.Ethernet { return next.Server(ctx).Request(ctx, request) } - postponeCtxFunc := postpone.ContextWithValues(ctx) conn, err := next.Server(ctx).Request(ctx, request) @@ -73,7 +118,7 @@ func (v *vxlanServer) Request(ctx context.Context, request *networkservice.Netwo return nil, err } - if err := addDel(ctx, conn, v.vppConn, true, metadata.IsClient(v)); err != nil { + if err := addDel(ctx, conn, v.vppConn, v.dumpMap, true, metadata.IsClient(v)); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -91,8 +136,7 @@ func (v *vxlanServer) Close(ctx context.Context, conn *networkservice.Connection if conn.GetPayload() != payload.Ethernet { return next.Server(ctx).Close(ctx, conn) } - - if err := addDel(ctx, conn, v.vppConn, false, false); err != nil { + if err := addDel(ctx, conn, v.vppConn, v.dumpMap,false, metadata.IsClient(v)); err != nil { log.FromContext(ctx).WithField("vxlan", "server").Errorf("error while deleting vxlan connection: %v", err.Error()) } diff --git a/pkg/networkservice/tag/common.go b/pkg/networkservice/tag/common.go index 6b22d198..a84006e1 100644 --- a/pkg/networkservice/tag/common.go +++ b/pkg/networkservice/tag/common.go @@ -20,6 +20,8 @@ import ( "context" "time" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "git.fd.io/govpp.git/api" interfaces "github.com/edwarnicke/govpp/binapi/interface" "github.com/networkservicemesh/api/pkg/api/networkservice" @@ -36,16 +38,21 @@ func create(ctx context.Context, conn *networkservice.Connection, vppConn api.Co } now := time.Now() + tag := dumptool.ConvertToTag(&dumptool.TagStruct{ + PodName: 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/networkservice/tag/option.go b/pkg/networkservice/tag/option.go new file mode 100644 index 00000000..60c68316 --- /dev/null +++ b/pkg/networkservice/tag/option.go @@ -0,0 +1,31 @@ +// Copyright (c) 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 tag + +type options struct { + name string +} + +// Option is an option pattern for +type Option func(o *options) + +// WithName - sets +func WithName(n string) Option { + return func(o *options) { + o.name = n + } +} diff --git a/pkg/networkservice/xconnect/l3xconnect/common.go b/pkg/networkservice/xconnect/l3xconnect/common.go index b812de7a..3660741f 100644 --- a/pkg/networkservice/xconnect/l3xconnect/common.go +++ b/pkg/networkservice/xconnect/l3xconnect/common.go @@ -18,6 +18,8 @@ package l3xconnect import ( "context" + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" "net" "time" @@ -144,3 +146,23 @@ func l3xcUpdate(fromSwIfIndex, toIfIndex interface_types.InterfaceIndex, nextHop } return rv } + +func dump(ctx context.Context, vppConn api.Connection, podName string, timeout time.Duration, isClient bool) (*dumptool.Map, error) { + return dumptool.DumpInterfaces(ctx, vppConn, podName, timeout, isClient, + /* Function on dump */ + func(details *interfaces.SwInterfaceDetails) (interface{}, error) { + return details.SwIfIndex, nil + }, + /* Function on delete */ + func(ifindex interface{}) error { + for _, isIP6 := range []bool{true, false} { + if _, err := l3xc.NewServiceClient(vppConn).L3xcDel(ctx, &l3xc.L3xcDel{ + SwIfIndex: ifindex.(interface_types.InterfaceIndex), + IsIP6: isIP6, + }); err != nil { + return errors.WithStack(err) + } + } + return nil + }) +} \ No newline at end of file diff --git a/pkg/networkservice/xconnect/l3xconnect/options.go b/pkg/networkservice/xconnect/l3xconnect/options.go new file mode 100644 index 00000000..907fc50e --- /dev/null +++ b/pkg/networkservice/xconnect/l3xconnect/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 l3xconnect + +import "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + +type options struct { + dumpOpt *dumptool.DumpOption +} + +// Option is an option pattern for kernel +type Option func(o *options) + +// WithDump - sets dump parameters +func WithDump(dump *dumptool.DumpOption) Option { + return func(o *options) { + o.dumpOpt = dump + } +} \ No newline at end of file diff --git a/pkg/networkservice/xconnect/l3xconnect/server.go b/pkg/networkservice/xconnect/l3xconnect/server.go index 2af6752f..2580ece6 100644 --- a/pkg/networkservice/xconnect/l3xconnect/server.go +++ b/pkg/networkservice/xconnect/l3xconnect/server.go @@ -18,6 +18,8 @@ package l3xconnect import ( "context" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/dumptool" + "github.com/networkservicemesh/sdk/pkg/tools/log" "git.fd.io/govpp.git/api" "github.com/golang/protobuf/ptypes/empty" @@ -31,12 +33,38 @@ import ( type l3XconnectServer struct { vppConn api.Connection + dumpMapC *dumptool.Map + dumpMapS *dumptool.Map } // NewServer returns a Server chain element that will cross connect a client and server vpp interface (if present) -func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer { +func NewServer(vppConn api.Connection, opts ...Option) networkservice.NetworkServiceServer { + o := &options{} + for _, opt := range opts { + opt(o) + } + + ctx := context.Background() + dumpMapC := dumptool.NewMap(ctx, 0) + dumpMapS := dumptool.NewMap(ctx, 0) + if o.dumpOpt != nil { + var err error + dumpMapC, err = dump(ctx, vppConn, o.dumpOpt.PodName, o.dumpOpt.Timeout, true) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + dumpMapS, err = dump(ctx, vppConn, o.dumpOpt.PodName, o.dumpOpt.Timeout, false) + if err != nil { + log.FromContext(ctx).Errorf("failed to Dump: %v", err) + /* TODO: set empty dumpMap here? */ + } + } + return &l3XconnectServer{ vppConn: vppConn, + dumpMapC: dumpMapC, + dumpMapS: dumpMapS, } } @@ -52,6 +80,8 @@ func (v *l3XconnectServer) Request(ctx context.Context, request *networkservice. return nil, err } + _,_ = v.dumpMapC.LoadAndDelete(conn.GetId()) + _,_ = v.dumpMapS.LoadAndDelete(conn.GetId()) if err := create(ctx, v.vppConn, conn); err != nil { closeCtx, cancelClose := postponeCtxFunc() defer cancelClose() @@ -70,6 +100,8 @@ func (v *l3XconnectServer) Close(ctx context.Context, conn *networkservice.Conne if conn.GetPayload() != payload.IP { return next.Server(ctx).Close(ctx, conn) } + _,_ = v.dumpMapC.LoadAndDelete(conn.GetId()) + _,_ = v.dumpMapS.LoadAndDelete(conn.GetId()) _ = del(ctx, v.vppConn) rv, err := next.Server(ctx).Close(ctx, conn) if err != nil { diff --git a/pkg/tools/dumptool/dump.go b/pkg/tools/dumptool/dump.go new file mode 100644 index 00000000..2ed8ef43 --- /dev/null +++ b/pkg/tools/dumptool/dump.go @@ -0,0 +1,69 @@ +// 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 + +import ( + "context" + "io" + "time" + + "git.fd.io/govpp.git/api" + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/pkg/errors" +) + +// DumpFn - +type DumpFn func(details *interfaces.SwInterfaceDetails) (interface{}, error) + +// DeleteFn - +type DeleteFn func(value interface{}) error + +// DumpOption - +type DumpOption struct { + PodName string + Timeout time.Duration +} + +// DumpInterfaces - key - connectionID, value - value +func DumpInterfaces(ctx context.Context, vppConn api.Connection, podName string, timeout time.Duration, isClient bool, onDump DumpFn, onDelete DeleteFn) (*Map, error) { + client, err := interfaces.NewServiceClient(vppConn).SwInterfaceDump(ctx, &interfaces.SwInterfaceDump{}) + if err != nil { + return nil, errors.Wrap(err, "SwInterfaceDump error") + } + defer func() { _ = client.Close() }() + + retMap := NewMap(ctx, timeout) + for { + details, err := client.Recv() + if err == io.EOF || details == nil { + break + } + + t, err := ConvertFromTag(details.Tag) + if err != nil { + continue + } + if t.PodName != podName || t.IsClient != isClient { + continue + } + + if val, err := onDump(details); err == nil && val != nil { + retMap.Store(t.ConnID, val, onDelete) + } + } + return retMap, nil +} diff --git a/pkg/tools/dumptool/dumptool.go b/pkg/tools/dumptool/dumptool.go new file mode 100644 index 00000000..e66516f0 --- /dev/null +++ b/pkg/tools/dumptool/dumptool.go @@ -0,0 +1,79 @@ +// 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 + +import ( + "strings" + + "github.com/pkg/errors" +) + +const ( + clientIdentifier = "c" + serverIdentifier = "s" + delimiter = "_" + + // DevTypeTap - + DevTypeTap = "virtio" + // DevTypeMemif - + DevTypeMemif = "memif" + // DevTypeMemif - + DevTypeVxlan = "VXLAN" + // DevTypeAfPacket - + DevTypeAfPacket = "af_packet" +) + +// TagStruct - +type TagStruct struct { + PodName string + ConnID string + IsClient bool +} + +// ConvertToTag - format: forwarder-xxx_c_0000-0000-0000-0000 +func ConvertToTag(t *TagStruct) string { + isClientStr := clientIdentifier + if !t.IsClient { + isClientStr = serverIdentifier + } + return t.PodName + delimiter + isClientStr + delimiter + t.ConnID +} + +// ConvertFromTag - format: forwarder-xxx_c_0000-0000-0000-0000 +func ConvertFromTag(tag string) (t *TagStruct, 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 = &TagStruct{ + PodName: 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 +} diff --git a/pkg/tools/dumptool/gen.go b/pkg/tools/dumptool/gen.go new file mode 100644 index 00000000..5d68c5a5 --- /dev/null +++ b/pkg/tools/dumptool/gen.go @@ -0,0 +1,26 @@ +// 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 + +import ( + "sync" +) + +//go:generate go-syncmap -output table_map.gen.go -type innerMap + +// Map - sync.Map with key == string (netNsURL) and value == policies +type innerMap sync.Map diff --git a/pkg/tools/dumptool/map.go b/pkg/tools/dumptool/map.go new file mode 100644 index 00000000..384721ed --- /dev/null +++ b/pkg/tools/dumptool/map.go @@ -0,0 +1,73 @@ +// 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 + +import ( + "context" + "time" + + "github.com/edwarnicke/govpp/binapi/interface_types" + "github.com/networkservicemesh/sdk/pkg/tools/clock" +) + +// Map - +type Map struct { + innerMap + ctx context.Context + timeout time.Duration +} + +// NewMap - +func NewMap(ctx context.Context, timeout time.Duration) *Map { + return &Map{ + ctx: ctx, + timeout: timeout, + } +} + +// Store - +func (m *Map) Store(connID string, val interface{}, onDelete DeleteFn) { + m.innerMap.Store(connID, val) + + if onDelete !=nil { + timeClock := clock.FromContext(m.ctx) + expireCh := timeClock.After(m.timeout) + go func() { + <-expireCh + if val, loaded := m.innerMap.LoadAndDelete(connID); loaded { + _ = onDelete(val.(interface_types.InterfaceIndex)) + } + }() + } +} + +// LoadOrStore - +func (m *Map) LoadOrStore(connID string, val interface{}, onDelete DeleteFn) (interface{}, bool) { + v, loaded := m.innerMap.LoadOrStore(connID, val) + + if !loaded && onDelete !=nil { + timeClock := clock.FromContext(m.ctx) + expireCh := timeClock.After(m.timeout) + go func() { + <-expireCh + if val, loaded := m.innerMap.LoadAndDelete(connID); loaded { + _ = onDelete(val.(interface_types.InterfaceIndex)) + } + }() + } + return v, loaded +} diff --git a/pkg/tools/dumptool/table_map.gen.go b/pkg/tools/dumptool/table_map.gen.go new file mode 100644 index 00000000..94cb3824 --- /dev/null +++ b/pkg/tools/dumptool/table_map.gen.go @@ -0,0 +1,73 @@ +// Code generated by "-output table_map.gen.go -type innerMap -output table_map.gen.go -type innerMap"; DO NOT EDIT. +package dumptool + +import ( + "sync" // Used by sync.Map. +) + +// Generate code that will fail if the constants change value. +func _() { + // An "cannot convert innerMap literal (type innerMap) to type sync.Map" compiler error signifies that the base type have changed. + // Re-run the go-syncmap command to generate them again. + _ = (sync.Map)(innerMap{}) +} + +var _nil_innerMap_interface___value = func() (val interface{}) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *innerMap) Load(key string) (interface{}, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_innerMap_interface___value, ok + } + return value.(interface{}), ok +} + +// Store sets the value for a key. +func (m *innerMap) Store(key string, value interface{}) { + (*sync.Map)(m).Store(key, value) +} + +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *innerMap) LoadOrStore(key string, value interface{}) (interface{}, bool) { + actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) + if actual == nil { + return _nil_innerMap_interface___value, loaded + } + return actual.(interface{}), loaded +} + +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *innerMap) LoadAndDelete(key string) (value interface{}, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_innerMap_interface___value, loaded + } + return actual.(interface{}), loaded +} + +// Delete deletes the value for a key. +func (m *innerMap) Delete(key string) { + (*sync.Map)(m).Delete(key) +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *innerMap) Range(f func(key string, value interface{}) bool) { + (*sync.Map)(m).Range(func(key, value interface{}) bool { + return f(key.(string), value.(interface{})) + }) +}