From bbfbfde7da81b1e27ecdead18523e871bd8d9f55 Mon Sep 17 00:00:00 2001 From: Ryan Tidwell Date: Thu, 23 Jan 2020 16:23:19 -0600 Subject: [PATCH] [WIP] Add initial utilities for link and namespace actions This change introduces basic utilities for managing interfaces, routes, and namespaces. See https://github.com/networkservicemesh/sdk-kernel/issues/2 Signed-off-by: Ryan Tidwell --- go.mod | 2 + pkg/networkservice/kernelutils/common.go | 89 +++++++++++++++++++ .../kernelutils/namespaceutils.go | 37 ++++++++ test/integration/namespace_utils_test.go | 49 ++++++++++ 4 files changed, 177 insertions(+) create mode 100644 pkg/networkservice/kernelutils/common.go create mode 100644 pkg/networkservice/kernelutils/namespaceutils.go create mode 100644 test/integration/namespace_utils_test.go diff --git a/go.mod b/go.mod index 8a925f50..c75b93b3 100644 --- a/go.mod +++ b/go.mod @@ -3,4 +3,6 @@ module github.com/networkservicemesh/sdk-kernel go 1.13 require ( + github.com/vishvananda/netlink v1.0.0 + github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f ) diff --git a/pkg/networkservice/kernelutils/common.go b/pkg/networkservice/kernelutils/common.go new file mode 100644 index 00000000..f78ef10d --- /dev/null +++ b/pkg/networkservice/kernelutils/common.go @@ -0,0 +1,89 @@ +// Copyright 2019 SUSE LLC. +// 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 kernelutils + +import ( + "github.com/networkservicemesh/networkservicemesh/controlplane/api/connectioncontext" + + "net" + + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// Kernel forwarding plane related constants +const ( + cLOCAL = iota + cINCOMING = iota + cOUTGOING = iota +) + +const ( + /* VETH pairs are used only for local connections(same node), so we can use a larger MTU size as there's no multi-node connection */ + cVETHMTU = 16000 + cCONNECT = true + cDISCONNECT = false +) + +type connectionConfig struct { + id string + srcNetNsInode string + dstNetNsInode string + srcName string + dstName string + srcIP string + dstIP string + srcIPVXLAN net.IP + dstIPVXLAN net.IP + srcRoutes []*connectioncontext.Route + dstRoutes []*connectioncontext.Route + neighbors []*connectioncontext.IpNeighbor + vni int +} + +// addNeighbors adds neighbors +func AddNeighbors(link netlink.Link, neighbors []*connectioncontext.IpNeighbor) error { + for _, neighbor := range neighbors { + mac, err := net.ParseMAC(neighbor.GetHardwareAddress()) + if err != nil { + logrus.Error("common: failed parsing the MAC address for IP neighbors:", err) + return err + } + neigh := netlink.Neigh{ + LinkIndex: link.Attrs().Index, + State: 0x02, // netlink.NUD_REACHABLE, // the constant is somehow not being found in the package in case of using a darwin based machine + IP: net.ParseIP(neighbor.GetIp()), + HardwareAddr: mac, + } + if err = netlink.NeighAdd(&neigh); err != nil { + logrus.Error("common: failed adding neighbor:", err) + return err + } + } + return nil +} + +// newVETH returns a VETH interface instance +func NewVETH(srcName, dstName string) *netlink.Veth { + /* Populate the VETH interface configuration */ + return &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: srcName, + MTU: cVETHMTU, + }, + PeerName: dstName, + } +} diff --git a/pkg/networkservice/kernelutils/namespaceutils.go b/pkg/networkservice/kernelutils/namespaceutils.go new file mode 100644 index 00000000..75a9a53b --- /dev/null +++ b/pkg/networkservice/kernelutils/namespaceutils.go @@ -0,0 +1,37 @@ +// Copyright 2019 SUSE LLC. +// 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 kernelutils + +import ( + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +func InjectLinkInNamespace(containerNs netns.NsHandle, ifaceName string) error { + /* Get a link object for the interface */ + ifaceLink, err := netlink.LinkByName(ifaceName) + if err != nil { + logrus.Errorf("common: failed to get link for %q - %v", ifaceName, err) + return err + } + /* Inject the interface into the desired namespace */ + if err = netlink.LinkSetNsFd(ifaceLink, int(containerNs)); err != nil { + logrus.Errorf("common: failed to inject %q in namespace - %v", ifaceName, err) + return err + } + return nil +} diff --git a/test/integration/namespace_utils_test.go b/test/integration/namespace_utils_test.go new file mode 100644 index 00000000..b9f5a788 --- /dev/null +++ b/test/integration/namespace_utils_test.go @@ -0,0 +1,49 @@ +package sdk_kernel_tests + +import ( + "testing" + + . "github.com/onsi/gomega" + + "github.com/networkservicemesh/sdk-kernel/pkg/networkservice/kernelutils" + + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +const testVethSrcName = "sdk-kernel-src" +const testVethDstName = "sdk-kernel-dst" + +func TestInjectLinkInNamespace(t *testing.T) { + g := NewWithT(t) + + // set up the basic resources for the test + currns, _ := netns.Get() + testns, _ := netns.New() + _ = netns.Set(currns) + vethErr := netlink.LinkAdd(kernelutils.NewVETH(testVethSrcName, testVethDstName)) + g.Expect(vethErr).To(BeNil()) + + // clean up after ourselves + defer func(rootns netns.NsHandle, testns netns.NsHandle, veth *netlink.Veth) { + _ = netns.Set(rootns) + testns.Close() + netlink.LinkDel(veth) + }(currns, testns, kernelutils.NewVETH(testVethSrcName, testVethDstName)) + + // move one end of the veth into testns and run assertions + injectError := kernelutils.InjectLinkInNamespace(testns, testVethDstName) + + _ = netns.Set(testns) + nsLinks, _ := netlink.LinkList() + _, linkErr := netlink.LinkByName(testVethDstName) + + // assert no error on link injection + g.Expect(injectError).To(BeNil()) + // assert only loopback and the interface we inject exist in the namespace + g.Expect(len(nsLinks)).To(Equal(2)) + g.Expect(linkErr).To(BeNil()) + + logrus.Printf("Done") +}