diff --git a/go.mod b/go.mod index 849c07b9..f3b9e3d2 100644 --- a/go.mod +++ b/go.mod @@ -17,5 +17,4 @@ require ( golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.25.0 - gopkg.in/yaml.v2 v2.2.4 // indirect ) diff --git a/go.sum b/go.sum index 31415085..12a8fd69 100644 --- a/go.sum +++ b/go.sum @@ -372,9 +372,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/networkservice/chains/forwarder/server.go b/pkg/networkservice/chains/forwarder/server.go index 4f3a1d1d..887f15e7 100644 --- a/pkg/networkservice/chains/forwarder/server.go +++ b/pkg/networkservice/chains/forwarder/server.go @@ -41,6 +41,7 @@ import ( "github.com/networkservicemesh/sdk/pkg/tools/token" "github.com/networkservicemesh/sdk-kernel/pkg/kernel/networkservice/connectioncontextkernel" + "github.com/networkservicemesh/sdk-kernel/pkg/kernel/networkservice/ethernetcontext" "github.com/networkservicemesh/sdk/pkg/networkservice/common/discover" "github.com/networkservicemesh/sdk/pkg/networkservice/common/roundrobin" @@ -50,6 +51,7 @@ import ( "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/connectioncontext/mtu" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/kernel" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/memif" + "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/vlan" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/vxlan" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/wireguard" "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/pinhole" @@ -70,7 +72,7 @@ type xconnectNSServer struct { } // NewServer - returns an implementation of the xconnectns network service -func NewServer(ctx context.Context, name string, authzServer networkservice.NetworkServiceServer, tokenGenerator token.GeneratorFunc, clientURL *url.URL, vppConn Connection, tunnelIP net.IP, tunnelPort uint16, dialTimeout time.Duration, clientDialOptions ...grpc.DialOption) endpoint.Endpoint { +func NewServer(ctx context.Context, name string, authzServer networkservice.NetworkServiceServer, tokenGenerator token.GeneratorFunc, clientURL *url.URL, vppConn Connection, tunnelIP net.IP, tunnelPort uint16, dialTimeout time.Duration, domain2Device map[string]string, clientDialOptions ...grpc.DialOption) endpoint.Endpoint { nseClient := registryclient.NewNetworkServiceEndpointRegistryClient(ctx, clientURL, registryclient.WithNSEAdditionalFunctionality(registryrecvfd.NewNetworkServiceEndpointRegistryClient()), registryclient.WithDialOptions(clientDialOptions...), @@ -87,6 +89,7 @@ func NewServer(ctx context.Context, name string, authzServer networkservice.Netw up.NewServer(ctx, vppConn), xconnect.NewServer(vppConn), connectioncontextkernel.NewServer(), + ethernetcontext.NewVFServer(), tag.NewServer(ctx, vppConn), mtu.NewServer(vppConn), mechanisms.NewServer(map[string]networkservice.NetworkServiceServer{ @@ -114,6 +117,7 @@ func NewServer(ctx context.Context, name string, authzServer networkservice.Netw kernel.NewClient(vppConn), vxlan.NewClient(vppConn, tunnelIP, vxlan.WithVniPort(tunnelPort)), wireguard.NewClient(vppConn, tunnelIP), + vlan.NewClient(vppConn, domain2Device), filtermechanisms.NewClient(), pinhole.NewClient(vppConn), recvfd.NewClient(), diff --git a/pkg/networkservice/mechanisms/vlan/client.go b/pkg/networkservice/mechanisms/vlan/client.go new file mode 100644 index 00000000..898eda37 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/client.go @@ -0,0 +1,186 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 vlan + +import ( + "context" + "io" + "time" + + "git.fd.io/govpp.git/api" + + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/golang/protobuf/ptypes/empty" + "github.com/pkg/errors" + "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/api/pkg/api/networkservice/payload" + "github.com/networkservicemesh/sdk/pkg/networkservice/core/chain" + "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" + "github.com/networkservicemesh/sdk/pkg/tools/log" + "github.com/networkservicemesh/sdk/pkg/tools/postpone" + + "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/vlan/hwaddress" + "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/vlan/l2vtr" + "github.com/networkservicemesh/sdk-vpp/pkg/networkservice/mechanisms/vlan/linkinit" + "github.com/networkservicemesh/sdk-vpp/pkg/tools/ifindex" +) + +const ( + serviceDomainLabel = "serviceDomain" +) + +type vlanClient struct { + vppConn api.Connection + deviceNames map[string]string +} + +// NewClient returns a VLAN client chain element +func NewClient(vppConn api.Connection, domain2Device map[string]string) networkservice.NetworkServiceClient { + return chain.NewNetworkServiceClient( + hwaddress.NewClient(vppConn), + l2vtr.NewClient(vppConn), + &vlanClient{ + vppConn: vppConn, + deviceNames: domain2Device, + }, + linkinit.NewClient(vppConn, domain2Device), + ) +} + +func (v *vlanClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { + if request.GetConnection().GetPayload() != payload.Ethernet { + return next.Client(ctx).Request(ctx, request, opts...) + } + + mechanism := &networkservice.Mechanism{ + Cls: cls.REMOTE, + Type: vlanmech.MECHANISM, + Parameters: make(map[string]string), + } + request.MechanismPreferences = append(request.MechanismPreferences, mechanism) + + postponeCtxFunc := postpone.ContextWithValues(ctx) + + conn, err := next.Client(ctx).Request(ctx, request, opts...) + if err != nil { + return nil, err + } + + if err := addSubIf(ctx, conn, v.vppConn, v.deviceNames); err != nil { + closeCtx, cancelClose := postponeCtxFunc() + defer cancelClose() + + if _, closeErr := v.Close(closeCtx, conn, opts...); closeErr != nil { + err = errors.Wrapf(err, "connection closed with error: %s", closeErr.Error()) + } + + return nil, err + } + + return conn, nil +} + +func (v *vlanClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { + if conn.GetPayload() != payload.Ethernet { + return next.Client(ctx).Close(ctx, conn, opts...) + } + _ = delSubIf(ctx, conn, v.vppConn) + return next.Client(ctx).Close(ctx, conn, opts...) +} + +func addSubIf(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, deviceNames map[string]string) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + _, ok := ifindex.Load(ctx, true) + if ok { + return nil + } + now := time.Now() + serviceDomain := conn.GetLabels()[serviceDomainLabel] + hostIFName, ok := deviceNames[serviceDomain] + if !ok { + return errors.Errorf("no interface name for service domain %s", serviceDomain) + } + + client, err := interfaces.NewServiceClient(vppConn).SwInterfaceDump(ctx, &interfaces.SwInterfaceDump{ + NameFilterValid: true, + NameFilter: hostIFName, + }) + if err != nil { + return errors.Wrapf(err, "error attempting to get interface dump client to set vlan subinterface on %q", hostIFName) + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceName", hostIFName). + WithField("vppapi", "SwInterfaceDump").Debug("completed") + + for { + details, err := client.Recv() + if err == io.EOF { + break + } + if err != nil { + return errors.Wrapf(err, "error attempting to get interface details to set vlan subinterface on %q", hostIFName) + } + now = time.Now() + swIfIndex := details.SwIfIndex + vlanID := mechanism.GetVlanID() + vlanSubif := &interfaces.CreateVlanSubif{ + SwIfIndex: swIfIndex, + VlanID: vlanID, + } + + rsp, err := interfaces.NewServiceClient(vppConn).CreateVlanSubif(ctx, vlanSubif) + if err != nil { + return errors.WithStack(err) + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceIndex", swIfIndex). + WithField("VlanID", vlanID). + WithField("vppapi", "CreateVlanSubIf").Debug("completed") + + ifindex.Store(ctx, true, rsp.SwIfIndex) + } + } + return nil +} +func delSubIf(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + swIfIndex, ok := ifindex.Load(ctx, true) + if !ok { + return nil + } + now := time.Now() + vlanSubif := &interfaces.DeleteSubif{ + SwIfIndex: swIfIndex, + } + _, err := interfaces.NewServiceClient(vppConn).DeleteSubif(ctx, vlanSubif) + if err != nil { + return errors.WithStack(err) + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceIndex", swIfIndex). + WithField("vppapi", "DeleteSubif").Debug("completed") + ifindex.Delete(ctx, true) + } + return nil +} diff --git a/pkg/networkservice/mechanisms/vlan/doc.go b/pkg/networkservice/mechanisms/vlan/doc.go new file mode 100644 index 00000000..4a101d38 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 vlan provides chain elements for implementing the vlan mechanism +package vlan diff --git a/pkg/networkservice/mechanisms/vlan/hwaddress/client.go b/pkg/networkservice/mechanisms/vlan/hwaddress/client.go new file mode 100644 index 00000000..a93931e8 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/hwaddress/client.go @@ -0,0 +1,72 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 hwaddress + +import ( + "context" + + "git.fd.io/govpp.git/api" + "github.com/golang/protobuf/ptypes/empty" + "github.com/pkg/errors" + "google.golang.org/grpc" + + "github.com/networkservicemesh/api/pkg/api/networkservice" + "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" + "github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata" + "github.com/networkservicemesh/sdk/pkg/tools/postpone" +) + +type hwaddressClient struct { + vppConn api.Connection +} + +// NewClient - updates ethernet context with hw address +func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { + return &hwaddressClient{ + vppConn: vppConn, + } +} + +func (h *hwaddressClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { + postponeCtxFunc := postpone.ContextWithValues(ctx) + + conn, err := next.Client(ctx).Request(ctx, request, opts...) + if err != nil { + return nil, err + } + + if err := setEthContextHwaddress(ctx, conn, h.vppConn, metadata.IsClient(h)); err != nil { + if closeErr := h.closeOnFailure(postponeCtxFunc, conn, opts); closeErr != nil { + err = errors.Wrapf(err, "connection closed with error: %s", closeErr.Error()) + } + return nil, err + } + return conn, nil +} + +func (h *hwaddressClient) closeOnFailure(postponeCtxFunc func() (context.Context, context.CancelFunc), conn *networkservice.Connection, opts []grpc.CallOption) error { + closeCtx, cancelClose := postponeCtxFunc() + defer cancelClose() + + _, err := h.Close(closeCtx, conn, opts...) + + return err +} + +func (h *hwaddressClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { + return next.Client(ctx).Close(ctx, conn, opts...) +} diff --git a/pkg/networkservice/mechanisms/vlan/hwaddress/common.go b/pkg/networkservice/mechanisms/vlan/hwaddress/common.go new file mode 100644 index 00000000..6f53b704 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/hwaddress/common.go @@ -0,0 +1,61 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 hwaddress + +import ( + "context" + "time" + + "git.fd.io/govpp.git/api" + "github.com/pkg/errors" + + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/networkservicemesh/api/pkg/api/networkservice" + vlanmech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/vlan" + "github.com/networkservicemesh/sdk/pkg/tools/log" + + "github.com/networkservicemesh/sdk-vpp/pkg/tools/ifindex" +) + +func setEthContextHwaddress(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, isClient bool) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + now := time.Now() + swIfIndex, ok := ifindex.Load(ctx, isClient) + if !ok { + return nil + } + + rsp, err := interfaces.NewServiceClient(vppConn).SwInterfaceGetMacAddress(ctx, &interfaces.SwInterfaceGetMacAddress{ + SwIfIndex: swIfIndex}) + if err != nil { + return errors.WithStack(err) + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceIndex", swIfIndex). + WithField("HwAddress", rsp.MacAddress). + WithField("vppapi", "SwInterfaceGetMacAddress").Debug("completed") + + if conn.GetContext().GetEthernetContext() == nil { + conn.GetContext().EthernetContext = new(networkservice.EthernetContext) + } + ethernetContext := conn.GetContext().GetEthernetContext() + + ethernetContext.SrcMac = rsp.MacAddress.String() + } + return nil +} diff --git a/pkg/networkservice/mechanisms/vlan/hwaddress/doc.go b/pkg/networkservice/mechanisms/vlan/hwaddress/doc.go new file mode 100644 index 00000000..7d659cf4 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/hwaddress/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 hwaddress provides networkservice chain elements to set the hwaddress in ethernetcontext +package hwaddress diff --git a/pkg/networkservice/mechanisms/vlan/l2vtr/client.go b/pkg/networkservice/mechanisms/vlan/l2vtr/client.go new file mode 100644 index 00000000..6c190c8a --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/l2vtr/client.go @@ -0,0 +1,120 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 l2vtr + +import ( + "context" + "time" + + "git.fd.io/govpp.git/api" + "github.com/edwarnicke/govpp/binapi/l2" + "github.com/golang/protobuf/ptypes/empty" + "github.com/pkg/errors" + "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/pkg/networkservice/core/next" + "github.com/networkservicemesh/sdk/pkg/tools/log" + "github.com/networkservicemesh/sdk/pkg/tools/postpone" + + "github.com/networkservicemesh/sdk-vpp/pkg/tools/ifindex" +) + +type l2vtrClient struct { + vppConn api.Connection +} + +// NewClient - set vlan tag rewrite for remote vlan mechanism +func NewClient(vppConn api.Connection) networkservice.NetworkServiceClient { + return &l2vtrClient{vppConn: vppConn} +} + +func (v *l2vtrClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { + postponeCtxFunc := postpone.ContextWithValues(ctx) + + conn, err := next.Client(ctx).Request(ctx, request, opts...) + if err != nil { + return nil, err + } + + if err = enableVtr(ctx, conn, v.vppConn); err != nil { + closeCtx, cancelClose := postponeCtxFunc() + defer cancelClose() + + if _, closeErr := v.Close(closeCtx, conn, opts...); closeErr != nil { + err = errors.Wrapf(err, "connection closed with error: %s", closeErr.Error()) + } + + return nil, err + } + + return conn, nil +} + +func (v *l2vtrClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { + _ = disableVtr(ctx, conn, v.vppConn) + return next.Client(ctx).Close(ctx, conn, opts...) +} + +func enableVtr(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + swIfIndex, ok := ifindex.Load(ctx, true) + if !ok { + return nil + } + now := time.Now() + if _, err := l2.NewServiceClient(vppConn).L2InterfaceVlanTagRewrite(ctx, &l2.L2InterfaceVlanTagRewrite{ + SwIfIndex: swIfIndex, + VtrOp: L2VtrPop1, + PushDot1q: 0, + Tag1: 0, + Tag2: 0, + }); err != nil { + return errors.WithStack(err) + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("SwIfIndex", swIfIndex). + WithField("operation", "POP 1"). + WithField("vppapi", "L2InterfaceVlanTagRewrite").Debug("completed") + } + return nil +} + +func disableVtr(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + swIfIndex, ok := ifindex.Load(ctx, true) + if !ok { + return nil + } + now := time.Now() + + if _, err := l2.NewServiceClient(vppConn).L2InterfaceVlanTagRewrite(ctx, &l2.L2InterfaceVlanTagRewrite{ + SwIfIndex: swIfIndex, + VtrOp: L2VtrDisabled, + }); err != nil { + return errors.WithStack(err) + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("SwIfIndex", swIfIndex). + WithField("operation", "DISABLE"). + WithField("vppapi", "L2InterfaceVlanTagRewrite").Debug("completed") + } + return nil +} diff --git a/pkg/networkservice/mechanisms/vlan/l2vtr/constants.go b/pkg/networkservice/mechanisms/vlan/l2vtr/constants.go new file mode 100644 index 00000000..a04c719b --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/l2vtr/constants.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 l2vtr + +// defines enum 'vtr_op'. +const ( + L2VtrDisabled uint32 = 0 + L2VtrPop1 uint32 = 3 + L2VtrPushDot1Q uint32 = 1 +) diff --git a/pkg/networkservice/mechanisms/vlan/l2vtr/doc.go b/pkg/networkservice/mechanisms/vlan/l2vtr/doc.go new file mode 100644 index 00000000..9a01ead8 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/l2vtr/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 l2vtr provides chain elements for setting Vlan Tag Rewrite on subinterfaces +package l2vtr diff --git a/pkg/networkservice/mechanisms/vlan/linkinit/client.go b/pkg/networkservice/mechanisms/vlan/linkinit/client.go new file mode 100644 index 00000000..5ac7f2df --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/linkinit/client.go @@ -0,0 +1,181 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 linkinit + +import ( + "context" + "io" + "time" + + "git.fd.io/govpp.git/api" + interfaces "github.com/edwarnicke/govpp/binapi/interface" + "github.com/edwarnicke/govpp/binapi/interface_types" + "github.com/golang/protobuf/ptypes/empty" + "github.com/pkg/errors" + "github.com/vishvananda/netlink" + "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/pkg/networkservice/core/next" + "github.com/networkservicemesh/sdk/pkg/tools/log" + "github.com/networkservicemesh/sdk/pkg/tools/postpone" + + "github.com/networkservicemesh/sdk-vpp/pkg/tools/initvpp" +) + +const ( + serviceDomainLabel = "serviceDomain" +) + +type linkinitClient struct { + vppConn api.Connection + deviceNames map[string]string +} + +// NewClient - creates links for vlan remote mechanism +func NewClient(vppConn api.Connection, domain2Device map[string]string) networkservice.NetworkServiceClient { + return &linkinitClient{ + vppConn: vppConn, + deviceNames: domain2Device, + } +} + +func (l *linkinitClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { + postponeCtxFunc := postpone.ContextWithValues(ctx) + + conn, err := next.Client(ctx).Request(ctx, request, opts...) + if err != nil { + return nil, err + } + + if err = initLink(ctx, conn, l.vppConn, l.deviceNames); err != nil { + closeCtx, cancelClose := postponeCtxFunc() + defer cancelClose() + + if _, closeErr := l.Close(closeCtx, conn, opts...); closeErr != nil { + err = errors.Wrapf(err, "connection closed with error: %s", closeErr.Error()) + } + return nil, err + } + return conn, nil +} + +func (l *linkinitClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { + _ = unsetLink(ctx, conn, l.deviceNames) + return next.Client(ctx).Close(ctx, conn, opts...) +} + +func initLink(ctx context.Context, conn *networkservice.Connection, vppConn api.Connection, deviceNames map[string]string) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + serviceDomain := conn.GetLabels()[serviceDomainLabel] + hostIFName, ok := deviceNames[serviceDomain] + if !ok { + return errors.Errorf("no interface name for service domain %s", serviceDomain) + } + log.FromContext(ctx).WithField("linkinit", "Request").Debugf("initLink", hostIFName) + client, err := interfaces.NewServiceClient(vppConn).SwInterfaceDump(ctx, &interfaces.SwInterfaceDump{ + NameFilterValid: true, + NameFilter: hostIFName, + }) + if err == nil { + _, err = client.Recv() + /* No interface found, it can be created */ + if err == io.EOF { + link, nerr := netlink.LinkByName(hostIFName) + if nerr != nil { + return errors.Errorf("error getting host device with name %s", hostIFName) + } + nerr = createInterface(ctx, vppConn, link) + if nerr != nil { + return errors.Wrapf(err, "error attempting to create AF_PACKET inteface") + } + + // Set promiscuous mode + now := time.Now() + + nerr = netlink.SetPromiscOn(link) + if nerr != nil { + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceName", hostIFName). + WithField("netlink", "SetPromiscOn"). + Warn("Promiscuous mode not set!") + } else { + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceName", hostIFName). + WithField("netlink", "SetPromiscOn").Debug("completed") + } + } + } else { + return errors.Wrapf(err, "error attempting to get interface dump client create AF_PACKET inteface") + } + } + return nil +} + +func unsetLink(ctx context.Context, conn *networkservice.Connection, deviceNames map[string]string) error { + if mechanism := vlanmech.ToMechanism(conn.GetMechanism()); mechanism != nil { + now := time.Now() + serviceDomain := conn.GetLabels()[serviceDomainLabel] + hostIFName, ok := deviceNames[serviceDomain] + if !ok { + return errors.Errorf("no interface name for service domain %s", serviceDomain) + } + link, err := netlink.LinkByName(hostIFName) + if err != nil { + return errors.Errorf("error getting host device with name %s", hostIFName) + } + + // Set promiscuous mode + err = netlink.SetPromiscOff(link) + if err != nil { + log.FromContext(ctx).Warn("Promiscuous mode not set OFF!") + } + log.FromContext(ctx). + WithField("duration", time.Since(now)). + WithField("HostInterfaceName", link.Attrs().Name). + WithField("netlink", "SetPromiscOff").Debug("completed") + } + return nil +} + +func createInterface(ctx context.Context, vppConn api.Connection, link netlink.Link) error { + swIfIndex, err := initvpp.CreateAfPacket(ctx, vppConn, link) + if err != nil { + return err + } + + if aclErr := initvpp.DenyAllACLToInterface(ctx, vppConn, swIfIndex); aclErr != nil { + return aclErr + } + + now := time.Now() + _, err = interfaces.NewServiceClient(vppConn).SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{ + SwIfIndex: swIfIndex, + Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, + }) + if err != nil { + return err + } + log.FromContext(ctx). + WithField("swIfIndex", swIfIndex). + WithField("duration", time.Since(now)). + WithField("vppapi", "SwInterfaceSetFlags").Debug("completed") + return nil +} diff --git a/pkg/networkservice/mechanisms/vlan/linkinit/doc.go b/pkg/networkservice/mechanisms/vlan/linkinit/doc.go new file mode 100644 index 00000000..ec770b2c --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/linkinit/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2021 Nordix Foundation. +// +// 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 linkinit provides chain elements for setting link to AF_PACKET interface if it is not already set +package linkinit