forked from networkservicemesh/sdk-kernel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add kernel forwarder and vlan mechanism
Signed-off-by: Laszlo Kiraly <laszlo.kiraly@est.tech>
- Loading branch information
Showing
9 changed files
with
594 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright (c) 2020-2021 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. | ||
|
||
//+build !windows | ||
|
||
// Package xconnectns provides an Endpoint implementing the kernel Forwarder networks service | ||
package xconnectns | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
|
||
"google.golang.org/grpc" | ||
|
||
"github.com/networkservicemesh/api/pkg/api/networkservice" | ||
vlanmech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vlan" | ||
"github.com/networkservicemesh/sdk-kernel/pkg/kernel/networkservice/mechanisms/vlan" | ||
"github.com/networkservicemesh/sdk-kernel/pkg/kernel/networkservice/netnsconnectioncontext" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/chains/client" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/chains/endpoint" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/clienturl" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/connect" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/heal" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/recvfd" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/sendfd" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanismtranslation" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/core/adapters" | ||
"github.com/networkservicemesh/sdk/pkg/tools/addressof" | ||
"github.com/networkservicemesh/sdk/pkg/tools/token" | ||
) | ||
|
||
type kernelServer struct { | ||
endpoint.Endpoint | ||
} | ||
|
||
// NewServer - returns an Endpoint implementing the Kernel Forwarder networks service | ||
// - name - name of the Forwarder | ||
// - authzServer - policy for allowing or rejecting requests | ||
// - tokenGenerator - token.GeneratorFunc - generates tokens for use in Path | ||
// - clientUrl - *url.URL for the talking to the NSMgr | ||
// - ...clientDialOptions - dialOptions for dialing the NSMgr | ||
func NewServer( | ||
ctx context.Context, | ||
name string, | ||
authzServer networkservice.NetworkServiceServer, | ||
tokenGenerator token.GeneratorFunc, | ||
clientURL *url.URL, | ||
clientDialOptions ...grpc.DialOption, | ||
) endpoint.Endpoint { | ||
rv := kernelServer{} | ||
|
||
rv.Endpoint = endpoint.NewServer(ctx, | ||
tokenGenerator, | ||
endpoint.WithName(name), | ||
endpoint.WithAuthorizeServer(authzServer), | ||
endpoint.WithAdditionalFunctionality( | ||
recvfd.NewServer(), | ||
clienturl.NewServer(clientURL), | ||
heal.NewServer(ctx, addressof.NetworkServiceClient(adapters.NewServerToClient(rv))), | ||
connect.NewServer(ctx, | ||
client.NewCrossConnectClientFactory( | ||
client.WithName(name), | ||
client.WithAdditionalFunctionality( | ||
mechanismtranslation.NewClient(), | ||
// setup IP and route context | ||
netnsconnectioncontext.NewClient(), | ||
// mechanism | ||
vlan.NewClient(), | ||
recvfd.NewClient(), | ||
sendfd.NewClient(), | ||
), | ||
), | ||
connect.WithDialOptions(clientDialOptions...), | ||
), | ||
mechanisms.NewServer(map[string]networkservice.NetworkServiceServer{ | ||
vlanmech.MECHANISM: vlan.NewServer(), | ||
//noopmech.MECHANISM: connectChainFactory(cls.LOCAL), | ||
}), | ||
// setup IP and route context | ||
netnsconnectioncontext.NewServer(), | ||
sendfd.NewServer(), | ||
), | ||
) | ||
|
||
return rv | ||
} |
79 changes: 79 additions & 0 deletions
79
pkg/kernel/networkservice/common/mechanisms/vlan/client.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package vlan | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
|
||
"github.com/golang/protobuf/ptypes/empty" | ||
"google.golang.org/grpc" | ||
|
||
"github.com/networkservicemesh/api/pkg/api/networkservice" | ||
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls" | ||
vlanmech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vlan" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next" | ||
) | ||
|
||
const ( | ||
netNSFilename = "/proc/thread-self/ns/net" | ||
) | ||
|
||
type vlanClient struct { | ||
interfaceName string | ||
} | ||
|
||
func NewClient(options ...Option) networkservice.NetworkServiceClient { | ||
v := &vlanClient{} | ||
for _, opt := range options { | ||
opt(v) | ||
} | ||
return v | ||
} | ||
|
||
func (v *vlanClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { | ||
if !v.updateMechanismPreferences(request) { | ||
request.MechanismPreferences = append(request.GetMechanismPreferences(), &networkservice.Mechanism{ | ||
Cls: cls.LOCAL, | ||
Type: vlanmech.MECHANISM, | ||
Parameters: map[string]string{ | ||
vlanmech.NetNSURL: (&url.URL{Scheme: "file", Path: netNSFilename}).String(), | ||
vlanmech.InterfaceNameKey: v.interfaceName, | ||
}, | ||
}) | ||
} | ||
return next.Client(ctx).Request(ctx, request, opts...) | ||
} | ||
|
||
// updateMechanismPreferences returns true if MechanismPreferences has updated | ||
func (v *vlanClient) updateMechanismPreferences(request *networkservice.NetworkServiceRequest) bool { | ||
var updated = false | ||
|
||
for _, m := range request.GetRequestMechanismPreferences() { | ||
if m.Type == vlanmech.MECHANISM { | ||
if m.Parameters == nil { | ||
m.Parameters = make(map[string]string) | ||
} | ||
if m.Parameters[vlanmech.InterfaceNameKey] == "" { | ||
m.Parameters[vlanmech.InterfaceNameKey] = v.interfaceName | ||
} | ||
if m.Parameters[vlanmech.NetNSURL] == "" { | ||
m.Parameters[vlanmech.NetNSURL] = (&url.URL{Scheme: "file", Path: netNSFilename}).String() | ||
} | ||
updated = true | ||
} | ||
} | ||
|
||
return updated | ||
} | ||
|
||
func (v *vlanClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { | ||
return next.Client(ctx).Close(ctx, conn, opts...) | ||
} | ||
|
||
type Option func(v *vlanClient) | ||
|
||
// WithInterfaceName sets interface name | ||
func WithInterfaceName(interfaceName string) Option { | ||
return func(v *vlanClient) { | ||
v.interfaceName = interfaceName | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
pkg/kernel/networkservice/common/mechanisms/vlan/server.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package vlan | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
|
||
"github.com/golang/protobuf/ptypes/empty" | ||
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vlan" | ||
|
||
"github.com/networkservicemesh/api/pkg/api/networkservice" | ||
|
||
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next" | ||
) | ||
|
||
type vlanMechanismServer struct{} | ||
|
||
// NewServer - creates a NetworkServiceServer that requests a vlan interface and populates the netns inode | ||
func NewServer() networkservice.NetworkServiceServer { | ||
return &vlanMechanismServer{} | ||
} | ||
|
||
func (m *vlanMechanismServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) { | ||
if mechanism := vlan.ToMechanism(request.GetConnection().GetMechanism()); mechanism != nil { | ||
mechanism.SetNetNSURL((&url.URL{Scheme: "file", Path: netNSFilename}).String()) | ||
} | ||
return next.Server(ctx).Request(ctx, request) | ||
} | ||
|
||
func (m *vlanMechanismServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { | ||
return next.Server(ctx).Close(ctx, conn) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package vlan | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/golang/protobuf/ptypes/empty" | ||
"github.com/networkservicemesh/api/pkg/api/networkservice" | ||
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/cls" | ||
vlanmech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vlan" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next" | ||
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata" | ||
|
||
"google.golang.org/grpc" | ||
) | ||
|
||
type vlanClient struct{} | ||
|
||
func NewClient() networkservice.NetworkServiceClient { | ||
return &vlanClient{} | ||
} | ||
|
||
func (k *vlanClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { | ||
getMechPref := func(class string) networkservice.Mechanism { | ||
return networkservice.Mechanism{ | ||
Cls: class, | ||
Type: vlanmech.MECHANISM, | ||
Parameters: make(map[string]string), | ||
} | ||
} | ||
localMechanism := getMechPref(cls.LOCAL) | ||
request.MechanismPreferences = append(request.MechanismPreferences, &localMechanism) | ||
remoteMechanism := getMechPref(cls.REMOTE) | ||
request.MechanismPreferences = append(request.MechanismPreferences, &remoteMechanism) | ||
|
||
conn, err := next.Client(ctx).Request(ctx, request, opts...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if err := create(ctx, conn, metadata.IsClient(k)); err != nil { | ||
_, _ = k.Close(ctx, conn, opts...) | ||
return nil, err | ||
} | ||
return conn, nil | ||
} | ||
|
||
func (k *vlanClient) Close( | ||
ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { | ||
return next.Client(ctx).Close(ctx, conn, opts...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package vlan | ||
|
||
import ( | ||
"context" | ||
"crypto/rand" | ||
"encoding/hex" | ||
"io" | ||
|
||
"github.com/networkservicemesh/api/pkg/api/networkservice" | ||
vlanmech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vlan" | ||
"github.com/networkservicemesh/sdk-kernel/pkg/kernel/tools/nshandle" | ||
"github.com/networkservicemesh/sdk/pkg/tools/log" | ||
"github.com/pkg/errors" | ||
"github.com/vishvananda/netlink" | ||
"github.com/vishvananda/netns" | ||
) | ||
|
||
func create(ctx context.Context, conn *networkservice.Connection, isClient bool) error { | ||
if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { | ||
nsFilename := mechanism.GetNetNSURL() | ||
hostIfName := mechanism.GetInterfaceName(conn) | ||
vlanID := mechanism.VlanID() | ||
baseInterface := mechanism.GetBaseInterfaceName(conn) | ||
logger := log.FromContext(ctx).WithField("vlan", "create"). | ||
WithField("HostIfName", hostIfName). | ||
WithField("HostNamespace", nsFilename). | ||
WithField("VlanID", vlanID). | ||
WithField("baseInterface", baseInterface). | ||
WithField("isClient", isClient) | ||
logger.Debug("request") | ||
|
||
if nsFilename == "" || vlanID == 0 || baseInterface == "" { | ||
return nil | ||
} | ||
|
||
// TODO generate this based on conn id | ||
tmpName, _ := generateRandomName(7) | ||
|
||
link, err := createLink(tmpName, baseInterface, vlanID) | ||
if err != nil { | ||
return err | ||
} | ||
logger.Debugf("Temporary link created Name = %s", tmpName) | ||
|
||
var clientNetNS netns.NsHandle | ||
clientNetNS, err = nshandle.FromURL(nsFilename) | ||
if err != nil { | ||
return errors.Wrapf(err, "handle can not get for client namespace %s", nsFilename) | ||
} | ||
defer func() { _ = clientNetNS.Close() }() | ||
|
||
err = moveInterfaceToNamespace(link, clientNetNS) | ||
if err != nil { | ||
return err | ||
} | ||
logger.Debugf("Moved temporary network interface %s into the client's namespace", tmpName) | ||
|
||
var currNetNS netns.NsHandle | ||
currNetNS, err = nshandle.Current() | ||
if err != nil { | ||
return errors.Wrap(err, "handle can not get for current namespace") | ||
} | ||
defer func() { _ = currNetNS.Close() }() | ||
|
||
err = renameInterfaceInNamespace(link, hostIfName, currNetNS, clientNetNS) | ||
if err != nil { | ||
return err | ||
} | ||
logger.Debug("Network interface set in client namespace") | ||
} | ||
return nil | ||
} | ||
|
||
func generateRandomName(size int) (string, error) { | ||
id := make([]byte, 32) | ||
if _, err := io.ReadFull(rand.Reader, id); err != nil { | ||
return "", err | ||
} | ||
return hex.EncodeToString(id)[:size], nil | ||
} | ||
|
||
func createLink(name, hostInterface string, vlanID int) (netlink.Link, error) { | ||
base, err := netlink.LinkByName(hostInterface) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to get base interface %s", hostInterface) | ||
} | ||
newLink := &netlink.Vlan{ | ||
LinkAttrs: netlink.LinkAttrs{ | ||
Name: name, | ||
ParentIndex: base.Attrs().Index, | ||
}, | ||
VlanId: vlanID, | ||
VlanProtocol: netlink.VLAN_PROTOCOL_8021Q, | ||
} | ||
if err := netlink.LinkAdd(newLink); err != nil { | ||
return nil, errors.Wrapf(err, "failed to create vlan interface %s", name) | ||
} | ||
|
||
if base.Attrs().OperState != netlink.OperUp { | ||
if err = netlink.LinkSetUp(base); err != nil { | ||
return nil, errors.Wrapf(err, "failed to set up host interface: %s", hostInterface) | ||
} | ||
} | ||
|
||
return newLink, nil | ||
} | ||
|
||
func moveInterfaceToNamespace(link netlink.Link, toNetNS netns.NsHandle) error { | ||
if err := netlink.LinkSetDown(link); err != nil { | ||
return errors.Errorf("failed to set %s down: %s", link, err) | ||
} | ||
|
||
if int(toNetNS) < 0 { | ||
return errors.Errorf("failed to conver ns handle %s to valid file descriptor", toNetNS) | ||
} | ||
|
||
if err := netlink.LinkSetNsFd(link, int(toNetNS)); err != nil { | ||
return errors.Wrapf(err, "failed to move net interface to net NS: %s %s", link, toNetNS) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func renameInterfaceInNamespace(link netlink.Link, newName string, currNetNS, toNetNS netns.NsHandle) error { | ||
return nshandle.RunIn(currNetNS, toNetNS, func() error { | ||
if err := netlink.LinkSetName(link, newName); err != nil { | ||
return errors.Wrapf(err, "failed to rename net interface:%s -> %s", link, newName) | ||
} | ||
err := netlink.LinkSetUp(link) | ||
if err != nil { | ||
return errors.Errorf("failed to set %s down: %s", link, err) | ||
} | ||
|
||
return nil | ||
}) | ||
} |
Oops, something went wrong.