diff --git a/go.mod b/go.mod index 5084970..957aabf 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/vishvananda/netlink v1.2.1-beta.2.0.20220630165224-c591ada0fb2b google.golang.org/grpc v1.49.0 + google.golang.org/protobuf v1.28.1 k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b ) @@ -95,7 +96,6 @@ require ( golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220908141613-51c1cc9bc6d0 // indirect - google.golang.org/protobuf v1.28.1 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/gcfg.v1 v1.2.3 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/networkservice/mechanisms/vlan/client.go b/pkg/networkservice/mechanisms/vlan/client.go index c487a39..7cb2dc9 100644 --- a/pkg/networkservice/mechanisms/vlan/client.go +++ b/pkg/networkservice/mechanisms/vlan/client.go @@ -30,12 +30,14 @@ import ( "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/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/pkg/errors" "google.golang.org/grpc" + "github.com/networkservicemesh/sdk-ovs/pkg/networkservice/mechanisms/vlan/mtu" "github.com/networkservicemesh/sdk-ovs/pkg/tools/ifnames" ovsutil "github.com/networkservicemesh/sdk-ovs/pkg/tools/utils" ) @@ -51,7 +53,10 @@ type vlanClient struct { // NewClient returns a client chain element implementing VLAN breakout for NS client func NewClient(bridgeName string, l2Connections map[string]*ovsutil.L2ConnectionPoint) networkservice.NetworkServiceClient { - return &vlanClient{bridgeName: bridgeName, l2Connections: l2Connections} + return chain.NewNetworkServiceClient( + mtu.NewClient(l2Connections), + &vlanClient{bridgeName: bridgeName, l2Connections: l2Connections}, + ) } func (c *vlanClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { diff --git a/pkg/networkservice/mechanisms/vlan/mtu/client.go b/pkg/networkservice/mechanisms/vlan/mtu/client.go new file mode 100644 index 0000000..56a5b29 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/mtu/client.go @@ -0,0 +1,100 @@ +// Copyright (c) 2022 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. + +//go:build linux +// +build linux + +package mtu + +import ( + "context" + + "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/networkservicemesh/api/pkg/api/networkservice" + "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" + + ovsutil "github.com/networkservicemesh/sdk-ovs/pkg/tools/utils" +) + +const ( + viaLabel = "via" +) + +type mtuClient struct { + l2Connections map[string]*ovsutil.L2ConnectionPoint + mtus mtuMap +} + +// NewClient - returns client chain element to manage vlan MTU +func NewClient(l2Connections map[string]*ovsutil.L2ConnectionPoint) networkservice.NetworkServiceClient { + return &mtuClient{ + l2Connections: l2Connections, + } +} + +func (m *mtuClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { + postponeCtxFunc := postpone.ContextWithValues(ctx) + logger := log.FromContext(ctx).WithField("vlanClient", "Request") + + conn, err := next.Client(ctx).Request(ctx, request, opts...) + if err != nil { + return nil, err + } + + if mechanism := vlan.ToMechanism(conn.GetMechanism()); mechanism != nil { + viaSelector, ok := conn.GetLabels()[viaLabel] + if !ok { + return conn, nil + } + l2Point, ok := m.l2Connections[viaSelector] + if !ok { + return conn, nil + } + if l2Point.Interface == "" { + return conn, nil + } + localMTU, loaded := m.mtus.Load(l2Point.Interface) + if !loaded { + localMTU, err = getMTU(l2Point, logger) + if err != nil { + closeCtx, cancelClose := postponeCtxFunc() + defer cancelClose() + if _, closeErr := m.Close(closeCtx, conn, opts...); closeErr != nil { + err = errors.Wrapf(err, "connection closed with error: %s", closeErr.Error()) + } + return nil, err + } + m.mtus.Store(l2Point.Interface, localMTU) + } + if localMTU > 0 && (conn.GetContext().GetMTU() > localMTU || conn.GetContext().GetMTU() == 0) { + if conn.GetContext() == nil { + conn.Context = &networkservice.ConnectionContext{} + } + conn.GetContext().MTU = localMTU + } + } + return conn, nil +} + +func (m *mtuClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*emptypb.Empty, error) { + return next.Client(ctx).Close(ctx, conn, opts...) +} diff --git a/pkg/networkservice/mechanisms/vlan/mtu/common.go b/pkg/networkservice/mechanisms/vlan/mtu/common.go new file mode 100644 index 0000000..fc1e263 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/mtu/common.go @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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. + +//go:build linux +// +build linux + +package mtu + +import ( + "time" + + "github.com/networkservicemesh/sdk/pkg/tools/log" + "github.com/pkg/errors" + "github.com/vishvananda/netlink" + + ovsutil "github.com/networkservicemesh/sdk-ovs/pkg/tools/utils" +) + +func getMTU(l2CP *ovsutil.L2ConnectionPoint, logger log.Logger) (uint32, error) { + now := time.Now() + link, err := netlink.LinkByName(l2CP.Interface) + if err != nil { + return 0, nil + } + mtu := link.Attrs().MTU + logger.WithField("link.Name", link.Attrs().Name). + WithField("link.MTU", mtu). + WithField("duration", time.Since(now)). + WithField("netlink", "LinkByName").Debug("completed") + if mtu >= 0 && mtu <= 65535 { + return uint32(mtu), nil + } + + return 0, errors.New("invalid MTU value") +} diff --git a/pkg/networkservice/mechanisms/vlan/mtu/doc.go b/pkg/networkservice/mechanisms/vlan/mtu/doc.go new file mode 100644 index 0000000..211b44f --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/mtu/doc.go @@ -0,0 +1,21 @@ +// Copyright (c) 2022 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. + +//go:build linux +// +build linux + +// Package mtu computes the mtu for the vlan interface and adds it to context +package mtu diff --git a/pkg/networkservice/mechanisms/vlan/mtu/gen.go b/pkg/networkservice/mechanisms/vlan/mtu/gen.go new file mode 100644 index 0000000..3271da1 --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/mtu/gen.go @@ -0,0 +1,26 @@ +// Copyright (c) 2022 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 mtu + +import ( + "sync" +) + +//go:generate go-syncmap -output mtu_map.gen.go -type mtuMap + +// mtuMap - sync.Map with key as interface name and value as MTU +type mtuMap sync.Map diff --git a/pkg/networkservice/mechanisms/vlan/mtu/mtu_map.gen.go b/pkg/networkservice/mechanisms/vlan/mtu/mtu_map.gen.go new file mode 100644 index 0000000..0c393ba --- /dev/null +++ b/pkg/networkservice/mechanisms/vlan/mtu/mtu_map.gen.go @@ -0,0 +1,73 @@ +// Code generated by "-output mtu_map.gen.go -type mtuMap -output mtu_map.gen.go -type mtuMap"; DO NOT EDIT. +package mtu + +import ( + "sync" // Used by sync.Map. +) + +// Generate code that will fail if the constants change value. +func _() { + // An "cannot convert mtuMap literal (type mtuMap) to type sync.Map" compiler error signifies that the base type have changed. + // Re-run the go-syncmap command to generate them again. + _ = (sync.Map)(mtuMap{}) +} + +var _nil_mtuMap_uint32_value = func() (val uint32) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *mtuMap) Load(key string) (uint32, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_mtuMap_uint32_value, ok + } + return value.(uint32), ok +} + +// Store sets the value for a key. +func (m *mtuMap) Store(key string, value uint32) { + (*sync.Map)(m).Store(key, value) +} + +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *mtuMap) LoadOrStore(key string, value uint32) (uint32, bool) { + actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) + if actual == nil { + return _nil_mtuMap_uint32_value, loaded + } + return actual.(uint32), loaded +} + +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *mtuMap) LoadAndDelete(key string) (value uint32, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_mtuMap_uint32_value, loaded + } + return actual.(uint32), loaded +} + +// Delete deletes the value for a key. +func (m *mtuMap) Delete(key string) { + (*sync.Map)(m).Delete(key) +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *mtuMap) Range(f func(key string, value uint32) bool) { + (*sync.Map)(m).Range(func(key, value interface{}) bool { + return f(key.(string), value.(uint32)) + }) +}