Skip to content

Commit

Permalink
Merge pull request #84 from edwarnicke/memifproxy
Browse files Browse the repository at this point in the history
Add memifproxy chain element
  • Loading branch information
denis-tingaikin committed Feb 8, 2021
2 parents d5f8a67 + fa0b0a9 commit bbccef9
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 3 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
git.fd.io/govpp.git v0.3.6-0.20200903151113-c94a96227985
github.com/edwarnicke/govpp v0.0.0-20201111163523-106f68b6ba26
github.com/golang/protobuf v1.4.3
github.com/hashicorp/go-multierror v1.0.0
github.com/networkservicemesh/api v0.0.0-20210202152048-ec956057eb3a
github.com/networkservicemesh/sdk v0.0.0-20210208092844-64f6aa269f63
github.com/pkg/errors v0.9.1
Expand Down
14 changes: 11 additions & 3 deletions pkg/networkservice/mechanisms/memif/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !windows

package memif

import (
Expand All @@ -23,10 +25,13 @@ import (
"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/chain"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"google.golang.org/grpc"

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

"github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/memif/memifproxy"
)

type memifClient struct {
Expand All @@ -35,9 +40,12 @@ type memifClient struct {

// NewClient provides a NetworkServiceClient chain elements that support the memif Mechanism
func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient {
return &memifClient{
vppConn: vppConn,
}
return chain.NewNetworkServiceClient(
&memifClient{
vppConn: vppConn,
},
memifproxy.New(),
)
}

func (m *memifClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
Expand Down
84 changes: 84 additions & 0 deletions pkg/networkservice/mechanisms/memif/memifproxy/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2020 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 !windows

package memifproxy

import (
"context"
"os"
"path/filepath"

"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"
memifMech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/memif"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
"github.com/pkg/errors"
"google.golang.org/grpc"
)

const (
memifNetwork = "unixpacket"
maxFDCount = 1
bufferSize = 128
)

type memifProxyClient struct{}

// New - create a new memifProxy client chain element
func New() networkservice.NetworkServiceClient {
return &memifProxyClient{}
}

func (m *memifProxyClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
conn, err := next.Client(ctx).Request(ctx, request, opts...)
if err != nil {
return nil, err
}
mechanism := memifMech.ToMechanism(conn.GetMechanism())
if mechanism == nil {
return conn, nil
}
// If we are already running a proxy... just keep running it
if _, ok := load(ctx, true); ok {
return conn, nil
}
err = os.MkdirAll(filepath.Dir(listenSocketFilename(conn)), 0700)
if err != nil {
return nil, errors.Wrapf(err, "unable to mkdir %s", filepath.Dir(listenSocketFilename(conn)))
}
listener, err := newProxyListener(mechanism, listenSocketFilename(conn))
if err != nil {
return nil, err
}
store(ctx, metadata.IsClient(m), listener)
return conn, nil
}

func (m *memifProxyClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
rv, err := next.Client(ctx).Close(ctx, conn)
if listener, ok := loadAndDelete(ctx, metadata.IsClient(m)); ok {
_ = listener.Close()
_ = os.RemoveAll(filepath.Dir(listenSocketFilename(conn)))
}
return rv, err
}

func listenSocketFilename(conn *networkservice.Connection) string {
return filepath.Join(os.TempDir(), "memifproxy", conn.GetId(), "memif.socket")
}
19 changes: 19 additions & 0 deletions pkg/networkservice/mechanisms/memif/memifproxy/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2020 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 memifproxy provides a NetworkServiceClient chain element to 'proxy' to the memif control socket
// This is done in case the vpp instance can't open the memif socketfile
package memifproxy
55 changes: 55 additions & 0 deletions pkg/networkservice/mechanisms/memif/memifproxy/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2020 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 !windows

package memifproxy

import (
"context"

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

type key struct{}

// store sets the *proxyListener stored in per Connection.Id metadata.
func store(ctx context.Context, isClient bool, listener *proxyListener) {
metadata.Map(ctx, isClient).Store(key{}, listener)
}

// load returns the *proxyListener 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 *proxyListener, ok bool) {
rawValue, ok := metadata.Map(ctx, isClient).Load(key{})
if !ok {
return
}
value, ok = rawValue.(*proxyListener)
return value, ok
}

// loadAndDelete deletes the *proxyListener 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 *proxyListener, ok bool) {
rawValue, ok := metadata.Map(ctx, isClient).LoadAndDelete(key{})
if !ok {
return
}
value, ok = rawValue.(*proxyListener)
return value, ok
}
91 changes: 91 additions & 0 deletions pkg/networkservice/mechanisms/memif/memifproxy/proxy_connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) 2020 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 !windows

package memifproxy

import (
"net"
"syscall"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
)

type proxyConnection struct {
in net.Conn
out net.Conn
}

func newProxyConnection(in, out net.Conn) (*proxyConnection, error) {
p := &proxyConnection{
in: in,
out: out,
}
if err := p.copy(in, out); err != nil {
return nil, err
}
if err := p.copy(out, in); err != nil {
return nil, err
}
return p, nil
}

func (p *proxyConnection) Close() error {
inErr := p.in.Close()
outErr := p.out.Close()
if inErr != nil {
return multierror.Append(inErr, outErr)
}
return outErr
}

func (p *proxyConnection) copy(dst, src net.Conn) error {
b := make([]byte, bufferSize)
unixsrc, unixSrcOK := src.(interface {
ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *net.UnixAddr, err error)
})
if !unixSrcOK {
return errors.Errorf("%s does not implement ReadMsgUnix", src.LocalAddr())
}

unixdst, unixdstOK := dst.(interface {
WriteMsgUnix(b, oob []byte, addr *net.UnixAddr) (n, oobn int, err error)
})

if !unixdstOK {
return errors.Errorf("%s does not implement ReadMsgUnix", dst.LocalAddr())
}

go func() {
oob := make([]byte, syscall.CmsgSpace(4*maxFDCount))
for {
var writeN, writeoob int
readn, readoobn, _, _, err := unixsrc.ReadMsgUnix(b, oob)
if err != nil {
return
}
for writeN < readn {
writeN, writeoob, err = unixdst.WriteMsgUnix(b[writeN:readn], oob[writeoob:readoobn], nil)
if err != nil {
return
}
}
}
}()
return nil
}
Loading

0 comments on commit bbccef9

Please sign in to comment.