Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add loopback chain-element #532

Merged
merged 5 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.16
require (
git.fd.io/govpp.git v0.3.6-0.20210927044411-385ccc0d8ba9
github.com/edwarnicke/govpp v0.0.0-20220311182453-f32f292e0e91
github.com/edwarnicke/serialize v1.0.7
github.com/golang/protobuf v1.5.2
github.com/hashicorp/go-multierror v1.1.1
github.com/networkservicemesh/api v1.2.1-0.20220315001249-f33f8c3f2feb
Expand Down
109 changes: 109 additions & 0 deletions pkg/networkservice/loopback/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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 loopback

import (
"context"
"time"

"github.com/pkg/errors"

"git.fd.io/govpp.git/api"
interfaces "github.com/edwarnicke/govpp/binapi/interface"
"github.com/edwarnicke/govpp/binapi/interface_types"
"github.com/edwarnicke/serialize"

"github.com/networkservicemesh/sdk/pkg/tools/log"
)

type loopInfo struct {
swIfIndex interface_types.InterfaceIndex
count uint32
}

type loopbackMap struct {
serialize.Executor
entries map[string]*loopInfo
}

/* Create loopback interface and store it in metadata */
func createLoopback(ctx context.Context, vppConn api.Connection, networkService string, t *loopbackMap, isClient bool) (err error) {
if _, ok := Load(ctx, isClient); !ok {
<-t.AsyncExec(func() {
/* Check if we have already created loopback for a given NetworkService previously */
info, ok := t.entries[networkService]
if !ok {
var swIfIndex interface_types.InterfaceIndex
swIfIndex, err = createLoopbackVPP(ctx, vppConn)
if err != nil {
return
}
info = &loopInfo{
swIfIndex: swIfIndex,
}
t.entries[networkService] = info
}
info.count++
Store(ctx, isClient, info.swIfIndex)
})
}
return
}

func createLoopbackVPP(ctx context.Context, vppConn api.Connection) (interface_types.InterfaceIndex, error) {
now := time.Now()
reply, err := interfaces.NewServiceClient(vppConn).CreateLoopback(ctx, &interfaces.CreateLoopback{})
if err != nil {
return interface_types.InterfaceIndex(^uint32(0)), errors.WithStack(err)
}
log.FromContext(ctx).
WithField("swIfIndex", reply.SwIfIndex).
WithField("duration", time.Since(now)).
WithField("vppapi", "CreateLoopback").Debug("completed")
return reply.SwIfIndex, nil
}

func del(ctx context.Context, vppConn api.Connection, networkService string, t *loopbackMap, isClient bool) {
if swIfIndex, ok := LoadAndDelete(ctx, isClient); ok {
t.AsyncExec(func() {
t.entries[networkService].count--

/* If there are no more clients using the loopback - delete it */
if t.entries[networkService].count == 0 {
delete(t.entries, networkService)
if err := delVPP(ctx, vppConn, swIfIndex); err != nil {
log.FromContext(ctx).Errorf("unable to delete loopback interface: %v", err)
}
}
})
}
}

func delVPP(ctx context.Context, vppConn api.Connection, swIfIndex interface_types.InterfaceIndex) error {
now := time.Now()
_, err := interfaces.NewServiceClient(vppConn).DeleteLoopback(ctx, &interfaces.DeleteLoopback{
SwIfIndex: swIfIndex,
})
if err != nil {
return errors.WithStack(err)
}
log.FromContext(ctx).
WithField("swIfIndex", swIfIndex).
WithField("duration", time.Since(now)).
WithField("vppapi", "DeleteLoopback").Debug("completed")
return nil
}
18 changes: 18 additions & 0 deletions pkg/networkservice/loopback/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 loopback provides networkservice.NetworkService chain elements for creating loopback interface
package loopback
72 changes: 72 additions & 0 deletions pkg/networkservice/loopback/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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 loopback

import (
"context"

"github.com/edwarnicke/govpp/binapi/interface_types"

"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

type key struct{}

// Store sets the loopback swIfIndex stored in per Connection.Id metadata.
func Store(ctx context.Context, isClient bool, swIfIndex interface_types.InterfaceIndex) {
metadata.Map(ctx, isClient).Store(key{}, swIfIndex)
}

// Delete deletes the swIfIndex stored in per Connection.Id metadata
func Delete(ctx context.Context, isClient bool) {
metadata.Map(ctx, isClient).Delete(key{})
}

// Load returns the swIfIndex stored in per Connection.Id metadata, or nil if no
// value is present.
// The ok result indicates whether value was found in the per Connection.Id metadata.
func Load(ctx context.Context, isClient bool) (value interface_types.InterfaceIndex, ok bool) {
rawValue, ok := metadata.Map(ctx, isClient).Load(key{})
if !ok {
return
}
value, ok = rawValue.(interface_types.InterfaceIndex)
return value, ok
}

// LoadOrStore returns the existing swIfIndex stored in per Connection.Id metadata if present.
// Otherwise, it stores and returns the given swIfIndex.
// The loaded result is true if the value was loaded, false if stored.
func LoadOrStore(ctx context.Context, isClient bool, swIfIndex interface_types.InterfaceIndex) (value interface_types.InterfaceIndex, ok bool) {
rawValue, ok := metadata.Map(ctx, isClient).LoadOrStore(key{}, swIfIndex)
if !ok {
return
}
value, ok = rawValue.(interface_types.InterfaceIndex)
return value, ok
}

// LoadAndDelete deletes the swIfIndex stored in per Connection.Id metadata,
// returning the previous value if any. The loaded result reports whether the key was present.
func LoadAndDelete(ctx context.Context, isClient bool) (value interface_types.InterfaceIndex, ok bool) {
rawValue, ok := metadata.Map(ctx, isClient).LoadAndDelete(key{})
if !ok {
return
}
value, ok = rawValue.(interface_types.InterfaceIndex)
return value, ok
}
61 changes: 61 additions & 0 deletions pkg/networkservice/loopback/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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 loopback

import (
"context"

"git.fd.io/govpp.git/api"
"github.com/golang/protobuf/ptypes/empty"

"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

type loopbackServer struct {
vppConn api.Connection

loopbacks *loopbackMap
}

// NewServer creates a NetworkServiceServer chain element to create the loopback vpp-interface
func NewServer(vppConn api.Connection) networkservice.NetworkServiceServer {
return &loopbackServer{
vppConn: vppConn,
loopbacks: &loopbackMap{
entries: make(map[string]*loopInfo),
},
}
}

func (l *loopbackServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
networkService := request.GetConnection().NetworkService
if err := createLoopback(ctx, l.vppConn, networkService, l.loopbacks, metadata.IsClient(l)); err != nil {
return nil, err
}
conn, err := next.Server(ctx).Request(ctx, request)
if err != nil {
del(ctx, l.vppConn, networkService, l.loopbacks, metadata.IsClient(l))
}
return conn, err
}

func (l *loopbackServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) {
del(ctx, l.vppConn, conn.NetworkService, l.loopbacks, metadata.IsClient(l))
return next.Server(ctx).Close(ctx, conn)
}